ani-client 1.1.0 → 1.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.
- package/README.md +215 -0
- package/dist/index.d.mts +275 -1
- package/dist/index.d.ts +275 -1
- package/dist/index.js +390 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +387 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -31,6 +31,20 @@ var MEDIA_FIELDS = `
|
|
|
31
31
|
trending
|
|
32
32
|
tags { id name description category rank isMediaSpoiler }
|
|
33
33
|
studios { nodes { id name isAnimationStudio siteUrl } }
|
|
34
|
+
relations {
|
|
35
|
+
edges {
|
|
36
|
+
relationType(version: 2)
|
|
37
|
+
node {
|
|
38
|
+
id
|
|
39
|
+
title { romaji english native userPreferred }
|
|
40
|
+
type
|
|
41
|
+
format
|
|
42
|
+
status
|
|
43
|
+
coverImage { large medium }
|
|
44
|
+
siteUrl
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
34
48
|
isAdult
|
|
35
49
|
siteUrl
|
|
36
50
|
`;
|
|
@@ -213,6 +227,124 @@ query ($type: MediaType, $sort: [MediaSort], $page: Int, $perPage: Int) {
|
|
|
213
227
|
}
|
|
214
228
|
}
|
|
215
229
|
}`;
|
|
230
|
+
var QUERY_MEDIA_BY_SEASON = `
|
|
231
|
+
query ($season: MediaSeason!, $seasonYear: Int!, $type: MediaType, $sort: [MediaSort], $page: Int, $perPage: Int) {
|
|
232
|
+
Page(page: $page, perPage: $perPage) {
|
|
233
|
+
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
234
|
+
media(season: $season, seasonYear: $seasonYear, type: $type, sort: $sort) {
|
|
235
|
+
${MEDIA_FIELDS}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}`;
|
|
239
|
+
var MEDIA_LIST_FIELDS = `
|
|
240
|
+
id
|
|
241
|
+
mediaId
|
|
242
|
+
status
|
|
243
|
+
score(format: POINT_100)
|
|
244
|
+
progress
|
|
245
|
+
progressVolumes
|
|
246
|
+
repeat
|
|
247
|
+
priority
|
|
248
|
+
private
|
|
249
|
+
notes
|
|
250
|
+
startedAt { year month day }
|
|
251
|
+
completedAt { year month day }
|
|
252
|
+
updatedAt
|
|
253
|
+
createdAt
|
|
254
|
+
media {
|
|
255
|
+
${MEDIA_FIELDS}
|
|
256
|
+
}
|
|
257
|
+
`;
|
|
258
|
+
var QUERY_RECOMMENDATIONS = `
|
|
259
|
+
query ($mediaId: Int!, $page: Int, $perPage: Int, $sort: [RecommendationSort]) {
|
|
260
|
+
Media(id: $mediaId) {
|
|
261
|
+
recommendations(page: $page, perPage: $perPage, sort: $sort) {
|
|
262
|
+
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
263
|
+
nodes {
|
|
264
|
+
id
|
|
265
|
+
rating
|
|
266
|
+
userRating
|
|
267
|
+
mediaRecommendation {
|
|
268
|
+
id
|
|
269
|
+
idMal
|
|
270
|
+
title { romaji english native userPreferred }
|
|
271
|
+
type
|
|
272
|
+
format
|
|
273
|
+
status
|
|
274
|
+
coverImage { extraLarge large medium color }
|
|
275
|
+
bannerImage
|
|
276
|
+
genres
|
|
277
|
+
averageScore
|
|
278
|
+
meanScore
|
|
279
|
+
popularity
|
|
280
|
+
favourites
|
|
281
|
+
siteUrl
|
|
282
|
+
}
|
|
283
|
+
user {
|
|
284
|
+
id
|
|
285
|
+
name
|
|
286
|
+
avatar { large medium }
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}`;
|
|
292
|
+
var QUERY_USER_MEDIA_LIST = `
|
|
293
|
+
query ($userId: Int, $userName: String, $type: MediaType!, $status: MediaListStatus, $sort: [MediaListSort], $page: Int, $perPage: Int) {
|
|
294
|
+
Page(page: $page, perPage: $perPage) {
|
|
295
|
+
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
296
|
+
mediaList(userId: $userId, userName: $userName, type: $type, status: $status, sort: $sort) {
|
|
297
|
+
${MEDIA_LIST_FIELDS}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}`;
|
|
301
|
+
var STUDIO_FIELDS = `
|
|
302
|
+
id
|
|
303
|
+
name
|
|
304
|
+
isAnimationStudio
|
|
305
|
+
siteUrl
|
|
306
|
+
favourites
|
|
307
|
+
media(page: 1, perPage: 25, sort: POPULARITY_DESC) {
|
|
308
|
+
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
309
|
+
nodes {
|
|
310
|
+
id
|
|
311
|
+
title { romaji english native userPreferred }
|
|
312
|
+
type
|
|
313
|
+
format
|
|
314
|
+
coverImage { large medium }
|
|
315
|
+
siteUrl
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
`;
|
|
319
|
+
var QUERY_STUDIO_BY_ID = `
|
|
320
|
+
query ($id: Int!) {
|
|
321
|
+
Studio(id: $id) {
|
|
322
|
+
${STUDIO_FIELDS}
|
|
323
|
+
}
|
|
324
|
+
}`;
|
|
325
|
+
var QUERY_STUDIO_SEARCH = `
|
|
326
|
+
query ($search: String, $page: Int, $perPage: Int) {
|
|
327
|
+
Page(page: $page, perPage: $perPage) {
|
|
328
|
+
pageInfo { total perPage currentPage lastPage hasNextPage }
|
|
329
|
+
studios(search: $search) {
|
|
330
|
+
${STUDIO_FIELDS}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}`;
|
|
334
|
+
var QUERY_GENRES = `
|
|
335
|
+
query {
|
|
336
|
+
GenreCollection
|
|
337
|
+
}`;
|
|
338
|
+
var QUERY_TAGS = `
|
|
339
|
+
query {
|
|
340
|
+
MediaTagCollection {
|
|
341
|
+
id
|
|
342
|
+
name
|
|
343
|
+
description
|
|
344
|
+
category
|
|
345
|
+
isAdult
|
|
346
|
+
}
|
|
347
|
+
}`;
|
|
216
348
|
|
|
217
349
|
// src/errors/index.ts
|
|
218
350
|
var AniListError = class extends Error {
|
|
@@ -549,6 +681,195 @@ var AniListClient = class {
|
|
|
549
681
|
const data = await this.request(QUERY_PLANNING, variables);
|
|
550
682
|
return { pageInfo: data.Page.pageInfo, results: data.Page.media };
|
|
551
683
|
}
|
|
684
|
+
/**
|
|
685
|
+
* Get recommendations for a specific media.
|
|
686
|
+
*
|
|
687
|
+
* Returns other anime/manga that users have recommended based on the given media.
|
|
688
|
+
*
|
|
689
|
+
* @param mediaId - The AniList media ID
|
|
690
|
+
* @param options - Optional sort / pagination parameters
|
|
691
|
+
* @returns Paginated list of recommendations
|
|
692
|
+
*
|
|
693
|
+
* @example
|
|
694
|
+
* ```ts
|
|
695
|
+
* // Get recommendations for Cowboy Bebop
|
|
696
|
+
* const recs = await client.getRecommendations(1);
|
|
697
|
+
* recs.results.forEach((r) =>
|
|
698
|
+
* console.log(`${r.mediaRecommendation.title.romaji} (rating: ${r.rating})`)
|
|
699
|
+
* );
|
|
700
|
+
* ```
|
|
701
|
+
*/
|
|
702
|
+
async getRecommendations(mediaId, options = {}) {
|
|
703
|
+
const variables = {
|
|
704
|
+
mediaId,
|
|
705
|
+
sort: options.sort ?? ["RATING_DESC"],
|
|
706
|
+
page: options.page ?? 1,
|
|
707
|
+
perPage: options.perPage ?? 20
|
|
708
|
+
};
|
|
709
|
+
const data = await this.request(QUERY_RECOMMENDATIONS, variables);
|
|
710
|
+
return {
|
|
711
|
+
pageInfo: data.Media.recommendations.pageInfo,
|
|
712
|
+
results: data.Media.recommendations.nodes
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Get anime (or manga) for a specific season and year.
|
|
717
|
+
*
|
|
718
|
+
* @param options - Season, year and optional filter / pagination parameters
|
|
719
|
+
* @returns Paginated list of media for the given season
|
|
720
|
+
*
|
|
721
|
+
* @example
|
|
722
|
+
* ```ts
|
|
723
|
+
* import { MediaSeason } from "ani-client";
|
|
724
|
+
*
|
|
725
|
+
* const winter2026 = await client.getMediaBySeason({
|
|
726
|
+
* season: MediaSeason.WINTER,
|
|
727
|
+
* seasonYear: 2026,
|
|
728
|
+
* perPage: 10,
|
|
729
|
+
* });
|
|
730
|
+
* ```
|
|
731
|
+
*/
|
|
732
|
+
async getMediaBySeason(options) {
|
|
733
|
+
const variables = {
|
|
734
|
+
season: options.season,
|
|
735
|
+
seasonYear: options.seasonYear,
|
|
736
|
+
type: options.type ?? "ANIME",
|
|
737
|
+
sort: options.sort ?? ["POPULARITY_DESC"],
|
|
738
|
+
page: options.page ?? 1,
|
|
739
|
+
perPage: options.perPage ?? 20
|
|
740
|
+
};
|
|
741
|
+
const data = await this.request(QUERY_MEDIA_BY_SEASON, variables);
|
|
742
|
+
return { pageInfo: data.Page.pageInfo, results: data.Page.media };
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Get a user's anime or manga list.
|
|
746
|
+
*
|
|
747
|
+
* Provide either `userId` or `userName` to identify the user.
|
|
748
|
+
* Requires `type` (ANIME or MANGA). Optionally filter by list status.
|
|
749
|
+
*
|
|
750
|
+
* @param options - User identifier, media type, and optional filters
|
|
751
|
+
* @returns Paginated list of media list entries
|
|
752
|
+
*
|
|
753
|
+
* @example
|
|
754
|
+
* ```ts
|
|
755
|
+
* import { MediaType, MediaListStatus } from "ani-client";
|
|
756
|
+
*
|
|
757
|
+
* // Get a user's completed anime list
|
|
758
|
+
* const list = await client.getUserMediaList({
|
|
759
|
+
* userName: "AniList",
|
|
760
|
+
* type: MediaType.ANIME,
|
|
761
|
+
* status: MediaListStatus.COMPLETED,
|
|
762
|
+
* });
|
|
763
|
+
* list.results.forEach((entry) =>
|
|
764
|
+
* console.log(`${entry.media.title.romaji} — ${entry.score}/100`)
|
|
765
|
+
* );
|
|
766
|
+
* ```
|
|
767
|
+
*/
|
|
768
|
+
async getUserMediaList(options) {
|
|
769
|
+
if (!options.userId && !options.userName) {
|
|
770
|
+
throw new Error("Either userId or userName must be provided");
|
|
771
|
+
}
|
|
772
|
+
const variables = {
|
|
773
|
+
userId: options.userId,
|
|
774
|
+
userName: options.userName,
|
|
775
|
+
type: options.type,
|
|
776
|
+
status: options.status,
|
|
777
|
+
sort: options.sort,
|
|
778
|
+
page: options.page ?? 1,
|
|
779
|
+
perPage: options.perPage ?? 20
|
|
780
|
+
};
|
|
781
|
+
const data = await this.request(QUERY_USER_MEDIA_LIST, variables);
|
|
782
|
+
return { pageInfo: data.Page.pageInfo, results: data.Page.mediaList };
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Fetch a studio by its AniList ID.
|
|
786
|
+
*
|
|
787
|
+
* Returns studio details along with its most popular productions.
|
|
788
|
+
*
|
|
789
|
+
* @param id - The AniList studio ID
|
|
790
|
+
*/
|
|
791
|
+
async getStudio(id) {
|
|
792
|
+
const data = await this.request(QUERY_STUDIO_BY_ID, { id });
|
|
793
|
+
return data.Studio;
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Search for studios by name.
|
|
797
|
+
*
|
|
798
|
+
* @param options - Search / pagination parameters
|
|
799
|
+
* @returns Paginated list of studios
|
|
800
|
+
*
|
|
801
|
+
* @example
|
|
802
|
+
* ```ts
|
|
803
|
+
* const studios = await client.searchStudios({ query: "MAPPA" });
|
|
804
|
+
* ```
|
|
805
|
+
*/
|
|
806
|
+
async searchStudios(options = {}) {
|
|
807
|
+
const variables = {
|
|
808
|
+
search: options.query,
|
|
809
|
+
page: options.page ?? 1,
|
|
810
|
+
perPage: options.perPage ?? 20
|
|
811
|
+
};
|
|
812
|
+
const data = await this.request(QUERY_STUDIO_SEARCH, variables);
|
|
813
|
+
return { pageInfo: data.Page.pageInfo, results: data.Page.studios };
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Get all available genres on AniList.
|
|
817
|
+
*
|
|
818
|
+
* @returns Array of genre strings (e.g. "Action", "Adventure", ...)
|
|
819
|
+
*/
|
|
820
|
+
async getGenres() {
|
|
821
|
+
const data = await this.request(QUERY_GENRES);
|
|
822
|
+
return data.GenreCollection;
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Get all available media tags on AniList.
|
|
826
|
+
*
|
|
827
|
+
* @returns Array of tag objects with id, name, description, category, isAdult
|
|
828
|
+
*/
|
|
829
|
+
async getTags() {
|
|
830
|
+
const data = await this.request(QUERY_TAGS);
|
|
831
|
+
return data.MediaTagCollection;
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Auto-paginating async iterator.
|
|
835
|
+
*
|
|
836
|
+
* Wraps any paginated method and yields individual items across all pages.
|
|
837
|
+
* Stops when `hasNextPage` is `false` or `maxPages` is reached.
|
|
838
|
+
*
|
|
839
|
+
* @param fetchPage - A function that takes a page number and returns a `PagedResult<T>`
|
|
840
|
+
* @param maxPages - Maximum number of pages to fetch (default: Infinity)
|
|
841
|
+
* @returns An async iterable iterator of individual items
|
|
842
|
+
*
|
|
843
|
+
* @example
|
|
844
|
+
* ```ts
|
|
845
|
+
* // Iterate over all search results
|
|
846
|
+
* for await (const anime of client.paginate((page) =>
|
|
847
|
+
* client.searchMedia({ query: "Naruto", page, perPage: 10 })
|
|
848
|
+
* )) {
|
|
849
|
+
* console.log(anime.title.romaji);
|
|
850
|
+
* }
|
|
851
|
+
*
|
|
852
|
+
* // Limit to 3 pages
|
|
853
|
+
* for await (const anime of client.paginate(
|
|
854
|
+
* (page) => client.getTrending(MediaType.ANIME, page, 20),
|
|
855
|
+
* 3,
|
|
856
|
+
* )) {
|
|
857
|
+
* console.log(anime.title.romaji);
|
|
858
|
+
* }
|
|
859
|
+
* ```
|
|
860
|
+
*/
|
|
861
|
+
async *paginate(fetchPage, maxPages = Infinity) {
|
|
862
|
+
let page = 1;
|
|
863
|
+
let hasNext = true;
|
|
864
|
+
while (hasNext && page <= maxPages) {
|
|
865
|
+
const result = await fetchPage(page);
|
|
866
|
+
for (const item of result.results) {
|
|
867
|
+
yield item;
|
|
868
|
+
}
|
|
869
|
+
hasNext = result.pageInfo.hasNextPage === true;
|
|
870
|
+
page++;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
552
873
|
/**
|
|
553
874
|
* Clear the entire response cache.
|
|
554
875
|
*/
|
|
@@ -635,7 +956,72 @@ var CharacterSort = /* @__PURE__ */ ((CharacterSort2) => {
|
|
|
635
956
|
CharacterSort2["FAVOURITES"] = "FAVOURITES";
|
|
636
957
|
return CharacterSort2;
|
|
637
958
|
})(CharacterSort || {});
|
|
959
|
+
var MediaRelationType = /* @__PURE__ */ ((MediaRelationType2) => {
|
|
960
|
+
MediaRelationType2["ADAPTATION"] = "ADAPTATION";
|
|
961
|
+
MediaRelationType2["PREQUEL"] = "PREQUEL";
|
|
962
|
+
MediaRelationType2["SEQUEL"] = "SEQUEL";
|
|
963
|
+
MediaRelationType2["PARENT"] = "PARENT";
|
|
964
|
+
MediaRelationType2["SIDE_STORY"] = "SIDE_STORY";
|
|
965
|
+
MediaRelationType2["CHARACTER"] = "CHARACTER";
|
|
966
|
+
MediaRelationType2["SUMMARY"] = "SUMMARY";
|
|
967
|
+
MediaRelationType2["ALTERNATIVE"] = "ALTERNATIVE";
|
|
968
|
+
MediaRelationType2["SPIN_OFF"] = "SPIN_OFF";
|
|
969
|
+
MediaRelationType2["OTHER"] = "OTHER";
|
|
970
|
+
MediaRelationType2["SOURCE"] = "SOURCE";
|
|
971
|
+
MediaRelationType2["COMPILATION"] = "COMPILATION";
|
|
972
|
+
MediaRelationType2["CONTAINS"] = "CONTAINS";
|
|
973
|
+
return MediaRelationType2;
|
|
974
|
+
})(MediaRelationType || {});
|
|
975
|
+
var RecommendationSort = /* @__PURE__ */ ((RecommendationSort2) => {
|
|
976
|
+
RecommendationSort2["ID"] = "ID";
|
|
977
|
+
RecommendationSort2["ID_DESC"] = "ID_DESC";
|
|
978
|
+
RecommendationSort2["RATING"] = "RATING";
|
|
979
|
+
RecommendationSort2["RATING_DESC"] = "RATING_DESC";
|
|
980
|
+
return RecommendationSort2;
|
|
981
|
+
})(RecommendationSort || {});
|
|
982
|
+
var MediaListStatus = /* @__PURE__ */ ((MediaListStatus2) => {
|
|
983
|
+
MediaListStatus2["CURRENT"] = "CURRENT";
|
|
984
|
+
MediaListStatus2["PLANNING"] = "PLANNING";
|
|
985
|
+
MediaListStatus2["COMPLETED"] = "COMPLETED";
|
|
986
|
+
MediaListStatus2["DROPPED"] = "DROPPED";
|
|
987
|
+
MediaListStatus2["PAUSED"] = "PAUSED";
|
|
988
|
+
MediaListStatus2["REPEATING"] = "REPEATING";
|
|
989
|
+
return MediaListStatus2;
|
|
990
|
+
})(MediaListStatus || {});
|
|
991
|
+
var MediaListSort = /* @__PURE__ */ ((MediaListSort2) => {
|
|
992
|
+
MediaListSort2["MEDIA_ID"] = "MEDIA_ID";
|
|
993
|
+
MediaListSort2["MEDIA_ID_DESC"] = "MEDIA_ID_DESC";
|
|
994
|
+
MediaListSort2["SCORE"] = "SCORE";
|
|
995
|
+
MediaListSort2["SCORE_DESC"] = "SCORE_DESC";
|
|
996
|
+
MediaListSort2["STATUS"] = "STATUS";
|
|
997
|
+
MediaListSort2["STATUS_DESC"] = "STATUS_DESC";
|
|
998
|
+
MediaListSort2["PROGRESS"] = "PROGRESS";
|
|
999
|
+
MediaListSort2["PROGRESS_DESC"] = "PROGRESS_DESC";
|
|
1000
|
+
MediaListSort2["PROGRESS_VOLUMES"] = "PROGRESS_VOLUMES";
|
|
1001
|
+
MediaListSort2["PROGRESS_VOLUMES_DESC"] = "PROGRESS_VOLUMES_DESC";
|
|
1002
|
+
MediaListSort2["REPEAT"] = "REPEAT";
|
|
1003
|
+
MediaListSort2["REPEAT_DESC"] = "REPEAT_DESC";
|
|
1004
|
+
MediaListSort2["PRIORITY"] = "PRIORITY";
|
|
1005
|
+
MediaListSort2["PRIORITY_DESC"] = "PRIORITY_DESC";
|
|
1006
|
+
MediaListSort2["STARTED_ON"] = "STARTED_ON";
|
|
1007
|
+
MediaListSort2["STARTED_ON_DESC"] = "STARTED_ON_DESC";
|
|
1008
|
+
MediaListSort2["FINISHED_ON"] = "FINISHED_ON";
|
|
1009
|
+
MediaListSort2["FINISHED_ON_DESC"] = "FINISHED_ON_DESC";
|
|
1010
|
+
MediaListSort2["ADDED_TIME"] = "ADDED_TIME";
|
|
1011
|
+
MediaListSort2["ADDED_TIME_DESC"] = "ADDED_TIME_DESC";
|
|
1012
|
+
MediaListSort2["UPDATED_TIME"] = "UPDATED_TIME";
|
|
1013
|
+
MediaListSort2["UPDATED_TIME_DESC"] = "UPDATED_TIME_DESC";
|
|
1014
|
+
MediaListSort2["MEDIA_TITLE_ROMAJI"] = "MEDIA_TITLE_ROMAJI";
|
|
1015
|
+
MediaListSort2["MEDIA_TITLE_ROMAJI_DESC"] = "MEDIA_TITLE_ROMAJI_DESC";
|
|
1016
|
+
MediaListSort2["MEDIA_TITLE_ENGLISH"] = "MEDIA_TITLE_ENGLISH";
|
|
1017
|
+
MediaListSort2["MEDIA_TITLE_ENGLISH_DESC"] = "MEDIA_TITLE_ENGLISH_DESC";
|
|
1018
|
+
MediaListSort2["MEDIA_TITLE_NATIVE"] = "MEDIA_TITLE_NATIVE";
|
|
1019
|
+
MediaListSort2["MEDIA_TITLE_NATIVE_DESC"] = "MEDIA_TITLE_NATIVE_DESC";
|
|
1020
|
+
MediaListSort2["MEDIA_POPULARITY"] = "MEDIA_POPULARITY";
|
|
1021
|
+
MediaListSort2["MEDIA_POPULARITY_DESC"] = "MEDIA_POPULARITY_DESC";
|
|
1022
|
+
return MediaListSort2;
|
|
1023
|
+
})(MediaListSort || {});
|
|
638
1024
|
|
|
639
|
-
export { AiringSort, AniListClient, AniListError, CharacterSort, MediaFormat, MediaSeason, MediaSort, MediaStatus, MediaType, MemoryCache, RateLimiter };
|
|
1025
|
+
export { AiringSort, AniListClient, AniListError, CharacterSort, MediaFormat, MediaListSort, MediaListStatus, MediaRelationType, MediaSeason, MediaSort, MediaStatus, MediaType, MemoryCache, RateLimiter, RecommendationSort };
|
|
640
1026
|
//# sourceMappingURL=index.mjs.map
|
|
641
1027
|
//# sourceMappingURL=index.mjs.map
|