@venizia/ignis-docs 0.2.1-0 → 0.2.1-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/content/best-practices/architectural-patterns.md +3 -3
- package/content/best-practices/code-style-standards/naming-conventions.md +1 -1
- package/content/best-practices/contribution-workflow.md +2 -2
- package/content/best-practices/error-handling.md +94 -89
- package/content/best-practices/security-guidelines.md +5 -5
- package/content/extensions/components/api-reference.md +22 -21
- package/content/extensions/components/authentication/api.md +64 -28
- package/content/extensions/components/authentication/errors.md +19 -5
- package/content/extensions/components/authentication/index.md +19 -18
- package/content/extensions/components/authentication/usage.md +55 -30
- package/content/extensions/components/authorization/api.md +351 -84
- package/content/extensions/components/authorization/errors.md +51 -17
- package/content/extensions/components/authorization/getting-started.md +227 -0
- package/content/extensions/components/authorization/index.md +24 -16
- package/content/extensions/components/authorization/usage.md +45 -21
- package/content/extensions/components/health-check.md +15 -9
- package/content/extensions/components/index.md +24 -90
- package/content/extensions/components/mail/api.md +105 -54
- package/content/extensions/components/mail/errors.md +12 -10
- package/content/extensions/components/mail/index.md +32 -13
- package/content/extensions/components/mail/usage.md +26 -18
- package/content/extensions/components/request-tracker.md +18 -14
- package/content/extensions/components/socket-io/api.md +377 -882
- package/content/extensions/components/socket-io/errors.md +49 -51
- package/content/extensions/components/socket-io/index.md +72 -88
- package/content/extensions/components/socket-io/usage.md +107 -117
- package/content/extensions/components/static-asset/api.md +83 -31
- package/content/extensions/components/static-asset/errors.md +18 -7
- package/content/extensions/components/static-asset/index.md +18 -11
- package/content/extensions/components/static-asset/usage.md +11 -6
- package/content/extensions/components/template/index.md +3 -3
- package/content/extensions/components/websocket/api.md +58 -27
- package/content/extensions/components/websocket/errors.md +3 -3
- package/content/extensions/components/websocket/index.md +8 -7
- package/content/extensions/components/websocket/usage.md +21 -8
- package/content/extensions/helpers/cron/index.md +8 -7
- package/content/extensions/helpers/crypto/index.md +16 -8
- package/content/extensions/helpers/crypto/reference.md +96 -24
- package/content/extensions/helpers/env/index.md +14 -10
- package/content/extensions/helpers/error/index.md +99 -23
- package/content/extensions/helpers/index.md +61 -47
- package/content/extensions/helpers/inversion/index.md +23 -6
- package/content/extensions/helpers/inversion/reference.md +30 -22
- package/content/extensions/helpers/kafka/admin.md +3 -2
- package/content/extensions/helpers/kafka/compile-binary.md +69 -44
- package/content/extensions/helpers/kafka/consumer.md +24 -21
- package/content/extensions/helpers/kafka/examples.md +22 -234
- package/content/extensions/helpers/kafka/index.md +30 -62
- package/content/extensions/helpers/kafka/producer.md +31 -26
- package/content/extensions/helpers/kafka/schema-registry.md +19 -14
- package/content/extensions/helpers/logger/hf-logger.md +49 -22
- package/content/extensions/helpers/logger/index.md +37 -12
- package/content/extensions/helpers/logger/pino.md +31 -11
- package/content/extensions/helpers/logger/reference.md +243 -52
- package/content/extensions/helpers/network/api.md +65 -30
- package/content/extensions/helpers/network/index.md +32 -11
- package/content/extensions/helpers/queue/index.md +17 -6
- package/content/extensions/helpers/queue/reference.md +52 -25
- package/content/extensions/helpers/redis/index.md +29 -11
- package/content/extensions/helpers/redis/reference.md +85 -28
- package/content/extensions/helpers/secrets/index.md +82 -12
- package/content/extensions/helpers/socket-io/api.md +40 -21
- package/content/extensions/helpers/socket-io/index.md +26 -10
- package/content/extensions/helpers/storage/api.md +42 -24
- package/content/extensions/helpers/storage/index.md +10 -7
- package/content/extensions/helpers/types/index.md +20 -7
- package/content/extensions/helpers/types/reference.md +53 -14
- package/content/extensions/helpers/uid/index.md +166 -10
- package/content/extensions/helpers/websocket/api.md +52 -13
- package/content/extensions/helpers/websocket/index.md +7 -4
- package/content/extensions/helpers/worker-thread/index.md +9 -5
- package/content/extensions/helpers/worker-thread/reference.md +20 -20
- package/content/extensions/index.md +38 -39
- package/content/extensions/src-details/mcp-server.md +96 -548
- package/content/guides/core-concepts/persistent/datasources.md +2 -2
- package/content/guides/core-concepts/persistent/index.md +5 -1
- package/content/guides/core-concepts/persistent/pglite.md +218 -0
- package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
- package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
- package/content/guides/core-concepts/persistent/search-typesense.md +26 -14
- package/content/guides/core-concepts/persistent/sqlite.md +296 -0
- package/content/guides/core-concepts/persistent/transactions.md +12 -11
- package/content/guides/get-started/5-minute-quickstart.md +59 -206
- package/content/guides/get-started/philosophy.md +135 -670
- package/content/guides/get-started/setup.md +53 -74
- package/content/guides/migrations/redis-helpers-migration.md +4 -3
- package/content/guides/migrations/scoped-rbac-migration.md +5 -5
- package/content/guides/migrations/unified-connectors-migration.md +7 -6
- package/content/guides/tutorials/realtime-chat.md +1 -1
- package/content/references/base/application.md +63 -21
- package/content/references/base/components.md +3 -3
- package/content/references/base/connectors.md +14 -14
- package/content/references/base/controllers.md +12 -12
- package/content/references/base/datasources-reference.md +32 -31
- package/content/references/base/datasources.md +6 -6
- package/content/references/base/dependency-injection.md +12 -11
- package/content/references/base/filter-system/application-usage.md +54 -30
- package/content/references/base/filter-system/array-operators.md +24 -46
- package/content/references/base/filter-system/comparison-operators.md +47 -67
- package/content/references/base/filter-system/default-filter.md +59 -53
- package/content/references/base/filter-system/fields-order-pagination.md +92 -146
- package/content/references/base/filter-system/index.md +25 -13
- package/content/references/base/filter-system/json-filtering.md +45 -184
- package/content/references/base/filter-system/list-operators.md +23 -53
- package/content/references/base/filter-system/logical-operators.md +63 -121
- package/content/references/base/filter-system/null-operators.md +34 -104
- package/content/references/base/filter-system/pattern-matching.md +40 -55
- package/content/references/base/filter-system/quick-reference.md +86 -198
- package/content/references/base/filter-system/range-operators.md +18 -46
- package/content/references/base/filter-system/tips.md +6 -6
- package/content/references/base/filter-system/use-cases.md +33 -15
- package/content/references/base/grpc-controllers.md +53 -17
- package/content/references/base/index.md +5 -3
- package/content/references/base/middlewares.md +11 -10
- package/content/references/base/models-reference.md +17 -17
- package/content/references/base/models.md +4 -3
- package/content/references/base/providers.md +8 -8
- package/content/references/base/repositories/advanced.md +224 -326
- package/content/references/base/repositories/index.md +19 -6
- package/content/references/base/repositories/mixins.md +5 -5
- package/content/references/base/repositories/relations.md +160 -293
- package/content/references/base/repositories/soft-deletable.md +16 -6
- package/content/references/base/secrets.md +17 -13
- package/content/references/base/services.md +6 -4
- package/content/references/configuration/environment-variables.md +31 -23
- package/content/references/configuration/index.md +6 -4
- package/content/references/index.md +1 -1
- package/content/references/utilities/duration.md +85 -0
- package/content/references/utilities/index.md +5 -1
- package/content/references/utilities/jsx-reference.md +11 -11
- package/content/references/utilities/jsx.md +2 -2
- package/content/references/utilities/module.md +78 -25
- package/content/references/utilities/request.md +2 -1
- package/content/references/utilities/retry.md +139 -0
- package/content/references/utilities/schema.md +2 -2
- package/content/references/utilities/statuses-reference.md +4 -4
- package/content/references/utilities/statuses.md +5 -5
- package/dist/mcp-server/index.js +0 -0
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
- package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
- package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
- package/package.json +17 -16
|
@@ -6,7 +6,9 @@ difficulty: intermediate
|
|
|
6
6
|
|
|
7
7
|
# Socket.IO
|
|
8
8
|
|
|
9
|
-
`SocketIOServerHelper` and `SocketIOClientHelper` wrap `socket.io` with a mandatory post-connection authentication handshake
|
|
9
|
+
`SocketIOServerHelper` and `SocketIOClientHelper` wrap `socket.io` with a mandatory post-connection authentication handshake and room management. A Redis adapter makes sure events reach a client no matter which server instance it's connected to.
|
|
10
|
+
|
|
11
|
+
These are the raw classes: construct and `configure()` them yourself. Need the server helper inside an IGNIS app instead? [`SocketIOComponent`](/extensions/components/socket-io/) wires it into DI for you, on both Node.js and Bun.
|
|
10
12
|
|
|
11
13
|
## In one example
|
|
12
14
|
|
|
@@ -58,14 +60,28 @@ const client = new SocketIOClientHelper({
|
|
|
58
60
|
|
|
59
61
|
## How it works
|
|
60
62
|
|
|
61
|
-
- **Two independent helpers
|
|
63
|
+
- **Two independent helpers, both extending `BaseHelper`:**
|
|
64
|
+
|
|
65
|
+
| Helper | Wraps |
|
|
66
|
+
|--------|-------|
|
|
67
|
+
| `SocketIOServerHelper` | a `socket.io` `Server` |
|
|
68
|
+
| `SocketIOClientHelper` | a `socket.io-client` `Socket` |
|
|
69
|
+
|
|
70
|
+
- **The client isn't locked to this server.** It can talk to any `socket.io` server, not only this one.
|
|
62
71
|
- **Runtime-agnostic server.** Pass a Node.js `http.Server` for `runtime: 'node'`, or an `@socket.io/bun-engine` instance for `runtime: 'bun'`.
|
|
63
|
-
- **Redis is mandatory server-side.** `configure()` duplicates the parent `redisConnection` into three dedicated clients:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
- **Redis is mandatory server-side.** `configure()` duplicates the parent `redisConnection` into three dedicated clients:
|
|
73
|
+
|
|
74
|
+
| Client | Powers |
|
|
75
|
+
|--------|--------|
|
|
76
|
+
| `redisPub` + `redisSub` | `@socket.io/redis-adapter` - cross-instance room broadcast |
|
|
77
|
+
| `redisEmitter` | `@socket.io/redis-emitter` - used by `send()` |
|
|
78
|
+
|
|
79
|
+
- **Boot fails fast on a broken Redis connection.** `configure()` waits for all three clients to reach `ready`. It rejects after 30 seconds if any never do, so a broken Redis connection fails boot instead of hanging it.
|
|
80
|
+
- **Authentication is a step separate from connecting.** A client connects at the transport level in state `UNAUTHORIZED`, then must emit `'authenticate'`. The server then calls `authenticateFn(handshake)`.
|
|
81
|
+
- **Only `true` authenticates.** It moves the client to `AUTHENTICATED` and joins it to `defaultRooms`.
|
|
82
|
+
- **Unauthenticated clients time out.** A client that never authenticates within `authenticateTimeout` is disconnected. That includes a client whose `authenticateFn` is still pending when the timeout fires.
|
|
83
|
+
- **Heartbeat has no pong check.** Once authenticated, the server pings on `pingInterval` as a keep-alive. A silently dead connection is only caught when the underlying transport itself notices.
|
|
84
|
+
- **Custom rooms need `validateRoomFn`.** Room joins beyond `defaultRooms` are rejected unless you supply `validateRoomFn`.
|
|
69
85
|
|
|
70
86
|
**Defaults**
|
|
71
87
|
|
|
@@ -90,7 +106,7 @@ socketServer.send({
|
|
|
90
106
|
|
|
91
107
|
### Listen for a custom event
|
|
92
108
|
|
|
93
|
-
Register on the server with `on()
|
|
109
|
+
Register on the server with `on()`. Subscribe on the client with `subscribe()`.
|
|
94
110
|
|
|
95
111
|
```typescript
|
|
96
112
|
socketServer.on({
|
|
@@ -106,7 +122,7 @@ client.subscribe({
|
|
|
106
122
|
|
|
107
123
|
### Manage rooms
|
|
108
124
|
|
|
109
|
-
Clients request rooms with `joinRooms()` / `leaveRooms()
|
|
125
|
+
Clients request rooms with `joinRooms()` / `leaveRooms()`. The server filters join requests through `validateRoomFn`.
|
|
110
126
|
|
|
111
127
|
```typescript
|
|
112
128
|
const socketServer = new SocketIOServerHelper({
|
|
@@ -19,6 +19,22 @@ Exhaustive reference for `BaseStorageHelper`, the three `IStorageHelper` backend
|
|
|
19
19
|
- [`packages/helpers/src/modules/storage/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/storage/types.ts) - `IStorageHelper` and every option/result type
|
|
20
20
|
- [`packages/helpers/src/common/constants/mime.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/common/constants/mime.ts) - `MimeTypes` const-class
|
|
21
21
|
|
|
22
|
+
## Find what you need
|
|
23
|
+
|
|
24
|
+
| You want to | Go to |
|
|
25
|
+
|---|---|
|
|
26
|
+
| See which class implements which backend | [Class and Interface Model](#class-and-interface-model) |
|
|
27
|
+
| Construct a backend and see its options | [MinioHelper](#miniohelper) / [BunS3Helper](#buns3helper) / [DiskHelper](#diskhelper) |
|
|
28
|
+
| Understand what `upload()` validates and how it writes files | [upload (template method)](#upload-template-method-shared-by-every-backend) |
|
|
29
|
+
| Validate a name or path before writing | [isValidName](#isvalidname) / [isValidPath](#isvalidpath) |
|
|
30
|
+
| Look up every error message `upload()` can throw | [validateUploadFiles](#upload-template-method-shared-by-every-backend) |
|
|
31
|
+
| Read a file back as a stream | [MinioHelper#getFile](#getfile) / [BunS3Helper#getFile](#getfile-1) / [DiskHelper#getFile](#getfile-2) |
|
|
32
|
+
| List or delete objects in a bucket | per-backend Methods tables ([MinioHelper](#methods), [BunS3Helper](#methods-1), [DiskHelper](#methods-2)) |
|
|
33
|
+
| Cache values in-process (not bucket storage) | [MemoryStorageHelper](#memorystoragehelper) |
|
|
34
|
+
| Look up a type or option shape | [Types Reference](#types-reference) |
|
|
35
|
+
| Compare backend differences at a glance | [Backend Behavior Matrix](#backend-behavior-matrix) |
|
|
36
|
+
| Fix a thrown error message | [Troubleshooting](#troubleshooting) |
|
|
37
|
+
|
|
22
38
|
## Class and Interface Model
|
|
23
39
|
|
|
24
40
|
```
|
|
@@ -30,8 +46,9 @@ BaseHelper
|
|
|
30
46
|
└── MemoryStorageHelper -- In-memory key-value store (standalone, not IStorageHelper)
|
|
31
47
|
```
|
|
32
48
|
|
|
33
|
-
- **`upload()` is a template method.** It validates the bucket and every file
|
|
34
|
-
- **Every other method is backend-specific.** `isBucketExists`, `getBuckets`, `getBucket`, `createBucket`, `removeBucket`, `getFile`, `getStat`, `removeObject`, `removeObjects`, and `listObjects` are declared `abstract` on `BaseStorageHelper
|
|
49
|
+
- **`upload()` is a template method.** It validates the bucket and every file. Then it calls two protected hooks each backend supplies: `defaultLinkPrefix` (a getter) and `writeObject()` (the write itself).
|
|
50
|
+
- **Every other method is backend-specific.** `isBucketExists`, `getBuckets`, `getBucket`, `createBucket`, `removeBucket`, `getFile`, `getStat`, `removeObject`, `removeObjects`, and `listObjects` are declared `abstract` on `BaseStorageHelper`.
|
|
51
|
+
- Each one is fully reimplemented per backend - no logic is shared between a filesystem read and a MinIO `statObject()` call.
|
|
35
52
|
|
|
36
53
|
> [!TIP] Typing rule
|
|
37
54
|
> Declare parameters and bindings as `IStorageHelper` for `MinioHelper` / `BunS3Helper` / `DiskHelper`. `MemoryStorageHelper` does not implement it and has its own standalone API - see [MemoryStorageHelper](#memorystoragehelper).
|
|
@@ -81,7 +98,7 @@ constructor(opts: { scope: string; identifier: string })
|
|
|
81
98
|
| `scope` | `string` | Logger scope name. |
|
|
82
99
|
| `identifier` | `string` | Helper identifier. |
|
|
83
100
|
|
|
84
|
-
Every concrete backend's
|
|
101
|
+
Every concrete backend's constructor calls `super()` with its own defaults: `scope: options.scope ?? <ClassName>`, `identifier: options.identifier ?? <ClassName>`. That's why `scope` and `identifier` are required here, but optional on every subclass's public options type.
|
|
85
102
|
|
|
86
103
|
### Static properties
|
|
87
104
|
|
|
@@ -139,7 +156,7 @@ storage.getMimeType('unknown.xyz'); // 'application/octet-stream'
|
|
|
139
156
|
isValidName(name: string): boolean
|
|
140
157
|
```
|
|
141
158
|
|
|
142
|
-
Validates a **single path segment
|
|
159
|
+
Validates a **single path segment**: a bucket name or a bare file name, which must not contain `/`. Used internally by `isValidPath` to validate each segment. Logs a specific error for whichever rule fails.
|
|
143
160
|
|
|
144
161
|
| Rule (checked in order) | Example rejected | Reason |
|
|
145
162
|
|---|---|---|
|
|
@@ -166,7 +183,7 @@ storage.isValidName('.hidden'); // false -- starts with dot
|
|
|
166
183
|
isValidPath(pathStr: string, opts?: { maxDepth?: number }): boolean
|
|
167
184
|
```
|
|
168
185
|
|
|
169
|
-
Validates a **full object path** that may include folder segments
|
|
186
|
+
Validates a **full object path** that may include folder segments, for example `2025/uploads/report.pdf`. It trims leading and trailing slashes, splits on `/`, validates each segment with `isValidName`, and enforces a maximum folder depth. See the exact rules below.
|
|
170
187
|
|
|
171
188
|
| Rule (checked in order) | Description |
|
|
172
189
|
|---|---|
|
|
@@ -191,7 +208,7 @@ storage.isValidPath('a/b/c/d/file.pdf'); // false -- exceeds default max dep
|
|
|
191
208
|
getFileType(opts: { mimeType: string }): string
|
|
192
209
|
```
|
|
193
210
|
|
|
194
|
-
Categorizes a MIME type using the `MimeTypes` const-class
|
|
211
|
+
Categorizes a MIME type using the `MimeTypes` const-class: `UNKNOWN`, `IMAGE`, `VIDEO`, `TEXT`. It lowercases `mimeType` first, then checks whether it starts with `image`, `video`, or `text`.
|
|
195
212
|
|
|
196
213
|
```typescript
|
|
197
214
|
storage.getFileType({ mimeType: 'image/png' }); // 'image'
|
|
@@ -220,9 +237,9 @@ Implemented once on `BaseStorageHelper`; `MinioHelper`, `BunS3Helper`, and `Disk
|
|
|
220
237
|
2. Calls `isBucketExists({ name: bucket })`; throws if the bucket does not exist.
|
|
221
238
|
3. Validates every file (`validateUploadFiles`, below).
|
|
222
239
|
4. For each file, in parallel via `Promise.all()`:
|
|
223
|
-
- Computes `normalizeName` via `normalizeNameFn` if provided
|
|
224
|
-
- Re-validates `normalizeName` with `isValidPath({ maxDepth: maxFolderDepth })
|
|
225
|
-
- Computes `normalizeLink` via `normalizeLinkFn` if provided
|
|
240
|
+
- Computes `normalizeName` via `normalizeNameFn` if provided. Otherwise the default normalizer lowercases the name, replaces spaces with `_`, and prefixes `{folderPath}/` if set.
|
|
241
|
+
- Re-validates `normalizeName` with `isValidPath({ maxDepth: maxFolderDepth })`. This catches a traversal payload returned by a **custom** `normalizeNameFn`, even though `originalName` already passed validation.
|
|
242
|
+
- Computes `normalizeLink` via `normalizeLinkFn` if provided. Otherwise it builds the default: `{defaultLinkPrefix}{bucket}/{normalizeName}`, with each `/`-segment run through `encodeURIComponent`.
|
|
226
243
|
- Calls the backend's `writeObject({ bucket, normalizeName, file })`.
|
|
227
244
|
- Logs an info line with `normalizeName`, `normalizeLink`, `mimeType`, `encoding`, `size`, and elapsed time.
|
|
228
245
|
5. Returns `{ bucketName, objectName, link }` per file.
|
|
@@ -234,7 +251,7 @@ Implemented once on `BaseStorageHelper`; `MinioHelper`, `BunS3Helper`, and `Disk
|
|
|
234
251
|
| `isValidName(originalName)` | `'[upload] Invalid original file name'` |
|
|
235
252
|
| If `folderPath` set: segment count vs. `maxFolderDepth ?? DEFAULT_MAX_FOLDER_DEPTH` | `` `[upload] Invalid folder path | depth: {depth} | max: {max}` `` |
|
|
236
253
|
| If `folderPath` set: `isValidPath(folderPath, { maxDepth })` | `'[upload] Invalid folder path'` |
|
|
237
|
-
| `size` must be a number `>= 0`
|
|
254
|
+
| `size` must be a number `>= 0`. `undefined`, `null`, and negative values are rejected; `0` is a legal empty file. | `` `[upload] Invalid file size | size: {size}` `` |
|
|
238
255
|
|
|
239
256
|
**Also throws:**
|
|
240
257
|
|
|
@@ -311,12 +328,12 @@ Creates a `minio.Client` internally and stores it as a private `client` field -
|
|
|
311
328
|
| `options.scope` | `string` | `'MinioHelper'` | Logger scope name. |
|
|
312
329
|
| `options.identifier` | `string` | `'MinioHelper'` | Helper identifier. |
|
|
313
330
|
|
|
314
|
-
All other `minio.ClientOptions` fields
|
|
331
|
+
All other `minio.ClientOptions` fields are also accepted and passed to the client: `region`, `transport`, `sessionToken`, `partSize`, `pathStyle`, and more. See the [minio JavaScript SDK docs](https://min.io/docs/minio/linux/developers/javascript/API.html) for the complete list.
|
|
315
332
|
|
|
316
333
|
### defaultLinkPrefix and writeObject
|
|
317
334
|
|
|
318
335
|
- `defaultLinkPrefix`: `'/static-assets/'`
|
|
319
|
-
- `writeObject`: calls `client.putObject(bucket, normalizeName, buffer, size, metadata)
|
|
336
|
+
- `writeObject`: calls `client.putObject(bucket, normalizeName, buffer, size, metadata)`, where `metadata` is `{ originalName, normalizeName, size, encoding, mimeType }`. The full upload metadata is persisted server-side and returned later by `getStat()`.
|
|
320
337
|
|
|
321
338
|
### Methods
|
|
322
339
|
|
|
@@ -366,7 +383,7 @@ const fileStream = await minioStorage.getFile({
|
|
|
366
383
|
async getStat(opts: { bucket: string; name: string }): Promise<IFileStat>
|
|
367
384
|
```
|
|
368
385
|
|
|
369
|
-
`client.statObject()`. Returns `size`, `metadata`
|
|
386
|
+
`client.statObject()`. Returns `size`, `metadata`, `lastModified`, `etag`, and `versionId`. `metadata` is MinIO's `metaData` field: the full dict written by `writeObject`. `versionId` is set only if versioning is enabled.
|
|
370
387
|
|
|
371
388
|
#### listObjects
|
|
372
389
|
|
|
@@ -428,13 +445,13 @@ Creates a Bun `S3Client` for object operations and stores `{ accessKey, secretKe
|
|
|
428
445
|
### defaultLinkPrefix and writeObject
|
|
429
446
|
|
|
430
447
|
- `defaultLinkPrefix`: `'/static-assets/'`
|
|
431
|
-
- `writeObject`: `client.write(normalizeName, buffer, { bucket, type: mimeType })
|
|
448
|
+
- `writeObject`: `client.write(normalizeName, buffer, { bucket, type: mimeType })`. Only the content type is persisted. Unlike `MinioHelper`, no `originalName`/`encoding`/`size` metadata dictionary is stored.
|
|
432
449
|
|
|
433
450
|
### Methods
|
|
434
451
|
|
|
435
452
|
| Method | Behavior |
|
|
436
453
|
|---|---|
|
|
437
|
-
| `isBucketExists` | Returns `false` if the name fails `isValidName()`. Otherwise attempts `client.list({ maxKeys: 1 }, { bucket: name })
|
|
454
|
+
| `isBucketExists` | Returns `false` if the name fails `isValidName()`. Otherwise attempts `client.list({ maxKeys: 1 }, { bucket: name })`, and returns `false` on any error - for example a network failure or a missing bucket. |
|
|
438
455
|
| `getBuckets` | Signed `GET /`; parses `<Bucket><Name>...<CreationDate>...` from the XML response. |
|
|
439
456
|
| `getBucket` | Finds the entry in `getBuckets()`; `null` if not found. |
|
|
440
457
|
| `createBucket` | Signed `PUT /{name}`. Throws `'[createBucket] Invalid name to create bucket!'` on invalid name, or `` `[createBucket] S3 error: {xml}` `` on a non-OK response. |
|
|
@@ -504,7 +521,7 @@ async function buildSignedRequest(opts: {
|
|
|
504
521
|
```
|
|
505
522
|
|
|
506
523
|
- **Internal only.** Not exported from the package barrel.
|
|
507
|
-
- **Builds the `Authorization` header from scratch.** Uses `crypto.subtle`
|
|
524
|
+
- **Builds the `Authorization` header from scratch.** Uses `crypto.subtle` for HMAC-SHA256 and SHA-256 digests, following the standard SigV4 derivation: `kDate -> kRegion -> kService -> kSigning`.
|
|
508
525
|
- **Signs four headers.** `host`, `x-amz-content-sha256`, `x-amz-date`, and (if present) `x-amz-security-token`.
|
|
509
526
|
- **Used exclusively for bucket management.** `getBuckets`, `createBucket`, and `removeBucket` on `BunS3Helper`.
|
|
510
527
|
|
|
@@ -524,7 +541,7 @@ interface IDiskHelperOptions extends IStorageHelperOptions {
|
|
|
524
541
|
}
|
|
525
542
|
```
|
|
526
543
|
|
|
527
|
-
Resolves `basePath` to an absolute path with `path.resolve()
|
|
544
|
+
Resolves `basePath` to an absolute path with `path.resolve()`. Creates it via `fs.mkdirSync(..., { recursive: true })` if it does not exist.
|
|
528
545
|
|
|
529
546
|
| Parameter | Type | Default | Description |
|
|
530
547
|
|-----------|------|---------|-------------|
|
|
@@ -546,8 +563,9 @@ app_data/storage/ <-- basePath
|
|
|
546
563
|
|
|
547
564
|
### defaultLinkPrefix and writeObject
|
|
548
565
|
|
|
549
|
-
- `defaultLinkPrefix`: `'/static-resources/'`
|
|
550
|
-
- `writeObject`: creates the object's parent directory if missing
|
|
566
|
+
- `defaultLinkPrefix`: `'/static-resources/'` - the one backend that differs from `/static-assets/`.
|
|
567
|
+
- `writeObject`: creates the object's parent directory if missing, via `fsp.mkdir(dir, { recursive: true })`. Then it calls `fsp.writeFile(objectPath, file.buffer)`.
|
|
568
|
+
- No metadata dictionary is persisted alongside the file. `getStat()` derives `mimetype` from the filename at read time instead.
|
|
551
569
|
|
|
552
570
|
### Methods
|
|
553
571
|
|
|
@@ -559,7 +577,7 @@ app_data/storage/ <-- basePath
|
|
|
559
577
|
| `createBucket` | `fsp.mkdir(bucketPath, { recursive: true })`, then returns `getBucket()`. |
|
|
560
578
|
| `removeBucket` | `fsp.rmdir(bucketPath)`. |
|
|
561
579
|
| `removeObject` | Checks the object exists first (`fsp.access`); throws if missing. Otherwise `fsp.unlink(objectPath)`. |
|
|
562
|
-
| `removeObjects` | Deletes **sequentially** by calling `removeObject()` per name in a `for` loop
|
|
580
|
+
| `removeObjects` | Deletes **sequentially** by calling `removeObject()` per name in a `for` loop. If any file is missing, the error propagates immediately and remaining names are not attempted. |
|
|
563
581
|
|
|
564
582
|
**`createBucket` throws:**
|
|
565
583
|
|
|
@@ -846,7 +864,7 @@ interface IBunS3HelperOptions extends IStorageHelperOptions {
|
|
|
846
864
|
|
|
847
865
|
### "[createBucket] Invalid name to create bucket!"
|
|
848
866
|
|
|
849
|
-
**Cause:** The bucket name failed `isValidName()
|
|
867
|
+
**Cause:** The bucket name failed `isValidName()`. See the [validation rules](#isvalidname) for exactly what's rejected.
|
|
850
868
|
|
|
851
869
|
**Fix:**
|
|
852
870
|
|
|
@@ -865,7 +883,7 @@ await storage.createBucket({ name: 'my-bucket' });
|
|
|
865
883
|
|
|
866
884
|
### "[createBucket] Bucket already exists | name: {name}"
|
|
867
885
|
|
|
868
|
-
**Cause:** `DiskHelper` throws this exact message when `createBucket()` targets a directory that already exists. `MinioHelper` and `BunS3Helper`
|
|
886
|
+
**Cause:** `DiskHelper` throws this exact message when `createBucket()` targets a directory that already exists. `MinioHelper` and `BunS3Helper` skip this check. An existing bucket instead surfaces whatever the `minio` SDK or the raw S3 `PUT` request returns, which depends on the server.
|
|
869
887
|
|
|
870
888
|
**Fix:** Check existence first.
|
|
871
889
|
|
|
@@ -929,7 +947,7 @@ await storage.upload({
|
|
|
929
947
|
|
|
930
948
|
### "[upload] Invalid folder path" / "[upload] Invalid folder path | depth: {depth} | max: {max}"
|
|
931
949
|
|
|
932
|
-
**Cause:** A file's `folderPath`
|
|
950
|
+
**Cause:** A file's `folderPath` triggers one of two messages. The depth-specific message means it exceeds `maxFolderDepth`. The generic message means it fails `isValidPath()` for another reason, such as a traversal segment.
|
|
933
951
|
|
|
934
952
|
**Fix:** Keep `folderPath` within `maxFolderDepth` (default `2`) segments, and free of `..`/invalid characters.
|
|
935
953
|
|
|
@@ -950,7 +968,7 @@ const file: IUploadFile = {
|
|
|
950
968
|
|
|
951
969
|
### "[upload] Invalid normalized object name | name: {name}"
|
|
952
970
|
|
|
953
|
-
**Cause:** A custom `normalizeNameFn` returned a value that fails `isValidPath()
|
|
971
|
+
**Cause:** A custom `normalizeNameFn` returned a value that fails `isValidPath()`. That's typically a traversal payload like `../../../etc/cron.d/pwn`, or a name exceeding `maxFolderDepth`. This check exists because `originalName` passing validation does not guarantee the function's *output* is safe.
|
|
954
972
|
|
|
955
973
|
**Fix:** Ensure `normalizeNameFn` returns a plain relative name/path - no `..` segments, no leading `/`, no more folder segments than `maxFolderDepth` allows.
|
|
956
974
|
|
|
@@ -10,7 +10,7 @@ Storage gives you one file-storage interface with interchangeable backends for S
|
|
|
10
10
|
|
|
11
11
|
## In one example
|
|
12
12
|
|
|
13
|
-
`DiskHelper` needs no external server, so it
|
|
13
|
+
`DiskHelper` needs no external server, so it's the fastest way to see the shape of the API. Create a bucket, upload a file, get a link back.
|
|
14
14
|
|
|
15
15
|
```typescript
|
|
16
16
|
import { DiskHelper } from '@venizia/ignis-helpers';
|
|
@@ -39,12 +39,14 @@ console.log(result);
|
|
|
39
39
|
|
|
40
40
|
## How it works
|
|
41
41
|
|
|
42
|
-
- **`BaseStorageHelper` owns the shared logic.** It's an abstract class implementing `IStorageHelper
|
|
43
|
-
- **Everything else is per-backend.** `isBucketExists`, `getBuckets`, `createBucket`, `getFile`, `getStat`, `removeObject`, `listObjects`, and the rest of `IStorageHelper` are implemented independently per backend
|
|
42
|
+
- **`BaseStorageHelper` owns the shared logic.** It's an abstract class implementing `IStorageHelper`: name/path validation, MIME type detection, and the `upload()` orchestration itself. Each backend only supplies two protected hooks: `defaultLinkPrefix` and `writeObject()`.
|
|
43
|
+
- **Everything else is per-backend.** `isBucketExists`, `getBuckets`, `createBucket`, `getFile`, `getStat`, `removeObject`, `listObjects`, and the rest of `IStorageHelper` are implemented independently per backend. A filesystem `stat()` and a MinIO `statObject()` share nothing beyond the return shape.
|
|
44
44
|
- **The three backends are interchangeable.** Write services against `IStorageHelper`, not a concrete class, and swap backends by construction only.
|
|
45
|
-
- **`MemoryStorageHelper` is unrelated.**
|
|
46
|
-
- **Every write path is validated first.** `originalName` and `folderPath` run through `isValidName()`/`isValidPath()` before touching the filesystem or object store
|
|
47
|
-
- **
|
|
45
|
+
- **`MemoryStorageHelper` is unrelated.** It's a standalone generic key-value store for in-process caching, extending `BaseHelper` directly - no bucket or file concept.
|
|
46
|
+
- **Every write path is validated first.** `originalName` and `folderPath` run through `isValidName()`/`isValidPath()` before touching the filesystem or object store.
|
|
47
|
+
- **Validation blocks four kinds of bad input:** path traversal (`../`), shell-injection characters, hidden files, and folder nesting beyond `maxFolderDepth` (default `2`).
|
|
48
|
+
- **A custom `normalizeNameFn` doesn't get a free pass.** Its output runs through the same check, so a traversal payload smuggled back from application code is rejected too.
|
|
49
|
+
- **Two backends stay optional.** `MinioHelper` and `BunS3Helper` live behind separate sub-path exports. Apps that only need `DiskHelper` or `MemoryStorageHelper` don't pull in the `minio` package or require the Bun runtime.
|
|
48
50
|
|
|
49
51
|
**Backends**
|
|
50
52
|
|
|
@@ -124,7 +126,8 @@ import type { IStorageHelper, IUploadFile } from '@venizia/ignis-helpers';
|
|
|
124
126
|
class FileService {
|
|
125
127
|
constructor(private storage: IStorageHelper) {}
|
|
126
128
|
|
|
127
|
-
uploadFile(bucket: string
|
|
129
|
+
uploadFile(opts: { bucket: string; file: IUploadFile }) {
|
|
130
|
+
const { bucket, file } = opts;
|
|
128
131
|
return this.storage.upload({ bucket, files: [file] });
|
|
129
132
|
}
|
|
130
133
|
}
|
|
@@ -6,7 +6,7 @@ difficulty: beginner
|
|
|
6
6
|
|
|
7
7
|
# Types
|
|
8
8
|
|
|
9
|
-
`@venizia/ignis-helpers` exports the utility types, lazy-value resolvers, and constant classes (`HTTP`, `GRPC`, `RuntimeModules`, ...) that the rest of the
|
|
9
|
+
`@venizia/ignis-helpers` exports the utility types, lazy-value resolvers, and constant classes (`HTTP`, `GRPC`, `RuntimeModules`, ...) that the rest of the IGNIS stack builds on.
|
|
10
10
|
|
|
11
11
|
## In one example
|
|
12
12
|
|
|
@@ -26,10 +26,23 @@ configure({ timeout: () => 5000 });
|
|
|
26
26
|
|
|
27
27
|
## How it works
|
|
28
28
|
|
|
29
|
-
- **Escape hatches, not the norm.** `AnyType`
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
- **Escape hatches, not the norm.** `AnyType` and `AnyObject` exist for the rare case a shape truly cannot be known at compile time. The framework prefers types derived from definitions, like `typeof X.schema`, everywhere else.
|
|
30
|
+
|
|
31
|
+
| Type | Is |
|
|
32
|
+
|---|---|
|
|
33
|
+
| `AnyType` | `any` |
|
|
34
|
+
| `AnyObject` | `Record<string \| symbol \| number, any>` |
|
|
35
|
+
|
|
36
|
+
- **Nullable and async are explicit.** `TNullable<T>` and `ValueOrPromise<T>` appear on most framework method signatures. One declared type covers both the sync and the async implementation.
|
|
37
|
+
|
|
38
|
+
| Type | Shape |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `TNullable<T>` | `T \| undefined \| null` |
|
|
41
|
+
| `ValueOrPromise<T>` | `T \| Promise<T>` |
|
|
42
|
+
|
|
43
|
+
- **Resolvers defer construction.** `TResolver`/`TAsyncResolver` and their `TValueOrResolver`/`TValueOrAsyncResolver` unions let a config option be given eagerly or lazily. `resolveValue`/`resolveValueAsync` collapse either shape to a value.
|
|
44
|
+
- **Class constructors pass through untouched.** `isClass()` detects them, so the resolver never invokes a class as if it were a function. It's re-exported from `@venizia/ignis-inversion` into `helpers`.
|
|
45
|
+
- **Const classes replace string unions.** A class of `static readonly` fields, for example `HTTP` or `RuntimeModules`, is a value namespace. `TConstValue<typeof X>` derives its own union type from those fields - one declaration, no duplicated string literals.
|
|
33
46
|
|
|
34
47
|
## Common tasks
|
|
35
48
|
|
|
@@ -105,7 +118,7 @@ function WithTimestamps<T extends TMixinTarget<BaseEntity>>(Base: T) {
|
|
|
105
118
|
|
|
106
119
|
### Flatten an intersection type for readable hover tooltips
|
|
107
120
|
|
|
108
|
-
`TPrettify<T>` collapses `A & B` into a single flat object type
|
|
121
|
+
`TPrettify<T>` collapses `A & B` into a single flat object type. IDE tooltips render that far more readably than a chain of intersections.
|
|
109
122
|
|
|
110
123
|
```typescript
|
|
111
124
|
import { TPrettify } from '@venizia/ignis-helpers';
|
|
@@ -114,7 +127,7 @@ type Merged = TPrettify<{ id: string } & { name: string }>;
|
|
|
114
127
|
// Hovers as { id: string; name: string } instead of { id: string } & { name: string }
|
|
115
128
|
```
|
|
116
129
|
|
|
117
|
-
|
|
130
|
+
See the [Full reference](/extensions/helpers/types/reference) for every type, resolver function, and constant - including the full `HTTP.Headers`, `HTTP.ResultCodes`, and `GRPC` tables.
|
|
118
131
|
|
|
119
132
|
## See also
|
|
120
133
|
|
|
@@ -17,6 +17,27 @@ Exhaustive reference for every utility type, resolver function, and constant cla
|
|
|
17
17
|
- [`packages/helpers/src/common/constants/mime.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/common/constants/mime.ts) - `MimeTypes`
|
|
18
18
|
- [`packages/helpers/src/common/constants/index.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/common/constants/index.ts) - constants barrel
|
|
19
19
|
|
|
20
|
+
## Find what you need
|
|
21
|
+
|
|
22
|
+
| Looking for | Go to |
|
|
23
|
+
|---|---|
|
|
24
|
+
| An escape hatch (`any`, a loose object) | [General Purpose Types](#general-purpose-types) |
|
|
25
|
+
| A value that may be absent, or a callback that may be async | [Nullable and Promise Types](#nullable-and-promise-types) |
|
|
26
|
+
| A constructor or class type parameter | [Class and Constructor Types](#class-and-constructor-types) |
|
|
27
|
+
| Telling a class from a plain function at runtime | [isClass](#isclass) |
|
|
28
|
+
| Making object keys optional or required, or flattening `A & B` | [Object Utility Types](#object-utility-types) |
|
|
29
|
+
| A union type derived from a const class | [Const Value Extraction Types](#const-value-extraction-types) |
|
|
30
|
+
| An option that accepts an eager value or a resolver function | [Value Resolution Types and Functions](#value-resolution-types-and-functions) |
|
|
31
|
+
| A config-driven field-to-type mapping | [Field Mapping Types](#field-mapping-types) |
|
|
32
|
+
| DI container getter types, the `configure()` interface | [DI and Lifecycle Types](#di-and-lifecycle-types) |
|
|
33
|
+
| JSX component prop types | [JSX Types](#jsx-types) |
|
|
34
|
+
| Query pagination defaults, the application name | [Defaults](#defaults) |
|
|
35
|
+
| Detecting Bun vs Node at runtime | [RuntimeModules](#runtimemodules) |
|
|
36
|
+
| Schema data type constants | [DataTypes](#datatypes) |
|
|
37
|
+
| HTTP headers, methods, status codes | [HTTP](#http) |
|
|
38
|
+
| gRPC methods, headers, status codes | [GRPC](#grpc) |
|
|
39
|
+
| Content-type classification (image, video, text) | [MimeTypes](#mimetypes) |
|
|
40
|
+
|
|
20
41
|
## Import paths
|
|
21
42
|
|
|
22
43
|
```typescript
|
|
@@ -76,7 +97,7 @@ import type {
|
|
|
76
97
|
import type { Child, FC, PropsWithChildren } from '@venizia/ignis-helpers';
|
|
77
98
|
```
|
|
78
99
|
|
|
79
|
-
All of the above resolve through the root `@venizia/ignis-helpers` barrel
|
|
100
|
+
All of the above resolve through the root `@venizia/ignis-helpers` barrel. It re-exports `./common` in full, which includes `./common/types` and `./common/constants`.
|
|
80
101
|
|
|
81
102
|
## General Purpose Types
|
|
82
103
|
|
|
@@ -142,7 +163,7 @@ function MyMixin<T extends TMixinTarget<BaseClass>>(Base: T) {
|
|
|
142
163
|
const isClass: <T>(target: any) => target is TClass<T>;
|
|
143
164
|
```
|
|
144
165
|
|
|
145
|
-
Declared in `@venizia/ignis-inversion` and re-exported by `helpers`.
|
|
166
|
+
Declared in `@venizia/ignis-inversion` and re-exported by `helpers`. It's the single predicate that tells a constructor from a resolver function. The boot booters, controller factories, and `resolveValue`/`resolveValueAsync`/`resolveClass` all branch on it.
|
|
146
167
|
|
|
147
168
|
- **Filters to functions with a `prototype`** - true of every non-arrow function, so this alone is not sufficient.
|
|
148
169
|
- **Decompiles the function via `Function.prototype.toString`** and regex-tests that the source text literally starts with the `class` keyword.
|
|
@@ -209,11 +230,13 @@ type TValueOrAsyncResolver<T> = T | TAsyncResolver<T>;
|
|
|
209
230
|
const resolveValue: <T>(valueOrResolver: TValueOrResolver<T>) => T;
|
|
210
231
|
```
|
|
211
232
|
|
|
212
|
-
Synchronously resolves a lazy value
|
|
233
|
+
Synchronously resolves a lazy value.
|
|
213
234
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
235
|
+
| Input | Result |
|
|
236
|
+
|---|---|
|
|
237
|
+
| Non-function value | Returned as-is |
|
|
238
|
+
| Class constructor | Returned as-is - detected via `isClass()`, never invoked |
|
|
239
|
+
| Resolver function | Invoked; the return value is returned |
|
|
217
240
|
|
|
218
241
|
### resolveValueAsync
|
|
219
242
|
|
|
@@ -231,11 +254,13 @@ const resolveClass: <T>(
|
|
|
231
254
|
) => TClass<T> | string;
|
|
232
255
|
```
|
|
233
256
|
|
|
234
|
-
Resolves lazy class references.
|
|
257
|
+
Resolves lazy class references.
|
|
235
258
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
259
|
+
| Input | Result |
|
|
260
|
+
|---|---|
|
|
261
|
+
| String binding key | Returned as-is, for DI key lookups |
|
|
262
|
+
| Class constructor | Returned as-is |
|
|
263
|
+
| Resolver function | Invoked via `resolveValue`; the result is returned |
|
|
239
264
|
|
|
240
265
|
### Resolution example
|
|
241
266
|
|
|
@@ -361,7 +386,13 @@ class RuntimeModules {
|
|
|
361
386
|
type TRuntimeModule = TConstValue<typeof RuntimeModules>; // 'node' | 'bun'
|
|
362
387
|
```
|
|
363
388
|
|
|
364
|
-
Runtime detection utility.
|
|
389
|
+
Runtime detection utility.
|
|
390
|
+
|
|
391
|
+
| Method | Behavior |
|
|
392
|
+
|---|---|
|
|
393
|
+
| `detect()` | Returns `'bun'` if `typeof Bun !== 'undefined'`, else `'node'` |
|
|
394
|
+
| `isBun()` | Calls `detect()` and compares to `'bun'` - no caching, runs the check every time |
|
|
395
|
+
| `isNode()` | Calls `detect()` and compares to `'node'` - no caching, runs the check every time |
|
|
365
396
|
|
|
366
397
|
### DataTypes
|
|
367
398
|
|
|
@@ -439,7 +470,7 @@ Grouped by category, matching the source file's comments:
|
|
|
439
470
|
| **Rate limiting** (de facto standard) | `X_RATELIMIT_LIMIT` | `'x-ratelimit-limit'` |
|
|
440
471
|
| | `X_RATELIMIT_REMAINING` | `'x-ratelimit-remaining'` |
|
|
441
472
|
| | `X_RATELIMIT_RESET` | `'x-ratelimit-reset'` |
|
|
442
|
-
| **
|
|
473
|
+
| **IGNIS custom** | `REQUEST_TRACING_ID` | `'x-request-id'` |
|
|
443
474
|
| | `REQUEST_DEVICE_INFO` | `'x-device-info'` |
|
|
444
475
|
| | `REQUEST_CHANNEL` | `'x-request-channel'` |
|
|
445
476
|
| | `REQUEST_COUNT_DATA` | `'x-request-count'` |
|
|
@@ -489,7 +520,15 @@ Grouped by category, matching the source file's comments:
|
|
|
489
520
|
| `HTTP.Methods.QUERY` | `'query'` (RFC 10008 `QUERY` method) |
|
|
490
521
|
|
|
491
522
|
> [!IMPORTANT]
|
|
492
|
-
> All `HTTP.Methods.*` tokens are lowercase
|
|
523
|
+
> All `HTTP.Methods.*` tokens are lowercase. `@hono/zod-openapi` route definitions accept no other case.
|
|
524
|
+
|
|
525
|
+
Every IGNIS network fetcher uppercases the method at the wire boundary, via `method.toUpperCase()`. But undici, Node's `fetch` implementation, only normalizes six of the eight methods on its own:
|
|
526
|
+
|
|
527
|
+
| Undici normalizes it on its own | Undici does not |
|
|
528
|
+
|---|---|
|
|
529
|
+
| `delete`, `get`, `head`, `options`, `post`, `put` | `patch`, `query` |
|
|
530
|
+
|
|
531
|
+
A lowercase `patch` or `query` sent as-is travels verbatim over Node, and the server rejects it. Bun's `fetch` uppercases every method, which hides the bug until the app runs on Node.
|
|
493
532
|
|
|
494
533
|
#### HTTP.Protocols
|
|
495
534
|
|
|
@@ -500,7 +539,7 @@ Grouped by category, matching the source file's comments:
|
|
|
500
539
|
|
|
501
540
|
#### HTTP.ResultCodes
|
|
502
541
|
|
|
503
|
-
Status codes are grouped by class under `HTTP.ResultCodes.RS_1` through `HTTP.ResultCodes.RS_5`
|
|
542
|
+
Status codes are grouped by class under `HTTP.ResultCodes.RS_1` through `HTTP.ResultCodes.RS_5` - for example, `HTTP.ResultCodes.RS_4.NotFound`.
|
|
504
543
|
|
|
505
544
|
| Group | Constant | Value |
|
|
506
545
|
|-------|----------|-------|
|