first commit
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# UuidPlugin
|
||||
|
||||
## Purpose
|
||||
|
||||
Generates UUID v4 and URL-safe nano-ID strings inline within templates. Because ID generation depends on `crypto.randomUUID` or `Math.random`, the plugin accepts an injectable `Rng` from `@suaveplan/types` so tests can produce deterministic output. Each template token renders a freshly generated ID, making the plugin appropriate for generating row identifiers, correlation IDs, or unique slugs during batch template rendering.
|
||||
|
||||
## Features
|
||||
|
||||
- `{_uuid}` — UUID v4 string (`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`).
|
||||
- `{_uuid:v4}` — explicit UUID v4 (same as above).
|
||||
- `{_nanoid}` — 21-character URL-safe nano-ID using the default base-64 alphabet.
|
||||
- `{_nanoid:10}` — nano-ID with custom length.
|
||||
- Injectable `Rng` for fully deterministic output in tests.
|
||||
- Prefers `globalThis.crypto.randomUUID()` when available and no `rng` is injected.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { TemplateEngine, createUuidPlugin } from "@suaveplan/templates";
|
||||
|
||||
const engine = new TemplateEngine().use(createUuidPlugin());
|
||||
|
||||
engine.parse("ID: {_uuid}", {});
|
||||
// → "ID: f47ac10b-58cc-4372-a567-0e02b2c3d479" (random)
|
||||
|
||||
engine.parse("Slug: {_nanoid:8}", {});
|
||||
// → "Slug: V1StGXR8" (random 8-char)
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { TemplateEngine, createUuidPlugin } from "@suaveplan/templates";
|
||||
import type { Rng } from "@suaveplan/types";
|
||||
|
||||
// Deterministic RNG for tests
|
||||
const deterministicRng: Rng = {
|
||||
nextUint32: () => 0xdeadbeef,
|
||||
nextFloat: () => 0.42,
|
||||
};
|
||||
|
||||
const engine = new TemplateEngine().use(createUuidPlugin({ rng: deterministicRng }));
|
||||
|
||||
const id1 = engine.parse("{_uuid}", {});
|
||||
const id2 = engine.parse("{_uuid}", {});
|
||||
// id1 === id2 — same RNG seed produces same output
|
||||
console.assert(id1 === id2);
|
||||
|
||||
// Nano-ID with explicit length
|
||||
engine.parse("{_nanoid:12}", {});
|
||||
// → same 12-char string every call with deterministic rng
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `UuidPluginOptions`
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `rng` | `Rng` (optional) | Injectable RNG; if omitted, uses `crypto.randomUUID` or `Math.random` |
|
||||
|
||||
### `createUuidPlugin<TContext>(opts?): Plugin<TContext>`
|
||||
|
||||
Returns a `Plugin` with:
|
||||
|
||||
- `name: "uuid"`
|
||||
- `priority: PluginPriority.HIGH` (80)
|
||||
- `pattern: /\{_(uuid|nanoid)(?::([^}]+))?\}/g`
|
||||
|
||||
The pattern captures two groups:
|
||||
|
||||
1. Type keyword: `uuid` or `nanoid`
|
||||
2. Optional parameter (`v4` for UUID variant, numeric length for nano-ID)
|
||||
|
||||
**transform(match)**: Dispatches on the type keyword. For `uuid`, calls `generateUuid(opts.rng)`. For `nanoid`, parses the parameter as an integer length (defaults to 21) and calls `generateNanoid(length, undefined, opts.rng)`. Returns the original match text for unrecognised type keywords (defensive — cannot occur given the pattern).
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `generateUuid` prefers `globalThis.crypto.randomUUID()` when it is available and no `rng` is injected. This avoids manual bit-manipulation and takes advantage of the cryptographically secure native implementation.
|
||||
- When `crypto.randomUUID` is unavailable (older runtimes), the fallback constructs a UUID using the standard `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx` template with bitwise operations to enforce RFC 4122 version and variant bits.
|
||||
- `generateNanoid` uses modular selection from the alphabet rather than the rejection-sampling algorithm used by the `nanoid` npm package. This is sufficient for the lengths and alphabets used in template generation and avoids the complexity of rejection sampling.
|
||||
- Runs at `HIGH` priority so IDs are generated before MEDIUM-priority transforms, allowing `{_uuid:upper}` style compositions in future plugin extensions.
|
||||
Reference in New Issue
Block a user