@moonbase.sh/storefront-api 3.7.0 → 3.8.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 CHANGED
@@ -24,7 +24,7 @@ Configuration notes:
24
24
 
25
25
  - `endpoint`: your Moonbase storefront URL.
26
26
  - `includeManifests`: include downloadable file manifests when available.
27
- - `persistUtm`: store detected UTM values in localStorage instead of sessionStorage.
27
+ - `persistUtm`: store detected UTM values and ad click IDs in localStorage instead of sessionStorage.
28
28
 
29
29
  ## Common usage
30
30
 
@@ -172,6 +172,38 @@ refused with `CaptchaRequiredError` (a `MoonbaseError`). A site with a
172
172
  Content-Security-Policy has to allow the store's `https://<account-id>.moonbase.sh`
173
173
  in `frame-src`.
174
174
 
175
+ ### Where a customer came from
176
+
177
+ Pass the UTM tags, ad click IDs and referrer the visitor arrived with, the same
178
+ shape orders take, so the merchant can see how the customer was acquired:
179
+
180
+ ```ts
181
+ import { parseOffSiteReferrer } from '@moonbase.sh/storefront-api'
182
+
183
+ const params = new URLSearchParams(window.location.search)
184
+
185
+ await client.identity.signUp('Jane', 'jane@example.com', 'password', null, true, true, true, {
186
+ utm: {
187
+ source: params.get('utm_source') || undefined,
188
+ campaign: params.get('utm_campaign') || undefined,
189
+ // Click IDs ad platforms add to the link, often with no UTM tags beside them.
190
+ gclid: params.get('gclid') || undefined,
191
+ fbclid: params.get('fbclid') || undefined,
192
+ msclkid: params.get('msclkid') || undefined,
193
+ ttclid: params.get('ttclid') || undefined,
194
+ // The referring site's host only, and nothing when the visitor came from this site.
195
+ referrer: parseOffSiteReferrer(document.referrer, window.location.origin),
196
+ },
197
+ })
198
+ ```
199
+
200
+ Fields left out are not sent. The click IDs (`gclid`, `gbraid` and `wbraid` from
201
+ Google Ads, `fbclid` from Meta, `msclkid` from Microsoft Advertising and `ttclid`
202
+ from TikTok) go to the API under those names, and the rest as `utm_*`.
203
+ `useAuth().signUp` in `@moonbase.sh/vue`, and so the storefront widget, fills this
204
+ in from the tags and click IDs it has already picked up, unless you pass your own
205
+ `utm`.
206
+
175
207
  ## Inventory flow
176
208
 
177
209
  Most inventory endpoints require an authenticated user:
@@ -191,6 +223,14 @@ while (page.hasMore && page.next) {
191
223
  }
