@zappie/sdk 0.2.0 → 0.3.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.
Files changed (129) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cart/branch.js.map +0 -1
  3. package/dist/cart/errors.js.map +0 -1
  4. package/dist/cart/index.js.map +0 -1
  5. package/dist/cart/item.d.ts +1 -1
  6. package/dist/cart/item.js.map +0 -1
  7. package/dist/cart/preview.js.map +0 -1
  8. package/dist/cart/price.js.map +0 -1
  9. package/dist/cart/quote-client.js.map +0 -1
  10. package/dist/cart/quote-response.js.map +0 -1
  11. package/dist/cart/quote.d.ts +1 -1
  12. package/dist/cart/reconcile.js.map +0 -1
  13. package/dist/cart/source-menu.js.map +0 -1
  14. package/dist/cart/storage.js.map +0 -1
  15. package/dist/cart/types.js.map +0 -1
  16. package/dist/cart/validate.js.map +0 -1
  17. package/dist/cart/wire.js.map +0 -1
  18. package/dist/cart/working-set.js.map +0 -1
  19. package/dist/checkout/client.js.map +0 -1
  20. package/dist/checkout/embed.js.map +0 -1
  21. package/dist/checkout/error.js.map +0 -1
  22. package/dist/checkout/index.js.map +0 -1
  23. package/dist/checkout/react.js.map +0 -1
  24. package/dist/checkout/readiness.js.map +0 -1
  25. package/dist/checkout/sdk.js.map +0 -1
  26. package/dist/checkout/session.js.map +0 -1
  27. package/dist/checkout/types.js.map +0 -1
  28. package/dist/checkout-defaults/index.js.map +0 -1
  29. package/dist/{client-B377AzNC.d.ts → client-DHxakZrv.d.ts} +2 -2
  30. package/dist/client.js.map +0 -1
  31. package/dist/config/index.js.map +0 -1
  32. package/dist/config.js.map +0 -1
  33. package/dist/customer/client.js.map +0 -1
  34. package/dist/customer/error.js.map +0 -1
  35. package/dist/customer/index.js.map +0 -1
  36. package/dist/customer/navigation.js.map +0 -1
  37. package/dist/customer/react.js.map +0 -1
  38. package/dist/customer/request.js.map +0 -1
  39. package/dist/customer/sdk.js.map +0 -1
  40. package/dist/customer/types.js.map +0 -1
  41. package/dist/customer/watch-order.js.map +0 -1
  42. package/dist/debug.js +1 -1
  43. package/dist/debug.js.map +0 -1
  44. package/dist/errors.js.map +0 -1
  45. package/dist/index.d.ts +6 -6
  46. package/dist/index.js +1 -1
  47. package/dist/index.js.map +0 -1
  48. package/dist/integrations/index.d.ts +2 -2
  49. package/dist/integrations/index.js.map +0 -1
  50. package/dist/internal/abort.js.map +0 -1
  51. package/dist/internal/cache.js.map +0 -1
  52. package/dist/internal/customer-http.js.map +0 -1
  53. package/dist/internal/digest.js.map +0 -1
  54. package/dist/internal/emitter.js.map +0 -1
  55. package/dist/internal/money-math.js.map +0 -1
  56. package/dist/internal/return-path.js.map +0 -1
  57. package/dist/internal.d.ts +2 -2
  58. package/dist/internal.js.map +0 -1
  59. package/dist/{models-tf9rNoki.d.ts → models-C-kEg1Id.d.ts} +1 -1
  60. package/dist/models.js.map +0 -1
  61. package/dist/money.js.map +0 -1
  62. package/dist/persist/catalogue.d.ts +3 -3
  63. package/dist/persist/catalogue.js.map +0 -1
  64. package/dist/places/client.js.map +0 -1
  65. package/dist/places/error.js.map +0 -1
  66. package/dist/places/index.js.map +0 -1
  67. package/dist/places/request.js.map +0 -1
  68. package/dist/places/sdk.js.map +0 -1
  69. package/dist/places/search.js.map +0 -1
  70. package/dist/places/types.js.map +0 -1
  71. package/dist/pricing/index.js.map +0 -1
  72. package/dist/react/cart.js.map +0 -1
  73. package/dist/react/hooks.js.map +0 -1
  74. package/dist/react/index.js.map +0 -1
  75. package/dist/react/provider.js.map +0 -1
  76. package/dist/redirect/index.js.map +0 -1
  77. package/dist/server/auth/crypto.js.map +0 -1
  78. package/dist/server/auth/jwt.js.map +0 -1
  79. package/dist/server/auth/session-store.js.map +0 -1
  80. package/dist/server/edge-snapshot.js.map +0 -1
  81. package/dist/server/index.d.ts +1 -1
  82. package/dist/server/index.js.map +0 -1
  83. package/dist/server/security.js.map +0 -1
  84. package/dist/server/storefront-url/build-v1.js.map +0 -1
  85. package/dist/server/storefront-url/build-v2.js.map +0 -1
  86. package/dist/server/storefront-url/build.js.map +0 -1
  87. package/dist/server/storefront-url/canonical.js.map +0 -1
  88. package/dist/server/storefront-url/contract.js.map +0 -1
  89. package/dist/server/storefront-url/index.js.map +0 -1
  90. package/dist/server/storefront-url/installation.js.map +0 -1
  91. package/dist/server/storefront-url/params.js.map +0 -1
  92. package/dist/server/storefront-url/parse-v1.js.map +0 -1
  93. package/dist/server/storefront-url/parse-v2.js.map +0 -1
  94. package/dist/server/storefront-url/parse.js.map +0 -1
  95. package/dist/server/storefront-url/public-id.js.map +0 -1
  96. package/dist/server/storefront-url/redirect.js.map +0 -1
  97. package/dist/server/storefront-url/sign-in.js.map +0 -1
  98. package/dist/server/storefront-url/types.js.map +0 -1
  99. package/dist/server/storefront-url/url.js.map +0 -1
  100. package/dist/server/webhook.js.map +0 -1
  101. package/dist/services/index.d.ts +21 -0
  102. package/dist/services/index.js +24 -0
  103. package/dist/services/index.js.map +6 -0
  104. package/dist/site-config/helpers.js.map +0 -1
  105. package/dist/site-config/sections.js.map +0 -1
  106. package/dist/site-config/types.js.map +0 -1
  107. package/dist/site-config/validate.js.map +0 -1
  108. package/dist/template/index.d.ts +2 -2
  109. package/dist/template/index.js +1 -1
  110. package/dist/template/index.js.map +1 -2
  111. package/dist/template/react.js.map +0 -1
  112. package/dist/testing/index.d.ts +1 -1
  113. package/dist/testing/index.js +2 -0
  114. package/dist/transport/category-page.js.map +0 -1
  115. package/dist/transport/mappers.js.map +0 -1
  116. package/dist/transport/presentation.js.map +0 -1
  117. package/dist/transport/quote-body.js.map +0 -1
  118. package/dist/transport/rest.js.map +0 -1
  119. package/dist/transport/search.d.ts +1 -1
  120. package/dist/transport/search.js.map +0 -1
  121. package/dist/transport/selected-menu.d.ts +3 -3
  122. package/dist/transport/selected-menu.js.map +0 -1
  123. package/dist/transport/store.js.map +0 -1
  124. package/dist/transport/types.js.map +0 -1
  125. package/dist/{types-B3Ghmpmy.d.ts → types-CxY5C6AN.d.ts} +1 -1
  126. package/dist/url/index.js.map +0 -1
  127. package/dist/visits/index.js.map +0 -1
  128. package/dist/visits/page-views.js.map +0 -1
  129. package/package.json +6 -2
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/transport/selected-menu.ts"],
4
- "sourcesContent": ["import type { Zappie } from \"../client\";\nimport type { Menu } from \"../models\";\nimport type { CatalogueScope } from \"../persist/catalogue\";\nimport type { RequestCtx } from \"./types\";\n\n/** Complete for the named source, never complete for the branch. */\nexport type SelectedMenu = {\n menu: Menu & { completeness: \"partial\" };\n coverage: { menuIds: [string]; complete: true };\n};\n/**\n * A source menu with its ETag, or an unchanged response carrying the matching ETag.\n */\nexport type SelectedMenuResponse =\n (SelectedMenu & { etag: string }) | { notModified: true; etag: string };\n/**\n * A branch-scoped source-menu request with order context and an optional conditional ETag.\n */\nexport type SelectedMenuRequest = RequestCtx & {\n branchId: string;\n menuId: string;\n orderContext: CatalogueScope[\"orderContext\"];\n ifNoneMatch?: string;\n};\n/**\n * A transport that reads one complete source menu without claiming branch-wide coverage.\n */\nexport type SelectedMenuTransport = {\n getSelectedMenu(request: SelectedMenuRequest): Promise<SelectedMenuResponse>;\n};\n/** Dependencies are passed by REST to avoid splitting core shared chunks. */\nexport async function selectedMenu(\n ctx: SelectedMenuRequest,\n request: (path: string) => Promise<Response>,\n json: (response: Response) => Promise<unknown>,\n mapMenu: (value: unknown, branchId: string) => Menu,\n invalid: () => Error,\n): Promise<SelectedMenuResponse> {\n const presentation = import(\"./presentation\");\n // Checked here rather than passed in: every argument REST passes costs the\n // core bytes, and the core has none to spare.\n const record = (value: unknown) => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw invalid();\n }\n return value as Record<string, unknown>;\n };\n const query = new URLSearchParams({\n branch: ctx.branchId,\n fulfilment: ctx.orderContext === \"dineIn\" ? \"dine_in\" : ctx.orderContext,\n });\n const response = await request(\n `/menus/${encodeURIComponent(ctx.menuId)}?${query}`,\n );\n const etag = response.headers.get(\"ETag\");\n if (!etag) {\n throw invalid();\n }\n if (response.status === 304) {\n return { notModified: true, etag };\n }\n const raw = record(await json(response));\n const coverage = record(raw.coverage);\n const menu = (await presentation).presentMenu(\n mapMenu(raw, ctx.branchId),\n raw,\n invalid,\n );\n if (\n raw.next_cursor ||\n coverage.complete !== true ||\n !Array.isArray(coverage.menu_ids) ||\n coverage.menu_ids.length !== 1 ||\n coverage.menu_ids[0] !== ctx.menuId ||\n menu.menus?.length !== 1 ||\n menu.menus[0]?.id !== ctx.menuId\n ) {\n throw invalid();\n }\n return {\n menu: { ...menu, completeness: \"partial\" },\n coverage: { menuIds: [ctx.menuId], complete: true },\n etag,\n };\n}\n/**\n * Build a versioned cache key for a source menu within a store, branch and order context.\n */\nexport function selectedMenuKey(scope: CatalogueScope) {\n return JSON.stringify([\n \"selected-menu:v1\",\n scope.store,\n scope.branchId,\n scope.orderContext,\n scope.menuId,\n ]);\n}\n/** Dedicated lazy resource: deliberately does not call menu.hydrate(). */\nexport function createSelectedMenuReader(client: Zappie) {\n const transport = client._internal.transport;\n return {\n async read(\n scope: CatalogueScope,\n options: { signal?: AbortSignal; ifNoneMatch?: string } = {},\n ) {\n if (!transport.getSelectedMenu) {\n throw new Error(\"Transport does not support selected menus\");\n }\n const response = await transport.getSelectedMenu({\n key: client.config.key,\n version: client.config.version,\n branchId: scope.branchId,\n menuId: scope.menuId,\n orderContext: scope.orderContext,\n ...options,\n });\n options.signal?.throwIfAborted();\n if (!(\"notModified\" in response)) {\n if (\n response.menu.branchId !== scope.branchId ||\n response.coverage.menuIds.length !== 1 ||\n response.coverage.menuIds[0] !== scope.menuId\n ) {\n throw new Error(\"Selected menu belongs to another scope\");\n }\n const key = selectedMenuKey(scope);\n const previous =\n client._internal.cache.snapshot<SelectedMenuResponse>(key).value;\n if (JSON.stringify(previous) !== JSON.stringify(response)) {\n client._internal.cache.set(key, response, response.etag);\n }\n }\n return response;\n },\n };\n}\n"],
5
4
  "mappings": ";AA+BA,eAAsB,aACpB,KACA,SACA,MACA,SACA,SAC+B;AAC/B,QAAM,eAAe,OAAO,mBAAgB;AAG5C,QAAM,SAAS,CAAC,UAAmB;AACjC,QAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;AAC/D,YAAM,QAAQ;AAAA,IAChB;AACA,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,IAAI,gBAAgB;AAAA,IAChC,QAAQ,IAAI;AAAA,IACZ,YAAY,IAAI,iBAAiB,WAAW,YAAY,IAAI;AAAA,EAC9D,CAAC;AACD,QAAM,WAAW,MAAM;AAAA,IACrB,UAAU,mBAAmB,IAAI,MAAM,CAAC,IAAI,KAAK;AAAA,EACnD;AACA,QAAM,OAAO,SAAS,QAAQ,IAAI,MAAM;AACxC,MAAI,CAAC,MAAM;AACT,UAAM,QAAQ;AAAA,EAChB;AACA,MAAI,SAAS,WAAW,KAAK;AAC3B,WAAO,EAAE,aAAa,MAAM,KAAK;AAAA,EACnC;AACA,QAAM,MAAM,OAAO,MAAM,KAAK,QAAQ,CAAC;AACvC,QAAM,WAAW,OAAO,IAAI,QAAQ;AACpC,QAAM,QAAQ,MAAM,cAAc;AAAA,IAChC,QAAQ,KAAK,IAAI,QAAQ;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AACA,MACE,IAAI,eACJ,SAAS,aAAa,QACtB,CAAC,MAAM,QAAQ,SAAS,QAAQ,KAChC,SAAS,SAAS,WAAW,KAC7B,SAAS,SAAS,CAAC,MAAM,IAAI,UAC7B,KAAK,OAAO,WAAW,KACvB,KAAK,MAAM,CAAC,GAAG,OAAO,IAAI,QAC1B;AACA,UAAM,QAAQ;AAAA,EAChB;AACA,SAAO;AAAA,IACL,MAAM,EAAE,GAAG,MAAM,cAAc,UAAU;AAAA,IACzC,UAAU,EAAE,SAAS,CAAC,IAAI,MAAM,GAAG,UAAU,KAAK;AAAA,IAClD;AAAA,EACF;AACF;AAIO,SAAS,gBAAgB,OAAuB;AACrD,SAAO,KAAK,UAAU;AAAA,IACpB;AAAA,IACA,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,EACR,CAAC;AACH;AAEO,SAAS,yBAAyB,QAAgB;AACvD,QAAM,YAAY,OAAO,UAAU;AACnC,SAAO;AAAA,IACL,MAAM,KACJ,OACA,UAA0D,CAAC,GAC3D;AACA,UAAI,CAAC,UAAU,iBAAiB;AAC9B,cAAM,IAAI,MAAM,2CAA2C;AAAA,MAC7D;AACA,YAAM,WAAW,MAAM,UAAU,gBAAgB;AAAA,QAC/C,KAAK,OAAO,OAAO;AAAA,QACnB,SAAS,OAAO,OAAO;AAAA,QACvB,UAAU,MAAM;AAAA,QAChB,QAAQ,MAAM;AAAA,QACd,cAAc,MAAM;AAAA,QACpB,GAAG;AAAA,MACL,CAAC;AACD,cAAQ,QAAQ,eAAe;AAC/B,UAAI,EAAE,iBAAiB,WAAW;AAChC,YACE,SAAS,KAAK,aAAa,MAAM,YACjC,SAAS,SAAS,QAAQ,WAAW,KACrC,SAAS,SAAS,QAAQ,CAAC,MAAM,MAAM,QACvC;AACA,gBAAM,IAAI,MAAM,wCAAwC;AAAA,QAC1D;AACA,cAAM,MAAM,gBAAgB,KAAK;AACjC,cAAM,WACJ,OAAO,UAAU,MAAM,SAA+B,GAAG,EAAE;AAC7D,YAAI,KAAK,UAAU,QAAQ,MAAM,KAAK,UAAU,QAAQ,GAAG;AACzD,iBAAO,UAAU,MAAM,IAAI,KAAK,UAAU,SAAS,IAAI;AAAA,QACzD;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/transport/store.ts"],
