@venizia/ignis-docs 0.2.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 (174) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +24 -13
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +8 -4
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +247 -153
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +109 -323
  26. package/content/extensions/components/authentication/api.md +489 -602
  27. package/content/extensions/components/authentication/errors.md +135 -498
  28. package/content/extensions/components/authentication/index.md +89 -801
  29. package/content/extensions/components/authentication/usage.md +231 -955
  30. package/content/extensions/components/authorization/api.md +991 -644
  31. package/content/extensions/components/authorization/errors.md +204 -208
  32. package/content/extensions/components/authorization/getting-started.md +227 -0
  33. package/content/extensions/components/authorization/index.md +88 -795
  34. package/content/extensions/components/authorization/usage.md +219 -528
  35. package/content/extensions/components/health-check.md +77 -243
  36. package/content/extensions/components/index.md +24 -90
  37. package/content/extensions/components/mail/api.md +553 -285
  38. package/content/extensions/components/mail/errors.md +76 -62
  39. package/content/extensions/components/mail/index.md +111 -463
  40. package/content/extensions/components/mail/usage.md +139 -173
  41. package/content/extensions/components/request-tracker.md +70 -173
  42. package/content/extensions/components/socket-io/api.md +462 -785
  43. package/content/extensions/components/socket-io/errors.md +49 -51
  44. package/content/extensions/components/socket-io/index.md +69 -372
  45. package/content/extensions/components/socket-io/usage.md +212 -105
  46. package/content/extensions/components/static-asset/api.md +461 -141
  47. package/content/extensions/components/static-asset/errors.md +121 -53
  48. package/content/extensions/components/static-asset/index.md +84 -606
  49. package/content/extensions/components/static-asset/usage.md +184 -299
  50. package/content/extensions/components/template/index.md +3 -3
  51. package/content/extensions/components/websocket/api.md +311 -404
  52. package/content/extensions/components/websocket/errors.md +47 -56
  53. package/content/extensions/components/websocket/index.md +75 -407
  54. package/content/extensions/components/websocket/usage.md +120 -338
  55. package/content/extensions/helpers/cron/index.md +52 -160
  56. package/content/extensions/helpers/crypto/index.md +67 -480
  57. package/content/extensions/helpers/crypto/reference.md +528 -0
  58. package/content/extensions/helpers/env/index.md +64 -178
  59. package/content/extensions/helpers/error/index.md +286 -196
  60. package/content/extensions/helpers/index.md +61 -47
  61. package/content/extensions/helpers/inversion/index.md +76 -550
  62. package/content/extensions/helpers/inversion/reference.md +530 -0
  63. package/content/extensions/helpers/kafka/admin.md +23 -3
  64. package/content/extensions/helpers/kafka/compile-binary.md +83 -52
  65. package/content/extensions/helpers/kafka/consumer.md +65 -28
  66. package/content/extensions/helpers/kafka/examples.md +46 -253
  67. package/content/extensions/helpers/kafka/index.md +55 -617
  68. package/content/extensions/helpers/kafka/producer.md +156 -22
  69. package/content/extensions/helpers/kafka/schema-registry.md +59 -79
  70. package/content/extensions/helpers/logger/hf-logger.md +220 -0
  71. package/content/extensions/helpers/logger/index.md +78 -552
  72. package/content/extensions/helpers/logger/pino.md +105 -0
  73. package/content/extensions/helpers/logger/reference.md +937 -0
  74. package/content/extensions/helpers/network/api.md +276 -195
  75. package/content/extensions/helpers/network/index.md +87 -524
  76. package/content/extensions/helpers/queue/index.md +80 -897
  77. package/content/extensions/helpers/queue/reference.md +494 -0
  78. package/content/extensions/helpers/redis/index.md +86 -640
  79. package/content/extensions/helpers/redis/reference.md +784 -0
  80. package/content/extensions/helpers/secrets/index.md +136 -0
  81. package/content/extensions/helpers/socket-io/api.md +325 -204
  82. package/content/extensions/helpers/socket-io/index.md +79 -429
  83. package/content/extensions/helpers/storage/api.md +584 -464
  84. package/content/extensions/helpers/storage/index.md +80 -573
  85. package/content/extensions/helpers/types/index.md +75 -495
  86. package/content/extensions/helpers/types/reference.md +689 -0
  87. package/content/extensions/helpers/uid/index.md +176 -189
  88. package/content/extensions/helpers/websocket/api.md +366 -214
  89. package/content/extensions/helpers/websocket/index.md +68 -503
  90. package/content/extensions/helpers/worker-thread/index.md +62 -396
  91. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  92. package/content/extensions/index.md +38 -39
  93. package/content/extensions/src-details/mcp-server.md +96 -548
  94. package/content/guides/core-concepts/persistent/datasources.md +2 -2
  95. package/content/guides/core-concepts/persistent/index.md +5 -1
  96. package/content/guides/core-concepts/persistent/models.md +1 -1
  97. package/content/guides/core-concepts/persistent/pglite.md +218 -0
  98. package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
  99. package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
  100. package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
  101. package/content/guides/core-concepts/persistent/sqlite.md +296 -0
  102. package/content/guides/core-concepts/persistent/transactions.md +12 -11
  103. package/content/guides/core-concepts/secrets-vault.md +177 -0
  104. package/content/guides/core-concepts/services.md +1 -1
  105. package/content/guides/get-started/5-minute-quickstart.md +59 -206
  106. package/content/guides/get-started/philosophy.md +135 -670
  107. package/content/guides/get-started/setup.md +53 -74
  108. package/content/guides/migrations/redis-helpers-migration.md +5 -4
  109. package/content/guides/migrations/scoped-rbac-migration.md +5 -5
  110. package/content/guides/migrations/unified-connectors-migration.md +8 -7
  111. package/content/guides/tutorials/ecommerce-api.md +3 -8
  112. package/content/guides/tutorials/realtime-chat.md +1 -1
  113. package/content/references/base/application.md +64 -22
  114. package/content/references/base/components.md +3 -3
  115. package/content/references/base/connectors.md +79 -136
  116. package/content/references/base/controllers.md +12 -12
  117. package/content/references/base/datasources-reference.md +600 -0
  118. package/content/references/base/datasources.md +84 -444
  119. package/content/references/base/dependency-injection.md +25 -46
  120. package/content/references/base/filter-system/application-usage.md +95 -123
  121. package/content/references/base/filter-system/array-operators.md +33 -43
  122. package/content/references/base/filter-system/comparison-operators.md +55 -63
  123. package/content/references/base/filter-system/default-filter.md +144 -350
  124. package/content/references/base/filter-system/fields-order-pagination.md +116 -148
  125. package/content/references/base/filter-system/index.md +114 -253
  126. package/content/references/base/filter-system/json-filtering.md +52 -181
  127. package/content/references/base/filter-system/list-operators.md +28 -44
  128. package/content/references/base/filter-system/logical-operators.md +69 -114
  129. package/content/references/base/filter-system/null-operators.md +41 -98
  130. package/content/references/base/filter-system/pattern-matching.md +48 -51
  131. package/content/references/base/filter-system/quick-reference.md +93 -196
  132. package/content/references/base/filter-system/range-operators.md +22 -38
  133. package/content/references/base/filter-system/tips.md +70 -133
  134. package/content/references/base/filter-system/use-cases.md +173 -232
  135. package/content/references/base/grpc-controllers.md +53 -17
  136. package/content/references/base/index.md +5 -3
  137. package/content/references/base/middlewares.md +43 -28
  138. package/content/references/base/models-reference.md +886 -0
  139. package/content/references/base/models.md +81 -1452
  140. package/content/references/base/providers.md +8 -8
  141. package/content/references/base/repositories/advanced.md +281 -419
  142. package/content/references/base/repositories/index.md +84 -644
  143. package/content/references/base/repositories/mixins.md +25 -21
  144. package/content/references/base/repositories/relations.md +194 -375
  145. package/content/references/base/repositories/soft-deletable.md +67 -55
  146. package/content/references/base/secrets.md +267 -0
  147. package/content/references/base/services.md +8 -6
  148. package/content/references/configuration/environment-variables.md +73 -21
  149. package/content/references/configuration/index.md +51 -31
  150. package/content/references/index.md +1 -1
  151. package/content/references/quick-reference.md +3 -16
  152. package/content/references/utilities/crypto.md +35 -76
  153. package/content/references/utilities/date.md +33 -73
  154. package/content/references/utilities/duration.md +85 -0
  155. package/content/references/utilities/index.md +6 -2
  156. package/content/references/utilities/jsx-reference.md +298 -0
  157. package/content/references/utilities/jsx.md +82 -525
  158. package/content/references/utilities/module.md +81 -61
  159. package/content/references/utilities/parse.md +34 -64
  160. package/content/references/utilities/performance.md +33 -58
  161. package/content/references/utilities/promise.md +28 -62
  162. package/content/references/utilities/request.md +58 -218
  163. package/content/references/utilities/retry.md +139 -0
  164. package/content/references/utilities/schema.md +43 -137
  165. package/content/references/utilities/statuses-reference.md +361 -0
  166. package/content/references/utilities/statuses.md +63 -667
  167. package/dist/mcp-server/index.js +0 -0
  168. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  169. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
  170. package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
  171. package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
  172. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
  173. package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
  174. package/package.json +24 -23
