generated from coulomb/repo-seed
Implement CE-WP-0002 T01-T02: engine types + PDF viewer adapter spike
T01: shared engine types (Document, Selector union, Annotation, EvidenceItem, branded IDs with newId factory) per wiki/SharedContracts.md §1-§3. T02: react-pdf-highlighter-plus v1.1.4 spike behind the §5 DocumentViewerAdapter contract in src/anchor/. Pure round-trip math extracted to pdf-selector-math.ts with 11 unit tests proving lossless capture → selectors → JSON → restored-rects. ADR-0004 accepted; full user-flow Playwright verification deferred to T09. Adds Vite app shell (index.html, src/app/SpikeApp.tsx) so the spike is exercisable via pnpm dev. tsconfig --noEmit prevents tsc -b from littering src/ with stray .js outputs. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
55
src/shared/ids.ts
Normal file
55
src/shared/ids.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Branded ID types and the `newId(kind)` factory.
|
||||
*
|
||||
* Implements the identifier portion of `wiki/SharedContracts.md` §1 and
|
||||
* `wiki/ArchitectureOverview.md` §3.2. Each branded type is structurally a
|
||||
* `string` but nominally distinct, so passing an `AnnotationId` where a
|
||||
* `DocumentId` is required is a compile-time error.
|
||||
*/
|
||||
|
||||
declare const __brand: unique symbol;
|
||||
|
||||
type Brand<K, T extends string> = K & { readonly [__brand]: T };
|
||||
|
||||
export type DocumentId = Brand<string, "DocumentId">;
|
||||
export type RepresentationId = Brand<string, "RepresentationId">;
|
||||
export type AnnotationId = Brand<string, "AnnotationId">;
|
||||
export type EvidenceItemId = Brand<string, "EvidenceItemId">;
|
||||
export type EvidenceSetId = Brand<string, "EvidenceSetId">;
|
||||
export type EvidenceLinkId = Brand<string, "EvidenceLinkId">;
|
||||
export type CitationCardId = Brand<string, "CitationCardId">;
|
||||
export type CitationRecoveryAttemptId = Brand<string, "CitationRecoveryAttemptId">;
|
||||
|
||||
export type IdKindMap = {
|
||||
document: DocumentId;
|
||||
representation: RepresentationId;
|
||||
annotation: AnnotationId;
|
||||
evidence: EvidenceItemId;
|
||||
"evidence-set": EvidenceSetId;
|
||||
"evidence-link": EvidenceLinkId;
|
||||
"citation-card": CitationCardId;
|
||||
"citation-recovery": CitationRecoveryAttemptId;
|
||||
};
|
||||
|
||||
export type IdKind = keyof IdKindMap;
|
||||
|
||||
const PREFIXES: Record<IdKind, string> = {
|
||||
document: "doc",
|
||||
representation: "rep",
|
||||
annotation: "ann",
|
||||
evidence: "ev",
|
||||
"evidence-set": "evset",
|
||||
"evidence-link": "evlink",
|
||||
"citation-card": "card",
|
||||
"citation-recovery": "crec",
|
||||
};
|
||||
|
||||
/**
|
||||
* Mint a new branded identifier of the requested kind.
|
||||
*
|
||||
* IDs use the shape `<prefix>_<uuid>` so they are human-recognizable when
|
||||
* they show up in logs, URLs, or stored JSON.
|
||||
*/
|
||||
export function newId<K extends IdKind>(kind: K): IdKindMap[K] {
|
||||
return `${PREFIXES[kind]}_${crypto.randomUUID()}` as IdKindMap[K];
|
||||
}
|
||||
Reference in New Issue
Block a user