@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/config-registry.md
CHANGED
|
@@ -1,348 +1,348 @@
|
|
|
1
|
-
# Config Registry
|
|
2
|
-
|
|
3
|
-
> Deep specs for project / deploy / services / avatar / paths config slots. Source: `packages/core/src/`. Bijgewerkt: 2026-05-20.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
The config registries are the single source of truth for runtime project configuration. They follow a uniform pattern:
|
|
8
|
-
|
|
9
|
-
1. A consumer's boot entry calls `registerXConfig({...})` once before any framework code reads a value.
|
|
10
|
-
2. Framework code reads at call time via `getXConfig()` (never at module load) so the registration timing is flexible.
|
|
11
|
-
3. Boolean `isXConfigRegistered()` helpers exist for code paths that must fail closed when the consumer forgot to register.
|
|
12
|
-
|
|
13
|
-
Four independent slots live in this topic:
|
|
14
|
-
|
|
15
|
-
- **Project config** — runtime tunables (rate limits, session policy, HTTP, auth, sockets, paths). Deep-merged over `DEFAULT_PROJECT_CONFIG` so consumers only supply the keys they want to override.
|
|
16
|
-
- **Deploy config** — multi-env topology consumed by `@luckystack/router` (resources, environments, routing, development).
|
|
17
|
-
- **Services config** — service + preset definitions consumed by the router / preset loader.
|
|
18
|
-
- **Avatar config** — disk formats + cache header for `serveAvatar`.
|
|
19
|
-
|
|
20
|
-
Path getters (`getSrcDir`, `getGeneratedApiDocsPath`, …) resolve project-relative paths against `ROOT_DIR` (auto-detected workspace root) and read `getProjectConfig().paths.*` at call time.
|
|
21
|
-
|
|
22
|
-
## API Reference — Project Config
|
|
23
|
-
|
|
24
|
-
### `registerProjectConfig(config: ProjectConfigInput): void`
|
|
25
|
-
|
|
26
|
-
**Signature:**
|
|
27
|
-
```typescript
|
|
28
|
-
export const registerProjectConfig = (config: ProjectConfigInput): void
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
**Parameters:**
|
|
32
|
-
| Name | Type | Required | Description |
|
|
33
|
-
|---|---|---|---|
|
|
34
|
-
| `config` | `ProjectConfigInput` (deep-partial of `ProjectConfig`) | yes | Only the keys to override. Missing keys fall back to `DEFAULT_PROJECT_CONFIG`. |
|
|
35
|
-
|
|
36
|
-
**Returns:** `void`
|
|
37
|
-
|
|
38
|
-
**Behavior:**
|
|
39
|
-
- Deep-merges `config` over `DEFAULT_PROJECT_CONFIG` (plain-object branches recurse; primitives + arrays are replaced wholesale).
|
|
40
|
-
- Sets the registration flag so `isProjectConfigRegistered()` returns true.
|
|
41
|
-
- Replaces (last-write-wins) on repeated calls — the latest call is the active config.
|
|
42
|
-
|
|
43
|
-
**Errors / Edge cases:**
|
|
44
|
-
- `undefined` keys in the override are skipped (treated as "no override").
|
|
45
|
-
- Arrays are NOT merged element-wise — passing `cors.allowedOrigins: ['x']` replaces the default `[]` array.
|
|
46
|
-
- Calling after framework hot paths already ran is allowed but the values only take effect on subsequent reads (everything reads call-time).
|
|
47
|
-
|
|
48
|
-
**Example:**
|
|
49
|
-
```typescript
|
|
50
|
-
import { registerProjectConfig } from '@luckystack/core';
|
|
51
|
-
|
|
52
|
-
registerProjectConfig({
|
|
53
|
-
defaultLanguage: 'en',
|
|
54
|
-
session: { expiryDays: 30 },
|
|
55
|
-
http: {
|
|
56
|
-
cors: {
|
|
57
|
-
allowedOrigins: ['https://app.example.com'],
|
|
58
|
-
allowLocalhost: false,
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
});
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### `getProjectConfig(): ProjectConfig`
|
|
65
|
-
|
|
66
|
-
**Signature:**
|
|
67
|
-
```typescript
|
|
68
|
-
export const getProjectConfig = (): ProjectConfig
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
**Returns:** The active fully-merged `ProjectConfig`.
|
|
72
|
-
|
|
73
|
-
**Behavior:**
|
|
74
|
-
- Returns the currently merged config object (reference-stable until the next `registerProjectConfig`).
|
|
75
|
-
- Returns `DEFAULT_PROJECT_CONFIG` when nothing was registered yet.
|
|
76
|
-
|
|
77
|
-
**Example:**
|
|
78
|
-
```typescript
|
|
79
|
-
import { getProjectConfig } from '@luckystack/core';
|
|
80
|
-
|
|
81
|
-
const { http, rateLimiting } = getProjectConfig();
|
|
82
|
-
const cookieName = http.sessionCookieName;
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### `getProjectName(): string`
|
|
86
|
-
|
|
87
|
-
**Signature:**
|
|
88
|
-
```typescript
|
|
89
|
-
export const getProjectName = (): string
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
**Returns:** The active project namespace (used as Redis key prefix for session, password reset, rate limits, OAuth state, etc.).
|
|
93
|
-
|
|
94
|
-
**Behavior:** Resolves in this order (first non-empty wins):
|
|
95
|
-
1. `projectConfig.session.projectName`
|
|
96
|
-
2. `process.env.PROJECT_NAME`
|
|
97
|
-
3. literal `'luckystack'`
|
|
98
|
-
|
|
99
|
-
**Example:**
|
|
100
|
-
```typescript
|
|
101
|
-
import { getProjectName } from '@luckystack/core';
|
|
102
|
-
|
|
103
|
-
const key = `${getProjectName()}-session:${token}`;
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### `isProjectConfigRegistered(): boolean`
|
|
107
|
-
|
|
108
|
-
**Signature:**
|
|
109
|
-
```typescript
|
|
110
|
-
export const isProjectConfigRegistered = (): boolean
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
**Returns:** `true` only after `registerProjectConfig` has been called at least once.
|
|
114
|
-
|
|
115
|
-
**Behavior:**
|
|
116
|
-
- Pure boot-time guard — does not throw.
|
|
117
|
-
- Framework packages use this for "did the installer forget to register?" warnings without crashing tests/CLI contexts.
|
|
118
|
-
|
|
119
|
-
### `DEFAULT_PROJECT_CONFIG: ProjectConfig`
|
|
120
|
-
|
|
121
|
-
Exported so consumers can spread + override structurally:
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
import { DEFAULT_PROJECT_CONFIG, registerProjectConfig } from '@luckystack/core';
|
|
125
|
-
|
|
126
|
-
registerProjectConfig({
|
|
127
|
-
...DEFAULT_PROJECT_CONFIG,
|
|
128
|
-
defaultLanguage: 'nl',
|
|
129
|
-
});
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
Notable defaults (verbatim from `projectConfig.ts`):
|
|
133
|
-
|
|
134
|
-
| Key | Default |
|
|
135
|
-
|---|---|
|
|
136
|
-
| `rateLimiting.enabled` | `true` |
|
|
137
|
-
| `rateLimiting.store` | `'memory'` |
|
|
138
|
-
| `rateLimiting.defaultApiLimit` | `60` |
|
|
139
|
-
| `rateLimiting.defaultIpLimit` | `100` |
|
|
140
|
-
| `rateLimiting.windowMs` | `60_000` |
|
|
141
|
-
| `session.expiryDays` | `7` |
|
|
142
|
-
| `session.perUser` | `'single'` |
|
|
143
|
-
| `session.onConflict` | `'revokeOld'` |
|
|
144
|
-
| `session.notifyOldDeviceOnRevoke` | `true` |
|
|
145
|
-
| `http.sessionCookieName` | `'token'` |
|
|
146
|
-
| `http.sessionCookieSameSite` | `'Strict'` |
|
|
147
|
-
| `http.requestBodyMaxBytes` | `1048576` |
|
|
148
|
-
| `http.healthEndpoint` | `'/_health'` |
|
|
149
|
-
| `http.liveEndpoint` | `'/livez'` |
|
|
150
|
-
| `http.readyEndpoint` | `'/readyz'` |
|
|
151
|
-
| `http.cors.credentials` | `true` |
|
|
152
|
-
| `http.cors.allowedOrigins` | `[]` |
|
|
153
|
-
| `http.cors.allowLocalhost` | `false` |
|
|
154
|
-
| `auth.bcryptRounds` | `10` |
|
|
155
|
-
| `auth.providerAccountStrategy` | `'per-provider'` |
|
|
156
|
-
| `auth.forgotPassword` | `'disabled'` |
|
|
157
|
-
| `auth.passwordResetTtlSeconds` | `3600` |
|
|
158
|
-
| `socket.maxHttpBufferSize` | `5 * 1024 * 1024` |
|
|
159
|
-
| `socket.pingTimeout` | `20_000` |
|
|
160
|
-
| `socket.pingInterval` | `25_000` |
|
|
161
|
-
| `sync.fanoutYieldEvery` | `100` |
|
|
162
|
-
| `sync.fanoutYieldMs` | `1` |
|
|
163
|
-
| `offlineQueue.maxSize` | `200` |
|
|
164
|
-
| `offlineQueue.maxAgeMs` | `3_600_000` |
|
|
165
|
-
| `offlineQueue.dropPolicy` | `'drop-oldest'` |
|
|
166
|
-
| `dev.hotReloadDebounceMs` | `120` |
|
|
167
|
-
| `defaultLanguage` | `'en'` |
|
|
168
|
-
| `defaultTheme` | `'light'` |
|
|
169
|
-
|
|
170
|
-
## API Reference — Deploy Config
|
|
171
|
-
|
|
172
|
-
### `registerDeployConfig(config: DeployConfigShape): DeployConfigShape`
|
|
173
|
-
|
|
174
|
-
**Signature:**
|
|
175
|
-
```typescript
|
|
176
|
-
export const registerDeployConfig = (config: DeployConfigShape): DeployConfigShape
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
**Parameters:**
|
|
180
|
-
| Name | Type | Required | Description |
|
|
181
|
-
|---|---|---|---|
|
|
182
|
-
| `config` | `DeployConfigShape` | yes | Deploy topology object — `{ resources, environments?, routing?, development? }`. |
|
|
183
|
-
|
|
184
|
-
**Returns:** The stored config (same reference passed in).
|
|
185
|
-
|
|
186
|
-
**Behavior:**
|
|
187
|
-
- Stores the provided config wholesale (no deep-merge, unlike `registerProjectConfig`).
|
|
188
|
-
- Sets `isDeployConfigRegistered()` to `true`.
|
|
189
|
-
|
|
190
|
-
**Example:**
|
|
191
|
-
```typescript
|
|
192
|
-
import { registerDeployConfig } from '@luckystack/core';
|
|
193
|
-
|
|
194
|
-
registerDeployConfig({
|
|
195
|
-
resources: {
|
|
196
|
-
redis: { type: 'redis', urlEnvKey: 'REDIS_URL', synchronizedEnvKeys: ['SESSION_SECRET'] },
|
|
197
|
-
mongo: { type: 'mongo', urlEnvKey: 'MONGO_URL' },
|
|
198
|
-
},
|
|
199
|
-
environments: {
|
|
200
|
-
production: { redis: 'redis', mongo: 'mongo', bindings: {} },
|
|
201
|
-
},
|
|
202
|
-
routing: { strictBootHandshake: true },
|
|
203
|
-
});
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
### `getDeployConfig(): DeployConfigShape`
|
|
207
|
-
|
|
208
|
-
Returns the registered deploy config, or `{ resources: {} }` when nothing was registered. Never throws.
|
|
209
|
-
|
|
210
|
-
### `isDeployConfigRegistered(): boolean`
|
|
211
|
-
|
|
212
|
-
Non-throwing guard. Used by router / boot probes that need a real registration before behaving differently from the empty default.
|
|
213
|
-
|
|
214
|
-
## API Reference — Services Config
|
|
215
|
-
|
|
216
|
-
### `registerServicesConfig(config: ServicesConfigShape): ServicesConfigShape`
|
|
217
|
-
|
|
218
|
-
**Signature:**
|
|
219
|
-
```typescript
|
|
220
|
-
export const registerServicesConfig = (config: ServicesConfigShape): ServicesConfigShape
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
**Parameters:**
|
|
224
|
-
| Name | Type | Required | Description |
|
|
225
|
-
|---|---|---|---|
|
|
226
|
-
| `config` | `ServicesConfigShape` | yes | `{ services, presets }` — services define folder sources, presets bundle multiple services into one artifact. |
|
|
227
|
-
|
|
228
|
-
**Returns:** The stored config.
|
|
229
|
-
|
|
230
|
-
**Behavior:**
|
|
231
|
-
- Stores the config (last-write-wins).
|
|
232
|
-
- Side-effect pattern: the consumer's `services.config.ts` is expected to call this on module import.
|
|
233
|
-
|
|
234
|
-
### `getServicesConfig(): ServicesConfigShape`
|
|
235
|
-
|
|
236
|
-
**Throws** `Error` when no services config has been registered. Message includes guidance to import `services.config.ts` as a side-effect.
|
|
237
|
-
|
|
238
|
-
### `isServicesConfigRegistered(): boolean`
|
|
239
|
-
|
|
240
|
-
Non-throwing guard for code that wants to fall back instead of crashing.
|
|
241
|
-
|
|
242
|
-
## API Reference — Avatar Config
|
|
243
|
-
|
|
244
|
-
### `registerAvatarConfig(config: AvatarConfigInput): void`
|
|
245
|
-
|
|
246
|
-
**Signature:**
|
|
247
|
-
```typescript
|
|
248
|
-
export const registerAvatarConfig = (config: AvatarConfigInput): void
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
**Parameters:**
|
|
252
|
-
| Name | Type | Required | Description |
|
|
253
|
-
|---|---|---|---|
|
|
254
|
-
| `config.formats` | `{ extension: string; contentType: string }[]` | no | Disk formats checked in order; first existing file wins. |
|
|
255
|
-
| `config.cacheControl` | `string` | no | `Cache-Control` header. Default `'public, max-age=86400'`. |
|
|
256
|
-
|
|
257
|
-
**Behavior:**
|
|
258
|
-
- Empty `formats` array is treated as "use default" (keeps the built-in `[{ extension: 'webp', contentType: 'image/webp' }]`).
|
|
259
|
-
- Missing `cacheControl` keeps the default.
|
|
260
|
-
|
|
261
|
-
**Example:**
|
|
262
|
-
```typescript
|
|
263
|
-
import { registerAvatarConfig } from '@luckystack/core';
|
|
264
|
-
|
|
265
|
-
registerAvatarConfig({
|
|
266
|
-
formats: [
|
|
267
|
-
{ extension: 'webp', contentType: 'image/webp' },
|
|
268
|
-
{ extension: 'png', contentType: 'image/png' },
|
|
269
|
-
],
|
|
270
|
-
cacheControl: 'public, max-age=604800',
|
|
271
|
-
});
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
### `getAvatarConfig(): AvatarConfig`
|
|
275
|
-
|
|
276
|
-
Returns the active avatar config (defaults applied).
|
|
277
|
-
|
|
278
|
-
### `DEFAULT_AVATAR_CONFIG: AvatarConfig`
|
|
279
|
-
|
|
280
|
-
```typescript
|
|
281
|
-
{
|
|
282
|
-
formats: [{ extension: 'webp', contentType: 'image/webp' }],
|
|
283
|
-
cacheControl: 'public, max-age=86400',
|
|
284
|
-
}
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
## API Reference — Paths
|
|
288
|
-
|
|
289
|
-
`paths.ts` exports lazy getters that resolve `projectConfig.paths.*` against `ROOT_DIR`. Read at call time so a late `registerProjectConfig({ paths: {...} })` still wins.
|
|
290
|
-
|
|
291
|
-
| Function | Returns | Reads from |
|
|
292
|
-
|---|---|---|
|
|
293
|
-
| `getSrcDir()` | absolute path | `paths.srcDir` |
|
|
294
|
-
| `getServerDir()` | absolute path | `paths.serverDir` |
|
|
295
|
-
| `getSharedDir()` | absolute path | `paths.sharedDir` |
|
|
296
|
-
| `getUploadsDir()` | absolute path | `paths.uploadsDir` |
|
|
297
|
-
| `getPublicDir()` | absolute path | `paths.publicDir` |
|
|
298
|
-
| `getServerFunctionsDir()` | absolute path | `paths.serverFunctionsDir` |
|
|
299
|
-
| `getGeneratedSocketTypesPath()` | absolute path | `paths.generatedSocketTypes` |
|
|
300
|
-
| `getGeneratedApiSchemasPath()` | absolute path | `paths.generatedApiSchemas` |
|
|
301
|
-
| `getGeneratedApiDocsPath()` | absolute path | `paths.generatedApiDocs` |
|
|
302
|
-
| `resolveFromRoot(...segments: string[])` | absolute path | `ROOT_DIR` + joined segments |
|
|
303
|
-
|
|
304
|
-
`ROOT_DIR` is auto-detected by walking up from `process.cwd()` (then `__dirname`) and stopping at the first directory containing `package.json` plus either `tsconfig.json` or `tsconfig.server.json`. Falls back to `process.cwd()` when no marker is found.
|
|
305
|
-
|
|
306
|
-
Deprecated module-level constants exist for backwards compatibility (`SRC_DIR`, `SERVER_DIR`, `SHARED_DIR`, `UPLOADS_DIR`, `PUBLIC_DIR`, `SERVER_FUNCTIONS_DIR`, `GENERATED_SOCKET_TYPES_PATH`, `GENERATED_API_SCHEMAS_PATH`, `GENERATED_API_DOCS_PATH`). These resolve at module load and ignore later `registerProjectConfig` overrides — prefer the getters for new code.
|
|
307
|
-
|
|
308
|
-
## Config keys (top-level `ProjectConfig`)
|
|
309
|
-
|
|
310
|
-
| Key | Type | Default | Description |
|
|
311
|
-
|---|---|---|---|
|
|
312
|
-
| `app.publicUrl` | `string` | `''` | Absolute origin of the deployment; consumed by OAuth callbacks and transactional email links. |
|
|
313
|
-
| `logging.*` | flags | all `false` | Dev-only logging toggles. |
|
|
314
|
-
| `rateLimiting.enabled` | `boolean` | `true` | Kill-switch — when `false`, every check short-circuits to allowed. |
|
|
315
|
-
| `rateLimiting.store` | `'memory' \| 'redis'` | `'memory'` | Backend for the default strategy. |
|
|
316
|
-
| `rateLimiting.windowMs` | `number` | `60000` | Default sliding window. |
|
|
317
|
-
| `session.basedToken` | `boolean` | `false` | When `true`, tokens are stored in sessionStorage + handshake auth instead of cookies. |
|
|
318
|
-
| `session.expiryDays` | `number` | `7` | Session TTL. |
|
|
319
|
-
| `session.perUser` | `'single' \| 'multiple'` | `'single'` | Whether a single user may be logged in on multiple devices/browsers concurrently. |
|
|
320
|
-
| `session.maxConcurrentPerUser` | `number \| null` | `null` | Cap when `perUser === 'multiple'`. |
|
|
321
|
-
| `session.onConflict` | `'revokeOld' \| 'rejectNew'` | `'revokeOld'` | Policy when the cap is hit. |
|
|
322
|
-
| `session.projectName` | `string` | `''` | Explicit Redis prefix; empty means fall back to `PROJECT_NAME` env. |
|
|
323
|
-
| `http.sessionCookieName` | `string` | `'token'` | Cookie name read by `extractTokenFromSocket` / `extractTokenFromRequest`. |
|
|
324
|
-
| `http.requestBodyMaxBytes` | `number` | `1048576` | Hard cap enforced by `getParams`. |
|
|
325
|
-
| `http.healthEndpoint` | `string` | `'/_health'` | Router boot-handshake endpoint. |
|
|
326
|
-
| `http.cors.allowedOrigins` | `string[] \| (origin: string) => boolean` | `[]` | Static allowlist or sync resolver. |
|
|
327
|
-
| `http.cors.allowLocalhost` | `boolean` | `false` | Convenience flag for dev. |
|
|
328
|
-
| `auth.bcryptRounds` | `number` | `10` | Hash cost factor. |
|
|
329
|
-
| `auth.passwordPolicy.*` | rules | various | Credentials policy. |
|
|
330
|
-
| `auth.forgotPassword` | `'framework' \| 'custom' \| 'disabled'` | `'disabled'` | Reset-flow ownership. |
|
|
331
|
-
| `socket.maxHttpBufferSize` | `number` | `5MB` | Per-message ceiling. |
|
|
332
|
-
| `sync.fanoutYieldEvery` | `number` | `100` | Recipients between event-loop yields. |
|
|
333
|
-
| `offlineQueue.dropPolicy` | `'drop-oldest' \| 'drop-newest' \| 'reject'` | `'drop-oldest'` | Default eviction when full. |
|
|
334
|
-
| `dev.hotReloadDebounceMs` | `number` | `120` | File-watcher debounce. |
|
|
335
|
-
| `paths.*` | paths | see defaults table | Project filesystem layout. |
|
|
336
|
-
| `defaultLanguage` | `string` | `'en'` | Initial language for new sessions. |
|
|
337
|
-
| `socketActivityBroadcaster` | `boolean` | `false` | Enable presence broadcasts. |
|
|
338
|
-
| `socketStatusIndicator` | `boolean` | `false` | Show the floating socket-status badge. |
|
|
339
|
-
| `loginRedirectUrl` | `string` | `'/'` | Post-login destination. |
|
|
340
|
-
|
|
341
|
-
See `packages/core/src/projectConfig.ts` for the full type definitions of `LoggingConfig`, `RateLimitingConfig`, `SessionConfig`, `HttpConfig`, `CorsConfig`, `SecurityHeadersConfig`, `AuthConfig`, `PasswordPolicyConfig`, `SocketConfig`, `SyncConfig`, `SyncStreamThrottleConfig`, `OfflineQueueConfig`, `DevConfig`, and `PathsConfig`.
|
|
342
|
-
|
|
343
|
-
## Related
|
|
344
|
-
|
|
345
|
-
- Function INDEX: `packages/core/CLAUDE.md`
|
|
346
|
-
- Architecture: `docs/ARCHITECTURE_PACKAGING.md`, `docs/ARCHITECTURE_ROUTING.md`
|
|
347
|
-
- README: `packages/core/README.md`
|
|
348
|
-
- Source: `packages/core/src/projectConfig.ts`, `deployConfigRegistry.ts`, `servicesConfigRegistry.ts`, `avatarConfig.ts`, `paths.ts`
|
|
1
|
+
# Config Registry
|
|
2
|
+
|
|
3
|
+
> Deep specs for project / deploy / services / avatar / paths config slots. Source: `packages/core/src/`. Bijgewerkt: 2026-05-20.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The config registries are the single source of truth for runtime project configuration. They follow a uniform pattern:
|
|
8
|
+
|
|
9
|
+
1. A consumer's boot entry calls `registerXConfig({...})` once before any framework code reads a value.
|
|
10
|
+
2. Framework code reads at call time via `getXConfig()` (never at module load) so the registration timing is flexible.
|
|
11
|
+
3. Boolean `isXConfigRegistered()` helpers exist for code paths that must fail closed when the consumer forgot to register.
|
|
12
|
+
|
|
13
|
+
Four independent slots live in this topic:
|
|
14
|
+
|
|
15
|
+
- **Project config** — runtime tunables (rate limits, session policy, HTTP, auth, sockets, paths). Deep-merged over `DEFAULT_PROJECT_CONFIG` so consumers only supply the keys they want to override.
|
|
16
|
+
- **Deploy config** — multi-env topology consumed by `@luckystack/router` (resources, environments, routing, development).
|
|
17
|
+
- **Services config** — service + preset definitions consumed by the router / preset loader.
|
|
18
|
+
- **Avatar config** — disk formats + cache header for `serveAvatar`.
|
|
19
|
+
|
|
20
|
+
Path getters (`getSrcDir`, `getGeneratedApiDocsPath`, …) resolve project-relative paths against `ROOT_DIR` (auto-detected workspace root) and read `getProjectConfig().paths.*` at call time.
|
|
21
|
+
|
|
22
|
+
## API Reference — Project Config
|
|
23
|
+
|
|
24
|
+
### `registerProjectConfig(config: ProjectConfigInput): void`
|
|
25
|
+
|
|
26
|
+
**Signature:**
|
|
27
|
+
```typescript
|
|
28
|
+
export const registerProjectConfig = (config: ProjectConfigInput): void
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Parameters:**
|
|
32
|
+
| Name | Type | Required | Description |
|
|
33
|
+
|---|---|---|---|
|
|
34
|
+
| `config` | `ProjectConfigInput` (deep-partial of `ProjectConfig`) | yes | Only the keys to override. Missing keys fall back to `DEFAULT_PROJECT_CONFIG`. |
|
|
35
|
+
|
|
36
|
+
**Returns:** `void`
|
|
37
|
+
|
|
38
|
+
**Behavior:**
|
|
39
|
+
- Deep-merges `config` over `DEFAULT_PROJECT_CONFIG` (plain-object branches recurse; primitives + arrays are replaced wholesale).
|
|
40
|
+
- Sets the registration flag so `isProjectConfigRegistered()` returns true.
|
|
41
|
+
- Replaces (last-write-wins) on repeated calls — the latest call is the active config.
|
|
42
|
+
|
|
43
|
+
**Errors / Edge cases:**
|
|
44
|
+
- `undefined` keys in the override are skipped (treated as "no override").
|
|
45
|
+
- Arrays are NOT merged element-wise — passing `cors.allowedOrigins: ['x']` replaces the default `[]` array.
|
|
46
|
+
- Calling after framework hot paths already ran is allowed but the values only take effect on subsequent reads (everything reads call-time).
|
|
47
|
+
|
|
48
|
+
**Example:**
|
|
49
|
+
```typescript
|
|
50
|
+
import { registerProjectConfig } from '@luckystack/core';
|
|
51
|
+
|
|
52
|
+
registerProjectConfig({
|
|
53
|
+
defaultLanguage: 'en',
|
|
54
|
+
session: { expiryDays: 30 },
|
|
55
|
+
http: {
|
|
56
|
+
cors: {
|
|
57
|
+
allowedOrigins: ['https://app.example.com'],
|
|
58
|
+
allowLocalhost: false,
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### `getProjectConfig(): ProjectConfig`
|
|
65
|
+
|
|
66
|
+
**Signature:**
|
|
67
|
+
```typescript
|
|
68
|
+
export const getProjectConfig = (): ProjectConfig
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Returns:** The active fully-merged `ProjectConfig`.
|
|
72
|
+
|
|
73
|
+
**Behavior:**
|
|
74
|
+
- Returns the currently merged config object (reference-stable until the next `registerProjectConfig`).
|
|
75
|
+
- Returns `DEFAULT_PROJECT_CONFIG` when nothing was registered yet.
|
|
76
|
+
|
|
77
|
+
**Example:**
|
|
78
|
+
```typescript
|
|
79
|
+
import { getProjectConfig } from '@luckystack/core';
|
|
80
|
+
|
|
81
|
+
const { http, rateLimiting } = getProjectConfig();
|
|
82
|
+
const cookieName = http.sessionCookieName;
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### `getProjectName(): string`
|
|
86
|
+
|
|
87
|
+
**Signature:**
|
|
88
|
+
```typescript
|
|
89
|
+
export const getProjectName = (): string
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**Returns:** The active project namespace (used as Redis key prefix for session, password reset, rate limits, OAuth state, etc.).
|
|
93
|
+
|
|
94
|
+
**Behavior:** Resolves in this order (first non-empty wins):
|
|
95
|
+
1. `projectConfig.session.projectName`
|
|
96
|
+
2. `process.env.PROJECT_NAME`
|
|
97
|
+
3. literal `'luckystack'`
|
|
98
|
+
|
|
99
|
+
**Example:**
|
|
100
|
+
```typescript
|
|
101
|
+
import { getProjectName } from '@luckystack/core';
|
|
102
|
+
|
|
103
|
+
const key = `${getProjectName()}-session:${token}`;
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `isProjectConfigRegistered(): boolean`
|
|
107
|
+
|
|
108
|
+
**Signature:**
|
|
109
|
+
```typescript
|
|
110
|
+
export const isProjectConfigRegistered = (): boolean
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Returns:** `true` only after `registerProjectConfig` has been called at least once.
|
|
114
|
+
|
|
115
|
+
**Behavior:**
|
|
116
|
+
- Pure boot-time guard — does not throw.
|
|
117
|
+
- Framework packages use this for "did the installer forget to register?" warnings without crashing tests/CLI contexts.
|
|
118
|
+
|
|
119
|
+
### `DEFAULT_PROJECT_CONFIG: ProjectConfig`
|
|
120
|
+
|
|
121
|
+
Exported so consumers can spread + override structurally:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import { DEFAULT_PROJECT_CONFIG, registerProjectConfig } from '@luckystack/core';
|
|
125
|
+
|
|
126
|
+
registerProjectConfig({
|
|
127
|
+
...DEFAULT_PROJECT_CONFIG,
|
|
128
|
+
defaultLanguage: 'nl',
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Notable defaults (verbatim from `projectConfig.ts`):
|
|
133
|
+
|
|
134
|
+
| Key | Default |
|
|
135
|
+
|---|---|
|
|
136
|
+
| `rateLimiting.enabled` | `true` |
|
|
137
|
+
| `rateLimiting.store` | `'memory'` |
|
|
138
|
+
| `rateLimiting.defaultApiLimit` | `60` |
|
|
139
|
+
| `rateLimiting.defaultIpLimit` | `100` |
|
|
140
|
+
| `rateLimiting.windowMs` | `60_000` |
|
|
141
|
+
| `session.expiryDays` | `7` |
|
|
142
|
+
| `session.perUser` | `'single'` |
|
|
143
|
+
| `session.onConflict` | `'revokeOld'` |
|
|
144
|
+
| `session.notifyOldDeviceOnRevoke` | `true` |
|
|
145
|
+
| `http.sessionCookieName` | `'token'` |
|
|
146
|
+
| `http.sessionCookieSameSite` | `'Strict'` |
|
|
147
|
+
| `http.requestBodyMaxBytes` | `1048576` |
|
|
148
|
+
| `http.healthEndpoint` | `'/_health'` |
|
|
149
|
+
| `http.liveEndpoint` | `'/livez'` |
|
|
150
|
+
| `http.readyEndpoint` | `'/readyz'` |
|
|
151
|
+
| `http.cors.credentials` | `true` |
|
|
152
|
+
| `http.cors.allowedOrigins` | `[]` |
|
|
153
|
+
| `http.cors.allowLocalhost` | `false` |
|
|
154
|
+
| `auth.bcryptRounds` | `10` |
|
|
155
|
+
| `auth.providerAccountStrategy` | `'per-provider'` |
|
|
156
|
+
| `auth.forgotPassword` | `'disabled'` |
|
|
157
|
+
| `auth.passwordResetTtlSeconds` | `3600` |
|
|
158
|
+
| `socket.maxHttpBufferSize` | `5 * 1024 * 1024` |
|
|
159
|
+
| `socket.pingTimeout` | `20_000` |
|
|
160
|
+
| `socket.pingInterval` | `25_000` |
|
|
161
|
+
| `sync.fanoutYieldEvery` | `100` |
|
|
162
|
+
| `sync.fanoutYieldMs` | `1` |
|
|
163
|
+
| `offlineQueue.maxSize` | `200` |
|
|
164
|
+
| `offlineQueue.maxAgeMs` | `3_600_000` |
|
|
165
|
+
| `offlineQueue.dropPolicy` | `'drop-oldest'` |
|
|
166
|
+
| `dev.hotReloadDebounceMs` | `120` |
|
|
167
|
+
| `defaultLanguage` | `'en'` |
|
|
168
|
+
| `defaultTheme` | `'light'` |
|
|
169
|
+
|
|
170
|
+
## API Reference — Deploy Config
|
|
171
|
+
|
|
172
|
+
### `registerDeployConfig(config: DeployConfigShape): DeployConfigShape`
|
|
173
|
+
|
|
174
|
+
**Signature:**
|
|
175
|
+
```typescript
|
|
176
|
+
export const registerDeployConfig = (config: DeployConfigShape): DeployConfigShape
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Parameters:**
|
|
180
|
+
| Name | Type | Required | Description |
|
|
181
|
+
|---|---|---|---|
|
|
182
|
+
| `config` | `DeployConfigShape` | yes | Deploy topology object — `{ resources, environments?, routing?, development? }`. |
|
|
183
|
+
|
|
184
|
+
**Returns:** The stored config (same reference passed in).
|
|
185
|
+
|
|
186
|
+
**Behavior:**
|
|
187
|
+
- Stores the provided config wholesale (no deep-merge, unlike `registerProjectConfig`).
|
|
188
|
+
- Sets `isDeployConfigRegistered()` to `true`.
|
|
189
|
+
|
|
190
|
+
**Example:**
|
|
191
|
+
```typescript
|
|
192
|
+
import { registerDeployConfig } from '@luckystack/core';
|
|
193
|
+
|
|
194
|
+
registerDeployConfig({
|
|
195
|
+
resources: {
|
|
196
|
+
redis: { type: 'redis', urlEnvKey: 'REDIS_URL', synchronizedEnvKeys: ['SESSION_SECRET'] },
|
|
197
|
+
mongo: { type: 'mongo', urlEnvKey: 'MONGO_URL' },
|
|
198
|
+
},
|
|
199
|
+
environments: {
|
|
200
|
+
production: { redis: 'redis', mongo: 'mongo', bindings: {} },
|
|
201
|
+
},
|
|
202
|
+
routing: { strictBootHandshake: true },
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### `getDeployConfig(): DeployConfigShape`
|
|
207
|
+
|
|
208
|
+
Returns the registered deploy config, or `{ resources: {} }` when nothing was registered. Never throws.
|
|
209
|
+
|
|
210
|
+
### `isDeployConfigRegistered(): boolean`
|
|
211
|
+
|
|
212
|
+
Non-throwing guard. Used by router / boot probes that need a real registration before behaving differently from the empty default.
|
|
213
|
+
|
|
214
|
+
## API Reference — Services Config
|
|
215
|
+
|
|
216
|
+
### `registerServicesConfig(config: ServicesConfigShape): ServicesConfigShape`
|
|
217
|
+
|
|
218
|
+
**Signature:**
|
|
219
|
+
```typescript
|
|
220
|
+
export const registerServicesConfig = (config: ServicesConfigShape): ServicesConfigShape
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Parameters:**
|
|
224
|
+
| Name | Type | Required | Description |
|
|
225
|
+
|---|---|---|---|
|
|
226
|
+
| `config` | `ServicesConfigShape` | yes | `{ services, presets }` — services define folder sources, presets bundle multiple services into one artifact. |
|
|
227
|
+
|
|
228
|
+
**Returns:** The stored config.
|
|
229
|
+
|
|
230
|
+
**Behavior:**
|
|
231
|
+
- Stores the config (last-write-wins).
|
|
232
|
+
- Side-effect pattern: the consumer's `services.config.ts` is expected to call this on module import.
|
|
233
|
+
|
|
234
|
+
### `getServicesConfig(): ServicesConfigShape`
|
|
235
|
+
|
|
236
|
+
**Throws** `Error` when no services config has been registered. Message includes guidance to import `services.config.ts` as a side-effect.
|
|
237
|
+
|
|
238
|
+
### `isServicesConfigRegistered(): boolean`
|
|
239
|
+
|
|
240
|
+
Non-throwing guard for code that wants to fall back instead of crashing.
|
|
241
|
+
|
|
242
|
+
## API Reference — Avatar Config
|
|
243
|
+
|
|
244
|
+
### `registerAvatarConfig(config: AvatarConfigInput): void`
|
|
245
|
+
|
|
246
|
+
**Signature:**
|
|
247
|
+
```typescript
|
|
248
|
+
export const registerAvatarConfig = (config: AvatarConfigInput): void
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Parameters:**
|
|
252
|
+
| Name | Type | Required | Description |
|
|
253
|
+
|---|---|---|---|
|
|
254
|
+
| `config.formats` | `{ extension: string; contentType: string }[]` | no | Disk formats checked in order; first existing file wins. |
|
|
255
|
+
| `config.cacheControl` | `string` | no | `Cache-Control` header. Default `'public, max-age=86400'`. |
|
|
256
|
+
|
|
257
|
+
**Behavior:**
|
|
258
|
+
- Empty `formats` array is treated as "use default" (keeps the built-in `[{ extension: 'webp', contentType: 'image/webp' }]`).
|
|
259
|
+
- Missing `cacheControl` keeps the default.
|
|
260
|
+
|
|
261
|
+
**Example:**
|
|
262
|
+
```typescript
|
|
263
|
+
import { registerAvatarConfig } from '@luckystack/core';
|
|
264
|
+
|
|
265
|
+
registerAvatarConfig({
|
|
266
|
+
formats: [
|
|
267
|
+
{ extension: 'webp', contentType: 'image/webp' },
|
|
268
|
+
{ extension: 'png', contentType: 'image/png' },
|
|
269
|
+
],
|
|
270
|
+
cacheControl: 'public, max-age=604800',
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### `getAvatarConfig(): AvatarConfig`
|
|
275
|
+
|
|
276
|
+
Returns the active avatar config (defaults applied).
|
|
277
|
+
|
|
278
|
+
### `DEFAULT_AVATAR_CONFIG: AvatarConfig`
|
|
279
|
+
|
|
280
|
+
```typescript
|
|
281
|
+
{
|
|
282
|
+
formats: [{ extension: 'webp', contentType: 'image/webp' }],
|
|
283
|
+
cacheControl: 'public, max-age=86400',
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## API Reference — Paths
|
|
288
|
+
|
|
289
|
+
`paths.ts` exports lazy getters that resolve `projectConfig.paths.*` against `ROOT_DIR`. Read at call time so a late `registerProjectConfig({ paths: {...} })` still wins.
|
|
290
|
+
|
|
291
|
+
| Function | Returns | Reads from |
|
|
292
|
+
|---|---|---|
|
|
293
|
+
| `getSrcDir()` | absolute path | `paths.srcDir` |
|
|
294
|
+
| `getServerDir()` | absolute path | `paths.serverDir` |
|
|
295
|
+
| `getSharedDir()` | absolute path | `paths.sharedDir` |
|
|
296
|
+
| `getUploadsDir()` | absolute path | `paths.uploadsDir` |
|
|
297
|
+
| `getPublicDir()` | absolute path | `paths.publicDir` |
|
|
298
|
+
| `getServerFunctionsDir()` | absolute path | `paths.serverFunctionsDir` |
|
|
299
|
+
| `getGeneratedSocketTypesPath()` | absolute path | `paths.generatedSocketTypes` |
|
|
300
|
+
| `getGeneratedApiSchemasPath()` | absolute path | `paths.generatedApiSchemas` |
|
|
301
|
+
| `getGeneratedApiDocsPath()` | absolute path | `paths.generatedApiDocs` |
|
|
302
|
+
| `resolveFromRoot(...segments: string[])` | absolute path | `ROOT_DIR` + joined segments |
|
|
303
|
+
|
|
304
|
+
`ROOT_DIR` is auto-detected by walking up from `process.cwd()` (then `__dirname`) and stopping at the first directory containing `package.json` plus either `tsconfig.json` or `tsconfig.server.json`. Falls back to `process.cwd()` when no marker is found.
|
|
305
|
+
|
|
306
|
+
Deprecated module-level constants exist for backwards compatibility (`SRC_DIR`, `SERVER_DIR`, `SHARED_DIR`, `UPLOADS_DIR`, `PUBLIC_DIR`, `SERVER_FUNCTIONS_DIR`, `GENERATED_SOCKET_TYPES_PATH`, `GENERATED_API_SCHEMAS_PATH`, `GENERATED_API_DOCS_PATH`). These resolve at module load and ignore later `registerProjectConfig` overrides — prefer the getters for new code.
|
|
307
|
+
|
|
308
|
+
## Config keys (top-level `ProjectConfig`)
|
|
309
|
+
|
|
310
|
+
| Key | Type | Default | Description |
|
|
311
|
+
|---|---|---|---|
|
|
312
|
+
| `app.publicUrl` | `string` | `''` | Absolute origin of the deployment; consumed by OAuth callbacks and transactional email links. |
|
|
313
|
+
| `logging.*` | flags | all `false` | Dev-only logging toggles. |
|
|
314
|
+
| `rateLimiting.enabled` | `boolean` | `true` | Kill-switch — when `false`, every check short-circuits to allowed. |
|
|
315
|
+
| `rateLimiting.store` | `'memory' \| 'redis'` | `'memory'` | Backend for the default strategy. |
|
|
316
|
+
| `rateLimiting.windowMs` | `number` | `60000` | Default sliding window. |
|
|
317
|
+
| `session.basedToken` | `boolean` | `false` | When `true`, tokens are stored in sessionStorage + handshake auth instead of cookies. |
|
|
318
|
+
| `session.expiryDays` | `number` | `7` | Session TTL. |
|
|
319
|
+
| `session.perUser` | `'single' \| 'multiple'` | `'single'` | Whether a single user may be logged in on multiple devices/browsers concurrently. |
|
|
320
|
+
| `session.maxConcurrentPerUser` | `number \| null` | `null` | Cap when `perUser === 'multiple'`. |
|
|
321
|
+
| `session.onConflict` | `'revokeOld' \| 'rejectNew'` | `'revokeOld'` | Policy when the cap is hit. |
|
|
322
|
+
| `session.projectName` | `string` | `''` | Explicit Redis prefix; empty means fall back to `PROJECT_NAME` env. |
|
|
323
|
+
| `http.sessionCookieName` | `string` | `'token'` | Cookie name read by `extractTokenFromSocket` / `extractTokenFromRequest`. |
|
|
324
|
+
| `http.requestBodyMaxBytes` | `number` | `1048576` | Hard cap enforced by `getParams`. |
|
|
325
|
+
| `http.healthEndpoint` | `string` | `'/_health'` | Router boot-handshake endpoint. |
|
|
326
|
+
| `http.cors.allowedOrigins` | `string[] \| (origin: string) => boolean` | `[]` | Static allowlist or sync resolver. |
|
|
327
|
+
| `http.cors.allowLocalhost` | `boolean` | `false` | Convenience flag for dev. |
|
|
328
|
+
| `auth.bcryptRounds` | `number` | `10` | Hash cost factor. |
|
|
329
|
+
| `auth.passwordPolicy.*` | rules | various | Credentials policy. |
|
|
330
|
+
| `auth.forgotPassword` | `'framework' \| 'custom' \| 'disabled'` | `'disabled'` | Reset-flow ownership. |
|
|
331
|
+
| `socket.maxHttpBufferSize` | `number` | `5MB` | Per-message ceiling. |
|
|
332
|
+
| `sync.fanoutYieldEvery` | `number` | `100` | Recipients between event-loop yields. |
|
|
333
|
+
| `offlineQueue.dropPolicy` | `'drop-oldest' \| 'drop-newest' \| 'reject'` | `'drop-oldest'` | Default eviction when full. |
|
|
334
|
+
| `dev.hotReloadDebounceMs` | `number` | `120` | File-watcher debounce. |
|
|
335
|
+
| `paths.*` | paths | see defaults table | Project filesystem layout. |
|
|
336
|
+
| `defaultLanguage` | `string` | `'en'` | Initial language for new sessions. |
|
|
337
|
+
| `socketActivityBroadcaster` | `boolean` | `false` | Enable presence broadcasts. |
|
|
338
|
+
| `socketStatusIndicator` | `boolean` | `false` | Show the floating socket-status badge. |
|
|
339
|
+
| `loginRedirectUrl` | `string` | `'/'` | Post-login destination. |
|
|
340
|
+
|
|
341
|
+
See `packages/core/src/projectConfig.ts` for the full type definitions of `LoggingConfig`, `RateLimitingConfig`, `SessionConfig`, `HttpConfig`, `CorsConfig`, `SecurityHeadersConfig`, `AuthConfig`, `PasswordPolicyConfig`, `SocketConfig`, `SyncConfig`, `SyncStreamThrottleConfig`, `OfflineQueueConfig`, `DevConfig`, and `PathsConfig`.
|
|
342
|
+
|
|
343
|
+
## Related
|
|
344
|
+
|
|
345
|
+
- Function INDEX: `packages/core/CLAUDE.md`
|
|
346
|
+
- Architecture: `docs/ARCHITECTURE_PACKAGING.md`, `docs/ARCHITECTURE_ROUTING.md`
|
|
347
|
+
- README: `packages/core/README.md`
|
|
348
|
+
- Source: `packages/core/src/projectConfig.ts`, `deployConfigRegistry.ts`, `servicesConfigRegistry.ts`, `avatarConfig.ts`, `paths.ts`
|