192
224
  ```
193
225
 
226
+ A product's release history needs no sign-in. `getReleases` lists its published releases, highest
227
+ version first, each with its version, release notes, publish date and files, and whether the caller
228
+ may download it (`downloadsAllowed`). It pages the same way:
229
+
230
+ ```ts
231
+ const releases = await client.inventory.products.getReleases('my-product')
232
+ ```
233
+
194
234
  ## Endpoint groups
195
235
 
196
236
  `client.storefront`, `client.identity`, `client.orders`, `client.vouchers`, `client.inventory`, `client.activationRequests`, `client.vendor`, `client.communications`, `client.groups`.
package/dist/index.cjs CHANGED
@@ -120,7 +120,13 @@ function utmToObject(utm) {
120
120
  utm_campaign: utm == null ? void 0 : utm.campaign,
121
121
  utm_term: utm == null ? void 0 : utm.term,
122
122
  utm_content: utm == null ? void 0 : utm.content,
123
- utm_referrer: utm == null ? void 0 : utm.referrer
123
+ utm_referrer: utm == null ? void 0 : utm.referrer,
124
+ gclid: utm == null ? void 0 : utm.gclid,
125
+ gbraid: utm == null ? void 0 : utm.gbraid,
126
+ wbraid: utm == null ? void 0 : utm.wbraid,
127
+ fbclid: utm == null ? void 0 : utm.fbclid,
128
+ msclkid: utm == null ? void 0 : utm.msclkid,
129
+ ttclid: utm == null ? void 0 : utm.ttclid
124
130
  }).filter(([_, value]) => value !== void 0).reduce((obj, [key, value]) => ({
125
131
  ...obj,
126
132
  [key]: value
@@ -212,6 +218,7 @@ var schemas_exports = {};
212
218
  __export(schemas_exports, {
213
219
  downloadSchema: () => downloadSchema,
214
220
  manifestSchema: () => manifestSchema,
221
+ productReleaseSchema: () => productReleaseSchema,
215
222
  productSummarySchema: () => productSummarySchema
216
223
  });
217
224
  var import_zod2 = require("zod");
@@ -274,10 +281,20 @@ var productSummarySchema = import_zod2.z.object({
274
281
  downloadsNeedsOwnership: import_zod2.z.boolean(),
275
282
  downloadsNeedsGroupMembership: import_zod2.z.boolean(),
276
283
  // Whether this caller clears every requirement above, including the group membership the client
277
- // can't see for itself.
284
+ // can't see for itself. False with none of them set means the merchant closed the release to everyone.
278
285
  downloadsAllowed: import_zod2.z.boolean(),
279
286
  downloads: import_zod2.z.array(downloadSchema).optional()
280
287
  });
288
+ var productReleaseSchema = import_zod2.z.object({
289
+ version: import_zod2.z.string(),
290
+ description: import_zod2.z.string().nullish(),
291
+ publishedAt: import_zod2.z.coerce.date(),
292
+ // Whether this caller may download the release, resolved against its own access control: a
293
+ // merchant can keep past releases to owners while the current one is open to anyone.
294
+ downloadsAllowed: import_zod2.z.boolean(),
295
+ downloads: import_zod2.z.array(downloadSchema),
296
+ properties: import_zod2.z.record(rawPropertyValueSchema).nullish()
297
+ });
281
298
 
282
299
  // src/storefront/schemas.ts
283
300
  var storefrontProductSchema = import_zod3.z.object({
@@ -841,8 +858,12 @@ var IdentityEndpoints = class {
841
858
  return userSchema.parse(identity);
842
859
  }
843
860
  async signUp(name, email, password, address, acceptedPrivacyPolicy, acceptedTermsAndConditions, communicationOptIn, options) {
844
- const path = `/api/customer/identity/sign-up?scheme=JWT&communicationOptIn=${communicationOptIn ? "true" : "false"}`;
845
- const response = await fetch(`${this.api.baseUrl}${path}`, {
861
+ const query = new URLSearchParams({
862
+ scheme: "JWT",
863
+ communicationOptIn: communicationOptIn ? "true" : "false",
864
+ ...utmToObject(options == null ? void 0 : options.utm)
865
+ });
866
+ const response = await fetch(`${this.api.baseUrl}/api/customer/identity/sign-up?${query.toString()}`, {
846
867
  method: "POST",
847
868
  headers: {
848
869
  "Accept": "application/json",
@@ -1091,6 +1112,15 @@ var ProductEndpoints = class {
1091
1112
  const response = await this.api.fetch(`/api/customer/inventory/products/${productId}${version2 ? `?version=${version2}${this.configuration.includeManifests ? "&includeManifests=true" : ""}` : this.configuration.includeManifests ? "?includeManifests=true" : ""}`, productSummarySchema);
1092
1113
  return response.data;
1093
1114
  }
1115
+ /**
1116
+ * The product's published releases, highest version first, with their release notes and files,
1117
+ * and whether the caller may download each. Open to anyone who can see the product, signed in or
1118
+ * not. A page can hold fewer releases than its size, so keep following `next` while `hasMore` is set.
1119
+ */
1120
+ async getReleases(productId, nextUrl) {
1121
+ const response = await this.api.fetch(nextUrl || `/api/customer/inventory/products/${productId}/releases${this.configuration.includeManifests ? "?includeManifests=true" : ""}`, paged(productReleaseSchema));
1122
+ return response.data;
1123
+ }
1094
1124
  async getOwned(nextUrl) {
1095
1125
  const response = await this.api.authenticatedFetch(nextUrl || `/api/customer/inventory/products${this.configuration.includeManifests ? "?includeManifests=true" : ""}`, paged(productSummarySchema));
1096
1126
  return response.data;
@@ -1117,7 +1147,7 @@ var ProductEndpoints = class {
1117
1147
  var import_zod14 = require("zod");
1118
1148
 
1119
1149
  // package.json
1120
- var version = "3.7.0";
1150
+ var version = "3.8.0";
1121
1151
 
1122
1152
  // src/utils/client.ts
1123
1153
  function buildClientHeader(sdk) {
@@ -2731,7 +2761,8 @@ function parseOffSiteReferrer(rawReferrer, currentOrigin) {
2731
2761
  }
2732
2762
  function resolveUtm(cached, fresh) {
2733
2763
  const freshHasUtm = !!(fresh.source || fresh.medium || fresh.campaign || fresh.term || fresh.content);
2734
- const freshIsExternalArrival = freshHasUtm || !!fresh.referrer;
2764
+ const freshHasClickId = !!(fresh.gclid || fresh.gbraid || fresh.wbraid || fresh.fbclid || fresh.msclkid || fresh.ttclid);
2765
+ const freshIsExternalArrival = freshHasUtm || freshHasClickId || !!fresh.referrer;
2735
2766
  if (freshIsExternalArrival)
2736
2767
  return fresh;
2737
2768
  return cached != null ? cached : fresh;