@builder.io/plugin-commercelayer 0.0.6 → 0.0.8-1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -4
- package/dist/plugin.system.js +84 -71
- package/dist/plugin.system.js.map +1 -1
- package/package.json +5 -13
package/README.md
CHANGED
|
@@ -6,7 +6,8 @@ Easily connect your Commerce Layer products to your Builder.io content!
|
|
|
6
6
|
|
|
7
7
|
Go to [builder.io/account/organization](https://builder.io/account/organization) and press on `@builder.io/plugin-commercelayer` in the list of plugins, then hit save. You'll be prompted for your Commerce Layer credentials:
|
|
8
8
|
- Client ID
|
|
9
|
-
-
|
|
9
|
+
- Client Secret (optional - required for integration tokens to access all markets)
|
|
10
|
+
- Market Scope (e.g., 'market:all' for integrations, 'market:id:YOUR_MARKET_ID' for sales channels)
|
|
10
11
|
|
|
11
12
|
## Features
|
|
12
13
|
|
|
@@ -68,10 +69,21 @@ Go to [builder.io/account/organization](https://builder.io/account/organization)
|
|
|
68
69
|
|
|
69
70
|
## Authentication
|
|
70
71
|
|
|
71
|
-
The plugin
|
|
72
|
+
The plugin supports both Commerce Layer authentication methods:
|
|
73
|
+
|
|
74
|
+
### Sales Channel Authentication (Market-Specific)
|
|
75
|
+
For accessing products from a specific market:
|
|
72
76
|
- A Commerce Layer account
|
|
73
|
-
- Sales Channel API credentials
|
|
74
|
-
-
|
|
77
|
+
- Sales Channel API credentials (Client ID only)
|
|
78
|
+
- Market-specific scope (e.g., `market:id:YOUR_MARKET_ID`)
|
|
79
|
+
|
|
80
|
+
### Integration Authentication (All Markets)
|
|
81
|
+
For accessing products from all markets:
|
|
82
|
+
- A Commerce Layer account
|
|
83
|
+
- Integration API credentials (Client ID + Client Secret)
|
|
84
|
+
- Market scope set to `market:all`
|
|
85
|
+
|
|
86
|
+
**Note:** Sales channel tokens with `market:all` scope will not return products due to Commerce Layer restrictions. Use integration tokens for cross-market access.
|
|
75
87
|
|
|
76
88
|
## Contributing
|
|
77
89
|
|
package/dist/plugin.system.js
CHANGED
|
@@ -146881,7 +146881,7 @@ System.register(['@builder.io/sdk', '@emotion/core', '@material-ui/core', 'react
|
|
|
146881
146881
|
});
|
|
146882
146882
|
|
|
146883
146883
|
var name = "@builder.io/plugin-commercelayer";
|
|
146884
|
-
var version = "0.0.
|
|
146884
|
+
var version = "0.0.8-0";
|
|
146885
146885
|
var description = "Commerce Layer plugin for Builder.io";
|
|
146886
146886
|
var keywords = [
|
|
146887
146887
|
];
|
|
@@ -146902,20 +146902,11 @@ System.register(['@builder.io/sdk', '@emotion/core', '@material-ui/core', 'react
|
|
|
146902
146902
|
var type = "module";
|
|
146903
146903
|
var types = "dist/index.d.ts";
|
|
146904
146904
|
var scripts = {
|
|
146905
|
-
lint: "tslint --project tsconfig.json -t codeFrame 'src/**/*.ts' 'test/**/*.ts'",
|
|
146906
146905
|
prebuild: "rimraf dist",
|
|
146907
146906
|
build: "rollup -c rollup.config.ts --configPlugin @rollup/plugin-typescript",
|
|
146908
146907
|
start: "SERVE=true rollup -c rollup.config.ts --configPlugin @rollup/plugin-typescript -w",
|
|
146909
146908
|
"release:dev": "npm run build && npm version prerelease --no-git-tag-version && npm publish --tag dev",
|
|
146910
|
-
"release:patch": "npm run build && npm version patch --no-git-tag-version && npm publish"
|
|
146911
|
-
test: "jest --coverage",
|
|
146912
|
-
"test:watch": "jest --coverage --watch",
|
|
146913
|
-
"test:prod": "npm run lint && npm run test -- --no-cache",
|
|
146914
|
-
"report-coverage": "cat ./coverage/lcov.info | coveralls",
|
|
146915
|
-
commit: "git-cz",
|
|
146916
|
-
"semantic-release": "semantic-release",
|
|
146917
|
-
"semantic-release-prepare": "ts-node tools/semantic-release-prepare",
|
|
146918
|
-
"travis-deploy-once": "travis-deploy-once"
|
|
146909
|
+
"release:patch": "npm run build && npm version patch --no-git-tag-version && npm publish"
|
|
146919
146910
|
};
|
|
146920
146911
|
var devDependencies = {
|
|
146921
146912
|
"@rollup/plugin-json": "^6.1.0",
|
|
@@ -146923,12 +146914,13 @@ System.register(['@builder.io/sdk', '@emotion/core', '@material-ui/core', 'react
|
|
|
146923
146914
|
"@rollup/plugin-replace": "^5.0.5",
|
|
146924
146915
|
"@rollup/plugin-typescript": "^11.1.6",
|
|
146925
146916
|
"@types/node": "^20.11.19",
|
|
146917
|
+
rimraf: "^2.6.2",
|
|
146926
146918
|
rollup: "^4.12.0",
|
|
146927
146919
|
"rollup-plugin-esbuild": "^6.1.1",
|
|
146928
146920
|
"rollup-plugin-serve": "^1.1.1",
|
|
146921
|
+
tslib: "^2.8.1",
|
|
146929
146922
|
tsx: "^4.7.1",
|
|
146930
|
-
typescript: "^5.2.2"
|
|
146931
|
-
rimraf: "^2.6.2"
|
|
146923
|
+
typescript: "^5.2.2"
|
|
146932
146924
|
};
|
|
146933
146925
|
var dependencies = {
|
|
146934
146926
|
"@builder.io/plugin-tools": "^0.0.6"
|
|
@@ -146952,26 +146944,44 @@ System.register(['@builder.io/sdk', '@emotion/core', '@material-ui/core', 'react
|
|
|
146952
146944
|
dependencies: dependencies
|
|
146953
146945
|
};
|
|
146954
146946
|
|
|
146955
|
-
|
|
146956
|
-
|
|
146947
|
+
let cachedAuth = null;
|
|
146948
|
+
const getValidToken = async (clientId, clientSecret, scope) => {
|
|
146949
|
+
const now = Date.now();
|
|
146950
|
+
if (cachedAuth && cachedAuth.clientId === clientId && cachedAuth.clientSecret === clientSecret && cachedAuth.scope === scope && now < cachedAuth.expiresAt) {
|
|
146951
|
+
return cachedAuth.token;
|
|
146952
|
+
}
|
|
146953
|
+
const auth = await authenticateClient({ clientId, clientSecret, scope });
|
|
146954
|
+
cachedAuth = {
|
|
146955
|
+
token: auth.accessToken,
|
|
146956
|
+
expiresAt: now + (auth.expiresIn - 300) * 1e3,
|
|
146957
|
+
// 5 min buffer
|
|
146958
|
+
clientId,
|
|
146959
|
+
clientSecret,
|
|
146960
|
+
scope
|
|
146961
|
+
};
|
|
146962
|
+
return auth.accessToken;
|
|
146963
|
+
};
|
|
146964
|
+
const authenticateClient = async ({ clientId, clientSecret, scope }) => {
|
|
146965
|
+
const authUrl = "https://auth.commercelayer.io/oauth/token";
|
|
146966
|
+
const requestBody = {
|
|
146967
|
+
grant_type: "client_credentials",
|
|
146968
|
+
client_id: clientId,
|
|
146969
|
+
scope
|
|
146970
|
+
};
|
|
146971
|
+
if (clientSecret) {
|
|
146972
|
+
requestBody.client_secret = clientSecret;
|
|
146973
|
+
}
|
|
146974
|
+
const response = await fetch(authUrl, {
|
|
146957
146975
|
method: "POST",
|
|
146958
146976
|
headers: {
|
|
146959
146977
|
"Accept": "application/json",
|
|
146960
|
-
"Content-Type": "application/json"
|
|
146961
|
-
"Access-Control-Allow-Origin": "*",
|
|
146962
|
-
"Access-Control-Allow-Methods": "POST, OPTIONS",
|
|
146963
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization"
|
|
146978
|
+
"Content-Type": "application/json"
|
|
146964
146979
|
},
|
|
146965
|
-
body: JSON.stringify(
|
|
146966
|
-
grant_type: "client_credentials",
|
|
146967
|
-
client_id: clientId,
|
|
146968
|
-
scope
|
|
146969
|
-
})
|
|
146980
|
+
body: JSON.stringify(requestBody)
|
|
146970
146981
|
});
|
|
146971
146982
|
if (!response.ok) {
|
|
146972
146983
|
const errorText = await response.text();
|
|
146973
|
-
|
|
146974
|
-
throw new Error(`Authentication failed: ${response.statusText}`);
|
|
146984
|
+
throw new Error(`Authentication failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
146975
146985
|
}
|
|
146976
146986
|
const data = await response.json();
|
|
146977
146987
|
return {
|
|
@@ -146982,6 +146992,23 @@ System.register(['@builder.io/sdk', '@emotion/core', '@material-ui/core', 'react
|
|
|
146982
146992
|
createdAt: data.created_at
|
|
146983
146993
|
};
|
|
146984
146994
|
};
|
|
146995
|
+
const makeApiRequest = async (url, accessToken, options = {}) => {
|
|
146996
|
+
const response = await fetch(url, {
|
|
146997
|
+
...options,
|
|
146998
|
+
headers: {
|
|
146999
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
147000
|
+
"Accept": "application/vnd.api+json",
|
|
147001
|
+
"Content-Type": "application/vnd.api+json",
|
|
147002
|
+
...options.headers
|
|
147003
|
+
}
|
|
147004
|
+
});
|
|
147005
|
+
if (!response.ok) {
|
|
147006
|
+
const errorText = await response.text();
|
|
147007
|
+
throw new Error(`API request failed: ${response.status} ${response.statusText} - ${errorText}`);
|
|
147008
|
+
}
|
|
147009
|
+
const data = await response.json();
|
|
147010
|
+
return data;
|
|
147011
|
+
};
|
|
146985
147012
|
const getOrganizationInfo = async (accessToken) => {
|
|
146986
147013
|
const [, payload] = accessToken.split(".");
|
|
146987
147014
|
const decodedPayload = JSON.parse(atob(payload));
|
|
@@ -146994,42 +147021,25 @@ System.register(['@builder.io/sdk', '@emotion/core', '@material-ui/core', 'react
|
|
|
146994
147021
|
throw new Error("Invalid token: no organization found");
|
|
146995
147022
|
};
|
|
146996
147023
|
const getProduct = async (id, accessToken, baseEndpoint) => {
|
|
146997
|
-
const
|
|
146998
|
-
|
|
146999
|
-
"Authorization": `Bearer ${accessToken}`,
|
|
147000
|
-
"Content-Type": "application/json"
|
|
147001
|
-
}
|
|
147002
|
-
});
|
|
147003
|
-
if (!response.ok) {
|
|
147004
|
-
throw new Error(`Failed to fetch product: ${response.statusText}`);
|
|
147005
|
-
}
|
|
147006
|
-
const data = await response.json();
|
|
147024
|
+
const url = `${baseEndpoint}/api/skus/${id}`;
|
|
147025
|
+
const data = await makeApiRequest(url, accessToken);
|
|
147007
147026
|
return data.data;
|
|
147008
147027
|
};
|
|
147009
147028
|
const searchProducts = async (search, accessToken, baseEndpoint) => {
|
|
147010
147029
|
const params = new URLSearchParams({
|
|
147011
|
-
"filter[q][
|
|
147012
|
-
"
|
|
147030
|
+
"filter[q][name_or_code_cont]": search,
|
|
147031
|
+
"page[size]": "25"
|
|
147013
147032
|
});
|
|
147014
|
-
const
|
|
147015
|
-
|
|
147016
|
-
|
|
147017
|
-
"Accept": "application/vnd.api+json",
|
|
147018
|
-
"Content-Type": "application/vnd.api+json"
|
|
147019
|
-
}
|
|
147020
|
-
});
|
|
147021
|
-
if (!response.ok) {
|
|
147022
|
-
console.error("Search Error:", await response.text());
|
|
147023
|
-
throw new Error(`Failed to search products: ${response.statusText}`);
|
|
147024
|
-
}
|
|
147025
|
-
const data = await response.json();
|
|
147026
|
-
return data.data;
|
|
147033
|
+
const url = `${baseEndpoint}/api/skus?${params}`;
|
|
147034
|
+
const data = await makeApiRequest(url, accessToken);
|
|
147035
|
+
return data.data || [];
|
|
147027
147036
|
};
|
|
147028
147037
|
const transformProduct = (product) => ({
|
|
147029
147038
|
id: product.id,
|
|
147030
147039
|
type: "product",
|
|
147031
147040
|
title: product.attributes.name,
|
|
147032
147041
|
handle: product.attributes.code,
|
|
147042
|
+
price: product.attributes.price,
|
|
147033
147043
|
image: {
|
|
147034
147044
|
src: product.attributes.image_url
|
|
147035
147045
|
}
|
|
@@ -147038,18 +147048,8 @@ System.register(['@builder.io/sdk', '@emotion/core', '@material-ui/core', 'react
|
|
|
147038
147048
|
const params = new URLSearchParams({
|
|
147039
147049
|
"filter[q][code_eq]": handle
|
|
147040
147050
|
});
|
|
147041
|
-
const
|
|
147042
|
-
|
|
147043
|
-
"Authorization": `Bearer ${accessToken}`,
|
|
147044
|
-
"Accept": "application/vnd.api+json",
|
|
147045
|
-
"Content-Type": "application/vnd.api+json"
|
|
147046
|
-
}
|
|
147047
|
-
});
|
|
147048
|
-
if (!response.ok) {
|
|
147049
|
-
console.error("Product Handle Error:", await response.text());
|
|
147050
|
-
throw new Error(`Failed to fetch product by handle: ${response.statusText}`);
|
|
147051
|
-
}
|
|
147052
|
-
const data = await response.json();
|
|
147051
|
+
const url = `${baseEndpoint}/api/skus?${params}`;
|
|
147052
|
+
const data = await makeApiRequest(url, accessToken);
|
|
147053
147053
|
if (!data.data?.[0]) {
|
|
147054
147054
|
throw new Error(`Product not found with handle: ${handle}`);
|
|
147055
147055
|
}
|
|
@@ -147064,44 +147064,57 @@ System.register(['@builder.io/sdk', '@emotion/core', '@material-ui/core', 'react
|
|
|
147064
147064
|
{
|
|
147065
147065
|
name: "clientId",
|
|
147066
147066
|
type: "string",
|
|
147067
|
-
required: true
|
|
147067
|
+
required: true,
|
|
147068
|
+
helperText: "Your Commerce Layer application Client ID"
|
|
147069
|
+
},
|
|
147070
|
+
{
|
|
147071
|
+
name: "clientSecret",
|
|
147072
|
+
type: "string",
|
|
147073
|
+
required: false,
|
|
147074
|
+
helperText: "Your Commerce Layer Client Secret (required for integration tokens to access all markets)"
|
|
147068
147075
|
},
|
|
147069
147076
|
{
|
|
147070
147077
|
name: "scope",
|
|
147071
147078
|
type: "string",
|
|
147072
|
-
required: true
|
|
147079
|
+
required: true,
|
|
147080
|
+
helperText: 'Commerce Layer scope (e.g., "market:all" for integrations, "market:id:YOUR_MARKET_ID" for sales channels)'
|
|
147073
147081
|
}
|
|
147074
147082
|
],
|
|
147075
|
-
ctaText: "Connect Commerce Layer"
|
|
147083
|
+
ctaText: "Connect your Commerce Layer store"
|
|
147076
147084
|
},
|
|
147077
147085
|
async (settings) => {
|
|
147078
147086
|
try {
|
|
147079
147087
|
const clientId = settings.get("clientId");
|
|
147088
|
+
const clientSecret = settings.get("clientSecret");
|
|
147080
147089
|
const scope = settings.get("scope");
|
|
147081
|
-
const auth = await authenticateClient({ clientId, scope });
|
|
147090
|
+
const auth = await authenticateClient({ clientId, clientSecret, scope });
|
|
147082
147091
|
const { baseEndpoint } = await getOrganizationInfo(auth.accessToken);
|
|
147083
147092
|
return {
|
|
147084
147093
|
product: {
|
|
147085
147094
|
async findById(id) {
|
|
147086
|
-
const
|
|
147095
|
+
const token = await getValidToken(clientId, clientSecret, scope);
|
|
147096
|
+
const product = await getProduct(id, token, baseEndpoint);
|
|
147087
147097
|
return transformProduct(product);
|
|
147088
147098
|
},
|
|
147089
147099
|
async findByHandle(handle) {
|
|
147090
|
-
const
|
|
147100
|
+
const token = await getValidToken(clientId, clientSecret, scope);
|
|
147101
|
+
const product = await getProductByHandle(handle, token, baseEndpoint);
|
|
147091
147102
|
return transformProduct(product);
|
|
147092
147103
|
},
|
|
147093
147104
|
async search(search) {
|
|
147094
|
-
const
|
|
147105
|
+
const token = await getValidToken(clientId, clientSecret, scope);
|
|
147106
|
+
const products = await searchProducts(search, token, baseEndpoint);
|
|
147095
147107
|
return products.map(transformProduct);
|
|
147096
147108
|
},
|
|
147097
147109
|
getRequestObject(id) {
|
|
147110
|
+
const token = cachedAuth?.token || auth.accessToken;
|
|
147098
147111
|
return {
|
|
147099
147112
|
"@type": "@builder.io/core:Request",
|
|
147100
147113
|
request: {
|
|
147101
147114
|
url: `${baseEndpoint}/api/skus/${id}`,
|
|
147102
147115
|
method: "GET",
|
|
147103
147116
|
headers: {
|
|
147104
|
-
"Authorization": `Bearer ${
|
|
147117
|
+
"Authorization": `Bearer ${token}`
|
|
147105
147118
|
}
|
|
147106
147119
|
},
|
|
147107
147120
|
options: {
|