@cimplify/sdk 0.48.2 → 0.49.1

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.js CHANGED
@@ -41,12 +41,25 @@ var tags = {
41
41
  collection: (id) => `cimplify:collection:${id}`,
42
42
  collectionProducts: (id) => `cimplify:collection:${id}:products`,
43
43
  business: () => "cimplify:business",
44
+ brand: () => "cimplify:brand",
44
45
  locations: () => "cimplify:locations",
46
+ location: (id) => `cimplify:location:${id}`,
47
+ locale: () => "cimplify:locale",
48
+ pricing: () => "cimplify:pricing",
49
+ // Product-level tag (e.g. "vegan", "bestseller"), not "cache tag".
50
+ tag: (name) => `cimplify:tag:${name}`,
51
+ addons: () => "cimplify:addons",
52
+ addon: (id) => `cimplify:addon:${id}`,
53
+ subscriptions: () => "cimplify:subscriptions",
54
+ subscription: (id) => `cimplify:subscription:${id}`,
55
+ stock: () => "cimplify:stock",
56
+ stockFor: (productId) => `cimplify:stock:${productId}`,
45
57
  orders: (customerId) => `cimplify:orders:${customerId}`,
46
58
  order: (id) => `cimplify:order:${id}`
47
59
  };
48
60
 
49
61
  // src/server/revalidate.ts
62
+ var DEFAULT_PROFILE = "max";
50
63
  var _revalidateTag = null;