@@ -1,272 +1,259 @@
1
+ ---
2
+ title: UID
3
+ description: Two ID generators - time-sortable Snowflake IDs, and short random IDs a human can read back
4
+ difficulty: beginner
5
+ ---
6
+
1
7
  # UID
2
8
 
3
- Snowflake ID generator with Base62 encoding for unique, time-sortable distributed identifiers.
9
+ Two generators, and they answer different questions.
10
+
11
+ | Helper | Reads back as | Pick it when |
12
+ |---|---|---|
13
+ | `SnowflakeUidHelper` | Timestamp, worker ID, sequence | The ID is a primary key and you want insert order for free |
14
+ | `OpaqueUidHelper` | Nothing | The ID leaves your system - a human reads it, or a stranger sees it |
4
15
 
5
- ## Quick Reference
16
+ A Snowflake ID is transparent by design. `parseId()` gives back the exact millisecond it was minted and the worker that minted it. That is what you want in a log, and what you do not want printed on an invoice a customer keeps.
6
17
 
7
- | Item | Value |
8
- |------|-------|
9
- | **Package** | `@venizia/ignis-helpers` |
10
- | **Class** | `SnowflakeUidHelper` |
11
- | **Extends** | `BaseHelper` |
12
- | **Runtimes** | Both |
18
+ An opaque ID gives nothing back. You trade the ordering for that, and for a short ID a human can read out loud.
13
19
 
