@smonn/ids 0.12.3 → 0.13.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.
- package/README.md +42 -32
- package/dist/{adapter-types-CdYJM6Sf.d.mts → adapter-types-CIc-4O-P.d.mts} +2 -2
- package/dist/{adapter-types-CdYJM6Sf.d.mts.map → adapter-types-CIc-4O-P.d.mts.map} +1 -1
- package/dist/cli.mjs +316 -178
- package/dist/cli.mjs.map +1 -1
- package/dist/{codec-shell-DvrTDa65.mjs → codec-shell-C2NKQEx2.mjs} +5 -1
- package/dist/codec-shell-C2NKQEx2.mjs.map +1 -0
- package/dist/{digest-CknNw2wa.mjs → digest-DsGeXfk3.mjs} +9 -24
- package/dist/digest-DsGeXfk3.mjs.map +1 -0
- package/dist/digest.d.mts +3 -3
- package/dist/digest.d.mts.map +1 -1
- package/dist/digest.mjs +1 -1
- package/dist/drizzle.d.mts +3 -3
- package/dist/drizzle.d.mts.map +1 -1
- package/dist/drizzle.mjs.map +1 -1
- package/dist/error-Cp5qYZcv.mjs.map +1 -1
- package/dist/{error-JIPylU_E.d.mts → error-Dqyho9vp.d.mts} +7 -2
- package/dist/error-Dqyho9vp.d.mts.map +1 -0
- package/dist/express.d.mts +2 -2
- package/dist/fastify.d.mts +2 -2
- package/dist/graphql.d.mts +3 -3
- package/dist/graphql.mjs +2 -2
- package/dist/graphql.mjs.map +1 -1
- package/dist/hono.d.mts +2 -2
- package/dist/hono.mjs +1 -2
- package/dist/hono.mjs.map +1 -1
- package/dist/index.d.mts +21 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{key-material-f29JIyrz.mjs → key-material-DvjACe89.mjs} +53 -2
- package/dist/key-material-DvjACe89.mjs.map +1 -0
- package/dist/kysely.d.mts +3 -3
- package/dist/kysely.d.mts.map +1 -1
- package/dist/kysely.mjs.map +1 -1
- package/dist/mikro-orm.d.mts +3 -3
- package/dist/mikro-orm.d.mts.map +1 -1
- package/dist/mikro-orm.mjs.map +1 -1
- package/dist/nestjs.d.mts +2 -2
- package/dist/nestjs.mjs +1 -1
- package/dist/nestjs.mjs.map +1 -1
- package/dist/{opaque-BQVNoIIh.mjs → opaque-BW3Uzeeb.mjs} +5 -28
- package/dist/opaque-BW3Uzeeb.mjs.map +1 -0
- package/dist/opaque.d.mts +22 -4
- package/dist/opaque.d.mts.map +1 -1
- package/dist/opaque.mjs +1 -1
- package/dist/prisma.d.mts +32 -27
- package/dist/prisma.d.mts.map +1 -1
- package/dist/prisma.mjs +11 -15
- package/dist/prisma.mjs.map +1 -1
- package/dist/{reverse-DsPd7Lco.mjs → reverse-BW8g_cln.mjs} +12 -5
- package/dist/reverse-BW8g_cln.mjs.map +1 -0
- package/dist/reverse.d.mts +20 -4
- package/dist/reverse.d.mts.map +1 -1
- package/dist/reverse.mjs +1 -1
- package/dist/{rng-Clos6uC0.mjs → rng-BHFxX1Fc.mjs} +2 -2
- package/dist/{rng-Clos6uC0.mjs.map → rng-BHFxX1Fc.mjs.map} +1 -1
- package/dist/{signed-4h2BnlWx.mjs → signed-BTz3ZFYE.mjs} +12 -33
- package/dist/signed-BTz3ZFYE.mjs.map +1 -0
- package/dist/signed.d.mts +13 -4
- package/dist/signed.d.mts.map +1 -1
- package/dist/signed.mjs +1 -1
- package/dist/{timestamp-Cg9nRfnK.mjs → timestamp-CleAIdZI.mjs} +12 -5
- package/dist/timestamp-CleAIdZI.mjs.map +1 -0
- package/dist/typeorm.d.mts +2 -2
- package/dist/typeorm.d.mts.map +1 -1
- package/dist/typeorm.mjs.map +1 -1
- package/dist/{types-g7CiQDyE.d.mts → types-wplmOgOK.d.mts} +20 -3
- package/dist/types-wplmOgOK.d.mts.map +1 -0
- package/dist/{wrapped-BQ-lNECo.mjs → wrapped-DPlsv1x-.mjs} +18 -76
- package/dist/wrapped-DPlsv1x-.mjs.map +1 -0
- package/dist/wrapped.d.mts +31 -5
- package/dist/wrapped.d.mts.map +1 -1
- package/dist/wrapped.mjs +1 -1
- package/package.json +80 -27
- package/dist/codec-shell-DvrTDa65.mjs.map +0 -1
- package/dist/digest-CknNw2wa.mjs.map +0 -1
- package/dist/error-JIPylU_E.d.mts.map +0 -1
- package/dist/key-material-f29JIyrz.mjs.map +0 -1
- package/dist/opaque-BQVNoIIh.mjs.map +0 -1
- package/dist/reverse-DsPd7Lco.mjs.map +0 -1
- package/dist/signed-4h2BnlWx.mjs.map +0 -1
- package/dist/timestamp-Cg9nRfnK.mjs.map +0 -1
- package/dist/types-g7CiQDyE.d.mts.map +0 -1
- package/dist/wrapped-BQ-lNECo.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -8,10 +8,7 @@ Public-facing branded IDs for TypeScript apps. Type-safe, sortable, and codec-pl
|
|
|
8
8
|
pnpm add @smonn/ids
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Each ID looks like `usr_01h7b3k9rqxn4cw3p9r8t2sgkw`: a three-letter brand, an
|
|
12
|
-
underscore, then 26 Crockford base32 characters of payload. The default
|
|
13
|
-
Timestamp codec encodes a 48-bit millisecond Unix timestamp followed by 80
|
|
14
|
-
random bits — the same byte layout as a [ULID](https://github.com/ulid/spec).
|
|
11
|
+
Each ID looks like `usr_01h7b3k9rqxn4cw3p9r8t2sgkw`: a three-letter brand, an underscore, then 26 Crockford base32 characters of payload. The default Timestamp codec encodes a 48-bit millisecond Unix timestamp followed by 80 random bits — the same byte layout as a [ULID](https://github.com/ulid/spec).
|
|
15
12
|
|
|
16
13
|
## Quickstart
|
|
17
14
|
|
|
@@ -35,26 +32,22 @@ if (r.ok) {
|
|
|
35
32
|
}
|
|
36
33
|
```
|
|
37
34
|
|
|
38
|
-
`safeParse` accepts mixed case and the Crockford visual aliases (`o → 0`,
|
|
39
|
-
`i → 1`, `l → 1`) and always returns the canonical lowercase form. See the
|
|
40
|
-
[Timestamp codec guide](https://ids.smonn.se/codecs/timestamp/) for sorting,
|
|
41
|
-
backfills (`generateAt`), range queries, structured errors, Standard Schema, and
|
|
42
|
-
JSON Schema.
|
|
35
|
+
`safeParse` accepts mixed case and the Crockford visual aliases (`o → 0`, `i → 1`, `l → 1`) and always returns the canonical lowercase form. See the [Timestamp codec guide](https://ids.smonn.se/codecs/timestamp/) for sorting, backfills (`generateAt`), range queries, structured errors, Standard Schema, and JSON Schema.
|
|
43
36
|
|
|
44
37
|
## Choosing a codec
|
|
45
38
|
|
|
46
|
-
All six codecs share the same `<brand>_<26 chars>` wire shape but make different
|
|
47
|
-
trade-offs. They are wire-indistinguishable, so codec choice is a per-brand
|
|
48
|
-
commitment.
|
|
39
|
+
All six codecs share the same `<brand>_<26 chars>` wire shape but make different trade-offs. They are wire-indistinguishable — `safeParse`, `is`, and `parse` cannot distinguish an Opaque Timestamp ID from a Timestamp ID at runtime. Cross-codec confusion is undetectable by the library; the consumer is responsible for routing a given ID to the correct codec for the brand. Codec choice is therefore a per-brand commitment.
|
|
49
40
|
|
|
50
|
-
| Codec
|
|
51
|
-
|
|
|
52
|
-
| Timestamp
|
|
53
|
-
| Reverse Timestamp | `@smonn/ids/reverse` | Descending (newest-first) | No
|
|
54
|
-
| Signed Timestamp
|
|
55
|
-
| Opaque Timestamp
|
|
56
|
-
| Wrapped key
|
|
57
|
-
| Digest
|
|
41
|
+
| Codec | Import | Sort direction | Key required | Timestamp extractable |
|
|
42
|
+
| --- | --- | --- | --- | --- |
|
|
43
|
+
| Timestamp | `@smonn/ids` | Ascending (oldest-first) | No | Always (plaintext) |
|
|
44
|
+
| Reverse Timestamp | `@smonn/ids/reverse` | Descending (newest-first) | No | Always (plaintext) |
|
|
45
|
+
| Signed Timestamp | `@smonn/ids/signed` | Ascending (oldest-first) | Yes (signing key) | Always (plaintext) |
|
|
46
|
+
| Opaque Timestamp | `@smonn/ids/opaque` | None (encrypted) | Yes (AES key) | With key only |
|
|
47
|
+
| Wrapped key | `@smonn/ids/wrapped` | None | Yes (wrapping key) | N/A — not timestamp-family |
|
|
48
|
+
| Digest | `@smonn/ids/digest` | None | Yes (digest key) | N/A — not timestamp-family |
|
|
49
|
+
|
|
50
|
+
The Timestamp codec is the default and ships from the root `@smonn/ids` entry — it has no `/timestamp` subpath by design. If you try `import ... from "@smonn/ids/timestamp"` you will get a module-resolution error; use `@smonn/ids` directly. Every other codec uses a named subpath (`/reverse`, `/signed`, `/opaque`, `/wrapped`, `/digest`); this asymmetry is intentional and permanent.
|
|
58
51
|
|
|
59
52
|
- **Newest-first scans** on forward-only KV stores → [Reverse Timestamp](https://ids.smonn.se/codecs/reverse/)
|
|
60
53
|
- **Tamper-evident share links** verified without a DB lookup → [Signed Timestamp](https://ids.smonn.se/codecs/signed/) (integrity)
|
|
@@ -66,28 +59,45 @@ Try them all live in the [playground](https://ids.smonn.se/playground/).
|
|
|
66
59
|
|
|
67
60
|
## Integrations
|
|
68
61
|
|
|
69
|
-
Framework and ORM adapters ship as optional subpath exports (each requires its
|
|
70
|
-
own peer dependency):
|
|
62
|
+
Framework and ORM adapters ship as optional subpath exports (each requires its own peer dependency):
|
|
71
63
|
|
|
72
64
|
- **HTTP route params:** [Hono](https://ids.smonn.se/adapters/hono/), [Express](https://ids.smonn.se/adapters/express/), [Fastify](https://ids.smonn.se/adapters/fastify/) — `idParam` middleware; [NestJS](https://ids.smonn.se/adapters/nestjs/) — `ParseIdPipe`
|
|
73
65
|
- **ORM columns:** [Drizzle](https://ids.smonn.se/adapters/drizzle/) — `idColumn`, [Kysely](https://ids.smonn.se/adapters/kysely/) — `idColumn`, [MikroORM](https://ids.smonn.se/adapters/mikro-orm/) — `idType`, [Prisma](https://ids.smonn.se/adapters/prisma/) — `idField`, [TypeORM](https://ids.smonn.se/adapters/typeorm/) — `idTransformer`
|
|
74
66
|
- **GraphQL:** [GraphQL](https://ids.smonn.se/adapters/graphql/) — `idScalar` custom scalar
|
|
75
67
|
- **CLI:** brand-agnostic `inspect` / `generate` / `keygen` — `npx @smonn/ids --help` ([docs](https://ids.smonn.se/cli/))
|
|
76
68
|
|
|
77
|
-
Every codec also implements [Standard Schema v1](https://standardschema.dev/), so
|
|
78
|
-
it slots into Zod, Valibot, ArkType, tRPC, and any validator-aware library.
|
|
69
|
+
Every codec also implements [Standard Schema v1](https://standardschema.dev/), so it slots into Zod, Valibot, ArkType, tRPC, and any validator-aware library.
|
|
79
70
|
|
|
80
71
|
## What this is **not** for
|
|
81
72
|
|
|
82
|
-
- **Internal surrogate primary keys.** If nobody outside your service sees the
|
|
83
|
-
|
|
84
|
-
sequence.
|
|
85
|
-
- **Wire-compatible ULIDs.** The byte layout is ULID-shaped, but the encoding is
|
|
86
|
-
lowercase and brand-wrapped. Stock ULID parsers will reject these.
|
|
73
|
+
- **Internal surrogate primary keys.** If nobody outside your service sees the ID, the brand prefix and lenient parsing are dead weight. Use a `bigint` sequence.
|
|
74
|
+
- **Wire-compatible ULIDs.** The byte layout is ULID-shaped, but the encoding is lowercase and brand-wrapped. Stock ULID parsers will reject these.
|
|
87
75
|
- **Distributed-trace / request-correlation IDs.** Use OpenTelemetry-format IDs.
|
|
88
|
-
- **Hiding creation time with the Timestamp codec.** Anyone with one ID at a
|
|
89
|
-
|
|
90
|
-
|
|
76
|
+
- **Hiding creation time with the Timestamp codec.** Anyone with one ID at a known creation time can compute the epoch offset. Use the Opaque Timestamp codec to hide creation time per-ID.
|
|
77
|
+
|
|
78
|
+
## API surface
|
|
79
|
+
|
|
80
|
+
Exports from the main `@smonn/ids` entry point only. Codec-specific subpath exports (`@smonn/ids/reverse`, `@smonn/ids/opaque`, `@smonn/ids/signed`, `@smonn/ids/wrapped`, `@smonn/ids/digest`) and adapter subpaths are not listed here.
|
|
81
|
+
|
|
82
|
+
### Types
|
|
83
|
+
|
|
84
|
+
- `Id<Brand>` — Canonical branded ID string for `Brand`; produced by `generate()` and `safeParse()`.
|
|
85
|
+
- `ParseError` — Parse failure reason string (`"not_string"`, `"invalid_prefix"`, or `"invalid_base32"`) returned by `safeParse()`.
|
|
86
|
+
- `ParseResult<Brand>` — Discriminated union returned by `safeParse()`: `{ ok: true; id: Id<Brand> }` or `{ ok: false; error: ParseError }`.
|
|
87
|
+
- `JsonSchema` — Shape of the object returned by a codec's `toJsonSchema()`.
|
|
88
|
+
- `IdsErrorCode` — String-literal union of the eleven stable error codes carried by `IdsError`.
|
|
89
|
+
- `TimestampCodec<Brand>` — Interface of a brand-scoped Timestamp codec instance returned by `createTimestampId()`.
|
|
90
|
+
- `TimestampOptions` — Construction options for `createTimestampId()`: `now`, `rng`, and `allowDuplicateBrand`.
|
|
91
|
+
- `ValidBrand<S>` — Compile-time validation that `S` is a well-formed brand (three lowercase `a–z` characters); intersect it with a codec constructor's brand parameter (`brand: Brand & ValidBrand<Brand>`) to reject malformed brands at the type level.
|
|
92
|
+
|
|
93
|
+
### Classes
|
|
94
|
+
|
|
95
|
+
- `IdsError` — Single error class thrown by caller-reachable failures; carries a stable `code: IdsErrorCode`. Use `isIdsError()` rather than `instanceof` to detect across realms.
|
|
96
|
+
|
|
97
|
+
### Functions
|
|
98
|
+
|
|
99
|
+
- `isIdsError(value)` — Type guard for `IdsError`; uses an internal brand to survive ESM/CJS dual-package duplication where bare `instanceof` fails.
|
|
100
|
+
- `createTimestampId(brand, options?)` — Creates a Timestamp codec for `brand` (three lowercase `a–z` characters).
|
|
91
101
|
|
|
92
102
|
## Links
|
|
93
103
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as ParseResult } from "./types-
|
|
1
|
+
import { i as ParseResult } from "./types-wplmOgOK.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/adapters/adapter-types.d.ts
|
|
4
4
|
/** Discriminated failure value passed to `onError` and emitted to the framework's error handler. */
|
|
@@ -17,4 +17,4 @@ type IdCodec<Brand extends string> = {
|
|
|
17
17
|
type IdColumnCodec<Brand extends string> = IdCodec<Brand>;
|
|
18
18
|
//#endregion
|
|
19
19
|
export { IdColumnCodec as n, IdParamFailure as r, IdCodec as t };
|
|
20
|
-
//# sourceMappingURL=adapter-types-
|
|
20
|
+
//# sourceMappingURL=adapter-types-CIc-4O-P.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter-types-
|
|
1
|
+
{"version":3,"file":"adapter-types-CIc-4O-P.d.mts","names":[],"sources":["../src/adapters/adapter-types.ts"],"mappings":";;;;KAIY,cAAA;EAAA,SACG,MAAA;EAAA,SAAmC,MAAA;AAAA;EAAA,SACnC,MAAA;EAAA,SAA8B,MAAA;AAAA;;KAGjC,OAAA;EACV,SAAA,CAAU,KAAA,YAAiB,WAAA,CAAY,KAAA;AAAA;AADzC;AAAA,KAKY,aAAA,yBAAsC,OAAA,CAAQ,KAAA"}
|