@siteping/widget 0.9.13 → 0.9.15

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.
@@ -8,4 +8,4 @@ export type { ScreenshotStorage } from "./screenshot-storage.js";
8
8
  export type { AssertEqual, Brand, DeepReadonly, IfEquals, KeysOfType, NonEmptyArray, Prettify, Replace, RequiredNonNull, } from "./type-utils.js";
9
9
  export { hasOwn, isRecord } from "./type-utils.js";
10
10
  export type { AnchorData, AnnotationCreateInput, AnnotationPayload, AnnotationRecord, AnnotationResponse, BuiltinLocale, ConsoleDiagnosticEntry, ConsoleDiagnosticLevel, DiagnosticsCaptureOptions, DiagnosticsSnapshot, FeedbackCreateInput, FeedbackPage, FeedbackPayload, FeedbackQuery, FeedbackRecord, FeedbackResponse, FeedbackResponseList, FeedbackStatus, FeedbackType, FeedbackUpdateInput, NetworkDiagnosticEntry, PageScope, RectData, SitepingConfig, SitepingDeepLinkOptions, SitepingIdentity, SitepingInstance, SitepingLocale, SitepingPosition, SitepingPublicEventListener, SitepingPublicEvents, SitepingSkipReason, SitepingStore, SitepingTheme, SitepingUnsubscribe, } from "./types.js";