14
- #### Import Paths
20
+ ## In one example
15
21
 
16
22
  ```typescript
17
- import { SnowflakeUidHelper, SnowflakeConfig } from '@venizia/ignis-helpers';
23
+ import { SnowflakeUidHelper } from '@venizia/ignis-helpers';
18
24
 
19
- // Types
20
- import type { IIdGeneratorOptions, ISnowflakeParsedId } from '@venizia/ignis-helpers';
25
+ const generator = new SnowflakeUidHelper(); // workerId: 199, epoch: 2025-01-01 UTC
26
+ const id = generator.nextId();
27
+ // => e.g. "9du1sJXO88"
21
28
  ```
22
29
 
23
- #### Snowflake ID Structure (70 bits)
24
-
25
- | Component | Bits | Range | Purpose |
26
- |-----------|------|-------|---------|
27
- | Timestamp | 48 | ~8,919 years | Time since epoch |
28
- | Worker ID | 10 | 0--1023 | Unique worker identifier |
29
- | Sequence | 12 | 0--4095 | IDs per millisecond |
30
-
31
- #### Key Specifications
32
-
33
- | Spec | Value |
34
- |------|-------|
35
- | Base62 Output | 10--12 characters |
36
- | Throughput | 4,096,000 IDs/second/worker |
37
- | Max Workers | 1024 |
38
- | Lifespan | Until ~10,944 AD |
39
- | Default Epoch | 2025-01-01 00:00:00 UTC |
30
+ Use one `SnowflakeUidHelper` instance per worker process. `nextId()` returns a URL-safe Base62 string, and it sorts in the same order the IDs were generated.
40
31
 
41
- ## Creating an Instance
32
+ ## How it works
42
33
 