4
- "sourcesContent": ["import type { Branch, Fulfilment, OpeningTime, Store } from \"../models\";\nimport type { Currency, Money } from \"../money\";\n\ntype Guard<T> = (value: unknown) => value is T;\ntype Optional = <K extends string, T>(\n key: K,\n value: unknown,\n is: Guard<T>,\n) => { [P in K]?: T };\n\n// Core parsing helpers are shared with the transport mapper.\nexport function mapStore(\n value: unknown,\n record: (value: unknown) => Record<string, unknown>,\n text: (value: unknown) => string,\n list: (value: unknown) => unknown[],\n hours: (value: unknown) => OpeningTime[],\n mapMoney: (value: unknown) => Money,\n money: (amountMinor: number, currency: Currency) => Money,\n opt: Optional,\n isString: Guard<string>,\n isBoolean: Guard<boolean>,\n mapSeo: (value: unknown) => NonNullable<Store[\"seo\"]>,\n): Store {\n const coordinates = (input: unknown) => {\n const coordinates = record(input ?? {});\n return typeof coordinates.lat === \"number\" &&\n typeof coordinates.lng === \"number\"\n ? { lat: coordinates.lat, lng: coordinates.lng }\n : undefined;\n };\n const fulfilment = (input: unknown): Fulfilment => {\n const value = record(input);\n return {\n pickup: value.pickup === true,\n delivery: value.delivery === true,\n dineIn: value.dine_in === true,\n scheduling: value.scheduled === true,\n };\n };\n const source = record(value);\n const brand = record(source.brand ?? {});\n const branches: Branch[] = list(source.branches).map((raw) => {\n const branch = record(raw);\n const pickup = record(branch.pickup_address ?? {});\n const pickupCoordinates = coordinates(pickup.coordinates);\n const pickupAddress = {\n ...opt(\"placeId\", pickup.place_id, isString),\n ...(pickupCoordinates ? { coordinates: pickupCoordinates } : {}),\n };\n const address =\n typeof branch.address === \"string\"\n ? branch.address\n : branch.address\n ? text(record(branch.address).line1).trim()\n : undefined;\n const addressFields =\n branch.address && typeof branch.address === \"object\"\n ? record(branch.address)\n : {};\n const branchCoordinates = coordinates(addressFields);\n return {\n ...(branch.currency !== undefined\n ? { currency: money(0, text(branch.currency) as Currency).currency }\n : {}),\n ...opt(\"country\", addressFields.country, isString),\n ...(branch.delivery_regions !== undefined\n ? {\n deliveryRegions: list(branch.delivery_regions).map((raw) => {\n const region = record(raw);\n return {\n id: text(region.id),\n name: text(region.name),\n fee: mapMoney(region.fee),\n };\n }),\n }\n : {}),\n id: text(branch.id),\n slug: text(branch.slug ?? branch.id),\n name: text(branch.name),\n openingTimes: hours(branch.hours),\n isOpen: branch.is_open === true,\n fulfilment: fulfilment(branch.fulfilment),\n ...(address !== undefined ? { address } : {}),\n ...(isString(branch.phone) ? { phone: branch.phone.trim() } : {}),\n ...opt(\"timezone\", branch.timezone, isString),\n ...(branchCoordinates ? { coordinates: branchCoordinates } : {}),\n ...opt(\"closedUntil\", branch.closed_until, isString),\n ...opt(\"nextChangeAt\", branch.next_change_at, isString),\n ...opt(\"isDefault\", branch.is_default, isBoolean),\n ...(Object.keys(pickupAddress).length > 0 ? { pickupAddress } : {}),\n ...(branch.social_links !== undefined\n ? {\n socialLinks: list(branch.social_links).flatMap((rawLink) => {\n const link = record(rawLink);\n // Templates render these as links; only http(s) URLs are kept.\n try {\n const url = new URL(text(link.url));\n return isString(link.type) &&\n (url.protocol === \"https:\" || url.protocol === \"http:\")\n ? [{ type: link.type, url: url.href }]\n : [];\n } catch {\n return [];\n }\n }),\n }\n : {}),\n ...(branch.minimum_order\n ? { minimumOrder: mapMoney(branch.minimum_order) }\n : {}),\n ...(branch.special_hours\n ? {\n specialHours: list(branch.special_hours).map((rawHours) => {\n const special = record(rawHours);\n return {\n date: text(special.date),\n closed: special.closed === true,\n ...opt(\"opens\", special.open, isString),\n ...opt(\"closes\", special.close, isString),\n };\n }),\n }\n : {}),\n };\n });\n const allowed = [\n \"card\",\n \"bank_transfer\",\n \"wallet\",\n \"zap\",\n \"apple_pay\",\n \"google_pay\",\n ];\n const paymentMethods = list(source.payment_methods)\n .map(text)\n .filter((method) => {\n if (allowed.includes(method)) {\n return true;\n }\n console.warn(\n `Zappie payment method \"${method}\" is newer than this SDK and was skipped`,\n );\n return false;\n });\n const aggregate = {\n pickup: false,\n delivery: false,\n dineIn: false,\n scheduling: false,\n };\n for (const branch of branches) {\n for (const key of Object.keys(aggregate) as (keyof Fulfilment)[]) {\n aggregate[key] ||= branch.fulfilment[key];\n }\n }\n const templateSettings = record(source.template_settings ?? {}) as Record<\n string,\n string | number | boolean\n >;\n return {\n ...(source.seo != null ? { seo: mapSeo(source.seo) } : {}),\n id: text(source.id),\n slug: text(source.slug),\n name: text(source.name),\n currency: money(0, text(source.currency) as Currency).currency,\n timezone: text(source.timezone),\n ...opt(\"description\", source.description, isString),\n ...(source.template_settings !== undefined ? { templateSettings } : {}),\n brand: {\n ...opt(\"logoUrl\", brand.logo_url, isString),\n ...opt(\"coverImageUrl\", brand.cover_image_url, isString),\n },\n fulfilment: source.fulfilment ? fulfilment(source.fulfilment) : aggregate,\n paymentMethods: paymentMethods as Store[\"paymentMethods\"],\n branches,\n };\n}\n"],
5
4
  "mappings": "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",
