@unchainedshop/api 4.4.0 → 4.6.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 +204 -0
- package/lib/api-index.js +3 -0
- package/lib/chat/generateImageHandler.d.ts +7 -1
- package/lib/chat/generateImageHandler.js +4 -5
- package/lib/context.js +2 -13
- package/lib/errors.d.ts +1 -0
- package/lib/errors.js +2 -1
- package/lib/events.js +0 -2
- package/lib/express/chatHandler.js +4 -4
- package/lib/express/createERCMetadataMiddleware.js +1 -1
- package/lib/express/index.js +23 -9
- package/lib/fastify/chatHandler.js +4 -4
- package/lib/fastify/ercMetadataHandler.js +1 -1
- package/lib/fastify/index.js +9 -3
- package/lib/loaders/assortmentMediaTextLoader.js +1 -1
- package/lib/loaders/assortmentMediasLoader.js +1 -1
- package/lib/loaders/assortmentTextLoader.js +1 -1
- package/lib/loaders/countryLoader.js +1 -1
- package/lib/loaders/currencyLoader.js +1 -1
- package/lib/loaders/deliveryProviderLoader.js +1 -1
- package/lib/loaders/fileLoader.js +1 -1
- package/lib/loaders/filterTextLoader.js +1 -1
- package/lib/loaders/languageLoader.js +1 -1
- package/lib/loaders/orderLoader.js +1 -1
- package/lib/loaders/paymentProviderLoader.js +1 -1
- package/lib/loaders/productBySKULoader.js +2 -4
- package/lib/loaders/productLoader.js +1 -3
- package/lib/loaders/productMediaTextLoader.js +1 -1
- package/lib/loaders/productMediasLoader.js +1 -1
- package/lib/loaders/productProxiesLoader.js +2 -10
- package/lib/loaders/productTextLoader.js +1 -1
- package/lib/loaders/productVariationByKeyLoader.js +1 -1
- package/lib/loaders/productVariationLoader.js +1 -1
- package/lib/loaders/productVariationTextLoader.js +1 -1
- package/lib/loaders/quotationLoader.js +1 -1
- package/lib/loaders/userLoader.js +1 -1
- package/lib/loaders/warehousingProviderLoader.js +1 -1
- package/lib/mcp/tools/assortment/handlers/addAssortmentFilter.d.ts +0 -1
- package/lib/mcp/tools/assortment/handlers/addAssortmentLink.d.ts +0 -1
- package/lib/mcp/tools/assortment/handlers/addAssortmentMedia.d.ts +0 -1
- package/lib/mcp/tools/assortment/handlers/addAssortmentProduct.d.ts +0 -1
- package/lib/mcp/tools/assortment/handlers/createAssortment.d.ts +0 -1
- package/lib/mcp/tools/assortment/handlers/getAssortment.d.ts +0 -1
- package/lib/mcp/tools/assortment/handlers/getAssortmentChildren.d.ts +0 -2
- package/lib/mcp/tools/assortment/handlers/getAssortmentFilters.d.ts +0 -1
- package/lib/mcp/tools/assortment/handlers/getAssortmentLinks.d.ts +0 -2
- package/lib/mcp/tools/assortment/handlers/getAssortmentMedia.d.ts +0 -1
- package/lib/mcp/tools/assortment/handlers/getAssortmentProducts.d.ts +0 -1
- package/lib/mcp/tools/assortment/handlers/index.d.ts +0 -2
- package/lib/mcp/tools/assortment/handlers/index.js +0 -2
- package/lib/mcp/tools/assortment/handlers/listAssortments.d.ts +0 -1
- package/lib/mcp/tools/assortment/handlers/reorderAssortmentLinks.d.ts +0 -1
- package/lib/mcp/tools/assortment/handlers/searchAssortmentProducts.d.ts +0 -1
- package/lib/mcp/tools/assortment/handlers/updateAssortment.d.ts +0 -1
- package/lib/mcp/tools/assortment/handlers/updateAssortmentStatus.d.ts +0 -1
- package/lib/mcp/tools/assortment/schemas.d.ts +0 -5
- package/lib/mcp/tools/assortment/schemas.js +0 -4
- package/lib/mcp/tools/order/handlers/confirmOrder.d.ts +1 -1
- package/lib/mcp/tools/order/handlers/deliverOrder.d.ts +1 -1
- package/lib/mcp/tools/order/handlers/getOrder.d.ts +1 -1
- package/lib/mcp/tools/order/handlers/getTopCustomers.d.ts +6 -6
- package/lib/mcp/tools/order/handlers/getTopCustomers.js +5 -55
- package/lib/mcp/tools/order/handlers/getTopProducts.d.ts +3 -3
- package/lib/mcp/tools/order/handlers/getTopProducts.js +4 -43
- package/lib/mcp/tools/order/handlers/getUserCart.d.ts +1 -1
- package/lib/mcp/tools/order/handlers/listOrders.d.ts +1 -1
- package/lib/mcp/tools/order/handlers/payOrder.d.ts +1 -1
- package/lib/mcp/tools/order/handlers/rejectOrder.d.ts +1 -1
- package/lib/mcp/tools/provider/handlers/listProviders.js +4 -8
- package/lib/mcp/tools/quotation/handlers/getQuotation.d.ts +2 -2
- package/lib/mcp/tools/quotation/handlers/listQuotations.d.ts +2 -2
- package/lib/mcp/tools/quotation/handlers/makeQuotationProposal.d.ts +2 -2
- package/lib/mcp/tools/quotation/handlers/rejectQuotation.d.ts +2 -2
- package/lib/mcp/tools/quotation/handlers/rejectQuotation.js +1 -1
- package/lib/mcp/tools/quotation/handlers/verifyQuotation.d.ts +2 -2
- package/lib/mcp/tools/quotation/index.js +1 -1
- package/lib/mcp/tools/quotation/schemas.d.ts +5 -5
- package/lib/mcp/tools/quotation/schemas.js +3 -3
- package/lib/mcp/tools/system/schemas.d.ts +6 -6
- package/lib/mcp/tools/system/schemas.js +14 -7
- package/lib/mcp/tools/users/handlers/addUserEmail.d.ts +1 -1
- package/lib/mcp/tools/users/handlers/createUser.d.ts +1 -1
- package/lib/mcp/tools/users/handlers/enrollUser.d.ts +1 -1
- package/lib/mcp/tools/users/handlers/getCurrentUser.d.ts +1 -1
- package/lib/mcp/tools/users/handlers/getUser.d.ts +1 -1
- package/lib/mcp/tools/users/handlers/getUserOrders.d.ts +1 -1
- package/lib/mcp/tools/users/handlers/getUserQuotations.d.ts +1 -1
- package/lib/mcp/tools/users/handlers/listUsers.d.ts +1 -1
- package/lib/mcp/tools/users/handlers/setUserTags.d.ts +1 -1
- package/lib/mcp/tools/users/handlers/setUserUsername.d.ts +1 -1
- package/lib/mcp/tools/users/handlers/updateUser.d.ts +1 -1
- package/lib/mcp/utils/getNormalizedAssortmentDetails.d.ts +0 -1
- package/lib/mcp/utils/getNormalizedOrderDetails.d.ts +1 -1
- package/lib/mcp/utils/getNormalizedQuotationDetails.d.ts +2 -2
- package/lib/mcp/utils/getNormalizedUserDetails.d.ts +1 -1
- package/lib/mongo-store.d.ts +9 -5
- package/lib/mongo-store.js +31 -25
- package/lib/resolvers/mutations/accounts/addWeb3Address.d.ts +2 -2
- package/lib/resolvers/mutations/accounts/addWeb3Address.js +1 -14
- package/lib/resolvers/mutations/accounts/addWebAuthnCredentials.d.ts +2 -2
- package/lib/resolvers/mutations/accounts/addWebAuthnCredentials.js +2 -6
- package/lib/resolvers/mutations/accounts/createUser.js +3 -1
- package/lib/resolvers/mutations/accounts/createWebAuthnCredentialCreationOptions.d.ts +1 -4
- package/lib/resolvers/mutations/accounts/createWebAuthnCredentialRequestOptions.d.ts +1 -4
- package/lib/resolvers/mutations/accounts/forgotPassword.js +4 -4
- package/lib/resolvers/mutations/accounts/loginAsGuest.js +1 -3
- package/lib/resolvers/mutations/accounts/removeWeb3Address.d.ts +1 -1
- package/lib/resolvers/mutations/accounts/removeWeb3Address.js +3 -7
- package/lib/resolvers/mutations/accounts/removeWebAuthnCredentials.d.ts +1 -1
- package/lib/resolvers/mutations/accounts/removeWebAuthnCredentials.js +1 -5
- package/lib/resolvers/mutations/accounts/verifyWeb3Address.d.ts +2 -2
- package/lib/resolvers/mutations/accounts/verifyWeb3Address.js +8 -89
- package/lib/resolvers/mutations/filters/removeFilter.d.ts +2 -2
- package/lib/resolvers/mutations/filters/removeFilter.js +3 -5
- package/lib/resolvers/mutations/index.d.ts +0 -1
- package/lib/resolvers/mutations/index.js +0 -2
- package/lib/resolvers/mutations/orders/addMultipleCartProducts.js +23 -31
- package/lib/resolvers/mutations/orders/removeCartDiscount.d.ts +2 -2
- package/lib/resolvers/mutations/orders/removeCartDiscount.js +9 -14
- package/lib/resolvers/mutations/orders/updateCart.js +2 -8
- package/lib/resolvers/mutations/quotations/rejectQuotation.js +1 -1
- package/lib/resolvers/mutations/users/removeUser.d.ts +1 -1
- package/lib/resolvers/mutations/users/removeUser.js +2 -3
- package/lib/resolvers/mutations/warehousing/exportToken.js +9 -17
- package/lib/resolvers/queries/payment/paymentProvidersCount.d.ts +3 -3
- package/lib/resolvers/queries/payment/paymentProvidersCount.js +1 -1
- package/lib/resolvers/queries/shopInfo.js +4 -36
- package/lib/resolvers/type/delivery-provider-interface.d.ts +1 -7
- package/lib/resolvers/type/delivery-provider-interface.js +10 -20
- package/lib/resolvers/type/delivery-provider-pickup-types.d.ts +1 -7
- package/lib/resolvers/type/delivery-provider-shipping-types.d.ts +1 -7
- package/lib/resolvers/type/delivery-provider-types.d.ts +1 -7
- package/lib/resolvers/type/index.d.ts +33 -54
- package/lib/resolvers/type/media-types.d.ts +1 -1
- package/lib/resolvers/type/media-types.js +7 -5
- package/lib/resolvers/type/order/order-item-types.d.ts +2 -9
- package/lib/resolvers/type/order/order-item-types.js +2 -37
- package/lib/resolvers/type/order/order-statistics-types.d.ts +10 -10
- package/lib/resolvers/type/order/order-statistics-types.js +10 -64
- package/lib/resolvers/type/payment/payment-provider-types.d.ts +1 -7
- package/lib/resolvers/type/payment/payment-provider-types.js +7 -17
- package/lib/resolvers/type/price-types.d.ts +0 -1
- package/lib/resolvers/type/product/product-bundle-types.d.ts +1 -1
- package/lib/resolvers/type/product/product-configurable-types.d.ts +1 -1
- package/lib/resolvers/type/product/product-configurable-types.js +9 -32
- package/lib/resolvers/type/product/product-plan-types.d.ts +1 -1
- package/lib/resolvers/type/product/product-simple-types.d.ts +1 -1
- package/lib/resolvers/type/product/product-tokenized-types.d.ts +1 -1
- package/lib/resolvers/type/product/product-types.d.ts +1 -1
- package/lib/resolvers/type/product/product-types.js +7 -17
- package/lib/resolvers/type/shop-types.js +2 -5
- package/lib/resolvers/type/token-types.d.ts +3 -3
- package/lib/resolvers/type/token-types.js +4 -25
- package/lib/resolvers/type/user-types.js +1 -4
- package/lib/resolvers/type/webauthn-credentials-types.d.ts +10 -1
- package/lib/roles/index.d.ts +2 -1
- package/lib/roles/index.js +7 -1
- package/lib/schema/mutation.js +1 -7
- package/lib/schema/types/assortment.js +0 -1
- package/lib/schema/types/order/order.js +2 -2
- package/lib/schema/types/quotation.js +2 -2
- package/lib/utils/getConfiguredTags.d.ts +1 -0
- package/lib/utils/getConfiguredTags.js +9 -0
- package/package.json +14 -23
- package/lib/mcp/tools/assortment/handlers/setAssortmentBase.d.ts +0 -28
- package/lib/mcp/tools/assortment/handlers/setAssortmentBase.js +0 -11
- package/lib/resolvers/mutations/assortments/setBaseAssortment.d.ts +0 -4
- package/lib/resolvers/mutations/assortments/setBaseAssortment.js +0 -11
|
@@ -2,7 +2,4 @@ import type { Context } from '../../../context.ts';
|
|
|
2
2
|
export default function createWebAuthnCredentialCreationOptions(root: never, { extensionOptions, username }: {
|
|
3
3
|
username: string;
|
|
4
4
|
extensionOptions: any;
|
|
5
|
-
}, { modules, userId, getHeader }: Context): Promise<
|
|
6
|
-
challenge: string;
|
|
7
|
-
requestId: number;
|
|
8
|
-
}) | null>;
|
|
5
|
+
}, { modules, userId, getHeader }: Context): Promise<import("@unchainedshop/core-users").WebAuthnCredentialCreationOptions>;
|
|
@@ -2,7 +2,4 @@ import type { Context } from '../../../context.ts';
|
|
|
2
2
|
export default function createWebAuthnCredentialRequestOptions(root: never, { extensionOptions, username }: {
|
|
3
3
|
username: string;
|
|
4
4
|
extensionOptions: any;
|
|
5
|
-
}, { modules, userId, getHeader }: Context): Promise<
|
|
6
|
-
challenge: string;
|
|
7
|
-
requestId: number;
|
|
8
|
-
}) | null>;
|
|
5
|
+
}, { modules, userId, getHeader }: Context): Promise<import("@unchainedshop/core-users").WebAuthnCredentialRequestOptions>;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
|
-
import { UserNotFoundError } from "../../../errors.js";
|
|
3
2
|
export default async function forgotPassword(root, { email }, { modules, userId }) {
|
|
4
|
-
log('mutation forgotPassword', {
|
|
3
|
+
log('mutation forgotPassword', { userId });
|
|
5
4
|
const user = await modules.users.findUserByEmail(email);
|
|
6
|
-
if (!user)
|
|
7
|
-
|
|
5
|
+
if (!user) {
|
|
6
|
+
return { success: true };
|
|
7
|
+
}
|
|
8
8
|
try {
|
|
9
9
|
await modules.users.sendResetPasswordEmail(user._id, email);
|
|
10
10
|
return {
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
|
-
import moniker from 'moniker';
|
|
3
|
-
import { generateDbObjectId } from '@unchainedshop/mongodb';
|
|
4
2
|
export default async function loginAsGuest(root, _, context) {
|
|
5
3
|
log('mutation loginAsGuest');
|
|
6
|
-
const guestname =
|
|
4
|
+
const guestname = `guest-${crypto.randomUUID()}`;
|
|
7
5
|
const guestUserId = await context.modules.users.createUser({
|
|
8
6
|
email: `${guestname}@unchained.local`,
|
|
9
7
|
guest: true,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { Context } from '../../../context.ts';
|
|
2
2
|
export default function removeWeb3Address(root: never, { address }: {
|
|
3
3
|
address: string;
|
|
4
|
-
}, { modules, userId, user }: Context): Promise<import("
|
|
4
|
+
}, { modules, userId, user }: Context): Promise<import("@unchainedshop/core-users").User>;
|
|
@@ -4,13 +4,9 @@ export default async function removeWeb3Address(root, { address }, { modules, us
|
|
|
4
4
|
log(`mutation removeWeb3Address ${address} ${user?.username}`, {
|
|
5
5
|
userId,
|
|
6
6
|
});
|
|
7
|
-
const
|
|
8
|
-
if (!
|
|
7
|
+
const updatedUser = await modules.users.removeWeb3Address(userId, address);
|
|
8
|
+
if (!updatedUser) {
|
|
9
9
|
throw new UserWeb3AddressNotFoundError({ userId, address });
|
|
10
10
|
}
|
|
11
|
-
return
|
|
12
|
-
$pull: {
|
|
13
|
-
'services.web3': { address: foundCredentials.address },
|
|
14
|
-
},
|
|
15
|
-
}, {});
|
|
11
|
+
return updatedUser;
|
|
16
12
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { Context } from '../../../context.ts';
|
|
2
2
|
export default function removeWebAuthnCredentials(root: never, { credentialsId }: {
|
|
3
3
|
credentialsId: string;
|
|
4
|
-
}, { modules, userId, user }: Context): Promise<import("
|
|
4
|
+
}, { modules, userId, user }: Context): Promise<import("@unchainedshop/core-users").User | null>;
|
|
@@ -8,9 +8,5 @@ export default async function removeWebAuthnCredentials(root, { credentialsId },
|
|
|
8
8
|
if (!foundCredentials) {
|
|
9
9
|
throw new UserWebAuthnCredentialsNotFoundError({ userId, credentialsId });
|
|
10
10
|
}
|
|
11
|
-
return modules.users.
|
|
12
|
-
$pull: {
|
|
13
|
-
'services.webAuthn': { id: credentialsId },
|
|
14
|
-
},
|
|
15
|
-
}, {});
|
|
11
|
+
return modules.users.removeWebAuthnCredential(userId, credentialsId);
|
|
16
12
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Context } from '../../../context.ts';
|
|
2
|
-
export default function verifyWeb3Address(
|
|
2
|
+
export default function verifyWeb3Address(_root: never, { address, hash }: {
|
|
3
3
|
address: string;
|
|
4
4
|
hash: `0x${string}`;
|
|
5
|
-
}, { modules, userId, user }: Context): Promise<import("
|
|
5
|
+
}, { modules, userId, user }: Context): Promise<import("@unchainedshop/core-users").User>;
|
|
@@ -1,100 +1,19 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
2
|
import { UserWeb3AddressNotFoundError, UserWeb3AddressSignatureError } from "../../../errors.js";
|
|
3
|
-
|
|
4
|
-
let keccak_256;
|
|
5
|
-
let bytesToHex;
|
|
6
|
-
let hexToBytes;
|
|
7
|
-
async function loadNoblePackages() {
|
|
8
|
-
try {
|
|
9
|
-
const curves = await import('@noble/curves/secp256k1.js');
|
|
10
|
-
const hashes = await import('@noble/hashes/sha3.js');
|
|
11
|
-
const utils = await import('@noble/hashes/utils.js');
|
|
12
|
-
if (!curves || !hashes || !utils) {
|
|
13
|
-
throw new Error('Missing required @noble packages for Web3 signature verification');
|
|
14
|
-
}
|
|
15
|
-
secp256k1 = curves.secp256k1;
|
|
16
|
-
keccak_256 = hashes.keccak_256;
|
|
17
|
-
bytesToHex = utils.bytesToHex;
|
|
18
|
-
hexToBytes = utils.hexToBytes;
|
|
19
|
-
return true;
|
|
20
|
-
}
|
|
21
|
-
catch (error) {
|
|
22
|
-
log('Failed to load @noble packages for Web3 verification', { error: error.message });
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
function fromRPCSig(sig) {
|
|
27
|
-
const bytes = hexToBytes(sig.startsWith('0x') ? sig.slice(2) : sig);
|
|
28
|
-
if (bytes.length !== 65)
|
|
29
|
-
throw new Error('Invalid signature length');
|
|
30
|
-
const r = bytes.slice(0, 32);
|
|
31
|
-
const s = bytes.slice(32, 64);
|
|
32
|
-
let v = BigInt(bytes[64]);
|
|
33
|
-
if (v >= 35n) {
|
|
34
|
-
v = v - 35n - 2n * 1n;
|
|
35
|
-
}
|
|
36
|
-
else if (v >= 27n) {
|
|
37
|
-
v = v - 27n;
|
|
38
|
-
}
|
|
39
|
-
return { v, r, s };
|
|
40
|
-
}
|
|
41
|
-
function hashPersonalMessage(message) {
|
|
42
|
-
const prefix = new TextEncoder().encode('\x19Ethereum Signed Message:\n');
|
|
43
|
-
const lengthBytes = new TextEncoder().encode(message.length.toString());
|
|
44
|
-
const combined = new Uint8Array(prefix.length + lengthBytes.length + message.length);
|
|
45
|
-
combined.set(prefix, 0);
|
|
46
|
-
combined.set(lengthBytes, prefix.length);
|
|
47
|
-
combined.set(message, prefix.length + lengthBytes.length);
|
|
48
|
-
return keccak_256(combined);
|
|
49
|
-
}
|
|
50
|
-
function ecrecover(msgHash, v, r, s) {
|
|
51
|
-
const recovery = Number(v);
|
|
52
|
-
const signature = new Uint8Array(64);
|
|
53
|
-
signature.set(r, 0);
|
|
54
|
-
signature.set(s, 32);
|
|
55
|
-
const publicKey = secp256k1.Signature.fromBytes(signature)
|
|
56
|
-
.addRecoveryBit(recovery)
|
|
57
|
-
.recoverPublicKey(msgHash);
|
|
58
|
-
return publicKey.toBytes(false).slice(1);
|
|
59
|
-
}
|
|
60
|
-
function publicToAddress(publicKey) {
|
|
61
|
-
const hash = keccak_256(publicKey);
|
|
62
|
-
return hash.slice(-20);
|
|
63
|
-
}
|
|
64
|
-
export default async function verifyWeb3Address(root, { address, hash }, { modules, userId, user }) {
|
|
3
|
+
export default async function verifyWeb3Address(_root, { address, hash }, { modules, userId, user }) {
|
|
65
4
|
log(`mutation verifyWeb3Address ${address} ${user?.username}`, {
|
|
66
5
|
userId,
|
|
67
6
|
});
|
|
68
|
-
const
|
|
69
|
-
if (!packagesLoaded) {
|
|
70
|
-
throw new Error('Web3 signature verification is not available. Please install the required @noble packages: @noble/curves, @noble/hashes');
|
|
71
|
-
}
|
|
72
|
-
const foundCredentials = user?.services?.web3?.find((service) => service.address.toLowerCase() === address.toLowerCase());
|
|
7
|
+
const foundCredentials = modules.users.findWeb3Address(user, address);
|
|
73
8
|
if (!foundCredentials) {
|
|
74
9
|
throw new UserWeb3AddressNotFoundError({ userId, address });
|
|
75
10
|
}
|
|
76
|
-
|
|
77
|
-
const sigParams = fromRPCSig(hash);
|
|
78
|
-
const publicKey = ecrecover(messageHash, sigParams.v, sigParams.r, sigParams.s);
|
|
79
|
-
const sender = publicToAddress(publicKey);
|
|
80
|
-
const recoveredAddr = `0x${bytesToHex(sender)}`;
|
|
81
|
-
const isSignatureCorrectForAddress = recoveredAddr.toLowerCase() === foundCredentials.address.toLowerCase();
|
|
82
|
-
if (!isSignatureCorrectForAddress) {
|
|
11
|
+
if (!foundCredentials.nonce) {
|
|
83
12
|
throw new UserWeb3AddressSignatureError({ userId, address: foundCredentials.address });
|
|
84
13
|
}
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
verified: true,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
return service;
|
|
94
|
-
});
|
|
95
|
-
return modules.users.updateUser({ _id: userId }, {
|
|
96
|
-
$set: {
|
|
97
|
-
'services.web3': web3Services,
|
|
98
|
-
},
|
|
99
|
-
}, {});
|
|
14
|
+
const updatedUser = await modules.users.verifyWeb3SignatureAndUpdate(user, { address: foundCredentials.address, nonce: foundCredentials.nonce }, hash);
|
|
15
|
+
if (!updatedUser) {
|
|
16
|
+
throw new UserWeb3AddressSignatureError({ userId, address: foundCredentials.address });
|
|
17
|
+
}
|
|
18
|
+
return updatedUser;
|
|
100
19
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { Context } from '../../../context.ts';
|
|
2
|
-
export default function removeFilter(
|
|
2
|
+
export default function removeFilter(_root: never, { filterId }: {
|
|
3
3
|
filterId: string;
|
|
4
|
-
}, context: Context): Promise<import("
|
|
4
|
+
}, context: Context): Promise<import("@unchainedshop/core-filters").Filter | null>;
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
2
|
import { FilterNotFoundError, InvalidIdError } from "../../../errors.js";
|
|
3
|
-
export default async function removeFilter(
|
|
4
|
-
const { modules, userId } = context;
|
|
3
|
+
export default async function removeFilter(_root, { filterId }, context) {
|
|
4
|
+
const { modules, services, userId } = context;
|
|
5
5
|
log(`mutation removeFilter ${filterId}`, { userId });
|
|
6
6
|
if (!filterId)
|
|
7
7
|
throw new InvalidIdError({ filterId });
|
|
8
8
|
const filter = await modules.filters.findFilter({ filterId });
|
|
9
9
|
if (!filter)
|
|
10
10
|
throw new FilterNotFoundError({ filterId });
|
|
11
|
-
|
|
12
|
-
await modules.filters.delete(filterId);
|
|
13
|
-
return filter;
|
|
11
|
+
return services.filters.removeFilter({ filter });
|
|
14
12
|
}
|
|
@@ -120,7 +120,6 @@ declare const _default: {
|
|
|
120
120
|
createAssortment: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
121
121
|
prepareAssortmentMediaUpload: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
122
122
|
updateAssortment: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
123
|
-
setBaseAssortment: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
124
123
|
removeAssortment: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
125
124
|
reorderAssortmentMedia: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
126
125
|
removeAssortmentMedia: (root: any, params: any, context: any, ...other: any[]) => Promise<any>;
|
|
@@ -98,7 +98,6 @@ import updateFilterTexts from "./filters/updateFilterTexts.js";
|
|
|
98
98
|
import removeFilterOption from "./filters/removeFilterOption.js";
|
|
99
99
|
import createAssortment from "./assortments/createAssortment.js";
|
|
100
100
|
import updateAssortment from "./assortments/updateAssortment.js";
|
|
101
|
-
import setBaseAssortment from "./assortments/setBaseAssortment.js";
|
|
102
101
|
import removeAssortment from "./assortments/removeAssortment.js";
|
|
103
102
|
import updateAssortmentTexts from "./assortments/updateAssortmentTexts.js";
|
|
104
103
|
import addAssortmentProduct from "./assortments/addAssortmentProduct.js";
|
|
@@ -278,7 +277,6 @@ export default {
|
|
|
278
277
|
createAssortment: acl(actions.manageAssortments)(createAssortment),
|
|
279
278
|
prepareAssortmentMediaUpload: acl(actions.manageAssortments)(prepareAssortmentMediaUpload),
|
|
280
279
|
updateAssortment: acl(actions.manageAssortments)(updateAssortment),
|
|
281
|
-
setBaseAssortment: acl(actions.manageAssortments)(setBaseAssortment),
|
|
282
280
|
removeAssortment: acl(actions.manageAssortments)(removeAssortment),
|
|
283
281
|
reorderAssortmentMedia: acl(actions.manageAssortments)(reorderAssortmentMedia),
|
|
284
282
|
removeAssortmentMedia: acl(actions.manageAssortments)(removeAssortmentMedia),
|
|
@@ -1,53 +1,45 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
2
|
import { ProductNotFoundError, OrderQuantityTooLowError, OrderWrongStatusError, OrderNotFoundError, } from "../../../errors.js";
|
|
3
|
-
import { ordersSettings } from '@unchainedshop/core-orders';
|
|
4
3
|
export default async function addMultipleCartProducts(root, params, context) {
|
|
5
|
-
const { modules, services, userId, user } = context;
|
|
4
|
+
const { modules, services, userId, user, locale, countryCode } = context;
|
|
6
5
|
const { orderId, items } = params;
|
|
7
6
|
log(`mutation addMultipleCartProducts ${JSON.stringify(items)}`, {
|
|
8
7
|
userId,
|
|
9
8
|
orderId,
|
|
10
9
|
});
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
if (!
|
|
10
|
+
for (const { productId, quantity } of items) {
|
|
11
|
+
const product = await modules.products.findProduct({ productId });
|
|
12
|
+
if (!product)
|
|
14
13
|
throw new ProductNotFoundError({ productId });
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
};
|
|
19
|
-
}));
|
|
14
|
+
if (quantity < 1)
|
|
15
|
+
throw new OrderQuantityTooLowError({ quantity, productId });
|
|
16
|
+
}
|
|
20
17
|
const order = await services.orders.findOrInitCart({
|
|
21
18
|
orderId,
|
|
22
19
|
user: user,
|
|
23
|
-
countryCode
|
|
20
|
+
countryCode,
|
|
24
21
|
});
|
|
25
22
|
if (!order)
|
|
26
23
|
throw new OrderNotFoundError({ orderId });
|
|
27
24
|
if (!modules.orders.isCart(order))
|
|
28
25
|
throw new OrderWrongStatusError({ status: order.status });
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
throw new OrderQuantityTooLowError({
|
|
32
|
-
quantity,
|
|
33
|
-
productId: originalProduct._id,
|
|
34
|
-
});
|
|
35
|
-
const product = await modules.products.resolveOrderableProduct(originalProduct, {
|
|
36
|
-
configuration,
|
|
37
|
-
});
|
|
38
|
-
await ordersSettings.validateOrderPosition({
|
|
39
|
-
order,
|
|
40
|
-
product,
|
|
41
|
-
configuration,
|
|
42
|
-
quantityDiff: quantity,
|
|
43
|
-
}, context);
|
|
44
|
-
await modules.orders.positions.addProductItem({
|
|
45
|
-
quantity,
|
|
46
|
-
configuration,
|
|
47
|
-
originalProductId: originalProduct._id,
|
|
48
|
-
productId: product._id,
|
|
26
|
+
try {
|
|
27
|
+
await services.orders.addMultipleCartProducts({
|
|
49
28
|
orderId: order._id,
|
|
29
|
+
items,
|
|
30
|
+
context: { localeContext: locale, userId, countryCode },
|
|
50
31
|
});
|
|
51
32
|
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (error.message?.startsWith('Product not found:')) {
|
|
35
|
+
const productId = error.message.split(': ')[1];
|
|
36
|
+
throw new ProductNotFoundError({ productId });
|
|
37
|
+
}
|
|
38
|
+
if (error.message?.startsWith('Invalid quantity')) {
|
|
39
|
+
const productId = error.message.split(': ')[1];
|
|
40
|
+
throw new OrderQuantityTooLowError({ quantity: 0, productId });
|
|
41
|
+
}
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
52
44
|
return services.orders.updateCalculation(order._id);
|
|
53
45
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { Context } from '../../../context.ts';
|
|
2
|
-
export default function removeCartDiscount(
|
|
2
|
+
export default function removeCartDiscount(_root: never, { discountId }: {
|
|
3
3
|
discountId: string;
|
|
4
|
-
}, requestContext: Context): Promise<import("
|
|
4
|
+
}, requestContext: Context): Promise<import("@unchainedshop/core-orders").OrderDiscount>;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
2
|
import { OrderDiscountNotFoundError, OrderWrongStatusError, InvalidIdError, OrderNotFoundError, } from "../../../errors.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export default async function removeCartDiscount(root, { discountId }, requestContext) {
|
|
6
|
-
const { modules, services, userId } = requestContext;
|
|
3
|
+
export default async function removeCartDiscount(_root, { discountId }, requestContext) {
|
|
4
|
+
const { modules, services, userId, locale } = requestContext;
|
|
7
5
|
log(`mutation removeCartDiscount ${discountId}`, { userId });
|
|
8
6
|
if (!discountId)
|
|
9
7
|
throw new InvalidIdError({ discountId });
|
|
@@ -20,16 +18,13 @@ export default async function removeCartDiscount(root, { discountId }, requestCo
|
|
|
20
18
|
if (!modules.orders.isCart(order)) {
|
|
21
19
|
throw new OrderWrongStatusError({ status: order.status });
|
|
22
20
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
const deletedDiscount = await modules.orders.discounts.delete(discountId);
|
|
21
|
+
const deletedDiscount = await services.orders.removeCartDiscount({
|
|
22
|
+
order,
|
|
23
|
+
orderDiscount,
|
|
24
|
+
requestContext: { localeContext: locale, userId },
|
|
25
|
+
});
|
|
26
|
+
if (!deletedDiscount)
|
|
27
|
+
throw new OrderDiscountNotFoundError({ orderDiscount });
|
|
33
28
|
await services.orders.updateCalculation(order._id);
|
|
34
29
|
return deletedDiscount;
|
|
35
30
|
}
|
|
@@ -13,14 +13,8 @@ export default async function updateCart(root, params, context) {
|
|
|
13
13
|
throw new OrderNotFoundError({ orderId });
|
|
14
14
|
if (!modules.orders.isCart(order))
|
|
15
15
|
throw new OrderWrongStatusError({ status: order.status });
|
|
16
|
-
if (meta) {
|
|
17
|
-
await modules.orders.
|
|
18
|
-
}
|
|
19
|
-
if (billingAddress) {
|
|
20
|
-
await modules.orders.updateBillingAddress(order._id, billingAddress);
|
|
21
|
-
}
|
|
22
|
-
if (contact) {
|
|
23
|
-
await modules.orders.updateContact(order._id, contact);
|
|
16
|
+
if (meta || billingAddress || contact) {
|
|
17
|
+
await modules.orders.updateCartFields(order._id, { meta, billingAddress, contact });
|
|
24
18
|
}
|
|
25
19
|
if (paymentProviderId) {
|
|
26
20
|
await modules.orders.setPaymentProvider(order._id, paymentProviderId);
|
|
@@ -10,7 +10,7 @@ export default async function rejectQuotation(root, params, context) {
|
|
|
10
10
|
const quotation = await modules.quotations.findQuotation({ quotationId });
|
|
11
11
|
if (!quotation)
|
|
12
12
|
throw new QuotationNotFoundError({ quotationId });
|
|
13
|
-
if (quotation.status === QuotationStatus.
|
|
13
|
+
if (quotation.status === QuotationStatus.FULFILLED) {
|
|
14
14
|
throw new QuotationWrongStatusError({ status: quotation.status });
|
|
15
15
|
}
|
|
16
16
|
return services.quotations.rejectQuotation(quotation, transactionContext);
|
|
@@ -2,4 +2,4 @@ import type { Context } from '../../../context.ts';
|
|
|
2
2
|
export default function removeUser(root: never, params: {
|
|
3
3
|
userId: string;
|
|
4
4
|
removeUserReviews?: boolean;
|
|
5
|
-
},
|
|
5
|
+
}, { modules, services, userId: currentUserId }: Context): Promise<import("mongodb").WithId<import("@unchainedshop/core-users").User> | null>;
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
2
|
import { UserNotFoundError } from "../../../errors.js";
|
|
3
|
-
export default async function removeUser(root, params,
|
|
4
|
-
const { modules, services, userId: currentUserId } = unchainedAPI;
|
|
3
|
+
export default async function removeUser(root, params, { modules, services, userId: currentUserId }) {
|
|
5
4
|
const { userId: paramUserId, removeUserReviews = false } = params;
|
|
6
5
|
const normalizedUserId = paramUserId || currentUserId;
|
|
7
6
|
log(`mutation removeUser ${normalizedUserId}`, { userId: currentUserId });
|
|
8
7
|
if (!(await modules.users.userExists({ userId: normalizedUserId })))
|
|
9
8
|
throw new UserNotFoundError({ userId: normalizedUserId });
|
|
10
9
|
if (removeUserReviews) {
|
|
11
|
-
await modules.products.reviews.
|
|
10
|
+
await modules.products.reviews.deleteByAuthorId(normalizedUserId);
|
|
12
11
|
}
|
|
13
12
|
return services.users.deleteUser({ userId: normalizedUserId });
|
|
14
13
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
|
-
import { WorkStatus } from '@unchainedshop/core-worker';
|
|
3
2
|
import { InvalidIdError, TokenNotFoundError, TokenWrongStatusError } from "../../../errors.js";
|
|
4
3
|
export default async function exportToken(root, { tokenId, quantity, recipientWalletAddress, }, { modules, userId }) {
|
|
5
4
|
log(`mutation exportToken ${tokenId}`, {
|
|
@@ -13,21 +12,14 @@ export default async function exportToken(root, { tokenId, quantity, recipientWa
|
|
|
13
12
|
if (token.walletAddress && !token.userId) {
|
|
14
13
|
throw new TokenWrongStatusError({ tokenId });
|
|
15
14
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
input: {
|
|
26
|
-
token,
|
|
27
|
-
quantity,
|
|
28
|
-
recipientWalletAddress,
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
}
|
|
15
|
+
await modules.worker.addWorkIfNotExists({
|
|
16
|
+
type: 'EXPORT_TOKEN',
|
|
17
|
+
retries: 5,
|
|
18
|
+
input: {
|
|
19
|
+
token,
|
|
20
|
+
quantity,
|
|
21
|
+
recipientWalletAddress,
|
|
22
|
+
},
|
|
23
|
+
}, (item) => item.input?.token?._id === token._id);
|
|
32
24
|
return modules.warehousing.findToken({ tokenId });
|
|
33
25
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Context } from '../../../context.ts';
|
|
2
|
-
import type {
|
|
3
|
-
export default function paymentProvidersCount(
|
|
4
|
-
type?:
|
|
2
|
+
import type { PaymentProviderType } from '@unchainedshop/core-payment';
|
|
3
|
+
export default function paymentProvidersCount(_root: never, params: {
|
|
4
|
+
type?: PaymentProviderType | null;
|
|
5
5
|
}, { modules, userId }: Context): Promise<number>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { log } from '@unchainedshop/logger';
|
|
2
|
-
export default async function paymentProvidersCount(
|
|
2
|
+
export default async function paymentProvidersCount(_root, params, { modules, userId }) {
|
|
3
3
|
log(`query paymentProvidersCount ${params.type}`, { userId });
|
|
4
4
|
return modules.payment.paymentProviders.count(params.type
|
|
5
5
|
? {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFile } from 'fs/promises';
|
|
2
2
|
import { log } from '@unchainedshop/logger';
|
|
3
|
+
import { getConfiguredTags } from "../../utils/getConfiguredTags.js";
|
|
3
4
|
export default function shopInfo(root, _, context) {
|
|
4
5
|
const { adminUiConfig, modules } = context;
|
|
5
6
|
log('query shopInfo', { userId: context.userId });
|
|
@@ -30,42 +31,9 @@ export default function shopInfo(root, _, context) {
|
|
|
30
31
|
return [];
|
|
31
32
|
}
|
|
32
33
|
},
|
|
33
|
-
productTags:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
.split(',')
|
|
37
|
-
.map((t) => t.trim())
|
|
38
|
-
.filter(Boolean);
|
|
39
|
-
const normalizedDefaultTags = envTags?.length
|
|
40
|
-
? envTags
|
|
41
|
-
: (adminUiConfig?.defaultProductTags || []).filter(Boolean);
|
|
42
|
-
const normalizedTags = Array.from(new Set(normalizedDefaultTags.concat(existingProductTags)));
|
|
43
|
-
return normalizedTags;
|
|
44
|
-
},
|
|
45
|
-
assortmentTags: async () => {
|
|
46
|
-
const existingAssortmentTags = await modules.assortments.existingTags();
|
|
47
|
-
const envTags = (process.env.UNCHAINED_ADMIN_UI_DEFAULT_ASSORTMENT_TAGS || '')
|
|
48
|
-
.split(',')
|
|
49
|
-
.map((t) => t.trim())
|
|
50
|
-
.filter(Boolean);
|
|
51
|
-
const normalizedDefaultTags = envTags?.length
|
|
52
|
-
? envTags
|
|
53
|
-
: (adminUiConfig?.defaultAssortmentTags || []).filter(Boolean);
|
|
54
|
-
const normalizedTags = Array.from(new Set(normalizedDefaultTags.concat(existingAssortmentTags)));
|
|
55
|
-
return normalizedTags;
|
|
56
|
-
},
|
|
57
|
-
userTags: async () => {
|
|
58
|
-
const existingUserTags = await modules.users.existingTags();
|
|
59
|
-
const envTags = (process.env.UNCHAINED_ADMIN_UI_DEFAULT_USER_TAGS || '')
|
|
60
|
-
.split(',')
|
|
61
|
-
.map((t) => t.trim())
|
|
62
|
-
.filter(Boolean);
|
|
63
|
-
const normalizedDefaultTags = envTags?.length
|
|
64
|
-
? envTags
|
|
65
|
-
: (adminUiConfig?.defaultUserTags || []).filter(Boolean);
|
|
66
|
-
const normalizedTags = Array.from(new Set(normalizedDefaultTags.concat(existingUserTags)));
|
|
67
|
-
return normalizedTags;
|
|
68
|
-
},
|
|
34
|
+
productTags: () => getConfiguredTags(() => modules.products.existingTags(), 'UNCHAINED_ADMIN_UI_DEFAULT_PRODUCT_TAGS', adminUiConfig?.defaultProductTags),
|
|
35
|
+
assortmentTags: () => getConfiguredTags(() => modules.assortments.existingTags(), 'UNCHAINED_ADMIN_UI_DEFAULT_ASSORTMENT_TAGS', adminUiConfig?.defaultAssortmentTags),
|
|
36
|
+
userTags: () => getConfiguredTags(() => modules.users.existingTags(), 'UNCHAINED_ADMIN_UI_DEFAULT_USER_TAGS', adminUiConfig?.defaultUserTags),
|
|
69
37
|
},
|
|
70
38
|
vapidPublicKey: process.env?.PUSH_NOTIFICATION_PUBLIC_KEY,
|
|
71
39
|
};
|
|
@@ -7,11 +7,5 @@ export declare const DeliveryProviderInterface: {
|
|
|
7
7
|
label: string;
|
|
8
8
|
version: string;
|
|
9
9
|
} | null;
|
|
10
|
-
simulatedPrice(deliveryProvider: any, args: any,
|
|
11
|
-
amount: number;
|
|
12
|
-
currencyCode: string;
|
|
13
|
-
countryCode: string;
|
|
14
|
-
isTaxable: boolean;
|
|
15
|
-
isNetPrice: any;
|
|
16
|
-
} | null>;
|
|
10
|
+
simulatedPrice(deliveryProvider: any, args: any, requestContext: Context): Promise<import("@unchainedshop/core/lib/services/simulateDeliveryPricing.js").SimulatedDeliveryPrice | null>;
|
|
17
11
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DeliveryDirector
|
|
1
|
+
import { DeliveryDirector } from '@unchainedshop/core';
|
|
2
2
|
export const DeliveryProviderInterface = {
|
|
3
3
|
async isActive(deliveryProvider, _, context) {
|
|
4
4
|
const director = await DeliveryDirector.actions(deliveryProvider, {}, context);
|
|
@@ -18,30 +18,20 @@ export const DeliveryProviderInterface = {
|
|
|
18
18
|
version: Adapter.version,
|
|
19
19
|
};
|
|
20
20
|
},
|
|
21
|
-
async simulatedPrice(deliveryProvider, args,
|
|
22
|
-
const { loaders, countryCode, user } =
|
|
21
|
+
async simulatedPrice(deliveryProvider, args, requestContext) {
|
|
22
|
+
const { loaders, services, countryCode, user } = requestContext;
|
|
23
23
|
const { currencyCode: forcedCurrencyCode, orderId, useNetPrice, context: providerContext } = args;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
24
|
+
if (!user)
|
|
25
|
+
return null;
|
|
26
|
+
const order = orderId ? await loaders.orderLoader.load({ orderId }) : undefined;
|
|
27
|
+
const currencyCode = forcedCurrencyCode || order?.currencyCode || requestContext.currencyCode;
|
|
28
|
+
return services.delivery.simulateDeliveryPricing({
|
|
27
29
|
countryCode,
|
|
28
30
|
currencyCode,
|
|
29
31
|
provider: deliveryProvider,
|
|
30
32
|
order,
|
|
31
33
|
providerContext,
|
|
32
|
-
user
|
|
33
|
-
};
|
|
34
|
-
const calculated = await DeliveryPricingDirector.rebuildCalculation(pricingContext, context);
|
|
35
|
-
if (!calculated?.length)
|
|
36
|
-
return null;
|
|
37
|
-
const pricing = DeliveryPricingDirector.calculationSheet(pricingContext, calculated);
|
|
38
|
-
const orderPrice = pricing.total({ useNetPrice });
|
|
39
|
-
return {
|
|
40
|
-
amount: orderPrice.amount,
|
|
41
|
-
currencyCode: orderPrice.currencyCode,
|
|
42
|
-
countryCode,
|
|
43
|
-
isTaxable: pricing.taxSum() > 0,
|
|
44
|
-
isNetPrice: useNetPrice,
|
|
45
|
-
};
|
|
34
|
+
user,
|
|
35
|
+
}, { useNetPrice });
|
|
46
36
|
},
|
|
47
37
|
};
|
|
@@ -9,11 +9,5 @@ export declare const DeliveryProviderPickUp: {
|
|
|
9
9
|
label: string;
|
|
10
10
|
version: string;
|
|
11
11
|
} | null;
|
|
12
|
-
simulatedPrice(deliveryProvider: any, args: any,
|
|
13
|
-
amount: number;
|
|
14
|
-
currencyCode: string;
|
|
15
|
-
countryCode: string;
|
|
16
|
-
isTaxable: boolean;
|
|
17
|
-
isNetPrice: any;
|
|
18
|
-
} | null>;
|
|
12
|
+
simulatedPrice(deliveryProvider: any, args: any, requestContext: Context): Promise<import("@unchainedshop/core/lib/services/simulateDeliveryPricing.js").SimulatedDeliveryPrice | null>;
|
|
19
13
|
};
|