51
64
  async function getRevalidateTag() {
52
65
  if (_revalidateTag) return _revalidateTag;
@@ -59,37 +72,241 @@ async function getRevalidateTag() {
59
72
  _revalidateTag = mod.revalidateTag;
60
73
  return _revalidateTag;
61
74
  }
62
- async function revalidate(...tagList) {
75
+ async function revalidate(profile, ...tagList) {
63
76
  const fn = await getRevalidateTag();
64
- for (const t of tagList) fn(t);
77
+ for (const t of tagList) fn(t, profile);
65
78
  }
66
- async function revalidateProducts() {
67
- return revalidate(tags.products());
79
+ async function revalidateProducts(profile = DEFAULT_PROFILE) {
80
+ return revalidate(profile, tags.products());
68
81
  }
69
- async function revalidateProduct(id) {
70
- return revalidate(tags.product(id), tags.products());
82
+ async function revalidateProduct(id, profile = DEFAULT_PROFILE) {
83
+ return revalidate(profile, tags.product(id), tags.products());
71
84
  }
72
- async function revalidateCategories() {
73
- return revalidate(tags.categories());
85
+ async function revalidateCategories(profile = DEFAULT_PROFILE) {
86
+ return revalidate(profile, tags.categories());
74
87
  }
75
- async function revalidateCategory(id) {
76
- return revalidate(tags.category(id), tags.categoryProducts(id), tags.categories());
88
+ async function revalidateCategory(id, profile = DEFAULT_PROFILE) {
89
+ return revalidate(profile, tags.category(id), tags.categoryProducts(id), tags.categories());
77
90
  }
78
- async function revalidateCollections() {
79
- return revalidate(tags.collections());
91
+ async function revalidateCollections(profile = DEFAULT_PROFILE) {
92
+ return revalidate(profile, tags.collections());
80
93
  }
81
- async function revalidateCollection(id) {
94
+ async function revalidateCollection(id, profile = DEFAULT_PROFILE) {
82
95
  return revalidate(
96
+ profile,
83
97
  tags.collection(id),
84
98
  tags.collectionProducts(id),
85
99
  tags.collections()
86
100
  );
87
101
  }
88
- async function revalidateBusiness() {
89
- return revalidate(tags.business());
102
+ async function revalidateBusiness(profile = DEFAULT_PROFILE) {
103
+ return revalidate(profile, tags.business());
104
+ }
105
+ async function revalidateBrand(profile = DEFAULT_PROFILE) {
106
+ return revalidate(profile, tags.brand());
107
+ }
108
+ async function revalidateLocations(profile = DEFAULT_PROFILE) {
109
+ return revalidate(profile, tags.locations());
110
+ }
111
+ async function revalidateLocation(id, profile = DEFAULT_PROFILE) {
112
+ return revalidate(profile, tags.location(id), tags.locations());
113
+ }
114
+ async function revalidatePricing(profile = DEFAULT_PROFILE) {
115
+ return revalidate(profile, tags.pricing(), tags.products());
116
+ }
117
+ async function revalidateAddOns(profile = DEFAULT_PROFILE) {
118
+ return revalidate(profile, tags.addons());
119
+ }
120
+ async function revalidateAddOn(id, profile = DEFAULT_PROFILE) {
121
+ return revalidate(profile, tags.addon(id), tags.addons());
122
+ }
123
+ async function revalidateSubscriptions(profile = DEFAULT_PROFILE) {
124
+ return revalidate(profile, tags.subscriptions());
125
+ }
126
+ async function revalidateSubscription(id, profile = DEFAULT_PROFILE) {
127
+ return revalidate(profile, tags.subscription(id), tags.subscriptions());
128
+ }
129
+ async function revalidateStock(productId, profile = DEFAULT_PROFILE) {
130
+ return productId ? revalidate(profile, tags.stockFor(productId), tags.stock()) : revalidate(profile, tags.stock());
131
+ }
132
+ async function revalidateByTag(tag, profile = DEFAULT_PROFILE) {
133
+ return revalidate(profile, tag);
134
+ }
135
+
136
+ // src/server/update.ts
137
+ var _updateTag = null;
138
+ var _refresh = null;
139
+ async function getUpdateTag() {
140
+ if (_updateTag) return _updateTag;
141
+ const specifier = "next/cache";
142
+ const mod = await import(
143
+ /* webpackIgnore: true */
144
+ /* @vite-ignore */
145
+ specifier
146
+ );
147
+ if (!mod.updateTag) {
148
+ throw new Error("updateTag is not exported from next/cache \u2014 requires Next 16+");
149
+ }
150
+ _updateTag = mod.updateTag;
151
+ return _updateTag;
152
+ }
153
+ async function getRefresh() {
154
+ if (_refresh) return _refresh;
155
+ const specifier = "next/cache";
156
+ const mod = await import(
157
+ /* webpackIgnore: true */
158
+ /* @vite-ignore */
159
+ specifier
160
+ );
161
+ if (!mod.refresh) {
162
+ throw new Error("refresh is not exported from next/cache \u2014 requires Next 16+");
163
+ }
164
+ _refresh = mod.refresh;
165
+ return _refresh;
166
+ }
167
+ async function update(...tagList) {
168
+ const fn = await getUpdateTag();
169
+ for (const t of tagList) fn(t);
170
+ }
171
+ async function updateProducts() {
172
+ return update(tags.products());
173
+ }
174
+ async function updateProduct(id) {
175
+ return update(tags.product(id), tags.products());
176
+ }
177
+ async function updateCategories() {
178
+ return update(tags.categories());
179
+ }
180
+ async function updateCategory(id) {
181
+ return update(tags.category(id), tags.categoryProducts(id), tags.categories());
182
+ }
183
+ async function updateCollections() {
184
+ return update(tags.collections());
185
+ }
186
+ async function updateCollection(id) {
187
+ return update(tags.collection(id), tags.collectionProducts(id), tags.collections());
188
+ }
189
+ async function updateBusiness() {
190
+ return update(tags.business());
191
+ }
192
+ async function updateBrand() {
193
+ return update(tags.brand());
194
+ }
195
+ async function updateLocations() {
196
+ return update(tags.locations());
197
+ }
198
+ async function updateLocation(id) {
199
+ return update(tags.location(id), tags.locations());
200
+ }
201
+ async function updatePricing() {
202
+ return update(tags.pricing(), tags.products());
203
+ }
204
+ async function updateAddOns() {
205
+ return update(tags.addons());
206
+ }
207
+ async function updateAddOn(id) {
208
+ return update(tags.addon(id), tags.addons());
209
+ }
210
+ async function updateSubscriptions() {
211
+ return update(tags.subscriptions());
212
+ }
213
+ async function updateSubscription(id) {
214
+ return update(tags.subscription(id), tags.subscriptions());
215
+ }
216
+ async function updateStock(productId) {
217
+ return productId ? update(tags.stockFor(productId), tags.stock()) : update(tags.stock());
218
+ }
219
+ async function updateByTag(tag) {
220
+ return update(tag);
221
+ }
222
+ async function refreshPage() {
223
+ const fn = await getRefresh();
224
+ fn();
225
+ }
226
+
227
+ // src/server/revalidate-route.ts
228
+ var TIMESTAMP_HEADER = "x-cimplify-timestamp";
229
+ var SIGNATURE_HEADER = "x-cimplify-signature";
230
+ var SIGNATURE_PREFIX = "sha256=";
231
+ var MAX_SKEW_MS = 5 * 60 * 1e3;
232
+ var SECRET_ENV = "CIMPLIFY_REVALIDATE_SECRET";
233
+ var DEFAULT_PROFILE2 = "max";
234
+ async function revalidateRouteHandler(req, options = {}) {
235
+ const secret = options.secret ?? envSecret();
236
+ if (!secret) return text(`revalidate disabled: ${SECRET_ENV} not set`, 500);
237
+ const timestamp = req.headers.get(TIMESTAMP_HEADER);
238
+ const signature = req.headers.get(SIGNATURE_HEADER);
239
+ if (!timestamp || !signature) return text("missing auth headers", 401);
240
+ const ts = Number.parseInt(timestamp, 10);
241
+ const now = options.now ?? Date.now;
242
+ if (!Number.isFinite(ts) || Math.abs(now() - ts) > MAX_SKEW_MS) {
243
+ return text("stale or invalid timestamp", 401);
244
+ }
245
+ const body = await req.text();
246
+ if (!await verifyHmac(secret, `${timestamp}.${body}`, signature)) {
247
+ return text("invalid signature", 401);
248
+ }
249
+ let parsed;
250
+ try {
251
+ parsed = JSON.parse(body);
252
+ } catch {
253
+ return text("invalid json", 400);
254
+ }
255
+ const tags2 = Array.isArray(parsed.tags) ? parsed.tags.filter((t) => typeof t === "string" && t.length > 0) : [];
256
+ if (tags2.length === 0) return text("no tags", 400);
257
+ const profile = parseProfile(parsed.profile) ?? options.defaultProfile ?? DEFAULT_PROFILE2;
258
+ const revalidate2 = options.revalidateTag ?? await loadRevalidateTag();
259
+ for (const tag of tags2) revalidate2(tag, profile);
260
+ return Response.json({ ok: true, revalidated: tags2.length, profile });
261
+ }
262
+ function parseProfile(raw) {
263
+ if (typeof raw === "string" && raw.length > 0) return raw;
264
+ if (raw && typeof raw === "object" && "expire" in raw && typeof raw.expire === "number") {
265
+ return { expire: raw.expire };
266
+ }
267
+ return void 0;
268
+ }
269
+ var cachedRevalidateTag = null;
270
+ async function loadRevalidateTag() {
271
+ if (cachedRevalidateTag) return cachedRevalidateTag;
272
+ const specifier = "next/cache";
273
+ const mod = await import(
274
+ /* webpackIgnore: true */
275
+ /* @vite-ignore */
276
+ specifier
277
+ );
278
+ cachedRevalidateTag = mod.revalidateTag;
279
+ return cachedRevalidateTag;
280
+ }
281
+ async function verifyHmac(secret, payload, signatureHeader) {
282
+ if (!signatureHeader.startsWith(SIGNATURE_PREFIX)) return false;
283
+ const providedBytes = hexToBytes(signatureHeader.slice(SIGNATURE_PREFIX.length));
284
+ if (!providedBytes) return false;
285
+ const enc = new TextEncoder();
286
+ const key = await crypto.subtle.importKey(
287
+ "raw",
288
+ enc.encode(secret),
289
+ { name: "HMAC", hash: "SHA-256" },
290
+ false,
291
+ ["verify"]
292
+ );
293
+ return crypto.subtle.verify("HMAC", key, providedBytes, enc.encode(payload));
294
+ }
295
+ function hexToBytes(hex) {
296
+ if (hex.length % 2 !== 0 || !/^[0-9a-f]+$/i.test(hex)) return null;
297
+ const buf = new ArrayBuffer(hex.length / 2);
298
+ const out = new Uint8Array(buf);
299
+ for (let i = 0; i < hex.length; i += 2) {
300
+ out[i / 2] = Number.parseInt(hex.slice(i, i + 2), 16);
301
+ }
302
+ return out;
303
+ }
304
+ function envSecret() {
305
+ const proc = globalThis.process;
306
+ return proc?.env?.[SECRET_ENV];
90
307
  }
91
- async function revalidateByTag(tag) {
92
- return revalidate(tag);
308
+ function text(message, status) {
309
+ return new Response(message, { status, headers: { "content-type": "text/plain" } });
93
310
  }
94
311
 
95
312
  Object.defineProperty(exports, "CimplifyError", {
@@ -97,12 +314,40 @@ Object.defineProperty(exports, "CimplifyError", {
97
314
  get: function () { return chunkTKOTACKZ_js.CimplifyError; }
98
315
  });
99
316
  exports.getServerClient = getServerClient;
317
+ exports.refreshPage = refreshPage;
318
+ exports.revalidateAddOn = revalidateAddOn;
319
+ exports.revalidateAddOns = revalidateAddOns;
320
+ exports.revalidateBrand = revalidateBrand;
100
321
  exports.revalidateBusiness = revalidateBusiness;
101
322
  exports.revalidateByTag = revalidateByTag;
102
323
  exports.revalidateCategories = revalidateCategories;
103
324
  exports.revalidateCategory = revalidateCategory;
104
325
  exports.revalidateCollection = revalidateCollection;
105
326
  exports.revalidateCollections = revalidateCollections;
327
+ exports.revalidateLocation = revalidateLocation;
328
+ exports.revalidateLocations = revalidateLocations;
329
+ exports.revalidatePricing = revalidatePricing;
106
330
  exports.revalidateProduct = revalidateProduct;
107
331
  exports.revalidateProducts = revalidateProducts;
332
+ exports.revalidateRouteHandler = revalidateRouteHandler;
333
+ exports.revalidateStock = revalidateStock;
334
+ exports.revalidateSubscription = revalidateSubscription;
335
+ exports.revalidateSubscriptions = revalidateSubscriptions;
108
336
  exports.tags = tags;
337
+ exports.updateAddOn = updateAddOn;
338
+ exports.updateAddOns = updateAddOns;
339
+ exports.updateBrand = updateBrand;
340
+ exports.updateBusiness = updateBusiness;
341
+ exports.updateByTag = updateByTag;
342
+ exports.updateCategories = updateCategories;
343
+ exports.updateCategory = updateCategory;
344
+ exports.updateCollection = updateCollection;
345
+ exports.updateCollections = updateCollections;
346
+ exports.updateLocation = updateLocation;
347
+ exports.updateLocations = updateLocations;
348
+ exports.updatePricing = updatePricing;
349
+ exports.updateProduct = updateProduct;
350
+ exports.updateProducts = updateProducts;
351
+ exports.updateStock = updateStock;
352
+ exports.updateSubscription = updateSubscription;
353
+ exports.updateSubscriptions = updateSubscriptions;
package/dist/server.mjs CHANGED
@@ -39,12 +39,25 @@ var tags = {
39
39
  collection: (id) => `cimplify:collection:${id}`,
40
40
  collectionProducts: (id) => `cimplify:collection:${id}:products`,
41
41
  business: () => "cimplify:business",
42
+ brand: () => "cimplify:brand",
42
43
  locations: () => "cimplify:locations",
44
+ location: (id) => `cimplify:location:${id}`,
45
+ locale: () => "cimplify:locale",
46
+ pricing: () => "cimplify:pricing",
47
+ // Product-level tag (e.g. "vegan", "bestseller"), not "cache tag".
48
+ tag: (name) => `cimplify:tag:${name}`,
49
+ addons: () => "cimplify:addons",
50
+ addon: (id) => `cimplify:addon:${id}`,
51
+ subscriptions: () => "cimplify:subscriptions",
52
+ subscription: (id) => `cimplify:subscription:${id}`,
53
+ stock: () => "cimplify:stock",
54
+ stockFor: (productId) => `cimplify:stock:${productId}`,
43
55
  orders: (customerId) => `cimplify:orders:${customerId}`,
44
56
  order: (id) => `cimplify:order:${id}`
45
57
  };
46
58
 
47
59
  // src/server/revalidate.ts
60
+ var DEFAULT_PROFILE = "max";
48
61
  var _revalidateTag = null;
49
62
  async function getRevalidateTag() {
50
63
  if (_revalidateTag) return _revalidateTag;
@@ -57,37 +70,241 @@ async function getRevalidateTag() {
57
70
  _revalidateTag = mod.revalidateTag;
58
71
  return _revalidateTag;
59
72
  }
60
- async function revalidate(...tagList) {
73
+ async function revalidate(profile, ...tagList) {
61
74
  const fn = await getRevalidateTag();
62
- for (const t of tagList) fn(t);
75
+ for (const t of tagList) fn(t, profile);
63
76
  }
64
- async function revalidateProducts() {
65
- return revalidate(tags.products());
77
+ async function revalidateProducts(profile = DEFAULT_PROFILE) {
78
+ return revalidate(profile, tags.products());
66
79
  }
67
- async function revalidateProduct(id) {
68
- return revalidate(tags.product(id), tags.products());
80
+ async function revalidateProduct(id, profile = DEFAULT_PROFILE) {
81
+ return revalidate(profile, tags.product(id), tags.products());
69
82
  }
70
- async function revalidateCategories() {
71
- return revalidate(tags.categories());
83
+ async function revalidateCategories(profile = DEFAULT_PROFILE) {
84
+ return revalidate(profile, tags.categories());
72
85
  }
73
- async function revalidateCategory(id) {
74
- return revalidate(tags.category(id), tags.categoryProducts(id), tags.categories());
86
+ async function revalidateCategory(id, profile = DEFAULT_PROFILE) {
87
+ return revalidate(profile, tags.category(id), tags.categoryProducts(id), tags.categories());
75
88
  }
76
- async function revalidateCollections() {
77
- return revalidate(tags.collections());
89
+ async function revalidateCollections(profile = DEFAULT_PROFILE) {
90
+ return revalidate(profile, tags.collections());
78
91
  }
79
- async function revalidateCollection(id) {
92
+ async function revalidateCollection(id, profile = DEFAULT_PROFILE) {
80
93
  return revalidate(
94
+ profile,
81
95
  tags.collection(id),
82
96
  tags.collectionProducts(id),
83
97
  tags.collections()
84
98
  );
85
99
  }
86
- async function revalidateBusiness() {
87
- return revalidate(tags.business());
100
+ async function revalidateBusiness(profile = DEFAULT_PROFILE) {
101
+ return revalidate(profile, tags.business());
102
+ }
103
+ async function revalidateBrand(profile = DEFAULT_PROFILE) {
104
+ return revalidate(profile, tags.brand());
105
+ }
106
+ async function revalidateLocations(profile = DEFAULT_PROFILE) {
107
+ return revalidate(profile, tags.locations());
108
+ }
109
+ async function revalidateLocation(id, profile = DEFAULT_PROFILE) {
110
+ return revalidate(profile, tags.location(id), tags.locations());
111
+ }
112
+ async function revalidatePricing(profile = DEFAULT_PROFILE) {
113
+ return revalidate(profile, tags.pricing(), tags.products());
114
+ }
115
+ async function revalidateAddOns(profile = DEFAULT_PROFILE) {
116
+ return revalidate(profile, tags.addons());
117
+ }
118
+ async function revalidateAddOn(id, profile = DEFAULT_PROFILE) {
119
+ return revalidate(profile, tags.addon(id), tags.addons());
120
+ }
121
+ async function revalidateSubscriptions(profile = DEFAULT_PROFILE) {
122
+ return revalidate(profile, tags.subscriptions());
123
+ }
124
+ async function revalidateSubscription(id, profile = DEFAULT_PROFILE) {
125
+ return revalidate(profile, tags.subscription(id), tags.subscriptions());
126
+ }
127
+ async function revalidateStock(productId, profile = DEFAULT_PROFILE) {
128
+ return productId ? revalidate(profile, tags.stockFor(productId), tags.stock()) : revalidate(profile, tags.stock());
129
+ }
130
+ async function revalidateByTag(tag, profile = DEFAULT_PROFILE) {
131
+ return revalidate(profile, tag);
132
+ }
133
+
134
+ // src/server/update.ts
135
+ var _updateTag = null;
136
+ var _refresh = null;
137
+ async function getUpdateTag() {
138
+ if (_updateTag) return _updateTag;
139
+ const specifier = "next/cache";
140
+ const mod = await import(
141
+ /* webpackIgnore: true */
142
+ /* @vite-ignore */
143
+ specifier
144
+ );
145
+ if (!mod.updateTag) {
146
+ throw new Error("updateTag is not exported from next/cache \u2014 requires Next 16+");
147
+ }
148
+ _updateTag = mod.updateTag;
149
+ return _updateTag;
150
+ }
151
+ async function getRefresh() {
152
+ if (_refresh) return _refresh;
153
+ const specifier = "next/cache";
154
+ const mod = await import(
155
+ /* webpackIgnore: true */
156
+ /* @vite-ignore */
157
+ specifier
158
+ );
159
+ if (!mod.refresh) {
160
+ throw new Error("refresh is not exported from next/cache \u2014 requires Next 16+");
161
+ }
162
+ _refresh = mod.refresh;
163
+ return _refresh;
164
+ }
165
+ async function update(...tagList) {
166
+ const fn = await getUpdateTag();
167
+ for (const t of tagList) fn(t);
168
+ }
169
+ async function updateProducts() {
170
+ return update(tags.products());
171
+ }
172
+ async function updateProduct(id) {
173
+ return update(tags.product(id), tags.products());
174
+ }
175
+ async function updateCategories() {
176
+ return update(tags.categories());
177
+ }
178
+ async function updateCategory(id) {
179
+ return update(tags.category(id), tags.categoryProducts(id), tags.categories());
180
+ }
181
+ async function updateCollections() {
182
+ return update(tags.collections());
183
+ }
184
+ async function updateCollection(id) {
185
+ return update(tags.collection(id), tags.collectionProducts(id), tags.collections());
186
+ }
187
+ async function updateBusiness() {
188
+ return update(tags.business());
189
+ }
190
+ async function updateBrand() {
191
+ return update(tags.brand());
192
+ }
193
+ async function updateLocations() {
194
+ return update(tags.locations());
195
+ }
196
+ async function updateLocation(id) {
197
+ return update(tags.location(id), tags.locations());
198
+ }
199
+ async function updatePricing() {
200
+ return update(tags.pricing(), tags.products());
201
+ }
202
+ async function updateAddOns() {
203
+ return update(tags.addons());
204
+ }
205
+ async function updateAddOn(id) {
206
+ return update(tags.addon(id), tags.addons());
207
+ }
208
+ async function updateSubscriptions() {
209
+ return update(tags.subscriptions());
210
+ }
211
+ async function updateSubscription(id) {
212
+ return update(tags.subscription(id), tags.subscriptions());
213
+ }
214
+ async function updateStock(productId) {
215
+ return productId ? update(tags.stockFor(productId), tags.stock()) : update(tags.stock());
216
+ }
217
+ async function updateByTag(tag) {
218
+ return update(tag);
219
+ }
220
+ async function refreshPage() {
221
+ const fn = await getRefresh();
222
+ fn();
223
+ }
224
+
225
+ // src/server/revalidate-route.ts
226
+ var TIMESTAMP_HEADER = "x-cimplify-timestamp";
227
+ var SIGNATURE_HEADER = "x-cimplify-signature";
228
+ var SIGNATURE_PREFIX = "sha256=";
229
+ var MAX_SKEW_MS = 5 * 60 * 1e3;
230
+ var SECRET_ENV = "CIMPLIFY_REVALIDATE_SECRET";
231
+ var DEFAULT_PROFILE2 = "max";
232
+ async function revalidateRouteHandler(req, options = {}) {
233
+ const secret = options.secret ?? envSecret();
234
+ if (!secret) return text(`revalidate disabled: ${SECRET_ENV} not set`, 500);
235
+ const timestamp = req.headers.get(TIMESTAMP_HEADER);
236
+ const signature = req.headers.get(SIGNATURE_HEADER);
237
+ if (!timestamp || !signature) return text("missing auth headers", 401);
238
+ const ts = Number.parseInt(timestamp, 10);
239
+ const now = options.now ?? Date.now;
240
+ if (!Number.isFinite(ts) || Math.abs(now() - ts) > MAX_SKEW_MS) {
241
+ return text("stale or invalid timestamp", 401);
242
+ }
243
+ const body = await req.text();
244
+ if (!await verifyHmac(secret, `${timestamp}.${body}`, signature)) {
245
+ return text("invalid signature", 401);
246
+ }
247
+ let parsed;
248
+ try {
249
+ parsed = JSON.parse(body);
250
+ } catch {
251
+ return text("invalid json", 400);
252
+ }
253
+ const tags2 = Array.isArray(parsed.tags) ? parsed.tags.filter((t) => typeof t === "string" && t.length > 0) : [];
254
+ if (tags2.length === 0) return text("no tags", 400);
255
+ const profile = parseProfile(parsed.profile) ?? options.defaultProfile ?? DEFAULT_PROFILE2;
256
+ const revalidate2 = options.revalidateTag ?? await loadRevalidateTag();
257
+ for (const tag of tags2) revalidate2(tag, profile);
258
+ return Response.json({ ok: true, revalidated: tags2.length, profile });
259
+ }
260
+ function parseProfile(raw) {
261
+ if (typeof raw === "string" && raw.length > 0) return raw;
262
+ if (raw && typeof raw === "object" && "expire" in raw && typeof raw.expire === "number") {
263
+ return { expire: raw.expire };
264
+ }
265
+ return void 0;
266
+ }
267
+ var cachedRevalidateTag = null;
268
+ async function loadRevalidateTag() {
269
+ if (cachedRevalidateTag) return cachedRevalidateTag;
270
+ const specifier = "next/cache";
271
+ const mod = await import(
272
+ /* webpackIgnore: true */
273
+ /* @vite-ignore */
274
+ specifier
275
+ );
276
+ cachedRevalidateTag = mod.revalidateTag;
277
+ return cachedRevalidateTag;
278
+ }
279
+ async function verifyHmac(secret, payload, signatureHeader) {
280
+ if (!signatureHeader.startsWith(SIGNATURE_PREFIX)) return false;
281
+ const providedBytes = hexToBytes(signatureHeader.slice(SIGNATURE_PREFIX.length));
282
+ if (!providedBytes) return false;
283
+ const enc = new TextEncoder();
284
+ const key = await crypto.subtle.importKey(
285
+ "raw",
286
+ enc.encode(secret),
287
+ { name: "HMAC", hash: "SHA-256" },
288
+ false,
289
+ ["verify"]
290
+ );
291
+ return crypto.subtle.verify("HMAC", key, providedBytes, enc.encode(payload));
292
+ }
293
+ function hexToBytes(hex) {
294
+ if (hex.length % 2 !== 0 || !/^[0-9a-f]+$/i.test(hex)) return null;
295
+ const buf = new ArrayBuffer(hex.length / 2);
296
+ const out = new Uint8Array(buf);
297
+ for (let i = 0; i < hex.length; i += 2) {
298
+ out[i / 2] = Number.parseInt(hex.slice(i, i + 2), 16);
299
+ }
300
+ return out;
301
+ }
302
+ function envSecret() {
303
+ const proc = globalThis.process;
304
+ return proc?.env?.[SECRET_ENV];
88
305
  }
89
- async function revalidateByTag(tag) {
90
- return revalidate(tag);
306
+ function text(message, status) {
307
+ return new Response(message, { status, headers: { "content-type": "text/plain" } });
91
308
  }
92
309
 
93
- export { getServerClient, revalidateBusiness, revalidateByTag, revalidateCategories, revalidateCategory, revalidateCollection, revalidateCollections, revalidateProduct, revalidateProducts, tags };
310
+ export { getServerClient, refreshPage, revalidateAddOn, revalidateAddOns, revalidateBrand, revalidateBusiness, revalidateByTag, revalidateCategories, revalidateCategory, revalidateCollection, revalidateCollections, revalidateLocation, revalidateLocations, revalidatePricing, revalidateProduct, revalidateProducts, revalidateRouteHandler, revalidateStock, revalidateSubscription, revalidateSubscriptions, tags, updateAddOn, updateAddOns, updateBrand, updateBusiness, updateByTag, updateCategories, updateCategory, updateCollection, updateCollections, updateLocation, updateLocations, updatePricing, updateProduct, updateProducts, updateStock, updateSubscription, updateSubscriptions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cimplify/sdk",
3
- "version": "0.48.2",
3
+ "version": "0.49.1",
4
4
  "description": "Cimplify Commerce SDK for storefronts",
5
5
  "keywords": [
6
6
  "cimplify",
@@ -71,6 +71,30 @@
71
71
  "type": "component",
72
72
  "registryDependencies": []
73
73
  },
74
+ {
75
+ "name": "store-video",
76
+ "title": "StoreVideo",
77
+ "description": "Autoplay-muted-loop video for storefront heroes / product demos. Lazy-loaded, mobile-safe (playsInline), poster fallback for browsers without video.",
78
+ "type": "component",
79
+ "registryDependencies": []
80
+ },
81
+ {
82
+ "name": "product-model-3d",
83
+ "title": "ProductModel3D",
84
+ "description": "Interactive 3D product viewer wrapping Google's <model-viewer>. Loads the web component on demand; AR mode lights up on iOS (Quick Look) and Android (Scene Viewer) automatically.",
85
+ "type": "component",
86
+ "registryDependencies": []
87
+ },
88
+ {
89
+ "name": "media-gallery",
90
+ "title": "MediaGallery",
91
+ "description": "Like ProductImageGallery but accepts mixed image/video/3D items in one carousel. Thumbnails show a poster + small overlay icon for video/3D.",
92
+ "type": "component",
93
+ "registryDependencies": [
94
+ "store-video",
95
+ "product-model-3d"
96
+ ]
97
+ },
74
98
  {
75
99
  "name": "cart-summary",
76
100
  "title": "CartSummary",
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "media-gallery",
3
+ "title": "MediaGallery",
4
+ "description": "Like ProductImageGallery but accepts mixed image/video/3D items in one carousel. Thumbnails show a poster + small overlay icon for video/3D.",
5
+ "type": "component",
6
+ "registryDependencies": [
7
+ "store-video",
8
+ "product-model-3d"
9
+ ],
10
+ "files": [
11
+ {
12
+ "path": "media-gallery.tsx",
13
+ "content": "\"use client\";\n\nimport React, { useEffect, useMemo, useState } from \"react\";\nimport { RadioGroup } from \"@base-ui/react/radio-group\";\nimport { Radio } from \"@base-ui/react/radio\";\nimport { StoreVideo } from \"@cimplify/sdk/react\";\nimport { ProductModel3D } from \"@cimplify/sdk/react\";\n\nexport type MediaItem =\n | { type: \"image\"; src: string; alt?: string }\n | { type: \"video\"; src: string; poster?: string; alt?: string }\n | { type: \"model\"; src: string; iosSrc?: string; poster?: string; alt?: string };\n\nexport interface MediaGalleryProps {\n items: MediaItem[];\n productName: string;\n aspectRatio?: \"square\" | \"4/3\" | \"16/10\" | \"3/4\";\n className?: string;\n}\n\nconst ASPECT_STYLES: Record<string, React.CSSProperties> = {\n square: { aspectRatio: \"1/1\" },\n \"4/3\": { aspectRatio: \"4/3\" },\n \"16/10\": { aspectRatio: \"16/10\" },\n \"3/4\": { aspectRatio: \"3/4\" },\n};\n\nfunction thumbnailFor(item: MediaItem): string | null {\n if (item.type === \"image\") return item.src;\n return item.poster ?? null;\n}\n\nfunction thumbIcon(item: MediaItem): string | null {\n if (item.type === \"video\") return \"▶\";\n if (item.type === \"model\") return \"◆\";\n return null;\n}\n\n/**\n * MediaGallery — like ProductImageGallery, but accepts mixed image/video/3D\n * items in one carousel. The active item renders via the matching SDK\n * component (img / StoreVideo / ProductModel3D); thumbnails show a poster\n * (or the image itself) with a small overlay icon for video/3D.\n */\nexport function MediaGallery({\n items,\n productName,\n aspectRatio = \"4/3\",\n className,\n}: MediaGalleryProps): React.ReactElement | null {\n const validItems = useMemo(\n () => items.filter((i) => typeof i.src === \"string\" && i.src.trim().length > 0),\n [items],\n );\n\n const [selected, setSelected] = useState(0);\n\n useEffect(() => {\n setSelected(0);\n }, [validItems.length, productName]);\n\n if (validItems.length === 0) return null;\n\n const active = validItems[selected] ?? validItems[0];\n\n return (\n <div data-cimplify-media-gallery className={className}>\n <div\n data-cimplify-media-gallery-main\n style={{ position: \"relative\", overflow: \"hidden\", ...ASPECT_STYLES[aspectRatio] }}\n >\n {active.type === \"image\" ? (\n <img\n src={active.src}\n alt={active.alt ?? productName}\n style={{ width: \"100%\", height: \"100%\", objectFit: \"cover\" }}\n data-cimplify-media-gallery-active\n />\n ) : active.type === \"video\" ? (\n <StoreVideo\n src={active.src}\n poster={active.poster}\n alt={active.alt ?? productName}\n aspectRatio=\"square\"\n lazy={false}\n />\n ) : (\n <ProductModel3D\n src={active.src}\n iosSrc={active.iosSrc}\n poster={active.poster}\n alt={active.alt ?? productName}\n aspectRatio=\"square\"\n />\n )}\n </div>\n\n {validItems.length > 1 && (\n <RadioGroup\n aria-label={`${productName} media thumbnails`}\n value={String(selected)}\n onValueChange={(v) => setSelected(Number(v))}\n data-cimplify-media-gallery-thumbnails\n style={{ display: \"flex\", gap: \"0.5rem\", marginTop: \"0.75rem\" }}\n >\n {validItems.map((item, index) => {\n const thumb = thumbnailFor(item);\n const icon = thumbIcon(item);\n const isSelected = selected === index;\n return (\n <Radio.Root\n key={`${item.src}-${index}`}\n value={String(index)}\n data-cimplify-media-gallery-thumb\n data-selected={isSelected || undefined}\n data-type={item.type}\n style={{\n position: \"relative\",\n width: \"4rem\",\n height: \"4rem\",\n overflow: \"hidden\",\n padding: 0,\n border: \"none\",\n cursor: \"pointer\",\n backgroundColor: \"var(--muted, #f3f4f6)\",\n }}\n >\n {thumb ? (\n <img\n src={thumb}\n alt=\"\"\n style={{ width: \"100%\", height: \"100%\", objectFit: \"cover\" }}\n />\n ) : null}\n {icon ? (\n <span\n aria-hidden\n style={{\n position: \"absolute\",\n inset: 0,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n color: \"white\",\n fontSize: \"0.875rem\",\n textShadow: \"0 1px 2px rgba(0,0,0,0.6)\",\n pointerEvents: \"none\",\n }}\n >\n {icon}\n </span>\n ) : null}\n </Radio.Root>\n );\n })}\n </RadioGroup>\n )}\n </div>\n );\n}\n"
14
+ }
15
+ ]
16
+ }