@siteping/server 0.1.1 → 0.2.1

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.
package/openapi.json ADDED
@@ -0,0 +1,1532 @@
1
+ {
2
+ "openapi": "3.1.0",
3
+ "info": {
4
+ "title": "SitePing HTTP API",
5
+ "version": "1",
6
+ "description": "The protocol `createSitepingHandler` (`@siteping/server`) serves: one endpoint, mounted at any path (`/api/siteping` here, what `npx @siteping/cli init` generates), five methods, JSON bodies. `info.version` is the protocol version a list response sends as `protocol`. Error strings are informational: branch on the status code. See https://siteping.dev/docs/server#wire-protocol.",
7
+ "license": {
8
+ "name": "MIT",
9
+ "identifier": "MIT"
10
+ }
11
+ },
12
+ "x-siteping-protocol": 1,
13
+ "paths": {
14
+ "/api/siteping": {
15
+ "get": {
16
+ "operationId": "listFeedbacks",
17
+ "summary": "List a project's feedbacks",
18
+ "description": "A page of feedbacks, newest first, each with its thread and the requester's permissions. Author emails are blanked unless the access policy lets the requester read them; `clientId` is never sent.",
19
+ "parameters": [
20
+ {
21
+ "name": "projectName",
22
+ "in": "query",
23
+ "required": true,
24
+ "schema": {
25
+ "type": "string",
26
+ "minLength": 1,
27
+ "maxLength": 191
28
+ }
29
+ },
30
+ {
31
+ "name": "page",
32
+ "in": "query",
33
+ "required": false,
34
+ "schema": {
35
+ "default": 1,
36
+ "type": "integer",
37
+ "minimum": 1,
38
+ "maximum": 9007199254740991
39
+ }
40
+ },
41
+ {
42
+ "name": "limit",
43
+ "in": "query",
44
+ "required": false,
45
+ "schema": {
46
+ "default": 50,
47
+ "type": "integer",
48
+ "minimum": 1,
49
+ "maximum": 100
50
+ }
51
+ },
52
+ {
53
+ "name": "type",
54
+ "in": "query",
55
+ "required": false,
56
+ "schema": {
57
+ "type": "string",
58
+ "enum": [
59
+ "question",
60
+ "change",
61
+ "bug",
62
+ "other"
63
+ ]
64
+ }
65
+ },
66
+ {
67
+ "name": "status",
68
+ "in": "query",
69
+ "required": false,
70
+ "schema": {
71
+ "type": "string",
72
+ "enum": [
73
+ "open",
74
+ "in_progress",
75
+ "resolved",
76
+ "wont_fix"
77
+ ]
78
+ }
79
+ },
80
+ {
81
+ "name": "statuses",
82
+ "in": "query",
83
+ "required": false,
84
+ "schema": {
85
+ "maxItems": 4,
86
+ "type": "array",
87
+ "items": {
88
+ "type": "string",
89
+ "enum": [
90
+ "open",
91
+ "in_progress",
92
+ "resolved",
93
+ "wont_fix"
94
+ ]
95
+ }
96
+ },
97
+ "style": "form",
98
+ "explode": false,
99
+ "description": "Statuses to include, comma-separated; takes precedence over `status`."
100
+ },
101
+ {
102
+ "name": "search",
103
+ "in": "query",
104
+ "required": false,
105
+ "schema": {
106
+ "type": "string",
107
+ "maxLength": 200
108
+ }
109
+ },
110
+ {
111
+ "name": "url",
112
+ "in": "query",
113
+ "required": false,
114
+ "schema": {
115
+ "type": "string",
116
+ "maxLength": 2000
117
+ }
118
+ },
119
+ {
120
+ "name": "urlPattern",
121
+ "in": "query",
122
+ "required": false,
123
+ "schema": {
124
+ "type": "string",
125
+ "maxLength": 2000
126
+ }
127
+ }
128
+ ],
129
+ "responses": {
130
+ "200": {
131
+ "description": "The page.",
132
+ "headers": {
133
+ "Cache-Control": {
134
+ "description": "`private, max-age=5` under the `apiKey` policy, `no-store` under `access`.",
135
+ "schema": {
136
+ "type": "string"
137
+ }
138
+ }
139
+ },
140
+ "content": {
141
+ "application/json": {
142
+ "schema": {
143
+ "$ref": "#/components/schemas/FeedbackList"
144
+ }
145
+ }
146
+ }
147
+ },
148
+ "400": {
149
+ "description": "A query parameter failed validation.",
150
+ "content": {
151
+ "application/json": {
152
+ "schema": {
153
+ "$ref": "#/components/schemas/ValidationErrors"
154
+ }
155
+ }
156
+ }
157
+ },
158
+ "401": {
159
+ "$ref": "#/components/responses/Unauthorized"
160
+ },
161
+ "403": {
162
+ "$ref": "#/components/responses/Forbidden"
163
+ },
164
+ "429": {
165
+ "$ref": "#/components/responses/TooManyRequests"
166
+ },
167
+ "500": {
168
+ "$ref": "#/components/responses/InternalServerError"
169
+ }
170
+ }
171
+ },
172
+ "post": {
173
+ "operationId": "createFeedbackOrComment",
174
+ "summary": "Create a feedback, or a comment on one",
175
+ "description": "Routed by the body: one carrying `feedbackId` posts a comment to that feedback's thread, any other creates a feedback. Both are idempotent on `clientId`: a replay answers `201` with the stored record.",
176
+ "requestBody": {
177
+ "required": true,
178
+ "content": {
179
+ "application/json": {
180
+ "schema": {
181
+ "oneOf": [
182
+ {
183
+ "$ref": "#/components/schemas/FeedbackCreate",
184
+ "not": {
185
+ "required": [
186
+ "feedbackId"
187
+ ]
188
+ }
189
+ },
190
+ {
191
+ "$ref": "#/components/schemas/CommentCreate"
192
+ }
193
+ ]
194
+ }
195
+ }
196
+ }
197
+ },
198
+ "responses": {
199
+ "201": {
200
+ "description": "The feedback (fresh or replayed), or the comment.",
201
+ "content": {
202
+ "application/json": {
203
+ "schema": {
204
+ "oneOf": [
205
+ {
206
+ "$ref": "#/components/schemas/Feedback"
207
+ },
208
+ {
209
+ "$ref": "#/components/schemas/Comment"
210
+ }
211
+ ]
212
+ }
213
+ }
214
+ }
215
+ },
216
+ "400": {
217
+ "$ref": "#/components/responses/BadRequest"
218
+ },
219
+ "401": {
220
+ "$ref": "#/components/responses/Unauthorized"
221
+ },
222
+ "403": {
223
+ "$ref": "#/components/responses/Forbidden"
224
+ },
225
+ "404": {
226
+ "description": "Comment: the feedback does not exist in this project.",
227
+ "content": {
228
+ "application/json": {
229
+ "schema": {
230
+ "$ref": "#/components/schemas/Error"
231
+ }
232
+ }
233
+ }
234
+ },
235
+ "409": {
236
+ "description": "Feedback: its `clientId` is already used by another project. Comment: the thread holds 100 `client` comments, or the comment's `clientId` is already used on another feedback. None is worth a retry.",
237
+ "content": {
238
+ "application/json": {
239
+ "schema": {
240
+ "$ref": "#/components/schemas/Error"
241
+ }
242
+ }
243
+ }
244
+ },
245
+ "413": {
246
+ "$ref": "#/components/responses/PayloadTooLarge"
247
+ },
248
+ "415": {
249
+ "$ref": "#/components/responses/UnsupportedMediaType"
250
+ },
251
+ "422": {
252
+ "$ref": "#/components/responses/ValueTooLong"
253
+ },
254
+ "429": {
255
+ "$ref": "#/components/responses/TooManyRequests"
256
+ },
257
+ "500": {
258
+ "$ref": "#/components/responses/InternalServerError"
259
+ },
260
+ "501": {
261
+ "description": "Comment: the store behind the endpoint keeps no comments.",
262
+ "content": {
263
+ "application/json": {
264
+ "schema": {
265
+ "$ref": "#/components/schemas/Error"
266
+ }
267
+ }
268
+ }
269
+ }
270
+ }
271
+ },
272
+ "patch": {
273
+ "operationId": "updateFeedbackStatus",
274
+ "summary": "Change a feedback's status",
275
+ "description": "`resolvedAt` follows: set when the status becomes `resolved` or `wont_fix`, cleared otherwise.",
276
+ "requestBody": {
277
+ "required": true,
278
+ "content": {
279
+ "application/json": {
280
+ "schema": {
281
+ "$ref": "#/components/schemas/FeedbackPatch"
282
+ }
283
+ }
284
+ }
285
+ },
286
+ "responses": {
287
+ "200": {
288
+ "description": "The updated feedback.",
289
+ "content": {
290
+ "application/json": {
291
+ "schema": {
292
+ "$ref": "#/components/schemas/Feedback"
293
+ }
294
+ }
295
+ }
296
+ },
297
+ "400": {
298
+ "$ref": "#/components/responses/BadRequest"
299
+ },
300
+ "401": {
301
+ "$ref": "#/components/responses/Unauthorized"
302
+ },
303
+ "403": {
304
+ "$ref": "#/components/responses/Forbidden"
305
+ },
306
+ "404": {
307
+ "description": "The feedback does not exist in this project.",
308
+ "content": {
309
+ "application/json": {
310
+ "schema": {
311
+ "$ref": "#/components/schemas/Error"
312
+ }
313
+ }
314
+ }
315
+ },
316
+ "413": {
317
+ "$ref": "#/components/responses/PayloadTooLarge"
318
+ },
319
+ "415": {
320
+ "$ref": "#/components/responses/UnsupportedMediaType"
321
+ },
322
+ "429": {
323
+ "$ref": "#/components/responses/TooManyRequests"
324
+ },
325
+ "500": {
326
+ "$ref": "#/components/responses/InternalServerError"
327
+ }
328
+ }
329
+ },
330
+ "delete": {
331
+ "operationId": "deleteFeedbackOrComment",
332
+ "summary": "Delete a feedback, every feedback of a project, or a comment",
333
+ "description": "Routed by the body: one carrying `commentId` deletes that comment from the feedback's thread, any other deletes feedback. The body is required: a proxy that strips `DELETE` bodies makes it a `400`.",
334
+ "requestBody": {
335
+ "required": true,
336
+ "content": {
337
+ "application/json": {
338
+ "schema": {
339
+ "oneOf": [
340
+ {
341
+ "$ref": "#/components/schemas/FeedbackDelete",
342
+ "not": {
343
+ "required": [
344
+ "commentId"
345
+ ]
346
+ }
347
+ },
348
+ {
349
+ "$ref": "#/components/schemas/FeedbackDeleteAll",
350
+ "not": {
351
+ "required": [
352
+ "commentId"
353
+ ]
354
+ }
355
+ },
356
+ {
357
+ "$ref": "#/components/schemas/CommentDelete"
358
+ }
359
+ ]
360
+ }
361
+ }
362
+ }
363
+ },
364
+ "responses": {
365
+ "200": {
366
+ "description": "Deleted.",
367
+ "content": {
368
+ "application/json": {
369
+ "schema": {
370
+ "$ref": "#/components/schemas/Deleted"
371
+ }
372
+ }
373
+ }
374
+ },
375
+ "400": {
376
+ "$ref": "#/components/responses/BadRequest"
377
+ },
378
+ "401": {
379
+ "$ref": "#/components/responses/Unauthorized"
380
+ },
381
+ "403": {
382
+ "$ref": "#/components/responses/Forbidden"
383
+ },
384
+ "404": {
385
+ "description": "The feedback, or the comment, does not exist in this project.",
386
+ "content": {
387
+ "application/json": {
388
+ "schema": {
389
+ "$ref": "#/components/schemas/Error"
390
+ }
391
+ }
392
+ }
393
+ },
394
+ "413": {
395
+ "$ref": "#/components/responses/PayloadTooLarge"
396
+ },
397
+ "415": {
398
+ "$ref": "#/components/responses/UnsupportedMediaType"
399
+ },
400
+ "429": {
401
+ "$ref": "#/components/responses/TooManyRequests"
402
+ },
403
+ "500": {
404
+ "$ref": "#/components/responses/InternalServerError"
405
+ },
406
+ "501": {
407
+ "description": "Comment: the store behind the endpoint keeps no comments.",
408
+ "content": {
409
+ "application/json": {
410
+ "schema": {
411
+ "$ref": "#/components/schemas/Error"
412
+ }
413
+ }
414
+ }
415
+ },
416
+ "502": {
417
+ "description": "An `onDeleting` hook threw: nothing was deleted.",
418
+ "content": {
419
+ "application/json": {
420
+ "schema": {
421
+ "$ref": "#/components/schemas/Error"
422
+ }
423
+ }
424
+ }
425
+ }
426
+ }
427
+ },
428
+ "options": {
429
+ "operationId": "preflight",
430
+ "summary": "CORS preflight",
431
+ "security": [],
432
+ "responses": {
433
+ "204": {
434
+ "description": "CORS headers for an origin listed in `allowedOrigins`; none otherwise, and the browser blocks the request."
435
+ }
436
+ }
437
+ }
438
+ }
439
+ },
440
+ "components": {
441
+ "schemas": {
442
+ "FeedbackCreate": {
443
+ "type": "object",
444
+ "properties": {
445
+ "projectName": {
446
+ "type": "string",
447
+ "minLength": 1,
448
+ "maxLength": 191
449
+ },
450
+ "type": {
451
+ "type": "string",
452
+ "enum": [
453
+ "question",
454
+ "change",
455
+ "bug",
456
+ "other"
457
+ ]
458
+ },
459
+ "message": {
460
+ "type": "string",
461
+ "minLength": 1,
462
+ "maxLength": 5000
463
+ },
464
+ "url": {
465
+ "type": "string",
466
+ "minLength": 1,
467
+ "maxLength": 2000
468
+ },
469
+ "urlPattern": {
470
+ "anyOf": [
471
+ {
472
+ "type": "string",
473
+ "maxLength": 2000
474
+ },
475
+ {
476
+ "type": "null"
477
+ }
478
+ ]
479
+ },
480
+ "viewport": {
481
+ "type": "string",
482
+ "minLength": 1,
483
+ "maxLength": 50
484
+ },
485
+ "userAgent": {
486
+ "type": "string",
487
+ "minLength": 1,
488
+ "maxLength": 500
489
+ },
490
+ "authorName": {
491
+ "type": "string",
492
+ "minLength": 1,
493
+ "maxLength": 200
494
+ },
495
+ "authorEmail": {
496
+ "type": "string",
497
+ "maxLength": 200,
498
+ "format": "email",
499
+ "pattern": "^(?!\\.)(?!.*\\.\\.)[\\p{L}\\p{M}\\p{N}!#$%&'*+/=?^_`{|}~.-]{0,63}[\\p{L}\\p{M}\\p{N}!#$%&'*+/=?^_`{|}~-]@(?:[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?\\.)+[\\p{L}\\p{N}][\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}]$"
500
+ },
501
+ "annotations": {
502
+ "maxItems": 50,
503
+ "type": "array",
504
+ "items": {
505
+ "type": "object",
506
+ "properties": {
507
+ "anchor": {
508
+ "type": "object",
509
+ "properties": {
510
+ "cssSelector": {
511
+ "type": "string",
512
+ "minLength": 1,
513
+ "maxLength": 2000
514
+ },
515
+ "xpath": {
516
+ "type": "string",
517
+ "minLength": 1,
518
+ "maxLength": 2000
519
+ },
520
+ "textSnippet": {
521
+ "type": "string",
522
+ "maxLength": 500
523
+ },
524
+ "elementTag": {
525
+ "type": "string",
526
+ "minLength": 1,
527
+ "maxLength": 191
528
+ },
529
+ "elementId": {
530
+ "type": "string"
531
+ },
532
+ "textPrefix": {
533
+ "type": "string",
534
+ "maxLength": 200
535
+ },
536
+ "textSuffix": {
537
+ "type": "string",
538
+ "maxLength": 200
539
+ },
540
+ "fingerprint": {
541
+ "type": "string",
542
+ "maxLength": 200
543
+ },
544
+ "neighborText": {
545
+ "type": "string",
546
+ "maxLength": 500
547
+ },
548
+ "anchorKey": {
549
+ "anyOf": [
550
+ {
551
+ "type": "string",
552
+ "maxLength": 200
553
+ },
554
+ {
555
+ "type": "null"
556
+ }
557
+ ]
558
+ }
559
+ },
560
+ "required": [
561
+ "cssSelector",
562
+ "xpath",
563
+ "textSnippet",
564
+ "elementTag",
565
+ "textPrefix",
566
+ "textSuffix",
567
+ "fingerprint",
568
+ "neighborText"
569
+ ]
570
+ },
571
+ "rect": {
572
+ "type": "object",
573
+ "properties": {
574
+ "xPct": {
575
+ "type": "number",
576
+ "minimum": 0,
577
+ "maximum": 1
578
+ },
579
+ "yPct": {
580
+ "type": "number",
581
+ "minimum": 0,
582
+ "maximum": 1
583
+ },
584
+ "wPct": {
585
+ "type": "number",
586
+ "minimum": 0,
587
+ "maximum": 1
588
+ },
589
+ "hPct": {
590
+ "type": "number",
591
+ "minimum": 0,
592
+ "maximum": 1
593
+ }
594
+ },
595
+ "required": [
596
+ "xPct",
597
+ "yPct",
598
+ "wPct",
599
+ "hPct"
600
+ ]
601
+ },
602
+ "scrollX": {
603
+ "type": "number"
604
+ },
605
+ "scrollY": {
606
+ "type": "number"
607
+ },
608
+ "viewportW": {
609
+ "type": "integer",
610
+ "exclusiveMinimum": 0,
611
+ "maximum": 2147483647
612
+ },
613
+ "viewportH": {
614
+ "type": "integer",
615
+ "exclusiveMinimum": 0,
616
+ "maximum": 2147483647
617
+ },
618
+ "devicePixelRatio": {
619
+ "default": 1,
620
+ "type": "number",
621
+ "exclusiveMinimum": 0
622
+ }
623
+ },
624
+ "required": [
625
+ "anchor",
626
+ "rect",
627
+ "scrollX",
628
+ "scrollY",
629
+ "viewportW",
630
+ "viewportH"
631
+ ]
632
+ }
633
+ },
634
+ "clientId": {
635
+ "type": "string",
636
+ "minLength": 1,
637
+ "maxLength": 191,
638
+ "pattern": "^[a-zA-Z0-9_-]+$"
639
+ },
640
+ "screenshotDataUrl": {
641
+ "anyOf": [
642
+ {
643
+ "type": "string",
644
+ "maxLength": 1500000,
645
+ "pattern": "^data:image\\/(jpeg|png|webp);base64,"
646
+ },
647
+ {
648
+ "type": "null"
649
+ }
650
+ ]
651
+ },
652
+ "screenshotRegion": {
653
+ "anyOf": [
654
+ {
655
+ "$ref": "#/components/schemas/ScreenshotRegion"
656
+ },
657
+ {
658
+ "type": "null"
659
+ }
660
+ ]
661
+ },
662
+ "diagnostics": {
663
+ "anyOf": [
664
+ {
665
+ "type": "object",
666
+ "properties": {
667
+ "console": {
668
+ "maxItems": 50,
669
+ "type": "array",
670
+ "items": {
671
+ "type": "object",
672
+ "properties": {
673
+ "level": {
674
+ "type": "string",
675
+ "enum": [
676
+ "log",
677
+ "info",
678
+ "warn",
679
+ "error"
680
+ ]
681
+ },
682
+ "timestamp": {
683
+ "type": "string",
684
+ "maxLength": 50
685
+ },
686
+ "message": {
687
+ "type": "string",
688
+ "maxLength": 600
689
+ }
690
+ },
691
+ "required": [
692
+ "level",
693
+ "timestamp",
694
+ "message"
695
+ ]
696
+ }
697
+ },
698
+ "network": {
699
+ "maxItems": 20,
700
+ "type": "array",
701
+ "items": {
702
+ "type": "object",
703
+ "properties": {
704
+ "url": {
705
+ "type": "string",
706
+ "maxLength": 2000
707
+ },
708
+ "method": {
709
+ "type": "string",
710
+ "maxLength": 20
711
+ },
712
+ "status": {
713
+ "type": "integer",
714
+ "minimum": 0,
715
+ "maximum": 599
716
+ },
717
+ "durationMs": {
718
+ "type": "number",
719
+ "minimum": 0,
720
+ "maximum": 600000
721
+ },
722
+ "timestamp": {
723
+ "type": "string",
724
+ "maxLength": 50
725
+ }
726
+ },
727
+ "required": [
728
+ "url",
729
+ "method",
730
+ "status",
731
+ "durationMs",
732
+ "timestamp"
733
+ ]
734
+ }
735
+ }
736
+ },
737
+ "required": [
738
+ "console",
739
+ "network"
740
+ ]
741
+ },
742
+ {
743
+ "type": "null"
744
+ }
745
+ ]
746
+ }
747
+ },
748
+ "required": [
749
+ "projectName",
750
+ "type",
751
+ "message",
752
+ "url",
753
+ "viewport",
754
+ "userAgent",
755
+ "authorName",
756
+ "authorEmail",
757
+ "annotations",
758
+ "clientId"
759
+ ]
760
+ },
761
+ "CommentCreate": {
762
+ "type": "object",
763
+ "properties": {
764
+ "projectName": {
765
+ "type": "string",
766
+ "minLength": 1,
767
+ "maxLength": 191
768
+ },
769
+ "feedbackId": {
770
+ "type": "string",
771
+ "minLength": 1,
772
+ "maxLength": 200
773
+ },
774
+ "body": {
775
+ "type": "string",
776
+ "minLength": 1,
777
+ "maxLength": 5000
778
+ },
779
+ "authorName": {
780
+ "type": "string",
781
+ "minLength": 1,
782
+ "maxLength": 200
783
+ },
784
+ "authorEmail": {
785
+ "anyOf": [
786
+ {
787
+ "type": "string",
788
+ "const": ""
789
+ },
790
+ {
791
+ "type": "string",
792
+ "maxLength": 200,
793
+ "format": "email",
794
+ "pattern": "^(?!\\.)(?!.*\\.\\.)[\\p{L}\\p{M}\\p{N}!#$%&'*+/=?^_`{|}~.-]{0,63}[\\p{L}\\p{M}\\p{N}!#$%&'*+/=?^_`{|}~-]@(?:[\\p{L}\\p{N}](?:[\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}])?\\.)+[\\p{L}\\p{N}][\\p{L}\\p{M}\\p{N}-]{0,61}[\\p{L}\\p{M}\\p{N}]$"
795
+ }
796
+ ]
797
+ },
798
+ "authorRole": {
799
+ "default": "client",
800
+ "type": "string",
801
+ "enum": [
802
+ "client",
803
+ "team"
804
+ ]
805
+ },
806
+ "clientId": {
807
+ "type": "string",
808
+ "minLength": 1,
809
+ "maxLength": 191,
810
+ "pattern": "^[a-zA-Z0-9_-]+$"
811
+ }
812
+ },
813
+ "required": [
814
+ "projectName",
815
+ "feedbackId",
816
+ "body",
817
+ "authorName",
818
+ "authorEmail",
819
+ "clientId"
820
+ ]
821
+ },
822
+ "FeedbackPatch": {
823
+ "type": "object",
824
+ "properties": {
825
+ "id": {
826
+ "type": "string",
827
+ "minLength": 1
828
+ },
829
+ "projectName": {
830
+ "type": "string",
831
+ "minLength": 1,
832
+ "maxLength": 191
833
+ },
834
+ "status": {
835
+ "type": "string",
836
+ "enum": [
837
+ "open",
838
+ "in_progress",
839
+ "resolved",
840
+ "wont_fix"
841
+ ]
842
+ }
843
+ },
844
+ "required": [
845
+ "id",
846
+ "projectName",
847
+ "status"
848
+ ]
849
+ },
850
+ "FeedbackDelete": {
851
+ "type": "object",
852
+ "properties": {
853
+ "id": {
854
+ "type": "string",
855
+ "minLength": 1
856
+ },
857
+ "projectName": {
858
+ "type": "string",
859
+ "minLength": 1,
860
+ "maxLength": 191
861
+ }
862
+ },
863
+ "required": [
864
+ "id",
865
+ "projectName"
866
+ ]
867
+ },
868
+ "FeedbackDeleteAll": {
869
+ "type": "object",
870
+ "properties": {
871
+ "projectName": {
872
+ "type": "string",
873
+ "minLength": 1,
874
+ "maxLength": 191
875
+ },
876
+ "deleteAll": {
877
+ "type": "boolean",
878
+ "const": true
879
+ }
880
+ },
881
+ "required": [
882
+ "projectName",
883
+ "deleteAll"
884
+ ]
885
+ },
886
+ "CommentDelete": {
887
+ "type": "object",
888
+ "properties": {
889
+ "projectName": {
890
+ "type": "string",
891
+ "minLength": 1,
892
+ "maxLength": 191
893
+ },
894
+ "feedbackId": {
895
+ "type": "string",
896
+ "minLength": 1,
897
+ "maxLength": 200
898
+ },
899
+ "commentId": {
900
+ "type": "string",
901
+ "minLength": 1,
902
+ "maxLength": 200
903
+ }
904
+ },
905
+ "required": [
906
+ "projectName",
907
+ "feedbackId",
908
+ "commentId"
909
+ ]
910
+ },
911
+ "Feedback": {
912
+ "type": "object",
913
+ "properties": {
914
+ "id": {
915
+ "type": "string"
916
+ },
917
+ "type": {
918
+ "type": "string",
919
+ "enum": [
920
+ "question",
921
+ "change",
922
+ "bug",
923
+ "other"
924
+ ]
925
+ },
926
+ "message": {
927
+ "type": "string"
928
+ },
929
+ "status": {
930
+ "type": "string",
931
+ "enum": [
932
+ "open",
933
+ "in_progress",
934
+ "resolved",
935
+ "wont_fix"
936
+ ]
937
+ },
938
+ "projectName": {
939
+ "type": "string"
940
+ },
941
+ "url": {
942
+ "type": "string"
943
+ },
944
+ "urlPattern": {
945
+ "type": [
946
+ "string",
947
+ "null"
948
+ ]
949
+ },
950
+ "authorName": {
951
+ "type": "string"
952
+ },
953
+ "authorEmail": {
954
+ "type": "string"
955
+ },
956
+ "viewport": {
957
+ "type": "string"
958
+ },
959
+ "userAgent": {
960
+ "type": "string"
961
+ },
962
+ "resolvedAt": {
963
+ "anyOf": [
964
+ {
965
+ "type": "string",
966
+ "format": "date-time"
967
+ },
968
+ {
969
+ "type": "null"
970
+ }
971
+ ]
972
+ },
973
+ "createdAt": {
974
+ "type": "string",
975
+ "format": "date-time"
976
+ },
977
+ "updatedAt": {
978
+ "type": "string",
979
+ "format": "date-time"
980
+ },
981
+ "annotations": {
982
+ "type": "array",
983
+ "items": {
984
+ "$ref": "#/components/schemas/Annotation"
985
+ }
986
+ },
987
+ "comments": {
988
+ "type": "array",
989
+ "items": {
990
+ "$ref": "#/components/schemas/Comment"
991
+ }
992
+ },
993
+ "screenshotUrl": {
994
+ "type": [
995
+ "string",
996
+ "null"
997
+ ]
998
+ },
999
+ "screenshotRegion": {
1000
+ "anyOf": [
1001
+ {
1002
+ "$ref": "#/components/schemas/ScreenshotRegion"
1003
+ },
1004
+ {
1005
+ "type": "null"
1006
+ }
1007
+ ]
1008
+ },
1009
+ "diagnostics": {
1010
+ "anyOf": [
1011
+ {
1012
+ "$ref": "#/components/schemas/Diagnostics"
1013
+ },
1014
+ {
1015
+ "type": "null"
1016
+ }
1017
+ ]
1018
+ },
1019
+ "permissions": {
1020
+ "$ref": "#/components/schemas/FeedbackPermissions"
1021
+ }
1022
+ },
1023
+ "required": [
1024
+ "id",
1025
+ "type",
1026
+ "message",
1027
+ "status",
1028
+ "projectName",
1029
+ "url",
1030
+ "urlPattern",
1031
+ "authorName",
1032
+ "authorEmail",
1033
+ "viewport",
1034
+ "userAgent",
1035
+ "resolvedAt",
1036
+ "createdAt",
1037
+ "updatedAt",
1038
+ "annotations",
1039
+ "comments",
1040
+ "screenshotUrl",
1041
+ "screenshotRegion",
1042
+ "diagnostics",
1043
+ "permissions"
1044
+ ]
1045
+ },
1046
+ "Annotation": {
1047
+ "type": "object",
1048
+ "properties": {
1049
+ "id": {
1050
+ "type": "string"
1051
+ },
1052
+ "feedbackId": {
1053
+ "type": "string"
1054
+ },
1055
+ "cssSelector": {
1056
+ "type": "string"
1057
+ },
1058
+ "xpath": {
1059
+ "type": "string"
1060
+ },
1061
+ "textSnippet": {
1062
+ "type": "string"
1063
+ },
1064
+ "elementTag": {
1065
+ "type": "string"
1066
+ },
1067
+ "elementId": {
1068
+ "type": [
1069
+ "string",
1070
+ "null"
1071
+ ]
1072
+ },
1073
+ "textPrefix": {
1074
+ "type": "string"
1075
+ },
1076
+ "textSuffix": {
1077
+ "type": "string"
1078
+ },
1079
+ "fingerprint": {
1080
+ "type": "string"
1081
+ },
1082
+ "neighborText": {
1083
+ "type": "string"
1084
+ },
1085
+ "anchorKey": {
1086
+ "type": [
1087
+ "string",
1088
+ "null"
1089
+ ]
1090
+ },
1091
+ "xPct": {
1092
+ "type": "number"
1093
+ },
1094
+ "yPct": {
1095
+ "type": "number"
1096
+ },
1097
+ "wPct": {
1098
+ "type": "number"
1099
+ },
1100
+ "hPct": {
1101
+ "type": "number"
1102
+ },
1103
+ "scrollX": {
1104
+ "type": "number"
1105
+ },
1106
+ "scrollY": {
1107
+ "type": "number"
1108
+ },
1109
+ "viewportW": {
1110
+ "type": "number"
1111
+ },
1112
+ "viewportH": {
1113
+ "type": "number"
1114
+ },
1115
+ "devicePixelRatio": {
1116
+ "type": "number"
1117
+ },
1118
+ "createdAt": {
1119
+ "type": "string",
1120
+ "format": "date-time"
1121
+ }
1122
+ },
1123
+ "required": [
1124
+ "id",
1125
+ "feedbackId",
1126
+ "cssSelector",
1127
+ "xpath",
1128
+ "textSnippet",
1129
+ "elementTag",
1130
+ "elementId",
1131
+ "textPrefix",
1132
+ "textSuffix",
1133
+ "fingerprint",
1134
+ "neighborText",
1135
+ "anchorKey",
1136
+ "xPct",
1137
+ "yPct",
1138
+ "wPct",
1139
+ "hPct",
1140
+ "scrollX",
1141
+ "scrollY",
1142
+ "viewportW",
1143
+ "viewportH",
1144
+ "devicePixelRatio",
1145
+ "createdAt"
1146
+ ]
1147
+ },
1148
+ "Comment": {
1149
+ "type": "object",
1150
+ "properties": {
1151
+ "id": {
1152
+ "type": "string"
1153
+ },
1154
+ "feedbackId": {
1155
+ "type": "string"
1156
+ },
1157
+ "body": {
1158
+ "type": "string"
1159
+ },
1160
+ "authorName": {
1161
+ "type": "string"
1162
+ },
1163
+ "authorEmail": {
1164
+ "type": "string"
1165
+ },
1166
+ "authorRole": {
1167
+ "type": "string",
1168
+ "enum": [
1169
+ "client",
1170
+ "team"
1171
+ ]
1172
+ },
1173
+ "createdAt": {
1174
+ "type": "string",
1175
+ "format": "date-time"
1176
+ },
1177
+ "permissions": {
1178
+ "$ref": "#/components/schemas/CommentPermissions"
1179
+ }
1180
+ },
1181
+ "required": [
1182
+ "id",
1183
+ "feedbackId",
1184
+ "body",
1185
+ "authorName",
1186
+ "authorEmail",
1187
+ "authorRole",
1188
+ "createdAt",
1189
+ "permissions"
1190
+ ]
1191
+ },
1192
+ "CommentPermissions": {
1193
+ "type": "object",
1194
+ "properties": {
1195
+ "canDelete": {
1196
+ "type": "boolean"
1197
+ }
1198
+ },
1199
+ "required": [
1200
+ "canDelete"
1201
+ ]
1202
+ },
1203
+ "ScreenshotRegion": {
1204
+ "type": "object",
1205
+ "properties": {
1206
+ "xPct": {
1207
+ "type": "number",
1208
+ "minimum": 0,
1209
+ "maximum": 1
1210
+ },
1211
+ "yPct": {
1212
+ "type": "number",
1213
+ "minimum": 0,
1214
+ "maximum": 1
1215
+ },
1216
+ "wPct": {
1217
+ "type": "number",
1218
+ "minimum": 0,
1219
+ "maximum": 1
1220
+ },
1221
+ "hPct": {
1222
+ "type": "number",
1223
+ "minimum": 0,
1224
+ "maximum": 1
1225
+ }
1226
+ },
1227
+ "required": [
1228
+ "xPct",
1229
+ "yPct",
1230
+ "wPct",
1231
+ "hPct"
1232
+ ],
1233
+ "additionalProperties": false
1234
+ },
1235
+ "Diagnostics": {
1236
+ "type": "object",
1237
+ "properties": {
1238
+ "console": {
1239
+ "type": "array",
1240
+ "items": {
1241
+ "type": "object",
1242
+ "properties": {
1243
+ "level": {
1244
+ "type": "string",
1245
+ "enum": [
1246
+ "log",
1247
+ "info",
1248
+ "warn",
1249
+ "error"
1250
+ ]
1251
+ },
1252
+ "timestamp": {
1253
+ "type": "string"
1254
+ },
1255
+ "message": {
1256
+ "type": "string"
1257
+ }
1258
+ },
1259
+ "required": [
1260
+ "level",
1261
+ "timestamp",
1262
+ "message"
1263
+ ]
1264
+ }
1265
+ },
1266
+ "network": {
1267
+ "type": "array",
1268
+ "items": {
1269
+ "type": "object",
1270
+ "properties": {
1271
+ "url": {
1272
+ "type": "string"
1273
+ },
1274
+ "method": {
1275
+ "type": "string"
1276
+ },
1277
+ "status": {
1278
+ "type": "number"
1279
+ },
1280
+ "durationMs": {
1281
+ "type": "number"
1282
+ },
1283
+ "timestamp": {
1284
+ "type": "string"
1285
+ }
1286
+ },
1287
+ "required": [
1288
+ "url",
1289
+ "method",
1290
+ "status",
1291
+ "durationMs",
1292
+ "timestamp"
1293
+ ]
1294
+ }
1295
+ }
1296
+ },
1297
+ "required": [
1298
+ "console",
1299
+ "network"
1300
+ ]
1301
+ },
1302
+ "FeedbackPermissions": {
1303
+ "type": "object",
1304
+ "properties": {
1305
+ "canChangeStatus": {
1306
+ "type": "boolean"
1307
+ },
1308
+ "canDelete": {
1309
+ "type": "boolean"
1310
+ },
1311
+ "canComment": {
1312
+ "type": "boolean"
1313
+ },
1314
+ "canDeleteComment": {
1315
+ "type": "boolean"
1316
+ }
1317
+ },
1318
+ "required": [
1319
+ "canChangeStatus",
1320
+ "canDelete",
1321
+ "canComment",
1322
+ "canDeleteComment"
1323
+ ]
1324
+ },
1325
+ "FeedbackList": {
1326
+ "type": "object",
1327
+ "properties": {
1328
+ "feedbacks": {
1329
+ "type": "array",
1330
+ "items": {
1331
+ "$ref": "#/components/schemas/Feedback"
1332
+ }
1333
+ },
1334
+ "total": {
1335
+ "type": "integer",
1336
+ "minimum": 0,
1337
+ "maximum": 9007199254740991
1338
+ },
1339
+ "protocol": {
1340
+ "type": "integer",
1341
+ "minimum": 1,
1342
+ "maximum": 9007199254740991
1343
+ },
1344
+ "capabilities": {
1345
+ "$ref": "#/components/schemas/Capabilities"
1346
+ },
1347
+ "permissions": {
1348
+ "$ref": "#/components/schemas/FeedbackListPermissions"
1349
+ }
1350
+ },
1351
+ "required": [
1352
+ "feedbacks",
1353
+ "total",
1354
+ "protocol",
1355
+ "capabilities",
1356
+ "permissions"
1357
+ ]
1358
+ },
1359
+ "Capabilities": {
1360
+ "type": "object",
1361
+ "properties": {
1362
+ "comments": {
1363
+ "type": "boolean"
1364
+ },
1365
+ "deleteComments": {
1366
+ "type": "boolean"
1367
+ }
1368
+ },
1369
+ "required": [
1370
+ "comments",
1371
+ "deleteComments"
1372
+ ]
1373
+ },
1374
+ "FeedbackListPermissions": {
1375
+ "type": "object",
1376
+ "properties": {
1377
+ "canDeleteAll": {
1378
+ "type": "boolean"
1379
+ }
1380
+ },
1381
+ "required": [
1382
+ "canDeleteAll"
1383
+ ]
1384
+ },
1385
+ "Error": {
1386
+ "type": "object",
1387
+ "properties": {
1388
+ "error": {
1389
+ "type": "string"
1390
+ }
1391
+ },
1392
+ "required": [
1393
+ "error"
1394
+ ]
1395
+ },
1396
+ "ValidationErrors": {
1397
+ "type": "object",
1398
+ "properties": {
1399
+ "errors": {
1400
+ "maxItems": 20,
1401
+ "type": "array",
1402
+ "items": {
1403
+ "type": "object",
1404
+ "properties": {
1405
+ "field": {
1406
+ "type": "string"
1407
+ },
1408
+ "message": {
1409
+ "type": "string"
1410
+ }
1411
+ },
1412
+ "required": [
1413
+ "field",
1414
+ "message"
1415
+ ]
1416
+ }
1417
+ }
1418
+ },
1419
+ "required": [
1420
+ "errors"
1421
+ ]
1422
+ },
1423
+ "Deleted": {
1424
+ "type": "object",
1425
+ "properties": {
1426
+ "deleted": {
1427
+ "type": "boolean",
1428
+ "const": true
1429
+ }
1430
+ },
1431
+ "required": [
1432
+ "deleted"
1433
+ ]
1434
+ }
1435
+ },
1436
+ "responses": {
1437
+ "BadRequest": {
1438
+ "description": "The body is not JSON, failed validation (the first issues, as `errors`), or holds too many annotations.",
1439
+ "content": {
1440
+ "application/json": {
1441
+ "schema": {
1442
+ "oneOf": [
1443
+ {
1444
+ "$ref": "#/components/schemas/Error"
1445
+ },
1446
+ {
1447
+ "$ref": "#/components/schemas/ValidationErrors"
1448
+ }
1449
+ ]
1450
+ }
1451
+ }
1452
+ }
1453
+ },
1454
+ "Unauthorized": {
1455
+ "description": "No credentials, or wrong ones, for a method the policy does not open.",
1456
+ "content": {
1457
+ "application/json": {
1458
+ "schema": {
1459
+ "$ref": "#/components/schemas/Error"
1460
+ }
1461
+ }
1462
+ }
1463
+ },
1464
+ "Forbidden": {
1465
+ "description": "The access policy refused the action, or, under `access`, a mutation came from an origin outside `allowedOrigins`.",
1466
+ "content": {
1467
+ "application/json": {
1468
+ "schema": {
1469
+ "$ref": "#/components/schemas/Error"
1470
+ }
1471
+ }
1472
+ }
1473
+ },
1474
+ "PayloadTooLarge": {
1475
+ "description": "The body is over `maxBodyBytes` (4 MiB by default).",
1476
+ "content": {
1477
+ "application/json": {
1478
+ "schema": {
1479
+ "$ref": "#/components/schemas/Error"
1480
+ }
1481
+ }
1482
+ }
1483
+ },
1484
+ "UnsupportedMediaType": {
1485
+ "description": "Under `access`, a mutation without `Content-Type: application/json`.",
1486
+ "content": {
1487
+ "application/json": {
1488
+ "schema": {
1489
+ "$ref": "#/components/schemas/Error"
1490
+ }
1491
+ }
1492
+ }
1493
+ },
1494
+ "ValueTooLong": {
1495
+ "description": "A value is longer than the store's column holds: not worth a retry.",
1496
+ "content": {
1497
+ "application/json": {
1498
+ "schema": {
1499
+ "$ref": "#/components/schemas/Error"
1500
+ }
1501
+ }
1502
+ }
1503
+ },
1504
+ "TooManyRequests": {
1505
+ "description": "Never sent by `@siteping/server`, which leaves rate limiting to the platform: a limiter in front of it may. Clients retry after `Retry-After`."
1506
+ },
1507
+ "InternalServerError": {
1508
+ "description": "An unexpected failure, logged; the message `describeError` returns, if any.",
1509
+ "content": {
1510
+ "application/json": {
1511
+ "schema": {
1512
+ "$ref": "#/components/schemas/Error"
1513
+ }
1514
+ }
1515
+ }
1516
+ }
1517
+ },
1518
+ "securitySchemes": {
1519
+ "apiKey": {
1520
+ "type": "http",
1521
+ "scheme": "bearer",
1522
+ "description": "The `apiKey` policy: `Authorization: Bearer <apiKey>`, required on every method `publicEndpoints` does not open. A custom `access` policy reads whatever credential it chooses (a session cookie, a JWT)."
1523
+ }
1524
+ }
1525
+ },
1526
+ "security": [
1527
+ {},
1528
+ {
1529
+ "apiKey": []
1530
+ }
1531
+ ]
1532
+ }