43
- `SnowflakeUidHelper` extends `BaseHelper`, providing scoped logging via `LoggerFactory`.
34
+ - **The ID packs three fields into 70 bits.** `nextSnowflake()` shifts and OR's together a timestamp, a worker ID, and a sequence - see the bit layout below.
35
+ - **Sorting the ID sorts by generation time.** This holds for the raw `bigint`, and for the Base62 string.
36
+ - **The sequence resets every millisecond, per worker.** It increments within the millisecond and wraps at 4096.
37
+ - **A wrap forces a busy-wait for the next millisecond.** That caps throughput at 4,096,000 IDs per second per worker.
38
+ - **A small backward clock drift busy-waits.** Up to 100ms (`MAX_CLOCK_BACKWARD_MS`), the generator waits for the clock to catch up, and logs a warning.
39
+ - **A larger drift throws instead.** IGNIS refuses to risk generating a duplicate ID.
40
+ - **`encodeBase62` / `decodeBase62` are a bigint <-> string codec.** They don't know about the Snowflake layout.
41
+ - **`parseId()` layers on top of the codec.** It calls `decodeBase62()`, then the three `extract*` calls, using the same instance's `epoch` and bit-shift constants.
42
+ - **An expiry warning logs once the epoch nears its 48-bit limit.** The timestamp field overflows about 8,919 years after the configured epoch. A warning starts logging 10 years before that.
44
43
 
45
- ```typescript
46
- import { SnowflakeUidHelper } from '@venizia/ignis-helpers';
44
+ **Snowflake layout (70 bits)**
47
45
 
48
- // Use defaults (workerId: 199, epoch: 2025-01-01 00:00:00 UTC)
49
- const generator = new SnowflakeUidHelper();
50
-
51
- // Or with custom values
52
- const customGenerator = new SnowflakeUidHelper({
53
- workerId: 123,
54
- epoch: BigInt(1735689600000),
55
- });
56
- ```
46
+ | Component | Bits | Range | Purpose |
47
+ |-----------|------|-------|---------|
48
+ | Timestamp | 48 | ~8,919 years | Milliseconds since `epoch` |
49
+ | Worker ID | 10 | 0-1023 | Set per instance, must be unique across a deployment |
50
+ | Sequence | 12 | 0-4095 | IDs generated within the same millisecond |
57
51
 
58
- #### `IIdGeneratorOptions`
52
+ **`IIdGeneratorOptions`**
59
53
 
60
54
  | Option | Type | Default | Description |
61
55
  |--------|------|---------|-------------|
62
- | `workerId` | `number` | `199` | Worker ID (0--1023). Must be unique per instance in distributed deployments |
63
- | `epoch` | `bigint` | `BigInt(1735689600000)` | Custom epoch timestamp in milliseconds since Unix epoch. Must be positive and in the past |
56
+ | `workerId` | `number` | `199` | Integer, `0`-`1023` |
57
+ | `epoch` | `bigint` | `BigInt(1735689600000)` (2025-01-01 00:00:00 UTC) | Positive, must be in the past |
64
58
 
65
- > [!TIP]
66
- > Use unique worker IDs in distributed systems to prevent ID collisions, and keep the epoch consistent across all instances to ensure proper ID ordering.
59
+ ## Common tasks
67
60
 
68
- #### `SnowflakeConfig` Constants
61
+ ### Generate an ID
62
+
63
+ `nextId()` is the common case - a Base62 string, 10-12 characters. `nextSnowflake()` returns the raw `bigint`, for arithmetic or bit-level access.
69
64
 
70
65
  ```typescript
71
- import { SnowflakeConfig } from '@venizia/ignis-helpers';
72
-
73
- SnowflakeConfig.DEFAULT_EPOCH // BigInt(1735689600000) - 2025-01-01 00:00:00 UTC
74
- SnowflakeConfig.MAX_WORKER_ID // 1023n
75
- SnowflakeConfig.MAX_SEQUENCE // 4095n
76
- SnowflakeConfig.TIMESTAMP_SHIFT // 22n
77
- SnowflakeConfig.WORKER_ID_SHIFT // 12n
78
-
79
- // Bit widths
80
- SnowflakeConfig.TIMESTAMP_BITS // 48n
81
- SnowflakeConfig.WORKER_ID_BITS // 10n
82
- SnowflakeConfig.SEQUENCE_BITS // 12n
83
-
84
- // Safety thresholds
85
- SnowflakeConfig.MAX_CLOCK_BACKWARD_MS // 100n (busy-wait tolerance)
86
- SnowflakeConfig.MAX_TIMESTAMP_MS // (1n << 48n) - 1n (~8,919 years)
87
- SnowflakeConfig.WARNING_THRESHOLD_MS // ~8,909 years (warns 10 years before expiry)
66
+ const id = generator.nextId(); // "9du1sJXO88"
67
+ const raw = generator.nextSnowflake(); // 130546360012247045n
88
68
  ```
89
69
 
