@ingram-tech/nk-db 1.5.0 → 1.6.0
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 +8 -7
- package/dist/id.d.ts +18 -131
- package/dist/id.d.ts.map +1 -1
- package/dist/id.js +17 -200
- package/dist/id.js.map +1 -1
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -144,19 +144,20 @@ The same surface is available programmatically:
|
|
|
144
144
|
|
|
145
145
|
## Prefixed ids (`@ingram-tech/nk-db/id`)
|
|
146
146
|
|
|
147
|
-
|
|
148
|
-
(
|
|
149
|
-
|
|
147
|
+
The codec is the standalone [`id758`](https://github.com/ingram-technologies/id758)
|
|
148
|
+
package: `uuidv7()`, `encodeId` / `decodeId` / `mintId`, and `createIdRegistry`
|
|
149
|
+
for typed per-entity helpers. This subpath re-exports all of it, plus the
|
|
150
|
+
pre-extraction names (`uuidGenerateId`, `toPrefixedId`, `fromPrefixedId`,
|
|
151
|
+
`base58Id`) as deprecated aliases, so either import path works.
|
|
150
152
|
|
|
151
|
-
**
|
|
153
|
+
**The module is isomorphic** (no imports, randomness from Web Crypto), so a
|
|
152
154
|
Drizzle `schema.ts`, a client component or an edge runtime can all use it. Keep
|
|
153
155
|
it that way: a single `node:crypto` import makes every module that touches an id
|
|
154
156
|
node-only (a test enforces this).
|
|
155
157
|
|
|
156
|
-
Store the raw `uuid` and
|
|
158
|
+
Store the raw `uuid` and encode at the edge. The prefixed id is presentation, not
|
|
157
159
|
identity: inside the DB, "which entity is this?" is already carried by the
|
|
158
|
-
column, so storing the prefix duplicates schema metadata into every row
|
|
159
|
-
codec bugs permanently on disk.
|
|
160
|
+
column, so storing the prefix duplicates schema metadata into every row.
|
|
160
161
|
|
|
161
162
|
**Ids are self-describing, uuids are not.** A prefix names its entity, so
|
|
162
163
|
decoding needs no context — `entityOf(registry, id)` and `decodeAnyId(registry,
|
package/dist/id.d.ts
CHANGED
|
@@ -1,134 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* (
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* `@ingram-tech/nk-db/id` — the fleet's id codec, now published standalone as
|
|
3
|
+
* [`id758`](https://github.com/ingram-technologies/id758). This subpath stays
|
|
4
|
+
* as the nextkit-facing entry point: it re-exports the whole `id758` surface
|
|
5
|
+
* plus the names nextkit sites adopted before the extraction, so nothing that
|
|
6
|
+
* imports from here needs to change. New code may import `id758` directly.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
*
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* the vectors in lockstep.
|
|
21
|
-
*
|
|
22
|
-
* Store the hyphenated UUIDv7 at rest (uuid columns stay native and time-ordered
|
|
23
|
-
* for index locality) and use {@link toPrefixedId} to skin it as a prefixed
|
|
24
|
-
* base58 id for the wire / display, {@link fromPrefixedId} to recover it.
|
|
25
|
-
* {@link base58Id} mints a fresh one directly; {@link createIdRegistry} builds a
|
|
26
|
-
* typed, prefix-validated set of helpers for a project's entities.
|
|
27
|
-
*/
|
|
28
|
-
/**
|
|
29
|
-
* Mint a **UUIDv7** (RFC 9562): a 48-bit Unix-ms timestamp prefix + random tail,
|
|
30
|
-
* version `7`, variant `10`. UUID-shaped (drops straight into a `uuid` column)
|
|
31
|
-
* while staying time-ordered for index locality. Used as Better Auth's
|
|
32
|
-
* `advanced.database.generateId`. Node/Bun's `randomUUID` is v4-only, so we lay
|
|
33
|
-
* the bytes out by hand.
|
|
34
|
-
*/
|
|
35
|
-
export declare const uuidGenerateId: () => Uuid;
|
|
36
|
-
/** Skin a stored hyphenated UUIDv7 as a prefixed base58 id, e.g. `team_…`. */
|
|
37
|
-
export declare function toPrefixedId(uuid: string, prefix: string): string;
|
|
38
|
-
/** Inverse of {@link toPrefixedId}: recover the hyphenated UUIDv7. */
|
|
39
|
-
export declare function fromPrefixedId(id: string): string;
|
|
40
|
-
/** Mint a fresh prefixed base58 id (UUIDv7 core), for text-id sites / API parity. */
|
|
41
|
-
export declare function base58Id(prefix: string): string;
|
|
42
|
-
declare const ID_BRAND: unique symbol;
|
|
43
|
-
declare const UUID_BRAND: unique symbol;
|
|
44
|
-
/**
|
|
45
|
-
* A prefixed base58 public id for entity `E` (e.g. `org_…`), branded by entity
|
|
46
|
-
* so ids of different entities — and plain strings — can't be mixed silently. A
|
|
47
|
-
* plain `string` at runtime; the brand is minted by a {@link createIdRegistry}
|
|
48
|
-
* helper and erased on the wire.
|
|
49
|
-
*/
|
|
50
|
-
export type Id<E extends string> = string & {
|
|
51
|
-
readonly [ID_BRAND]: E;
|
|
52
|
-
};
|
|
53
|
-
/**
|
|
54
|
-
* A hyphenated UUIDv7 at rest, branded distinct from a public {@link Id} so a
|
|
55
|
-
* skinned id can't be fed into a uuid slot (or a uuid into an id slot) by
|
|
56
|
-
* accident. Recovered from an {@link Id} by a registry helper's `decode`.
|
|
57
|
-
*/
|
|
58
|
-
export type Uuid = string & {
|
|
59
|
-
readonly [UUID_BRAND]: "Uuid";
|
|
60
|
-
};
|
|
61
|
-
/**
|
|
62
|
-
* Whether `value` is a hyphenated uuid — the sanctioned string→{@link Uuid}
|
|
63
|
-
* bless: narrowing through the shape check is never a blind cast. Use at trust
|
|
64
|
-
* boundaries (env/config, external payloads, `crypto.randomUUID()`).
|
|
65
|
-
*/
|
|
66
|
-
export declare const isUuid: (value: string | null | undefined) => value is Uuid;
|
|
67
|
-
/** The throwing {@link isUuid}: bless a string you require to be a uuid. */
|
|
68
|
-
export declare function asUuid(value: string): Uuid;
|
|
69
|
-
/** Typed helpers for one entity's prefixed ids — built by {@link createIdRegistry}. */
|
|
70
|
-
export interface IdHelper<E extends string = string> {
|
|
71
|
-
/** This entity's prefix, e.g. `"agt"` (no trailing underscore). */
|
|
72
|
-
readonly prefix: string;
|
|
73
|
-
/** Mint a fresh prefixed id (new UUIDv7 core). */
|
|
74
|
-
mint(): Id<E>;
|
|
75
|
-
/** Skin a stored hyphenated UUIDv7 as this entity's prefixed id. */
|
|
76
|
-
encode(uuid: string): Id<E>;
|
|
77
|
-
/** Recover the hyphenated UUIDv7; throws if the prefix / shape doesn't match. */
|
|
78
|
-
decode(id: string): Uuid;
|
|
79
|
-
/**
|
|
80
|
-
* Recover the hyphenated UUIDv7, or `null` if `id` isn't a well-formed
|
|
81
|
-
* prefixed id for this entity. The throw-free {@link decode}, for validating
|
|
82
|
-
* untrusted input (a path param, a query string) without a try/catch.
|
|
83
|
-
*/
|
|
84
|
-
decodeOrNull(id: string): Uuid | null;
|
|
85
|
-
/** Whether `id` is a well-formed prefixed id for this entity (narrows to {@link Id}). */
|
|
86
|
-
is(id: string): id is Id<E>;
|
|
87
|
-
}
|
|
88
|
-
/** The map returned by {@link createIdRegistry}: one {@link IdHelper} per key. */
|
|
89
|
-
export type IdRegistry<K extends string> = {
|
|
90
|
-
[E in K]: IdHelper<E>;
|
|
91
|
-
};
|
|
92
|
-
/**
|
|
93
|
-
* Build a typed, prefix-validated id registry for a project's entities — one
|
|
94
|
-
* place to declare every prefix instead of hand-rolling encode/decode wrappers:
|
|
95
|
-
*
|
|
96
|
-
* ```ts
|
|
97
|
-
* const ids = createIdRegistry({ org: "org", agent: "agt", session: "cs" });
|
|
98
|
-
* ids.org.mint(); // "org_3k9…"
|
|
99
|
-
* ids.org.encode(uuid); // "org_…"
|
|
100
|
-
* ids.org.decode("org_…"); // uuid — throws on a wrong / missing prefix
|
|
101
|
-
* ids.agent.is(someId); // boolean
|
|
102
|
-
* ```
|
|
103
|
-
*/
|
|
104
|
-
export declare function createIdRegistry<const T extends Record<string, string>>(prefixes: T): {
|
|
105
|
-
[K in keyof T]: IdHelper;
|
|
106
|
-
};
|
|
107
|
-
/**
|
|
108
|
-
* Which entity a prefixed id belongs to, or `null` if no helper in `registry`
|
|
109
|
-
* recognises it (a raw uuid, a sentinel, a foreign prefix).
|
|
110
|
-
*
|
|
111
|
-
* A public id is **self-describing**: its prefix names its entity, so resolving
|
|
112
|
-
* one needs no surrounding context. That is the property behind every
|
|
113
|
-
* entity-agnostic decode — a polymorphic FK whose target is named by a sibling
|
|
114
|
-
* `*_type` column, a raw-SQL binding that only has a value, a webhook payload.
|
|
115
|
-
* The inverse does **not** hold: a bare uuid carries no entity, so encoding
|
|
116
|
-
* always needs to know which entity you meant.
|
|
117
|
-
*
|
|
118
|
-
* Standalone rather than a registry method: the registry is keyed by entity
|
|
119
|
-
* name, so a method would collide with an entity literally called `entityOf`.
|
|
120
|
-
*/
|
|
121
|
-
export declare function entityOf<K extends string>(registry: Record<K, IdHelper>, value: string): K | null;
|
|
122
|
-
/**
|
|
123
|
-
* Decode a prefixed id belonging to **any** entity in `registry` (see
|
|
124
|
-
* {@link entityOf}), for the boundaries that can't name one: polymorphic FKs,
|
|
125
|
-
* raw-SQL id bindings, generic audit/event payloads.
|
|
126
|
-
*
|
|
127
|
-
* Tolerant by design — a value no helper recognises is returned unchanged, so an
|
|
128
|
-
* already-decoded uuid passes straight through and this is safe to apply to a
|
|
129
|
-
* value that may or may not be skinned. Use a helper's `decode`/`decodeOrNull`
|
|
130
|
-
* when you know the entity and want a wrong prefix rejected.
|
|
131
|
-
*/
|
|
132
|
-
export declare function decodeAnyId<K extends string>(registry: Record<K, IdHelper>, value: string): Uuid;
|
|
133
|
-
export {};
|
|
8
|
+
* Like `id758` itself, this module must stay isomorphic (no node-only imports):
|
|
9
|
+
* it is pulled into Drizzle schemas, client components and edge runtimes.
|
|
10
|
+
*/
|
|
11
|
+
export * from "id758";
|
|
12
|
+
import { decodeId, encodeId, mintId, uuidv7 } from "id758";
|
|
13
|
+
/** @deprecated Use `uuidv7` from `id758`. */
|
|
14
|
+
export declare const uuidGenerateId: typeof uuidv7;
|
|
15
|
+
/** @deprecated Use `encodeId` from `id758`. */
|
|
16
|
+
export declare const toPrefixedId: typeof encodeId;
|
|
17
|
+
/** @deprecated Use `decodeId` from `id758`. */
|
|
18
|
+
export declare const fromPrefixedId: typeof decodeId;
|
|
19
|
+
/** @deprecated Use `mintId` from `id758`. */
|
|
20
|
+
export declare const base58Id: typeof mintId;
|
|
134
21
|
//# sourceMappingURL=id.d.ts.map
|
package/dist/id.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"id.d.ts","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,OAAO,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAE3D,6CAA6C;AAC7C,eAAO,MAAM,cAAc,eAAS,CAAC;AACrC,+CAA+C;AAC/C,eAAO,MAAM,YAAY,iBAAW,CAAC;AACrC,+CAA+C;AAC/C,eAAO,MAAM,cAAc,iBAAW,CAAC;AACvC,6CAA6C;AAC7C,eAAO,MAAM,QAAQ,eAAS,CAAC"}
|
package/dist/id.js
CHANGED
|
@@ -1,204 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* (
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* `@ingram-tech/nk-db/id` — the fleet's id codec, now published standalone as
|
|
3
|
+
* [`id758`](https://github.com/ingram-technologies/id758). This subpath stays
|
|
4
|
+
* as the nextkit-facing entry point: it re-exports the whole `id758` surface
|
|
5
|
+
* plus the names nextkit sites adopted before the extraction, so nothing that
|
|
6
|
+
* imports from here needs to change. New code may import `id758` directly.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* Node 19+, Bun, Deno, edge runtimes and browsers), not `node:crypto`. That is
|
|
10
|
-
* load-bearing, not incidental — a `node:crypto` import here makes every module
|
|
11
|
-
* that touches an id node-only, which in practice means a Drizzle `schema.ts`
|
|
12
|
-
* cannot encode/decode without dragging `node:crypto` into client bundles, and
|
|
13
|
-
* sites end up dependency-injecting the codec to route around it. Keep the
|
|
14
|
-
* import list empty.
|
|
15
|
-
*
|
|
16
|
-
* When a service has a non-JS twin of this codec (e.g. a Python API), keep its
|
|
17
|
-
* byte → string vectors identical to the ones in `id.test.ts`, so a stored
|
|
18
|
-
* UUIDv7 and a prefixed base58 id are the same encoding of the same 16 bytes.
|
|
19
|
-
* That cross-impl contract is the most important property of this file — keep
|
|
20
|
-
* the vectors in lockstep.
|
|
21
|
-
*
|
|
22
|
-
* Store the hyphenated UUIDv7 at rest (uuid columns stay native and time-ordered
|
|
23
|
-
* for index locality) and use {@link toPrefixedId} to skin it as a prefixed
|
|
24
|
-
* base58 id for the wire / display, {@link fromPrefixedId} to recover it.
|
|
25
|
-
* {@link base58Id} mints a fresh one directly; {@link createIdRegistry} builds a
|
|
26
|
-
* typed, prefix-validated set of helpers for a project's entities.
|
|
27
|
-
*/
|
|
28
|
-
/**
|
|
29
|
-
* Mint a **UUIDv7** (RFC 9562): a 48-bit Unix-ms timestamp prefix + random tail,
|
|
30
|
-
* version `7`, variant `10`. UUID-shaped (drops straight into a `uuid` column)
|
|
31
|
-
* while staying time-ordered for index locality. Used as Better Auth's
|
|
32
|
-
* `advanced.database.generateId`. Node/Bun's `randomUUID` is v4-only, so we lay
|
|
33
|
-
* the bytes out by hand.
|
|
34
|
-
*/
|
|
35
|
-
export const uuidGenerateId = () => {
|
|
36
|
-
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
37
|
-
const ts = Date.now();
|
|
38
|
-
bytes[0] = Math.floor(ts / 2 ** 40) & 0xff;
|
|
39
|
-
bytes[1] = Math.floor(ts / 2 ** 32) & 0xff;
|
|
40
|
-
bytes[2] = Math.floor(ts / 2 ** 24) & 0xff;
|
|
41
|
-
bytes[3] = Math.floor(ts / 2 ** 16) & 0xff;
|
|
42
|
-
bytes[4] = Math.floor(ts / 2 ** 8) & 0xff;
|
|
43
|
-
bytes[5] = ts & 0xff;
|
|
44
|
-
bytes[6] = ((bytes[6] ?? 0) & 0x0f) | 0x70; // version 7
|
|
45
|
-
bytes[8] = ((bytes[8] ?? 0) & 0x3f) | 0x80; // variant 10
|
|
46
|
-
return bytesToUuid(bytes); // mint site
|
|
47
|
-
};
|
|
48
|
-
const B58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
49
|
-
// ceil(128 / log2(58)): a 16-byte value never needs more than 22 digits. We
|
|
50
|
-
// left-pad to it so every body is uniform width and sorts lexically ==
|
|
51
|
-
// chronologically (UUIDv7's ms-timestamp prefix lives in the high bytes).
|
|
52
|
-
const WIDTH = 22;
|
|
53
|
-
// A bare base58 body: 22 chars, Bitcoin alphabet (no 0 / I / O / l).
|
|
54
|
-
const BODY = "[1-9A-HJ-NP-Za-km-z]{22}";
|
|
55
|
-
/** Big-endian base58 (Bitcoin alphabet) of 16 bytes, left-padded to `WIDTH`. */
|
|
56
|
-
function encode58(bytes) {
|
|
57
|
-
let n = 0n;
|
|
58
|
-
for (const b of bytes)
|
|
59
|
-
n = (n << 8n) | BigInt(b);
|
|
60
|
-
let out = "";
|
|
61
|
-
while (n > 0n) {
|
|
62
|
-
out = B58.charAt(Number(n % 58n)) + out;
|
|
63
|
-
n /= 58n;
|
|
64
|
-
}
|
|
65
|
-
return out.padStart(WIDTH, B58.charAt(0));
|
|
66
|
-
}
|
|
67
|
-
/** Inverse of {@link encode58}: a base58 body back to 16 bytes. */
|
|
68
|
-
function decode58(body) {
|
|
69
|
-
let n = 0n;
|
|
70
|
-
for (const ch of body) {
|
|
71
|
-
const v = B58.indexOf(ch);
|
|
72
|
-
if (v < 0)
|
|
73
|
-
throw new Error(`invalid base58 char: ${ch}`);
|
|
74
|
-
n = n * 58n + BigInt(v);
|
|
75
|
-
}
|
|
76
|
-
// 58^22 slightly exceeds 2^128, so a high-range 22-char body can overflow 16
|
|
77
|
-
// bytes; silently truncating would alias two distinct wire ids to one UUID.
|
|
78
|
-
if (n >= 1n << 128n)
|
|
79
|
-
throw new Error(`base58 value out of uuid range: ${body}`);
|
|
80
|
-
const bytes = new Uint8Array(16);
|
|
81
|
-
for (let i = 15; i >= 0; i--) {
|
|
82
|
-
bytes[i] = Number(n & 0xffn);
|
|
83
|
-
n >>= 8n;
|
|
84
|
-
}
|
|
85
|
-
return bytes;
|
|
86
|
-
}
|
|
87
|
-
/** A hyphenated UUID string → its 16 raw bytes. */
|
|
88
|
-
function uuidToBytes(uuid) {
|
|
89
|
-
const hex = uuid.replace(/-/g, "");
|
|
90
|
-
if (!/^[0-9a-fA-F]{32}$/.test(hex))
|
|
91
|
-
throw new Error(`not a uuid: ${uuid}`);
|
|
92
|
-
const bytes = new Uint8Array(16);
|
|
93
|
-
for (let i = 0; i < 16; i++) {
|
|
94
|
-
bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
95
|
-
}
|
|
96
|
-
return bytes;
|
|
97
|
-
}
|
|
98
|
-
/** 16 raw bytes → a canonical hyphenated UUID string. */
|
|
99
|
-
function bytesToUuid(bytes) {
|
|
100
|
-
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
101
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
102
|
-
}
|
|
103
|
-
/** Skin a stored hyphenated UUIDv7 as a prefixed base58 id, e.g. `team_…`. */
|
|
104
|
-
export function toPrefixedId(uuid, prefix) {
|
|
105
|
-
return `${prefix}_${encode58(uuidToBytes(uuid))}`;
|
|
106
|
-
}
|
|
107
|
-
/** Inverse of {@link toPrefixedId}: recover the hyphenated UUIDv7. */
|
|
108
|
-
export function fromPrefixedId(id) {
|
|
109
|
-
const body = id.slice(id.indexOf("_") + 1);
|
|
110
|
-
return bytesToUuid(decode58(body));
|
|
111
|
-
}
|
|
112
|
-
/** Mint a fresh prefixed base58 id (UUIDv7 core), for text-id sites / API parity. */
|
|
113
|
-
export function base58Id(prefix) {
|
|
114
|
-
return toPrefixedId(uuidGenerateId(), prefix);
|
|
115
|
-
}
|
|
116
|
-
// Any RFC 9562 version (1-8): sites mint v7, but migrated rows are often v4.
|
|
117
|
-
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
118
|
-
/**
|
|
119
|
-
* Whether `value` is a hyphenated uuid — the sanctioned string→{@link Uuid}
|
|
120
|
-
* bless: narrowing through the shape check is never a blind cast. Use at trust
|
|
121
|
-
* boundaries (env/config, external payloads, `crypto.randomUUID()`).
|
|
122
|
-
*/
|
|
123
|
-
export const isUuid = (value) => typeof value === "string" && UUID_RE.test(value);
|
|
124
|
-
/** The throwing {@link isUuid}: bless a string you require to be a uuid. */
|
|
125
|
-
export function asUuid(value) {
|
|
126
|
-
if (!isUuid(value))
|
|
127
|
-
throw new Error(`not a uuid: ${value}`);
|
|
128
|
-
return value;
|
|
129
|
-
}
|
|
130
|
-
function makeHelper(prefix) {
|
|
131
|
-
// Prefixes are developer-controlled identifier constants, never user input.
|
|
132
|
-
const matcher = new RegExp(`^${prefix}_${BODY}$`);
|
|
133
|
-
// The brands are erased at runtime; these casts are the sole mint sites.
|
|
134
|
-
return {
|
|
135
|
-
prefix,
|
|
136
|
-
mint: () => base58Id(prefix),
|
|
137
|
-
encode: (uuid) => toPrefixedId(uuid, prefix),
|
|
138
|
-
decode: (id) => {
|
|
139
|
-
if (!matcher.test(id))
|
|
140
|
-
throw new Error(`not a ${prefix}_ id: ${id}`);
|
|
141
|
-
return fromPrefixedId(id);
|
|
142
|
-
},
|
|
143
|
-
decodeOrNull: (id) => (matcher.test(id) ? fromPrefixedId(id) : null),
|
|
144
|
-
is: (id) => matcher.test(id),
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Build a typed, prefix-validated id registry for a project's entities — one
|
|
149
|
-
* place to declare every prefix instead of hand-rolling encode/decode wrappers:
|
|
150
|
-
*
|
|
151
|
-
* ```ts
|
|
152
|
-
* const ids = createIdRegistry({ org: "org", agent: "agt", session: "cs" });
|
|
153
|
-
* ids.org.mint(); // "org_3k9…"
|
|
154
|
-
* ids.org.encode(uuid); // "org_…"
|
|
155
|
-
* ids.org.decode("org_…"); // uuid — throws on a wrong / missing prefix
|
|
156
|
-
* ids.agent.is(someId); // boolean
|
|
157
|
-
* ```
|
|
158
|
-
*/
|
|
159
|
-
export function createIdRegistry(prefixes) {
|
|
160
|
-
const reg = {};
|
|
161
|
-
for (const [key, prefix] of Object.entries(prefixes)) {
|
|
162
|
-
reg[key] = makeHelper(prefix);
|
|
163
|
-
}
|
|
164
|
-
return reg;
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Which entity a prefixed id belongs to, or `null` if no helper in `registry`
|
|
168
|
-
* recognises it (a raw uuid, a sentinel, a foreign prefix).
|
|
169
|
-
*
|
|
170
|
-
* A public id is **self-describing**: its prefix names its entity, so resolving
|
|
171
|
-
* one needs no surrounding context. That is the property behind every
|
|
172
|
-
* entity-agnostic decode — a polymorphic FK whose target is named by a sibling
|
|
173
|
-
* `*_type` column, a raw-SQL binding that only has a value, a webhook payload.
|
|
174
|
-
* The inverse does **not** hold: a bare uuid carries no entity, so encoding
|
|
175
|
-
* always needs to know which entity you meant.
|
|
176
|
-
*
|
|
177
|
-
* Standalone rather than a registry method: the registry is keyed by entity
|
|
178
|
-
* name, so a method would collide with an entity literally called `entityOf`.
|
|
179
|
-
*/
|
|
180
|
-
export function entityOf(registry, value) {
|
|
181
|
-
// Cheap reject: every prefixed id has one, and raw uuids never do.
|
|
182
|
-
if (!value.includes("_"))
|
|
183
|
-
return null;
|
|
184
|
-
for (const entity of Object.keys(registry)) {
|
|
185
|
-
if (registry[entity].is(value))
|
|
186
|
-
return entity;
|
|
187
|
-
}
|
|
188
|
-
return null;
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Decode a prefixed id belonging to **any** entity in `registry` (see
|
|
192
|
-
* {@link entityOf}), for the boundaries that can't name one: polymorphic FKs,
|
|
193
|
-
* raw-SQL id bindings, generic audit/event payloads.
|
|
194
|
-
*
|
|
195
|
-
* Tolerant by design — a value no helper recognises is returned unchanged, so an
|
|
196
|
-
* already-decoded uuid passes straight through and this is safe to apply to a
|
|
197
|
-
* value that may or may not be skinned. Use a helper's `decode`/`decodeOrNull`
|
|
198
|
-
* when you know the entity and want a wrong prefix rejected.
|
|
8
|
+
* Like `id758` itself, this module must stay isomorphic (no node-only imports):
|
|
9
|
+
* it is pulled into Drizzle schemas, client components and edge runtimes.
|
|
199
10
|
*/
|
|
200
|
-
export
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
11
|
+
export * from "id758";
|
|
12
|
+
import { decodeId, encodeId, mintId, uuidv7 } from "id758";
|
|
13
|
+
/** @deprecated Use `uuidv7` from `id758`. */
|
|
14
|
+
export const uuidGenerateId = uuidv7;
|
|
15
|
+
/** @deprecated Use `encodeId` from `id758`. */
|
|
16
|
+
export const toPrefixedId = encodeId;
|
|
17
|
+
/** @deprecated Use `decodeId` from `id758`. */
|
|
18
|
+
export const fromPrefixedId = decodeId;
|
|
19
|
+
/** @deprecated Use `mintId` from `id758`. */
|
|
20
|
+
export const base58Id = mintId;
|
|
204
21
|
//# sourceMappingURL=id.js.map
|
package/dist/id.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"id.js","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"id.js","sourceRoot":"","sources":["../src/id.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,OAAO,CAAC;AACtB,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAE3D,6CAA6C;AAC7C,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AACrC,+CAA+C;AAC/C,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AACrC,+CAA+C;AAC/C,MAAM,CAAC,MAAM,cAAc,GAAG,QAAQ,CAAC;AACvC,6CAA6C;AAC7C,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ingram-tech/nk-db",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "The Ingram Postgres data layer: one TLS-aware pg pool, raw-SQL helpers, Drizzle wiring, and a PGlite (no-Docker) dev/test harness for Next.js sites.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"test": "vitest run"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
+
"id758": "^1.0.0",
|
|
54
55
|
"zod": "^4.4.3"
|
|
55
56
|
},
|
|
56
57
|
"peerDependencies": {
|
|
@@ -68,13 +69,13 @@
|
|
|
68
69
|
}
|
|
69
70
|
},
|
|
70
71
|
"devDependencies": {
|
|
71
|
-
"@electric-sql/pglite": "^0.5.
|
|
72
|
-
"@electric-sql/pglite-socket": "^0.2.
|
|
73
|
-
"@ingram-tech/nk-dev": "0.11.
|
|
74
|
-
"@types/node": "^26.
|
|
75
|
-
"@types/pg": "^8.
|
|
72
|
+
"@electric-sql/pglite": "^0.5.5",
|
|
73
|
+
"@electric-sql/pglite-socket": "^0.2.8",
|
|
74
|
+
"@ingram-tech/nk-dev": "0.11.1",
|
|
75
|
+
"@types/node": "^26.2.0",
|
|
76
|
+
"@types/pg": "^8.23.1",
|
|
76
77
|
"drizzle-orm": "^0.45.2",
|
|
77
|
-
"pg": "^8.
|
|
78
|
+
"pg": "^8.23.0",
|
|
78
79
|
"typescript": "^7.0.2",
|
|
79
80
|
"vitest": "^4.1.10"
|
|
80
81
|
}
|