@venizia/ignis-docs 0.0.7-2 → 0.0.8-0

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.
Files changed (161) hide show
  1. package/dist/mcp-server/common/paths.d.ts +4 -2
  2. package/dist/mcp-server/common/paths.d.ts.map +1 -1
  3. package/dist/mcp-server/common/paths.js +8 -6
  4. package/dist/mcp-server/common/paths.js.map +1 -1
  5. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts +1 -1
  6. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts.map +1 -1
  7. package/dist/mcp-server/tools/docs/get-document-content.tool.js +7 -7
  8. package/dist/mcp-server/tools/docs/get-document-metadata.tool.js +3 -3
  9. package/dist/mcp-server/tools/docs/get-package-overview.tool.d.ts +1 -1
  10. package/dist/mcp-server/tools/docs/get-package-overview.tool.js +1 -1
  11. package/package.json +1 -1
  12. package/wiki/best-practices/api-usage-examples.md +9 -9
  13. package/wiki/best-practices/architectural-patterns.md +19 -3
  14. package/wiki/best-practices/architecture-decisions.md +6 -6
  15. package/wiki/best-practices/code-style-standards/advanced-patterns.md +1 -1
  16. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  17. package/wiki/best-practices/code-style-standards/function-patterns.md +2 -2
  18. package/wiki/best-practices/code-style-standards/index.md +2 -2
  19. package/wiki/best-practices/code-style-standards/naming-conventions.md +1 -1
  20. package/wiki/best-practices/code-style-standards/route-definitions.md +4 -4
  21. package/wiki/best-practices/data-modeling.md +1 -1
  22. package/wiki/best-practices/deployment-strategies.md +1 -1
  23. package/wiki/best-practices/error-handling.md +2 -2
  24. package/wiki/best-practices/performance-optimization.md +3 -3
  25. package/wiki/best-practices/security-guidelines.md +2 -2
  26. package/wiki/best-practices/troubleshooting-tips.md +1 -1
  27. package/wiki/{references → extensions}/components/authentication/api.md +12 -20
  28. package/wiki/{references → extensions}/components/authentication/errors.md +1 -1
  29. package/wiki/{references → extensions}/components/authentication/index.md +5 -8
  30. package/wiki/{references → extensions}/components/authentication/usage.md +20 -36
  31. package/wiki/{references → extensions}/components/authorization/api.md +62 -13
  32. package/wiki/{references → extensions}/components/authorization/errors.md +12 -7
  33. package/wiki/{references → extensions}/components/authorization/index.md +93 -6
  34. package/wiki/{references → extensions}/components/authorization/usage.md +42 -4
  35. package/wiki/{references → extensions}/components/health-check.md +5 -4
  36. package/wiki/{references → extensions}/components/index.md +2 -0
  37. package/wiki/{references → extensions}/components/mail/index.md +1 -1
  38. package/wiki/{references → extensions}/components/request-tracker.md +1 -1
  39. package/wiki/{references → extensions}/components/socket-io/api.md +2 -2
  40. package/wiki/{references → extensions}/components/socket-io/errors.md +2 -0
  41. package/wiki/{references → extensions}/components/socket-io/index.md +24 -20
  42. package/wiki/{references → extensions}/components/socket-io/usage.md +2 -2
  43. package/wiki/{references → extensions}/components/static-asset/api.md +14 -15
  44. package/wiki/{references → extensions}/components/static-asset/errors.md +3 -1
  45. package/wiki/{references → extensions}/components/static-asset/index.md +158 -89
  46. package/wiki/{references → extensions}/components/static-asset/usage.md +8 -5
  47. package/wiki/{references → extensions}/components/swagger.md +3 -3
  48. package/wiki/{references → extensions}/components/template/index.md +4 -4
  49. package/wiki/{references → extensions}/components/template/setup-page.md +1 -1
  50. package/wiki/{references → extensions}/components/template/single-page.md +1 -1
  51. package/wiki/{references → extensions}/components/websocket/api.md +7 -6
  52. package/wiki/{references → extensions}/components/websocket/errors.md +17 -3
  53. package/wiki/{references → extensions}/components/websocket/index.md +17 -11
  54. package/wiki/{references → extensions}/components/websocket/usage.md +2 -2
  55. package/wiki/{references → extensions}/helpers/crypto/index.md +1 -1
  56. package/wiki/{references → extensions}/helpers/env/index.md +9 -5
  57. package/wiki/{references → extensions}/helpers/error/index.md +2 -7
  58. package/wiki/{references → extensions}/helpers/index.md +18 -6
  59. package/wiki/{references → extensions}/helpers/kafka/admin.md +33 -16
  60. package/wiki/extensions/helpers/kafka/consumer.md +384 -0
  61. package/wiki/extensions/helpers/kafka/examples.md +361 -0
  62. package/wiki/extensions/helpers/kafka/index.md +639 -0
  63. package/wiki/{references → extensions}/helpers/kafka/producer.md +100 -96
  64. package/wiki/extensions/helpers/kafka/schema-registry.md +214 -0
  65. package/wiki/{references → extensions}/helpers/logger/index.md +2 -2
  66. package/wiki/{references → extensions}/helpers/queue/index.md +400 -4
  67. package/wiki/{references → extensions}/helpers/storage/api.md +170 -10
  68. package/wiki/{references → extensions}/helpers/storage/index.md +44 -8
  69. package/wiki/{references → extensions}/helpers/template/index.md +1 -1
  70. package/wiki/{references → extensions}/helpers/testing/index.md +4 -4
  71. package/wiki/{references → extensions}/helpers/types/index.md +63 -16
  72. package/wiki/{references → extensions}/helpers/websocket/index.md +1 -1
  73. package/wiki/extensions/index.md +48 -0
  74. package/wiki/guides/core-concepts/application/bootstrapping.md +55 -37
  75. package/wiki/guides/core-concepts/application/index.md +95 -35
  76. package/wiki/guides/core-concepts/components-guide.md +23 -19
  77. package/wiki/guides/core-concepts/components.md +34 -10
  78. package/wiki/guides/core-concepts/dependency-injection.md +99 -34
  79. package/wiki/guides/core-concepts/grpc-controllers.md +295 -0
  80. package/wiki/guides/core-concepts/persistent/datasources.md +27 -8
  81. package/wiki/guides/core-concepts/persistent/models.md +43 -1
  82. package/wiki/guides/core-concepts/persistent/repositories.md +75 -8
  83. package/wiki/guides/core-concepts/persistent/transactions.md +38 -8
  84. package/wiki/guides/core-concepts/{controllers.md → rest-controllers.md} +30 -33
  85. package/wiki/guides/core-concepts/services.md +19 -5
  86. package/wiki/guides/get-started/5-minute-quickstart.md +6 -7
  87. package/wiki/guides/get-started/philosophy.md +1 -1
  88. package/wiki/guides/index.md +2 -2
  89. package/wiki/guides/reference/glossary.md +7 -7
  90. package/wiki/guides/reference/mcp-docs-server.md +1 -1
  91. package/wiki/guides/tutorials/building-a-crud-api.md +2 -2
  92. package/wiki/guides/tutorials/complete-installation.md +17 -14
  93. package/wiki/guides/tutorials/ecommerce-api.md +18 -18
  94. package/wiki/guides/tutorials/realtime-chat.md +8 -8
  95. package/wiki/guides/tutorials/testing.md +2 -2
  96. package/wiki/index.md +4 -3
  97. package/wiki/references/base/application.md +341 -21
  98. package/wiki/references/base/bootstrapping.md +43 -13
  99. package/wiki/references/base/components.md +259 -8
  100. package/wiki/references/base/controllers.md +556 -253
  101. package/wiki/references/base/datasources.md +159 -79
  102. package/wiki/references/base/dependency-injection.md +299 -48
  103. package/wiki/references/base/filter-system/application-usage.md +18 -2
  104. package/wiki/references/base/filter-system/array-operators.md +14 -6
  105. package/wiki/references/base/filter-system/comparison-operators.md +9 -3
  106. package/wiki/references/base/filter-system/default-filter.md +28 -3
  107. package/wiki/references/base/filter-system/fields-order-pagination.md +17 -13
  108. package/wiki/references/base/filter-system/index.md +169 -11
  109. package/wiki/references/base/filter-system/json-filtering.md +51 -18
  110. package/wiki/references/base/filter-system/list-operators.md +4 -3
  111. package/wiki/references/base/filter-system/logical-operators.md +7 -2
  112. package/wiki/references/base/filter-system/null-operators.md +50 -0
  113. package/wiki/references/base/filter-system/quick-reference.md +82 -243
  114. package/wiki/references/base/filter-system/range-operators.md +7 -1
  115. package/wiki/references/base/filter-system/tips.md +34 -7
  116. package/wiki/references/base/filter-system/use-cases.md +6 -5
  117. package/wiki/references/base/grpc-controllers.md +984 -0
  118. package/wiki/references/base/index.md +32 -24
  119. package/wiki/references/base/middleware.md +347 -0
  120. package/wiki/references/base/models.md +390 -46
  121. package/wiki/references/base/providers.md +14 -14
  122. package/wiki/references/base/repositories/advanced.md +84 -69
  123. package/wiki/references/base/repositories/index.md +447 -12
  124. package/wiki/references/base/repositories/mixins.md +103 -98
  125. package/wiki/references/base/repositories/relations.md +129 -45
  126. package/wiki/references/base/repositories/soft-deletable.md +104 -23
  127. package/wiki/references/base/services.md +94 -14
  128. package/wiki/references/index.md +12 -10
  129. package/wiki/references/quick-reference.md +98 -65
  130. package/wiki/references/utilities/crypto.md +21 -4
  131. package/wiki/references/utilities/date.md +25 -7
  132. package/wiki/references/utilities/index.md +26 -24
  133. package/wiki/references/utilities/jsx.md +54 -54
  134. package/wiki/references/utilities/module.md +8 -6
  135. package/wiki/references/utilities/parse.md +16 -9
  136. package/wiki/references/utilities/performance.md +22 -7
  137. package/wiki/references/utilities/promise.md +19 -16
  138. package/wiki/references/utilities/request.md +48 -26
  139. package/wiki/references/utilities/schema.md +69 -6
  140. package/wiki/references/utilities/statuses.md +131 -140
  141. package/wiki/references/helpers/kafka/consumer.md +0 -473
  142. package/wiki/references/helpers/kafka/examples.md +0 -234
  143. package/wiki/references/helpers/kafka/index.md +0 -482
  144. /package/wiki/{references → extensions}/components/mail/api.md +0 -0
  145. /package/wiki/{references → extensions}/components/mail/errors.md +0 -0
  146. /package/wiki/{references → extensions}/components/mail/usage.md +0 -0
  147. /package/wiki/{references → extensions}/components/template/api-page.md +0 -0
  148. /package/wiki/{references → extensions}/components/template/errors-page.md +0 -0
  149. /package/wiki/{references → extensions}/components/template/usage-page.md +0 -0
  150. /package/wiki/{references → extensions}/helpers/cron/index.md +0 -0
  151. /package/wiki/{references → extensions}/helpers/inversion/index.md +0 -0
  152. /package/wiki/{references → extensions}/helpers/network/api.md +0 -0
  153. /package/wiki/{references → extensions}/helpers/network/index.md +0 -0
  154. /package/wiki/{references → extensions}/helpers/redis/index.md +0 -0
  155. /package/wiki/{references → extensions}/helpers/socket-io/api.md +0 -0
  156. /package/wiki/{references → extensions}/helpers/socket-io/index.md +0 -0
  157. /package/wiki/{references → extensions}/helpers/template/single-page.md +0 -0
  158. /package/wiki/{references → extensions}/helpers/uid/index.md +0 -0
  159. /package/wiki/{references → extensions}/helpers/websocket/api.md +0 -0
  160. /package/wiki/{references → extensions}/helpers/worker-thread/index.md +0 -0
  161. /package/wiki/{references → extensions}/src-details/mcp-server.md +0 -0