11
- export { BUILTIN_LOCALES, FEEDBACK_STATUSES, FEEDBACK_TYPES, flattenAnnotation, isStoreDuplicate, isStoreNotFound, StoreDuplicateError, StoreNotFoundError, } from "./types.js";
11
+ export { BUILTIN_LOCALES, FEEDBACK_STATUSES, FEEDBACK_TYPES, flattenAnnotation, isStoreDuplicate, isStoreNotFound, isStorePersistence, StoreDuplicateError, StoreNotFoundError, StorePersistenceError, } from "./types.js";
package/dist/types.d.ts CHANGED
@@ -57,8 +57,21 @@ export interface SitepingConfig {
57
57
  showAnnotationsToggle?: boolean | undefined;
58
58
  /** Accent color for the widget UI — defaults to '#0066ff' */
59
59
  accentColor?: string;
60
- /** Show the widget even in production — defaults to false */
60
+ /**
61
+ * Render the widget even when it would normally be skipped — this bypasses
62
+ * BOTH the production-environment guard AND the mobile-viewport guard.
63
+ * Defaults to false. Use it for dedicated review tools, staging environments,
64
+ * or responsive testing where you always want the widget present.
65
+ */
61
66
  forceShow?: boolean;
67
+ /**
68
+ * Minimum viewport width (px) at or above which the widget renders. Below it,
69
+ * the widget is skipped and `onSkip("mobile")` fires. Defaults to `768`.
70
+ *
71
+ * Set lower (e.g. `0`) to allow narrow/mobile viewports, or use `forceShow`
72
+ * to bypass the viewport check entirely.
73
+ */
74
+ minViewportWidth?: number | undefined;
62
75
  /** Enable debug logging of lifecycle events — defaults to false */
63
76
  debug?: boolean;
64
77
  /** Color theme — defaults to 'light' */
@@ -148,6 +161,28 @@ export interface SitepingConfig {
148
161
  * `instance.focusFeedback(id)` explicitly.
149
162
  */
150
163
  deepLink?: boolean | SitepingDeepLinkOptions | undefined;
164
+ /**
165
+ * Automatically re-fetch feedbacks when the page changes during client-side
166
+ * (SPA) navigation. Enabled by default.
167
+ *
168
+ * The widget is normally mounted once (singleton) inside a persistent layout
169
+ * — e.g. a Next.js App Router `layout.tsx`, which does NOT remount on
170
+ * client-side navigation. Without this, init runs a single time and both the
171
+ * panel list and the page markers stay frozen on the page where the widget
172
+ * first mounted. With it on, the widget patches the History API
173
+ * (`pushState`/`replaceState`, which SPA routers call instead of triggering
174
+ * `popstate`) and listens for `popstate`/`hashchange`, then re-fetches when
175
+ * the scope key (`getPageScope().url` + template) actually changes.
176
+ *
177
+ * This re-fetches data only — it deliberately does NOT re-focus or re-scroll
178
+ * to an annotation (deep-link focus stays initial-load only; see `deepLink`),
179
+ * so normal browsing is never interrupted by a surprise scroll.
180
+ *
181
+ * - `true` (default) — watch navigation and re-fetch on route change.
182
+ * - `false` — never touch the History API; hosts drive updates manually via
183
+ * `instance.refresh()`.
184
+ */
185
+ watchNavigation?: boolean | undefined;
151
186
  /**
152
187
  * Pre-fill author identity from the host application — typically the
153
188
  * currently signed-in user. When set, the widget uses these values
@@ -418,6 +453,16 @@ export declare class StoreDuplicateError extends Error {
418
453
  readonly code: "STORE_DUPLICATE";
419
454
  constructor(message?: string);
420
455
  }
456
+ /**
457
+ * Thrown when a store accepts a mutation but cannot persist it — e.g.
458
+ * `localStorage` is full (QuotaExceededError). Adapters MUST throw this rather
459
+ * than swallow the failure, so callers learn the write was lost instead of
460
+ * seeing a phantom success.
461
+ */
462
+ export declare class StorePersistenceError extends Error {
463
+ readonly code: "STORE_PERSISTENCE";
464
+ constructor(message?: string, options?: ErrorOptions);
465
+ }
421
466
  /** Shape of any ORM error that carries a Prisma-style `code` field. */
422
467
  type CodedError<C extends string = string> = {
423
468
  code: C;
@@ -426,6 +471,13 @@ type CodedError<C extends string = string> = {
426
471
  export declare function isStoreNotFound(error: unknown): error is StoreNotFoundError | CodedError<"P2025">;
427
472
  /** Type guard — works for `StoreDuplicateError` and ORM-specific equivalents (e.g. Prisma P2002). */
428
473
  export declare function isStoreDuplicate(error: unknown): error is StoreDuplicateError | CodedError<"P2002">;
474
+ /**
475
+ * Type guard for `StorePersistenceError`. Matches on the stable `code` field
476
+ * in addition to `instanceof`: every consumer package bundles its own copy of
477
+ * core (tsup `noExternal`), so an instance thrown by one package fails an
478
+ * `instanceof` check against another package's class identity.
479
+ */
480
+ export declare function isStorePersistence(error: unknown): error is StorePersistenceError | CodedError<"STORE_PERSISTENCE">;
429
481
  /** Flatten a widget `AnnotationPayload` (nested anchor + rect) into a flat `AnnotationCreateInput`. */
430
482
  export declare function flattenAnnotation(ann: AnnotationPayload): Prettify<AnnotationCreateInput>;
431
483
  /** Paginated result returned by `SitepingStore.getFeedbacks`. */
@@ -447,20 +499,23 @@ export interface FeedbackPage {
447
499
  * - **`createFeedback`**: either return the existing record on duplicate
448
500
  * `clientId` (idempotent) or throw `StoreDuplicateError`. The handler
449
501
  * handles both patterns.
502
+ * - **All mutations**: when a write is accepted but cannot be persisted
503
+ * (e.g. storage quota), throw `StorePersistenceError` instead of reporting
504
+ * a phantom success. Detect it with `isStorePersistence`.
450
505
  * - Other methods should not throw on empty results — return empty arrays or `null`.
451
506
  */
452
507
  export interface SitepingStore {
453
- /** Create a feedback with its annotations. Idempotent on `clientId` — return existing record on duplicate, or throw `StoreDuplicateError`. */
508
+ /** Create a feedback with its annotations. Idempotent on `clientId` — return existing record on duplicate, or throw `StoreDuplicateError`. Throws `StorePersistenceError` when the write cannot be persisted. */
454
509
  createFeedback(data: FeedbackCreateInput): Promise<FeedbackRecord>;
455
510
  /** Paginated query with optional filters. Returns empty array (not error) when no results. */
456
511
  getFeedbacks(query: FeedbackQuery): Promise<FeedbackPage>;
457
512
  /** Lookup by client-generated UUID. Returns `null` (not error) when not found. */
458
513
  findByClientId(clientId: string): Promise<FeedbackRecord | null>;
459
- /** Update status/resolvedAt. Throws `StoreNotFoundError` if `id` does not exist. */
514
+ /** Update status/resolvedAt. Throws `StoreNotFoundError` if `id` does not exist, `StorePersistenceError` when the write cannot be persisted. */
460
515
  updateFeedback(id: string, data: FeedbackUpdateInput): Promise<FeedbackRecord>;
461
- /** Delete a single record. Throws `StoreNotFoundError` if `id` does not exist. */
516
+ /** Delete a single record. Throws `StoreNotFoundError` if `id` does not exist, `StorePersistenceError` when the write cannot be persisted. */
462
517
  deleteFeedback(id: string): Promise<void>;
463
- /** Bulk delete all feedbacks for a project. No-op (not error) if none exist. */
518
+ /** Bulk delete all feedbacks for a project. No-op (not error) if none exist. Throws `StorePersistenceError` when the write cannot be persisted. */
464
519
  deleteAllFeedbacks(projectName: string): Promise<void>;
465
520
  }
466
521
  /** Payload sent from the widget to the server when submitting feedback. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siteping/widget",
3
- "version": "0.9.13",
3
+ "version": "0.9.15",
4
4
  "description": "Feedback widget for client review during development — annotations, bugs, questions directly on the site",
5
5
  "type": "module",
6
6
  "sideEffects": false,