90
- ## Usage
91
-
92
- ### Generating IDs
93
-
94
- Generate unique Snowflake IDs as either compact Base62 strings or raw bigints.
70
+ ### Parse an ID back into its components
95
71
 
96
72
  ```typescript
97
- // Generate Base62 encoded ID (recommended for most use cases)
98
- const id = generator.nextId();
99
- // => e.g., "9du1sJXO88"
100
-
101
- // Generate raw Snowflake ID as bigint
102
- const snowflakeId = generator.nextSnowflake();
103
- // => e.g., 130546360012247045n
73
+ const parsed = generator.parseId('9du1sJXO88');
74
+ // => { raw: 130546360012247045n, timestamp: Date, workerId: 199, sequence: 0 }
104
75
  ```
105
76
 
106
- > [!TIP]
107
- > Use `nextId()` for most cases -- it returns a compact Base62 string (10--12 chars) that works well as database primary keys and URL-safe identifiers. Use `nextSnowflake()` only when you need the raw bigint for arithmetic or bit-level operations.
77
+ ### Extract a single component from a raw ID
108
78
 
109
- #### Clock Drift Handling
79
+ ```typescript
80
+ const timestamp = generator.extractTimestamp(raw);
81
+ const workerId = generator.extractWorkerId(raw);
82
+ const sequence = generator.extractSequence(raw);
83
+ ```
110
84
 
111
- When `nextSnowflake()` detects the system clock has moved backward:
112
- - Drifts up to 100ms are handled automatically by busy-waiting until the clock catches up.
113
- - Drifts exceeding 100ms throw an error to protect ID uniqueness.
114
- - A warning is logged whenever any backward clock movement is detected.
85
+ ### Run one generator per worker in a distributed deployment
115
86
 
116
- #### Sequence Exhaustion
87
+ Give each process a unique `workerId`. Keep `epoch` identical across every process, so ID ordering stays meaningful.
117
88
 
118
- If more than 4096 IDs are requested in a single millisecond from the same worker, the generator busy-waits until the next millisecond before continuing. This is transparent to the caller.
89
+ ```typescript
90
+ const generator = new SnowflakeUidHelper({ workerId: Number(process.env.WORKER_ID) });
91
+ ```
119
92
 
120
- ### Base62 Encoding and Decoding
93
+ ### Decode Base62 defensively
121
94
 
122
- Encode any bigint to a compact, URL-safe Base62 string using the alphabet `0-9A-Za-z`, or decode back to the original bigint.
95
+ `decodeBase62()` throws on any character outside `0-9A-Za-z` - including a leading space or a URL-decoded `+`.
123
96
 
124
97
  ```typescript
125
- // Encode
126
- const encoded = generator.encodeBase62(130546360012247045n);
127
- // => "9du1sJXO88"
128
-
129
- // Decode
130
- const decoded = generator.decodeBase62('9du1sJXO88');
131
- // => 130546360012247045n
98
+ try {
99
+ const raw = generator.decodeBase62(userSuppliedId);
100
+ } catch (error) {
101
+ // not a valid Base62 ID
102
+ }
132
103
  ```
133
104
 
134
- > [!WARNING]
135
- > `decodeBase62()` throws if the input contains characters outside the Base62 alphabet (`0-9`, `A-Z`, `a-z`). Watch for URL-encoded strings or accidental whitespace.
136
-
137
- ### Parsing IDs
105
+ ## OpaqueUidHelper
138
106
 