6
5
  "names": ["coordinates", "value", "raw"]
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": [],
4
- "sourcesContent": [],
5
4
  "mappings": "",
6
5
  "names": []
7
6
  }
@@ -1,4 +1,4 @@
1
- import { d as Currency, M as Money, I as Item } from './models-tf9rNoki.js';
1
+ import { d as Currency, M as Money, I as Item } from './models-C-kEg1Id.js';
2
2
 
3
3
  /**
4
4
  * An item selection you submit to the cart, with optional quantity, choices, inputs and source menu.
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/url/index.ts"],
4
- "sourcesContent": ["export {\n STOREFRONT_QUERY_PARAMETERS,\n STOREFRONT_V1_QUERY_PARAMETERS,\n buildStorefrontUrl,\n parseStorefrontUrl,\n storefrontRedirect,\n type StorefrontInstallation,\n type StorefrontPageState,\n type StorefrontQuery,\n type StorefrontRoute,\n type StorefrontUrlInput,\n type StorefrontUrlOptions,\n} from \"../server/storefront-url/index\";\n"],
5
4
  "mappings": ";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAOK;",
6
5
  "names": []
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/visits/index.ts"],
4
- "sourcesContent": ["import {\n pageView,\n visitEvent,\n visitSource,\n type PageViewInput,\n type VisitEventName,\n type VisitStorage,\n} from \"./page-views\";\n\nexport {\n pageView,\n visitEvent,\n visitSource,\n type PageView,\n type PageViewInput,\n type VisitEvent,\n type VisitEventName,\n type VisitStorage,\n} from \"./page-views\";\n\n/**\n * Page-view details with an optional enabled flag. Browser session storage is supplied automatically.\n */\nexport type CountPageInput = Omit<PageViewInput, \"storage\"> & {\n enabled?: boolean;\n};\n\nlet counting = false;\n\nconst emptyStorage: VisitStorage = {\n getItem: () => null,\n setItem: () => undefined,\n};\n\nfunction session(): VisitStorage {\n try {\n return window.sessionStorage;\n } catch {\n return emptyStorage;\n }\n}\n\nfunction canSend(): boolean {\n return (\n typeof navigator !== \"undefined\" &&\n typeof navigator.sendBeacon === \"function\"\n );\n}\n\nfunction send(body: unknown): void {\n navigator.sendBeacon(\"/_zappie/view\", JSON.stringify(body));\n}\n\n/**\n * Record a page view in session storage and send it to /_zappie/view when sendBeacon is available.\n * Passing enabled: false disables this call and subsequent event counting until an enabled page is counted.\n */\nexport function countPage(input: CountPageInput): void {\n counting = false;\n if (input.enabled === false) {\n return;\n }\n const { enabled: _enabled, ...page } = input;\n // The visit is recorded even without a beacon, so checkout can still name its source.\n const view = pageView({ ...page, storage: session() });\n counting = true;\n if (canSend()) {\n send(view);\n }\n}\n\n/**\n * Send an add or bag event for the current path after countPage has enabled counting.\n * Does nothing without a browser window or sendBeacon support.\n */\nexport function countEvent(event: VisitEventName): void {\n if (!counting || typeof window === \"undefined\" || !canSend()) {\n return;\n }\n send(visitEvent(event, window.location.pathname, session()));\n}\n\n/**\n * Read visit attribution from browser session storage.\n * Returns empty source and campaign strings on the server or when no visit is stored.\n */\nexport function currentSource(): { source: string; campaign: string } {\n if (typeof window === \"undefined\") {\n return { source: \"\", campaign: \"\" };\n }\n return visitSource(session());\n}\n"],
5
4
  "mappings": ";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAEP;AAAA,EACE,YAAAA;AAAA,EACA,cAAAC;AAAA,EACA,eAAAC;AAAA,OAMK;AASP,IAAI,WAAW;AAEf,IAAM,eAA6B;AAAA,EACjC,SAAS,MAAM;AAAA,EACf,SAAS,MAAM;AACjB;AAEA,SAAS,UAAwB;AAC/B,MAAI;AACF,WAAO,OAAO;AAAA,EAChB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,UAAmB;AAC1B,SACE,OAAO,cAAc,eACrB,OAAO,UAAU,eAAe;AAEpC;AAEA,SAAS,KAAK,MAAqB;AACjC,YAAU,WAAW,iBAAiB,KAAK,UAAU,IAAI,CAAC;AAC5D;AAMO,SAAS,UAAU,OAA6B;AACrD,aAAW;AACX,MAAI,MAAM,YAAY,OAAO;AAC3B;AAAA,EACF;AACA,QAAM,EAAE,SAAS,UAAU,GAAG,KAAK,IAAI;AAEvC,QAAM,OAAO,SAAS,EAAE,GAAG,MAAM,SAAS,QAAQ,EAAE,CAAC;AACrD,aAAW;AACX,MAAI,QAAQ,GAAG;AACb,SAAK,IAAI;AAAA,EACX;AACF;AAMO,SAAS,WAAW,OAA6B;AACtD,MAAI,CAAC,YAAY,OAAO,WAAW,eAAe,CAAC,QAAQ,GAAG;AAC5D;AAAA,EACF;AACA,OAAK,WAAW,OAAO,OAAO,SAAS,UAAU,QAAQ,CAAC,CAAC;AAC7D;AAMO,SAAS,gBAAsD;AACpE,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO,EAAE,QAAQ,IAAI,UAAU,GAAG;AAAA,EACpC;AACA,SAAO,YAAY,QAAQ,CAAC;AAC9B;",
