@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,40 @@
1
+ /**
2
+ * Shared conformance test suite for `SitepingStore` implementations.
3
+ *
4
+ * Adapters import this and run it with their store factory to verify they
5
+ * satisfy the full store contract — no need to write the same 40+ tests
6
+ * from scratch.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { testSitepingStore } from './siteping-core-testing.js'
11
+ * import { DrizzleStore } from '../src/index.js'
12
+ *
13
+ * testSitepingStore(() => new DrizzleStore(db))
14
+ * ```
15
+ */
16
+ import type { SitepingStore } from "./types.js";
17
+ /** Tuning knobs for backends whose documented contract legitimately varies. */
18
+ export interface StoreConformanceOptions {
19
+ /**
20
+ * How `createFeedback` reacts to a duplicate `clientId` — both are valid
21
+ * per the `SitepingStore` contract:
22
+ * - `"return"` (default): idempotently return the existing record.
23
+ * - `"throw"`: throw `StoreDuplicateError` (matched via `isStoreDuplicate`).
24
+ */
25
+ duplicateBehavior?: "return" | "throw" | undefined;
26
+ /**
27
+ * Whether `search` matches case-insensitively. Defaults to `true` (the
28
+ * in-memory pipeline's behavior). Set to `false` for SQL backends whose
29
+ * collation is case-sensitive — the suite then only asserts same-case
30
+ * substring matching.
31
+ */
32
+ caseInsensitiveSearch?: boolean | undefined;
33
+ }
34
+ /**
35
+ * Run the full `SitepingStore` conformance test suite.
36
+ *
37
+ * @param factory — called before each test to create a fresh, empty store instance. May be async.
38
+ * @param options — contract variations, see {@link StoreConformanceOptions}.
39
+ */
40
+ export declare function testSitepingStore(factory: () => SitepingStore | Promise<SitepingStore>, options?: StoreConformanceOptions): void;
@@ -0,0 +1,16 @@
1
+ export type { SitepingErrorCode } from "./errors.cjs";
2
+ export { SitepingAuthError, SitepingError, SitepingNetworkError, SitepingValidationError } from "./errors.cjs";
3
+ export type { FilterResult } from "./filters.cjs";
4
+ export { applyFeedbackFilters } from "./filters.cjs";
5
+ export type { I18n, LocaleLoaders, TranslateFunction } from "./i18n.cjs";
6
+ export { createI18n, interpolate, tWithParams } from "./i18n.cjs";
7
+ export type { FieldDef, IndexDef, ModelDef, PrismaNativeType, PrismaScalarType, RelationDef, RelationKind, RelationOnDelete, SitepingModelFieldName, SitepingModelName, } from "./schema.cjs";
8
+ export { isRelationField, isScalarField, SITEPING_MODELS } from "./schema.cjs";
9
+ export type { ScreenshotStorage } from "./screenshot-storage.cjs";
10
+ export type { CollectionStore, CollectionStoreBackend } from "./store-helpers.cjs";
11
+ export { buildAnnotationRecord, buildFeedbackRecord, createCollectionStore } from "./store-helpers.cjs";
12
+ export type { AssertEqual, IfEquals, Prettify, Serialized } from "./type-utils.cjs";
13
+ export { hasOwn, isRecord } from "./type-utils.cjs";
14
+ export type { AnchorData, AnnotationCreateInput, AnnotationPayload, AnnotationRecord, AnnotationResponse, BuiltinLocale, ClosedFeedbackStatus, ConsoleDiagnosticEntry, ConsoleDiagnosticLevel, DiagnosticsCaptureOptions, DiagnosticsSnapshot, FeedbackCreateInput, FeedbackPage, FeedbackPayload, FeedbackQuery, FeedbackRecord, FeedbackResponse, FeedbackResponseList, FeedbackStatus, FeedbackType, FeedbackUpdateInput, NetworkDiagnosticEntry, OpenFeedbackStatus, PageScope, RectData, ScreenshotRegion, SitepingBaseConfig, SitepingConfig, SitepingDeepLinkOptions, SitepingHeadersOption, SitepingHttpConfig, SitepingIdentity, SitepingInstance, SitepingLocale, SitepingPosition, SitepingPublicEventListener, SitepingPublicEvents, SitepingSkipReason, SitepingStore, SitepingStoreConfig, SitepingTheme, SitepingUnsubscribe, } from "./types.cjs";
15
+ export { BUILTIN_LOCALES, CLOSED_FEEDBACK_STATUSES, CONSOLE_DIAGNOSTIC_LEVELS, FEEDBACK_STATUSES, FEEDBACK_TYPES, flattenAnnotation, isClosedStatus, isStoreDuplicate, isStoreNotFound, isStorePersistence, OPEN_FEEDBACK_STATUSES, StoreDuplicateError, StoreNotFoundError, StorePersistenceError, toFeedbackUpdate, } from "./types.cjs";
16
+ export { errorFromResponse, feedbackQueryToSearchParams, networkErrorFromException } from "./wire.cjs";
@@ -0,0 +1,16 @@
1
+ export type { SitepingErrorCode } from "./errors.js";
2
+ export { SitepingAuthError, SitepingError, SitepingNetworkError, SitepingValidationError } from "./errors.js";
3
+ export type { FilterResult } from "./filters.js";
4
+ export { applyFeedbackFilters } from "./filters.js";
5
+ export type { I18n, LocaleLoaders, TranslateFunction } from "./i18n.js";
6
+ export { createI18n, interpolate, tWithParams } from "./i18n.js";
7
+ export type { FieldDef, IndexDef, ModelDef, PrismaNativeType, PrismaScalarType, RelationDef, RelationKind, RelationOnDelete, SitepingModelFieldName, SitepingModelName, } from "./schema.js";
8
+ export { isRelationField, isScalarField, SITEPING_MODELS } from "./schema.js";
9
+ export type { ScreenshotStorage } from "./screenshot-storage.js";
10
+ export type { CollectionStore, CollectionStoreBackend } from "./store-helpers.js";
11
+ export { buildAnnotationRecord, buildFeedbackRecord, createCollectionStore } from "./store-helpers.js";
12
+ export type { AssertEqual, IfEquals, Prettify, Serialized } from "./type-utils.js";
13
+ export { hasOwn, isRecord } from "./type-utils.js";
14
+ export type { AnchorData, AnnotationCreateInput, AnnotationPayload, AnnotationRecord, AnnotationResponse, BuiltinLocale, ClosedFeedbackStatus, ConsoleDiagnosticEntry, ConsoleDiagnosticLevel, DiagnosticsCaptureOptions, DiagnosticsSnapshot, FeedbackCreateInput, FeedbackPage, FeedbackPayload, FeedbackQuery, FeedbackRecord, FeedbackResponse, FeedbackResponseList, FeedbackStatus, FeedbackType, FeedbackUpdateInput, NetworkDiagnosticEntry, OpenFeedbackStatus, PageScope, RectData, ScreenshotRegion, SitepingBaseConfig, SitepingConfig, SitepingDeepLinkOptions, SitepingHeadersOption, SitepingHttpConfig, SitepingIdentity, SitepingInstance, SitepingLocale, SitepingPosition, SitepingPublicEventListener, SitepingPublicEvents, SitepingSkipReason, SitepingStore, SitepingStoreConfig, SitepingTheme, SitepingUnsubscribe, } from "./types.js";
15
+ export { BUILTIN_LOCALES, CLOSED_FEEDBACK_STATUSES, CONSOLE_DIAGNOSTIC_LEVELS, FEEDBACK_STATUSES, FEEDBACK_TYPES, flattenAnnotation, isClosedStatus, isStoreDuplicate, isStoreNotFound, isStorePersistence, OPEN_FEEDBACK_STATUSES, StoreDuplicateError, StoreNotFoundError, StorePersistenceError, toFeedbackUpdate, } from "./types.js";
16
+ export { errorFromResponse, feedbackQueryToSearchParams, networkErrorFromException } from "./wire.js";
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Record-construction helpers and the collection-store engine.
3
+ *
4
+ * Every snapshot-style adapter (memory, localStorage, flat file, KV, …)
5
+ * needs the same three ingredients: turn a `FeedbackCreateInput` into a
6
+ * `FeedbackRecord` (null-normalizing optional fields, stamping ids and
7
+ * timestamps), filter/paginate with `applyFeedbackFilters`, and implement
8
+ * the dedup/update/delete choreography of the `SitepingStore` contract.
9
+ *
10
+ * `buildFeedbackRecord` / `buildAnnotationRecord` cover the first part for
11
+ * any adapter. `createCollectionStore` covers all of it: give it `load`,
12
+ * `persist`, and `generateId`, and it returns a fully conformant
13
+ * `SitepingStore` — writing a new snapshot adapter is ~20 lines plus its
14
+ * storage specifics.
15
+ */
16
+ import type { AnnotationCreateInput, AnnotationRecord, FeedbackCreateInput, FeedbackRecord, SitepingStore } from "./types.cjs";
17
+ /**
18
+ * Build a persisted `AnnotationRecord` from its create input — normalizes
19
+ * the optional anchor fields to `null` and stamps identity/timestamp.
20
+ */
21
+ export declare function buildAnnotationRecord(input: AnnotationCreateInput, ctx: {
22
+ id: string;
23
+ feedbackId: string;
24
+ now: Date;
25
+ }): AnnotationRecord;
26
+ /**
27
+ * Build a persisted `FeedbackRecord` (with its annotations) from a create
28
+ * input — normalizes every optional field to `null` and stamps ids and
29
+ * timestamps. Adapters without external screenshot storage keep the data
30
+ * URL inline on `screenshotUrl`, which is what this helper does; adapters
31
+ * with a `ScreenshotStorage` upload first and override `screenshotUrl`.
32
+ */
33
+ export declare function buildFeedbackRecord(input: FeedbackCreateInput, ctx: {
34
+ id: string;
35
+ annotationId: () => string;
36
+ now?: Date;
37
+ }): FeedbackRecord;
38
+ /**
39
+ * Storage primitives behind a collection store. `load`/`persist` may be
40
+ * sync or async — the engine awaits both, so in-memory arrays, localStorage
41
+ * and async KV stores all fit the same three functions.
42
+ */
43
+ export interface CollectionStoreBackend {
44
+ /** Return the current full snapshot of feedback records. */
45
+ load(): FeedbackRecord[] | Promise<FeedbackRecord[]>;
46
+ /**
47
+ * Persist the full snapshot. Throw `StorePersistenceError` when the write
48
+ * is lost (quota, storage disabled, …) — never swallow the failure.
49
+ */
50
+ persist(feedbacks: FeedbackRecord[]): void | Promise<void>;
51
+ /** Generate a unique id for a new feedback or annotation record. */
52
+ generateId(): string;
53
+ }
54
+ /**
55
+ * A `SitepingStore` with the optional `verifyProjectOwnership` guaranteed —
56
+ * what `createCollectionStore` returns.
57
+ */
58
+ export type CollectionStore = SitepingStore & Required<Pick<SitepingStore, "verifyProjectOwnership">>;
59
+ /**
60
+ * Build a fully conformant `SitepingStore` on top of a snapshot backend.
61
+ *
62
+ * The engine implements the whole store contract: clientId dedup (idempotent
63
+ * create), newest-first ordering, the standard filter/pagination pipeline,
64
+ * `StoreNotFoundError` on missing update/delete, project-scoped bulk delete,
65
+ * and `verifyProjectOwnership`. When `persist` fails during `createFeedback`
66
+ * and the record carries an inline screenshot, the engine retries once
67
+ * without the screenshot (by far the heaviest field) so the text feedback
68
+ * survives a storage-quota hit; if that also fails, the error propagates —
69
+ * returning the record would claim a success that was never persisted.
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * export class MemoryStore implements SitepingStore {
74
+ * private feedbacks: FeedbackRecord[] = [];
75
+ * private readonly store = createCollectionStore({
76
+ * load: () => this.feedbacks,
77
+ * persist: (next) => {
78
+ * this.feedbacks = next;
79
+ * },
80
+ * generateId: () => crypto.randomUUID(),
81
+ * });
82
+ * createFeedback = this.store.createFeedback;
83
+ * // …delegate the remaining methods the same way
84
+ * }
85
+ * ```
86
+ */
87
+ export declare function createCollectionStore(backend: CollectionStoreBackend): CollectionStore;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Record-construction helpers and the collection-store engine.
3
+ *
4
+ * Every snapshot-style adapter (memory, localStorage, flat file, KV, …)
5
+ * needs the same three ingredients: turn a `FeedbackCreateInput` into a
6
+ * `FeedbackRecord` (null-normalizing optional fields, stamping ids and
7
+ * timestamps), filter/paginate with `applyFeedbackFilters`, and implement
8
+ * the dedup/update/delete choreography of the `SitepingStore` contract.
9
+ *
10
+ * `buildFeedbackRecord` / `buildAnnotationRecord` cover the first part for
11
+ * any adapter. `createCollectionStore` covers all of it: give it `load`,
12
+ * `persist`, and `generateId`, and it returns a fully conformant
13
+ * `SitepingStore` — writing a new snapshot adapter is ~20 lines plus its
14
+ * storage specifics.
15
+ */
16
+ import type { AnnotationCreateInput, AnnotationRecord, FeedbackCreateInput, FeedbackRecord, SitepingStore } from "./types.js";
17
+ /**
18
+ * Build a persisted `AnnotationRecord` from its create input — normalizes
19
+ * the optional anchor fields to `null` and stamps identity/timestamp.
20
+ */
21
+ export declare function buildAnnotationRecord(input: AnnotationCreateInput, ctx: {
22
+ id: string;
23
+ feedbackId: string;
24
+ now: Date;
25
+ }): AnnotationRecord;
26
+ /**
27
+ * Build a persisted `FeedbackRecord` (with its annotations) from a create
28
+ * input — normalizes every optional field to `null` and stamps ids and
29
+ * timestamps. Adapters without external screenshot storage keep the data
30
+ * URL inline on `screenshotUrl`, which is what this helper does; adapters
31
+ * with a `ScreenshotStorage` upload first and override `screenshotUrl`.
32
+ */
33
+ export declare function buildFeedbackRecord(input: FeedbackCreateInput, ctx: {
34
+ id: string;
35
+ annotationId: () => string;
36
+ now?: Date;
37
+ }): FeedbackRecord;
38
+ /**
39
+ * Storage primitives behind a collection store. `load`/`persist` may be
40
+ * sync or async — the engine awaits both, so in-memory arrays, localStorage
41
+ * and async KV stores all fit the same three functions.
42
+ */
43
+ export interface CollectionStoreBackend {
44
+ /** Return the current full snapshot of feedback records. */
45
+ load(): FeedbackRecord[] | Promise<FeedbackRecord[]>;
46
+ /**
47
+ * Persist the full snapshot. Throw `StorePersistenceError` when the write
48
+ * is lost (quota, storage disabled, …) — never swallow the failure.
49
+ */
50
+ persist(feedbacks: FeedbackRecord[]): void | Promise<void>;
51
+ /** Generate a unique id for a new feedback or annotation record. */
52
+ generateId(): string;
53
+ }
54
+ /**
55
+ * A `SitepingStore` with the optional `verifyProjectOwnership` guaranteed —
56
+ * what `createCollectionStore` returns.
57
+ */
58
+ export type CollectionStore = SitepingStore & Required<Pick<SitepingStore, "verifyProjectOwnership">>;
59
+ /**
60
+ * Build a fully conformant `SitepingStore` on top of a snapshot backend.
61
+ *
62
+ * The engine implements the whole store contract: clientId dedup (idempotent
63
+ * create), newest-first ordering, the standard filter/pagination pipeline,
64
+ * `StoreNotFoundError` on missing update/delete, project-scoped bulk delete,
65
+ * and `verifyProjectOwnership`. When `persist` fails during `createFeedback`
66
+ * and the record carries an inline screenshot, the engine retries once
67
+ * without the screenshot (by far the heaviest field) so the text feedback
68
+ * survives a storage-quota hit; if that also fails, the error propagates —
69
+ * returning the record would claim a success that was never persisted.
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * export class MemoryStore implements SitepingStore {
74
+ * private feedbacks: FeedbackRecord[] = [];
75
+ * private readonly store = createCollectionStore({
76
+ * load: () => this.feedbacks,
77
+ * persist: (next) => {
78
+ * this.feedbacks = next;
79
+ * },
80
+ * generateId: () => crypto.randomUUID(),
81
+ * });
82
+ * createFeedback = this.store.createFeedback;
83
+ * // …delegate the remaining methods the same way
84
+ * }
85
+ * ```
86
+ */
87
+ export declare function createCollectionStore(backend: CollectionStoreBackend): CollectionStore;