139
- Extract the embedded timestamp, worker ID, and sequence from a generated ID.
107
+ ```typescript
108
+ import { OpaqueUidHelper } from '@venizia/ignis-helpers';
140
109
 
141
- #### Parse a Base62 ID
110
+ const generator = new OpaqueUidHelper({
111
+ prefix: { enable: true, value: 'INV' },
112
+ delimiter: { enable: true, value: '-' },
113
+ length: 6,
114
+ });
142
115
 
143
- ```typescript
144
- const parsed = generator.parseId('9du1sJXO88');
145
- // => {
146
- // raw: 130546360012247045n,
147
- // timestamp: Date,
148
- // workerId: 199,
149
- // sequence: 0
150
- // }
116
+ generator.nextId();
117
+ // => e.g. "INV-7K2MQ9"
151
118
  ```
152
119
 
153
- #### `ISnowflakeParsedId`
120
+ `length` counts the body only. The prefix and the delimiter sit in front of it, so that ID is 10 characters.
154
121
 
155
- ```typescript
156
- interface ISnowflakeParsedId {
157
- raw: bigint;
158
- timestamp: Date;
159
- workerId: number;
160
- sequence: number;
161
- }
162
- ```
122
+ ### Options
163
123
 
164
- #### Extract Individual Components
124
+ | Option | Type | Default | Meaning |
125
+ |---|---|---|---|
126
+ | `prefix` | `{ enable, value? }` | disabled | Leads the ID. `value` survives being disabled, so you can toggle it back on |
127
+ | `delimiter` | `{ enable, value? }` | disabled, `_` | Separates prefix from body. Requires a prefix |
128
+ | `length` | `number` | `6` | Characters in the body |
129
+ | `caseForm` | `'upper' \| 'lower' \| 'mixed'` | `'upper'` | Folds the alphabet before sampling |
130
+ | `alphabet` | `string` | `UidAlphabets.CROCKFORD` | The characters to draw from |
131
+ | `exclude` | `string` | `''` | Removed from `alphabet` |
165
132
 
166
- ```typescript
167
- const snowflakeId = generator.nextSnowflake();
133
+ Four alphabets ship with it:
168
134
 
169
- // Extract timestamp
170
- const timestamp = generator.extractTimestamp(snowflakeId);
171
- // => Date object
135
+ | Alphabet | Size | Drops | Use for |
136
+ |---|---|---|---|
137
+ | `CROCKFORD` | 32 | Lowercase, `I` `L` `O` `U` | IDs a human reads, types, or says aloud |
138
+ | `BASE58` | 58 | `0` `O` `I` `l` | IDs in a URL or a log |
139
+ | `NO_VOWEL` | 50 | `BASE58` plus every vowel | Customer-facing IDs - no word can form |
140
+ | `BASE62` | 62 | Nothing | Machines only |
172
141
 
173
- // Extract worker ID
174
- const workerId = generator.extractWorkerId(snowflakeId);
175
- // => 199
142
+ `getAlphabet()` returns the set actually in use, after the case fold and every exclusion. That is what `length` is measured against, so read it before calculating how many IDs a length affords you.
176
143
 
177
- // Extract sequence
178
- const sequence = generator.extractSequence(snowflakeId);
179
- // => 0-4095
144
+ ### Six characters needs a unique index
180
145
 
181
- // Get current instance's worker ID
182
- const currentWorkerId = generator.getWorkerId();
183
- // => 199
184
- ```
146
+ Six Crockford characters is 2^30 combinations, the size of an airline record locator. That works for airlines because of three things, not because 2^30 is large:
185
147
 
186
- ## API Summary
148
+ 1. **Scoped.** A record locator is unique per carrier, not worldwide.
149
+ 2. **Recycled.** The airline reuses it once the trip is over.
150
+ 3. **Retried.** The reservation system regenerates on conflict.
187
151
 
188
- | Method | Signature | Description |
189
- |--------|-----------|-------------|
190
- | `nextId` | `nextId(): string` | Generate a Base62-encoded Snowflake ID (10--12 chars) |
191
- | `nextSnowflake` | `nextSnowflake(): bigint` | Generate a raw 70-bit Snowflake ID |
192
- | `encodeBase62` | `encodeBase62(num: bigint): string` | Encode a bigint to Base62 string |
193
- | `decodeBase62` | `decodeBase62(str: string): bigint` | Decode a Base62 string to bigint |
194
- | `parseId` | `parseId(base62Id: string): ISnowflakeParsedId` | Parse a Base62 ID into its components |
195
- | `extractTimestamp` | `extractTimestamp(id: bigint): Date` | Extract the timestamp from a raw Snowflake ID |
196
- | `extractWorkerId` | `extractWorkerId(id: bigint): number` | Extract the worker ID from a raw Snowflake ID |
197
- | `extractSequence` | `extractSequence(id: bigint): number` | Extract the sequence number from a raw Snowflake ID |
198
- | `getWorkerId` | `getWorkerId(): number` | Get the current instance's worker ID |
152
+ > [!WARNING]
153
+ > At the default length, a 1% chance of at least one collision arrives at roughly 4,600 IDs in one space, and 50% at roughly 38,000.
199
154
 
200
- ## Troubleshooting
155
+ A `prefix` PARTITIONS the space. `INV-7K2MQ9` can never collide with `CUS-7K2MQ9`, so ten entity types get ten separate spaces instead of sharing one. It does not make an ID unique inside its own space - within `INV`, the numbers above still hold.
201
156
 
202
- ### "Worker ID must be between 0 and 1023"
157
+ So do both. Give each entity type its own `prefix`, and put the column behind a unique index.
203
158
 
204
- **Cause:** The `workerId` option passed to the constructor is negative or exceeds 1023 (10-bit maximum).
159
+ ### Reject an ID you already hold
205
160
 
206
- **Fix:** Ensure the worker ID is within the valid range (0--1023):
161
+ `nextId()` takes a callback. Return `true` to accept the ID, `false` to draw another.
207
162
 
208
163
  ```typescript
