brainerce 1.39.0 → 1.42.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 +74 -6
- package/dist/index.d.mts +238 -26
- package/dist/index.d.ts +238 -26
- package/dist/index.js +158 -25
- package/dist/index.mjs +158 -25
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -115,7 +115,7 @@ function isDevGuardsEnabled() {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
// src/version.ts
|
|
118
|
-
var SDK_VERSION = "1.
|
|
118
|
+
var SDK_VERSION = "1.42.0";
|
|
119
119
|
|
|
120
120
|
// src/client.ts
|
|
121
121
|
var DEFAULT_BASE_URL = "https://api.brainerce.com";
|
|
@@ -583,6 +583,7 @@ var BrainerceClient = class {
|
|
|
583
583
|
}
|
|
584
584
|
}
|
|
585
585
|
this.proxyMode = options.proxyMode || false;
|
|
586
|
+
this.analyticsBaseUrl = this.resolveAnalyticsBaseUrl(options.analyticsBaseUrl, resolvedBase);
|
|
586
587
|
this.onAuthError = options.onAuthError;
|
|
587
588
|
this.hydrateSessionCart();
|
|
588
589
|
this.detectRecoverCartFromUrl();
|
|
@@ -1036,12 +1037,46 @@ var BrainerceClient = class {
|
|
|
1036
1037
|
* ```
|
|
1037
1038
|
*/
|
|
1038
1039
|
async trackEvent(payload = {}) {
|
|
1040
|
+
if (this.isVibeCodedMode() && this.connectionId) {
|
|
1041
|
+
this.sendAnalyticsBeacon(`/api/vc/${encodePathSegment(this.connectionId)}/events`, payload);
|
|
1042
|
+
} else if (this.isStorefrontMode() && this.storeId) {
|
|
1043
|
+
this.sendAnalyticsBeacon(`/api/stores/${encodePathSegment(this.storeId)}/events`, payload);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Resolve the base URL for analytics beacons. Analytics must go DIRECTLY to the
|
|
1048
|
+
* Brainerce API (never through a same-origin BFF proxy) so the visitor's real IP —
|
|
1049
|
+
* hence country — reaches the backend. Explicit `analyticsBaseUrl` wins; otherwise
|
|
1050
|
+
* use the direct API when `baseUrl` is a same-origin proxy, else the (already-direct)
|
|
1051
|
+
* `baseUrl` (which may be a custom / self-hosted Brainerce API).
|
|
1052
|
+
*/
|
|
1053
|
+
resolveAnalyticsBaseUrl(explicit, resolvedBase) {
|
|
1054
|
+
if (explicit) return explicit.replace(/\/$/, "");
|
|
1055
|
+
const sameOriginProxy = this.proxyMode || typeof window !== "undefined" && !!window.location?.origin && resolvedBase.startsWith(window.location.origin);
|
|
1056
|
+
return sameOriginProxy ? DEFAULT_BASE_URL : resolvedBase;
|
|
1057
|
+
}
|
|
1058
|
+
/**
|
|
1059
|
+
* Fire-and-forget analytics beacon sent directly to `analyticsBaseUrl`. Uses
|
|
1060
|
+
* `keepalive` so it survives page unload (engagement events fired on pagehide),
|
|
1061
|
+
* omits credentials (cookieless), and never throws.
|
|
1062
|
+
*/
|
|
1063
|
+
sendAnalyticsBeacon(eventsPath, payload) {
|
|
1039
1064
|
try {
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
}
|
|
1065
|
+
const headers = {
|
|
1066
|
+
"Content-Type": "application/json",
|
|
1067
|
+
"X-SDK-Version": SDK_VERSION,
|
|
1068
|
+
"ngrok-skip-browser-warning": "true"
|
|
1069
|
+
};
|
|
1070
|
+
if (this.origin) headers["Origin"] = this.origin;
|
|
1071
|
+
void fetch(`${this.analyticsBaseUrl}${eventsPath}`, {
|
|
1072
|
+
method: "POST",
|
|
1073
|
+
headers,
|
|
1074
|
+
body: JSON.stringify(payload),
|
|
1075
|
+
keepalive: true,
|
|
1076
|
+
credentials: "omit",
|
|
1077
|
+
mode: "cors"
|
|
1078
|
+
}).catch(() => {
|
|
1079
|
+
});
|
|
1045
1080
|
} catch {
|
|
1046
1081
|
}
|
|
1047
1082
|
}
|
|
@@ -5159,6 +5194,14 @@ var BrainerceClient = class {
|
|
|
5159
5194
|
*
|
|
5160
5195
|
* @returns Payment providers config with hasPayments flag and provider list
|
|
5161
5196
|
*
|
|
5197
|
+
* Each provider carries a `methodType` (Shopify-parity taxonomy) and a derived
|
|
5198
|
+
* `isAdditive` / `presentation`. There is at most ONE primary card processor
|
|
5199
|
+
* (`methodType: 'CREDIT_CARD'`, the `defaultProvider`) that settles the order;
|
|
5200
|
+
* additive methods (`isAdditive: true`, e.g. PayPal as a `'WALLET'`) are shown
|
|
5201
|
+
* *alongside* it as accelerated-checkout express buttons — they never replace
|
|
5202
|
+
* the card form. A wallet-only store is the one exception: with no card
|
|
5203
|
+
* processor installed, the wallet becomes the `defaultProvider` and stands alone.
|
|
5204
|
+
*
|
|
5162
5205
|
* @example
|
|
5163
5206
|
* ```typescript
|
|
5164
5207
|
* const { providers, hasPayments, defaultProvider } = await client.getPaymentProviders();
|
|
@@ -5168,27 +5211,21 @@ var BrainerceClient = class {
|
|
|
5168
5211
|
* return;
|
|
5169
5212
|
* }
|
|
5170
5213
|
*
|
|
5171
|
-
* //
|
|
5172
|
-
* const
|
|
5173
|
-
*
|
|
5174
|
-
*
|
|
5175
|
-
*
|
|
5176
|
-
* if (stripeProvider) {
|
|
5177
|
-
* const stripe = await loadStripe(stripeProvider.publicKey, {
|
|
5178
|
-
* stripeAccount: stripeProvider.stripeAccountId, // For Stripe Connect
|
|
5179
|
-
* });
|
|
5180
|
-
* // Show Stripe payment form
|
|
5181
|
-
* }
|
|
5182
|
-
*
|
|
5183
|
-
* // Initialize PayPal if available
|
|
5184
|
-
* if (paypalProvider) {
|
|
5185
|
-
* // Load PayPal SDK with paypalProvider.publicKey as client-id
|
|
5186
|
-
* // Show PayPal buttons
|
|
5214
|
+
* // Render additive methods (wallets) as express buttons ABOVE the card form.
|
|
5215
|
+
* const expressMethods = providers.filter(p => p.isAdditive); // e.g. PayPal
|
|
5216
|
+
* for (const m of expressMethods) {
|
|
5217
|
+
* // m.presentation === 'express_button' — render an express/accelerated button
|
|
5218
|
+
* // and call createPaymentIntent({ providerId: m.id }) when the buyer taps it.
|
|
5187
5219
|
* }
|
|
5188
5220
|
*
|
|
5189
|
-
* //
|
|
5190
|
-
* if (defaultProvider) {
|
|
5191
|
-
*
|
|
5221
|
+
* // Render the primary card processor (the settling default) as the main form.
|
|
5222
|
+
* if (defaultProvider && !defaultProvider.isAdditive) {
|
|
5223
|
+
* if (defaultProvider.provider === 'stripe') {
|
|
5224
|
+
* const stripe = await loadStripe(defaultProvider.publicKey, {
|
|
5225
|
+
* stripeAccount: defaultProvider.stripeAccountId, // For Stripe Connect
|
|
5226
|
+
* });
|
|
5227
|
+
* // Show the Stripe card form
|
|
5228
|
+
* }
|
|
5192
5229
|
* }
|
|
5193
5230
|
* ```
|
|
5194
5231
|
*/
|
|
@@ -6208,6 +6245,102 @@ var BrainerceClient = class {
|
|
|
6208
6245
|
400
|
|
6209
6246
|
);
|
|
6210
6247
|
}
|
|
6248
|
+
// -------------------- Loyalty --------------------
|
|
6249
|
+
// Storefront-mode only (storeId + customerToken). The backend exposes loyalty
|
|
6250
|
+
// solely under /api/stores/:storeId/loyalty — there is no vibe-coded route, so
|
|
6251
|
+
// these do NOT branch on isVibeCodedMode() (that would 404 silently).
|
|
6252
|
+
/**
|
|
6253
|
+
* Get the logged-in customer's loyalty status: enrollment, points balance,
|
|
6254
|
+
* lifetime earned, and the program's display config (requires customerToken).
|
|
6255
|
+
* Only available in storefront mode. `program` is null when the store has no
|
|
6256
|
+
* loyalty program.
|
|
6257
|
+
*
|
|
6258
|
+
* @example
|
|
6259
|
+
* ```typescript
|
|
6260
|
+
* client.setCustomerToken(auth.token);
|
|
6261
|
+
* const status = await client.getLoyaltyStatus();
|
|
6262
|
+
* if (status.enrolled) console.log(`${status.pointsBalance} ${status.program?.pointsName}`);
|
|
6263
|
+
* ```
|
|
6264
|
+
*/
|
|
6265
|
+
async getLoyaltyStatus() {
|
|
6266
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6267
|
+
throw new BrainerceError(
|
|
6268
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6269
|
+
401
|
|
6270
|
+
);
|
|
6271
|
+
}
|
|
6272
|
+
if (this.storeId && !this.apiKey) {
|
|
6273
|
+
return this.storefrontRequest("GET", "/loyalty/me");
|
|
6274
|
+
}
|
|
6275
|
+
throw new BrainerceError("getLoyaltyStatus is only available in storefront mode", 400);
|
|
6276
|
+
}
|
|
6277
|
+
/**
|
|
6278
|
+
* Enroll the logged-in customer in the store's loyalty program (requires
|
|
6279
|
+
* customerToken). Idempotent — returns the same status shape as
|
|
6280
|
+
* getLoyaltyStatus(). Only available in storefront mode.
|
|
6281
|
+
*
|
|
6282
|
+
* @example
|
|
6283
|
+
* ```typescript
|
|
6284
|
+
* const status = await client.enrollInLoyalty();
|
|
6285
|
+
* ```
|
|
6286
|
+
*/
|
|
6287
|
+
async enrollInLoyalty() {
|
|
6288
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6289
|
+
throw new BrainerceError(
|
|
6290
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6291
|
+
401
|
|
6292
|
+
);
|
|
6293
|
+
}
|
|
6294
|
+
if (this.storeId && !this.apiKey) {
|
|
6295
|
+
return this.storefrontRequest("POST", "/loyalty/enroll");
|
|
6296
|
+
}
|
|
6297
|
+
throw new BrainerceError("enrollInLoyalty is only available in storefront mode", 400);
|
|
6298
|
+
}
|
|
6299
|
+
/**
|
|
6300
|
+
* List the rewards the customer can redeem points for (active rewards, cheapest
|
|
6301
|
+
* first). Requires customerToken. Only available in storefront mode.
|
|
6302
|
+
*
|
|
6303
|
+
* @example
|
|
6304
|
+
* ```typescript
|
|
6305
|
+
* client.setCustomerToken(auth.token);
|
|
6306
|
+
* const rewards = await client.getAvailableRewards();
|
|
6307
|
+
* ```
|
|
6308
|
+
*/
|
|
6309
|
+
async getAvailableRewards() {
|
|
6310
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6311
|
+
throw new BrainerceError(
|
|
6312
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6313
|
+
401
|
|
6314
|
+
);
|
|
6315
|
+
}
|
|
6316
|
+
if (this.storeId && !this.apiKey) {
|
|
6317
|
+
return this.storefrontRequest("GET", "/loyalty/rewards/available");
|
|
6318
|
+
}
|
|
6319
|
+
throw new BrainerceError("getAvailableRewards is only available in storefront mode", 400);
|
|
6320
|
+
}
|
|
6321
|
+
/**
|
|
6322
|
+
* Redeem a reward: spends the customer's points and mints a one-time coupon
|
|
6323
|
+
* that applies at checkout (requires customerToken). Only available in
|
|
6324
|
+
* storefront mode.
|
|
6325
|
+
*
|
|
6326
|
+
* @example
|
|
6327
|
+
* ```typescript
|
|
6328
|
+
* const { couponCode, pointsBalance } = await client.redeemLoyaltyReward('rw_123');
|
|
6329
|
+
* // apply couponCode to the cart at checkout
|
|
6330
|
+
* ```
|
|
6331
|
+
*/
|
|
6332
|
+
async redeemLoyaltyReward(rewardId) {
|
|
6333
|
+
if (!this.customerToken && !this.proxyMode) {
|
|
6334
|
+
throw new BrainerceError(
|
|
6335
|
+
"Customer token required. Call setCustomerToken() after login.",
|
|
6336
|
+
401
|
|
6337
|
+
);
|
|
6338
|
+
}
|
|
6339
|
+
if (this.storeId && !this.apiKey) {
|
|
6340
|
+
return this.storefrontRequest("POST", "/loyalty/redeem", { rewardId });
|
|
6341
|
+
}
|
|
6342
|
+
throw new BrainerceError("redeemLoyaltyReward is only available in storefront mode", 400);
|
|
6343
|
+
}
|
|
6211
6344
|
/**
|
|
6212
6345
|
* Get the current customer's orders (requires customerToken)
|
|
6213
6346
|
* Works in vibe-coded and storefront modes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brainerce",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.42.0",
|
|
4
4
|
"description": "Official SDK for building e-commerce storefronts with Brainerce Platform. Perfect for vibe-coded sites, AI-built stores (Cursor, Lovable, v0), and custom storefronts.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|