@siteping/server 0.1.1 → 0.2.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,30 @@
1
+ /**
2
+ * Where SitePing reports what goes wrong. The arguments come in console's and
3
+ * winston's order, so `console` and winston satisfy it as they are. pino's
4
+ * methods take the context first and type-check here anyway, then drop the
5
+ * context: adapt them — `{ error: (message, { error, ...context }) =>
6
+ * log.error({ err: error, ...context }, message), warn: … }`.
7
+ *
8
+ * Each option asks only for the level it reports — the handler's `logger`
9
+ * for `error`, the stores' and screenshot storages' for `warn`
10
+ * ({@link SitepingWarnLogger}) — so one object can be handed to all of them.
11
+ */
12
+ export interface SitepingLogger {
13
+ /** Unexpected failures: a request answered 500, a hook or a lookup failed. */
14
+ error(message: string, context: Record<string, unknown>): void | Promise<void>;
15
+ /**
16
+ * Degraded but handled: a screenshot dropped or left orphaned, screenshots
17
+ * stored inline. The handler never calls it; the stores and screenshot
18
+ * storages require it.
19
+ */
20
+ warn?(message: string, context: Record<string, unknown>): void | Promise<void>;
21
+ }
22
+ /** What the stores and screenshot storages report through: a {@link SitepingLogger} with `warn`. */
23
+ export type SitepingWarnLogger = Required<Pick<SitepingLogger, "warn">>;
24
+ /**
25
+ * Report through `logger.warn` without letting the logger fail its caller,
26
+ * on paths documented as non-throwing: a throw, or a rejection of what it
27
+ * returns — unhandled, fatal in Node by default — is dropped. A broken
28
+ * logger has no other channel to report through.
29
+ */
30
+ export declare function warnSafely(logger: SitepingWarnLogger, message: string, context: Record<string, unknown>): void;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Where SitePing reports what goes wrong. The arguments come in console's and
3
+ * winston's order, so `console` and winston satisfy it as they are. pino's
4
+ * methods take the context first and type-check here anyway, then drop the
5
+ * context: adapt them — `{ error: (message, { error, ...context }) =>
6
+ * log.error({ err: error, ...context }, message), warn: … }`.
7
+ *
8
+ * Each option asks only for the level it reports — the handler's `logger`
9
+ * for `error`, the stores' and screenshot storages' for `warn`
10
+ * ({@link SitepingWarnLogger}) — so one object can be handed to all of them.
11
+ */
12
+ export interface SitepingLogger {
13
+ /** Unexpected failures: a request answered 500, a hook or a lookup failed. */
14
+ error(message: string, context: Record<string, unknown>): void | Promise<void>;
15
+ /**
16
+ * Degraded but handled: a screenshot dropped or left orphaned, screenshots
17
+ * stored inline. The handler never calls it; the stores and screenshot
18
+ * storages require it.
19
+ */
20
+ warn?(message: string, context: Record<string, unknown>): void | Promise<void>;
21
+ }
22
+ /** What the stores and screenshot storages report through: a {@link SitepingLogger} with `warn`. */
23
+ export type SitepingWarnLogger = Required<Pick<SitepingLogger, "warn">>;
24
+ /**
25
+ * Report through `logger.warn` without letting the logger fail its caller,
26
+ * on paths documented as non-throwing: a throw, or a rejection of what it
27
+ * returns — unhandled, fatal in Node by default — is dropped. A broken
28
+ * logger has no other channel to report through.
29
+ */
30
+ export declare function warnSafely(logger: SitepingWarnLogger, message: string, context: Record<string, unknown>): void;
package/dist/schema.d.cts CHANGED
@@ -52,6 +52,13 @@ export interface FieldDef {
52
52
  isUnique?: boolean;
53
53
  /** Prisma native type attribute (e.g. "Text" for @db.Text) — used for MySQL compatibility on long strings */
54
54
  nativeType?: PrismaNativeType;
55
+ /**
56
+ * Longest value the HTTP handler accepts for this column, in characters —
57
+ * set on every `String` column filled from a request body, and checked
58
+ * against the handler's own caps. The CLI sizes the column from it where a
59
+ * plain `String` is shorter: on MySQL, a `VARCHAR(191)`.
60
+ */
61
+ maxLength?: number;
55
62
  /** Prisma @updatedAt attribute */
56
63
  isUpdatedAt?: boolean;
57
64
  }
@@ -82,6 +89,7 @@ declare const _SITEPING_MODELS: {
82
89
  };
83
90
  readonly projectName: {
84
91
  readonly type: "String";
92
+ readonly maxLength: 191;
85
93
  };