@@ -1,6 +1,6 @@
1
1
  # Static Asset
2
2
 
3
- > Flexible file management system with support for multiple storage backends (local disk, MinIO/S3-compatible) through a unified interface, featuring factory-based controller generation and optional database file tracking via MetaLink.
3
+ > Flexible file management system with support for multiple storage backends (local disk, MinIO/S3-compatible, Bun S3) through a unified interface, featuring factory-based controller generation and optional database file tracking via MetaLink.
4
4
 
5
5
  ## Quick Reference
6
6
 
@@ -8,23 +8,35 @@
8
8
  |------|-------|
9
9
  | **Package** | `@venizia/ignis` |
10
10
  | **Class** | `StaticAssetComponent` |
11
- | **Helper** | [`DiskHelper`](/references/helpers/storage/), [`MinioHelper`](/references/helpers/storage/) |
11
+ | **Helper** | [`DiskHelper`](/extensions/helpers/storage/), [`MinioHelper`](/extensions/helpers/storage/), [`BunS3Helper`](/extensions/helpers/storage/) |
12
12
  | **Runtimes** | Both |
13
13
 
14
14
  #### Import Paths
15
+
16
+ > [!IMPORTANT]
17
+ > `StaticAssetComponent` and its related exports are **not** exported from the `@venizia/ignis` barrel. You must import from the `@venizia/ignis/static-asset` subpath.
18
+
15
19
  ```typescript
20
+ // From core -- subpath import (NOT from '@venizia/ignis')
16
21
  import {
17
22
  StaticAssetComponent,
18
23
  StaticAssetComponentBindingKeys,
19
24
  StaticAssetStorageTypes,
20
- } from '@venizia/ignis';
21
- import { DiskHelper, MinioHelper } from '@venizia/ignis-helpers';
25
+ AssetControllerFactory,
26
+ BaseMetaLinkModel,
27
+ BaseMetaLinkRepository,
28
+ } from '@venizia/ignis/static-asset';
29
+
30
+ import { DiskHelper } from '@venizia/ignis-helpers';
31
+ import { MinioHelper } from '@venizia/ignis-helpers/minio';
32
+ import { BunS3Helper } from '@venizia/ignis-helpers/bun-s3';
33
+
22
34
  import type {
23
35
  TStaticAssetsComponentOptions,
24
36
  TMetaLinkConfig,
25
37
  TStaticAssetExtraOptions,
26
38
  TStaticAssetStorageType,
27
- } from '@venizia/ignis';
39
+ } from '@venizia/ignis/static-asset';
28
40
  ```
