@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.
Files changed (145) hide show
  1. package/content/best-practices/architectural-patterns.md +3 -3
  2. package/content/best-practices/code-style-standards/naming-conventions.md +1 -1
  3. package/content/best-practices/contribution-workflow.md +2 -2
  4. package/content/best-practices/error-handling.md +94 -89
  5. package/content/best-practices/security-guidelines.md +5 -5
  6. package/content/extensions/components/api-reference.md +22 -21
  7. package/content/extensions/components/authentication/api.md +64 -28
  8. package/content/extensions/components/authentication/errors.md +19 -5
  9. package/content/extensions/components/authentication/index.md +19 -18
  10. package/content/extensions/components/authentication/usage.md +55 -30
  11. package/content/extensions/components/authorization/api.md +351 -84
  12. package/content/extensions/components/authorization/errors.md +51 -17
  13. package/content/extensions/components/authorization/getting-started.md +227 -0
  14. package/content/extensions/components/authorization/index.md +24 -16
  15. package/content/extensions/components/authorization/usage.md +45 -21
  16. package/content/extensions/components/health-check.md +15 -9
  17. package/content/extensions/components/index.md +24 -90
  18. package/content/extensions/components/mail/api.md +105 -54
  19. package/content/extensions/components/mail/errors.md +12 -10
  20. package/content/extensions/components/mail/index.md +32 -13
  21. package/content/extensions/components/mail/usage.md +26 -18
  22. package/content/extensions/components/request-tracker.md +18 -14
  23. package/content/extensions/components/socket-io/api.md +377 -882
  24. package/content/extensions/components/socket-io/errors.md +49 -51
  25. package/content/extensions/components/socket-io/index.md +72 -88
  26. package/content/extensions/components/socket-io/usage.md +107 -117
  27. package/content/extensions/components/static-asset/api.md +83 -31
  28. package/content/extensions/components/static-asset/errors.md +18 -7
  29. package/content/extensions/components/static-asset/index.md +18 -11
  30. package/content/extensions/components/static-asset/usage.md +11 -6
  31. package/content/extensions/components/template/index.md +3 -3
  32. package/content/extensions/components/websocket/api.md +58 -27
  33. package/content/extensions/components/websocket/errors.md +3 -3
  34. package/content/extensions/components/websocket/index.md +8 -7
  35. package/content/extensions/components/websocket/usage.md +21 -8
  36. package/content/extensions/helpers/cron/index.md +8 -7
  37. package/content/extensions/helpers/crypto/index.md +16 -8
  38. package/content/extensions/helpers/crypto/reference.md +96 -24
  39. package/content/extensions/helpers/env/index.md +14 -10
  40. package/content/extensions/helpers/error/index.md +99 -23
  41. package/content/extensions/helpers/index.md +61 -47
  42. package/content/extensions/helpers/inversion/index.md +23 -6
  43. package/content/extensions/helpers/inversion/reference.md +30 -22
  44. package/content/extensions/helpers/kafka/admin.md +3 -2
  45. package/content/extensions/helpers/kafka/compile-binary.md +69 -44
  46. package/content/extensions/helpers/kafka/consumer.md +24 -21
  47. package/content/extensions/helpers/kafka/examples.md +22 -234
  48. package/content/extensions/helpers/kafka/index.md +30 -62
  49. package/content/extensions/helpers/kafka/producer.md +31 -26
  50. package/content/extensions/helpers/kafka/schema-registry.md +19 -14
  51. package/content/extensions/helpers/logger/hf-logger.md +49 -22
  52. package/content/extensions/helpers/logger/index.md +37 -12
  53. package/content/extensions/helpers/logger/pino.md +31 -11
  54. package/content/extensions/helpers/logger/reference.md +243 -52
  55. package/content/extensions/helpers/network/api.md +65 -30
  56. package/content/extensions/helpers/network/index.md +32 -11
  57. package/content/extensions/helpers/queue/index.md +17 -6
  58. package/content/extensions/helpers/queue/reference.md +52 -25
  59. package/content/extensions/helpers/redis/index.md +29 -11
  60. package/content/extensions/helpers/redis/reference.md +85 -28
  61. package/content/extensions/helpers/secrets/index.md +82 -12
  62. package/content/extensions/helpers/socket-io/api.md +40 -21
  63. package/content/extensions/helpers/socket-io/index.md +26 -10
  64. package/content/extensions/helpers/storage/api.md +42 -24
  65. package/content/extensions/helpers/storage/index.md +10 -7
  66. package/content/extensions/helpers/types/index.md +20 -7
  67. package/content/extensions/helpers/types/reference.md +53 -14
  68. package/content/extensions/helpers/uid/index.md +166 -10
  69. package/content/extensions/helpers/websocket/api.md +52 -13
  70. package/content/extensions/helpers/websocket/index.md +7 -4
  71. package/content/extensions/helpers/worker-thread/index.md +9 -5
  72. package/content/extensions/helpers/worker-thread/reference.md +20 -20
  73. package/content/extensions/index.md +38 -39
  74. package/content/extensions/src-details/mcp-server.md +96 -548
  75. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  76. package/content/guides/core-concepts/persistent/index.md +5 -1
  77. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  78. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  79. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  80. package/content/guides/core-concepts/persistent/search-typesense.md +26 -14
  81. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  82. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  83. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  84. package/content/guides/get-started/philosophy.md +135 -670
  85. package/content/guides/get-started/setup.md +53 -74
  86. package/content/guides/migrations/redis-helpers-migration.md +4 -3
  87. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  88. package/content/guides/migrations/unified-connectors-migration.md +7 -6
  89. package/content/guides/tutorials/realtime-chat.md +1 -1
  90. package/content/references/base/application.md +63 -21
  91. package/content/references/base/components.md +3 -3
  92. package/content/references/base/connectors.md +14 -14
  93. package/content/references/base/controllers.md +12 -12
  94. package/content/references/base/datasources-reference.md +32 -31
  95. package/content/references/base/datasources.md +6 -6
  96. package/content/references/base/dependency-injection.md +12 -11
  97. package/content/references/base/filter-system/application-usage.md +54 -30
  98. package/content/references/base/filter-system/array-operators.md +24 -46
  99. package/content/references/base/filter-system/comparison-operators.md +47 -67
  100. package/content/references/base/filter-system/default-filter.md +59 -53
  101. package/content/references/base/filter-system/fields-order-pagination.md +92 -146
  102. package/content/references/base/filter-system/index.md +25 -13
  103. package/content/references/base/filter-system/json-filtering.md +45 -184
  104. package/content/references/base/filter-system/list-operators.md +23 -53
  105. package/content/references/base/filter-system/logical-operators.md +63 -121
  106. package/content/references/base/filter-system/null-operators.md +34 -104
  107. package/content/references/base/filter-system/pattern-matching.md +40 -55
  108. package/content/references/base/filter-system/quick-reference.md +86 -198
  109. package/content/references/base/filter-system/range-operators.md +18 -46
  110. package/content/references/base/filter-system/tips.md +6 -6
  111. package/content/references/base/filter-system/use-cases.md +33 -15
  112. package/content/references/base/grpc-controllers.md +53 -17
  113. package/content/references/base/index.md +5 -3
  114. package/content/references/base/middlewares.md +11 -10
  115. package/content/references/base/models-reference.md +17 -17
  116. package/content/references/base/models.md +4 -3
  117. package/content/references/base/providers.md +8 -8
  118. package/content/references/base/repositories/advanced.md +224 -326
  119. package/content/references/base/repositories/index.md +19 -6
  120. package/content/references/base/repositories/mixins.md +5 -5
  121. package/content/references/base/repositories/relations.md +160 -293
  122. package/content/references/base/repositories/soft-deletable.md +16 -6
  123. package/content/references/base/secrets.md +17 -13
  124. package/content/references/base/services.md +6 -4
  125. package/content/references/configuration/environment-variables.md +31 -23
  126. package/content/references/configuration/index.md +6 -4
  127. package/content/references/index.md +1 -1
  128. package/content/references/utilities/duration.md +85 -0
  129. package/content/references/utilities/index.md +5 -1
  130. package/content/references/utilities/jsx-reference.md +11 -11
  131. package/content/references/utilities/jsx.md +2 -2
  132. package/content/references/utilities/module.md +78 -25
  133. package/content/references/utilities/request.md +2 -1
  134. package/content/references/utilities/retry.md +139 -0
  135. package/content/references/utilities/schema.md +2 -2
  136. package/content/references/utilities/statuses-reference.md +4 -4
  137. package/content/references/utilities/statuses.md +5 -5
  138. package/dist/mcp-server/index.js +0 -0
  139. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  140. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  141. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  142. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  143. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  144. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  145. package/package.json +17 -16