86
94
  readonly type: {
87
95
  readonly type: "String";
@@ -89,6 +97,7 @@ declare const _SITEPING_MODELS: {
89
97
  readonly message: {
90
98
  readonly type: "String";
91
99
  readonly nativeType: "Text";
100
+ readonly maxLength: 5000;
92
101
  };
93
102
  readonly status: {
94
103
  readonly type: "String";
@@ -96,15 +105,18 @@ declare const _SITEPING_MODELS: {
96
105
  };
97
106
  readonly url: {
98
107
  readonly type: "String";
108
+ readonly maxLength: 2000;
99
109
  };
100
110
  readonly urlPattern: {
101
111
  readonly type: "String";
102
112
  readonly optional: true;
113
+ readonly maxLength: 2000;
103
114
  };
104
115
  readonly screenshotUrl: {
105
116
  readonly type: "String";
106
117
  readonly optional: true;
107
118
  readonly nativeType: "Text";
119
+ readonly maxLength: 1500000;
108
120
  };
109
121
  readonly screenshotRegion: {
110
122
  readonly type: "Json";
@@ -116,19 +128,24 @@ declare const _SITEPING_MODELS: {
116
128
  };
117
129
  readonly viewport: {
118
130
  readonly type: "String";
131
+ readonly maxLength: 50;
119
132
  };
120
133
  readonly userAgent: {
121
134
  readonly type: "String";
135
+ readonly maxLength: 500;
122
136
  };
123
137
  readonly authorName: {
124
138
  readonly type: "String";
139
+ readonly maxLength: 200;
125
140
  };
126
141
  readonly authorEmail: {
127
142
  readonly type: "String";
143
+ readonly maxLength: 200;
128
144
  };
129
145
  readonly clientId: {
130
146
  readonly type: "String";
131
147
  readonly isUnique: true;
148
+ readonly maxLength: 191;
132
149
  };
133
150
  readonly resolvedAt: {
134
151
  readonly type: "DateTime";
@@ -188,40 +205,50 @@ declare const _SITEPING_MODELS: {
188
205
  readonly cssSelector: {
189
206
  readonly type: "String";
190
207
  readonly nativeType: "Text";
208
+ readonly maxLength: 2000;
191
209
  };
192
210
  readonly xpath: {
193
211
  readonly type: "String";
194
212
  readonly nativeType: "Text";
213
+ readonly maxLength: 2000;
195
214
  };
196
215
  readonly textSnippet: {
197
216
  readonly type: "String";
198
217
  readonly nativeType: "Text";
218
+ readonly maxLength: 500;
199
219
  };
200
220
  readonly elementTag: {
201
221
  readonly type: "String";
222
+ readonly maxLength: 191;
202
223
  };
203
224
  readonly elementId: {
204
225
  readonly type: "String";
205
226
  readonly optional: true;
227
+ readonly maxLength: 191;
206
228
  };
207
229
  readonly textPrefix: {
208
230
  readonly type: "String";
209
231
  readonly nativeType: "Text";
232
+ readonly maxLength: 200;
210
233
  };
211
234
  readonly textSuffix: {
212
235
  readonly type: "String";
213
236
  readonly nativeType: "Text";
237
+ readonly maxLength: 200;
214
238
  };
215
239
  readonly fingerprint: {
216
240
  readonly type: "String";
241
+ readonly maxLength: 200;
217
242
  };
218
243
  readonly neighborText: {
219
244
  readonly type: "String";
220
245
  readonly nativeType: "Text";
246
+ readonly maxLength: 500;
221
247
  };
222
248
  readonly anchorKey: {
223
249
  readonly type: "String";
224
250
  readonly optional: true;
251
+ readonly maxLength: 200;
225
252
  };
226
253
  readonly xPct: {
227
254
  readonly type: "Float";
@@ -283,12 +310,15 @@ declare const _SITEPING_MODELS: {
283
310
  readonly body: {
284
311
  readonly type: "String";
285
312
  readonly nativeType: "Text";
313
+ readonly maxLength: 5000;
286
314
  };
287
315
  readonly authorName: {
288
316
  readonly type: "String";
317
+ readonly maxLength: 200;
289
318
  };
290
319
  readonly authorEmail: {
291
320
  readonly type: "String";
321
+ readonly maxLength: 200;
292
322
  };
293
323
  readonly authorRole: {
294
324
  readonly type: "String";
@@ -297,6 +327,7 @@ declare const _SITEPING_MODELS: {
297
327
  readonly clientId: {
298
328
  readonly type: "String";
299
329
  readonly isUnique: true;
330
+ readonly maxLength: 191;
300
331
  };
301
332
  readonly createdAt: {
302
333
  readonly type: "DateTime";
package/dist/schema.d.ts CHANGED
@@ -52,6 +52,13 @@ export interface FieldDef {
52
52
  isUnique?: boolean;
53
53
  /** Prisma native type attribute (e.g. "Text" for @db.Text) — used for MySQL compatibility on long strings */
54
54
  nativeType?: PrismaNativeType;
55
+ /**
56
+ * Longest value the HTTP handler accepts for this column, in characters —
57
+ * set on every `String` column filled from a request body, and checked
58
+ * against the handler's own caps. The CLI sizes the column from it where a
59
+ * plain `String` is shorter: on MySQL, a `VARCHAR(191)`.
60
+ */
61
+ maxLength?: number;
55
62
  /** Prisma @updatedAt attribute */
56
63
  isUpdatedAt?: boolean;
57
64
  }
@@ -82,6 +89,7 @@ declare const _SITEPING_MODELS: {
82
89
  };
83
90
  readonly projectName: {
84
91
  readonly type: "String";
92
+ readonly maxLength: 191;
85
93
  };
86
94
  readonly type: {
87
95
  readonly type: "String";
@@ -89,6 +97,7 @@ declare const _SITEPING_MODELS: {
89
97
  readonly message: {
90
98
  readonly type: "String";
91
99
  readonly nativeType: "Text";
100
+ readonly maxLength: 5000;
92
101
  };
93
102
  readonly status: {
94
103
  readonly type: "String";
@@ -96,15 +105,18 @@ declare const _SITEPING_MODELS: {
96
105
  };
97
106
  readonly url: {
98
107
  readonly type: "String";
108
+ readonly maxLength: 2000;
99
109
  };
100
110
  readonly urlPattern: {
101
111
  readonly type: "String";
102
112
  readonly optional: true;
113
+ readonly maxLength: 2000;
103
114
  };
104
115
  readonly screenshotUrl: {
105
116
  readonly type: "String";
106
117
  readonly optional: true;
107
118
  readonly nativeType: "Text";
119
+ readonly maxLength: 1500000;
108
120
  };
109
121
  readonly screenshotRegion: {
110
122
  readonly type: "Json";
@@ -116,19 +128,24 @@ declare const _SITEPING_MODELS: {
116
128
  };
117
129
  readonly viewport: {
118
130
  readonly type: "String";
131
+ readonly maxLength: 50;
119
132
  };
120
133
  readonly userAgent: {
121
134
  readonly type: "String";
135
+ readonly maxLength: 500;
122
136
  };
123
137
  readonly authorName: {
124
138
  readonly type: "String";
139
+ readonly maxLength: 200;
125
140
  };
126
141
  readonly authorEmail: {
127
142
  readonly type: "String";
143
+ readonly maxLength: 200;
128
144
  };
129
145
  readonly clientId: {
130
146
  readonly type: "String";
131
147
  readonly isUnique: true;
148
+ readonly maxLength: 191;
132
149
  };
133
150
  readonly resolvedAt: {
134
151
  readonly type: "DateTime";
@@ -188,40 +205,50 @@ declare const _SITEPING_MODELS: {
188
205
  readonly cssSelector: {
189
206
  readonly type: "String";
190
207
  readonly nativeType: "Text";
208
+ readonly maxLength: 2000;
191
209
  };
192
210
  readonly xpath: {
193
211
  readonly type: "String";
194
212
  readonly nativeType: "Text";
213
+ readonly maxLength: 2000;
195
214
  };
196
215
  readonly textSnippet: {
197
216
  readonly type: "String";
198
217
  readonly nativeType: "Text";
218
+ readonly maxLength: 500;
199
219
  };
200
220
  readonly elementTag: {
201
221
  readonly type: "String";
222
+ readonly maxLength: 191;
202
223
  };
203
224
  readonly elementId: {
204
225
  readonly type: "String";
205
226
  readonly optional: true;
227
+ readonly maxLength: 191;
206
228
  };
207
229
  readonly textPrefix: {
208
230
  readonly type: "String";
209
231
  readonly nativeType: "Text";
232
+ readonly maxLength: 200;
210
233
  };
211
234
  readonly textSuffix: {
212
235
  readonly type: "String";
213
236
  readonly nativeType: "Text";
237
+ readonly maxLength: 200;
214
238
  };
215
239
  readonly fingerprint: {
216
240
  readonly type: "String";
241
+ readonly maxLength: 200;
217
242
  };
218
243
  readonly neighborText: {
219
244
  readonly type: "String";
220
245
  readonly nativeType: "Text";
246
+ readonly maxLength: 500;
221
247
  };
222
248
  readonly anchorKey: {
223
249
  readonly type: "String";
224
250
  readonly optional: true;
251
+ readonly maxLength: 200;
225
252
  };
226
253
  readonly xPct: {
227
254
  readonly type: "Float";
@@ -283,12 +310,15 @@ declare const _SITEPING_MODELS: {
283
310
  readonly body: {
284
311
  readonly type: "String";
285
312
  readonly nativeType: "Text";
313
+ readonly maxLength: 5000;
286
314
  };
287
315
  readonly authorName: {
288
316
  readonly type: "String";
317
+ readonly maxLength: 200;
289
318
  };
290
319
  readonly authorEmail: {
291
320
  readonly type: "String";
321
+ readonly maxLength: 200;
292
322
  };
293
323
  readonly authorRole: {
294
324
  readonly type: "String";
@@ -297,6 +327,7 @@ declare const _SITEPING_MODELS: {
297
327
  readonly clientId: {
298
328
  readonly type: "String";
299
329
  readonly isUnique: true;
330
+ readonly maxLength: 191;
300
331
  };
301
332
  readonly createdAt: {
302
333
  readonly type: "DateTime";
@@ -42,26 +42,24 @@ export interface ScreenshotStorage {
42
42
  * Adapters call this synchronously inside `createFeedback` — keep it
43
43
  * fast or move to a queue if needed.
44
44
  *
45
- * `ctx.feedbackId` identifies the upload. Adapters that upload before the
46
- * record exists pass the *client-generated* `clientId` (Prisma); the
47
- * Drizzle adapter passes the server-generated id the create attempt will
48
- * insert the record under.
45
+ * `ctx.feedbackId` is the id the feedback is stored under. The record does
46
+ * not exist yet, so the store draws it before the upload — fresh for each
47
+ * create attempt, never client-supplied — and inserts the row under it.
48
+ * Two submissions of one `clientId` therefore upload under two ids, and
49
+ * only one of them is stored.
49
50
  *
50
51
  * **URL ownership:** every call must return a URL no other call returns —
51
52
  * key the object by a fresh random value (`crypto.randomUUID()`, as the
52
53
  * example does), never by `ctx.feedbackId` alone nor by a content hash.
53
54
  * Adapters treat each URL as the property of the one record that stores it
54
55
  * and may pass it to `delete` once that record is deleted, or once its
55
- * create lost a race: two submissions of one `clientId` both upload, and
56
- * the object of the one that is not stored is deleted. Under Prisma,
57
- * `ctx.feedbackId` is that client-supplied `clientId`, so an object keyed
58
- * by it is rewritten by every replay — a retry, or anyone who learns the
59
- * id — and a URL shared by several records lets deleting one remove the
60
- * object another still points at.
56
+ * create lost a race: the object of the submission that is not stored is
57
+ * deleted, and a URL shared by several records would let deleting one
58
+ * remove the object another still points at.
61
59
  *
62
- * **Security note:** treat `ctx.feedbackId` as attacker-controlled:
63
- * sanitize before using it in filesystem paths or object keys, even though
64
- * server adapters validate its shape upstream.
60
+ * **Security note:** the bundled stores generate `ctx.feedbackId`, but a
61
+ * third-party one may not honour the contract: sanitize it before using it
62
+ * in a filesystem path or an object key.
65
63
  */
66
64
  upload(dataUrl: string, ctx: {
67
65
  feedbackId: string;
@@ -72,10 +70,9 @@ export interface ScreenshotStorage {
72
70
  /**
73
71
  * Optional cleanup hook called when the feedback is deleted, and for an
74
72
  * object uploaded by a create whose record was not stored: one rejected
75
- * because its `clientId` is already stored, or — in adapters that key
76
- * uploads per attempt, like Drizzle — one whose insert failed. An object a
77
- * stored row still references (a deterministic key reused by the replay)
78
- * is kept. Receives only URLs returned by {@link ScreenshotStorage.upload}.
73
+ * because its `clientId` is already stored, or one whose insert failed. An
74
+ * object a stored row still references (a storage that broke URL
75
+ * ownership) is kept. Receives only URLs returned by {@link ScreenshotStorage.upload}.
79
76
  * Adapters call this best-effort and swallow errors — orphaned objects are
80
77
  * preferred over failed deletes.
81
78
  */
@@ -42,26 +42,24 @@ export interface ScreenshotStorage {
42
42
  * Adapters call this synchronously inside `createFeedback` — keep it
43
43
  * fast or move to a queue if needed.
44
44
  *
45
- * `ctx.feedbackId` identifies the upload. Adapters that upload before the
46
- * record exists pass the *client-generated* `clientId` (Prisma); the
47
- * Drizzle adapter passes the server-generated id the create attempt will
48
- * insert the record under.
45
+ * `ctx.feedbackId` is the id the feedback is stored under. The record does
46
+ * not exist yet, so the store draws it before the upload — fresh for each
47
+ * create attempt, never client-supplied — and inserts the row under it.
48
+ * Two submissions of one `clientId` therefore upload under two ids, and
49
+ * only one of them is stored.
49
50
  *
50
51
  * **URL ownership:** every call must return a URL no other call returns —
51
52
  * key the object by a fresh random value (`crypto.randomUUID()`, as the
52
53
  * example does), never by `ctx.feedbackId` alone nor by a content hash.
53
54
  * Adapters treat each URL as the property of the one record that stores it
54
55
  * and may pass it to `delete` once that record is deleted, or once its
55
- * create lost a race: two submissions of one `clientId` both upload, and
56
- * the object of the one that is not stored is deleted. Under Prisma,
57
- * `ctx.feedbackId` is that client-supplied `clientId`, so an object keyed
58
- * by it is rewritten by every replay — a retry, or anyone who learns the
59
- * id — and a URL shared by several records lets deleting one remove the
60
- * object another still points at.
56
+ * create lost a race: the object of the submission that is not stored is
57
+ * deleted, and a URL shared by several records would let deleting one
58
+ * remove the object another still points at.
61
59
  *
62
- * **Security note:** treat `ctx.feedbackId` as attacker-controlled:
63
- * sanitize before using it in filesystem paths or object keys, even though
64
- * server adapters validate its shape upstream.
60
+ * **Security note:** the bundled stores generate `ctx.feedbackId`, but a
61
+ * third-party one may not honour the contract: sanitize it before using it
62
+ * in a filesystem path or an object key.
65
63
  */
66
64
  upload(dataUrl: string, ctx: {
67
65
  feedbackId: string;
@@ -72,10 +70,9 @@ export interface ScreenshotStorage {
72
70
  /**
73
71
  * Optional cleanup hook called when the feedback is deleted, and for an
74
72
  * object uploaded by a create whose record was not stored: one rejected
75
- * because its `clientId` is already stored, or — in adapters that key
76
- * uploads per attempt, like Drizzle — one whose insert failed. An object a
77
- * stored row still references (a deterministic key reused by the replay)
78
- * is kept. Receives only URLs returned by {@link ScreenshotStorage.upload}.
73
+ * because its `clientId` is already stored, or one whose insert failed. An
74
+ * object a stored row still references (a storage that broke URL
75
+ * ownership) is kept. Receives only URLs returned by {@link ScreenshotStorage.upload}.
79
76
  * Adapters call this best-effort and swallow errors — orphaned objects are
80
77
  * preferred over failed deletes.
81
78
  */
@@ -1,20 +1,23 @@
1
1
  export { settleWithConcurrencyLimit } from "./concurrency.cjs";
2
- export { buildDeepLink, parseHttpUrl } from "./deep-link.cjs";
2
+ export { buildDeepLink, isSafeImageUrl, parseHttpUrl } from "./deep-link.cjs";
3
3
  export { EMAIL_PATTERN, isValidEmail } from "./email.cjs";
4
- export type { SitepingErrorCode } from "./errors.cjs";
5
- export { SitepingAuthError, SitepingError, SitepingNetworkError, SitepingValidationError } from "./errors.cjs";
4
+ export type { SitepingClientError, SitepingConfigErrorCode, SitepingErrorCode } from "./errors.cjs";
5
+ export { isSitepingConfigError, isSitepingError, SitepingAuthError, SitepingConfigError, SitepingError, SitepingNetworkError, SitepingValidationError, } from "./errors.cjs";
6
6
  export type { FilterResult, Pagination } from "./filters.cjs";
7
7
  export { applyFeedbackFilters, clampPagination, DEFAULT_PAGE_LIMIT, isUnreachableOffset, MAX_PAGE_LIMIT, matchesFeedbackQuery, } from "./filters.cjs";
8
8
  export type { I18n, LocaleLoaders, TranslateFunction } from "./i18n.cjs";
9
9
  export { canonicalizeLocale, createI18n, interpolate, intlLocale, tWithParams } from "./i18n.cjs";
10
+ export type { SitepingLogger, SitepingWarnLogger } from "./logger.cjs";
11
+ export { warnSafely } from "./logger.cjs";
10
12
  export type { FieldDef, IndexDef, ModelDef, PrismaNativeType, PrismaScalarType, RelationDef, RelationKind, RelationOnDelete, SitepingModelFieldName, SitepingModelName, } from "./schema.cjs";
11
13
  export { isRelationField, isScalarField, SITEPING_MODELS } from "./schema.cjs";
12
14
  export type { ScreenshotStorage } from "./screenshot-storage.cjs";
13
15
  export { SCREENSHOT_DELETE_CONCURRENCY, screenshotMimeType } from "./screenshot-storage.cjs";
14
16
  export type { CollectionStore, CollectionStoreBackend } from "./store-helpers.cjs";
15
17
  export { buildAnnotationRecord, buildCommentRecord, buildFeedbackRecord, createCollectionStore, } from "./store-helpers.cjs";
18
+ export { REPLACEMENT_CHARACTER, toStorableText, toStorableValue, UNSTORABLE_CODE_UNITS } from "./text.cjs";
16
19
  export type { AssertEqual, IfEquals, Prettify, Serialized } from "./type-utils.cjs";
17
20
  export { hasOwn, isRecord } from "./type-utils.cjs";
18
- export type { AnchorData, AnnotationCreateInput, AnnotationPayload, AnnotationRecord, AnnotationResponse, BuiltinLocale, ClosedFeedbackStatus, CommentAuthorRole, CommentCreateInput, CommentDeletePayload, CommentPayload, CommentRecord, CommentResponse, ConsoleDiagnosticEntry, ConsoleDiagnosticLevel, DiagnosticsCaptureOptions, DiagnosticsSnapshot, FeedbackCreateInput, FeedbackCreateOutcome, FeedbackListPermissions, FeedbackPage, FeedbackPayload, FeedbackPermissions, FeedbackQuery, FeedbackRecord, FeedbackResponse, FeedbackResponseList, FeedbackStatus, FeedbackType, FeedbackUpdateInput, NetworkDiagnosticEntry, OpenFeedbackStatus, PageScope, RectData, ScreenshotRegion, SitepingBaseConfig, SitepingCapabilities, SitepingConfig, SitepingDeepLinkOptions, SitepingHeadersOption, SitepingHttpConfig, SitepingIdentity, SitepingInstance, SitepingLocale, SitepingPanelAction, SitepingPanelActionContext, SitepingPanelActionFeedback, SitepingPanelButtonAction, SitepingPanelLinkAction, SitepingPosition, SitepingPublicEventListener, SitepingPublicEvents, SitepingSkipReason, SitepingStore, SitepingStoreConfig, SitepingTheme, SitepingUnsubscribe, } from "./types.cjs";
19
- export { ANCHOR_ELEMENT_ID_MAX, ANCHOR_ELEMENT_TAG_MAX, BUILTIN_LOCALES, CLOSED_FEEDBACK_STATUSES, COMMENT_AUTHOR_ROLES, COMMENT_BODY_MAX_LENGTH, CONSOLE_DIAGNOSTIC_LEVELS, FEEDBACK_STATUSES, FEEDBACK_TYPES, flattenAnnotation, IDENTITY_FIELD_MAX_LENGTH, isClosedStatus, isStoreDuplicate, isStoreLimit, isStoreNotFound, isStorePersistence, isStoreValueTooLong, MAX_COMMENTS_PER_FEEDBACK, OPEN_FEEDBACK_STATUSES, StoreDuplicateError, StoreLimitError, StoreNotFoundError, StorePersistenceError, StoreValueTooLongError, toFeedbackUpdate, } from "./types.cjs";
21
+ export type { AnchorData, AnnotationCreateInput, AnnotationPayload, AnnotationRecord, AnnotationResponse, BuiltinLocale, ClosedFeedbackStatus, CommentAuthorRole, CommentCreateInput, CommentCreateOutcome, CommentDeletePayload, CommentPayload, CommentPermissions, CommentRecord, CommentResponse, ConsoleDiagnosticEntry, ConsoleDiagnosticLevel, DiagnosticsCaptureOptions, DiagnosticsSnapshot, FeedbackCreateInput, FeedbackCreateOutcome, FeedbackListPermissions, FeedbackPage, FeedbackPayload, FeedbackPermissions, FeedbackQuery, FeedbackRecord, FeedbackResponse, FeedbackResponseList, FeedbackStatus, FeedbackType, FeedbackUpdateInput, NetworkDiagnosticEntry, OpenFeedbackStatus, PageScope, RectData, ScreenshotRegion, SitepingBaseConfig, SitepingCapabilities, SitepingConfig, SitepingDeepLinkOptions, SitepingHeadersOption, SitepingHttpConfig, SitepingIdentity, SitepingInstance, SitepingLocale, SitepingPanelAction, SitepingPanelActionContext, SitepingPanelActionFeedback, SitepingPanelButtonAction, SitepingPanelLinkAction, SitepingPosition, SitepingPublicEventListener, SitepingPublicEvents, SitepingSkipReason, SitepingStore, SitepingStoreConfig, SitepingTheme, SitepingUnsubscribe, } from "./types.cjs";
22
+ export { ANCHOR_ELEMENT_ID_MAX, ANCHOR_ELEMENT_TAG_MAX, BUILTIN_LOCALES, CLIENT_ID_MAX_LENGTH, CLOSED_FEEDBACK_STATUSES, COMMENT_AUTHOR_ROLES, COMMENT_BODY_MAX_LENGTH, CONSOLE_DIAGNOSTIC_LEVELS, FEEDBACK_STATUSES, FEEDBACK_TYPES, flattenAnnotation, IDENTITY_FIELD_MAX_LENGTH, isClosedStatus, isStoreDuplicate, isStoreLimit, isStoreNotFound, isStorePersistence, isStoreValueTooLong, MAX_COMMENTS_PER_FEEDBACK, OPEN_FEEDBACK_STATUSES, PROJECT_NAME_MAX_LENGTH, SITEPING_PROTOCOL_VERSION, StoreDuplicateError, StoreLimitError, StoreNotFoundError, StorePersistenceError, StoreValueTooLongError, toFeedbackUpdate, } from "./types.cjs";
20
23
  export { errorFromResponse, feedbackQueryToSearchParams, isCommentGone, isThreadFull, mergeRequestHeaders, networkErrorFromException, newClientId, withSearchParams, } from "./wire.cjs";
@@ -1,20 +1,23 @@
1
1
  export { settleWithConcurrencyLimit } from "./concurrency.js";
2
- export { buildDeepLink, parseHttpUrl } from "./deep-link.js";
2
+ export { buildDeepLink, isSafeImageUrl, parseHttpUrl } from "./deep-link.js";
3
3
  export { EMAIL_PATTERN, isValidEmail } from "./email.js";
4
- export type { SitepingErrorCode } from "./errors.js";
5
- export { SitepingAuthError, SitepingError, SitepingNetworkError, SitepingValidationError } from "./errors.js";
4
+ export type { SitepingClientError, SitepingConfigErrorCode, SitepingErrorCode } from "./errors.js";
5
+ export { isSitepingConfigError, isSitepingError, SitepingAuthError, SitepingConfigError, SitepingError, SitepingNetworkError, SitepingValidationError, } from "./errors.js";
6
6
  export type { FilterResult, Pagination } from "./filters.js";
7
7
  export { applyFeedbackFilters, clampPagination, DEFAULT_PAGE_LIMIT, isUnreachableOffset, MAX_PAGE_LIMIT, matchesFeedbackQuery, } from "./filters.js";
8
8
  export type { I18n, LocaleLoaders, TranslateFunction } from "./i18n.js";
9
9
  export { canonicalizeLocale, createI18n, interpolate, intlLocale, tWithParams } from "./i18n.js";
10
+ export type { SitepingLogger, SitepingWarnLogger } from "./logger.js";
11
+ export { warnSafely } from "./logger.js";
10
12
  export type { FieldDef, IndexDef, ModelDef, PrismaNativeType, PrismaScalarType, RelationDef, RelationKind, RelationOnDelete, SitepingModelFieldName, SitepingModelName, } from "./schema.js";
11
13
  export { isRelationField, isScalarField, SITEPING_MODELS } from "./schema.js";
12
14
  export type { ScreenshotStorage } from "./screenshot-storage.js";
13
15
  export { SCREENSHOT_DELETE_CONCURRENCY, screenshotMimeType } from "./screenshot-storage.js";
14
16
  export type { CollectionStore, CollectionStoreBackend } from "./store-helpers.js";
15
17
  export { buildAnnotationRecord, buildCommentRecord, buildFeedbackRecord, createCollectionStore, } from "./store-helpers.js";
18
+ export { REPLACEMENT_CHARACTER, toStorableText, toStorableValue, UNSTORABLE_CODE_UNITS } from "./text.js";
16
19
  export type { AssertEqual, IfEquals, Prettify, Serialized } from "./type-utils.js";
17
20
  export { hasOwn, isRecord } from "./type-utils.js";
18
- export type { AnchorData, AnnotationCreateInput, AnnotationPayload, AnnotationRecord, AnnotationResponse, BuiltinLocale, ClosedFeedbackStatus, CommentAuthorRole, CommentCreateInput, CommentDeletePayload, CommentPayload, CommentRecord, CommentResponse, ConsoleDiagnosticEntry, ConsoleDiagnosticLevel, DiagnosticsCaptureOptions, DiagnosticsSnapshot, FeedbackCreateInput, FeedbackCreateOutcome, FeedbackListPermissions, FeedbackPage, FeedbackPayload, FeedbackPermissions, FeedbackQuery, FeedbackRecord, FeedbackResponse, FeedbackResponseList, FeedbackStatus, FeedbackType, FeedbackUpdateInput, NetworkDiagnosticEntry, OpenFeedbackStatus, PageScope, RectData, ScreenshotRegion, SitepingBaseConfig, SitepingCapabilities, SitepingConfig, SitepingDeepLinkOptions, SitepingHeadersOption, SitepingHttpConfig, SitepingIdentity, SitepingInstance, SitepingLocale, SitepingPanelAction, SitepingPanelActionContext, SitepingPanelActionFeedback, SitepingPanelButtonAction, SitepingPanelLinkAction, SitepingPosition, SitepingPublicEventListener, SitepingPublicEvents, SitepingSkipReason, SitepingStore, SitepingStoreConfig, SitepingTheme, SitepingUnsubscribe, } from "./types.js";
19
- export { ANCHOR_ELEMENT_ID_MAX, ANCHOR_ELEMENT_TAG_MAX, BUILTIN_LOCALES, CLOSED_FEEDBACK_STATUSES, COMMENT_AUTHOR_ROLES, COMMENT_BODY_MAX_LENGTH, CONSOLE_DIAGNOSTIC_LEVELS, FEEDBACK_STATUSES, FEEDBACK_TYPES, flattenAnnotation, IDENTITY_FIELD_MAX_LENGTH, isClosedStatus, isStoreDuplicate, isStoreLimit, isStoreNotFound, isStorePersistence, isStoreValueTooLong, MAX_COMMENTS_PER_FEEDBACK, OPEN_FEEDBACK_STATUSES, StoreDuplicateError, StoreLimitError, StoreNotFoundError, StorePersistenceError, StoreValueTooLongError, toFeedbackUpdate, } from "./types.js";
21
+ export type { AnchorData, AnnotationCreateInput, AnnotationPayload, AnnotationRecord, AnnotationResponse, BuiltinLocale, ClosedFeedbackStatus, CommentAuthorRole, CommentCreateInput, CommentCreateOutcome, CommentDeletePayload, CommentPayload, CommentPermissions, CommentRecord, CommentResponse, ConsoleDiagnosticEntry, ConsoleDiagnosticLevel, DiagnosticsCaptureOptions, DiagnosticsSnapshot, FeedbackCreateInput, FeedbackCreateOutcome, FeedbackListPermissions, FeedbackPage, FeedbackPayload, FeedbackPermissions, FeedbackQuery, FeedbackRecord, FeedbackResponse, FeedbackResponseList, FeedbackStatus, FeedbackType, FeedbackUpdateInput, NetworkDiagnosticEntry, OpenFeedbackStatus, PageScope, RectData, ScreenshotRegion, SitepingBaseConfig, SitepingCapabilities, SitepingConfig, SitepingDeepLinkOptions, SitepingHeadersOption, SitepingHttpConfig, SitepingIdentity, SitepingInstance, SitepingLocale, SitepingPanelAction, SitepingPanelActionContext, SitepingPanelActionFeedback, SitepingPanelButtonAction, SitepingPanelLinkAction, SitepingPosition, SitepingPublicEventListener, SitepingPublicEvents, SitepingSkipReason, SitepingStore, SitepingStoreConfig, SitepingTheme, SitepingUnsubscribe, } from "./types.js";
22
+ export { ANCHOR_ELEMENT_ID_MAX, ANCHOR_ELEMENT_TAG_MAX, BUILTIN_LOCALES, CLIENT_ID_MAX_LENGTH, CLOSED_FEEDBACK_STATUSES, COMMENT_AUTHOR_ROLES, COMMENT_BODY_MAX_LENGTH, CONSOLE_DIAGNOSTIC_LEVELS, FEEDBACK_STATUSES, FEEDBACK_TYPES, flattenAnnotation, IDENTITY_FIELD_MAX_LENGTH, isClosedStatus, isStoreDuplicate, isStoreLimit, isStoreNotFound, isStorePersistence, isStoreValueTooLong, MAX_COMMENTS_PER_FEEDBACK, OPEN_FEEDBACK_STATUSES, PROJECT_NAME_MAX_LENGTH, SITEPING_PROTOCOL_VERSION, StoreDuplicateError, StoreLimitError, StoreNotFoundError, StorePersistenceError, StoreValueTooLongError, toFeedbackUpdate, } from "./types.js";
20
23
  export { errorFromResponse, feedbackQueryToSearchParams, isCommentGone, isThreadFull, mergeRequestHeaders, networkErrorFromException, newClientId, withSearchParams, } from "./wire.js";
@@ -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">>;