@reactionary/lipscore 0.9.22
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/capabilities/index.js +1 -0
- package/capabilities/product-reviews.capability.js +105 -0
- package/core/client.js +38 -0
- package/core/initialize.js +51 -0
- package/core/initialize.types.js +8 -0
- package/factories/index.js +1 -0
- package/factories/product-reviews/product-reviews.factory.js +79 -0
- package/index.js +7 -0
- package/package.json +16 -0
- package/schema/capabilities.schema.js +15 -0
- package/schema/configuration.schema.js +11 -0
- package/schema/lipscore.schema.js +37 -0
- package/src/capabilities/index.d.ts +1 -0
- package/src/capabilities/product-reviews.capability.d.ts +16 -0
- package/src/core/client.d.ts +13 -0
- package/src/core/initialize.d.ts +5 -0
- package/src/core/initialize.types.d.ts +34 -0
- package/src/factories/index.d.ts +1 -0
- package/src/factories/product-reviews/product-reviews.factory.d.ts +18 -0
- package/src/index.d.ts +7 -0
- package/src/schema/capabilities.schema.d.ts +26 -0
- package/src/schema/configuration.schema.d.ts +7 -0
- package/src/schema/lipscore.schema.d.ts +82 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./product-reviews.capability.js";
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result)
|
|
9
|
+
__defProp(target, key, result);
|
|
10
|
+
return result;
|
|
11
|
+
};
|
|
12
|
+
import {
|
|
13
|
+
ProductReviewPaginatedResultSchema,
|
|
14
|
+
ProductRatingSummarySchema,
|
|
15
|
+
ProductReviewSchema,
|
|
16
|
+
ProductReviewsCapability,
|
|
17
|
+
Reactionary,
|
|
18
|
+
error,
|
|
19
|
+
success
|
|
20
|
+
} from "@reactionary/core";
|
|
21
|
+
import { LipscoreProductsResponseSchema } from "../schema/lipscore.schema.js";
|
|
22
|
+
class LipscoreProductReviewsCapability extends ProductReviewsCapability {
|
|
23
|
+
constructor(cache, context, config, client, factory) {
|
|
24
|
+
super(cache, context);
|
|
25
|
+
this.config = config;
|
|
26
|
+
this.client = client;
|
|
27
|
+
this.factory = factory;
|
|
28
|
+
}
|
|
29
|
+
async getRatingSummary(query) {
|
|
30
|
+
const response = await this.client.callGet(
|
|
31
|
+
this.getReviewsUrl(),
|
|
32
|
+
this.getRatingSummaryParams(query.product.key),
|
|
33
|
+
LipscoreProductsResponseSchema
|
|
34
|
+
);
|
|
35
|
+
return success(this.factory.parseRatingSummary(this.context, response));
|
|
36
|
+
}
|
|
37
|
+
async findReviews(query) {
|
|
38
|
+
const pageSize = query.paginationOptions?.pageSize ?? 10;
|
|
39
|
+
const pageNumber = query.paginationOptions?.pageNumber ?? 1;
|
|
40
|
+
const response = await this.client.callGet(
|
|
41
|
+
this.getReviewsUrl(),
|
|
42
|
+
this.getReviewsParams(query.product.key),
|
|
43
|
+
LipscoreProductsResponseSchema
|
|
44
|
+
);
|
|
45
|
+
return success(
|
|
46
|
+
this.factory.parseReviewPaginatedResult(this.context, {
|
|
47
|
+
response,
|
|
48
|
+
productKey: query.product.key,
|
|
49
|
+
pageSize,
|
|
50
|
+
pageNumber
|
|
51
|
+
})
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
async submitReview(mutation) {
|
|
55
|
+
return error({
|
|
56
|
+
type: "InvalidInput",
|
|
57
|
+
error: "Review submission is handled via the Lipscore widget and is not available through this API."
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Extension points — override in subclasses to customise API calls
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
getReviewsUrl() {
|
|
64
|
+
return `${this.client.baseUrl}/products`;
|
|
65
|
+
}
|
|
66
|
+
getReviewsParams(productId) {
|
|
67
|
+
const params = new URLSearchParams();
|
|
68
|
+
params.set("internal_id[]", productId);
|
|
69
|
+
params.set("fields", "reviews");
|
|
70
|
+
return params;
|
|
71
|
+
}
|
|
72
|
+
getRatingSummaryParams(productId) {
|
|
73
|
+
return this.getReviewsParams(productId);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
__decorateClass([
|
|
77
|
+
Reactionary({
|
|
78
|
+
outputSchema: ProductRatingSummarySchema,
|
|
79
|
+
cache: true,
|
|
80
|
+
cacheTimeToLiveInSeconds: 300,
|
|
81
|
+
currencyDependentCaching: false,
|
|
82
|
+
localeDependentCaching: false
|
|
83
|
+
})
|
|
84
|
+
], LipscoreProductReviewsCapability.prototype, "getRatingSummary", 1);
|
|
85
|
+
__decorateClass([
|
|
86
|
+
Reactionary({
|
|
87
|
+
outputSchema: ProductReviewPaginatedResultSchema,
|
|
88
|
+
cache: true,
|
|
89
|
+
cacheTimeToLiveInSeconds: 60,
|
|
90
|
+
currencyDependentCaching: false,
|
|
91
|
+
localeDependentCaching: false
|
|
92
|
+
})
|
|
93
|
+
], LipscoreProductReviewsCapability.prototype, "findReviews", 1);
|
|
94
|
+
__decorateClass([
|
|
95
|
+
Reactionary({
|
|
96
|
+
outputSchema: ProductReviewSchema,
|
|
97
|
+
cache: false,
|
|
98
|
+
cacheTimeToLiveInSeconds: 0,
|
|
99
|
+
currencyDependentCaching: false,
|
|
100
|
+
localeDependentCaching: false
|
|
101
|
+
})
|
|
102
|
+
], LipscoreProductReviewsCapability.prototype, "submitReview", 1);
|
|
103
|
+
export {
|
|
104
|
+
LipscoreProductReviewsCapability
|
|
105
|
+
};
|
package/core/client.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
class LipscoreClient {
|
|
2
|
+
constructor(config, context) {
|
|
3
|
+
this.config = config;
|
|
4
|
+
this.context = context;
|
|
5
|
+
this.baseUrl = config.apiUrl.replace(/\/+$/, "");
|
|
6
|
+
}
|
|
7
|
+
baseUrl;
|
|
8
|
+
async callGet(url, params, schema) {
|
|
9
|
+
const merged = this.buildParams(params);
|
|
10
|
+
const query = merged.toString() ? `?${merged.toString()}` : "";
|
|
11
|
+
const response = await fetch(`${url}${query}`, {
|
|
12
|
+
headers: this.buildHeaders()
|
|
13
|
+
});
|
|
14
|
+
if (!response.ok) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`Lipscore GET ${url} error ${response.status} ${response.statusText}`
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
const json = await response.json();
|
|
20
|
+
return schema.parse(json);
|
|
21
|
+
}
|
|
22
|
+
buildParams(params) {
|
|
23
|
+
const merged = new URLSearchParams(params);
|
|
24
|
+
if (!merged.has("api_key"))
|
|
25
|
+
merged.set("api_key", this.config.apiKey);
|
|
26
|
+
return merged;
|
|
27
|
+
}
|
|
28
|
+
buildHeaders() {
|
|
29
|
+
const headers = {};
|
|
30
|
+
if (this.config.apiSecret) {
|
|
31
|
+
headers["X-Authorization"] = this.config.apiSecret;
|
|
32
|
+
}
|
|
33
|
+
return headers;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
LipscoreClient
|
|
38
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ProductRatingSummarySchema,
|
|
3
|
+
ProductReviewPaginatedResultSchema,
|
|
4
|
+
ProductReviewSchema
|
|
5
|
+
} from "@reactionary/core";
|
|
6
|
+
import { LipscoreProductReviewsCapability } from "../capabilities/product-reviews.capability.js";
|
|
7
|
+
import { LipscoreProductReviewsFactory } from "../factories/product-reviews/product-reviews.factory.js";
|
|
8
|
+
import { LipscoreCapabilitiesSchema } from "../schema/capabilities.schema.js";
|
|
9
|
+
import { LipscoreConfigurationSchema } from "../schema/configuration.schema.js";
|
|
10
|
+
import { LipscoreClient } from "./client.js";
|
|
11
|
+
import {
|
|
12
|
+
resolveCapabilityWithFactory
|
|
13
|
+
} from "./initialize.types.js";
|
|
14
|
+
function withLipscoreCapabilities(configuration, capabilities) {
|
|
15
|
+
return (cache, context) => {
|
|
16
|
+
const client = {};
|
|
17
|
+
const config = LipscoreConfigurationSchema.parse(configuration);
|
|
18
|
+
const caps = LipscoreCapabilitiesSchema.parse(capabilities);
|
|
19
|
+
const lipscoreClient = new LipscoreClient(config, context);
|
|
20
|
+
if (caps.productReviews?.enabled) {
|
|
21
|
+
client.productReviews = resolveCapabilityWithFactory(
|
|
22
|
+
capabilities.productReviews,
|
|
23
|
+
{
|
|
24
|
+
factory: new LipscoreProductReviewsFactory(
|
|
25
|
+
ProductRatingSummarySchema,
|
|
26
|
+
ProductReviewSchema,
|
|
27
|
+
ProductReviewPaginatedResultSchema
|
|
28
|
+
),
|
|
29
|
+
capability: (args) => new LipscoreProductReviewsCapability(
|
|
30
|
+
args.cache,
|
|
31
|
+
args.context,
|
|
32
|
+
args.config,
|
|
33
|
+
args.client,
|
|
34
|
+
args.factory
|
|
35
|
+
)
|
|
36
|
+
},
|
|
37
|
+
(factory) => ({
|
|
38
|
+
cache,
|
|
39
|
+
context,
|
|
40
|
+
config,
|
|
41
|
+
client: lipscoreClient,
|
|
42
|
+
factory
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return client;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export {
|
|
50
|
+
withLipscoreCapabilities
|
|
51
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
function resolveCapabilityWithFactory(capability, defaults, buildCapabilityArgs) {
|
|
2
|
+
const factory = capability?.factory ?? defaults.factory;
|
|
3
|
+
const capabilityFactory = capability?.capability ?? defaults.capability;
|
|
4
|
+
return capabilityFactory(buildCapabilityArgs(factory));
|
|
5
|
+
}
|
|
6
|
+
export {
|
|
7
|
+
resolveCapabilityWithFactory
|
|
8
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./product-reviews/product-reviews.factory.js";
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
class LipscoreProductReviewsFactory {
|
|
2
|
+
constructor(ratingSummarySchema, reviewSchema, reviewPaginatedSchema) {
|
|
3
|
+
this.ratingSummarySchema = ratingSummarySchema;
|
|
4
|
+
this.reviewSchema = reviewSchema;
|
|
5
|
+
this.reviewPaginatedSchema = reviewPaginatedSchema;
|
|
6
|
+
}
|
|
7
|
+
parseRatingSummary(_context, data) {
|
|
8
|
+
const product = data[0];
|
|
9
|
+
const reviews = product?.reviews ?? [];
|
|
10
|
+
if (reviews.length === 0) {
|
|
11
|
+
return this.ratingSummarySchema.parse({
|
|
12
|
+
identifier: { product: { key: String(product?.internal_id ?? "") } },
|
|
13
|
+
averageRating: 0,
|
|
14
|
+
totalRatings: void 0,
|
|
15
|
+
ratingDistribution: void 0
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
const total = reviews.length;
|
|
19
|
+
const sum = reviews.reduce((acc, r) => acc + r.rating, 0);
|
|
20
|
+
const averageRating = Math.round(sum / total * 100) / 100;
|
|
21
|
+
const distribution = {
|
|
22
|
+
"1": 0,
|
|
23
|
+
"2": 0,
|
|
24
|
+
"3": 0,
|
|
25
|
+
"4": 0,
|
|
26
|
+
"5": 0
|
|
27
|
+
};
|
|
28
|
+
for (const review of reviews) {
|
|
29
|
+
const key = String(Math.min(5, Math.max(1, Math.round(review.rating))));
|
|
30
|
+
distribution[key] = (distribution[key] ?? 0) + 1;
|
|
31
|
+
}
|
|
32
|
+
return this.ratingSummarySchema.parse({
|
|
33
|
+
identifier: { product: { key: String(product?.internal_id ?? "") } },
|
|
34
|
+
averageRating,
|
|
35
|
+
totalRatings: total,
|
|
36
|
+
ratingDistribution: distribution
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
parseReview(_context, data) {
|
|
40
|
+
return this.reviewSchema.parse({
|
|
41
|
+
identifier: { key: String(data.id) },
|
|
42
|
+
product: { key: String(data.id) },
|
|
43
|
+
authorName: data.displayed_name ?? data.user?.name ?? "Anonymous",
|
|
44
|
+
authorId: data.user?.id != null ? String(data.user.id) : void 0,
|
|
45
|
+
rating: data.rating,
|
|
46
|
+
title: "",
|
|
47
|
+
content: data.text ?? data.translated_text ?? "",
|
|
48
|
+
reply: typeof data.review_reply === "string" ? data.review_reply : void 0,
|
|
49
|
+
repliedAt: void 0,
|
|
50
|
+
createdAt: data.created_at,
|
|
51
|
+
updatedAt: void 0,
|
|
52
|
+
verified: data.testimonial ?? false
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
parseReviewPaginatedResult(context, data) {
|
|
56
|
+
const product = data.response[0];
|
|
57
|
+
const allReviews = product?.reviews ?? [];
|
|
58
|
+
const totalCount = allReviews.length;
|
|
59
|
+
const pageSize = data.pageSize > 0 ? data.pageSize : 10;
|
|
60
|
+
const pageNumber = data.pageNumber > 0 ? data.pageNumber : 1;
|
|
61
|
+
const totalPages = pageSize > 0 ? Math.ceil(totalCount / pageSize) : 1;
|
|
62
|
+
const offset = (pageNumber - 1) * pageSize;
|
|
63
|
+
const pageReviews = allReviews.slice(offset, offset + pageSize);
|
|
64
|
+
const items = pageReviews.map((r) => {
|
|
65
|
+
const parsed = this.parseReview(context, r);
|
|
66
|
+
return { ...parsed, product: { key: data.productKey } };
|
|
67
|
+
});
|
|
68
|
+
return this.reviewPaginatedSchema.parse({
|
|
69
|
+
items,
|
|
70
|
+
totalCount,
|
|
71
|
+
pageSize,
|
|
72
|
+
pageNumber,
|
|
73
|
+
totalPages
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export {
|
|
78
|
+
LipscoreProductReviewsFactory
|
|
79
|
+
};
|
package/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from "./core/initialize.js";
|
|
2
|
+
export * from "./core/initialize.types.js";
|
|
3
|
+
export * from "./capabilities/index.js";
|
|
4
|
+
export * from "./factories/index.js";
|
|
5
|
+
export * from "./schema/configuration.schema.js";
|
|
6
|
+
export * from "./schema/capabilities.schema.js";
|
|
7
|
+
export * from "./schema/lipscore.schema.js";
|
package/package.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@reactionary/lipscore",
|
|
3
|
+
"version": "0.9.22",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./index.js",
|
|
6
|
+
"types": "./src/index.d.ts",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@reactionary/core": "0.9.22",
|
|
9
|
+
"zod": "4.1.9"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@nx/vite": "22.4.5",
|
|
13
|
+
"vitest": "^4.0.9"
|
|
14
|
+
},
|
|
15
|
+
"sideEffects": false
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { CapabilitiesSchema } from "@reactionary/core";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
const ProductReviewsCapabilitySchema = z.looseObject({
|
|
4
|
+
enabled: z.boolean(),
|
|
5
|
+
factory: z.unknown().optional(),
|
|
6
|
+
capability: z.unknown().optional()
|
|
7
|
+
});
|
|
8
|
+
const LipscoreCapabilitiesSchema = CapabilitiesSchema.pick({
|
|
9
|
+
productReviews: true
|
|
10
|
+
}).extend({
|
|
11
|
+
productReviews: ProductReviewsCapabilitySchema.optional()
|
|
12
|
+
}).partial();
|
|
13
|
+
export {
|
|
14
|
+
LipscoreCapabilitiesSchema
|
|
15
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
const LipscoreConfigurationSchema = z.looseObject({
|
|
3
|
+
apiKey: z.string().meta({ description: "Lipscore public API key." }),
|
|
4
|
+
apiSecret: z.string().optional().meta({
|
|
5
|
+
description: "Lipscore server-side API secret. When set it is sent as the X-Authorization header."
|
|
6
|
+
}),
|
|
7
|
+
apiUrl: z.string().default("https://api.lipscore.com").meta({ description: "Base Lipscore API URL." })
|
|
8
|
+
});
|
|
9
|
+
export {
|
|
10
|
+
LipscoreConfigurationSchema
|
|
11
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
const LipscoreReviewUserSchema = z.looseObject({
|
|
3
|
+
id: z.number().optional(),
|
|
4
|
+
name: z.string().optional(),
|
|
5
|
+
short_name: z.string().optional(),
|
|
6
|
+
email: z.string().optional()
|
|
7
|
+
});
|
|
8
|
+
const LipscoreReviewImageSchema = z.looseObject({
|
|
9
|
+
thumb_url: z.string().optional(),
|
|
10
|
+
image_url: z.string().optional()
|
|
11
|
+
});
|
|
12
|
+
const LipscoreReviewSchema = z.looseObject({
|
|
13
|
+
id: z.number(),
|
|
14
|
+
text: z.string().optional(),
|
|
15
|
+
translated_text: z.string().optional(),
|
|
16
|
+
created_at: z.string(),
|
|
17
|
+
votes_up: z.number().optional(),
|
|
18
|
+
votes_down: z.number().optional(),
|
|
19
|
+
purchase_date: z.string().optional(),
|
|
20
|
+
rating: z.number(),
|
|
21
|
+
user: LipscoreReviewUserSchema.optional(),
|
|
22
|
+
images: z.array(LipscoreReviewImageSchema).optional(),
|
|
23
|
+
review_reply: z.unknown().optional(),
|
|
24
|
+
displayed_name: z.string().optional(),
|
|
25
|
+
testimonial: z.boolean().optional(),
|
|
26
|
+
attributes: z.array(z.unknown()).optional()
|
|
27
|
+
});
|
|
28
|
+
const LipscoreProductSchema = z.looseObject({
|
|
29
|
+
internal_id: z.union([z.string(), z.number()]).optional(),
|
|
30
|
+
reviews: z.array(LipscoreReviewSchema).default([])
|
|
31
|
+
});
|
|
32
|
+
const LipscoreProductsResponseSchema = z.array(LipscoreProductSchema);
|
|
33
|
+
export {
|
|
34
|
+
LipscoreProductSchema,
|
|
35
|
+
LipscoreProductsResponseSchema,
|
|
36
|
+
LipscoreReviewSchema
|
|
37
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './product-reviews.capability.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ProductReviewsCapability, type Cache, type ProductReviewMutationSubmit, type ProductReviewsFactory, type ProductReviewsFactoryRatingOutput, type ProductReviewsFactoryReviewOutput, type ProductReviewsFactoryReviewPaginatedOutput, type ProductReviewsFactoryWithOutput, type ProductReviewsGetRatingSummaryQuery, type ProductReviewsListQuery, type RequestContext, type Result } from '@reactionary/core';
|
|
2
|
+
import type { LipscoreConfiguration } from '../schema/configuration.schema.js';
|
|
3
|
+
import type { LipscoreClient } from '../core/client.js';
|
|
4
|
+
import type { LipscoreProductReviewsFactory } from '../factories/product-reviews/product-reviews.factory.js';
|
|
5
|
+
export declare class LipscoreProductReviewsCapability<TFactory extends ProductReviewsFactory = LipscoreProductReviewsFactory> extends ProductReviewsCapability<ProductReviewsFactoryRatingOutput<TFactory>, ProductReviewsFactoryReviewPaginatedOutput<TFactory>, ProductReviewsFactoryReviewOutput<TFactory>> {
|
|
6
|
+
protected readonly config: LipscoreConfiguration;
|
|
7
|
+
protected readonly client: LipscoreClient;
|
|
8
|
+
protected readonly factory: ProductReviewsFactoryWithOutput<TFactory>;
|
|
9
|
+
constructor(cache: Cache, context: RequestContext, config: LipscoreConfiguration, client: LipscoreClient, factory: ProductReviewsFactoryWithOutput<TFactory>);
|
|
10
|
+
getRatingSummary(query: ProductReviewsGetRatingSummaryQuery): Promise<Result<ProductReviewsFactoryRatingOutput<TFactory>>>;
|
|
11
|
+
findReviews(query: ProductReviewsListQuery): Promise<Result<ProductReviewsFactoryReviewPaginatedOutput<TFactory>>>;
|
|
12
|
+
submitReview(mutation: ProductReviewMutationSubmit): Promise<Result<ProductReviewsFactoryReviewOutput<TFactory>>>;
|
|
13
|
+
protected getReviewsUrl(): string;
|
|
14
|
+
protected getReviewsParams(productId: string): URLSearchParams;
|
|
15
|
+
protected getRatingSummaryParams(productId: string): URLSearchParams;
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RequestContext } from '@reactionary/core';
|
|
2
|
+
import type { LipscoreConfiguration } from '../schema/configuration.schema.js';
|
|
3
|
+
export declare class LipscoreClient {
|
|
4
|
+
protected readonly config: LipscoreConfiguration;
|
|
5
|
+
protected readonly context: RequestContext;
|
|
6
|
+
readonly baseUrl: string;
|
|
7
|
+
constructor(config: LipscoreConfiguration, context: RequestContext);
|
|
8
|
+
callGet<T>(url: string, params: URLSearchParams, schema: {
|
|
9
|
+
parse(data: unknown): T;
|
|
10
|
+
}): Promise<T>;
|
|
11
|
+
protected buildParams(params: URLSearchParams): URLSearchParams;
|
|
12
|
+
protected buildHeaders(): Record<string, string>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Cache, RequestContext } from '@reactionary/core';
|
|
2
|
+
import type { LipscoreCapabilities } from '../schema/capabilities.schema.js';
|
|
3
|
+
import type { LipscoreConfiguration } from '../schema/configuration.schema.js';
|
|
4
|
+
import { type LipscoreClientFromCapabilities } from './initialize.types.js';
|
|
5
|
+
export declare function withLipscoreCapabilities<T extends LipscoreCapabilities>(configuration: LipscoreConfiguration, capabilities: T): (cache: Cache, context: RequestContext) => LipscoreClientFromCapabilities<T>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ClientFromCapabilities, ProductReviewsFactory } from '@reactionary/core';
|
|
2
|
+
import type { LipscoreCapabilities } from '../schema/capabilities.schema.js';
|
|
3
|
+
import type { LipscoreProductReviewsFactory } from '../factories/product-reviews/product-reviews.factory.js';
|
|
4
|
+
import type { LipscoreProductReviewsCapability } from '../capabilities/product-reviews.capability.js';
|
|
5
|
+
type EnabledCapability<TCapability> = TCapability extends {
|
|
6
|
+
enabled: true;
|
|
7
|
+
} ? true : false;
|
|
8
|
+
type NormalizeConfiguredCapabilities<T extends LipscoreCapabilities> = Omit<T, 'productReviews'> & {
|
|
9
|
+
productReviews?: EnabledCapability<T['productReviews']>;
|
|
10
|
+
};
|
|
11
|
+
type ExtractCapabilityFactory<TCapability, TContract, TDefaultFactory> = TCapability extends {
|
|
12
|
+
enabled: true;
|
|
13
|
+
factory?: infer TFactory;
|
|
14
|
+
} ? TFactory extends TContract ? TFactory : TDefaultFactory : TDefaultFactory;
|
|
15
|
+
type ExtractCapabilityImplementation<TCapability, TDefaultCapability> = TCapability extends {
|
|
16
|
+
enabled: true;
|
|
17
|
+
capability?: infer TCapabilityFactory;
|
|
18
|
+
} ? TCapabilityFactory extends (...args: unknown[]) => infer TResolvedCapability ? TResolvedCapability : TDefaultCapability : TDefaultCapability;
|
|
19
|
+
type CapabilityOverride<TCapability, TKey extends string, TResolvedCapability> = TCapability extends {
|
|
20
|
+
enabled: true;
|
|
21
|
+
} ? {
|
|
22
|
+
[K in TKey]: TResolvedCapability;
|
|
23
|
+
} : Record<never, never>;
|
|
24
|
+
type ProductReviewsFactoryFor<T extends LipscoreCapabilities> = ExtractCapabilityFactory<T['productReviews'], ProductReviewsFactory, LipscoreProductReviewsFactory>;
|
|
25
|
+
type ProductReviewsCapabilityFor<T extends LipscoreCapabilities> = ExtractCapabilityImplementation<T['productReviews'], LipscoreProductReviewsCapability<ProductReviewsFactoryFor<T>>>;
|
|
26
|
+
export type LipscoreClientFromCapabilities<T extends LipscoreCapabilities> = Omit<ClientFromCapabilities<NormalizeConfiguredCapabilities<T>>, 'productReviews'> & CapabilityOverride<T['productReviews'], 'productReviews', ProductReviewsCapabilityFor<T>>;
|
|
27
|
+
export declare function resolveCapabilityWithFactory<TFactory, TResolvedCapability, TCapabilityArgs>(capability: {
|
|
28
|
+
factory?: TFactory;
|
|
29
|
+
capability?: (args: TCapabilityArgs) => TResolvedCapability;
|
|
30
|
+
} | undefined, defaults: {
|
|
31
|
+
factory: TFactory;
|
|
32
|
+
capability: (args: TCapabilityArgs) => TResolvedCapability;
|
|
33
|
+
}, buildCapabilityArgs: (factory: TFactory) => TCapabilityArgs): TResolvedCapability;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './product-reviews/product-reviews.factory.js';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type * as z from 'zod';
|
|
2
|
+
import type { AnyProductRatingSummarySchema, AnyProductReviewPaginatedSchema, AnyProductReviewSchema, ProductReviewsFactory, RequestContext, ProductRatingSummarySchema, ProductReviewSchema, ProductReviewPaginatedResultSchema } from '@reactionary/core';
|
|
3
|
+
import type { LipscoreReview, LipscoreProductsResponse } from '../../schema/lipscore.schema.js';
|
|
4
|
+
export interface LipscoreReviewsPage {
|
|
5
|
+
response: LipscoreProductsResponse;
|
|
6
|
+
productKey: string;
|
|
7
|
+
pageSize: number;
|
|
8
|
+
pageNumber: number;
|
|
9
|
+
}
|
|
10
|
+
export declare class LipscoreProductReviewsFactory<TRatingSummarySchema extends AnyProductRatingSummarySchema = typeof ProductRatingSummarySchema, TReviewSchema extends AnyProductReviewSchema = typeof ProductReviewSchema, TReviewPaginatedSchema extends AnyProductReviewPaginatedSchema = typeof ProductReviewPaginatedResultSchema> implements ProductReviewsFactory<TRatingSummarySchema, TReviewSchema, TReviewPaginatedSchema> {
|
|
11
|
+
readonly ratingSummarySchema: TRatingSummarySchema;
|
|
12
|
+
readonly reviewSchema: TReviewSchema;
|
|
13
|
+
readonly reviewPaginatedSchema: TReviewPaginatedSchema;
|
|
14
|
+
constructor(ratingSummarySchema: TRatingSummarySchema, reviewSchema: TReviewSchema, reviewPaginatedSchema: TReviewPaginatedSchema);
|
|
15
|
+
parseRatingSummary(_context: RequestContext, data: LipscoreProductsResponse): z.output<TRatingSummarySchema>;
|
|
16
|
+
parseReview(_context: RequestContext, data: LipscoreReview): z.output<TReviewSchema>;
|
|
17
|
+
parseReviewPaginatedResult(context: RequestContext, data: LipscoreReviewsPage): z.output<TReviewPaginatedSchema>;
|
|
18
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './core/initialize.js';
|
|
2
|
+
export * from './core/initialize.types.js';
|
|
3
|
+
export * from './capabilities/index.js';
|
|
4
|
+
export * from './factories/index.js';
|
|
5
|
+
export * from './schema/configuration.schema.js';
|
|
6
|
+
export * from './schema/capabilities.schema.js';
|
|
7
|
+
export * from './schema/lipscore.schema.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Cache, ProductReviewsCapability, ProductReviewsFactory, ProductReviewsFactoryWithOutput, RequestContext } from '@reactionary/core';
|
|
2
|
+
import type { LipscoreConfiguration } from './configuration.schema.js';
|
|
3
|
+
import type { LipscoreClient } from '../core/client.js';
|
|
4
|
+
import * as z from 'zod';
|
|
5
|
+
export declare const LipscoreCapabilitiesSchema: z.ZodObject<{
|
|
6
|
+
productReviews: z.ZodOptional<z.ZodOptional<z.ZodObject<{
|
|
7
|
+
enabled: z.ZodBoolean;
|
|
8
|
+
factory: z.ZodOptional<z.ZodUnknown>;
|
|
9
|
+
capability: z.ZodOptional<z.ZodUnknown>;
|
|
10
|
+
}, z.core.$loose>>>;
|
|
11
|
+
}, z.core.$loose>;
|
|
12
|
+
export type LipscoreCapabilities = z.infer<typeof LipscoreCapabilitiesSchema>;
|
|
13
|
+
export interface LipscoreCapabilityFactoryArgs {
|
|
14
|
+
cache: Cache;
|
|
15
|
+
context: RequestContext;
|
|
16
|
+
config: LipscoreConfiguration;
|
|
17
|
+
}
|
|
18
|
+
export interface LipscoreProductReviewsCapabilityFactoryArgs<TFactory extends ProductReviewsFactory = ProductReviewsFactory> extends LipscoreCapabilityFactoryArgs {
|
|
19
|
+
client: LipscoreClient;
|
|
20
|
+
factory: ProductReviewsFactoryWithOutput<TFactory>;
|
|
21
|
+
}
|
|
22
|
+
export interface LipscoreProductReviewsCapabilityConfig<TFactory extends ProductReviewsFactory = ProductReviewsFactory, TCapability extends ProductReviewsCapability = ProductReviewsCapability> {
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
factory?: ProductReviewsFactoryWithOutput<TFactory>;
|
|
25
|
+
capability?: (args: LipscoreProductReviewsCapabilityFactoryArgs<TFactory>) => TCapability;
|
|
26
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
export declare const LipscoreConfigurationSchema: z.ZodObject<{
|
|
3
|
+
apiKey: z.ZodString;
|
|
4
|
+
apiSecret: z.ZodOptional<z.ZodString>;
|
|
5
|
+
apiUrl: z.ZodDefault<z.ZodString>;
|
|
6
|
+
}, z.core.$loose>;
|
|
7
|
+
export type LipscoreConfiguration = z.infer<typeof LipscoreConfigurationSchema>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
export declare const LipscoreReviewSchema: z.ZodObject<{
|
|
3
|
+
id: z.ZodNumber;
|
|
4
|
+
text: z.ZodOptional<z.ZodString>;
|
|
5
|
+
translated_text: z.ZodOptional<z.ZodString>;
|
|
6
|
+
created_at: z.ZodString;
|
|
7
|
+
votes_up: z.ZodOptional<z.ZodNumber>;
|
|
8
|
+
votes_down: z.ZodOptional<z.ZodNumber>;
|
|
9
|
+
purchase_date: z.ZodOptional<z.ZodString>;
|
|
10
|
+
rating: z.ZodNumber;
|
|
11
|
+
user: z.ZodOptional<z.ZodObject<{
|
|
12
|
+
id: z.ZodOptional<z.ZodNumber>;
|
|
13
|
+
name: z.ZodOptional<z.ZodString>;
|
|
14
|
+
short_name: z.ZodOptional<z.ZodString>;
|
|
15
|
+
email: z.ZodOptional<z.ZodString>;
|
|
16
|
+
}, z.core.$loose>>;
|
|
17
|
+
images: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
18
|
+
thumb_url: z.ZodOptional<z.ZodString>;
|
|
19
|
+
image_url: z.ZodOptional<z.ZodString>;
|
|
20
|
+
}, z.core.$loose>>>;
|
|
21
|
+
review_reply: z.ZodOptional<z.ZodUnknown>;
|
|
22
|
+
displayed_name: z.ZodOptional<z.ZodString>;
|
|
23
|
+
testimonial: z.ZodOptional<z.ZodBoolean>;
|
|
24
|
+
attributes: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
|
|
25
|
+
}, z.core.$loose>;
|
|
26
|
+
export type LipscoreReview = z.infer<typeof LipscoreReviewSchema>;
|
|
27
|
+
export declare const LipscoreProductSchema: z.ZodObject<{
|
|
28
|
+
internal_id: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
|
|
29
|
+
reviews: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
30
|
+
id: z.ZodNumber;
|
|
31
|
+
text: z.ZodOptional<z.ZodString>;
|
|
32
|
+
translated_text: z.ZodOptional<z.ZodString>;
|
|
33
|
+
created_at: z.ZodString;
|
|
34
|
+
votes_up: z.ZodOptional<z.ZodNumber>;
|
|
35
|
+
votes_down: z.ZodOptional<z.ZodNumber>;
|
|
36
|
+
purchase_date: z.ZodOptional<z.ZodString>;
|
|
37
|
+
rating: z.ZodNumber;
|
|
38
|
+
user: z.ZodOptional<z.ZodObject<{
|
|
39
|
+
id: z.ZodOptional<z.ZodNumber>;
|
|
40
|
+
name: z.ZodOptional<z.ZodString>;
|
|
41
|
+
short_name: z.ZodOptional<z.ZodString>;
|
|
42
|
+
email: z.ZodOptional<z.ZodString>;
|
|
43
|
+
}, z.core.$loose>>;
|
|
44
|
+
images: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
45
|
+
thumb_url: z.ZodOptional<z.ZodString>;
|
|
46
|
+
image_url: z.ZodOptional<z.ZodString>;
|
|
47
|
+
}, z.core.$loose>>>;
|
|
48
|
+
review_reply: z.ZodOptional<z.ZodUnknown>;
|
|
49
|
+
displayed_name: z.ZodOptional<z.ZodString>;
|
|
50
|
+
testimonial: z.ZodOptional<z.ZodBoolean>;
|
|
51
|
+
attributes: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
|
|
52
|
+
}, z.core.$loose>>>;
|
|
53
|
+
}, z.core.$loose>;
|
|
54
|
+
export type LipscoreProduct = z.infer<typeof LipscoreProductSchema>;
|
|
55
|
+
export declare const LipscoreProductsResponseSchema: z.ZodArray<z.ZodObject<{
|
|
56
|
+
internal_id: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
|
|
57
|
+
reviews: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
58
|
+
id: z.ZodNumber;
|
|
59
|
+
text: z.ZodOptional<z.ZodString>;
|
|
60
|
+
translated_text: z.ZodOptional<z.ZodString>;
|
|
61
|
+
created_at: z.ZodString;
|
|
62
|
+
votes_up: z.ZodOptional<z.ZodNumber>;
|
|
63
|
+
votes_down: z.ZodOptional<z.ZodNumber>;
|
|
64
|
+
purchase_date: z.ZodOptional<z.ZodString>;
|
|
65
|
+
rating: z.ZodNumber;
|
|
66
|
+
user: z.ZodOptional<z.ZodObject<{
|
|
67
|
+
id: z.ZodOptional<z.ZodNumber>;
|
|
68
|
+
name: z.ZodOptional<z.ZodString>;
|
|
69
|
+
short_name: z.ZodOptional<z.ZodString>;
|
|
70
|
+
email: z.ZodOptional<z.ZodString>;
|
|
71
|
+
}, z.core.$loose>>;
|
|
72
|
+
images: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
73
|
+
thumb_url: z.ZodOptional<z.ZodString>;
|
|
74
|
+
image_url: z.ZodOptional<z.ZodString>;
|
|
75
|
+
}, z.core.$loose>>>;
|
|
76
|
+
review_reply: z.ZodOptional<z.ZodUnknown>;
|
|
77
|
+
displayed_name: z.ZodOptional<z.ZodString>;
|
|
78
|
+
testimonial: z.ZodOptional<z.ZodBoolean>;
|
|
79
|
+
attributes: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
|
|
80
|
+
}, z.core.$loose>>>;
|
|
81
|
+
}, z.core.$loose>>;
|
|
82
|
+
export type LipscoreProductsResponse = z.infer<typeof LipscoreProductsResponseSchema>;
|