209
- // Wrong
210
- new SnowflakeUidHelper({ workerId: 2000 });
164
+ const issued = new Set<string>();
165
+ const generator = new OpaqueUidHelper({ length: 8 });
211
166
 
212
- // Correct
213
- new SnowflakeUidHelper({ workerId: 1 });
167
+ const id = generator.nextId({
168
+ isAvailable: candidate => !issued.has(candidate),
169
+ });
170
+ issued.add(id);
214
171
  ```
215
172
 
216
- ### "Epoch must be a positive number"
173
+ Every argument is optional, including the options object itself - `nextId()` alone is the common case.
217
174
 
218
- **Cause:** The `epoch` option is zero or negative.
175
+ **`nextId()` options**
219
176
 
220
- **Fix:** Provide a valid epoch as a positive bigint representing milliseconds since Unix epoch:
177
+ | Option | Type | Default | Meaning |
178
+ |---|---|---|---|
179
+ | `prefix` | `string` | The configured prefix | Replaces it for this call |
180
+ | `isAvailable` | `(id: string) => ValueOrPromise<boolean>` | none | `true` accepts, `false` draws again |
181
+ | `maxAttempts` | `number` | `10` | Draws before it throws. Requires `isAvailable` |
182
+
183
+ The callback sees the finished ID, prefix and delimiter included - the string the column stores.
184
+
185
+ **The callback's return type decides `nextId`'s.** A synchronous check keeps the call synchronous, so no promise is allocated. An asynchronous one makes it a promise:
221
186
 
222
187
  ```typescript
223
- // Wrong
224
- new SnowflakeUidHelper({ epoch: BigInt(0) });
188
+ const id = generator.nextId({ isAvailable: candidate => !issued.has(candidate) });
189
+ // string
225
190
 
226
- // Correct
227
- new SnowflakeUidHelper({ epoch: BigInt(1735689600000) });
191
+ const id = await generator.nextId({
192
+ isAvailable: async candidate => !(await invoiceRepository.exists(candidate)),
193
+ });
194
+ // Promise<string>
228
195
  ```
229
196
 
230
- ### "Epoch cannot be in the future"
197
+ > [!WARNING]
198
+ > An asynchronous check does not make an ID safe. Against a database it is a read and then a write with a gap between them, and another request can take the ID inside that gap. `isAvailable` lowers the collision rate; the unique index is what removes it.
231
199
 
232
- **Cause:** The `epoch` option is set to a timestamp later than the current time. A future epoch would produce negative timestamp offsets in generated IDs.
200
+ `maxAttempts` bounds the redraws. Repeated rejection means the space is full, and the helper throws with the length and alphabet size rather than looping.
233
201
 
234
- **Fix:** Use a past date as the epoch. The default (`2025-01-01 00:00:00 UTC`) is recommended unless you have a specific reason to change it.
202
+ ### Regenerate on conflict, never check first
235
203
 
236
- ### "Clock moved backward by Xms. Refusing to generate ID."
204
+ Insert and catch the violation. Checking whether an ID exists and then inserting it leaves a window where another request takes the same ID between the two statements.
237
205
 
238
- **Cause:** The system clock moved backward by more than 100ms, typically caused by NTP time synchronization. Small drifts (up to 100ms) are handled by busy-waiting, but larger jumps are rejected to protect ID uniqueness.
206
+ ```typescript
207
+ import { executeWithRetry, OpaqueUidHelper } from '@venizia/ignis-helpers';
239
208
 
240
- **Fix:** Ensure your system clock is stable. If running in containers, verify the host clock is not being adjusted aggressively. There is no code-level workaround -- the error protects against duplicate IDs.
209
+ const generator = new OpaqueUidHelper({
210
+ prefix: { enable: true, value: 'INV' },
211
+ delimiter: { enable: true, value: '-' },
212
+ });
241
213
 