@@ -1,52 +1,105 @@
1
1
  ---
2
2
  title: Module Utility
3
- description: Pre-flight check that fails fast with an install instruction when an optional peer dependency is missing
3
+ description: Loads an optional peer dependency with a clear install error, and without letting the bundler see the specifier
4
4
  difficulty: beginner
5
- lastUpdated: 2026-07-16
5
+ lastUpdated: 2026-07-27
6
6
  ---
7
7
 
8
8
  # Module Utility
9
9
 
10
- A pre-flight check for optional peer dependencies. IGNIS helpers and components often depend on packages that are not bundled with the framework (`@connectrpc/connect` for gRPC, `@hono/swagger-ui` for the Swagger component, and so on). Calling `validateModule` before a lazy `import()` throws a clear, actionable error instead of a cryptic "Cannot find module" crash.
10
+ `ModuleUtility` loads an optional peer dependency. IGNIS components depend on packages the framework does not bundle - `nodemailer` for mail, `node-vault` for secrets. Loading one through `ModuleUtility` buys you two things: a missing package throws an install instruction instead of a cryptic crash, and the specifier stays invisible to `Bun.build`.
11
11
 
12
12
  ## In one example
13
13
 
14
+ This is how the secrets helper reaches `node-vault`:
15
+
14
16
  ```typescript
15
- import { validateModule } from '@venizia/ignis-helpers';
17
+ import { ModuleUtility } from '@venizia/ignis-helpers';
18
+
19
+ // Fails fast with a clear install instruction if the peer is absent.
20
+ ModuleUtility.assertInstalled({
21
+ scope: 'HashiCorpVaultHelper',
22
+ modules: ['node-vault'],
23
+ allowRegistered: true,
24
+ });
25
+
26
+ // The specifier is a parameter, so Bun.build never sees it.
27
+ const vault = await ModuleUtility.load({ module: 'node-vault' });
28
+ const client = vault.default({ endpoint, apiVersion: 'v1' });
29
+ ```
16
30
 
