@internetarchive/elements 1.1.3-webdev-8840.0 → 1.1.3-webdev-9107.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.
- package/README.md +1 -1
- package/dist/src/elements/ia-radio-player/ia-radio-player.d.ts +2 -0
- package/dist/src/elements/ia-radio-player/ia-radio-player.d.ts.map +1 -1
- package/dist/src/elements/ia-radio-player/ia-radio-player.js +6 -1
- package/dist/src/elements/ia-radio-player/ia-radio-player.js.map +1 -1
- package/dist/src/elements/ia-reviews/ia-review-form.d.ts +7 -2
- package/dist/src/elements/ia-reviews/ia-review-form.d.ts.map +1 -1
- package/dist/src/elements/ia-reviews/ia-review-form.js +43 -11
- package/dist/src/elements/ia-reviews/ia-review-form.js.map +1 -1
- package/dist/src/elements/ia-reviews/ia-review.d.ts +1 -2
- package/dist/src/elements/ia-reviews/ia-review.d.ts.map +1 -1
- package/dist/src/elements/ia-reviews/ia-review.js +25 -13
- package/dist/src/elements/ia-reviews/ia-review.js.map +1 -1
- package/dist/src/elements/ia-reviews/ia-reviews.d.ts +4 -9
- package/dist/src/elements/ia-reviews/ia-reviews.d.ts.map +1 -1
- package/dist/src/elements/ia-reviews/ia-reviews.js +20 -16
- package/dist/src/elements/ia-reviews/ia-reviews.js.map +1 -1
- package/dist/src/elements/ia-reviews/mocks/mock-fetch-handler.d.ts +2 -17
- package/dist/src/elements/ia-reviews/mocks/mock-fetch-handler.d.ts.map +1 -1
- package/dist/src/elements/ia-reviews/mocks/mock-fetch-handler.js +2 -24
- package/dist/src/elements/ia-reviews/mocks/mock-fetch-handler.js.map +1 -1
- package/dist/src/elements/index.d.ts +0 -2
- package/dist/src/elements/index.d.ts.map +1 -1
- package/dist/src/elements/index.js +0 -2
- package/dist/src/elements/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/src/elements/ia-reviews/mocks/mock-review-service.d.ts +0 -12
- package/dist/src/elements/ia-reviews/mocks/mock-review-service.d.ts.map +0 -1
- package/dist/src/elements/ia-reviews/mocks/mock-review-service.js +0 -19
- package/dist/src/elements/ia-reviews/mocks/mock-review-service.js.map +0 -1
- package/dist/src/elements/ia-reviews/models.d.ts +0 -38
- package/dist/src/elements/ia-reviews/models.d.ts.map +0 -1
- package/dist/src/elements/ia-reviews/models.js +0 -2
- package/dist/src/elements/ia-reviews/models.js.map +0 -1
- package/dist/src/elements/ia-reviews/review-service.d.ts +0 -53
- package/dist/src/elements/ia-reviews/review-service.d.ts.map +0 -1
- package/dist/src/elements/ia-reviews/review-service.js +0 -97
- package/dist/src/elements/ia-reviews/review-service.js.map +0 -1
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/** A review the patron is submitting, before it's mapped onto wire field names */
|
|
2
|
-
export type ReviewSubmission = {
|
|
3
|
-
/** The IA item being reviewed */
|
|
4
|
-
identifier: string;
|
|
5
|
-
/** Review subject */
|
|
6
|
-
title: string;
|
|
7
|
-
/** Review body */
|
|
8
|
-
body: string;
|
|
9
|
-
/** Star rating, '0' for no rating */
|
|
10
|
-
stars?: string;
|
|
11
|
-
/** ReCaptcha response, when the form ran a challenge */
|
|
12
|
-
recaptchaToken?: string;
|
|
13
|
-
};
|
|
14
|
-
/** The review to remove, identified by its reviewer */
|
|
15
|
-
export type ReviewDeletion = {
|
|
16
|
-
/** The IA item the review belongs to */
|
|
17
|
-
identifier: string;
|
|
18
|
-
/** Reviewer screenname */
|
|
19
|
-
reviewer: string;
|
|
20
|
-
/** Reviewer itemname, used when the reviewer's account is gone */
|
|
21
|
-
reviewerItemname?: string;
|
|
22
|
-
};
|
|
23
|
-
/** The outcome of a review write or delete */
|
|
24
|
-
export type ReviewServiceResult = {
|
|
25
|
-
success: boolean;
|
|
26
|
-
/** A message to show the patron, present when success is false */
|
|
27
|
-
error?: string;
|
|
28
|
-
};
|
|
29
|
-
/**
|
|
30
|
-
* Handles the network side of reviews, so the components only render state.
|
|
31
|
-
*/
|
|
32
|
-
export interface ReviewServiceInterface {
|
|
33
|
-
/** Add or update the patron's own review of an item */
|
|
34
|
-
submitReview(submission: ReviewSubmission): Promise<ReviewServiceResult>;
|
|
35
|
-
/** Remove a review, which the backend allows only for review moderators */
|
|
36
|
-
deleteReview(deletion: ReviewDeletion): Promise<ReviewServiceResult>;
|
|
37
|
-
}
|
|
38
|
-
//# sourceMappingURL=models.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../../src/elements/ia-reviews/models.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IAEnB,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IAEd,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;IAEb,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,wDAAwD;IACxD,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,uDAAuD;AACvD,MAAM,MAAM,cAAc,GAAG;IAC3B,wCAAwC;IACxC,UAAU,EAAE,MAAM,CAAC;IAEnB,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;IAEjB,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,8CAA8C;AAC9C,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,OAAO,CAAC;IAEjB,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,uDAAuD;IACvD,YAAY,CAAC,UAAU,EAAE,gBAAgB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAEzE,2EAA2E;IAC3E,YAAY,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;CACtE"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../../../src/elements/ia-reviews/models.ts"],"names":[],"mappings":"","sourcesContent":["/** A review the patron is submitting, before it's mapped onto wire field names */\nexport type ReviewSubmission = {\n /** The IA item being reviewed */\n identifier: string;\n\n /** Review subject */\n title: string;\n\n /** Review body */\n body: string;\n\n /** Star rating, '0' for no rating */\n stars?: string;\n\n /** ReCaptcha response, when the form ran a challenge */\n recaptchaToken?: string;\n};\n\n/** The review to remove, identified by its reviewer */\nexport type ReviewDeletion = {\n /** The IA item the review belongs to */\n identifier: string;\n\n /** Reviewer screenname */\n reviewer: string;\n\n /** Reviewer itemname, used when the reviewer's account is gone */\n reviewerItemname?: string;\n};\n\n/** The outcome of a review write or delete */\nexport type ReviewServiceResult = {\n success: boolean;\n\n /** A message to show the patron, present when success is false */\n error?: string;\n};\n\n/**\n * Handles the network side of reviews, so the components only render state.\n */\nexport interface ReviewServiceInterface {\n /** Add or update the patron's own review of an item */\n submitReview(submission: ReviewSubmission): Promise<ReviewServiceResult>;\n\n /** Remove a review, which the backend allows only for review moderators */\n deleteReview(deletion: ReviewDeletion): Promise<ReviewServiceResult>;\n}\n"]}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import type { FetchHandlerInterface } from '@internetarchive/fetch-handler';
|
|
2
|
-
import type { ReviewDeletion, ReviewServiceInterface, ReviewServiceResult, ReviewSubmission } from './models.js';
|
|
3
|
-
export type ReviewServiceOptions = {
|
|
4
|
-
/**
|
|
5
|
-
* Handles retries and the CSRF token.
|
|
6
|
-
*
|
|
7
|
-
* Writing and deleting need a handler built with a `getCsrfToken` source, since that is what
|
|
8
|
-
* supplies the `X-CSRF-Token` header these endpoints require. The token resolves per request,
|
|
9
|
-
* so it doesn't have to be known when the service is constructed.
|
|
10
|
-
*/
|
|
11
|
-
fetchHandler?: FetchHandlerInterface;
|
|
12
|
-
/** Origin for the endpoints. Empty string keeps requests same-origin. */
|
|
13
|
-
baseHost?: string;
|
|
14
|
-
/** Path a review submission POSTs to */
|
|
15
|
-
submitPath?: string;
|
|
16
|
-
/** Path a review deletion goes to */
|
|
17
|
-
deletePath?: string;
|
|
18
|
-
/** Verb the delete endpoint expects */
|
|
19
|
-
deleteMethod?: 'POST' | 'DELETE';
|
|
20
|
-
};
|
|
21
|
-
/**
|
|
22
|
-
* Talks to the archive.org review endpoints.
|
|
23
|
-
*
|
|
24
|
-
* The CSRF token is left to the fetch handler: both operations opt into its automatic
|
|
25
|
-
* `X-CSRF-Token` header, which resolves the token per request.
|
|
26
|
-
*/
|
|
27
|
-
export declare class ReviewService implements ReviewServiceInterface {
|
|
28
|
-
private fetchHandler;
|
|
29
|
-
private baseHost;
|
|
30
|
-
private submitPath;
|
|
31
|
-
private deletePath;
|
|
32
|
-
private deleteMethod;
|
|
33
|
-
constructor(options?: ReviewServiceOptions);
|
|
34
|
-
/** @inheritdoc */
|
|
35
|
-
submitReview(submission: ReviewSubmission): Promise<ReviewServiceResult>;
|
|
36
|
-
/** @inheritdoc */
|
|
37
|
-
deleteReview(deletion: ReviewDeletion): Promise<ReviewServiceResult>;
|
|
38
|
-
/**
|
|
39
|
-
* Sends a credentialed request and reads the `{ success, error }` result out of it.
|
|
40
|
-
*/
|
|
41
|
-
private request;
|
|
42
|
-
/**
|
|
43
|
-
* Reads a result out of a response.
|
|
44
|
-
*
|
|
45
|
-
* The status decides the outcome, so a rejected request can't read as a success. fetch
|
|
46
|
-
* resolves on a 4xx or 5xx, so that check can't be left to the catch. A JSON body supplies
|
|
47
|
-
* the patron-facing message; the legacy delete endpoint answers with an HTML page instead,
|
|
48
|
-
* and there the status is all there is to go on.
|
|
49
|
-
*/
|
|
50
|
-
private parseResult;
|
|
51
|
-
private get genericError();
|
|
52
|
-
}
|
|
53
|
-
//# sourceMappingURL=review-service.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"review-service.d.ts","sourceRoot":"","sources":["../../../../src/elements/ia-reviews/review-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAE5E,OAAO,KAAK,EACV,cAAc,EACd,sBAAsB,EACtB,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,UAAU,CAAC;AAElB,MAAM,MAAM,oBAAoB,GAAG;IACjC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,qBAAqB,CAAC;IAErC,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,qCAAqC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,uCAAuC;IACvC,YAAY,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;CAClC,CAAC;AAMF;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,sBAAsB;IAC1D,OAAO,CAAC,YAAY,CAAwB;IAE5C,OAAO,CAAC,QAAQ,CAAS;IAEzB,OAAO,CAAC,UAAU,CAAS;IAE3B,OAAO,CAAC,UAAU,CAAS;IAE3B,OAAO,CAAC,YAAY,CAAoB;gBAE5B,OAAO,CAAC,EAAE,oBAAoB;IAQ1C,kBAAkB;IACZ,YAAY,CAChB,UAAU,EAAE,gBAAgB,GAC3B,OAAO,CAAC,mBAAmB,CAAC;IAoB/B,kBAAkB;IACZ,YAAY,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAe1E;;OAEG;YACW,OAAO;IAqBrB;;;;;;;OAOG;YACW,WAAW;IAiBzB,OAAO,KAAK,YAAY,GAEvB;CACF"}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { msg } from '@lit/localize';
|
|
2
|
-
import { IaFetchHandler } from '@internetarchive/fetch-handler';
|
|
3
|
-
/** The endpoints the legacy details page serves */
|
|
4
|
-
const LEGACY_SUBMIT_PATH = '/write-review.php';
|
|
5
|
-
const LEGACY_DELETE_PATH = '/edit-reviews.php';
|
|
6
|
-
/**
|
|
7
|
-
* Talks to the archive.org review endpoints.
|
|
8
|
-
*
|
|
9
|
-
* The CSRF token is left to the fetch handler: both operations opt into its automatic
|
|
10
|
-
* `X-CSRF-Token` header, which resolves the token per request.
|
|
11
|
-
*/
|
|
12
|
-
export class ReviewService {
|
|
13
|
-
constructor(options) {
|
|
14
|
-
this.fetchHandler = options?.fetchHandler ?? new IaFetchHandler();
|
|
15
|
-
this.baseHost = options?.baseHost ?? 'https://archive.org';
|
|
16
|
-
this.submitPath = options?.submitPath ?? LEGACY_SUBMIT_PATH;
|
|
17
|
-
this.deletePath = options?.deletePath ?? LEGACY_DELETE_PATH;
|
|
18
|
-
this.deleteMethod = options?.deleteMethod ?? 'POST';
|
|
19
|
-
}
|
|
20
|
-
/** @inheritdoc */
|
|
21
|
-
async submitReview(submission) {
|
|
22
|
-
const body = new URLSearchParams();
|
|
23
|
-
body.append('identifier', submission.identifier);
|
|
24
|
-
body.append('field_reviewtitle', submission.title);
|
|
25
|
-
body.append('field_reviewbody', submission.body);
|
|
26
|
-
body.append('field_stars', submission.stars ?? '0');
|
|
27
|
-
if (submission.recaptchaToken) {
|
|
28
|
-
body.append('g-recaptcha-response', submission.recaptchaToken);
|
|
29
|
-
}
|
|
30
|
-
// tells write-review.php the post came from the form rather than a direct visit
|
|
31
|
-
body.append('submitter', 'review-form');
|
|
32
|
-
return this.request(`${this.baseHost}${this.submitPath}`, {
|
|
33
|
-
method: 'POST',
|
|
34
|
-
body,
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
/** @inheritdoc */
|
|
38
|
-
async deleteReview(deletion) {
|
|
39
|
-
// Screennames can contain spaces and ampersands, so the parameters are
|
|
40
|
-
// encoded rather than interpolated straight into the query string.
|
|
41
|
-
const params = new URLSearchParams();
|
|
42
|
-
params.append('identifier', deletion.identifier);
|
|
43
|
-
params.append('deleteReviewer', deletion.reviewer);
|
|
44
|
-
if (deletion.reviewerItemname) {
|
|
45
|
-
params.append('deleteReviewerItemname', deletion.reviewerItemname);
|
|
46
|
-
}
|
|
47
|
-
const url = `${this.baseHost}${this.deletePath}?${params.toString()}`;
|
|
48
|
-
return this.request(url, { method: this.deleteMethod });
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Sends a credentialed request and reads the `{ success, error }` result out of it.
|
|
52
|
-
*/
|
|
53
|
-
async request(url, init) {
|
|
54
|
-
try {
|
|
55
|
-
const response = await this.fetchHandler.fetch(url, {
|
|
56
|
-
requestInit: {
|
|
57
|
-
method: init.method,
|
|
58
|
-
body: init.body,
|
|
59
|
-
credentials: 'include',
|
|
60
|
-
},
|
|
61
|
-
includeCsrfToken: true,
|
|
62
|
-
});
|
|
63
|
-
return await this.parseResult(response);
|
|
64
|
-
}
|
|
65
|
-
catch (e) {
|
|
66
|
-
console.error('Review request failed', e);
|
|
67
|
-
return { success: false, error: this.genericError };
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Reads a result out of a response.
|
|
72
|
-
*
|
|
73
|
-
* The status decides the outcome, so a rejected request can't read as a success. fetch
|
|
74
|
-
* resolves on a 4xx or 5xx, so that check can't be left to the catch. A JSON body supplies
|
|
75
|
-
* the patron-facing message; the legacy delete endpoint answers with an HTML page instead,
|
|
76
|
-
* and there the status is all there is to go on.
|
|
77
|
-
*/
|
|
78
|
-
async parseResult(response) {
|
|
79
|
-
let payload;
|
|
80
|
-
try {
|
|
81
|
-
payload = (await response.json());
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
payload = undefined;
|
|
85
|
-
}
|
|
86
|
-
if (!response.ok) {
|
|
87
|
-
return { success: false, error: payload?.error ?? this.genericError };
|
|
88
|
-
}
|
|
89
|
-
if (!payload || payload.success)
|
|
90
|
-
return { success: true };
|
|
91
|
-
return { success: false, error: payload.error ?? this.genericError };
|
|
92
|
-
}
|
|
93
|
-
get genericError() {
|
|
94
|
-
return msg('Sorry, something went wrong. Please try again later.');
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
//# sourceMappingURL=review-service.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"review-service.js","sourceRoot":"","sources":["../../../../src/elements/ia-reviews/review-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAiChE,mDAAmD;AACnD,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AAC/C,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,OAAO,aAAa;IAWxB,YAAY,OAA8B;QACxC,IAAI,CAAC,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,IAAI,cAAc,EAAE,CAAC;QAClE,IAAI,CAAC,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,qBAAqB,CAAC;QAC3D,IAAI,CAAC,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,kBAAkB,CAAC;QAC5D,IAAI,CAAC,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,kBAAkB,CAAC;QAC5D,IAAI,CAAC,YAAY,GAAG,OAAO,EAAE,YAAY,IAAI,MAAM,CAAC;IACtD,CAAC;IAED,kBAAkB;IAClB,KAAK,CAAC,YAAY,CAChB,UAA4B;QAE5B,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;QACnC,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;QACnD,IAAI,CAAC,MAAM,CAAC,kBAAkB,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,UAAU,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC;QAEpD,IAAI,UAAU,CAAC,cAAc,EAAE,CAAC;YAC9B,IAAI,CAAC,MAAM,CAAC,sBAAsB,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC;QACjE,CAAC;QAED,gFAAgF;QAChF,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;QAExC,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,EAAE;YACxD,MAAM,EAAE,MAAM;YACd,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IAED,kBAAkB;IAClB,KAAK,CAAC,YAAY,CAAC,QAAwB;QACzC,uEAAuE;QACvE,mEAAmE;QACnE,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,gBAAgB,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACnD,IAAI,QAAQ,CAAC,gBAAgB,EAAE,CAAC;YAC9B,MAAM,CAAC,MAAM,CAAC,wBAAwB,EAAE,QAAQ,CAAC,gBAAgB,CAAC,CAAC;QACrE,CAAC;QAED,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;QAEtE,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,OAAO,CACnB,GAAW,EACX,IAAyC;QAEzC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,EAAE;gBAClD,WAAW,EAAE;oBACX,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,WAAW,EAAE,SAAS;iBACvB;gBACD,gBAAgB,EAAE,IAAI;aACvB,CAAC,CAAC;YAEH,OAAO,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,CAAC,CAAC,CAAC;YAC1C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC;QACtD,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACK,KAAK,CAAC,WAAW,CAAC,QAAkB;QAC1C,IAAI,OAAwC,CAAC;QAC7C,IAAI,CAAC;YACH,OAAO,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAwB,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,GAAG,SAAS,CAAC;QACtB,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;QACxE,CAAC;QAED,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO;YAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAE1D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;IACvE,CAAC;IAED,IAAY,YAAY;QACtB,OAAO,GAAG,CAAC,sDAAsD,CAAC,CAAC;IACrE,CAAC;CACF","sourcesContent":["import { msg } from '@lit/localize';\nimport { IaFetchHandler } from '@internetarchive/fetch-handler';\nimport type { FetchHandlerInterface } from '@internetarchive/fetch-handler';\n\nimport type {\n ReviewDeletion,\n ReviewServiceInterface,\n ReviewServiceResult,\n ReviewSubmission,\n} from './models';\n\nexport type ReviewServiceOptions = {\n /**\n * Handles retries and the CSRF token.\n *\n * Writing and deleting need a handler built with a `getCsrfToken` source, since that is what\n * supplies the `X-CSRF-Token` header these endpoints require. The token resolves per request,\n * so it doesn't have to be known when the service is constructed.\n */\n fetchHandler?: FetchHandlerInterface;\n\n /** Origin for the endpoints. Empty string keeps requests same-origin. */\n baseHost?: string;\n\n /** Path a review submission POSTs to */\n submitPath?: string;\n\n /** Path a review deletion goes to */\n deletePath?: string;\n\n /** Verb the delete endpoint expects */\n deleteMethod?: 'POST' | 'DELETE';\n};\n\n/** The endpoints the legacy details page serves */\nconst LEGACY_SUBMIT_PATH = '/write-review.php';\nconst LEGACY_DELETE_PATH = '/edit-reviews.php';\n\n/**\n * Talks to the archive.org review endpoints.\n *\n * The CSRF token is left to the fetch handler: both operations opt into its automatic\n * `X-CSRF-Token` header, which resolves the token per request.\n */\nexport class ReviewService implements ReviewServiceInterface {\n private fetchHandler: FetchHandlerInterface;\n\n private baseHost: string;\n\n private submitPath: string;\n\n private deletePath: string;\n\n private deleteMethod: 'POST' | 'DELETE';\n\n constructor(options?: ReviewServiceOptions) {\n this.fetchHandler = options?.fetchHandler ?? new IaFetchHandler();\n this.baseHost = options?.baseHost ?? 'https://archive.org';\n this.submitPath = options?.submitPath ?? LEGACY_SUBMIT_PATH;\n this.deletePath = options?.deletePath ?? LEGACY_DELETE_PATH;\n this.deleteMethod = options?.deleteMethod ?? 'POST';\n }\n\n /** @inheritdoc */\n async submitReview(\n submission: ReviewSubmission,\n ): Promise<ReviewServiceResult> {\n const body = new URLSearchParams();\n body.append('identifier', submission.identifier);\n body.append('field_reviewtitle', submission.title);\n body.append('field_reviewbody', submission.body);\n body.append('field_stars', submission.stars ?? '0');\n\n if (submission.recaptchaToken) {\n body.append('g-recaptcha-response', submission.recaptchaToken);\n }\n\n // tells write-review.php the post came from the form rather than a direct visit\n body.append('submitter', 'review-form');\n\n return this.request(`${this.baseHost}${this.submitPath}`, {\n method: 'POST',\n body,\n });\n }\n\n /** @inheritdoc */\n async deleteReview(deletion: ReviewDeletion): Promise<ReviewServiceResult> {\n // Screennames can contain spaces and ampersands, so the parameters are\n // encoded rather than interpolated straight into the query string.\n const params = new URLSearchParams();\n params.append('identifier', deletion.identifier);\n params.append('deleteReviewer', deletion.reviewer);\n if (deletion.reviewerItemname) {\n params.append('deleteReviewerItemname', deletion.reviewerItemname);\n }\n\n const url = `${this.baseHost}${this.deletePath}?${params.toString()}`;\n\n return this.request(url, { method: this.deleteMethod });\n }\n\n /**\n * Sends a credentialed request and reads the `{ success, error }` result out of it.\n */\n private async request(\n url: string,\n init: { method: string; body?: BodyInit },\n ): Promise<ReviewServiceResult> {\n try {\n const response = await this.fetchHandler.fetch(url, {\n requestInit: {\n method: init.method,\n body: init.body,\n credentials: 'include',\n },\n includeCsrfToken: true,\n });\n\n return await this.parseResult(response);\n } catch (e) {\n console.error('Review request failed', e);\n return { success: false, error: this.genericError };\n }\n }\n\n /**\n * Reads a result out of a response.\n *\n * The status decides the outcome, so a rejected request can't read as a success. fetch\n * resolves on a 4xx or 5xx, so that check can't be left to the catch. A JSON body supplies\n * the patron-facing message; the legacy delete endpoint answers with an HTML page instead,\n * and there the status is all there is to go on.\n */\n private async parseResult(response: Response): Promise<ReviewServiceResult> {\n let payload: ReviewServiceResult | undefined;\n try {\n payload = (await response.json()) as ReviewServiceResult;\n } catch {\n payload = undefined;\n }\n\n if (!response.ok) {\n return { success: false, error: payload?.error ?? this.genericError };\n }\n\n if (!payload || payload.success) return { success: true };\n\n return { success: false, error: payload.error ?? this.genericError };\n }\n\n private get genericError(): string {\n return msg('Sorry, something went wrong. Please try again later.');\n }\n}\n"]}
|