@siteping/widget 0.11.1 → 0.12.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.
Files changed (126) hide show
  1. package/README.md +5 -5
  2. package/dist/chunk-ISSCC4XP.js +9 -0
  3. package/dist/chunk-ISSCC4XP.js.map +1 -0
  4. package/dist/chunk-QDJDQQ6E.cjs +9 -0
  5. package/dist/chunk-QDJDQQ6E.cjs.map +1 -0
  6. package/dist/de-GCYQ3JCH.cjs +2 -0
  7. package/dist/de-GCYQ3JCH.cjs.map +1 -0
  8. package/dist/de-U3V6TPCZ.js +2 -0
  9. package/dist/de-U3V6TPCZ.js.map +1 -0
  10. package/dist/deep-link.d.cts +10 -0
  11. package/dist/deep-link.d.ts +10 -0
  12. package/dist/errors.d.cts +46 -16
  13. package/dist/errors.d.ts +46 -16
  14. package/dist/es-435K3LIV.cjs +2 -0
  15. package/dist/es-435K3LIV.cjs.map +1 -0
  16. package/dist/es-CSZUS23Z.js +2 -0
  17. package/dist/es-CSZUS23Z.js.map +1 -0
  18. package/dist/fr-DVOFE3WF.js +2 -0
  19. package/dist/fr-DVOFE3WF.js.map +1 -0
  20. package/dist/fr-RKRDGOTP.cjs +2 -0
  21. package/dist/fr-RKRDGOTP.cjs.map +1 -0
  22. package/dist/i18n.d.cts +14 -10
  23. package/dist/i18n.d.ts +14 -10
  24. package/dist/index.cjs +3 -3
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +26 -19
  27. package/dist/index.d.ts +26 -19
  28. package/dist/index.global.js +2 -2
  29. package/dist/index.global.js.map +1 -1
  30. package/dist/index.js +3 -3
  31. package/dist/index.js.map +1 -1
  32. package/dist/it-COLRVG26.cjs +2 -0
  33. package/dist/it-COLRVG26.cjs.map +1 -0
  34. package/dist/it-ZWPEN22A.js +2 -0
  35. package/dist/it-ZWPEN22A.js.map +1 -0
  36. package/dist/ja-HLZ2GVFS.js +2 -0
  37. package/dist/ja-HLZ2GVFS.js.map +1 -0
  38. package/dist/ja-SNVDY7QP.cjs +2 -0
  39. package/dist/ja-SNVDY7QP.cjs.map +1 -0
  40. package/dist/ko-ORUCHGUZ.cjs +2 -0
  41. package/dist/ko-ORUCHGUZ.cjs.map +1 -0
  42. package/dist/ko-ZNF7AMXI.js +2 -0
  43. package/dist/ko-ZNF7AMXI.js.map +1 -0
  44. package/dist/logger.d.cts +30 -0
  45. package/dist/logger.d.ts +30 -0
  46. package/dist/panel-4CIN3Y66.js +2 -0
  47. package/dist/panel-4CIN3Y66.js.map +1 -0
  48. package/dist/panel-TD4UUEZA.cjs +2 -0
  49. package/dist/panel-TD4UUEZA.cjs.map +1 -0
  50. package/dist/pt-QQX5CA7D.cjs +2 -0
  51. package/dist/pt-QQX5CA7D.cjs.map +1 -0
  52. package/dist/pt-WGEJRQFC.js +2 -0
  53. package/dist/pt-WGEJRQFC.js.map +1 -0
  54. package/dist/react.cjs +2 -3
  55. package/dist/react.cjs.map +1 -1
  56. package/dist/react.d.cts +9 -0
  57. package/dist/react.d.ts +9 -0
  58. package/dist/react.js +2 -3
  59. package/dist/react.js.map +1 -1
  60. package/dist/ru-SERDK6PQ.js +2 -0
  61. package/dist/ru-SERDK6PQ.js.map +1 -0
  62. package/dist/ru-YRXPV57R.cjs +2 -0
  63. package/dist/ru-YRXPV57R.cjs.map +1 -0
  64. package/dist/schema.d.cts +31 -0
  65. package/dist/schema.d.ts +31 -0
  66. package/dist/screenshot-storage.d.cts +14 -17
  67. package/dist/screenshot-storage.d.ts +14 -17
  68. package/dist/siteping-core.d.cts +8 -5
  69. package/dist/siteping-core.d.ts +8 -5
  70. package/dist/store-helpers.d.cts +13 -9
  71. package/dist/store-helpers.d.ts +13 -9
  72. package/dist/text.d.cts +28 -0
  73. package/dist/text.d.ts +28 -0
  74. package/dist/types.d.cts +114 -37
  75. package/dist/types.d.ts +114 -37
  76. package/dist/wire.d.cts +6 -1
  77. package/dist/wire.d.ts +6 -1
  78. package/package.json +7 -5
  79. package/dist/chunk-26577FDQ.js +0 -9
  80. package/dist/chunk-26577FDQ.js.map +0 -1
  81. package/dist/chunk-3EHIWTJH.js +0 -9
  82. package/dist/chunk-3EHIWTJH.js.map +0 -1
  83. package/dist/chunk-6MCFOKVG.cjs +0 -9
  84. package/dist/chunk-6MCFOKVG.cjs.map +0 -1
  85. package/dist/chunk-Q34GDCKE.cjs +0 -9
  86. package/dist/chunk-Q34GDCKE.cjs.map +0 -1
  87. package/dist/de-73CTH2E4.cjs +0 -2
  88. package/dist/de-73CTH2E4.cjs.map +0 -1
  89. package/dist/de-GUUJUHYP.js +0 -2
  90. package/dist/de-GUUJUHYP.js.map +0 -1
  91. package/dist/es-3MXMSDUC.js +0 -2
  92. package/dist/es-3MXMSDUC.js.map +0 -1
  93. package/dist/es-WJZ3XARH.cjs +0 -2
  94. package/dist/es-WJZ3XARH.cjs.map +0 -1
  95. package/dist/fr-52F3ZCLQ.js +0 -2
  96. package/dist/fr-52F3ZCLQ.js.map +0 -1
  97. package/dist/fr-ZCRIJWI3.cjs +0 -2
  98. package/dist/fr-ZCRIJWI3.cjs.map +0 -1
  99. package/dist/it-MEUT3RCC.js +0 -2
  100. package/dist/it-MEUT3RCC.js.map +0 -1
  101. package/dist/it-TGUTW6MO.cjs +0 -2
  102. package/dist/it-TGUTW6MO.cjs.map +0 -1
  103. package/dist/ja-DOS7AWLW.cjs +0 -2
  104. package/dist/ja-DOS7AWLW.cjs.map +0 -1
  105. package/dist/ja-U7NRNGPQ.js +0 -2
  106. package/dist/ja-U7NRNGPQ.js.map +0 -1
  107. package/dist/ko-U2CS6XQ4.js +0 -2
  108. package/dist/ko-U2CS6XQ4.js.map +0 -1
  109. package/dist/ko-ZUL567LW.cjs +0 -2
  110. package/dist/ko-ZUL567LW.cjs.map +0 -1
  111. package/dist/panel-3BEMZFIR.cjs +0 -2
  112. package/dist/panel-3BEMZFIR.cjs.map +0 -1
  113. package/dist/panel-HPDIAYLQ.js +0 -2
  114. package/dist/panel-HPDIAYLQ.js.map +0 -1
  115. package/dist/panel-MIWFGD7X.cjs +0 -2
  116. package/dist/panel-MIWFGD7X.cjs.map +0 -1
  117. package/dist/panel-XTNU6XHV.js +0 -2
  118. package/dist/panel-XTNU6XHV.js.map +0 -1
  119. package/dist/pt-BZRBHCYN.cjs +0 -2
  120. package/dist/pt-BZRBHCYN.cjs.map +0 -1
  121. package/dist/pt-MOS5DVOG.js +0 -2
  122. package/dist/pt-MOS5DVOG.js.map +0 -1
  123. package/dist/ru-KDNRHHNZ.cjs +0 -2
  124. package/dist/ru-KDNRHHNZ.cjs.map +0 -1
  125. package/dist/ru-VSWB5NKK.js +0 -2
  126. package/dist/ru-VSWB5NKK.js.map +0 -1
