@rebuy/rebuy 3.17.0 → 3.18.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.
@@ -25,7 +25,7 @@ declare const TAXONOMY: {
25
25
  element_id: string;
26
26
  experiment_type: string;
27
27
  };
28
- buildTags: ({ experimentId }: Record<string, unknown>) => string[] | undefined;
28
+ buildTags: ({ experimentId }: Record<string, unknown>, input: AnalyticsInput) => string[] | undefined;
29
29
  noun: "cart";
30
30
  subject: "abtest";
31
31
  usesWidgetId: false;
@@ -37,7 +37,7 @@ declare const TAXONOMY: {
37
37
  buildMeta: ({ productId, variantId }: Record<string, unknown>) => {
38
38
  [x: string]: true;
39
39
  };
40
- buildTags: ({ experimentId }: Record<string, unknown>) => string[] | undefined;
40
+ buildTags: ({ experimentId }: Record<string, unknown>, input: AnalyticsInput) => string[] | undefined;
41
41
  /** Under an experiment the add belongs to the VARIANT element, not the placeholder (React parity). */
42
42
  buildWidgetId: ({ elementId }: Record<string, unknown>, input: {
43
43
  events: {
@@ -49,6 +49,11 @@ declare const TAXONOMY: {
49
49
  visitorId: string;
50
50
  cartToken?: string | undefined;
51
51
  checkoutToken?: string | undefined;
52
+ segments?: {
53
+ device?: string | undefined;
54
+ source?: string | undefined;
55
+ visitor?: string | undefined;
56
+ } | undefined;
52
57
  widgetId?: string | undefined;
53
58
  }) => string | undefined;
54
59
  noun: "widget";
@@ -77,7 +82,7 @@ declare const TAXONOMY: {
77
82
  verb: "tracking";
78
83
  };
79
84
  'widget-viewed': {
80
- buildTags: ({ experimentId }: Record<string, unknown>) => string[] | undefined;
85
+ buildTags: ({ experimentId }: Record<string, unknown>, input: AnalyticsInput) => string[] | undefined;
81
86
  /** Under an experiment the view belongs to the VARIANT element, not the placeholder (React parity, same as added-to-cart) — else views credit the placeholder while adds credit the variant and per-widget conversion is meaningless. */
82
87
  buildWidgetId: ({ elementId }: Record<string, unknown>, input: {
83
88
  events: {
@@ -89,6 +94,11 @@ declare const TAXONOMY: {
89
94
  visitorId: string;
90
95
  cartToken?: string | undefined;
91
96
  checkoutToken?: string | undefined;
97
+ segments?: {
98
+ device?: string | undefined;
99
+ source?: string | undefined;
100
+ visitor?: string | undefined;
101
+ } | undefined;
92
102
  widgetId?: string | undefined;
93
103
  }) => string | undefined;
94
104
  noun: "widget";
@@ -0,0 +1,6 @@
1
+ import { type AnalyticsSegmentsInput } from '../server/requestSchemas';
2
+ /**
3
+ * One `<baseTag>.<dimension>.<bucket>` tag per segment whose bucket passes {@link SEGMENT_BUCKETS}; invalid
4
+ * or absent dimensions contribute nothing (so valid siblings still ride along).
5
+ */
6
+ export declare const segmentTags: (baseTag: string, segments: AnalyticsSegmentsInput | undefined) => string[];
@@ -22,6 +22,7 @@ var server_exports = {};
22
22
  __export(server_exports, {
23
23
  AnalyticsEventInputSchema: () => AnalyticsEventInputSchema,
24
24
  AnalyticsInputSchema: () => AnalyticsInputSchema,
25
+ AnalyticsSegmentsInputSchema: () => AnalyticsSegmentsInputSchema,
25
26
  CartInputSchema: () => CartInputSchema,
26
27
  CartLineInputSchema: () => CartLineInputSchema,
27
28
  DataSourceInputSchema: () => DataSourceInputSchema,
@@ -4542,6 +4543,19 @@ var validateGifts = async (input, ctx) => {
4542
4543
 
4543
4544
  // src/server/analytics.ts
4544
4545
  var import_es_toolkit7 = require("es-toolkit");
4546
+
4547
+ // src/server/analyticsSegments.ts
4548
+ var SEGMENT_BUCKETS = {
4549
+ device: ["desktop", "mobile"],
4550
+ source: ["direct", "email", "google_ads", "meta_social", "other", "sms"],
4551
+ visitor: ["new", "returning"]
4552
+ };
4553
+ var segmentTags = (baseTag, segments) => Object.entries(SEGMENT_BUCKETS).flatMap(([dimension, buckets]) => {
4554
+ const bucket = segments?.[dimension];
4555
+ return bucket !== void 0 && buckets.includes(bucket) ? [`${baseTag}.${dimension}.${bucket}`] : [];
4556
+ });
4557
+
4558
+ // src/server/analytics.ts
4545
4559
  var EXPERIMENT_TYPE_MAP = {
4546
4560
  CheckoutOffer: "checkout_offer",
4547
4561
  Generic: "generic",
@@ -4549,7 +4563,11 @@ var EXPERIMENT_TYPE_MAP = {
4549
4563
  SmartCart: "smart_cart",
4550
4564
  Widget: "widget"
4551
4565
  };
4552
- var experimentTags = ({ experimentId }) => experimentId != null ? [`ABN.TEST.${experimentId}`] : void 0;
4566
+ var experimentTags = ({ experimentId }, input) => {
4567
+ if (experimentId == null) return void 0;
4568
+ const baseTag = `ABN.TEST.${experimentId}`;
4569
+ return [baseTag, ...segmentTags(baseTag, input.segments)];
4570
+ };
4553
4571
  var TAXONOMY = {
4554
4572
  "ab-test-viewed": {
4555
4573
  // Only when BOTH ids are present — otherwise the template literal would emit "undefined.undefined".
@@ -4618,7 +4636,7 @@ var translateAnalyticsEvent = (event, input) => {
4618
4636
  const taxonomy = TAXONOMY[event.name];
4619
4637
  const properties = event.properties ?? {};
4620
4638
  const meta = taxonomy.buildMeta?.(properties);
4621
- const tags = taxonomy.buildTags?.(properties);
4639
+ const tags = taxonomy.buildTags?.(properties, input);
4622
4640
  const definedNounId = taxonomy.buildDefinedNounId?.(properties);
4623
4641
  const widgetId = taxonomy.buildWidgetId?.(properties, input) ?? input.widgetId;
4624
4642
  const eventTime = toEngineEventTime(event.sentAt);
@@ -4886,12 +4904,23 @@ var AnalyticsEventInputSchema = import_zod30.z.object({
4886
4904
  properties: import_zod30.z.record(import_zod30.z.string().max(REQUEST_BOUNDS.STRING_KEY_MAX), import_zod30.z.unknown()).optional(),
4887
4905
  sentAt: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
4888
4906
  });
4907
+ var AnalyticsSegmentsInputSchema = import_zod30.z.object({
4908
+ device: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional().catch(void 0),
4909
+ source: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional().catch(void 0),
4910
+ visitor: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional().catch(void 0)
4911
+ });
4889
4912
  var AnalyticsInputSchema = import_zod30.z.object({
4890
4913
  /** Rebuy cart token (the `r_cart_token` cart attribute), when available — primary aggregation key. */
4891
4914
  cartToken: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
4892
4915
  /** Shopify checkout token, when available — drives cart/checkout aggregation downstream. */
4893
4916
  checkoutToken: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
4894
4917
  events: import_zod30.z.array(AnalyticsEventInputSchema).max(REQUEST_BOUNDS.EVENTS_MAX),
4918
+ /**
4919
+ * Request-scoped like the tokens: one visitor's segments apply to every event in the batch. The container
4920
+ * degrades too (`null` is a realistic wire artifact of `attributes ? {…} : null`), so the degrade rule is
4921
+ * total: nothing about `segments` can cost the envelope.
4922
+ */
4923
+ segments: AnalyticsSegmentsInputSchema.optional().catch(void 0),
4895
4924
  shop: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
4896
4925
  visitorId: import_zod30.z.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
4897
4926
  /** The widget id (smartBlockId) the events belong to. */
@@ -14,5 +14,5 @@ export { fetchUserConfig } from '../server/userConfig';
14
14
  export { validateGifts } from '../server/giftValidation';
15
15
  export { fetchMonetizeOffers, type MonetizeOffers } from '../server/monetizeOffers';
16
16
  export { postAnalyticsEvents, translateAnalyticsEvent, type CabAnalyticsEventName } from '../server/analytics';
17
- export { AnalyticsEventInputSchema, AnalyticsInputSchema, CartInputSchema, CartLineInputSchema, DataSourceInputSchema, GiftAdjustmentSchema, GiftInputSchema, GiftValidationInputSchema, GiftValidationResponseSchema, MonetizeInputSchema, MonetizeTransactionItemSchema, REQUEST_BOUNDS, UserConfigInputSchema, WidgetSettingsInputSchema, } from '../server/requestSchemas';
18
- export { NotFoundError, REBUY_ENGINE_HOST, UpstreamError, type AnalyticsEventInput, type AnalyticsInput, type CartInput, type CartLineInput, type DataSourceInput, type GiftAdjustment, type GiftInput, type GiftValidationInput, type GiftValidationResponse, type MonetizeInput, type MonetizeTransactionItem, type ServerContext, type ServerDrift, type UpstreamFetch, type UserConfigInput, type WidgetSettingsInput, } from '../server/shared';
17
+ export { AnalyticsEventInputSchema, AnalyticsInputSchema, AnalyticsSegmentsInputSchema, CartInputSchema, CartLineInputSchema, DataSourceInputSchema, GiftAdjustmentSchema, GiftInputSchema, GiftValidationInputSchema, GiftValidationResponseSchema, MonetizeInputSchema, MonetizeTransactionItemSchema, REQUEST_BOUNDS, UserConfigInputSchema, WidgetSettingsInputSchema, } from '../server/requestSchemas';
18
+ export { NotFoundError, REBUY_ENGINE_HOST, UpstreamError, type AnalyticsEventInput, type AnalyticsInput, type AnalyticsSegmentsInput, type CartInput, type CartLineInput, type DataSourceInput, type GiftAdjustment, type GiftInput, type GiftValidationInput, type GiftValidationResponse, type MonetizeInput, type MonetizeTransactionItem, type ServerContext, type ServerDrift, type UpstreamFetch, type UserConfigInput, type WidgetSettingsInput, } from '../server/shared';
@@ -4492,6 +4492,19 @@ var validateGifts = async (input, ctx) => {
4492
4492
 
4493
4493
  // src/server/analytics.ts
4494
4494
  import { chunk } from "es-toolkit";
4495
+
4496
+ // src/server/analyticsSegments.ts
4497
+ var SEGMENT_BUCKETS = {
4498
+ device: ["desktop", "mobile"],
4499
+ source: ["direct", "email", "google_ads", "meta_social", "other", "sms"],
4500
+ visitor: ["new", "returning"]
4501
+ };
4502
+ var segmentTags = (baseTag, segments) => Object.entries(SEGMENT_BUCKETS).flatMap(([dimension, buckets]) => {
4503
+ const bucket = segments?.[dimension];
4504
+ return bucket !== void 0 && buckets.includes(bucket) ? [`${baseTag}.${dimension}.${bucket}`] : [];
4505
+ });
4506
+
4507
+ // src/server/analytics.ts
4495
4508
  var EXPERIMENT_TYPE_MAP = {
4496
4509
  CheckoutOffer: "checkout_offer",
4497
4510
  Generic: "generic",
@@ -4499,7 +4512,11 @@ var EXPERIMENT_TYPE_MAP = {
4499
4512
  SmartCart: "smart_cart",
4500
4513
  Widget: "widget"
4501
4514
  };
4502
- var experimentTags = ({ experimentId }) => experimentId != null ? [`ABN.TEST.${experimentId}`] : void 0;
4515
+ var experimentTags = ({ experimentId }, input) => {
4516
+ if (experimentId == null) return void 0;
4517
+ const baseTag = `ABN.TEST.${experimentId}`;
4518
+ return [baseTag, ...segmentTags(baseTag, input.segments)];
4519
+ };
4503
4520
  var TAXONOMY = {
4504
4521
  "ab-test-viewed": {
4505
4522
  // Only when BOTH ids are present — otherwise the template literal would emit "undefined.undefined".
@@ -4568,7 +4585,7 @@ var translateAnalyticsEvent = (event, input) => {
4568
4585
  const taxonomy = TAXONOMY[event.name];
4569
4586
  const properties = event.properties ?? {};
4570
4587
  const meta = taxonomy.buildMeta?.(properties);
4571
- const tags = taxonomy.buildTags?.(properties);
4588
+ const tags = taxonomy.buildTags?.(properties, input);
4572
4589
  const definedNounId = taxonomy.buildDefinedNounId?.(properties);
4573
4590
  const widgetId = taxonomy.buildWidgetId?.(properties, input) ?? input.widgetId;
4574
4591
  const eventTime = toEngineEventTime(event.sentAt);
@@ -4836,12 +4853,23 @@ var AnalyticsEventInputSchema = z30.object({
4836
4853
  properties: z30.record(z30.string().max(REQUEST_BOUNDS.STRING_KEY_MAX), z30.unknown()).optional(),
4837
4854
  sentAt: z30.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional()
4838
4855
  });
4856
+ var AnalyticsSegmentsInputSchema = z30.object({
4857
+ device: z30.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional().catch(void 0),
4858
+ source: z30.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional().catch(void 0),
4859
+ visitor: z30.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional().catch(void 0)
4860
+ });
4839
4861
  var AnalyticsInputSchema = z30.object({
4840
4862
  /** Rebuy cart token (the `r_cart_token` cart attribute), when available — primary aggregation key. */
4841
4863
  cartToken: z30.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
4842
4864
  /** Shopify checkout token, when available — drives cart/checkout aggregation downstream. */
4843
4865
  checkoutToken: z30.string().max(REQUEST_BOUNDS.STRING_ID_MAX).optional(),
4844
4866
  events: z30.array(AnalyticsEventInputSchema).max(REQUEST_BOUNDS.EVENTS_MAX),
4867
+ /**
4868
+ * Request-scoped like the tokens: one visitor's segments apply to every event in the batch. The container
4869
+ * degrades too (`null` is a realistic wire artifact of `attributes ? {…} : null`), so the degrade rule is
4870
+ * total: nothing about `segments` can cost the envelope.
4871
+ */
4872
+ segments: AnalyticsSegmentsInputSchema.optional().catch(void 0),
4845
4873
  shop: z30.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
4846
4874
  visitorId: z30.string().max(REQUEST_BOUNDS.STRING_ID_MAX),
4847
4875
  /** The widget id (smartBlockId) the events belong to. */
@@ -4850,6 +4878,7 @@ var AnalyticsInputSchema = z30.object({
4850
4878
  export {
4851
4879
  AnalyticsEventInputSchema,
4852
4880
  AnalyticsInputSchema,
4881
+ AnalyticsSegmentsInputSchema,
4853
4882
  CartInputSchema,
4854
4883
  CartLineInputSchema,
4855
4884
  DataSourceInputSchema,
@@ -331,6 +331,22 @@ export declare const AnalyticsEventInputSchema: z.ZodObject<{
331
331
  properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
332
332
  sentAt: z.ZodOptional<z.ZodString>;
333
333
  }, z.core.$strip>;
334
+ /**
335
+ * The visitor's A/B segment buckets, RAW as onsite-js wrote them into the `_r_segment_{device,source,visitor}`
336
+ * cart attributes (the checkout sandbox can't compute them itself). Deliberately unvalidated here: the
337
+ * bucket allow-list lives in the tag translation (`analyticsSegments.ts`) so a stray value drops ONE tag,
338
+ * never the whole batch — and the extension bundle never has to carry a copy of the list. Cart attributes
339
+ * are shopper-writable, so even the size bound must degrade rather than fail: `.catch(undefined)` (the
340
+ * `cacheKey` precedent) turns an over-limit or non-string value into a missing segment instead of a
341
+ * rejected envelope. `STRING_ID_MAX` (not the fat `STRING_VALUE_MAX`) because every valid bucket is a
342
+ * short identifier — anything longer can only be junk, and the bound is a payload guard, not validation.
343
+ * Only present keys are sent; the client omits an absent attribute rather than sending an empty string.
344
+ */
345
+ export declare const AnalyticsSegmentsInputSchema: z.ZodObject<{
346
+ device: z.ZodCatch<z.ZodOptional<z.ZodString>>;
347
+ source: z.ZodCatch<z.ZodOptional<z.ZodString>>;
348
+ visitor: z.ZodCatch<z.ZodOptional<z.ZodString>>;
349
+ }, z.core.$strip>;
334
350
  /** Input for `postAnalyticsEvents`: a batch of events plus the request-scoped identity fields. */
335
351
  export declare const AnalyticsInputSchema: z.ZodObject<{
336
352
  cartToken: z.ZodOptional<z.ZodString>;
@@ -340,12 +356,18 @@ export declare const AnalyticsInputSchema: z.ZodObject<{
340
356
  properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
341
357
  sentAt: z.ZodOptional<z.ZodString>;
342
358
  }, z.core.$strip>>;
359
+ segments: z.ZodCatch<z.ZodOptional<z.ZodObject<{
360
+ device: z.ZodCatch<z.ZodOptional<z.ZodString>>;
361
+ source: z.ZodCatch<z.ZodOptional<z.ZodString>>;
362
+ visitor: z.ZodCatch<z.ZodOptional<z.ZodString>>;
363
+ }, z.core.$strip>>>;
343
364
  shop: z.ZodString;
344
365
  visitorId: z.ZodString;
345
366
  widgetId: z.ZodOptional<z.ZodString>;
346
367
  }, z.core.$strip>;
347
368
  export type AnalyticsEventInput = z.infer<typeof AnalyticsEventInputSchema>;
348
369
  export type AnalyticsInput = z.infer<typeof AnalyticsInputSchema>;
370
+ export type AnalyticsSegmentsInput = z.infer<typeof AnalyticsSegmentsInputSchema>;
349
371
  export type CartInput = z.infer<typeof CartInputSchema>;
350
372
  export type CartLineInput = z.infer<typeof CartLineInputSchema>;
351
373
  export type DataSourceInput = z.infer<typeof DataSourceInputSchema>;
@@ -70,4 +70,4 @@ export declare class NotFoundError extends Error {
70
70
  * requests against the exact contract the orchestrators are typed on. Kept re-exported here so the
71
71
  * `@rebuy/rebuy/server` type surface is unchanged.
72
72
  */
73
- export type { AnalyticsEventInput, AnalyticsInput, CartInput, CartLineInput, DataSourceInput, GiftAdjustment, GiftInput, GiftValidationInput, GiftValidationResponse, MonetizeInput, MonetizeTransactionItem, UserConfigInput, WidgetSettingsInput, } from '../server/requestSchemas';
73
+ export type { AnalyticsEventInput, AnalyticsInput, AnalyticsSegmentsInput, CartInput, CartLineInput, DataSourceInput, GiftAdjustment, GiftInput, GiftValidationInput, GiftValidationResponse, MonetizeInput, MonetizeTransactionItem, UserConfigInput, WidgetSettingsInput, } from '../server/requestSchemas';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rebuy/rebuy",
3
- "version": "3.17.0",
3
+ "version": "3.18.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.",