first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -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.