@@ -69,8 +69,9 @@ export interface CollectionStoreBackend {
69
69
  /** Generate a unique id for a new feedback, annotation or comment record. */
70
70
  generateId(): string;
71
71
  /**
72
- * Keep discussion threads on the records: the store gains `addComment` and
73
- * `deleteComment`, and new records start with `comments: []`. Opt in once
72
+ * Keep discussion threads on the records: the store gains `addComment`,
73
+ * `addCommentIfAbsent` and `deleteComment`, and new records start with
74
+ * `comments: []`. Opt in once
74
75
  * `load` hands back what `persist` wrote for them, each comment's
75
76
  * `createdAt` a `Date` again (a JSON backend revives it like the record's
76
77
  * own dates). Off by default, so an adapter written before threads never
@@ -87,12 +88,14 @@ export type CollectionStore = SitepingStore & Required<Pick<SitepingStore, "veri
87
88
  /**
88
89
  * Build a fully conformant `SitepingStore` on top of a snapshot backend.
89
90
  *
90
- * The engine implements the whole store contract: clientId dedup (idempotent
91
- * create, with `createFeedbackIfAbsent` reporting inserts), newest-first
91
+ * The engine implements the whole store contract: clientId dedup (a repeated
92
+ * `clientId` makes `createFeedback` throw `StoreDuplicateError`, and
93
+ * `createFeedbackIfAbsent` report the existing record), newest-first
92
94
  * ordering, the standard filter/pagination pipeline, `StoreNotFoundError` on
93
95
  * missing update/delete, project-scoped bulk delete,
94
96
  * `verifyProjectOwnership`, and — with `comments: true` — discussion threads
95
- * (`addComment`, `deleteComment`) kept on each record. The snapshot returned by `load` is
97
+ * (`addComment`, `addCommentIfAbsent` reporting inserts, `deleteComment`)
98
+ * kept on each record. The snapshot returned by `load` is
96
99
  * never mutated: every write hands `persist` a new array, so a failed write leaves
97
100
  * a cached snapshot exactly as it was. When `persist` fails during `createFeedback`
98
101
  * and the record carries an inline screenshot, the engine retries once
@@ -101,12 +104,13 @@ export type CollectionStore = SitepingStore & Required<Pick<SitepingStore, "veri
101
104
  * returning the record would claim a success that was never persisted.
102
105
  *
103
106
  * Mutations (`createFeedbackIfAbsent`, `createFeedback`, `updateFeedback`,
104
- * `deleteFeedback`, `deleteAllFeedbacks`, `addComment`, `deleteComment`) run
107
+ * `deleteFeedback`, `deleteAllFeedbacks`, `addComment`, `addCommentIfAbsent`,
108
+ * `deleteComment`) run
105
109
  * one at a time through a queue owned by the returned store, so concurrent
106
110
  * calls — the widget's `Promise.all` bulk resolve/delete, a comment posted
107
111
  * while its feedback is resolved — never start from the same snapshot and
108
- * overwrite each other, and `createFeedbackIfAbsent` reports `created: true`
109
- * exactly once per `clientId`. A failed mutation rejects with its own error
112
+ * overwrite each other, and `createFeedbackIfAbsent` and `addCommentIfAbsent`
113
+ * report `created: true` exactly once per `clientId`. A failed mutation rejects with its own error
110
114
  * and does not block the ones queued after it, but `load` and `persist` must
111
115
  * always settle: one that never does stalls every later mutation on the
112
116
  * store, so give network-backed primitives a timeout. Reads are not queued: they see the
@@ -136,5 +140,5 @@ export type CollectionStore = SitepingStore & Required<Pick<SitepingStore, "veri
136
140
  */
137
141
  export declare function createCollectionStore(backend: CollectionStoreBackend & {
138
142
  comments: true;
139
- }): CollectionStore & Required<Pick<SitepingStore, "createFeedbackIfAbsent" | "addComment" | "deleteComment">>;
143
+ }): CollectionStore & Required<Pick<SitepingStore, "createFeedbackIfAbsent" | "addComment" | "addCommentIfAbsent" | "deleteComment">>;
140
144
  export declare function createCollectionStore(backend: CollectionStoreBackend): CollectionStore & Required<Pick<SitepingStore, "createFeedbackIfAbsent">>;
