Skip to content
File

Blob: src/contracts/common.ts

typescript90 lines
1import { eg, type TypeFromCodec } from "@cloudflare/util-en-garde";
2 
3const DURABLE_ENTITY_ID_PATTERN = /^(usr|prj|run|whk)_[0-9A-Za-z]{22}$/u;
4const SESSION_TOKEN_PATTERN = /^[A-Za-z0-9_-]{20,}$/u;
5const ISO_DATE_TIME_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u;
6const SLUG_PATTERN = /^[A-Za-z0-9_-]+$/u;
7const COMMIT_SHA_PATTERN = /^[A-Fa-f0-9]{7,64}$/u;
8 
9const brandedString = <Name extends string>(name: Name, predicate: (value: string) => boolean) =>
10 eg.brand(name, eg.string, predicate);
11 
12const isIsoDateTime = (value: string): boolean =>
13 ISO_DATE_TIME_PATTERN.test(value) && !Number.isNaN(Date.parse(value)) && new Date(value).toISOString() === value;
14 
15const isNonEmptyString = (value: string): boolean => value.length > 0;
16 
17export const EntityId = brandedString("DurableEntityId", (value) => DURABLE_ENTITY_ID_PATTERN.test(value));
18export type EntityId = TypeFromCodec<typeof EntityId>;
19 
20export const OpaqueId = brandedString("OpaqueId", isNonEmptyString);
21export type OpaqueId = TypeFromCodec<typeof OpaqueId>;
22 
23export const UserId = brandedString("UserId", (value) => /^usr_[0-9A-Za-z]{22}$/u.test(value));
24export type UserId = TypeFromCodec<typeof UserId>;
25 
26export const ProjectId = brandedString("ProjectId", (value) => /^prj_[0-9A-Za-z]{22}$/u.test(value));
27export type ProjectId = TypeFromCodec<typeof ProjectId>;
28 
29export const RunId = brandedString("RunId", (value) => /^run_[0-9A-Za-z]{22}$/u.test(value));
30export type RunId = TypeFromCodec<typeof RunId>;
31 
32export const WebhookId = brandedString("WebhookId", (value) => /^whk_[0-9A-Za-z]{22}$/u.test(value));
33export type WebhookId = TypeFromCodec<typeof WebhookId>;
34 
35export const SessionId = brandedString("SessionId", (value) => SESSION_TOKEN_PATTERN.test(value));
36export type SessionId = TypeFromCodec<typeof SessionId>;
37 
38export const IsoDateTime = brandedString("IsoDateTime", isIsoDateTime);
39export type IsoDateTime = TypeFromCodec<typeof IsoDateTime>;
40 
41export const UnixTimestampMs = eg.brand("UnixTimestampMs", eg.number, (value) => Number.isInteger(value) && value >= 0);
42export type UnixTimestampMs = TypeFromCodec<typeof UnixTimestampMs>;
43 
44export const OwnerSlug = brandedString("OwnerSlug", (value) => SLUG_PATTERN.test(value));
45export type OwnerSlug = TypeFromCodec<typeof OwnerSlug>;
46 
47export const ProjectSlug = brandedString("ProjectSlug", (value) => SLUG_PATTERN.test(value));
48export type ProjectSlug = TypeFromCodec<typeof ProjectSlug>;
49 
50export const BranchName = brandedString("BranchName", isNonEmptyString);
51export type BranchName = TypeFromCodec<typeof BranchName>;
52 
53export const CommitSha = brandedString("CommitSha", (value) => COMMIT_SHA_PATTERN.test(value));
54export type CommitSha = TypeFromCodec<typeof CommitSha>;
55 
56export const TriggerType = eg.union([eg.literal("manual"), eg.literal("webhook")]);
57export type TriggerType = TypeFromCodec<typeof TriggerType>;
58 
59export const RunStatus = eg.union([
60 eg.literal("queued"),
61 eg.literal("starting"),
62 eg.literal("running"),
63 eg.literal("cancel_requested"),
64 eg.literal("canceling"),
65 eg.literal("passed"),
66 eg.literal("failed"),
67 eg.literal("canceled"),
68]);
69export type RunStatus = TypeFromCodec<typeof RunStatus>;
70 
71export const isRunStatus = (value: unknown): value is RunStatus => RunStatus.is(value);
72 
73export const toRunStatusOrNull = (value: string | undefined): RunStatus | null =>
74 value && isRunStatus(value) ? value : null;
75 
76export const WebhookProvider = eg.union([eg.literal("github"), eg.literal("gitlab"), eg.literal("gitea")]);
77export type WebhookProvider = TypeFromCodec<typeof WebhookProvider>;
78 
79export const UserSummary = eg.exactStrict(
80 eg.object({
81 id: UserId,
82 slug: OwnerSlug,
83 email: eg.string,
84 displayName: eg.string,
85 createdAt: IsoDateTime,
86 disabledAt: eg.union([IsoDateTime, eg.null]),
87 }),
88);
89export type UserSummary = TypeFromCodec<typeof UserSummary>;