6
5
  "names": ["pageView", "visitEvent", "visitSource"]
7
6
  }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/visits/page-views.ts"],
4
- "sourcesContent": ["const VISIT_KEY = \"zappie_visit\";\n\n/**\n * The string storage interface used to retain visit attribution and reached steps.\n */\nexport type VisitStorage = {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n};\n\ntype Visit = { source: string; campaign: string; steps?: string[] };\n\n/**\n * A page-view payload with attribution and flags for a new visit and a newly reached page kind.\n */\nexport type PageView = {\n path: string;\n title: string;\n source: string;\n campaign: string;\n new: boolean;\n step: boolean;\n};\n\n/**\n * Supported visit events: adding an item or viewing the bag.\n */\nexport type VisitEventName = \"add\" | \"bag\";\n\n/**\n * A visit event with its path, attribution and whether this event is a new step.\n */\nexport type VisitEvent = {\n event: VisitEventName;\n path: string;\n source: string;\n campaign: string;\n step: boolean;\n};\n\n/**\n * Page location, title, referrer and store name plus the storage used to track the visit.\n */\nexport type PageViewInput = {\n path: string;\n search: string;\n title: string;\n referrer: string;\n storeName: string;\n storage: VisitStorage;\n};\n\nfunction stored(storage: VisitStorage): Visit | null {\n try {\n const raw = storage.getItem(VISIT_KEY);\n if (raw) {\n return JSON.parse(raw) as Visit;\n }\n return null;\n } catch {\n return null;\n }\n}\n\nfunction save(storage: VisitStorage, visit: Visit): void {\n try {\n storage.setItem(VISIT_KEY, JSON.stringify(visit));\n } catch {\n // Without storage, each page is its own visit.\n }\n}\n\nfunction kindOf(path: string): string {\n if (path === \"/\") {\n return \"menu\";\n }\n if (path.startsWith(\"/item/\")) {\n return \"item\";\n }\n if (path === \"/cart\") {\n return \"bag\";\n }\n if (path === \"/checkout\") {\n return \"checkout\";\n }\n if (path.startsWith(\"/checkout/\")) {\n return \"payment\";\n }\n if (path.startsWith(\"/orders\")) {\n return \"order\";\n }\n return \"page\";\n}\n\nfunction reached(storage: VisitStorage, visit: Visit, kind: string): boolean {\n const steps = visit.steps ?? [];\n if (steps.includes(kind)) {\n return false;\n }\n visit.steps = [...steps, kind];\n save(storage, visit);\n return true;\n}\n\nfunction titleOf(title: string, storeName: string, path: string): string {\n const own = title\n .split(/\\s+[\u00B7|\u2013-]\\s+/)\n .filter((part) => part.trim() && part.trim() !== storeName)\n .join(\" \u00B7 \")\n .trim();\n if (own) {\n return own;\n }\n if (path === \"/\") {\n return \"Home\";\n }\n return \"\";\n}\n\n/**\n * Build a page-view payload and save visit attribution and reached steps in your storage.\n * On the first page, utm_source takes precedence over the referrer and utm_campaign supplies the campaign.\n *\n * @returns The payload with the store name removed from its title and visit/step flags. Does not send a request.\n */\nexport function pageView(input: PageViewInput): PageView {\n const { path, search, title, referrer, storeName, storage } = input;\n const known = stored(storage);\n let visit = known;\n if (!visit) {\n const query = new URLSearchParams(search);\n visit = {\n source: query.get(\"utm_source\") || referrer,\n campaign: query.get(\"utm_campaign\") || \"\",\n };\n save(storage, visit);\n }\n return {\n path,\n title: titleOf(title, storeName, path),\n source: visit.source,\n campaign: visit.campaign,\n new: known === null,\n step: reached(storage, visit, kindOf(path)),\n };\n}\n\n/**\n * Build a visit-event payload and record whether this event has already occurred in storage.\n * Uses stored attribution or empty strings. Does not send a request.\n */\nexport function visitEvent(\n event: VisitEventName,\n path: string,\n storage: VisitStorage,\n): VisitEvent {\n const visit = stored(storage) ?? { source: \"\", campaign: \"\" };\n return {\n event,\n path,\n source: visit.source,\n campaign: visit.campaign,\n step: reached(storage, visit, event),\n };\n}\n\n/**\n * Read the saved visit source and campaign from your storage, defaulting each to an empty string.\n */\nexport function visitSource(storage: VisitStorage): {\n source: string;\n campaign: string;\n} {\n const visit = stored(storage);\n return {\n source: visit?.source ?? \"\",\n campaign: visit?.campaign ?? \"\",\n };\n}\n"],
5
4
  "mappings": ";AAAA,IAAM,YAAY;AAoDlB,SAAS,OAAO,SAAqC;AACnD,MAAI;AACF,UAAM,MAAM,QAAQ,QAAQ,SAAS;AACrC,QAAI,KAAK;AACP,aAAO,KAAK,MAAM,GAAG;AAAA,IACvB;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,KAAK,SAAuB,OAAoB;AACvD,MAAI;AACF,YAAQ,QAAQ,WAAW,KAAK,UAAU,KAAK,CAAC;AAAA,EAClD,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,OAAO,MAAsB;AACpC,MAAI,SAAS,KAAK;AAChB,WAAO;AAAA,EACT;AACA,MAAI,KAAK,WAAW,QAAQ,GAAG;AAC7B,WAAO;AAAA,EACT;AACA,MAAI,SAAS,SAAS;AACpB,WAAO;AAAA,EACT;AACA,MAAI,SAAS,aAAa;AACxB,WAAO;AAAA,EACT;AACA,MAAI,KAAK,WAAW,YAAY,GAAG;AACjC,WAAO;AAAA,EACT;AACA,MAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,QAAQ,SAAuB,OAAc,MAAuB;AAC3E,QAAM,QAAQ,MAAM,SAAS,CAAC;AAC9B,MAAI,MAAM,SAAS,IAAI,GAAG;AACxB,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,CAAC,GAAG,OAAO,IAAI;AAC7B,OAAK,SAAS,KAAK;AACnB,SAAO;AACT;AAEA,SAAS,QAAQ,OAAe,WAAmB,MAAsB;AACvE,QAAM,MAAM,MACT,MAAM,cAAc,EACpB,OAAO,CAAC,SAAS,KAAK,KAAK,KAAK,KAAK,KAAK,MAAM,SAAS,EACzD,KAAK,QAAK,EACV,KAAK;AACR,MAAI,KAAK;AACP,WAAO;AAAA,EACT;AACA,MAAI,SAAS,KAAK;AAChB,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAQO,SAAS,SAAS,OAAgC;AACvD,QAAM,EAAE,MAAM,QAAQ,OAAO,UAAU,WAAW,QAAQ,IAAI;AAC9D,QAAM,QAAQ,OAAO,OAAO;AAC5B,MAAI,QAAQ;AACZ,MAAI,CAAC,OAAO;AACV,UAAM,QAAQ,IAAI,gBAAgB,MAAM;AACxC,YAAQ;AAAA,MACN,QAAQ,MAAM,IAAI,YAAY,KAAK;AAAA,MACnC,UAAU,MAAM,IAAI,cAAc,KAAK;AAAA,IACzC;AACA,SAAK,SAAS,KAAK;AAAA,EACrB;AACA,SAAO;AAAA,IACL;AAAA,IACA,OAAO,QAAQ,OAAO,WAAW,IAAI;AAAA,IACrC,QAAQ,MAAM;AAAA,IACd,UAAU,MAAM;AAAA,IAChB,KAAK,UAAU;AAAA,IACf,MAAM,QAAQ,SAAS,OAAO,OAAO,IAAI,CAAC;AAAA,EAC5C;AACF;AAMO,SAAS,WACd,OACA,MACA,SACY;AACZ,QAAM,QAAQ,OAAO,OAAO,KAAK,EAAE,QAAQ,IAAI,UAAU,GAAG;AAC5D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,QAAQ,MAAM;AAAA,IACd,UAAU,MAAM;AAAA,IAChB,MAAM,QAAQ,SAAS,OAAO,KAAK;AAAA,EACrC;AACF;AAKO,SAAS,YAAY,SAG1B;AACA,QAAM,QAAQ,OAAO,OAAO;AAC5B,SAAO;AAAA,IACL,QAAQ,OAAO,UAAU;AAAA,IACzB,UAAU,OAAO,YAAY;AAAA,EAC/B;AACF;",