242
- ### "Invalid Base62 character: X"
214
+ const invoice = await executeWithRetry({
215
+ operation: 'createInvoice',
216
+ maxAttempts: 5,
217
+ // A fresh ID per attempt - retrying with the same one would collide forever.
218
+ execution: () => invoiceRepository.create({ data: { id: generator.nextId(), total } }),
219
+ shouldRetry: context => isUniqueViolation(context.error),
220
+ });
221
+ ```
243
222
 
244
- **Cause:** `decodeBase62()` encountered a character outside the Base62 alphabet (`0-9`, `A-Z`, `a-z`).
223
+ Five attempts is generous. At the default length a second collision on the same insert has a probability in the millionths.
245
224
 
246
- **Fix:** Ensure the input string only contains valid Base62 characters:
225
+ Or raise `length` - every extra character multiplies the space by 32.
247
226
 
248
- ```typescript
249
- // Wrong
250
- generator.decodeBase62('9du1sJ+O88'); // '+' is not Base62
251
- generator.decodeBase62(' 9du1sJXO88'); // leading space
227
+ ### What the constructor refuses
252
228
 
253
- // Correct
254
- generator.decodeBase62('9du1sJXO88');
255
- ```
229
+ Each of these fails loudly at construction, because each one fails silently at runtime:
230
+
231
+ | Configuration | Why it is refused |
232
+ |---|---|
233
+ | `delimiter` enabled, `prefix` disabled | The delimiter would lead the ID and separate nothing |
234
+ | A delimiter character that is in the alphabet | The ID could not be split back into prefix and body |
235
+ | A `prefix` that disagrees with `caseForm` | A lowercase prefix on an uppercase ID defeats the reason for choosing one case |
236
+ | `caseForm` folding a two-case alphabet | Uppercasing `BASE58` brings `I` and `O` back - the pair it drops so `1` and `0` stay readable |
237
+ | An alphabet under 16 characters | `exclude` has eaten it |
238
+ | `maxAttempts` passed without `isAvailable` | Nothing rejects a draw, so the retry it configures cannot happen |
239
+
240
+ ### Randomness
241
+
242
+ `OpaqueUidHelper` draws from `crypto.getRandomValues`, and rejects samples that fall outside the alphabet rather than wrapping them. A `byte % 58` would favour the first 24 characters by about 1.5%, forever, and no caller could see it.
256
243
 
257
- ## See Also
244
+ Unlike `crypto.randomUUID`, `getRandomValues` works outside a secure context. The generator runs on a plain-http origin and inside a browser Worker.
258
245
 
259
- - **Related Concepts:**
260
- - [Models](/guides/core-concepts/persistent/models) - Using UIDs as primary keys
261
- - [Services](/guides/core-concepts/services) - Generating unique IDs in services
246
+ ## See also
262
247
 
263
- - **Other Helpers:**
264
- - [Helpers Index](../index) - All available helpers
265
- - [Crypto Helper](../crypto/) - For cryptographic random values
248
+ - [Models](/guides/core-concepts/persistent/models) - using UIDs as primary keys
249
+ - [Services](/guides/core-concepts/services) - generating unique IDs in services
250
+ - [Helpers Overview](/extensions/helpers/) - all available helpers
251
+ - [Crypto Helper](/extensions/helpers/crypto/) - cryptographic random values
252
+ - [Snowflake ID](https://en.wikipedia.org/wiki/Snowflake_ID) - the algorithm this helper implements
266
253
 
267
- - **External Resources:**
268
- - [Snowflake ID](https://en.wikipedia.org/wiki/Snowflake_ID) - Snowflake algorithm explained
269
- - [Base62 Encoding](https://en.wikipedia.org/wiki/Base62) - Base62 encoding overview
254
+ **Files:**
270
255
 
271
- - **Best Practices:**
272
- - [Data Modeling](/best-practices/data-modeling) - ID generation strategies
256
+ - [`packages/helpers/src/modules/uid/helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/uid/helper.ts) - `SnowflakeUidHelper`, `SnowflakeConfig`
257
+ - [`packages/helpers/src/modules/uid/opaque.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/uid/opaque.ts) - `OpaqueUidHelper`
258
+ - [`packages/helpers/src/modules/uid/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/uid/common/constants.ts) - `UidAlphabets`, `UidCaseForms`
259
+ - [`packages/helpers/src/modules/uid/index.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/modules/uid/index.ts) - module barrel