@@ -69,8 +69,9 @@ export interface CollectionStoreBackend {
69
69
  /** Generate a unique id for a new feedback, annotation or comment record. */
70
70
  generateId(): string;
71
71
  /**
72
- * Keep discussion threads on the records: the store gains `addComment` and
73
- * `deleteComment`, and new records start with `comments: []`. Opt in once
72
+ * Keep discussion threads on the records: the store gains `addComment`,
73
+ * `addCommentIfAbsent` and `deleteComment`, and new records start with
74
+ * `comments: []`. Opt in once
74
75
  * `load` hands back what `persist` wrote for them, each comment's
75
76
  * `createdAt` a `Date` again (a JSON backend revives it like the record's
76
77
  * own dates). Off by default, so an adapter written before threads never
@@ -87,12 +88,14 @@ export type CollectionStore = SitepingStore & Required<Pick<SitepingStore, "veri
87
88
  /**
88
89
  * Build a fully conformant `SitepingStore` on top of a snapshot backend.
89
90
  *
90
- * The engine implements the whole store contract: clientId dedup (idempotent
91
- * create, with `createFeedbackIfAbsent` reporting inserts), newest-first
91
+ * The engine implements the whole store contract: clientId dedup (a repeated
92
+ * `clientId` makes `createFeedback` throw `StoreDuplicateError`, and
93
+ * `createFeedbackIfAbsent` report the existing record), newest-first
92
94
  * ordering, the standard filter/pagination pipeline, `StoreNotFoundError` on
93
95
  * missing update/delete, project-scoped bulk delete,
94
96
  * `verifyProjectOwnership`, and — with `comments: true` — discussion threads
95
- * (`addComment`, `deleteComment`) kept on each record. The snapshot returned by `load` is
97
+ * (`addComment`, `addCommentIfAbsent` reporting inserts, `deleteComment`)
98
+ * kept on each record. The snapshot returned by `load` is
96
99
  * never mutated: every write hands `persist` a new array, so a failed write leaves
97
100
  * a cached snapshot exactly as it was. When `persist` fails during `createFeedback`
98
101
  * and the record carries an inline screenshot, the engine retries once
@@ -101,12 +104,13 @@ export type CollectionStore = SitepingStore & Required<Pick<SitepingStore, "veri
101
104
  * returning the record would claim a success that was never persisted.
102
105
  *
103
106
  * Mutations (`createFeedbackIfAbsent`, `createFeedback`, `updateFeedback`,
104
- * `deleteFeedback`, `deleteAllFeedbacks`, `addComment`, `deleteComment`) run
107
+ * `deleteFeedback`, `deleteAllFeedbacks`, `addComment`, `addCommentIfAbsent`,
108
+ * `deleteComment`) run
105
109
  * one at a time through a queue owned by the returned store, so concurrent
106
110
  * calls — the widget's `Promise.all` bulk resolve/delete, a comment posted
107
111
  * while its feedback is resolved — never start from the same snapshot and
108
- * overwrite each other, and `createFeedbackIfAbsent` reports `created: true`
109
- * exactly once per `clientId`. A failed mutation rejects with its own error
112
+ * overwrite each other, and `createFeedbackIfAbsent` and `addCommentIfAbsent`
113
+ * report `created: true` exactly once per `clientId`. A failed mutation rejects with its own error
110
114
  * and does not block the ones queued after it, but `load` and `persist` must
111
115
  * always settle: one that never does stalls every later mutation on the
112
116
  * store, so give network-backed primitives a timeout. Reads are not queued: they see the
@@ -136,5 +140,5 @@ export type CollectionStore = SitepingStore & Required<Pick<SitepingStore, "veri
136
140
  */
137
141
  export declare function createCollectionStore(backend: CollectionStoreBackend & {
138
142
  comments: true;
139
- }): CollectionStore & Required<Pick<SitepingStore, "createFeedbackIfAbsent" | "addComment" | "deleteComment">>;
143
+ }): CollectionStore & Required<Pick<SitepingStore, "createFeedbackIfAbsent" | "addComment" | "addCommentIfAbsent" | "deleteComment">>;
140
144
  export declare function createCollectionStore(backend: CollectionStoreBackend): CollectionStore & Required<Pick<SitepingStore, "createFeedbackIfAbsent">>;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Text a database may refuse — shared by the stores that write to one.