17
- const GRPC_MODULES = ['@connectrpc/connect'];
31
+ ## Methods
18
32
 
19
- export class MyGrpcController extends BaseGrpcController {
20
- async configure() {
21
- // Fails fast with a clear install instruction if the peer dep is absent
22
- await validateModule({ scope: MyGrpcController.name, modules: GRPC_MODULES });
33
+ | Method | Signature | What it does |
34
+ |--------|-----------|---------------|
35
+ | `load` | `load<T>(opts: { module: string }): Promise<T>` | Imports the module. Use this everywhere except a constructor |
36
+ | `loadSync` | `loadSync<T>(opts: { module: string }): T` | Same, without awaiting - for a constructor or any path that cannot be async |
37
+ | `assertInstalled` | `assertInstalled(opts: { modules: Array<string>; scope?: string; allowRegistered?: boolean }): void` | Presence check only. Resolves each module in order and throws on the first miss, without executing any of them |
38
+ | `register` | `register(opts: { modules: Record<string, AnyType> }): void` | Hands the framework peers the application already holds. `load` and `loadSync` serve a registered specifier from memory, with no filesystem lookup |
23
39
 
24
- const { ConnectRouter } = await import('@connectrpc/connect');
25
- // ... register routes
26
- }
27
- }
28
- ```
40
+ ### `allowRegistered`
29
41
 
30
- ## Functions
42
+ `assertInstalled` reports what is on disk. Set `allowRegistered` to count a `register` entry as present too.
31
43
 
32
- | Function | Signature | What it does |
33
- |----------|-----------|---------------|
34
- | `validateModule` | `validateModule(opts: { scope?: string; modules: Array<string> }): Promise<void>` | Resolves each module in `modules`, in order, via `require.resolve`. Throws an `ApplicationError` on the first one that cannot be found. |
44
+ Set it only where `load` or `loadSync` is what finally loads the module. A caller that resolves the specifier itself cannot see the registry - `pino.transport()` resolves inside a worker thread, and the gRPC adapter uses its own `createRequire`. For those, counting a registration as installed would replace a precise error with an opaque one.
35
45
 
36
46
  ## Error message format
37
47
 
38
48
  | `scope` | Message |
39
49
  |---------|---------|
40
- | provided | `[validateModule] <module> is required for <scope>. Please install '<module>'` |
41
- | omitted | `[validateModule] <module> is required. Please install '<module>'` |
50
+ | provided | `[ModuleUtility.<method>] <module> is required for <scope>. Please install '<module>'` |
51
+ | omitted | `[ModuleUtility.<method>] <module> is required. Please install '<module>'` |
52
+
53
+ ## Why not a plain import
54
+
55
+ `Bun.build` resolves a literal specifier at bundle time. A literal `require('mailgun.js')` anywhere reachable from an entry point forces every consumer who compiles a binary to install that peer or list it in `external` - even a consumer who never touches mail.
56
+
57
+ A `const` does not help. `minify: { syntax: true }` folds `const s = 'mailgun.js'; import(s)` straight back into a resolvable literal.
58
+
59
+ Only a specifier that crosses a function boundary survives as a runtime import. That is the whole reason `ModuleUtility` takes the module name as a parameter.
60
+
61
+ ## Compiled binaries
62
+
63
+ Runtime resolution needs a `node_modules` to resolve against. A `bun build --compile` binary usually runs without one - the deployment ships the executable and nothing else - so a peer the application genuinely installed is still unreachable at runtime, and the component that needs it dies at boot with the install hint.
64
+
65
+ **Check the component's options first.** Every component that reaches a peer this way takes it through its own options:
66
+
67
+ | Component | Option | You pass |
68
+ |---|---|---|
69
+ | Mail transports | `module` | the `nodemailer` or `mailgun.js` module |
70
+ | gRPC component | `module` | `{ connect, protocol }` from `@connectrpc/connect` |
71
+ | `HashiCorpVaultHelper` | `client` | an already-built vault client |
72
+ | `DotenvVaultHelper` | `decode` | your own decode function |
73
+
74
+ Prefer the option. It is typed, it lands where it is used, and no ordering can defeat it.
75
+
76
+ `register` is the fallback for a peer the framework reaches with no options seam in between. The static import is what pulls the library into the binary; `register` is what lets the framework find it there:
77
+
78
+ ```typescript
79
+ import { ModuleUtility } from '@venizia/ignis-helpers';
80
+ import * as vault from 'node-vault';
81
+
82
+ // At the entrypoint, before anything that reaches the peer runs.
83
+ ModuleUtility.register({ modules: { 'node-vault': vault } });
84
+ ```
85
+
86
+ The registry is keyed by specifier and the value is returned as-is: what you register under `node-vault` is exactly what `load({ module: 'node-vault' })` hands the caller. `import * as` gives the right shape for a CommonJS peer.
87
+
88
+ Register before the consumer runs. Nothing enforces that ordering, which is the reason to prefer an options seam wherever one exists.
89
+
90
+ `register` cannot help a consumer that resolves the specifier itself. A `pino` transport target loads inside a worker thread the registry never reaches, so `pino-pretty` and `pino-roll` must be installed on disk.
91
+
92
+ Registration is only worth it for the compiled-binary case. An application running from source resolves its peers from `node_modules` already.
42
93
 
43
94
  ## Notes
44
95
 
45
- - **Resolution is rooted at `process.cwd()/node_modules`** via Node's `createRequire`, so peer dependencies installed in the consuming application resolve correctly even though this utility ships inside `packages/helpers/dist/`.
46
- - **Evaluated in order - first miss wins.** With multiple `modules`, the loop stops at the first unresolved one and throws; later entries are never checked.
47
- - **`async` for call-site consistency only.** Resolution itself is synchronous (`require.resolve`); the function returns a `Promise<void>` so every call site can `await` it uniformly.
48
- - **Call it once, at startup - not per request.** Place it in an initialisation hook (`configure`, `binding`, `boot`), before the guarded `import()`.
49
- - **Recommended pattern:** declare the dependency as `peerDependenciesMeta` with `optional: true` in `package.json`, call `validateModule` at the top of the method that needs it, and pass the feature or class name as `scope` so the thrown message pinpoints the caller.
96
+ - **Resolution is rooted at `process.cwd()/node_modules`** via Node's `createRequire`, so peers installed in the consuming application resolve even though this utility ships inside `packages/helpers/dist/`.
97
+ - **`assertInstalled` stops at the first miss.** Later entries are never checked.
98
+ - **`assertInstalled` never executes the module** - it only locates the file. Reach for it when you want to fail at startup rather than on first use.
99
+ - **Call it once, at startup, not per request.** Place it in an initialisation hook (`configure`, `binding`, `boot`).
100
+ - **`register` wins over the filesystem.** `load` and `loadSync` check the registry first, so a registered peer is never resolved. `assertInstalled` ignores the registry unless you set `allowRegistered`.
101
+ - **`register` never logs.** It runs at the entrypoint of a compiled binary, which is exactly where no logger provider exists yet.
102
+ - **Recommended pattern:** declare the dependency in `peerDependenciesMeta` with `optional: true`, then load it through `ModuleUtility` and pass the feature or class name as `scope` so the thrown message pinpoints the caller.
50
103
 
51
104
  ## See also
52
105
 
@@ -71,7 +71,8 @@ export class FileController extends BaseRestController {
71
71
  - **`storage` defaults to `'memory'`**; `uploadDir` defaults to `'./uploads'` and is created recursively if it does not exist.
72
72
  - **`sanitizeFilename` is applied automatically** inside `createContentDispositionHeader` - callers do not need to sanitize twice. It also removes leading dots, collapses repeated dots, and strips `..` sequences to block directory traversal and hidden-file tricks.
73
73
  - **`createContentDispositionHeader` always emits both forms** (`filename="..."; filename*=UTF-8''...`) for maximum browser compatibility - older browsers read the ASCII fallback, modern ones read the UTF-8 form.
74
- - **`IRequestedRemark`** is a separately exported interface for describing a request: `{ id: string; url: string; method: string; [extra: string | symbol]: any }`. It is not consumed internally by `parseMultipartBody` or any other function on this page - it is a general-purpose shape for application code that needs to tag a request with an id, URL, method, and arbitrary extra fields.
74
+ - **`IRequestedRemark`** is a separately exported interface for describing a request: `{ id: string; url: string; method: string; [extra: string | symbol]: any }`.
75
+ - It is not consumed internally by `parseMultipartBody` or any other function on this page - it is a general-purpose shape for application code that needs to tag a request with an id, URL, method, and arbitrary extra fields.
75
76
 
76
77
  ## See also
77
78
 
@@ -0,0 +1,139 @@
1
+ ---
2
+ title: Retry Utility
3
+ description: Backoff-driven retry helpers - executeWithRetry retries on errors, executeWithRetryUntil retries until a result looks right
4
+ difficulty: intermediate
5
+ lastUpdated: 2026-07-18
6
+ ---
7
+
8
+ # Retry Utility
9
+
10
+ Two retry helpers. One retries when a call **throws**. The other retries when a call **succeeds but the result is not what you want yet**.
11
+
12
+ ## In one example
13
+
14
+ ```typescript
15
+ import { executeWithRetry, executeWithRetryUntil } from '@venizia/ignis-helpers';
16
+
17
+ // Retries because the call THREW
18
+ const data = await executeWithRetry({
19
+ operation: 'fetch-remote-config',
20
+ execution: () => fetchConfig(),
21
+ maxAttempts: 5,
22
+ });
23
+
24
+ // Retries because the result is not YET what we want
25
+ const order = await executeWithRetryUntil({
26
+ operation: 'wait-for-paid-order',
27
+ execution: () => orderRepository.findById({ id: orderId }),
28
+ until: result => result?.status === 'PAID',
29
+ maxAttempts: 5,
30
+ });
31
+ ```
32
+
33
+ ## Which one do I need?
34
+
35
+ | Function | Retries when | Typical use |
36
+ |---|---|---|
37
+ | `executeWithRetry` | `execution` throws | Flaky network calls, connection setup |
38
+ | `executeWithRetryUntil` | `until(result)` returns `false` | Polling until data is fresh or a job is done. Powers the repository [`retry` option](/references/base/repositories/advanced#read-retry-replica-lag). |
39
+
40
+ Both share the same backoff engine and the same habit: on exhaustion, log one `logger.warn` and hand back the LAST outcome.
41
+
42
+ ## `executeWithRetry`
43
+
44
+ ```typescript
45
+ const executeWithRetry: <T>(opts: {
46
+ operation: string;
47
+ execution: (context: { attempt: number; signal?: AbortSignal }) => ValueOrPromise<T>;
48
+ maxAttempts?: number; // default 3
49
+ maxTotalMs?: number; // total budget across attempts and sleeps
50
+ perAttemptTimeoutMs?: number; // race each attempt against a timeout
51
+ backoff?: IRetryBackoffOptions;
52
+ shouldRetry?: (context: IRetryContext) => boolean;
53
+ onRetry?: (context: IRetryContext & { nextDelayMs: number }) => ValueOrPromise<void>;
54
+ signal?: AbortSignal;
55
+ logger?: ILogger;
56
+ }) => Promise<T>;
57
+ ```
58
+
59
+ The rules:
60
+
61
+ - Every thrown error retries, unless `shouldRetry` returns `false` - then it rethrows immediately. Use this for permanent errors like a `400`.
62
+ - Out of attempts or budget? The LAST error is thrown.
63
+ - `signal` aborts between attempts and during sleeps. It is also passed to `execution` - a running promise cannot be cancelled from outside, so honor it inside if you can.
64
+
65
+ ## `executeWithRetryUntil`
66
+
67
+ ```typescript
68
+ const executeWithRetryUntil: <T>(opts: {
69
+ operation: string;
70
+ execution: (context: { attempt: number; signal?: AbortSignal }) => ValueOrPromise<T>;
71
+ until: (result: T) => boolean; // return true to stop: "the result is good"
72
+ maxAttempts?: number; // default 3
73
+ maxTotalMs?: number; // stop starting NEW attempts after this much time
74
+ backoff?: IRetryBackoffOptions;
75
+ signal?: AbortSignal;
76
+ logger?: ILogger;
77
+ }) => Promise<T>;
78
+ ```
79
+
80
+ The rules:
81
+
82
+ - A thrown error is never retried. It rethrows immediately. Only a successful call with a "not yet" result retries.
83
+ - Out of attempts or budget? The LAST result is returned as-is. No error.
84
+ - `maxTotalMs` never cuts a running read short. It only stops NEW attempts from starting. Zero or negative just means "no retries" - one call still runs.
85
+ - `maxAttempts` below `1` throws before anything runs.
86
+ - An aborted `signal` rejects the call - a cancelled caller does not want a stale result.
87
+
88
+ Use it for any polling: waiting for a job status to flip, for a downstream service to come up, for a replica to catch up.
89
+
90
+ ## Backoff and jitter
91
+
92
+ Both helpers wait between attempts using `IRetryBackoffOptions`:
93
+
94
+ ```typescript
95
+ interface IRetryBackoffOptions {
96
+ strategy?: 'fixed' | 'linear' | 'exponential' | 'schedule'; // default exponential
97
+ initialDelayMs?: number; // default 250
98
+ multiplier?: number; // exponential growth factor, default 2
99
+ maxDelayMs?: number; // cap before jitter, default 30000
100
+ scheduleMs?: readonly number[]; // required for 'schedule'
101
+ jitter?: 'none' | 'full' | 'equal'; // default full
102
+ }
103
+ ```
104
+
105
+ | Strategy | Delay for attempt N |
106
+ |---|---|
107
+ | `fixed` | `initialDelayMs` |
108
+ | `linear` | `initialDelayMs * N` |
109
+ | `exponential` | `initialDelayMs * multiplier ** (N - 1)` |
110
+ | `schedule` | `scheduleMs[N - 1]`, last entry repeats |
111
+
112
+ | Jitter | Effect |
113
+ |---|---|
114
+ | `none` | delay used as-is |
115
+ | `full` | random in `[0, delay)` |
116
+ | `equal` | random in `[delay/2, delay)` |
117
+
118
+ Prefer named constants? `RetryBackoffStrategies.EXPONENTIAL`, `RetryJitterModes.EQUAL`, etc.
119
+
120
+ > [!NOTE]
121
+ > These defaults (250ms, 30s cap) suit network retries. The repository `retry` option uses its own tighter defaults (50ms up to 500ms) - see [Read Retry](/references/base/repositories/advanced#read-retry-replica-lag).
122
+
123
+ ## Other exports
124
+
125
+ | Export | What it does |
126
+ |---|---|
127
+ | `runWithTimeout({ operation, timeoutMs, execution })` | Races `execution` against a timeout. Omitted or `<= 0` means no timeout. |
128
+ | `isRetryTimeoutError(error)` | `true` when the error is a timeout from `runWithTimeout`/`executeWithRetry`. |
129
+ | `computeBackoffDelayMs({ attempt, backoff })` | The delay both helpers use, exposed for your own loops. |
130
+
131
+ ## See also
132
+
133
+ - [Read Retry](/references/base/repositories/advanced#read-retry-replica-lag) - the repository `retry` option built on `executeWithRetryUntil`
134
+ - [Repository Read Retry changelog](/changelogs/2026-07-18-repository-read-retry) - what shipped and why
135
+ - [Utilities Overview](/references/utilities/)
136
+
137
+ **Files:**
138
+
139
+ - [`packages/helpers/src/utilities/retry.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/utilities/retry.utility.ts)
@@ -64,5 +64,5 @@ this.defineRoute({
64
64
 
65
65
  **Files:**
66
66
 
67
- - [`packages/core/src/utilities/schema.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/utilities/schema.utility.ts)
68
- - [`packages/core/src/base/models/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/models/common/types.ts)
67
+ - [`packages/core-server/src/utilities/schema.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/utilities/schema.utility.ts)
68
+ - [`packages/core-server/src/base/models/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/base/models/common/types.ts)
@@ -11,14 +11,14 @@ Exhaustive reference for `Statuses`, its specialized subclasses, and the `Bindin
11
11
 
12
12
  **Files:**
13
13
 
14
- - [`packages/core/src/common/statuses.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/common/statuses.ts)
15
- - [`packages/core/src/common/bindings.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/common/bindings.ts)
14
+ - [`packages/core-server/src/common/statuses.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/common/statuses.ts)
15
+ - [`packages/core-server/src/common/bindings.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/common/bindings.ts)
16
16
 
17
17
  ## `Statuses`
18
18
 
19
19
  A static class of `'NNN_NAME'` status code constants, grouped by an HTTP-inspired numeric prefix, plus `Set` groupings and validator methods.
20
20
 
21
- `Source ->` [`packages/core/src/common/statuses.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/common/statuses.ts)
21
+ `Source ->` [`packages/core-server/src/common/statuses.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/common/statuses.ts)
22
22
 
23
23
  ### Status code scheme
24
24
 
@@ -227,7 +227,7 @@ export class UserTypes {
227
227
 
228
228
  Organizes dependency-injection binding keys by artifact type. Distinct from `Statuses` - not a lifecycle catalog.
229
229
 
230
- `Source ->` [`packages/core/src/common/bindings.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/common/bindings.ts)
230
+ `Source ->` [`packages/core-server/src/common/bindings.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/common/bindings.ts)
231
231
 
232
232
  ### `BindingNamespaces`
233
233
 
@@ -7,7 +7,7 @@ lastUpdated: 2026-07-16
7
7
 
8
8
  # Statuses
9
9
 
10
- `Statuses` is a static catalog of lifecycle status codes - a shared vocabulary so every entity in an app uses the same strings for "draft", "active", "failed", and so on.
10
+ `Statuses` is a static catalog of lifecycle status codes - a shared vocabulary. Every entity in an app uses the same strings for "draft", "active", "failed", and so on.
11
11
 
12
12
  ## In one example
13
13
 
@@ -30,8 +30,8 @@ if (Statuses.isActive(job.status)) {
30
30
 
31
31
  - **HTTP-inspired numeric prefix.** Each status is a `'NNN_NAME'` string (e.g. `'302_SUCCESS'`). The leading digit groups statuses into six phases.
32
32
  - **Group sets for classification.** Every phase has a matching `*_SCHEME_SET` (a `Set<string>`) plus a validator method (`isActive`, `isFailed`, etc.) that just checks set membership.
33
- - **Specialized classes narrow the catalog.** `MigrationStatuses`, `CommonStatuses`, `UserStatuses`, and `RoleStatuses` each expose a small, named subset of `Statuses` values for a specific use case - they don't invent new status strings.
34
- - **A separate constant catalog for DI.** `BindingNamespaces` and `CoreBindings` (in the same file area) are unrelated to entity lifecycle - they're the namespace/key strings the container uses for dependency injection.
33
+ - **Specialized classes narrow the catalog.** `MigrationStatuses`, `CommonStatuses`, `UserStatuses`, and `RoleStatuses` each expose a small, named subset of `Statuses` values for a specific use case. None of them invent new status strings.
34
+ - **A separate constant catalog for DI.** `BindingNamespaces` and `CoreBindings` (in the same file area) are unrelated to entity lifecycle. They're the namespace/key strings the container uses for dependency injection.
35
35
 
36
36
  **The six phases**
37
37
 
@@ -118,5 +118,5 @@ await userRepository.create({
118
118
 
119
119
  **Files:**
120
120
 
121
- - [`packages/core/src/common/statuses.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/common/statuses.ts)
122
- - [`packages/core/src/common/bindings.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/common/bindings.ts)
121
+ - [`packages/core-server/src/common/statuses.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/common/statuses.ts)
122
+ - [`packages/core-server/src/common/bindings.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/common/bindings.ts)
File without changes
@@ -11,7 +11,7 @@ declare const OutputSchema: z.ZodObject<{
11
11
  }, z.core.$strip>;
12
12
  export declare class ListProjectFilesTool extends BaseTool<typeof InputSchema, typeof OutputSchema> {
13
13
  readonly id = "listProjectFiles";
14
- readonly description = "\nLists files and directories within the Ignis GitHub repository.\n\nPURPOSE:\nExplore the project structure, discover source code files, and navigate the monorepo.\nPrimary tool for understanding codebase layout before reading specific files.\n\nWHEN TO USE:\n- To understand overall project structure\n- To find location of specific modules (e.g., \"where are the controllers?\")\n- To get list of files in a directory before using viewSourceFile\n- To find configuration files, examples, or documentation\n\nWHEN NOT TO USE:\n- When searching for specific code patterns or keywords (use searchCode instead)\n- When you need to read a file's content (use viewSourceFile instead)\n- For documentation content (use searchDocs, listDocs, getDocContent instead)\n\nWORKFLOW:\n1. Start at root: listProjectFiles()\n2. Explore a package: listProjectFiles({ directoryPath: \"packages/core/src\" })\n3. Identify a file of interest\n4. Read the file: viewSourceFile({ filePath: \"packages/core/src/application.ts\" })\n";
14
+ readonly description = "\nLists files and directories within the Ignis GitHub repository.\n\nPURPOSE:\nExplore the project structure, discover source code files, and navigate the monorepo.\nPrimary tool for understanding codebase layout before reading specific files.\n\nWHEN TO USE:\n- To understand overall project structure\n- To find location of specific modules (e.g., \"where are the controllers?\")\n- To get list of files in a directory before using viewSourceFile\n- To find configuration files, examples, or documentation\n\nWHEN NOT TO USE:\n- When searching for specific code patterns or keywords (use searchCode instead)\n- When you need to read a file's content (use viewSourceFile instead)\n- For documentation content (use searchDocs, listDocs, getDocContent instead)\n\nWORKFLOW:\n1. Start at root: listProjectFiles()\n2. Explore a package: listProjectFiles({ directoryPath: \"packages/core-server/src\" })\n3. Identify a file of interest\n4. Read the file: viewSourceFile({ filePath: \"packages/core-server/src/application.ts\" })\n";
15
15
  readonly inputSchema: z.ZodObject<{
16
16
  directoryPath: z.ZodDefault<z.ZodString>;
17
17
  }, z.core.$strip>;
@@ -1 +1 @@
1
- {"version":3,"file":"list-project-files.tool.d.ts","sourceRoot":"","sources":["../../../../mcp-server/tools/github/list-project-files.tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAsCxC,QAAA,MAAM,WAAW;;iBAEf,CAAC;AAEH,QAAA,MAAM,YAAY;;;;;iBAOhB,CAAC;AAEH,qBAAa,oBAAqB,SAAQ,QAAQ,CAAC,OAAO,WAAW,EAAE,OAAO,YAAY,CAAC;IACzF,QAAQ,CAAC,EAAE,sBAAsB;IACjC,QAAQ,CAAC,WAAW,0/BAAoB;IACxC,QAAQ,CAAC,WAAW;;sBAAe;IACnC,QAAQ,CAAC,YAAY;;;;;sBAAgB;IAE/B,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;IAuBvF,OAAO;;;;;;;;CASR"}
1
+ {"version":3,"file":"list-project-files.tool.d.ts","sourceRoot":"","sources":["../../../../mcp-server/tools/github/list-project-files.tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAsCxC,QAAA,MAAM,WAAW;;iBAEf,CAAC;AAEH,QAAA,MAAM,YAAY;;;;;iBAOhB,CAAC;AAEH,qBAAa,oBAAqB,SAAQ,QAAQ,CAAC,OAAO,WAAW,EAAE,OAAO,YAAY,CAAC;IACzF,QAAQ,CAAC,EAAE,sBAAsB;IACjC,QAAQ,CAAC,WAAW,wgCAAoB;IACxC,QAAQ,CAAC,WAAW;;sBAAe;IACnC,QAAQ,CAAC,YAAY;;;;;sBAAgB;IAE/B,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;IAuBvF,OAAO;;;;;;;;CASR"}
@@ -25,9 +25,9 @@ WHEN NOT TO USE:
25
25
 
26
26
  WORKFLOW:
27
27
  1. Start at root: listProjectFiles()
28
- 2. Explore a package: listProjectFiles({ directoryPath: "packages/core/src" })
28
+ 2. Explore a package: listProjectFiles({ directoryPath: "packages/core-server/src" })
29
29
  3. Identify a file of interest
30
- 4. Read the file: viewSourceFile({ filePath: "packages/core/src/application.ts" })
30
+ 4. Read the file: viewSourceFile({ filePath: "packages/core-server/src/application.ts" })
31
31
  `;
32
32
  const DIRECTORY_PATH_DESCRIPTION = `
33
33
  The path to the directory you want to list, relative to the project root.
@@ -36,7 +36,7 @@ If omitted, it defaults to the root of the repository.
36
36
  EXAMPLES:
37
37
  - "." (or omitted) -> lists the root directory
38
38
  - "packages" -> lists the contents of the 'packages' directory
39
- - "packages/core/src" -> lists the source files of the core package
39
+ - "packages/core-server/src" -> lists the source files of the core package
40
40
  `;
41
41
  const InputSchema = zod_1.z.object({
42
42
  directoryPath: zod_1.z.string().default('.').describe(DIRECTORY_PATH_DESCRIPTION),
@@ -43,7 +43,7 @@ QUERY SYNTAX:
43
43
  - Multiple terms: "inject service" - files containing both terms
44
44
  - Exact phrase: "\\"dependency injection\\"" - exact phrase match
45
45
  - File extension: "extension:ts middleware" - only TypeScript files
46
- - Path filter: "path:packages/core createTool" - search in specific path
46
+ - Path filter: "path:packages/core-server createTool" - search in specific path
47
47
 
48
48
  EXAMPLES:
49
49
  - "class Application" - find Application class definition
@@ -32,7 +32,7 @@ The path to the package directory from the repository root.
32
32
  This directory must contain a 'package.json' file.
33
33
 
34
34
  EXAMPLES:
35
- - "packages/core"
35
+ - "packages/core-server"
36
36
  - "packages/helpers"
37
37
  - "examples/5-mins-qs"
38
38
  `;
@@ -32,7 +32,7 @@ HOW TO OBTAIN:
32
32
  - Navigate from root directory down to the specific file
33
33
 
34
34
  EXAMPLES:
35
- - "packages/core/src/application.ts"
35
+ - "packages/core-server/src/application.ts"
36
36
  - "examples/5-mins-qs/src/index.ts"
37
37
  - "package.json"
38
38
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@venizia/ignis-docs",
3
- "version": "0.2.1-0",
3
+ "version": "0.2.1-1",
4
4
  "description": "Interactive documentation site and MCP (Model Context Protocol) server for the Ignis Framework. Includes a VitePress-powered documentation site with guides, API references, and best practices. Ships an MCP server (CLI: ignis-docs-mcp) with 11 tools for AI assistants to search docs, browse source code, verify dependencies, and access real-time framework knowledge. Built with Mastra MCP SDK and Fuse.js fuzzy search.",
5
5
  "keywords": [
6
6
  "ai",
@@ -96,40 +96,41 @@
96
96
  "access": "public"
97
97
  },
98
98
  "scripts": {
99
- "force-update": "sh ./scripts/force-update.sh",
99
+ "docs:build": "sh ./scripts/docs-build.sh",
100
+ "docs:check-sidebar": "bun scripts/check-sidebar.ts",
101
+ "docs:clean": "sh ./scripts/docs-clean.sh",
102
+ "docs:dev": "vitepress dev site",
103
+ "docs:preview": "vitepress preview site",
100
104
  "eslint": "eslint --report-unused-disable-directives .",
105
+ "force-update": "sh ./scripts/force-update.sh",
101
106
  "lint": "bun run eslint && bun run prettier:cli",
102
107
  "lint:fix": "bun run eslint --fix && bun run prettier:fix",
103
- "prettier:cli": "prettier \"**/*.{js,ts}\" -l",
104
- "prettier:fix": "bun run prettier:cli --write",
105
- "docs:dev": "vitepress dev site",
106
- "docs:clean": "sh ./scripts/docs-clean.sh",
107
- "docs:build": "sh ./scripts/docs-build.sh",
108
- "docs:preview": "vitepress preview site",
109
- "rebuild": "sh ./scripts/mcp-rebuild.sh no-version",
110
- "mcp:rebuild": "bun run rebuild",
111
108
  "mcp:build": "sh ./scripts/mcp-build.sh",
112
109
  "mcp:clean": "sh ./scripts/mcp-clean.sh",
113
- "mcp:start": "bun run mcp-server/index.ts",
114
110
  "mcp:dev": "bun --watch mcp-server/index.ts",
115
- "prepublishOnly": "bun run mcp:rebuild"
111
+ "mcp:rebuild": "bun run rebuild",
112
+ "mcp:start": "bun run mcp-server/index.ts",
113
+ "prepublishOnly": "bun run mcp:rebuild",
114
+ "prettier:cli": "prettier \"**/*.{js,ts}\" -l",
115
+ "prettier:fix": "bun run prettier:cli --write",
116
+ "rebuild": "sh ./scripts/mcp-rebuild.sh no-version"
116
117
  },
117
118
  "dependencies": {
118
119
  "@mastra/core": "^1.51.0",
119
120
  "@mastra/mcp": "^1.14.0",
120
- "cytoscape": "^3.33.4",
121
+ "cytoscape": "^3.34.0",
121
122
  "cytoscape-cose-bilkent": "^4.1.0",
122
- "dayjs": "^1.11.20",
123
+ "dayjs": "^1.11.21",
123
124
  "debug": "^4.4.3",
124
125
  "fast-glob": "^3.3.3",
125
126
  "fuse.js": "^7.5.0",
126
127
  "gray-matter": "^4.0.3",
127
- "zod": "^4.3.6"
128
+ "zod": "^4.4.3"
128
129
  },
129
130
  "devDependencies": {
130
131
  "@braintree/sanitize-url": "^7.1.2",
131
132
  "@types/bun": "^1.3.14",
132
- "@venizia/dev-configs": "^0.1.0",
133
+ "@venizia/dev-configs": "^0.2.0-1",
133
134
  "eslint": "^10.7.0",
134
135
  "prettier": "^3.9.5",
135
136
  "tsc-alias": "^1.9.1",