6
5
  "names": []
7
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zappie/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Typed, framework-agnostic client for Zappie menus, carts, checkout, auth and events.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -33,6 +33,10 @@
33
33
  "types": "./dist/redirect/index.d.ts",
34
34
  "import": "./dist/redirect/index.js"
35
35
  },
36
+ "./services": {
37
+ "types": "./dist/services/index.d.ts",
38
+ "import": "./dist/services/index.js"
39
+ },
36
40
  "./testing": {
37
41
  "types": "./dist/testing/index.d.ts",
38
42
  "import": "./dist/testing/index.js"
@@ -144,7 +148,7 @@
144
148
  "vite": "8.3.5"
145
149
  },
146
150
  "scripts": {
147
- "build": "tsup && tsup --config tsup.checkout.config.ts && tsup --config tsup.checkout-defaults.config.ts && tsup --config tsup.customer.config.ts && tsup --config tsup.places.config.ts && tsup --config tsup.url.config.ts && tsup --config tsup.redirect.config.ts && tsup --config tsup.visits.config.ts && tsup --config tsup.pricing.config.ts && tsup --config tsup.site-config.config.ts && tsup --config tsup.template.config.ts && node ../../scripts/release-packages/build-modules.mjs --unmapped=studio/index.js --unmapped=testing/index.js --unmapped=vite/index.js",
151
+ "build": "tsup && tsup --config tsup.checkout.config.ts && tsup --config tsup.checkout-defaults.config.ts && tsup --config tsup.customer.config.ts && tsup --config tsup.places.config.ts && tsup --config tsup.url.config.ts && tsup --config tsup.redirect.config.ts && tsup --config tsup.services.config.ts && tsup --config tsup.visits.config.ts && tsup --config tsup.pricing.config.ts && tsup --config tsup.site-config.config.ts && tsup --config tsup.template.config.ts && node ../../scripts/release-packages/build-modules.mjs --no-sources-content --unmapped=studio/index.js --unmapped=testing/index.js --unmapped=vite/index.js",
148
152
  "test": "vitest run --root ../.. packages/sdk/test",
149
153
  "size": "node ../../scripts/release-packages/size-sdk.mjs && size-limit"
150
154
  }