29
41
 
30
42
  ### Key Features
@@ -35,7 +47,8 @@ import type {
35
47
  | **Multiple Storage Instances** | Configure multiple storage backends simultaneously |
36
48
  | **Factory Pattern** | Dynamic controller generation per storage backend |
37
49
  | **Built-in Security** | Comprehensive name validation, path traversal protection, header sanitization |
38
- | **Database Tracking (MetaLink)** | Optional database-backed file tracking with metadata, principal association, and sync status |
50
+ | **Database Tracking (MetaLink)** | Optional database-backed file tracking with metadata, principal association, variant support, and sync status |
51
+ | **Per-Route Configuration** | Override authentication, middleware, and path for individual routes |
39
52
  | **Flexible Configuration** | Environment-based, production-ready setup |
40
53
 
41
54
  ## Setup
@@ -43,13 +56,14 @@ import type {
43
56
  ### Step 1: Bind Configuration
44
57
 
45
58
  ```typescript
59
+ import { BaseApplication } from '@venizia/ignis';
46
60
  import {
47
- BaseApplication,
48
61
  StaticAssetComponentBindingKeys,
49
62
  StaticAssetStorageTypes,
50
- } from '@venizia/ignis';
51
- import { DiskHelper, MinioHelper } from '@venizia/ignis-helpers';
52
- import type { TStaticAssetsComponentOptions } from '@venizia/ignis';
63
+ } from '@venizia/ignis/static-asset';
64
+ import { DiskHelper } from '@venizia/ignis-helpers';
65
+ import { MinioHelper } from '@venizia/ignis-helpers/minio';
66
+ import type { TStaticAssetsComponentOptions } from '@venizia/ignis/static-asset';
53
67
 
54
68
  export class Application extends BaseApplication {
55
69
  preConfigure() {
@@ -100,7 +114,7 @@ Each storage backend gets a unique key (`staticAsset`, `staticResource`), its ow
100
114
  ### Step 2: Register Component
101
115
 
102
116
  ```typescript
103
- import { StaticAssetComponent } from '@venizia/ignis';
117
+ import { StaticAssetComponent } from '@venizia/ignis/static-asset';
104
118
 
105
119
  export class Application extends BaseApplication {
106
120
  preConfigure() {
@@ -186,13 +200,15 @@ Start with `docker-compose up -d` and access the console at `http://localhost:90
186
200
  |------|----------|--------|-------------|
187
201
  | `'disk'` | `StaticAssetStorageTypes.DISK` | `DiskHelper` | Local filesystem with bucket-based directory structure |
188
202
  | `'minio'` | `StaticAssetStorageTypes.MINIO` | `MinioHelper` | S3-compatible object storage (MinIO, AWS S3, etc.) |
203
+ | `'bun-s3'` | `StaticAssetStorageTypes.BUN_S3` | `BunS3Helper` | Bun-native S3 client (requires Bun runtime) |
189
204
 
190
205
  The `StaticAssetStorageTypes` class provides a `SCHEME_SET` (a `Set` of all valid storage type strings) and an `isValid(orgType)` method for runtime validation:
191
206
 
192
207
  ```typescript
193
- StaticAssetStorageTypes.isValid('minio'); // true
194
- StaticAssetStorageTypes.isValid('s3'); // false
195
- StaticAssetStorageTypes.SCHEME_SET; // Set { 'disk', 'minio' }
208
+ StaticAssetStorageTypes.isValid('minio'); // true
209
+ StaticAssetStorageTypes.isValid('bun-s3'); // true
210
+ StaticAssetStorageTypes.isValid('s3'); // false
211
+ StaticAssetStorageTypes.SCHEME_SET; // Set { 'disk', 'minio', 'bun-s3' }
196
212
  ```
197
213
 
198
214
  ### `TStaticAssetsComponentOptions`
@@ -204,12 +220,37 @@ Each key in the options object defines a separate storage backend with its own c
204
220
  | `controller.name` | `string` | -- | Controller class name |
205
221
  | `controller.basePath` | `string` | -- | Base URL path (e.g., `'/assets'`) |
206
222
  | `controller.isStrict` | `boolean` | `true` | Strict routing mode |
207
- | `storage` | `'disk' \| 'minio'` | -- | Storage type |
208
- | `helper` | `DiskHelper \| MinioHelper` | -- | Storage helper instance |
223
+ | `controller.routes` | `object` | `undefined` | Per-route overrides (authenticate, middleware, path) |
224
+ | `storage` | `'disk' \| 'minio' \| 'bun-s3'` | -- | Storage type |
225
+ | `helper` | `DiskHelper \| MinioHelper \| BunS3Helper` | -- | Storage helper instance |
209
226
  | `extra` | `TStaticAssetExtraOptions` | `undefined` | Extra options (multipart parsing, name normalization) |
210
227
  | `useMetaLink` | `boolean` | `false` | Enable database file tracking |
211
228
  | `metaLink` | `TMetaLinkConfig` | -- | MetaLink configuration (required when `useMetaLink: true`) |
212
229
 
230
+ #### Per-Route Configuration
231
+
232
+ Each route can be individually configured with authentication, middleware, and path overrides:
233
+
234
+ ```typescript
235
+ {
236
+ controller: {
237
+ name: 'AssetController',
238
+ basePath: '/assets',
239
+ routes: {
240
+ getBuckets: { authenticate: { strategies: ['jwt'], mode: 'required' } },
241
+ upload: { authenticate: { strategies: ['jwt'], mode: 'required' }, middleware: [rateLimitMw] },
242
+ getObjectByName: { /* public -- no authenticate */ },
243
+ downloadObjectByName: { /* public */ },
244
+ deleteObject: { authenticate: { strategies: ['jwt'], mode: 'required' } },
245
+ deleteBucket: { authenticate: { strategies: ['jwt'], mode: 'required' } },
246
+ },
247
+ },
248
+ // ...
249
+ }
250
+ ```
251
+
252
+ Available route keys: `getBuckets`, `getBucketByName`, `createBucket`, `deleteBucket`, `upload`, `listObjects`, `deleteObject`, `getObjectByName`, `downloadObjectByName`, `recreateMetaLink`.
253
+
213
254
  #### TStaticAssetsComponentOptions -- Full Reference
214
255
  ```typescript
215
256
  type TStaticAssetsComponentOptions = {
@@ -218,9 +259,22 @@ type TStaticAssetsComponentOptions = {
218
259
  name: string;
219
260
  basePath: string;
220
261
  isStrict?: boolean;
262
+ routes?: {
263
+ getBuckets?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
264
+ getBucketByName?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
265
+ createBucket?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
266
+ deleteBucket?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
267
+ upload?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
268
+ listObjects?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
269
+ deleteObject?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
270
+ getObjectByName?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
271
+ downloadObjectByName?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
272
+ recreateMetaLink?: Partial<Omit<IAuthRouteConfig, 'method' | 'request' | 'responses'>>;
273
+ };
221
274
  };
222
275
  extra?: TStaticAssetExtraOptions;
223
276
  } & (
277
+ | { storage: typeof StaticAssetStorageTypes.BUN_S3; helper: BunS3Helper }
224
278
  | { storage: typeof StaticAssetStorageTypes.DISK; helper: DiskHelper }
225
279
  | { storage: typeof StaticAssetStorageTypes.MINIO; helper: MinioHelper }
226
280
  ) &
@@ -232,7 +286,7 @@ type TStaticAssetExtraOptions = {
232
286
  storage?: 'memory' | 'disk';
233
287
  uploadDir?: string;
234
288
  };
235
- normalizeNameFn?: (opts: { originalName: string; folderPath?: string }) => string;
289
+ normalizeNameFn?: (opts: { originalName: string }) => string;
236
290
  normalizeLinkFn?: (opts: { bucketName: string; normalizeName: string }) => string;
237
291
  [key: string]: any;
238
292
  };
@@ -240,9 +294,20 @@ type TStaticAssetExtraOptions = {
240
294
  type TMetaLinkConfig<Schema extends TMetaLinkSchema = TMetaLinkSchema> = {
241
295
  model: typeof BaseEntity<Schema>;
242
296
  repository: DefaultCRUDRepository<Schema>;
297
+ createMetaLink?: (opts: {
298
+ uploadResult: IUploadResult;
299
+ fileStat: IFileStat;
300
+ query: TUploadQuery;
301
+ }) => ValueOrPromise<{ count: number; data: Schema }>;
243
302
  };
244
303
  ```
245
304
 
305
+ > [!NOTE]
306
+ > The `normalizeNameFn` receives only `{ originalName }` -- there is no `folderPath` parameter.
307
+
308
+ > [!TIP]
309
+ > The `createMetaLink` callback on `TMetaLinkConfig` is optional. When provided, it replaces the default MetaLink creation logic during upload, giving you full control over how file metadata is stored.
310
+
246
311
  ### DiskHelper
247
312
 
248
313
  Stores files on the local filesystem using a bucket-based directory structure.
@@ -301,16 +366,26 @@ const minioHelper = new MinioHelper({
301
366
  });
302
367
  ```
303
368
 
369
+ ### BunS3Helper
370
+
371
+ Bun-native S3 client for direct S3/S3-compatible access using Bun's built-in S3 support. Requires Bun runtime.
372
+
373
+ ```typescript
374
+ import { BunS3Helper } from '@venizia/ignis-helpers/bun-s3';
375
+ ```
376
+
304
377
  ### MetaLink Configuration
305
378
 
306
- MetaLink is an optional feature that tracks uploaded files in a database, storing file location, metadata (mimetype, size, etag), storage type, principal association (`principalType`, `principalId`), timestamps, and custom metadata (JSONB).
379
+ MetaLink is an optional feature that tracks uploaded files in a database, storing file location, metadata (mimetype, size, etag), storage type, principal association (`principalType`, `principalId`), variant, timestamps, and custom metadata (JSONB).
307
380
 
308
381
  #### Benefits
309
382
 
310
- - Query uploaded files by bucket, name, mimetype, etc.
383
+ - Query uploaded files by bucket, name, mimetype, variant, etc.
311
384
  - Track file history and audit trails
312
385
  - Store custom metadata about files
313
386
  - Associate files with principals via `principalType` and `principalId` (passed as query parameters on the upload endpoint)
387
+ - Tag uploads with a `variant` query parameter (e.g., `"thumbnail"`, `"original"`)
388
+ - Custom `createMetaLink` callback for full control over MetaLink creation
314
389
  - Graceful errors -- upload succeeds even if MetaLink creation fails
315
390
 
316
391
  #### Setup
@@ -318,7 +393,7 @@ MetaLink is an optional feature that tracks uploaded files in a database, storin
318
393
  **1. Create Model:**
319
394
 
320
395
  ```typescript
321
- import { BaseMetaLinkModel, model } from '@venizia/ignis';
396
+ import { BaseMetaLinkModel, model } from '@venizia/ignis/static-asset';
322
397
 
323
398
  @model({ type: 'entity' })
324
399
  export class FileMetaLinkModel extends BaseMetaLinkModel {
@@ -329,7 +404,9 @@ export class FileMetaLinkModel extends BaseMetaLinkModel {
329
404
  **2. Create Repository:**
330
405
 
331
406
  ```typescript
332
- import { BaseMetaLinkRepository, repository, inject, IDataSource } from '@venizia/ignis';
407
+ import { BaseMetaLinkRepository } from '@venizia/ignis/static-asset';
408
+ import { repository, inject } from '@venizia/ignis';
409
+ import type { IDataSource } from '@venizia/ignis';
333
410
 
334
411
  @repository({})
335
412
  export class FileMetaLinkRepository extends BaseMetaLinkRepository {
@@ -361,6 +438,7 @@ CREATE TABLE "MetaLink" (
361
438
  metadata JSONB,
362
439
  storage_type TEXT NOT NULL,
363
440
  is_synced BOOLEAN NOT NULL DEFAULT false,
441
+ variant TEXT,
364
442
  principal_type TEXT,
365
443
  principal_id TEXT
366
444
  );
@@ -375,6 +453,12 @@ CREATE INDEX "IDX_MetaLink_isSynced" ON "MetaLink"(is_synced);
375
453
 
376
454
  ```typescript
377
455
  import { FileMetaLinkModel, FileMetaLinkRepository } from './your-models';
456
+ import {
457
+ StaticAssetComponent,
458
+ StaticAssetComponentBindingKeys,
459
+ StaticAssetStorageTypes,
460
+ } from '@venizia/ignis/static-asset';
461
+ import type { TStaticAssetsComponentOptions } from '@venizia/ignis/static-asset';
378
462
 
379
463
  export class Application extends BaseApplication {
380
464
  configureComponents(): void {
@@ -407,23 +491,56 @@ export class Application extends BaseApplication {
407
491
  }
408
492
  ```
409
493
 
410
- **5. Upload with Principal Association:**
494
+ **5. Upload with Principal Association and Variant:**
411
495
 
412
- When MetaLink is enabled, you can associate uploaded files with a principal (user, service, etc.) by passing query parameters on the upload endpoint:
496
+ When MetaLink is enabled, you can associate uploaded files with a principal and variant by passing query parameters on the upload endpoint:
413
497
 
414
498
  ```typescript
415
499
  const formData = new FormData();
416
500
  formData.append('file', fileBlob, 'document.pdf');
417
501
 
418
- // Associate the upload with a user
502
+ // Associate the upload with a user and tag as 'original' variant
419
503
  const response = await fetch(
420
- '/uploads/buckets/user-files/upload?principalType=user&principalId=42',
504
+ '/uploads/buckets/user-files/upload?principalType=user&principalId=42&variant=original',
421
505
  { method: 'POST', body: formData },
422
506
  );
423
507
  ```
424
508
 
425
509
  The `principalId` value is always stored as a string regardless of input type (coerced via `String()`).
426
510
 
511
+ #### Custom MetaLink Creation
512
+
513
+ You can provide a custom `createMetaLink` callback to fully control how MetaLink records are created:
514
+
515
+ ```typescript
516
+ metaLink: {
517
+ model: FileMetaLinkModel,
518
+ repository: this.getSync(FileMetaLinkRepository),
519
+ createMetaLink: async ({ uploadResult, fileStat, query }) => {
520
+ // Custom logic -- e.g., add extra fields, validate, transform
521
+ return metaLinkRepo.create({
522
+ data: {
523
+ bucketName: uploadResult.bucketName,
524
+ objectName: uploadResult.objectName,
525
+ link: uploadResult.link,
526
+ mimetype: fileStat.metadata?.['mimetype'],
527
+ size: fileStat.size,
528
+ etag: fileStat.etag,
529
+ metadata: fileStat.metadata,
530
+ storageType: 'minio',
531
+ isSynced: true,
532
+ principalId: query.principalId ? String(query.principalId) : undefined,
533
+ principalType: query.principalType,
534
+ variant: query.variant,
535
+ // ... additional custom fields
536
+ },
537
+ });
538
+ },
539
+ },
540
+ ```
541
+
542
+ When `createMetaLink` is not provided, the component uses a default implementation that stores all standard fields.
543
+
427
544
  #### Querying MetaLinks
428
545
 
429
546
  ```typescript
@@ -447,32 +564,27 @@ const userFiles = await fileMetaLinkRepository.find({
447
564
  where: { principalType: 'user', principalId: '42' },
448
565
  });
449
566
 
450
- // Get synced files only
451
- const syncedFiles = await fileMetaLinkRepository.find({
452
- where: { isSynced: true },
567
+ // Get files by variant
568
+ const thumbnails = await fileMetaLinkRepository.find({
569
+ where: { variant: 'thumbnail' },
453
570
  });
454
571
 
455
- // Get unsynced files (for manual sync operations)
456
- const unsyncedFiles = await fileMetaLinkRepository.find({
457
- where: { isSynced: false },
458
- });
459
-
460
- // Count synced files
461
- const syncedCount = await fileMetaLinkRepository.count({
572
+ // Get synced files only
573
+ const syncedFiles = await fileMetaLinkRepository.find({
462
574
  where: { isSynced: true },
463
575
  });
464
-
465
- // Get recent uploads
466
- const recent = await fileMetaLinkRepository.find({
467
- orderBy: { createdAt: 'desc' },
468
- limit: 10,
469
- });
470
576
  ```
471
577
 
472
578
  ### Quick Start Options
473
579
 
474
580
  **Option 1: MinIO Only**
475
581
  ```typescript
582
+ import {
583
+ StaticAssetComponent,
584
+ StaticAssetComponentBindingKeys,
585
+ StaticAssetStorageTypes,
586
+ } from '@venizia/ignis/static-asset';
587
+
476
588
  this.bind({
477
589
  key: StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS,
478
590
  }).toValue({
@@ -538,9 +650,8 @@ this.component(StaticAssetComponent);
538
650
  helper: new MinioHelper({ /* ... */ }),
539
651
  extra: {
540
652
  parseMultipartBody: { storage: 'memory' },
541
- normalizeNameFn: ({ originalName, folderPath }) => {
542
- const prefix = folderPath ? `${folderPath}/` : '';
543
- return `${prefix}${Date.now()}_${originalName.toLowerCase().replace(/\s/g, '_')}`;
653
+ normalizeNameFn: ({ originalName }) => {
654
+ return `${Date.now()}_${originalName.toLowerCase().replace(/\s/g, '_')}`;
544
655
  },
545
656
  normalizeLinkFn: ({ bucketName, normalizeName }) => {
546
657
  return `/api/files/${bucketName}/${encodeURIComponent(normalizeName)}`;
@@ -550,49 +661,7 @@ this.component(StaticAssetComponent);
550
661
  }
551
662
  ```
552
663
 
553
- The `normalizeNameFn` receives both the `originalName` and an optional `folderPath` from the uploaded file. The `folderPath` is passed through from the `IUploadFile` object and can be used to organize files into subdirectories.
554
-
555
- ### Custom Storage Implementation
556
-
557
- You can implement your own storage backend by extending `BaseStorageHelper`:
558
-
559
- ```typescript
560
- import { BaseStorageHelper, IUploadFile, IUploadResult } from '@venizia/ignis-helpers';
561
-
562
- class S3Helper extends BaseStorageHelper {
563
- constructor(config: S3Config) {
564
- super({ scope: 'S3Helper', identifier: 'S3Helper' });
565
- // Initialize S3 client
566
- }
567
-
568
- async isBucketExists(opts: { name: string }): Promise<boolean> {
569
- // Implementation
570
- }
571
-
572
- async upload(opts: {
573
- bucket: string;
574
- files: IUploadFile[];
575
- normalizeNameFn?: (opts: { originalName: string; folderPath?: string }) => string;
576
- normalizeLinkFn?: (opts: { bucketName: string; normalizeName: string }) => string;
577
- }): Promise<IUploadResult[]> {
578
- // Implementation
579
- }
580
-
581
- // Implement other IStorageHelper methods...
582
- }
583
-
584
- // Usage
585
- this.bind<TStaticAssetsComponentOptions>({
586
- key: StaticAssetComponentBindingKeys.STATIC_ASSET_COMPONENT_OPTIONS,
587
- }).toValue({
588
- s3Storage: {
589
- controller: { name: 'S3Controller', basePath: '/s3-assets' },
590
- storage: 'custom-s3',
591
- helper: new S3Helper({ /* ... */ }),
592
- extra: {},
593
- },
594
- });
595
- ```
664
+ The `normalizeNameFn` receives only the `originalName` of the uploaded file.
596
665
 
597
666
  ## Binding Keys
598
667
 
@@ -608,8 +677,8 @@ this.bind<TStaticAssetsComponentOptions>({
608
677
  - [Usage & Examples](./usage) - API Endpoints and Frontend Integration
609
678
  - [API Reference](./api) - Controller Factory, Storage Interface, MetaLink Schema
610
679
  - [Error Reference](./errors) - Name Validation and Troubleshooting
611
- - [Storage Helpers](/references/helpers/storage/) - DiskHelper, MinioHelper, BaseStorageHelper
680
+ - [Storage Helpers](/extensions/helpers/storage/) - DiskHelper, MinioHelper, BaseStorageHelper
612
681
  - [Request Utilities](/references/utilities/request) - File upload utilities
613
682
  - [Security Guidelines](/best-practices/security-guidelines) - File upload security
614
683
  - [Components Overview](/guides/core-concepts/components) - Component system basics
615
- - [Controllers](/guides/core-concepts/controllers) - File upload endpoints
684
+ - [Controllers](/guides/core-concepts/rest-controllers) - File upload endpoints
@@ -73,6 +73,7 @@ The `isDeleted` field is a boolean indicating whether the bucket was successfull
73
73
  **Query Parameters:**
74
74
  - `principalType` (optional, string): Type of the principal to associate with the uploaded files (e.g., `"user"`, `"service"`)
75
75
  - `principalId` (optional, string or number): ID of the principal. Always coerced to a string via `String()` before storage regardless of input type
76
+ - `variant` (optional, string): Variant tag for the upload (e.g., `"thumbnail"`, `"original"`)
76
77
 
77
78
  **Validation:** Bucket name validated with `isValidName()`. Returns 400 `"Invalid bucket name"` if invalid.
78
79
 
@@ -84,7 +85,7 @@ const MultipartBodySchema = z.object({
84
85
  });
85
86
  ```
86
87
 
87
- This accepts either a single `File` or an array of `File` objects. Each file can optionally include `folderPath` for organization.
88
+ This accepts either a single `File` or an array of `File` objects.
88
89
 
89
90
  **Response `200` (without MetaLink):**
90
91
  ```json
@@ -115,6 +116,7 @@ This accepts either a single `File` or an array of `File` objects. Each file can
115
116
  "metadata": {},
116
117
  "storageType": "minio",
117
118
  "isSynced": true,
119
+ "variant": "original",
118
120
  "principalType": "user",
119
121
  "principalId": "42",
120
122
  "createdAt": "2025-12-15T03:00:00.000Z",
@@ -144,9 +146,9 @@ When MetaLink creation fails, the upload itself still succeeds. The response inc
144
146
  const formData = new FormData();
145
147
  formData.append('file', fileBlob, 'document.pdf');
146
148
 
147
- // Upload with principal association
149
+ // Upload with principal association and variant
148
150
  const response = await fetch(
149
- '/assets/buckets/uploads/upload?principalType=user&principalId=123',
151
+ '/assets/buckets/uploads/upload?principalType=user&principalId=123&variant=original',
150
152
  { method: 'POST', body: formData },
151
153
  );
152
154
 
@@ -322,14 +324,15 @@ for (const obj of objects) {
322
324
  ## Frontend Integration
323
325
 
324
326
  ```typescript
325
- // Upload file with principal association
326
- async function uploadFile(file: File, principalType?: string, principalId?: string) {
327
+ // Upload file with principal association and variant
328
+ async function uploadFile(file: File, principalType?: string, principalId?: string, variant?: string) {
327
329
  const formData = new FormData();
328
330
  formData.append('file', file);
329
331
 
330
332
  const url = new URL('/assets/buckets/user-uploads/upload', window.location.origin);
331
333
  if (principalType) url.searchParams.append('principalType', principalType);
332
334
  if (principalId) url.searchParams.append('principalId', principalId);
335
+ if (variant) url.searchParams.append('variant', variant);
333
336
 
334
337
  const response = await fetch(url, {
335
338
  method: 'POST',
@@ -67,11 +67,11 @@ To get the most out of the documentation, define your routes with `zod` schemas:
67
67
  ```typescript
68
68
  // src/controllers/hello.controller.ts
69
69
  import { z } from '@hono/zod-openapi';
70
- import { BaseController, controller, jsonContent, ValueOrPromise } from '@venizia/ignis';
70
+ import { BaseRestController, controller, jsonContent, ValueOrPromise } from '@venizia/ignis';
71
71
  import { HTTP } from '@venizia/ignis-helpers';
72
72
 
73
73
  @controller({ path: '/hello' })
74
- export class HelloController extends BaseController {
74
+ export class HelloController extends BaseRestController {
75
75
  constructor() {
76
76
  super({ scope: HelloController.name, path: '/hello' });
77
77
  }
@@ -457,7 +457,7 @@ this.defineRoute({
457
457
 
458
458
  - **Guides:**
459
459
  - [Components Overview](/guides/core-concepts/components) - Component system basics
460
- - [Controllers](/guides/core-concepts/controllers) - Defining OpenAPI routes
460
+ - [Controllers](/guides/core-concepts/rest-controllers) - Defining OpenAPI routes
461
461
 
462
462
  - **Components:**
463
463
  - [All Components](./index) - Built-in components list
@@ -40,10 +40,10 @@ A directory with 4 files:
40
40
  text: 'ComponentName',
41
41
  collapsed: true,
42
42
  items: [
43
- { text: 'Setup & Configuration', link: '/references/components/slug/' },
44
- { text: 'Usage & Examples', link: '/references/components/slug/usage' },
45
- { text: 'API Reference', link: '/references/components/slug/api' },
46
- { text: 'Error Reference', link: '/references/components/slug/errors' },
43
+ { text: 'Setup & Configuration', link: '/extensions/components/slug/' },
44
+ { text: 'Usage & Examples', link: '/extensions/components/slug/usage' },
45
+ { text: 'API Reference', link: '/extensions/components/slug/api' },
46
+ { text: 'Error Reference', link: '/extensions/components/slug/errors' },
47
47
  ],
48
48
  },
49
49
  ```
@@ -26,7 +26,7 @@ Paired with [Usage](./usage-page), [API Reference](./api-page), and [Error Refer
26
26
  |------|-------|
27
27
  | **Package** | `@venizia/ignis` |
28
28
  | **Class** | `{ComponentClass}` |
29
- | **Helper** | [`{HelperClass}`](/references/helpers/{slug}) |
29
+ | **Helper** | [`{HelperClass}`](/extensions/helpers/{slug}) |
30
30
  | **Runtimes** | Both / Bun only |
31
31
 
32
32
  | Sub-component | Purpose |
@@ -24,7 +24,7 @@ For simple components with few configuration options and straightforward behavio
24
24
  |------|-------|
25
25
  | **Package** | `@venizia/ignis` |
26
26
  | **Class** | `{ComponentClass}` |
27
- | **Helper** | [`{HelperClass}`](/references/helpers/{slug}) |
27
+ | **Helper** | [`{HelperClass}`](/extensions/helpers/{slug}) |
28
28
  | **Runtimes** | Both / Bun only |
29
29
 
30
30
  #### Import Paths
@@ -232,11 +232,12 @@ Registers a post-start hook that executes the following steps:
232
232
 
233
233
  1. **Get Bun server instance** via `getServerInstance<TBunServerInstance>()`
234
234
  2. **Get Hono server** via `getServer()`
235
- 3. **Create WebSocketServerHelper** with all resolved bindings and server options
236
- 4. **Await `wsHelper.configure()`** which connects Redis clients and sets up subscriptions
237
- 5. **Bind the helper** to `WEBSOCKET_INSTANCE` in the DI container
238
- 6. **Create custom `fetch` handler** via `createBunFetchHandler({ wsPath, honoServer })`
239
- 7. **Wire WebSocket into running server** via `serverInstance.reload({ fetch, websocket })`
235
+ 3. **Validate server instance** -- throws `"[WebSocketComponent] Bun server instance not available!"` if not found
236
+ 4. **Create WebSocketServerHelper** with all resolved bindings and server options
237
+ 5. **Await `wsHelper.configure()`** which connects Redis clients and sets up subscriptions
238
+ 6. **Bind the helper** to `WEBSOCKET_INSTANCE` in the DI container
239
+ 7. **Create custom `fetch` handler** via `createBunFetchHandler({ wsPath, honoServer })`
240
+ 8. **Wire WebSocket into running server** via `serverInstance.reload({ fetch, websocket })`
240
241
 
241
242
  #### Post-Start Hook Code Flow
242
243
  ```typescript
@@ -503,6 +504,6 @@ The emitter shutdown is simpler since it only has one Redis client and no local
503
504
  - [Setup & Configuration](./) - Quick reference, imports, setup steps, configuration, and binding keys
504
505
  - [Usage & Examples](./usage) - Server-side usage, emitter, wire protocol, client tracking, and delivery strategy
505
506
  - [Error Reference](./errors) - Error conditions table and troubleshooting
506
- - [WebSocketServerHelper](/references/helpers/websocket/) - Helper API documentation
507
+ - [WebSocketServerHelper](/extensions/helpers/websocket/) - Helper API documentation
507
508
  - [Socket.IO Component](../socket-io/) - Node.js-compatible alternative with Socket.IO
508
509
  - [Bun WebSocket Documentation](https://bun.sh/docs/api/websockets) - Official Bun WebSocket API reference
@@ -22,6 +22,16 @@ The server can send `error` events or close the connection under the following c
22
22
  | `"Invalid redis connection!"` | Constructor: `redisConnection` is falsy | thrown `Error` (startup) |
23
23
  | `"WebSocket upgrade failed"` | `server.upgrade()` returned `false` | HTTP `500` response |
24
24
 
25
+ ### Component Errors
26
+
27
+ | Method | Condition | Error Message |
28
+ |--------|-----------|---------------|
29
+ | `binding()` | `application` is falsy | `"[binding] Invalid application to bind WebSocketComponent"` |
30
+ | `binding()` | Node.js runtime detected | `"[WebSocketComponent] Node.js runtime is not supported yet. Please use Bun runtime."` |
31
+ | `resolveBindings()` | `REDIS_CONNECTION` not instanceof `DefaultRedisHelper` | `"[WebSocketComponent][resolveBindings] Invalid instance of redisConnection | Please init connection with RedisHelper for single redis connection or RedisClusterHelper for redis cluster mode!"` |
32
+ | `resolveBindings()` | `AUTHENTICATE_HANDLER` is falsy | `"[WebSocketComponent] Invalid authenticateFn to setup WebSocket server!"` |
33
+ | `registerBunHook()` | Bun server instance not available | `"[WebSocketComponent] Bun server instance not available!"` |
34
+
25
35
  ## Troubleshooting
26
36
 
27
37
  ### "WebSocket not initialized"
@@ -32,11 +42,13 @@ The server can send `error` events or close the connection under the following c
32
42
 
33
43
  ### "Invalid instance of redisConnection"
34
44
 
35
- **Cause**: The value bound to `REDIS_CONNECTION` is not an instance of `DefaultRedisHelper` (or its subclass `RedisHelper`).
45
+ **Cause**: The value bound to `REDIS_CONNECTION` is not an instance of `DefaultRedisHelper` (or its subclasses `RedisHelper` / `RedisClusterHelper`).
36
46
 
37
- **Fix**: Use `RedisHelper` (recommended) or `DefaultRedisHelper`:
47
+ **Fix**: Use `RedisHelper` (single instance) or `RedisClusterHelper` (cluster mode):
38
48
 
39
49
  ```typescript
50
+ import { WebSocketBindingKeys } from '@venizia/ignis/websocket';
51
+
40
52
  // Correct
41
53
  this.bind({ key: WebSocketBindingKeys.REDIS_CONNECTION })
42
54
  .toValue(new RedisHelper({ name: 'websocket', host, port, password }));
@@ -53,6 +65,8 @@ this.bind({ key: WebSocketBindingKeys.REDIS_CONNECTION })
53
65
  **Fix**: Bind a valid authentication function before registering the component:
54
66
 
55
67
  ```typescript
68
+ import { WebSocketBindingKeys } from '@venizia/ignis/websocket';
69
+
56
70
  this.bind<TWebSocketAuthenticateFn>({
57
71
  key: WebSocketBindingKeys.AUTHENTICATE_HANDLER,
58
72
  }).toValue(async (data) => {
@@ -118,6 +132,6 @@ setInterval(() => {
118
132
  - [Setup & Configuration](./) - Quick reference, imports, setup steps, configuration, and binding keys
119
133
  - [Usage & Examples](./usage) - Server-side usage, emitter, wire protocol, client tracking, and delivery strategy
120
134
  - [API Reference](./api) - Architecture, WebSocketEmitter API, and internals
121
- - [WebSocketServerHelper](/references/helpers/websocket/) - Helper API documentation
135
+ - [WebSocketServerHelper](/extensions/helpers/websocket/) - Helper API documentation
122
136
  - [Socket.IO Component](../socket-io/) - Node.js-compatible alternative with Socket.IO
123
137
  - [Bun WebSocket Documentation](https://bun.sh/docs/api/websockets) - Official Bun WebSocket API reference