3
+ *
4
+ * PostgreSQL rejects NUL in `text` and `jsonb`, and a surrogate without its
5
+ * pair, which UTF-8 cannot encode: `jsonb` rejects it and `text` turns it
6
+ * into U+FFFD. A string cut inside an emoji ends with such an unpaired
7
+ * surrogate. The store contract accepts either the text as sent or U+FFFD in
8
+ * place of each such code unit (see the conformance suite), so a SQL store
9
+ * normalizes everything it writes or looks up with {@link toStorableText}.
10
+ */
11
+ /** The UTF-16 code units a JavaScript string may hold but PostgreSQL cannot store: NUL and an unpaired surrogate. */
12
+ export declare const UNSTORABLE_CODE_UNITS: RegExp;
13
+ /** U+FFFD, the Unicode replacement character — what stands for each unstorable code unit. */
14
+ export declare const REPLACEMENT_CHARACTER = "\uFFFD";
15
+ /**
16
+ * `text` with every code unit PostgreSQL cannot store (NUL, an unpaired
17
+ * surrogate) replaced by U+FFFD. Applied to everything a store writes or
18
+ * looks up, a submission holding one is stored instead of failing, a record
19
+ * reads back exactly as the store returned it, and a search or lookup
20
+ * holding one matches what was stored for it.
21
+ */
22
+ export declare function toStorableText(text: string): string;
23
+ /**
24
+ * `value` with {@link toStorableText} applied to every string it holds, keys
25
+ * included, through arrays and plain objects (annotations, diagnostics…).
26
+ * Other values — numbers, dates, `null` — are kept as they are.
27
+ */
28
+ export declare function toStorableValue<Value>(value: Value): Value;
package/dist/text.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Text a database may refuse — shared by the stores that write to one.
3
+ *
4
+ * PostgreSQL rejects NUL in `text` and `jsonb`, and a surrogate without its
5
+ * pair, which UTF-8 cannot encode: `jsonb` rejects it and `text` turns it
6
+ * into U+FFFD. A string cut inside an emoji ends with such an unpaired
7
+ * surrogate. The store contract accepts either the text as sent or U+FFFD in
8
+ * place of each such code unit (see the conformance suite), so a SQL store
9
+ * normalizes everything it writes or looks up with {@link toStorableText}.
10
+ */
11
+ /** The UTF-16 code units a JavaScript string may hold but PostgreSQL cannot store: NUL and an unpaired surrogate. */
12
+ export declare const UNSTORABLE_CODE_UNITS: RegExp;
13
+ /** U+FFFD, the Unicode replacement character — what stands for each unstorable code unit. */
14
+ export declare const REPLACEMENT_CHARACTER = "\uFFFD";
15
+ /**
16
+ * `text` with every code unit PostgreSQL cannot store (NUL, an unpaired
17
+ * surrogate) replaced by U+FFFD. Applied to everything a store writes or
18
+ * looks up, a submission holding one is stored instead of failing, a record
19
+ * reads back exactly as the store returned it, and a search or lookup
20
+ * holding one matches what was stored for it.
21
+ */
22
+ export declare function toStorableText(text: string): string;
23
+ /**
24
+ * `value` with {@link toStorableText} applied to every string it holds, keys
25
+ * included, through arrays and plain objects (annotations, diagnostics…).
26
+ * Other values — numbers, dates, `null` — are kept as they are.
27
+ */
28
+ export declare function toStorableValue<Value>(value: Value): Value;
package/dist/types.d.cts CHANGED
@@ -1,7 +1,10 @@
1
1
  import { type DeepReadonly, type Prettify, type Serialized } from "./type-utils.cjs";
