@siteping/adapter-kit 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../core/src/testing.ts"],"sourcesContent":["/**\n * Shared conformance test suite for `SitepingStore` implementations.\n *\n * Adapters import this and run it with their store factory to verify they\n * satisfy the full store contract — no need to write the same 40+ tests\n * from scratch.\n *\n * @example\n * ```ts\n * import { testSitepingStore } from '@siteping/core/testing'\n * import { DrizzleStore } from '../src/index.js'\n *\n * testSitepingStore(() => new DrizzleStore(db))\n * ```\n */\n\nimport { beforeEach, describe, expect, it } from \"vitest\";\nimport type { DiagnosticsSnapshot, FeedbackCreateInput, SitepingStore } from \"./types.js\";\nimport { isStoreDuplicate, StoreNotFoundError } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Test fixture\n// ---------------------------------------------------------------------------\n\nfunction createInput(overrides?: Partial<FeedbackCreateInput>): FeedbackCreateInput {\n return {\n projectName: \"test-project\",\n type: \"bug\",\n message: \"Something is broken\",\n status: \"open\",\n url: \"https://example.com\",\n viewport: \"1920x1080\",\n userAgent: \"Mozilla/5.0\",\n authorName: \"Alice\",\n authorEmail: \"alice@test.com\",\n clientId: `client-${Date.now()}-${Math.random()}`,\n annotations: [\n {\n cssSelector: \"div.main\",\n xpath: \"/html/body/div\",\n textSnippet: \"Hello\",\n elementTag: \"DIV\",\n elementId: \"main\",\n textPrefix: \"before\",\n textSuffix: \"after\",\n fingerprint: \"3:1:abc\",\n neighborText: \"sibling\",\n xPct: 0.1,\n yPct: 0.2,\n wPct: 0.5,\n hPct: 0.3,\n scrollX: 0,\n scrollY: 100,\n viewportW: 1920,\n viewportH: 1080,\n devicePixelRatio: 2,\n },\n ],\n ...overrides,\n };\n}\n\nconst MINIMAL_ANNOTATION = {\n cssSelector: \"div\",\n xpath: \"/div\",\n textSnippet: \"\",\n elementTag: \"DIV\",\n textPrefix: \"\",\n textSuffix: \"\",\n fingerprint: \"1:0:x\",\n neighborText: \"\",\n xPct: 0,\n yPct: 0,\n wPct: 1,\n hPct: 1,\n scrollX: 0,\n scrollY: 0,\n viewportW: 1920,\n viewportH: 1080,\n devicePixelRatio: 1,\n};\n\n// ---------------------------------------------------------------------------\n// Conformance suite\n// ---------------------------------------------------------------------------\n\n/** Tuning knobs for backends whose documented contract legitimately varies. */\nexport interface StoreConformanceOptions {\n /**\n * How `createFeedback` reacts to a duplicate `clientId` — both are valid\n * per the `SitepingStore` contract:\n * - `\"return\"` (default): idempotently return the existing record.\n * - `\"throw\"`: throw `StoreDuplicateError` (matched via `isStoreDuplicate`).\n */\n duplicateBehavior?: \"return\" | \"throw\" | undefined;\n /**\n * Whether `search` matches case-insensitively. Defaults to `true` (the\n * in-memory pipeline's behavior). Set to `false` for SQL backends whose\n * collation is case-sensitive — the suite then only asserts same-case\n * substring matching.\n */\n caseInsensitiveSearch?: boolean | undefined;\n}\n\n/**\n * Run the full `SitepingStore` conformance test suite.\n *\n * @param factory — called before each test to create a fresh, empty store instance. May be async.\n * @param options — contract variations, see {@link StoreConformanceOptions}.\n */\nexport function testSitepingStore(\n factory: () => SitepingStore | Promise<SitepingStore>,\n options?: StoreConformanceOptions,\n): void {\n const duplicateBehavior = options?.duplicateBehavior ?? \"return\";\n const caseInsensitiveSearch = options?.caseInsensitiveSearch ?? true;\n\n describe(\"SitepingStore conformance\", () => {\n let store: SitepingStore;\n\n beforeEach(async () => {\n store = await factory();\n });\n\n // ------------------------------------------------------------------\n // createFeedback\n // ------------------------------------------------------------------\n\n describe(\"createFeedback\", () => {\n it(\"creates a feedback and returns a FeedbackRecord\", async () => {\n const record = await store.createFeedback(createInput());\n\n expect(record.id).toBeDefined();\n expect(record.projectName).toBe(\"test-project\");\n expect(record.type).toBe(\"bug\");\n expect(record.message).toBe(\"Something is broken\");\n expect(record.status).toBe(\"open\");\n expect(record.resolvedAt).toBeNull();\n expect(record.createdAt).toBeInstanceOf(Date);\n expect(record.updatedAt).toBeInstanceOf(Date);\n });\n\n it(\"creates annotations with feedbackId reference\", async () => {\n const record = await store.createFeedback(createInput());\n\n expect(record.annotations).toHaveLength(1);\n const [ann] = record.annotations;\n expect(ann).toBeDefined();\n expect(ann?.id).toBeDefined();\n expect(ann?.feedbackId).toBe(record.id);\n expect(ann?.cssSelector).toBe(\"div.main\");\n expect(ann?.xPct).toBe(0.1);\n expect(ann?.elementId).toBe(\"main\");\n expect(ann?.createdAt).toBeInstanceOf(Date);\n });\n\n it(\"sets elementId to null when undefined in input\", async () => {\n const record = await store.createFeedback(createInput({ annotations: [{ ...MINIMAL_ANNOTATION }] }));\n expect(record.annotations[0]?.elementId).toBeNull();\n });\n\n it(\"persists anchorKey when provided\", async () => {\n const record = await store.createFeedback(\n createInput({\n annotations: [\n {\n ...MINIMAL_ANNOTATION,\n cssSelector: \"section\",\n xpath: \"/section\",\n textSnippet: \"Services\",\n elementTag: \"SECTION\",\n anchorKey: \"order-card.services\",\n },\n ],\n }),\n );\n expect(record.annotations[0]?.anchorKey).toBe(\"order-card.services\");\n });\n\n it(\"persists anchorKey as null when omitted\", async () => {\n const record = await store.createFeedback(createInput());\n expect(record.annotations[0]?.anchorKey).toBeNull();\n });\n\n it(\"persists screenshotUrl as null when no data URL is provided\", async () => {\n const record = await store.createFeedback(createInput());\n expect(record.screenshotUrl).toBeNull();\n });\n\n it(\"persists the screenshot data URL inline when no external storage is configured\", async () => {\n // Stores without external storage (memory, localStorage) keep the\n // data URL as-is. Adapter-prisma with a `screenshotStorage` replaces\n // it with the remote URL — that path is covered by adapter-prisma tests.\n const dataUrl = \"data:image/jpeg;base64,/9j/4AAQ\"; // truncated but valid prefix\n const record = await store.createFeedback(createInput({ screenshotDataUrl: dataUrl }));\n expect(record.screenshotUrl).toBe(dataUrl);\n });\n\n it(\"persists screenshotRegion verbatim when provided\", async () => {\n const region = { xPct: 0.1234, yPct: 0.5678, wPct: 0.25, hPct: 0.125 };\n const record = await store.createFeedback(createInput({ screenshotRegion: region }));\n expect(record.screenshotRegion).toEqual(region);\n });\n\n it(\"persists screenshotRegion as null when omitted\", async () => {\n const record = await store.createFeedback(createInput());\n expect(record.screenshotRegion).toBeNull();\n });\n\n it(\"persists diagnostics verbatim when provided\", async () => {\n const diagnostics: DiagnosticsSnapshot = {\n console: [{ level: \"error\", timestamp: \"2026-01-01T00:00:00.000Z\", message: \"boom\" }],\n network: [\n {\n url: \"https://api.test/things\",\n method: \"GET\",\n status: 500,\n durationMs: 123,\n timestamp: \"2026-01-01T00:00:01.000Z\",\n },\n ],\n };\n const record = await store.createFeedback(createInput({ diagnostics }));\n expect(record.diagnostics).toEqual(diagnostics);\n });\n\n it(\"persists diagnostics as null when omitted\", async () => {\n const record = await store.createFeedback(createInput());\n expect(record.diagnostics).toBeNull();\n });\n\n if (duplicateBehavior === \"return\") {\n it(\"deduplicates by clientId (idempotent)\", async () => {\n const input = createInput({ clientId: \"same-id\" });\n const first = await store.createFeedback(input);\n const second = await store.createFeedback(input);\n\n expect(second.id).toBe(first.id);\n const { total } = await store.getFeedbacks({ projectName: \"test-project\" });\n expect(total).toBe(1);\n });\n } else {\n it(\"throws StoreDuplicateError on duplicate clientId\", async () => {\n const input = createInput({ clientId: \"same-id\" });\n await store.createFeedback(input);\n await expect(store.createFeedback(input)).rejects.toSatisfy(isStoreDuplicate);\n\n const { total } = await store.getFeedbacks({ projectName: \"test-project\" });\n expect(total).toBe(1);\n });\n }\n\n it(\"stores newest feedbacks first\", async () => {\n const a = await store.createFeedback(createInput({ message: \"first\" }));\n const b = await store.createFeedback(createInput({ message: \"second\" }));\n const { feedbacks } = await store.getFeedbacks({ projectName: \"test-project\" });\n expect(feedbacks[0]?.id).toBe(b.id);\n expect(feedbacks[1]?.id).toBe(a.id);\n });\n\n it(\"generates unique IDs across calls\", async () => {\n const a = await store.createFeedback(createInput());\n const b = await store.createFeedback(createInput());\n expect(a.id).not.toBe(b.id);\n });\n\n it(\"creates feedbacks with no annotations\", async () => {\n const record = await store.createFeedback(createInput({ annotations: [] }));\n expect(record.annotations).toHaveLength(0);\n });\n });\n\n // ------------------------------------------------------------------\n // getFeedbacks\n // ------------------------------------------------------------------\n\n describe(\"getFeedbacks\", () => {\n it(\"returns empty array when no feedbacks\", async () => {\n const result = await store.getFeedbacks({ projectName: \"test-project\" });\n expect(result.feedbacks).toHaveLength(0);\n expect(result.total).toBe(0);\n });\n\n it(\"filters by projectName\", async () => {\n await store.createFeedback(createInput({ projectName: \"a\" }));\n await store.createFeedback(createInput({ projectName: \"b\" }));\n\n const result = await store.getFeedbacks({ projectName: \"a\" });\n expect(result.total).toBe(1);\n expect(result.feedbacks[0]?.projectName).toBe(\"a\");\n });\n\n it(\"filters by type\", async () => {\n await store.createFeedback(createInput({ type: \"bug\" }));\n await store.createFeedback(createInput({ type: \"question\" }));\n\n const result = await store.getFeedbacks({ projectName: \"test-project\", type: \"bug\" });\n expect(result.feedbacks).toHaveLength(1);\n expect(result.feedbacks[0]?.type).toBe(\"bug\");\n });\n\n it(\"filters by status\", async () => {\n const fb = await store.createFeedback(createInput());\n await store.updateFeedback(fb.id, { status: \"resolved\", resolvedAt: new Date() });\n await store.createFeedback(createInput());\n\n const result = await store.getFeedbacks({ projectName: \"test-project\", status: \"open\" });\n expect(result.feedbacks).toHaveLength(1);\n });\n\n it(\"filters by status in_progress\", async () => {\n const fb = await store.createFeedback(createInput());\n await store.updateFeedback(fb.id, { status: \"in_progress\", resolvedAt: null });\n await store.createFeedback(createInput());\n\n const result = await store.getFeedbacks({ projectName: \"test-project\", status: \"in_progress\" });\n expect(result.feedbacks).toHaveLength(1);\n expect(result.feedbacks[0]?.status).toBe(\"in_progress\");\n });\n\n it(\"filters by status wont_fix\", async () => {\n const fb = await store.createFeedback(createInput());\n await store.updateFeedback(fb.id, { status: \"wont_fix\", resolvedAt: new Date() });\n await store.createFeedback(createInput());\n\n const result = await store.getFeedbacks({ projectName: \"test-project\", status: \"wont_fix\" });\n expect(result.feedbacks).toHaveLength(1);\n expect(result.feedbacks[0]?.status).toBe(\"wont_fix\");\n });\n\n it(\"filters by a statuses bucket (any of the listed values)\", async () => {\n const open = await store.createFeedback(createInput());\n const prog = await store.createFeedback(createInput());\n await store.updateFeedback(prog.id, { status: \"in_progress\", resolvedAt: null });\n const resolved = await store.createFeedback(createInput());\n await store.updateFeedback(resolved.id, { status: \"resolved\", resolvedAt: new Date() });\n\n const result = await store.getFeedbacks({\n projectName: \"test-project\",\n statuses: [\"open\", \"in_progress\"],\n });\n expect(result.total).toBe(2);\n expect(result.feedbacks.map((f) => f.id).sort()).toEqual([open.id, prog.id].sort());\n });\n\n it(\"paginates a statuses bucket with the correct total\", async () => {\n for (let i = 0; i < 3; i++) {\n const fb = await store.createFeedback(createInput());\n await store.updateFeedback(fb.id, { status: \"in_progress\", resolvedAt: null });\n }\n // A closed record that must never appear in the open bucket.\n const closed = await store.createFeedback(createInput());\n await store.updateFeedback(closed.id, { status: \"resolved\", resolvedAt: new Date() });\n\n const page1 = await store.getFeedbacks({\n projectName: \"test-project\",\n statuses: [\"open\", \"in_progress\"],\n page: 1,\n limit: 2,\n });\n expect(page1.total).toBe(3);\n expect(page1.feedbacks).toHaveLength(2);\n\n const page2 = await store.getFeedbacks({\n projectName: \"test-project\",\n statuses: [\"open\", \"in_progress\"],\n page: 2,\n limit: 2,\n });\n expect(page2.total).toBe(3);\n expect(page2.feedbacks).toHaveLength(1);\n });\n\n it(\"prefers statuses over status when both are set\", async () => {\n await store.createFeedback(createInput());\n const prog = await store.createFeedback(createInput());\n await store.updateFeedback(prog.id, { status: \"in_progress\", resolvedAt: null });\n\n // `status: \"open\"` alone would exclude the in_progress record, but the\n // `statuses` bucket wins — both records match.\n const result = await store.getFeedbacks({\n projectName: \"test-project\",\n status: \"open\",\n statuses: [\"open\", \"in_progress\"],\n });\n expect(result.total).toBe(2);\n });\n\n it(\"ignores an empty statuses array (no status filter)\", async () => {\n await store.createFeedback(createInput());\n const prog = await store.createFeedback(createInput());\n await store.updateFeedback(prog.id, { status: \"in_progress\", resolvedAt: null });\n\n const result = await store.getFeedbacks({ projectName: \"test-project\", statuses: [] });\n expect(result.total).toBe(2);\n });\n\n it(\"filters by search (same-case substring)\", async () => {\n await store.createFeedback(createInput({ message: \"Button is broken\" }));\n await store.createFeedback(createInput({ message: \"Layout looks great\" }));\n\n const result = await store.getFeedbacks({ projectName: \"test-project\", search: \"broken\" });\n expect(result.feedbacks).toHaveLength(1);\n expect(result.feedbacks[0]?.message).toBe(\"Button is broken\");\n });\n\n if (caseInsensitiveSearch) {\n it(\"filters by search (case-insensitive)\", async () => {\n await store.createFeedback(createInput({ message: \"Button is broken\" }));\n await store.createFeedback(createInput({ message: \"Layout looks great\" }));\n\n const result = await store.getFeedbacks({ projectName: \"test-project\", search: \"BROKEN\" });\n expect(result.feedbacks).toHaveLength(1);\n expect(result.feedbacks[0]?.message).toBe(\"Button is broken\");\n });\n }\n\n it(\"filters by exact url\", async () => {\n await store.createFeedback(createInput({ url: \"https://app.test/orders/42\" }));\n await store.createFeedback(createInput({ url: \"https://app.test/dashboard\" }));\n\n const result = await store.getFeedbacks({ projectName: \"test-project\", url: \"https://app.test/dashboard\" });\n expect(result.feedbacks).toHaveLength(1);\n expect(result.feedbacks[0]?.url).toBe(\"https://app.test/dashboard\");\n });\n\n it(\"filters by urlPattern\", async () => {\n await store.createFeedback(createInput({ url: \"https://app.test/orders/42\", urlPattern: \"/orders/:id\" }));\n await store.createFeedback(createInput({ url: \"https://app.test/orders/99\", urlPattern: \"/orders/:id\" }));\n await store.createFeedback(createInput({ url: \"https://app.test/dashboard\", urlPattern: \"/dashboard\" }));\n\n const result = await store.getFeedbacks({ projectName: \"test-project\", urlPattern: \"/orders/:id\" });\n expect(result.feedbacks).toHaveLength(2);\n });\n\n it(\"persists urlPattern as null when omitted from input\", async () => {\n const record = await store.createFeedback(createInput());\n expect(record.urlPattern).toBeNull();\n });\n\n it(\"paginates correctly\", async () => {\n for (let i = 0; i < 5; i++) {\n await store.createFeedback(createInput());\n }\n\n const page1 = await store.getFeedbacks({ projectName: \"test-project\", page: 1, limit: 2 });\n expect(page1.feedbacks).toHaveLength(2);\n expect(page1.total).toBe(5);\n\n const page3 = await store.getFeedbacks({ projectName: \"test-project\", page: 3, limit: 2 });\n expect(page3.feedbacks).toHaveLength(1);\n });\n\n it(\"caps limit at 100\", async () => {\n // 105 records so a limit above the cap actually exercises it.\n for (let i = 0; i < 105; i++) {\n await store.createFeedback(createInput({ annotations: [] }));\n }\n const result = await store.getFeedbacks({ projectName: \"test-project\", limit: 200 });\n expect(result.total).toBe(105);\n expect(result.feedbacks).toHaveLength(100);\n });\n });\n\n // ------------------------------------------------------------------\n // findByClientId\n // ------------------------------------------------------------------\n\n describe(\"findByClientId\", () => {\n it(\"returns the record when found\", async () => {\n const created = await store.createFeedback(createInput({ clientId: \"find-me\" }));\n const found = await store.findByClientId(\"find-me\");\n expect(found).not.toBeNull();\n expect(found?.id).toBe(created.id);\n });\n\n it(\"returns null when not found\", async () => {\n expect(await store.findByClientId(\"nope\")).toBeNull();\n });\n });\n\n // ------------------------------------------------------------------\n // updateFeedback\n // ------------------------------------------------------------------\n\n describe(\"updateFeedback\", () => {\n it(\"updates status to resolved with resolvedAt\", async () => {\n const fb = await store.createFeedback(createInput());\n const resolvedAt = new Date();\n const updated = await store.updateFeedback(fb.id, { status: \"resolved\", resolvedAt });\n\n expect(updated.status).toBe(\"resolved\");\n expect(updated.resolvedAt).toEqual(resolvedAt);\n expect(updated.updatedAt.getTime()).toBeGreaterThanOrEqual(fb.updatedAt.getTime());\n });\n\n it(\"updates status to in_progress with resolvedAt null\", async () => {\n const fb = await store.createFeedback(createInput());\n const updated = await store.updateFeedback(fb.id, { status: \"in_progress\", resolvedAt: null });\n\n expect(updated.status).toBe(\"in_progress\");\n expect(updated.resolvedAt).toBeNull();\n });\n\n it(\"updates status to wont_fix with the given resolvedAt\", async () => {\n const fb = await store.createFeedback(createInput());\n const resolvedAt = new Date();\n const updated = await store.updateFeedback(fb.id, { status: \"wont_fix\", resolvedAt });\n\n expect(updated.status).toBe(\"wont_fix\");\n expect(updated.resolvedAt).toEqual(resolvedAt);\n });\n\n it(\"throws StoreNotFoundError for unknown id\", async () => {\n await expect(store.updateFeedback(\"unknown\", { status: \"resolved\", resolvedAt: new Date() })).rejects.toThrow(\n StoreNotFoundError,\n );\n });\n\n it(\"can reopen a closed feedback\", async () => {\n const fb = await store.createFeedback(createInput());\n await store.updateFeedback(fb.id, { status: \"resolved\", resolvedAt: new Date() });\n const reopened = await store.updateFeedback(fb.id, { status: \"open\", resolvedAt: null });\n expect(reopened.status).toBe(\"open\");\n expect(reopened.resolvedAt).toBeNull();\n\n await store.updateFeedback(fb.id, { status: \"wont_fix\", resolvedAt: new Date() });\n const reopenedAgain = await store.updateFeedback(fb.id, { status: \"open\", resolvedAt: null });\n expect(reopenedAgain.status).toBe(\"open\");\n expect(reopenedAgain.resolvedAt).toBeNull();\n });\n });\n\n // ------------------------------------------------------------------\n // deleteFeedback\n // ------------------------------------------------------------------\n\n describe(\"deleteFeedback\", () => {\n it(\"removes the feedback\", async () => {\n const fb = await store.createFeedback(createInput());\n await store.deleteFeedback(fb.id);\n const { total } = await store.getFeedbacks({ projectName: \"test-project\" });\n expect(total).toBe(0);\n });\n\n it(\"throws StoreNotFoundError for unknown id\", async () => {\n await expect(store.deleteFeedback(\"unknown\")).rejects.toThrow(StoreNotFoundError);\n });\n });\n\n // ------------------------------------------------------------------\n // deleteAllFeedbacks\n // ------------------------------------------------------------------\n\n describe(\"deleteAllFeedbacks\", () => {\n it(\"removes all feedbacks for a project but keeps others\", async () => {\n await store.createFeedback(createInput({ projectName: \"delete-me\" }));\n await store.createFeedback(createInput({ projectName: \"delete-me\" }));\n await store.createFeedback(createInput({ projectName: \"keep-me\" }));\n\n await store.deleteAllFeedbacks(\"delete-me\");\n\n expect((await store.getFeedbacks({ projectName: \"delete-me\" })).total).toBe(0);\n expect((await store.getFeedbacks({ projectName: \"keep-me\" })).total).toBe(1);\n });\n\n it(\"is a no-op when project has no feedbacks\", async () => {\n await expect(store.deleteAllFeedbacks(\"nonexistent\")).resolves.toBeUndefined();\n });\n });\n\n // ------------------------------------------------------------------\n // verifyProjectOwnership (optional contract member)\n // ------------------------------------------------------------------\n\n describe(\"verifyProjectOwnership\", () => {\n it(\"returns true for the owning project, false otherwise (when implemented)\", async () => {\n // Optional member — stores without it skip the assertion but the\n // test still runs, so implementing it later is covered automatically.\n if (!store.verifyProjectOwnership) return;\n\n const fb = await store.createFeedback(createInput({ projectName: \"owner\" }));\n await expect(store.verifyProjectOwnership(fb.id, \"owner\")).resolves.toBe(true);\n await expect(store.verifyProjectOwnership(fb.id, \"intruder\")).resolves.toBe(false);\n await expect(store.verifyProjectOwnership(\"unknown-id\", \"owner\")).resolves.toBe(false);\n });\n });\n });\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * General-purpose TypeScript utility types used across `@siteping/*`.
3
+ *
4
+ * These are kept dependency-free and re-exported from the package entry
5
+ * so adapters and integrators can rely on the same primitives the core
6
+ * uses internally.
7
+ */
8
+ /**
9
+ * Force TypeScript to expand a computed type into a flat object literal in
10
+ * tooltips and error messages. Purely cosmetic — same structural type, just
11
+ * easier to read.
12
+ *
13
+ * @example
14
+ * type Raw = Omit<FeedbackRecord, "annotations"> & { annotations: number };
15
+ * type Pretty = Prettify<Raw>; // displayed as a flat object
16
+ */
17
+ export type Prettify<T> = {
18
+ [K in keyof T]: T[K];
19
+ } & {};
20
+ /**
21
+ * Returns `Y` when `A` is exactly assignable to `B` and vice-versa,
22
+ * otherwise `N`. Powers compile-time equality assertions.
23
+ */
24
+ export type IfEquals<A, B, Y = true, N = false> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? Y : N;
25
+ /**
26
+ * Compile-time exact-type guard — resolves to `true` when `Actual` and
27
+ * `Expected` are identical, `never` otherwise. Assign the result to a
28
+ * `const _lock: AssertEqual<A, B> = true;` so any drift becomes a compile
29
+ * error at the declaration site.
30
+ */
31
+ export type AssertEqual<Actual, Expected> = IfEquals<Actual, Expected, true, never>;
32
+ /**
33
+ * JSON-serialized shape of `T` — the wire form produced by `Response.json()`
34
+ * / `JSON.stringify`: `Date` becomes ISO `string` (nullability preserved),
35
+ * arrays are serialized element-wise, everything else is untouched.
36
+ *
37
+ * Used to derive the `*Response` API types from the `*Record` store types so
38
+ * the two can never drift: add a field to `FeedbackRecord` and
39
+ * `FeedbackResponse` follows automatically.
40
+ */
41
+ export type Serialized<T> = {
42
+ [K in keyof T]: T[K] extends Date ? string : T[K] extends Date | null ? string | null : T[K] extends (infer U)[] ? Serialized<U>[] : T[K];
43
+ };
44
+ /**
45
+ * Type guard that narrows `value` to a non-null `Record<PropertyKey, unknown>`.
46
+ * Useful when validating arbitrary inputs before reading fields.
47
+ */
48
+ export declare function isRecord(value: unknown): value is Record<PropertyKey, unknown>;
49
+ /**
50
+ * Returns true when `value` is an object that exposes the requested key.
51
+ * Type-narrows `value` so the property can be accessed without further
52
+ * casting — a strictly typed replacement for `"k" in obj`.
53
+ *
54
+ * Named after the standardised `Object.hasOwn` helper rather than the
55
+ * legacy `Object.prototype.hasOwnProperty`, which the linter forbids
56
+ * shadowing.
57
+ */
58
+ export declare function hasOwn<K extends PropertyKey>(value: unknown, key: K): value is Record<K, unknown>;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * General-purpose TypeScript utility types used across `@siteping/*`.
3
+ *
4
+ * These are kept dependency-free and re-exported from the package entry
5
+ * so adapters and integrators can rely on the same primitives the core
6
+ * uses internally.
7
+ */
8
+ /**
9
+ * Force TypeScript to expand a computed type into a flat object literal in
10
+ * tooltips and error messages. Purely cosmetic — same structural type, just
11
+ * easier to read.
12
+ *
13
+ * @example
14
+ * type Raw = Omit<FeedbackRecord, "annotations"> & { annotations: number };
15
+ * type Pretty = Prettify<Raw>; // displayed as a flat object
16
+ */
17
+ export type Prettify<T> = {
18
+ [K in keyof T]: T[K];
19
+ } & {};
20
+ /**
21
+ * Returns `Y` when `A` is exactly assignable to `B` and vice-versa,
22
+ * otherwise `N`. Powers compile-time equality assertions.
23
+ */
24
+ export type IfEquals<A, B, Y = true, N = false> = (<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? Y : N;
25
+ /**
26
+ * Compile-time exact-type guard — resolves to `true` when `Actual` and
27
+ * `Expected` are identical, `never` otherwise. Assign the result to a
28
+ * `const _lock: AssertEqual<A, B> = true;` so any drift becomes a compile
29
+ * error at the declaration site.
30
+ */
31
+ export type AssertEqual<Actual, Expected> = IfEquals<Actual, Expected, true, never>;
32
+ /**
33
+ * JSON-serialized shape of `T` — the wire form produced by `Response.json()`
34
+ * / `JSON.stringify`: `Date` becomes ISO `string` (nullability preserved),
35
+ * arrays are serialized element-wise, everything else is untouched.
36
+ *
37
+ * Used to derive the `*Response` API types from the `*Record` store types so
38
+ * the two can never drift: add a field to `FeedbackRecord` and
39
+ * `FeedbackResponse` follows automatically.
40
+ */
41
+ export type Serialized<T> = {
42
+ [K in keyof T]: T[K] extends Date ? string : T[K] extends Date | null ? string | null : T[K] extends (infer U)[] ? Serialized<U>[] : T[K];
43
+ };
44
+ /**
45
+ * Type guard that narrows `value` to a non-null `Record<PropertyKey, unknown>`.
46
+ * Useful when validating arbitrary inputs before reading fields.
47
+ */
48
+ export declare function isRecord(value: unknown): value is Record<PropertyKey, unknown>;
49
+ /**
50
+ * Returns true when `value` is an object that exposes the requested key.
51
+ * Type-narrows `value` so the property can be accessed without further
52
+ * casting — a strictly typed replacement for `"k" in obj`.
53
+ *
54
+ * Named after the standardised `Object.hasOwn` helper rather than the
55
+ * legacy `Object.prototype.hasOwnProperty`, which the linter forbids
56
+ * shadowing.
57
+ */
58
+ export declare function hasOwn<K extends PropertyKey>(value: unknown, key: K): value is Record<K, unknown>;