Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 161 additions & 1 deletion packages/core/src/v3/isomorphic/friendlyId.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,22 @@ import {
WebhookDeliveryId,
RUN_OPS_ID_LENGTH,
RUN_OPS_ID_REGION_INDEX,
RUN_OPS_ID_SHARD_INDEX,
RUN_OPS_ID_VERSION,
RUN_OPS_ID_VERSION_2,
RUN_OPS_ID_VERSION_INDEX,
base32hexDecode,
base32hexEncode,
generateRunOpsId,
generateRunOpsIdV2,
parseRunId,
parseRunOpsIdBody,
parseRunOpsIdV2Body,
} from "./friendlyId.js";

/** Every legal gen-2 shard char: the full DNS-safe lowercase range. */
const SHARD_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789".split("");

const CUID_LEN = 25;

describe("RunId + WaitpointId mint cuid by default; run-ops v1 via generateRunOpsId", () => {
Expand Down Expand Up @@ -173,7 +181,8 @@ describe("parseRunId — version-char discrimination (not length)", () => {

it("falls back to legacy on a malformed v1 (bad alphabet / wrong version char)", () => {
expect(parseRunId(`run_${"A".repeat(25)}1`).format).toBe("legacy"); // uppercase core
expect(parseRunId(`run_${"a".repeat(25)}2`).format).toBe("legacy"); // wrong version
expect(parseRunId(`run_${"a".repeat(25)}9`).format).toBe("legacy"); // unallocated version
expect(parseRunId(`run_${"a".repeat(25)}2`).format).toBe("b32hexV2"); // "2" is now gen-2
expect(parseRunId(`run_${"a".repeat(24)}-1`).format).toBe("legacy"); // region char not [a-z0-9]
expect(parseRunId(`run_${"a".repeat(27)}`).format).toBe("legacy"); // old 27-char shape
});
Expand Down Expand Up @@ -250,3 +259,154 @@ describe("WebhookDeliveryId (time-encoded)", () => {
expect(WebhookDeliveryId.parseTimestamp(`whd_${"0".repeat(24)}9`)).toBeUndefined();
});
});

describe("generateRunOpsIdV2 — gen-2 id spec (shard char at 24, version '2' at 25)", () => {
afterEach(() => vi.useRealTimers());

it("emits <24-char base32hex core><shard char><version '2'> — 26 chars total", () => {
const id = generateRunOpsIdV2("a");
expect(id.length).toBe(RUN_OPS_ID_LENGTH);
expect(id).toMatch(/^[0-9a-v]{24}[a-z0-9]2$/);
expect(id[RUN_OPS_ID_VERSION_INDEX]).toBe(RUN_OPS_ID_VERSION_2);
expect(id[RUN_OPS_ID_SHARD_INDEX]).toBe("a");
});

it("round-trips every legal shard char [a-z0-9] through parseRunOpsIdV2Body", () => {
for (const c of SHARD_CHARS) {
const id = generateRunOpsIdV2(c);
const parsed = parseRunOpsIdV2Body(id);
expect(parsed).toBeDefined();
expect(parsed?.shard).toBe(c);
expect(parsed?.version).toBe(RUN_OPS_ID_VERSION_2);
// the core survives the round-trip: its bytes re-encode to the id's first 24 chars
expect(base32hexEncode(base32hexDecode(id.slice(0, 24)))).toBe(id.slice(0, 24));
}
});

it("throws on a shard char outside [a-z0-9] (fail loud, never mint an unroutable id)", () => {
for (const bad of ["", "-", "_", "A", "ab", " ", "/"]) {
expect(() => generateRunOpsIdV2(bad)).toThrow(/shard/i);
}
});

it("only ever uses lowercase [a-z0-9] and NEVER '-' (DNS-1123 / pod-name invariant)", () => {
for (let i = 0; i < 5_000; i++) {
const id = generateRunOpsIdV2(SHARD_CHARS[i % SHARD_CHARS.length]!);
expect(id).toMatch(/^[a-z0-9]+$/);
expect(id).not.toContain("-");
}
});

it("sorts lexicographically in creation order at ms resolution, like gen-1", () => {
vi.useFakeTimers();
const t = new Date("2026-07-04T12:00:00.000Z").getTime();
vi.setSystemTime(t);
const a = generateRunOpsIdV2("a");
vi.setSystemTime(t + 1000);
const b = generateRunOpsIdV2("a");
vi.setSystemTime(t + 3);
const c = generateRunOpsIdV2("a");
expect([b, c, a].sort()).toEqual([a, c, b]);
});

it("decode recovers the exact ms timestamp", () => {
vi.useFakeTimers();
const t = new Date("2026-07-04T12:34:56.789Z");
vi.setSystemTime(t);
expect(parseRunOpsIdV2Body(generateRunOpsIdV2("e"))?.timestamp.getTime()).toBe(t.getTime());
});

it("is unique across many mints in the same ms (72 bits of CSPRNG)", () => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-07-04T00:00:00.000Z"));
const n = 2_000;
expect(new Set(Array.from({ length: n }, () => generateRunOpsIdV2("a"))).size).toBe(n);
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});

describe("parseRunOpsIdV2Body — the mirror of the v1 shape check", () => {
it("rejects a body that is not exactly 26 chars", () => {
const core = "a".repeat(24);
expect(parseRunOpsIdV2Body("")).toBeUndefined();
expect(parseRunOpsIdV2Body(`${core}2`)).toBeUndefined(); // 25
expect(parseRunOpsIdV2Body(`${core}ee2`)).toBeUndefined(); // 27
expect(parseRunOpsIdV2Body("a".repeat(40))).toBeUndefined();
});

it("rejects a body without '2' at index 25", () => {
const core = "a".repeat(24);
for (const version of ["1", "0", "3", "z", "-"]) {
expect(parseRunOpsIdV2Body(`${core}e${version}`)).toBeUndefined();
}
});

it("rejects a body whose 24-char core is not base32hex", () => {
for (const badCore of ["w".repeat(24), "z".repeat(24), "A".repeat(24), `${"a".repeat(23)}-`]) {
expect(parseRunOpsIdV2Body(`${badCore}e2`)).toBeUndefined();
}
});

it("rejects a body whose char at index 24 is outside [a-z0-9]", () => {
const core = "a".repeat(24);
for (const badShard of ["-", "_", "A", ".", " "]) {
expect(parseRunOpsIdV2Body(`${core}${badShard}2`)).toBeUndefined();
}
});

it("never throws, for any input string", () => {
for (const input of ["", "x", "-".repeat(26), " ".repeat(26), "\u{1F642}".repeat(26)]) {
expect(() => parseRunOpsIdV2Body(input)).not.toThrow();
}
});
});

describe("gen-1 and gen-2 parsers reject each other (the disjointness foundation)", () => {
it("parseRunOpsIdBody rejects every gen-2 id", () => {
for (const c of SHARD_CHARS) {
expect(parseRunOpsIdBody(generateRunOpsIdV2(c))).toBeUndefined();
}
});

it("parseRunOpsIdV2Body rejects every gen-1 v1 id", () => {
for (const region of [undefined, "us-east-1", "us-west-2", "eu-central-1"]) {
expect(parseRunOpsIdV2Body(generateRunOpsId(region))).toBeUndefined();
}
});

it("generateRunOpsId still mints v1 ids — the gen-1 generator is unchanged", () => {
const id = generateRunOpsId("us-east-1");
expect(id).toMatch(/^[0-9a-v]{24}[a-z0-9]1$/);
expect(id[RUN_OPS_ID_VERSION_INDEX]).toBe(RUN_OPS_ID_VERSION);
expect(parseRunOpsIdBody(id)?.region).toBe("e");
});

it("the shard index and the region index are the same position", () => {
expect(RUN_OPS_ID_SHARD_INDEX).toBe(RUN_OPS_ID_REGION_INDEX);
});
});

describe("parseRunId — v2 arm", () => {
it("parses a gen-2 friendly id as partitioned with its shard + version", () => {
const parsed = parseRunId(`run_${generateRunOpsIdV2("e")}`);
expect(parsed).toMatchObject({
format: "b32hexV2",
table: "partitioned",
shard: "e",
version: "2",
});
});

it("still parses a gen-1 v1 friendly id as b32hex — the v1 arm is unchanged", () => {
expect(parseRunId(`run_${generateRunOpsId("us-west-2")}`)).toMatchObject({
format: "b32hex",
table: "partitioned",
region: "w",
version: "1",
});
});

it("classifies a gen-2 body without the run_ prefix, and under a wrong prefix, legacy", () => {
expect(parseRunId(generateRunOpsIdV2("a")).format).toBe("legacy");
expect(parseRunId(`waitpoint_${generateRunOpsIdV2("a")}`).format).toBe("legacy");
});
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.
84 changes: 73 additions & 11 deletions packages/core/src/v3/isomorphic/friendlyId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ export const RUN_OPS_ID_LENGTH = 26;
export const RUN_OPS_ID_REGION_INDEX = 24;
export const RUN_OPS_ID_VERSION_INDEX = 25;
export const RUN_OPS_ID_VERSION = "1";
// Gen-2 id: same 26-char layout, but index 24 carries a routing SHARD KEY rather
// than a region char. MUST stay 26 chars: a 27-char shape could collide with the
// pre-cutover base62 format, which must keep classifying legacy.
export const RUN_OPS_ID_VERSION_2 = "2";
export const RUN_OPS_ID_SHARD_INDEX = RUN_OPS_ID_REGION_INDEX;
const RUN_OPS_ID_CORE_BYTES = 15; // 6 timestamp + 9 random → exactly 24 base32hex chars
const RUN_OPS_ID_CORE_LENGTH = 24;
const RUN_OPS_ID_TIMESTAMP_BYTES = 6;
Expand All @@ -33,6 +38,8 @@ export const DEFAULT_REGION_CHAR = "0";
// decoding), NOT part of the base32hex core — so it may use the full DNS-safe
// lowercase [a-z0-9] range (e.g. "w" for us-west-2, which is outside [0-9a-v]).
const REGION_CHAR_PATTERN = /^[a-z0-9]$/;
// Same slot, same range: the gen-2 shard key is a region char's positional twin.
const SHARD_CHAR_PATTERN = REGION_CHAR_PATTERN;
/** One lowercase [a-z0-9] char per supported region, at RUN_OPS_ID_REGION_INDEX. */
export const REGION_CODES: Readonly<Record<string, string>> = {
"us-east-1": "e",
Expand Down Expand Up @@ -110,27 +117,47 @@ export function base32hexDecode(s: string): Uint8Array {
return Uint8Array.from(out);
}

// Shared by both generations. The buffer MUST be per-call — a hoisted one would
// let concurrent mints overwrite each other's bytes.
function mintRunOpsIdCore(): string {
const core = new Uint8Array(RUN_OPS_ID_CORE_BYTES);

let ms = Date.now();
for (let i = RUN_OPS_ID_TIMESTAMP_BYTES - 1; i >= 0; i--) {
core[i] = ms % 256;
ms = Math.floor(ms / 256);
}
getRandomValues(core.subarray(RUN_OPS_ID_TIMESTAMP_BYTES));

return base32hexEncode(core);
}

/**
* Mint a run-ops v1 id body (26 chars, no prefix): 24-char base32hex core
* (6-byte ms timestamp + 9 CSPRNG bytes) + region char + version char "1".
* The trailing version char at RUN_OPS_ID_VERSION_INDEX is the residency
* discriminator — see runOpsResidency.ts.
*/
export function generateRunOpsId(region?: string): string {
const core = new Uint8Array(RUN_OPS_ID_CORE_BYTES);
return `${mintRunOpsIdCore()}${regionCharForRegion(region)}${RUN_OPS_ID_VERSION}`;
}

let ms = Date.now();
for (let i = RUN_OPS_ID_TIMESTAMP_BYTES - 1; i >= 0; i--) {
core[i] = ms % 256;
ms = Math.floor(ms / 256);
/**
* Mint a gen-2 id body (26 chars, no prefix): the same core, then the shard key,
* then version char "2". Throws on a shard char outside [a-z0-9] — an id that
* cannot be routed must never be minted.
*/
export function generateRunOpsIdV2(shardChar: string): string {
if (!SHARD_CHAR_PATTERN.test(shardChar)) {
throw new Error(`invalid run-ops shard char: ${JSON.stringify(shardChar)}`);
}
getRandomValues(core.subarray(RUN_OPS_ID_TIMESTAMP_BYTES));

return `${base32hexEncode(core)}${regionCharForRegion(region)}${RUN_OPS_ID_VERSION}`;
return `${mintRunOpsIdCore()}${shardChar}${RUN_OPS_ID_VERSION_2}`;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

export type ParsedRunId =
| { format: "b32hex"; table: "partitioned"; timestamp: Date; region: string; version: string }
| { format: "b32hexV2"; table: "partitioned"; timestamp: Date; shard: string; version: string }
| { format: "legacy"; table: "legacy" };

const LEGACY_RUN_ID: ParsedRunId = { format: "legacy", table: "legacy" };
Expand All @@ -149,6 +176,15 @@ export function parseRunOpsIdBody(
const region = body[RUN_OPS_ID_REGION_INDEX] ?? "";
if (!REGION_CHAR_PATTERN.test(region)) return undefined;

const timestamp = parseRunOpsIdCoreTimestamp(body);
if (timestamp === undefined) return undefined;

return { timestamp, region, version: RUN_OPS_ID_VERSION };
}

// Decode the leading 24-char core and recover its embedded ms timestamp.
// Returns undefined (never throws) when the core is outside the base32hex alphabet.
function parseRunOpsIdCoreTimestamp(body: string): Date | undefined {
let core: Uint8Array;
try {
core = base32hexDecode(body.slice(0, RUN_OPS_ID_CORE_LENGTH));
Expand All @@ -161,19 +197,45 @@ export function parseRunOpsIdBody(
ms = ms * 256 + (core[i] ?? 0);
}

return { timestamp: new Date(ms), region, version: RUN_OPS_ID_VERSION };
return new Date(ms);
}

/**
* Parse a gen-2 id body (no prefix): the mirror of {@link parseRunOpsIdBody},
* requiring version "2" at index 25 and a shard key in [a-z0-9] at index 24.
* Total: returns undefined for any other string, and never throws.
*/
export function parseRunOpsIdV2Body(
body: string
): { timestamp: Date; shard: string; version: string } | undefined {
if (body.length !== RUN_OPS_ID_LENGTH) return undefined;
if (body[RUN_OPS_ID_VERSION_INDEX] !== RUN_OPS_ID_VERSION_2) return undefined;
const shard = body[RUN_OPS_ID_SHARD_INDEX] ?? "";
if (!SHARD_CHAR_PATTERN.test(shard)) return undefined;

const timestamp = parseRunOpsIdCoreTimestamp(body);
if (timestamp === undefined) return undefined;

return { timestamp, shard, version: RUN_OPS_ID_VERSION_2 };
}

/** True if the (prefixless) id body is a well-formed run-ops v1 id. */
export function isRunOpsIdBody(body: string): boolean {
return parseRunOpsIdBody(body) !== undefined;
}

/** Parse a `run_`-prefixed friendly id; anything not a well-formed v1 id is legacy. */
/** Parse a `run_`-prefixed friendly id; anything not a well-formed v1/gen-2 id is legacy. */
export function parseRunId(id: string): ParsedRunId {
if (!id.startsWith("run_")) return LEGACY_RUN_ID;
const parsed = parseRunOpsIdBody(id.slice(4));
return parsed ? { format: "b32hex", table: "partitioned", ...parsed } : LEGACY_RUN_ID;
const body = id.slice(4);

const v1 = parseRunOpsIdBody(body);
if (v1) return { format: "b32hex", table: "partitioned", ...v1 };

const v2 = parseRunOpsIdV2Body(body);
if (v2) return { format: "b32hexV2", table: "partitioned", ...v2 };

return LEGACY_RUN_ID;
}

export function generateInternalId(): string {
Expand Down
Loading
Loading