2
2
  /** FAB anchor — bottom-corner placement supported by the widget. */
3
3
  export type SitepingPosition = "bottom-right" | "bottom-left";
4
- /** Visual theme — `auto` resolves to `light` or `dark` via system preference. */
4
+ /**
5
+ * Visual theme of the widget and the dashboard — `auto` follows the system
6
+ * preference, live: an OS switch mid-session re-themes the UI.
7
+ */
5
8
  export type SitepingTheme = "light" | "dark" | "auto";
6
9
  /** Built-in UI locales shipped with the widget. */
7
10
  export declare const BUILTIN_LOCALES: readonly ["en", "fr", "de", "es", "it", "pt", "ru", "ja", "ko"];
@@ -38,6 +41,15 @@ export interface SitepingIdentity {
38
41
  * it persists is never a 400 on every later submission.
39
42
  */
40
43
  export declare const IDENTITY_FIELD_MAX_LENGTH = 200;
44
+ /**
45
+ * Max length of a `projectName` and of a feedback's or comment's `clientId`
46
+ * — the identifiers the stores index. 191 characters is the longest
47
+ * `VARCHAR` every MySQL version indexes under `utf8mb4` (767 bytes), which
48
+ * is why Prisma maps a plain `String` to `VARCHAR(191)` there: the HTTP
49
+ * schema caps them at that length, so their columns need no prefix index.
50
+ */
51
+ export declare const PROJECT_NAME_MAX_LENGTH = 191;
52
+ export declare const CLIENT_ID_MAX_LENGTH = 191;
41
53
  /** Deep-link configuration — controls how a feedback id is read from the URL. */
42
54
  export interface SitepingDeepLinkOptions {
43
55
  /** Query parameter name carrying the feedback id. Defaults to `"siteping"`. */
@@ -184,7 +196,7 @@ export interface SitepingBaseConfig {
184
196
  minViewportWidth?: number | undefined;
185
197
  /** Enable debug logging of lifecycle events — defaults to false */
186
198
  debug?: boolean | undefined;
187
- /** Color theme — defaults to 'light' */
199
+ /** Color theme — defaults to `light`; `auto` follows the system preference live. */
188
200
  theme?: SitepingTheme | undefined;
189
201
  /** UI locale — defaults to 'en'. Built-in: en, fr, de, es, it, pt (Brazilian), ru, ja, ko. Any other string falls back to English. */
190
202
  locale?: SitepingLocale | undefined;
@@ -204,7 +216,9 @@ export interface SitepingBaseConfig {
204
216
  * When true (default), the widget filters initial markers and panel results
205
217
  * by `feedback.url === scope.url`, so annotations created on one page never
206
218
  * leak to other pages — even if their CSS selector accidentally matches.
207
- * Set to `false` to revert to the legacy project-wide behavior.
219
+ * Set to `false` for a project-wide view: the panel lists every feedback of
220
+ * the project, and a marker shows wherever its anchor resolves — for
221
+ * feedback on what every page shares, such as a header or a footer.
208
222
  */
209
223
  scopeAnnotationsByUrl?: boolean | undefined;
210
224
  /**
@@ -276,7 +290,7 @@ export interface SitepingBaseConfig {
276
290
  */
277
291
  captureDiagnostics?: boolean | DiagnosticsCaptureOptions | undefined;
278
292
  /** Called when the widget is skipped (production mode, viewport under `minViewportWidth`, SSR — no DOM) */
279
- onSkip?: (reason: SitepingSkipReason) => void;
293
+ onSkip?: ((reason: SitepingSkipReason) => void) | undefined;
280
294
  /**
281
295
  * Auto-focus a specific annotation when its ID appears in the URL query
282
296
  * string. Lets hosts deeplink directly into a feedback from external
@@ -286,7 +300,8 @@ export interface SitepingBaseConfig {
286
300
  * `window.location.search` right after the initial markers load. If the
287
301
  * value matches a visible feedback ID, the widget scrolls the annotation
288
302
  * into view, pins its highlight, and pulses the marker — the same visual
289
- * affordance a marker click produces.
303
+ * affordance a marker click produces — then opens the panel on that
304
+ * feedback's detail view, where its discussion thread is.
290
305
  *
291
306
  * - `false` / `undefined` (default): no URL parsing. Existing behavior
292
307
  * unchanged, no host URL inspection.
@@ -327,6 +342,8 @@ export interface SitepingBaseConfig {
327
342
  * Pre-fill author identity from the host application — typically the
328
343
  * currently signed-in user. When set, the widget uses these values
329
344
  * directly and never shows the identity modal, even on first feedback.
345
+ * It names the reviewer who sends feedback — the dashboard's `author` is
346
+ * the team member who replies, a different person.
330
347
  *
331
348
  * Use case: SSO-integrated apps where the end user is already
332
349
  * authenticated by the host. Avoids the awkward "enter your name and
@@ -372,12 +389,13 @@ export interface SitepingBaseConfig {
372
389
  *
373
390
  * The widget always emits a `SitepingError` (or a subclass:
374
391
  * `SitepingNetworkError`, `SitepingValidationError`, `SitepingAuthError`)
375
- * for HTTP-mode failures — host apps can `instanceof` to drive retry
376
- * logic, or read `error.code` (`"NETWORK" | "VALIDATION" | "AUTH" |
377
- * "SERVER"`) and `error.retryable` — and an `AUTH` error's `status`
378
- * (`401` for missing or dead credentials, `403` for a refusal). The type
379
- * is widened to `Error` so direct-store callers can still surface raw
380
- * errors without breaking the contract.
392
+ * for a failed call — match it with `isSitepingError(error)` (not
393
+ * `instanceof`: the classes aren't exported), then read `error.code`
394
+ * (`"NETWORK" | "VALIDATION" | "AUTH" | "SERVER" | "TIMEOUT"`),
395
+ * `error.retryable` and an `AUTH` error's `status` (`401` for missing or
396
+ * dead credentials, `403` for a refusal). The type is widened to `Error`
397
+ * because a store in `store` mode may throw its own errors, which reach
398
+ * here unchanged.
381
399
  *
382
400
  * Also receives whatever a `panelActions` callback throws or rejects with
383
401
  * (non-`Error` values are wrapped). Those host failures are not API
@@ -450,14 +468,24 @@ export interface SitepingStoreConfig extends SitepingBaseConfig {
450
468
  export type SitepingConfig = SitepingHttpConfig | SitepingStoreConfig;
451
469
  /** Instance returned by initSiteping() with lifecycle methods. */
452
470
  export interface SitepingInstance {
453
- /** Remove the widget from the DOM and clean up all listeners. */
471
+ /**
472
+ * Release this handle and detach the listeners added through its `on()`.
473
+ * Every `initSiteping()` call returns its own handle over the one widget,
474
+ * which leaves the DOM, all its listeners cleaned up, once the last handle
475
+ * is destroyed. A repeat call is a no-op, and the handle's other methods do
476
+ * nothing afterwards.
477
+ */
454
478
  destroy: () => void;
455
479
  /** Open the panel programmatically */
456
480
  open: () => void;
457
481
  /** Close the panel */
458
482
  close: () => void;
459
- /** Reload feedbacks from server */
460
- refresh: () => void;
483
+ /**
484
+ * Reload feedbacks from the server. Resolves once the pins (and the open
485
+ * panel) show the reloaded list; never rejects — like the dashboard's
486
+ * `refresh()`, a failed reload leaves the current view in place.
487
+ */
488
+ refresh: () => Promise<void>;
461
489
  /**
462
490
  * Scroll the matching annotation into view, pin its highlight, and
463
491
  * pulse its marker. Returns `true` when a visible feedback matched the
@@ -835,9 +863,10 @@ export declare class StoreLimitError extends Error {
835
863
  }
836
864
  /**
837
865
  * Thrown when a value is longer than the store can hold, though the HTTP
838
- * validation accepts it — on MySQL, Prisma maps a plain `String` to
839
- * `VARCHAR(191)`. Handlers translate this to HTTP 422: the submission can
840
- * never be stored as it is, so a client must not retry it.
866
+ * validation accepts it — a MySQL schema that predates the columns `sync`
867
+ * now sizes from the HTTP caps keeps Prisma's `VARCHAR(191)` for a plain
868
+ * `String`. Handlers translate this to HTTP 422: the submission can never be
869
+ * stored as it is, so a client must not retry it.
841
870
  */
842
871
  export declare class StoreValueTooLongError extends Error {
843
872
  readonly code: "STORE_VALUE_TOO_LONG";
@@ -885,6 +914,19 @@ export interface FeedbackCreateOutcome {
885
914
  */
886
915
  created: boolean;
887
916
  }
917
+ /**
918
+ * Outcome of `SitepingStore.addCommentIfAbsent` — the comment plus whether
919
+ * this very call inserted it.
920
+ */
921
+ export interface CommentCreateOutcome {
922
+ comment: CommentRecord;
923
+ /**
924
+ * `true` when this call inserted the comment, `false` when a comment with
925
+ * the same `clientId` already existed and is returned instead (a replay, or
926
+ * a concurrent request that won the race).
927
+ */
928
+ created: boolean;
929
+ }
888
930
  /** Paginated result returned by `SitepingStore.getFeedbacks`. */
889
931
  export interface FeedbackPage {
890
932
  feedbacks: FeedbackRecord[];
@@ -901,19 +943,17 @@ export interface FeedbackPage {
901
943
  *
902
944
  * - **`updateFeedback` / `deleteFeedback`**: throw `StoreNotFoundError` when
903
945
  * the record does not exist.
904
- * - **`createFeedback`**: either return the existing record on duplicate
905
- * `clientId` (idempotent) or throw `StoreDuplicateError`. The handler
906
- * handles both patterns — but only a throw, or the optional
907
- * `createFeedbackIfAbsent`, tells it the record was not inserted by this
908
- * call; stores that return the existing record should implement
909
- * `createFeedbackIfAbsent` so creation side effects never run twice.
946
+ * - **`createFeedback`**: throw `StoreDuplicateError` on a duplicate
947
+ * `clientId` — never return the existing record, so a caller always knows
948
+ * whether this call inserted. The optional `createFeedbackIfAbsent`
949
+ * reports the existing record instead, as `{ created: false }`.
910
950
  * - **All mutations**: when a write is accepted but cannot be persisted
911
951
  * (e.g. storage quota), throw `StorePersistenceError` instead of reporting
912
952
  * a phantom success. Detect it with `isStorePersistence`.
913
953
  * - Other methods should not throw on empty results — return empty arrays or `null`.
914
954
  */
915
955
  export interface SitepingStore {
916
- /** 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. */
956
+ /** Create a feedback with its annotations. Throws `StoreDuplicateError` when a feedback with the same `clientId` exists, `StorePersistenceError` when the write cannot be persisted. */
917
957
  createFeedback(data: FeedbackCreateInput): Promise<FeedbackRecord>;
918
958
  /** Paginated query with optional filters. Returns empty array (not error) when no results. */
919
959
  getFeedbacks(query: FeedbackQuery): Promise<FeedbackPage>;
@@ -926,14 +966,11 @@ export interface SitepingStore {
926
966
  /** Bulk delete all feedbacks for a project. No-op (not error) if none exist. Throws `StorePersistenceError` when the write cannot be persisted. */
927
967
  deleteAllFeedbacks(projectName: string): Promise<void>;
928
968
  /**
929
- * Optional — return `true` when the record with `id` belongs to
930
- * `projectName`, `false` otherwise (including when it does not exist).
931
- *
932
- * HTTP handlers use this to reject cross-project PATCH/DELETE requests.
933
- * Implement it whenever your store serves multiple projects; when absent,
934
- * handlers skip the ownership check and rely on `id` alone.
969
+ * Return `true` when the record with `id` belongs to `projectName`, `false`
970
+ * otherwise (including when it does not exist). HTTP handlers use it to
971
+ * reject cross-project PATCH/DELETE requests, which address records by id.
935
972
  */
936
- verifyProjectOwnership?(id: string, projectName: string): Promise<boolean>;
973
+ verifyProjectOwnership(id: string, projectName: string): Promise<boolean>;
937
974
  /**
938
975
  * Optional — `createFeedback` that reports whether this call inserted the
939
976
  * record (`created: true`) or found an existing one with the same
@@ -945,11 +982,10 @@ export interface SitepingStore {
945
982
  * processes need an atomic backend primitive (unique constraint,
946
983
  * transaction, compare-and-set).
947
984
  *
948
- * HTTP handlers prefer it over `createFeedback` to fire creation side
949
- * effects (webhooks) exactly once when concurrent requests race on the
950
- * same `clientId`. Stores whose `createFeedback` throws
951
- * `StoreDuplicateError` on a duplicate already give that signal and may
952
- * leave it out.
985
+ * HTTP handlers prefer it over `createFeedback`: a replayed or racing
986
+ * submission resolves to the stored record in one atomic call, without a
987
+ * `StoreDuplicateError` to catch and a lookup to follow it. Without it,
988
+ * they fall back to `createFeedback` and recover its duplicate error.
953
989
  */
954
990
  createFeedbackIfAbsent?(data: FeedbackCreateInput): Promise<FeedbackCreateOutcome>;
955
991
  /**
@@ -971,6 +1007,19 @@ export interface SitepingStore {
971
1007
  * HTTP handlers serve empty threads and answer comment writes with 501.
972
1008
  */
973
1009
  addComment?(feedbackId: string, data: CommentCreateInput): Promise<CommentRecord>;
1010
+ /**
1011
+ * Optional, alongside `addComment` — `addComment` that reports whether this
1012
+ * call inserted the comment (`created: true`) or found one with the same
1013
+ * `clientId` (`created: false`). Same errors as `addComment`, and the dedup
1014
+ * check and the insert must be atomic like `createFeedbackIfAbsent`'s: of N
1015
+ * concurrent calls with the same `clientId`, exactly one may report
1016
+ * `created: true`, and all must return that same comment.
1017
+ *
1018
+ * HTTP handlers need it to fire the `comment.created` webhook and the
1019
+ * `onCommentCreated` hook exactly once per reply — a retried post returns
1020
+ * the stored comment, which `addComment` alone cannot tell from a new one.
1021
+ */
1022
+ addCommentIfAbsent?(feedbackId: string, data: CommentCreateInput): Promise<CommentCreateOutcome>;
974
1023
  /**
975
1024
  * Optional — delete comment `commentId` from the thread of `feedbackId`.
976
1025
  * Scoped to that thread, which the caller was authorized for: throws
@@ -1167,6 +1216,16 @@ export interface FeedbackPermissions {
1167
1216
  /** Delete replies from its thread. */
1168
1217
  canDeleteComment: boolean;
1169
1218
  }
1219
+ /**
1220
+ * What a requester may do with one comment of a thread, as the server's
1221
+ * access policy decides — finer than the feedback's `canDeleteComment`, which
1222
+ * covers the whole thread: a policy that lets authors delete their own
1223
+ * replies only grants it comment by comment.
1224
+ */
1225
+ export interface CommentPermissions {
1226
+ /** Delete it. */
1227
+ canDelete: boolean;
1228
+ }
1170
1229
  /** What a requester may do with a whole project, sent with each list. */
1171
1230
  export interface FeedbackListPermissions {
1172
1231
  /** Delete every feedback of the project at once. */
@@ -1182,7 +1241,23 @@ export type AnnotationResponse = Prettify<Serialized<AnnotationRecord>>;
1182
1241
  * serialized and `clientId` omitted, like on `FeedbackResponse`. Its
1183
1242
  * `authorEmail` is redacted on the same terms as the feedback's.
1184
1243
  */
1185
- export type CommentResponse = Prettify<Serialized<Omit<CommentRecord, "clientId">>>;
1244
+ export type CommentResponse = Prettify<Serialized<Omit<CommentRecord, "clientId">> & {
1245
+ /**
1246
+ * What the requester may do with this comment — always sent by
1247
+ * `@siteping/server`. Absent from servers that predate it, and in store
1248
+ * mode: the feedback's `canDeleteComment` decides then.
1249
+ */
1250
+ permissions?: CommentPermissions | undefined;
1251
+ }>;
1252
+ /**
1253
+ * Version of the HTTP protocol `@siteping/server` speaks, sent as `protocol`
1254
+ * on every list response. It rises with each addition a client may rely on —
1255
+ * a route, a field, a status — so a client checks `protocol >= N` before
1256
+ * using what protocol N added, instead of probing for fields. A server that
1257
+ * predates it sends none: read that as 0. The OpenAPI document shipped as
1258
+ * `@siteping/server/openapi.json` describes this version.
1259
+ */
1260
+ export declare const SITEPING_PROTOCOL_VERSION = 1;
1186
1261
  /** What the store behind an endpoint supports — advertised on every list response. */
1187
1262
  export interface SitepingCapabilities {
1188
1263
  /** Whether comments can be posted: the store implements `addComment`. */
@@ -1198,6 +1273,8 @@ export interface SitepingCapabilities {
1198
1273
  export interface FeedbackResponseList {
1199
1274
  feedbacks: FeedbackResponse[];
1200
1275
  total: number;
1276
+ /** The server's {@link SITEPING_PROTOCOL_VERSION} — absent from servers that predate it: read that as 0. */
1277
+ protocol?: number | undefined;
1201
1278
  /** Always sent by `@siteping/server` — absent from servers that predate it. */
1202
1279
  capabilities?: SitepingCapabilities | undefined;
1203
1280
  /** Always sent by `@siteping/server` — absent from servers that predate it. */