@rebuy/rebuy 3.0.0 → 3.1.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/dist/server/index.cjs
CHANGED
|
@@ -3676,12 +3676,20 @@ var reportUnreadableVerdict = (metadata, report) => {
|
|
|
3676
3676
|
var dedupeById = (products) => (0, import_es_toolkit6.uniqBy)(products, (product) => product.id);
|
|
3677
3677
|
var fetchDataSourceResults = async (input, ctx) => {
|
|
3678
3678
|
const { shop } = await fetchUserConfig({ shop: input.shop }, ctx);
|
|
3679
|
+
const marketHeaders = {
|
|
3680
|
+
...input.language && { "Accept-Language": input.language },
|
|
3681
|
+
...input.country && { "X-Country-Code": input.country }
|
|
3682
|
+
};
|
|
3679
3683
|
let upstream;
|
|
3680
3684
|
try {
|
|
3681
3685
|
upstream = await ctx.fetchUpstream(
|
|
3682
3686
|
`/api/v1${input.dataSourcePath}`,
|
|
3683
3687
|
{},
|
|
3684
|
-
{
|
|
3688
|
+
{
|
|
3689
|
+
...Object.keys(marketHeaders).length > 0 && { headers: marketHeaders },
|
|
3690
|
+
host: ctx.host,
|
|
3691
|
+
search: serialize(buildEngineParams(input, shop.apiKey))
|
|
3692
|
+
}
|
|
3685
3693
|
);
|
|
3686
3694
|
} catch (err) {
|
|
3687
3695
|
if (err instanceof UpstreamError && (err.status === 404 || err.status === 410)) {
|
|
@@ -3851,10 +3859,13 @@ var validateGifts = async (input, ctx) => {
|
|
|
3851
3859
|
const { metadata } = await fetchDataSourceResults(
|
|
3852
3860
|
{
|
|
3853
3861
|
cart: input.cart,
|
|
3862
|
+
/** Market context threads through so a market-gated gift rule answers for the buyer's market (REB-21102). */
|
|
3863
|
+
country: input.country,
|
|
3854
3864
|
customerId: input.customerId,
|
|
3855
3865
|
dataSourcePath,
|
|
3856
3866
|
/** The gift being validated is in the cart — input filtering would erase it from the answer. */
|
|
3857
3867
|
filterInputs: "no",
|
|
3868
|
+
language: input.language,
|
|
3858
3869
|
/**
|
|
3859
3870
|
* `limit: 0` fetches NO products — only the rule verdict is needed. Safe because the engine
|
|
3860
3871
|
* derives `matchedRules` from rule-config evaluation, not from fetched results (traced through
|
|
@@ -4295,6 +4306,16 @@ var DataSourceInputSchema = import_zod28.z.object({
|
|
|
4295
4306
|
* client's country-context market check is bypassed for B2B (it false-negatives B2B-catalog products).
|
|
4296
4307
|
*/
|
|
4297
4308
|
companyLocationId: import_zod28.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4309
|
+
/**
|
|
4310
|
+
* Buyer's market country (ISO 3166-1 alpha-2) — forwarded to the engine as the `X-Country-Code`
|
|
4311
|
+
* HEADER (REB-21102), so it's regex-bound (not just length-bound like this schema's other passthrough
|
|
4312
|
+
* identifiers): an unfiltered value reaching a header sink can throw a raw `TypeError` (CRLF, or any
|
|
4313
|
+
* non-Latin1 codepoint) that isn't an `UpstreamError`, escaping `fetchDataSourceResults`'s error
|
|
4314
|
+
* handling as an unhandled 500 on this public, unauthenticated route instead of a clean 422 here.
|
|
4315
|
+
* Normalized to uppercase (the wire value is forwarded verbatim as the header) so a case-sensitive
|
|
4316
|
+
* engine lookup can't silently miss a lowercase code — `adsEngine.ts` uppercases for the same reason.
|
|
4317
|
+
*/
|
|
4318
|
+
country: import_zod28.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).regex(/^[A-Za-z]{2}$/, "must be an ISO 3166-1 alpha-2 country code (e.g. CA)").transform((value) => value.toUpperCase()).optional(),
|
|
4298
4319
|
customerId: import_zod28.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4299
4320
|
dataSourcePath: import_zod28.z.string().max(REQUEST_BOUNDS.STRING_KEY_MAX),
|
|
4300
4321
|
/**
|
|
@@ -4306,6 +4327,14 @@ var DataSourceInputSchema = import_zod28.z.object({
|
|
|
4306
4327
|
integrations: import_zod28.z.record(import_zod28.z.string().max(REQUEST_BOUNDS.STRING_KEY_MAX), import_zod28.z.boolean()).refine((record) => Object.keys(record).length <= REQUEST_BOUNDS.INTEGRATION_KEYS_MAX, {
|
|
4307
4328
|
message: `Too many integration keys (max ${REQUEST_BOUNDS.INTEGRATION_KEYS_MAX})`
|
|
4308
4329
|
}).optional(),
|
|
4330
|
+
/**
|
|
4331
|
+
* Buyer's checkout language (BCP 47) — forwarded to the engine as the `Accept-Language` HEADER
|
|
4332
|
+
* (REB-21102); regex-bound for the same header-sink reason as `country` above. MUST be a single tag
|
|
4333
|
+
* (e.g. `fr-CA`) — a q-value list like `fr-CA,fr;q=0.9` (the raw shape of a browser `Accept-Language`
|
|
4334
|
+
* request header) fails the regex. Callers should send the buyer's resolved locale (e.g. Shopify
|
|
4335
|
+
* checkout's `localization.language.isoCode`), not the request header verbatim.
|
|
4336
|
+
*/
|
|
4337
|
+
language: import_zod28.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).regex(/^[A-Za-z0-9-]+$/, "must be a single BCP-47 tag (e.g. fr-CA), not a q-value list").optional(),
|
|
4309
4338
|
limit: import_zod28.z.number().max(REQUEST_BOUNDS.LIMIT_MAX),
|
|
4310
4339
|
productType: import_zod28.z.enum(["both", "one-time", "subscription"]).optional(),
|
|
4311
4340
|
shop: import_zod28.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
|
|
@@ -4320,8 +4349,19 @@ var GiftInputSchema = import_zod28.z.object({
|
|
|
4320
4349
|
});
|
|
4321
4350
|
var GiftValidationInputSchema = import_zod28.z.object({
|
|
4322
4351
|
cart: CartInputSchema,
|
|
4352
|
+
/**
|
|
4353
|
+
* Buyer's market country — threaded into the per-widget data-source evaluations (REB-21102), which
|
|
4354
|
+
* forward it to a header sink; regex-bound and uppercase-normalized for the same reasons as
|
|
4355
|
+
* `DataSourceInputSchema`'s `country`.
|
|
4356
|
+
*/
|
|
4357
|
+
country: import_zod28.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).regex(/^[A-Za-z]{2}$/, "must be an ISO 3166-1 alpha-2 country code (e.g. CA)").transform((value) => value.toUpperCase()).optional(),
|
|
4323
4358
|
customerId: import_zod28.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4324
4359
|
gifts: import_zod28.z.array(GiftInputSchema).max(REQUEST_BOUNDS.GIFTS_MAX),
|
|
4360
|
+
/**
|
|
4361
|
+
* Buyer's checkout language — threaded into the per-widget data-source evaluations (REB-21102). MUST
|
|
4362
|
+
* be a single BCP-47 tag, for the same reason as `DataSourceInputSchema`'s `language` above.
|
|
4363
|
+
*/
|
|
4364
|
+
language: import_zod28.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).regex(/^[A-Za-z0-9-]+$/, "must be a single BCP-47 tag (e.g. fr-CA), not a q-value list").optional(),
|
|
4325
4365
|
shop: import_zod28.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
|
|
4326
4366
|
visitorId: import_zod28.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
|
|
4327
4367
|
});
|
package/dist/server/index.mjs
CHANGED
|
@@ -3628,12 +3628,20 @@ var reportUnreadableVerdict = (metadata, report) => {
|
|
|
3628
3628
|
var dedupeById = (products) => uniqBy2(products, (product) => product.id);
|
|
3629
3629
|
var fetchDataSourceResults = async (input, ctx) => {
|
|
3630
3630
|
const { shop } = await fetchUserConfig({ shop: input.shop }, ctx);
|
|
3631
|
+
const marketHeaders = {
|
|
3632
|
+
...input.language && { "Accept-Language": input.language },
|
|
3633
|
+
...input.country && { "X-Country-Code": input.country }
|
|
3634
|
+
};
|
|
3631
3635
|
let upstream;
|
|
3632
3636
|
try {
|
|
3633
3637
|
upstream = await ctx.fetchUpstream(
|
|
3634
3638
|
`/api/v1${input.dataSourcePath}`,
|
|
3635
3639
|
{},
|
|
3636
|
-
{
|
|
3640
|
+
{
|
|
3641
|
+
...Object.keys(marketHeaders).length > 0 && { headers: marketHeaders },
|
|
3642
|
+
host: ctx.host,
|
|
3643
|
+
search: serialize(buildEngineParams(input, shop.apiKey))
|
|
3644
|
+
}
|
|
3637
3645
|
);
|
|
3638
3646
|
} catch (err) {
|
|
3639
3647
|
if (err instanceof UpstreamError && (err.status === 404 || err.status === 410)) {
|
|
@@ -3803,10 +3811,13 @@ var validateGifts = async (input, ctx) => {
|
|
|
3803
3811
|
const { metadata } = await fetchDataSourceResults(
|
|
3804
3812
|
{
|
|
3805
3813
|
cart: input.cart,
|
|
3814
|
+
/** Market context threads through so a market-gated gift rule answers for the buyer's market (REB-21102). */
|
|
3815
|
+
country: input.country,
|
|
3806
3816
|
customerId: input.customerId,
|
|
3807
3817
|
dataSourcePath,
|
|
3808
3818
|
/** The gift being validated is in the cart — input filtering would erase it from the answer. */
|
|
3809
3819
|
filterInputs: "no",
|
|
3820
|
+
language: input.language,
|
|
3810
3821
|
/**
|
|
3811
3822
|
* `limit: 0` fetches NO products — only the rule verdict is needed. Safe because the engine
|
|
3812
3823
|
* derives `matchedRules` from rule-config evaluation, not from fetched results (traced through
|
|
@@ -4247,6 +4258,16 @@ var DataSourceInputSchema = z28.object({
|
|
|
4247
4258
|
* client's country-context market check is bypassed for B2B (it false-negatives B2B-catalog products).
|
|
4248
4259
|
*/
|
|
4249
4260
|
companyLocationId: z28.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4261
|
+
/**
|
|
4262
|
+
* Buyer's market country (ISO 3166-1 alpha-2) — forwarded to the engine as the `X-Country-Code`
|
|
4263
|
+
* HEADER (REB-21102), so it's regex-bound (not just length-bound like this schema's other passthrough
|
|
4264
|
+
* identifiers): an unfiltered value reaching a header sink can throw a raw `TypeError` (CRLF, or any
|
|
4265
|
+
* non-Latin1 codepoint) that isn't an `UpstreamError`, escaping `fetchDataSourceResults`'s error
|
|
4266
|
+
* handling as an unhandled 500 on this public, unauthenticated route instead of a clean 422 here.
|
|
4267
|
+
* Normalized to uppercase (the wire value is forwarded verbatim as the header) so a case-sensitive
|
|
4268
|
+
* engine lookup can't silently miss a lowercase code — `adsEngine.ts` uppercases for the same reason.
|
|
4269
|
+
*/
|
|
4270
|
+
country: z28.string().max(REQUEST_BOUNDS.STRING_ID_MAX).regex(/^[A-Za-z]{2}$/, "must be an ISO 3166-1 alpha-2 country code (e.g. CA)").transform((value) => value.toUpperCase()).optional(),
|
|
4250
4271
|
customerId: z28.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4251
4272
|
dataSourcePath: z28.string().max(REQUEST_BOUNDS.STRING_KEY_MAX),
|
|
4252
4273
|
/**
|
|
@@ -4258,6 +4279,14 @@ var DataSourceInputSchema = z28.object({
|
|
|
4258
4279
|
integrations: z28.record(z28.string().max(REQUEST_BOUNDS.STRING_KEY_MAX), z28.boolean()).refine((record) => Object.keys(record).length <= REQUEST_BOUNDS.INTEGRATION_KEYS_MAX, {
|
|
4259
4280
|
message: `Too many integration keys (max ${REQUEST_BOUNDS.INTEGRATION_KEYS_MAX})`
|
|
4260
4281
|
}).optional(),
|
|
4282
|
+
/**
|
|
4283
|
+
* Buyer's checkout language (BCP 47) — forwarded to the engine as the `Accept-Language` HEADER
|
|
4284
|
+
* (REB-21102); regex-bound for the same header-sink reason as `country` above. MUST be a single tag
|
|
4285
|
+
* (e.g. `fr-CA`) — a q-value list like `fr-CA,fr;q=0.9` (the raw shape of a browser `Accept-Language`
|
|
4286
|
+
* request header) fails the regex. Callers should send the buyer's resolved locale (e.g. Shopify
|
|
4287
|
+
* checkout's `localization.language.isoCode`), not the request header verbatim.
|
|
4288
|
+
*/
|
|
4289
|
+
language: z28.string().max(REQUEST_BOUNDS.STRING_ID_MAX).regex(/^[A-Za-z0-9-]+$/, "must be a single BCP-47 tag (e.g. fr-CA), not a q-value list").optional(),
|
|
4261
4290
|
limit: z28.number().max(REQUEST_BOUNDS.LIMIT_MAX),
|
|
4262
4291
|
productType: z28.enum(["both", "one-time", "subscription"]).optional(),
|
|
4263
4292
|
shop: z28.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
|
|
@@ -4272,8 +4301,19 @@ var GiftInputSchema = z28.object({
|
|
|
4272
4301
|
});
|
|
4273
4302
|
var GiftValidationInputSchema = z28.object({
|
|
4274
4303
|
cart: CartInputSchema,
|
|
4304
|
+
/**
|
|
4305
|
+
* Buyer's market country — threaded into the per-widget data-source evaluations (REB-21102), which
|
|
4306
|
+
* forward it to a header sink; regex-bound and uppercase-normalized for the same reasons as
|
|
4307
|
+
* `DataSourceInputSchema`'s `country`.
|
|
4308
|
+
*/
|
|
4309
|
+
country: z28.string().max(REQUEST_BOUNDS.STRING_ID_MAX).regex(/^[A-Za-z]{2}$/, "must be an ISO 3166-1 alpha-2 country code (e.g. CA)").transform((value) => value.toUpperCase()).optional(),
|
|
4275
4310
|
customerId: z28.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
|
|
4276
4311
|
gifts: z28.array(GiftInputSchema).max(REQUEST_BOUNDS.GIFTS_MAX),
|
|
4312
|
+
/**
|
|
4313
|
+
* Buyer's checkout language — threaded into the per-widget data-source evaluations (REB-21102). MUST
|
|
4314
|
+
* be a single BCP-47 tag, for the same reason as `DataSourceInputSchema`'s `language` above.
|
|
4315
|
+
*/
|
|
4316
|
+
language: z28.string().max(REQUEST_BOUNDS.STRING_ID_MAX).regex(/^[A-Za-z0-9-]+$/, "must be a single BCP-47 tag (e.g. fr-CA), not a q-value list").optional(),
|
|
4277
4317
|
shop: z28.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
|
|
4278
4318
|
visitorId: z28.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
|
|
4279
4319
|
});
|
|
@@ -114,6 +114,7 @@ export declare const DataSourceInputSchema: z.ZodObject<{
|
|
|
114
114
|
subtotal: z.ZodNumber;
|
|
115
115
|
}, z.core.$strip>;
|
|
116
116
|
companyLocationId: z.ZodOptional<z.ZodString>;
|
|
117
|
+
country: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
117
118
|
customerId: z.ZodOptional<z.ZodString>;
|
|
118
119
|
dataSourcePath: z.ZodString;
|
|
119
120
|
filterInputs: z.ZodOptional<z.ZodEnum<{
|
|
@@ -121,6 +122,7 @@ export declare const DataSourceInputSchema: z.ZodObject<{
|
|
|
121
122
|
yes: "yes";
|
|
122
123
|
}>>;
|
|
123
124
|
integrations: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodBoolean>>;
|
|
125
|
+
language: z.ZodOptional<z.ZodString>;
|
|
124
126
|
limit: z.ZodNumber;
|
|
125
127
|
productType: z.ZodOptional<z.ZodEnum<{
|
|
126
128
|
"one-time": "one-time";
|
|
@@ -160,6 +162,7 @@ export declare const GiftValidationInputSchema: z.ZodObject<{
|
|
|
160
162
|
}, z.core.$strip>>;
|
|
161
163
|
subtotal: z.ZodNumber;
|
|
162
164
|
}, z.core.$strip>;
|
|
165
|
+
country: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
163
166
|
customerId: z.ZodOptional<z.ZodString>;
|
|
164
167
|
gifts: z.ZodArray<z.ZodObject<{
|
|
165
168
|
cost: z.ZodNumber;
|
|
@@ -168,6 +171,7 @@ export declare const GiftValidationInputSchema: z.ZodObject<{
|
|
|
168
171
|
productId: z.ZodNumber;
|
|
169
172
|
widgetId: z.ZodNumber;
|
|
170
173
|
}, z.core.$strip>>;
|
|
174
|
+
language: z.ZodOptional<z.ZodString>;
|
|
171
175
|
shop: z.ZodString;
|
|
172
176
|
visitorId: z.ZodOptional<z.ZodString>;
|
|
173
177
|
}, z.core.$strip>;
|
package/dist/server/shared.d.ts
CHANGED
|
@@ -10,8 +10,10 @@ export declare const REBUY_ENGINE_HOST = "rebuyengine.com";
|
|
|
10
10
|
* caller (rebuy-api owns the egress, timeouts, and the `X-Rebuy-Upstream` proxy header); MUST throw
|
|
11
11
|
* {@link UpstreamError} on a non-2xx response so orchestrators can interpret the status.
|
|
12
12
|
*
|
|
13
|
-
* Defaults to GET with `params`/`search` as the query string.
|
|
14
|
-
*
|
|
13
|
+
* Defaults to GET with `params`/`search` as the query string; `headers` (e.g. an auth token, or the
|
|
14
|
+
* market-context headers `fetchDataSourceResults` sends, REB-21102) apply on GET too — implementations
|
|
15
|
+
* must not gate `headers` behind a `method === 'POST'` branch. Pass `method: 'POST'` with `body` (JSON
|
|
16
|
+
* body) for write-style calls like analytics ingest.
|
|
15
17
|
*/
|
|
16
18
|
export type UpstreamFetch = (path: string, params: Record<string, string>, options?: {
|
|
17
19
|
body?: unknown;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebuy/rebuy",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Shared zod schemas, legacy-to-CAB widget transforms, and server orchestrators for Rebuy's Shopify consumers (rebuy-api, admin-nextjs, rebuy-shopify-extensions)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Rebuy, Inc.",
|