@cimplify/cli 0.7.22 → 0.7.25

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 (43) hide show
  1. package/dist/{add-BNQSUAKS.mjs → add-DMA7USQR.mjs} +1 -1
  2. package/dist/{chunk-TC6KEJ3V.mjs → chunk-5SVTTTD2.mjs} +1 -1
  3. package/dist/{chunk-YUXVTC3O.mjs → chunk-ML6JUUXS.mjs} +34 -34
  4. package/dist/{chunk-AYVOCJTK.mjs → chunk-NUXKAEX5.mjs} +7 -7
  5. package/dist/dispatcher.mjs +9 -9
  6. package/dist/{doctor-3UN3HWZK.mjs → doctor-IOEKC6LJ.mjs} +2 -2
  7. package/dist/{explain-57MQ3RJ5.mjs → explain-GBKONBB3.mjs} +1 -1
  8. package/dist/{introspect-DX2OYRTY.mjs → introspect-PJ6SLINK.mjs} +2 -2
  9. package/dist/{list-LKO7J47M.mjs → list-ZTKE2YVE.mjs} +1 -1
  10. package/dist/{update-TY6HIDIQ.mjs → update-BZ53TKVN.mjs} +1 -1
  11. package/package.json +7 -7
  12. package/templates/storefront-auto/app/products/[slug]/page.tsx +4 -9
  13. package/templates/storefront-auto/bun.lock +2 -2
  14. package/templates/storefront-auto/components/account-pill.tsx +1 -1
  15. package/templates/storefront-auto/lib/product-offer.ts +46 -0
  16. package/templates/storefront-auto/package.json +1 -1
  17. package/templates/storefront-bakery/bun.lock +2 -2
  18. package/templates/storefront-bakery/components/account-pill.tsx +1 -1
  19. package/templates/storefront-bakery/package.json +1 -1
  20. package/templates/storefront-fashion/app/products/[slug]/page.tsx +4 -9
  21. package/templates/storefront-fashion/bun.lock +2 -2
  22. package/templates/storefront-fashion/components/account-pill.tsx +1 -1
  23. package/templates/storefront-fashion/lib/product-offer.ts +46 -0
  24. package/templates/storefront-fashion/package.json +1 -1
  25. package/templates/storefront-grocery/bun.lock +2 -2
  26. package/templates/storefront-grocery/components/account-pill.tsx +1 -1
  27. package/templates/storefront-grocery/package.json +1 -1
  28. package/templates/storefront-pharmacy/app/products/[slug]/page.tsx +4 -9
  29. package/templates/storefront-pharmacy/bun.lock +2 -2
  30. package/templates/storefront-pharmacy/components/account-pill.tsx +1 -1
  31. package/templates/storefront-pharmacy/lib/product-offer.ts +46 -0
  32. package/templates/storefront-pharmacy/package.json +1 -1
  33. package/templates/storefront-restaurant/bun.lock +2 -2
  34. package/templates/storefront-restaurant/components/account-pill.tsx +1 -1
  35. package/templates/storefront-restaurant/package.json +1 -1
  36. package/templates/storefront-retail/app/products/[slug]/page.tsx +4 -9
  37. package/templates/storefront-retail/bun.lock +2 -2
  38. package/templates/storefront-retail/components/account-pill.tsx +1 -1
  39. package/templates/storefront-retail/lib/product-offer.ts +46 -0
  40. package/templates/storefront-retail/package.json +1 -1
  41. package/templates/storefront-services/bun.lock +2 -2
  42. package/templates/storefront-services/components/account-pill.tsx +1 -1
  43. package/templates/storefront-services/package.json +1 -1
@@ -2,7 +2,7 @@
2
2
  // package.json
3
3
  var package_default = {
4
4
  name: "@cimplify/cli",
5
- version: "0.7.22",
5
+ version: "0.7.25",
6
6
  description: "Cimplify CLI \u2014 deploy, manage env vars, link projects, and scaffold storefronts",
7
7
  keywords: [
8
8
  "cimplify",
@@ -39,15 +39,15 @@ var package_default = {
39
39
  "test:run": "vitest run"
40
40
  },
41
41
  devDependencies: {
42
- "@aws-sdk/client-s3": "^3.700.0",
43
- "@types/node": "^25.6.0",
44
- "@typescript/native-preview": "^7.0.0-dev.20260503.1",
42
+ "@aws-sdk/client-s3": "^3.1070.0",
43
+ "@types/node": "^25.9.3",
44
+ "@typescript/native-preview": "^7.0.0-dev.20260616.1",
45
45
  tsup: "^8.5.1",
46
- typescript: "5.9.3",
47
- vitest: "^4.1.5"
46
+ typescript: "6.0.3",
47
+ vitest: "^4.1.9"
48
48
  },
49
49
  dependencies: {
50
- "@cimplify/sdk": "^0.83.0"
50
+ "@cimplify/sdk": "^0.86.0"
51
51
  }
52
52
  };
53
53
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { package_default } from './chunk-AYVOCJTK.mjs';
3
- import { TEMPLATES } from './chunk-YUXVTC3O.mjs';
2
+ import { package_default } from './chunk-NUXKAEX5.mjs';
3
+ import { TEMPLATES } from './chunk-ML6JUUXS.mjs';
4
4
 
5
5
  // src/dispatcher.ts
6
6
  var VERSION = package_default.version ?? "unknown";
@@ -144,16 +144,16 @@ var COMMANDS = {
144
144
  status: () => import('./status-JSYXM5RT.mjs'),
145
145
  dev: () => import('./dev-ONW2S77K.mjs'),
146
146
  mock: () => import('./mock-TSUKCLWU.mjs'),
147
- introspect: () => import('./introspect-DX2OYRTY.mjs'),
147
+ introspect: () => import('./introspect-PJ6SLINK.mjs'),
148
148
  inspect: () => import('./inspect-CGYX4DDF.mjs'),
149
- doctor: () => import('./doctor-3UN3HWZK.mjs'),
150
- explain: () => import('./explain-57MQ3RJ5.mjs'),
149
+ doctor: () => import('./doctor-IOEKC6LJ.mjs'),
150
+ explain: () => import('./explain-GBKONBB3.mjs'),
151
151
  assets: () => import('./assets-74SK63TR.mjs'),
152
152
  repo: () => import('./repo-KNQMSPVV.mjs'),
153
- list: () => import('./list-LKO7J47M.mjs'),
154
- add: () => import('./add-BNQSUAKS.mjs'),
155
- update: () => import('./update-TY6HIDIQ.mjs'),
156
- upgrade: () => import('./update-TY6HIDIQ.mjs'),
153
+ list: () => import('./list-ZTKE2YVE.mjs'),
154
+ add: () => import('./add-DMA7USQR.mjs'),
155
+ update: () => import('./update-BZ53TKVN.mjs'),
156
+ upgrade: () => import('./update-BZ53TKVN.mjs'),
157
157
  "auth-step-up": () => import('./auth-step-up-BIUYQJP6.mjs')
158
158
  };
159
159
  var COMMAND_PREFIXES = {
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { gatherIntrospection } from './chunk-TC6KEJ3V.mjs';
2
+ import { gatherIntrospection } from './chunk-5SVTTTD2.mjs';
3
3
  import './chunk-K5464A3L.mjs';
4
4
  import './chunk-DBZ3UOQ2.mjs';
5
- import './chunk-AYVOCJTK.mjs';
5
+ import './chunk-NUXKAEX5.mjs';
6
6
  import { parseArgs, flagBool } from './chunk-C4M3DXKC.mjs';
7
7
  import { ApiClient } from './chunk-MAOO6ZZ5.mjs';
8
8
  import { readAuthOrNull } from './chunk-UBAI443T.mjs';
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { package_default } from './chunk-AYVOCJTK.mjs';
2
+ import { package_default } from './chunk-NUXKAEX5.mjs';
3
3
  import { parseArgs } from './chunk-C4M3DXKC.mjs';
4
4
  import { bold, dim, info, result, CliError, CLI_ERROR_CODE } from './chunk-E2T2SBP5.mjs';
5
5
 
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- export { run as default, extractMockSeed, gatherIntrospection, renderIntrospection } from './chunk-TC6KEJ3V.mjs';
2
+ export { run as default, extractMockSeed, gatherIntrospection, renderIntrospection } from './chunk-5SVTTTD2.mjs';
3
3
  import './chunk-K5464A3L.mjs';
4
4
  import './chunk-DBZ3UOQ2.mjs';
5
- import './chunk-AYVOCJTK.mjs';
5
+ import './chunk-NUXKAEX5.mjs';
6
6
  import './chunk-C4M3DXKC.mjs';
7
7
  import './chunk-UBAI443T.mjs';
8
8
  import './chunk-E2T2SBP5.mjs';
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { REGISTRY_INDEX } from './chunk-YUXVTC3O.mjs';
2
+ import { REGISTRY_INDEX } from './chunk-ML6JUUXS.mjs';
3
3
  import { parseArgs, flagBool } from './chunk-C4M3DXKC.mjs';
4
4
  import { CliError, CLI_ERROR_CODE, info, bold, dim, green, result } from './chunk-E2T2SBP5.mjs';
5
5
 
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { package_default } from './chunk-AYVOCJTK.mjs';
2
+ import { package_default } from './chunk-NUXKAEX5.mjs';
3
3
  import { promptYesNo } from './chunk-ITAFAORS.mjs';
4
4
  import { parseArgs, flagBool, flagString } from './chunk-C4M3DXKC.mjs';
5
5
  import { success, bold, info, dim, result, failure, CliError, CLI_ERROR_CODE, step, isJsonMode } from './chunk-E2T2SBP5.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cimplify/cli",
3
- "version": "0.7.22",
3
+ "version": "0.7.25",
4
4
  "description": "Cimplify CLI — deploy, manage env vars, link projects, and scaffold storefronts",
5
5
  "keywords": [
6
6
  "cimplify",
@@ -37,14 +37,14 @@
37
37
  "test:run": "vitest run"
38
38
  },
39
39
  "devDependencies": {
40
- "@aws-sdk/client-s3": "^3.700.0",
41
- "@types/node": "^25.6.0",
42
- "@typescript/native-preview": "^7.0.0-dev.20260503.1",
40
+ "@aws-sdk/client-s3": "^3.1070.0",
41
+ "@types/node": "^25.9.3",
42
+ "@typescript/native-preview": "^7.0.0-dev.20260616.1",
43
43
  "tsup": "^8.5.1",
44
- "typescript": "5.9.3",
45
- "vitest": "^4.1.5"
44
+ "typescript": "6.0.3",
45
+ "vitest": "^4.1.9"
46
46
  },
47
47
  "dependencies": {
48
- "@cimplify/sdk": "^0.83.0"
48
+ "@cimplify/sdk": "^0.86.0"
49
49
  }
50
50
  }
@@ -11,6 +11,7 @@ import {
11
11
  import { ProductDetail } from "./product-detail";
12
12
  import { brand } from "@/lib/brand";
13
13
  import { getSiteUrl } from "@/lib/site-url";
14
+ import { buildProductOffer } from "@/lib/product-offer";
14
15
 
15
16
  // Pre-enumerate every product slug at build time so Next can emit cacheable
16
17
  // responses (s-maxage instead of no-store) for the route. Without this, every
@@ -29,7 +30,6 @@ export const revalidate = 3600;
29
30
 
30
31
  function productLd(product: ProductWithDetails, SITE_URL: string) {
31
32
  const image = product.image_url ?? product.images?.[0];
32
- const inStock = product.inventory_status?.in_stock !== false;
33
33
  return {
34
34
  "@context": "https://schema.org",
35
35
  "@type": "Product",
@@ -38,15 +38,10 @@ function productLd(product: ProductWithDetails, SITE_URL: string) {
38
38
  image: image ? [image] : undefined,
39
39
  sku: product.id,
40
40
  brand: { "@type": "Brand", name: brand.name },
41
- offers: {
42
- "@type": "Offer",
43
- price: product.default_price,
44
- priceCurrency: brand.currency,
45
- availability: inStock
46
- ? "https://schema.org/InStock"
47
- : "https://schema.org/OutOfStock",
41
+ offers: buildProductOffer(product, {
48
42
  url: `${SITE_URL}/products/${product.slug ?? product.id}`,
49
- },
43
+ currency: brand.currency,
44
+ }),
50
45
  };
51
46
  }
52
47
 
@@ -5,7 +5,7 @@
5
5
  "": {
6
6
  "name": "__STOREFRONT_NAME__",
7
7
  "dependencies": {
8
- "@cimplify/sdk": "^0.64.1",
8
+ "@cimplify/sdk": "^0.84.0",
9
9
  "next": "^16.2.6",
10
10
  "react": "^19.0.0",
11
11
  "react-dom": "^19.0.0",
@@ -31,7 +31,7 @@
31
31
 
32
32
  "@base-ui/utils": ["@base-ui/utils@0.2.9", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@floating-ui/utils": "^0.2.11", "reselect": "^5.1.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-x/PDDCYzoqPpjrdyb3VcyylTI2IjUXEtYDGi5foh7KsnmNJIIaVwA2GLgDH1dps1GgXiJbA60hM+AyuTfQzIvw=="],
33
33
 
34
- "@cimplify/sdk": ["@cimplify/sdk@0.64.1", "", { "dependencies": { "@base-ui/react": "^1.4.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", "jose": "^6.2.3", "libphonenumber-js": "1.12.41", "react-day-picker": "^9.14.0", "tailwind-merge": "^3.5.0", "xss": "^1.0.15", "zod": "^4.4.3" }, "peerDependencies": { "@paystack/inline-js": "^2.22.8", "msw": ">=2.0.0", "react": ">=17.0.0", "vitest": ">=2.0.0" }, "optionalPeers": ["@paystack/inline-js", "msw", "react", "vitest"], "bin": { "cimplify-mock": "dist/mock/cli.mjs" } }, "sha512-qY3ngAxAyeRwHR5jJZ7eeeNKgvayZlVUuNglp779bJiwtwL3cZ8Ux4L+k3SgRzgAxtKz9YU2mmVBZxEu14frUw=="],
34
+ "@cimplify/sdk": ["@cimplify/sdk@0.84.0", "", { "dependencies": { "@base-ui/react": "^1.4.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", "jose": "^6.2.3", "libphonenumber-js": "1.12.41", "react-day-picker": "^9.14.0", "tailwind-merge": "^3.5.0", "xss": "^1.0.15", "zod": "^4.4.3" }, "peerDependencies": { "@paystack/inline-js": "^2.22.8", "msw": ">=2.0.0", "react": ">=17.0.0", "react-dom": ">=17.0.0", "vitest": ">=2.0.0" }, "optionalPeers": ["@paystack/inline-js", "msw", "react", "react-dom", "vitest"], "bin": { "cimplify-mock": "dist/mock/cli.mjs" } }, "sha512-0fhHjNgBrxLt3pjI51gVfAj8lrd81NCw0DzsTdLIiM59UqOwCWCt166NqPOZg6VxpMITeE+z+fDKJn1tJpV/0Q=="],
35
35
 
36
36
  "@date-fns/tz": ["@date-fns/tz@1.4.1", "", {}, "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA=="],
37
37
 
@@ -23,7 +23,7 @@ export function AccountPill() {
23
23
  });
24
24
  }, [session, refresh]);
25
25
 
26
- if (loading || !CLIENT_ID) return <span className="h-9 w-24" aria-hidden />;
26
+ if (loading) return <span className="h-9 w-24" aria-hidden />;
27
27
 
28
28
  return (
29
29
  <CimplifyUserButton
@@ -0,0 +1,46 @@
1
+ import { parsePrice, type ProductWithDetails } from "@cimplify/sdk";
2
+
3
+ const IN_STOCK = "https://schema.org/InStock";
4
+ const OUT_OF_STOCK = "https://schema.org/OutOfStock";
5
+
6
+ interface SaleOverlay {
7
+ price: string | number;
8
+ ends_at: string;
9
+ // Present only for capped, claims-driven sales, whose price moves per purchase.
10
+ cap?: { claimed: number; of: number } | null;
11
+ }
12
+
13
+ export interface ProductOffer {
14
+ "@type": "Offer";
15
+ price: number;
16
+ priceCurrency: string;
17
+ priceValidUntil?: string;
18
+ availability: string;
19
+ url: string;
20
+ }
21
+
22
+ /**
23
+ * schema.org Offer for a product page. Indexes the price a shopper actually
24
+ * sees: a scheduled/time sale carries the sale price plus a validity window; a
25
+ * capped (claims-driven) sale keeps the base price out of the index, since its
26
+ * number moves on every purchase and would mismatch the product feed.
27
+ */
28
+ export function buildProductOffer(
29
+ product: ProductWithDetails & { sale?: SaleOverlay | null },
30
+ opts: { url: string; currency: string },
31
+ ): ProductOffer {
32
+ const inStock = product.inventory_status?.in_stock !== false;
33
+ const sale = product.sale;
34
+ const indexableSale = sale && !sale.cap ? sale : null;
35
+ const offer: ProductOffer = {
36
+ "@type": "Offer",
37
+ price: parsePrice(indexableSale ? indexableSale.price : product.default_price),
38
+ priceCurrency: opts.currency,
39
+ availability: inStock ? IN_STOCK : OUT_OF_STOCK,
40
+ url: opts.url,
41
+ };
42
+ if (indexableSale) {
43
+ offer.priceValidUntil = indexableSale.ends_at;
44
+ }
45
+ return offer;
46
+ }
@@ -17,7 +17,7 @@
17
17
  "check": "bun run typecheck && bun run test:run"
18
18
  },
19
19
  "dependencies": {
20
- "@cimplify/sdk": "^0.83.0",
20
+ "@cimplify/sdk": "^0.86.0",
21
21
  "next": "^16.2.6",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
@@ -5,7 +5,7 @@
5
5
  "": {
6
6
  "name": "__STOREFRONT_NAME__",
7
7
  "dependencies": {
8
- "@cimplify/sdk": "^0.64.1",
8
+ "@cimplify/sdk": "^0.84.0",
9
9
  "next": "^16.2.6",
10
10
  "react": "^19.0.0",
11
11
  "react-dom": "^19.0.0",
@@ -31,7 +31,7 @@
31
31
 
32
32
  "@base-ui/utils": ["@base-ui/utils@0.2.9", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@floating-ui/utils": "^0.2.11", "reselect": "^5.1.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-x/PDDCYzoqPpjrdyb3VcyylTI2IjUXEtYDGi5foh7KsnmNJIIaVwA2GLgDH1dps1GgXiJbA60hM+AyuTfQzIvw=="],
33
33
 
34
- "@cimplify/sdk": ["@cimplify/sdk@0.64.1", "", { "dependencies": { "@base-ui/react": "^1.4.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", "jose": "^6.2.3", "libphonenumber-js": "1.12.41", "react-day-picker": "^9.14.0", "tailwind-merge": "^3.5.0", "xss": "^1.0.15", "zod": "^4.4.3" }, "peerDependencies": { "@paystack/inline-js": "^2.22.8", "msw": ">=2.0.0", "react": ">=17.0.0", "vitest": ">=2.0.0" }, "optionalPeers": ["@paystack/inline-js", "msw", "react", "vitest"], "bin": { "cimplify-mock": "dist/mock/cli.mjs" } }, "sha512-qY3ngAxAyeRwHR5jJZ7eeeNKgvayZlVUuNglp779bJiwtwL3cZ8Ux4L+k3SgRzgAxtKz9YU2mmVBZxEu14frUw=="],
34
+ "@cimplify/sdk": ["@cimplify/sdk@0.84.0", "", { "dependencies": { "@base-ui/react": "^1.4.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", "jose": "^6.2.3", "libphonenumber-js": "1.12.41", "react-day-picker": "^9.14.0", "tailwind-merge": "^3.5.0", "xss": "^1.0.15", "zod": "^4.4.3" }, "peerDependencies": { "@paystack/inline-js": "^2.22.8", "msw": ">=2.0.0", "react": ">=17.0.0", "react-dom": ">=17.0.0", "vitest": ">=2.0.0" }, "optionalPeers": ["@paystack/inline-js", "msw", "react", "react-dom", "vitest"], "bin": { "cimplify-mock": "dist/mock/cli.mjs" } }, "sha512-0fhHjNgBrxLt3pjI51gVfAj8lrd81NCw0DzsTdLIiM59UqOwCWCt166NqPOZg6VxpMITeE+z+fDKJn1tJpV/0Q=="],
35
35
 
36
36
  "@date-fns/tz": ["@date-fns/tz@1.5.0", "", {}, "sha512-lwYN/vDPeNRULcepoE/LO2Pgx+7/RV+S9ARfbc9lr2DtGkOD7pAiruHvbR1RX3Qyf6ja47EWJDMsNK5vK08DJg=="],
37
37
 
@@ -23,7 +23,7 @@ export function AccountPill() {
23
23
  });
24
24
  }, [session, refresh]);
25
25
 
26
- if (loading || !CLIENT_ID) return <span className="h-9 w-24" aria-hidden />;
26
+ if (loading) return <span className="h-9 w-24" aria-hidden />;
27
27
 
28
28
  return (
29
29
  <CimplifyUserButton
@@ -17,7 +17,7 @@
17
17
  "check": "bun run typecheck && bun run test:run"
18
18
  },
19
19
  "dependencies": {
20
- "@cimplify/sdk": "^0.83.0",
20
+ "@cimplify/sdk": "^0.86.0",
21
21
  "next": "^16.2.6",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
@@ -11,6 +11,7 @@ import {
11
11
  import { ProductDetail } from "./product-detail";
12
12
  import { brand } from "@/lib/brand";
13
13
  import { getSiteUrl } from "@/lib/site-url";
14
+ import { buildProductOffer } from "@/lib/product-offer";
14
15
 
15
16
  // Pre-enumerate every product slug at build time so Next can emit cacheable
16
17
  // responses (s-maxage instead of no-store) for the route. Without this, every
@@ -29,7 +30,6 @@ export const revalidate = 3600;
29
30
 
30
31
  function productLd(product: ProductWithDetails, SITE_URL: string) {
31
32
  const image = product.image_url ?? product.images?.[0];
32
- const inStock = product.inventory_status?.in_stock !== false;
33
33
  return {
34
34
  "@context": "https://schema.org",
35
35
  "@type": "Product",
@@ -38,15 +38,10 @@ function productLd(product: ProductWithDetails, SITE_URL: string) {
38
38
  image: image ? [image] : undefined,
39
39
  sku: product.id,
40
40
  brand: { "@type": "Brand", name: brand.name },
41
- offers: {
42
- "@type": "Offer",
43
- price: product.default_price,
44
- priceCurrency: brand.currency,
45
- availability: inStock
46
- ? "https://schema.org/InStock"
47
- : "https://schema.org/OutOfStock",
41
+ offers: buildProductOffer(product, {
48
42
  url: `${SITE_URL}/products/${product.slug ?? product.id}`,
49
- },
43
+ currency: brand.currency,
44
+ }),
50
45
  };
51
46
  }
52
47
 
@@ -5,7 +5,7 @@
5
5
  "": {
6
6
  "name": "__STOREFRONT_NAME__",
7
7
  "dependencies": {
8
- "@cimplify/sdk": "^0.64.1",
8
+ "@cimplify/sdk": "^0.84.0",
9
9
  "next": "^16.2.6",
10
10
  "react": "^19.0.0",
11
11
  "react-dom": "^19.0.0",
@@ -32,7 +32,7 @@
32
32
 
33
33
  "@base-ui/utils": ["@base-ui/utils@0.2.9", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@floating-ui/utils": "^0.2.11", "reselect": "^5.1.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-x/PDDCYzoqPpjrdyb3VcyylTI2IjUXEtYDGi5foh7KsnmNJIIaVwA2GLgDH1dps1GgXiJbA60hM+AyuTfQzIvw=="],
34
34
 
35
- "@cimplify/sdk": ["@cimplify/sdk@0.64.1", "", { "dependencies": { "@base-ui/react": "^1.4.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", "jose": "^6.2.3", "libphonenumber-js": "1.12.41", "react-day-picker": "^9.14.0", "tailwind-merge": "^3.5.0", "xss": "^1.0.15", "zod": "^4.4.3" }, "peerDependencies": { "@paystack/inline-js": "^2.22.8", "msw": ">=2.0.0", "react": ">=17.0.0", "vitest": ">=2.0.0" }, "optionalPeers": ["@paystack/inline-js", "msw", "react", "vitest"], "bin": { "cimplify-mock": "dist/mock/cli.mjs" } }, "sha512-qY3ngAxAyeRwHR5jJZ7eeeNKgvayZlVUuNglp779bJiwtwL3cZ8Ux4L+k3SgRzgAxtKz9YU2mmVBZxEu14frUw=="],
35
+ "@cimplify/sdk": ["@cimplify/sdk@0.84.0", "", { "dependencies": { "@base-ui/react": "^1.4.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", "jose": "^6.2.3", "libphonenumber-js": "1.12.41", "react-day-picker": "^9.14.0", "tailwind-merge": "^3.5.0", "xss": "^1.0.15", "zod": "^4.4.3" }, "peerDependencies": { "@paystack/inline-js": "^2.22.8", "msw": ">=2.0.0", "react": ">=17.0.0", "react-dom": ">=17.0.0", "vitest": ">=2.0.0" }, "optionalPeers": ["@paystack/inline-js", "msw", "react", "react-dom", "vitest"], "bin": { "cimplify-mock": "dist/mock/cli.mjs" } }, "sha512-0fhHjNgBrxLt3pjI51gVfAj8lrd81NCw0DzsTdLIiM59UqOwCWCt166NqPOZg6VxpMITeE+z+fDKJn1tJpV/0Q=="],
36
36
 
37
37
  "@date-fns/tz": ["@date-fns/tz@1.4.1", "", {}, "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA=="],
38
38
 
@@ -23,7 +23,7 @@ export function AccountPill() {
23
23
  });
24
24
  }, [session, refresh]);
25
25
 
26
- if (loading || !CLIENT_ID) return <span className="h-9 w-24" aria-hidden />;
26
+ if (loading) return <span className="h-9 w-24" aria-hidden />;
27
27
 
28
28
  return (
29
29
  <CimplifyUserButton
@@ -0,0 +1,46 @@
1
+ import { parsePrice, type ProductWithDetails } from "@cimplify/sdk";
2
+
3
+ const IN_STOCK = "https://schema.org/InStock";
4
+ const OUT_OF_STOCK = "https://schema.org/OutOfStock";
5
+
6
+ interface SaleOverlay {
7
+ price: string | number;
8
+ ends_at: string;
9
+ // Present only for capped, claims-driven sales, whose price moves per purchase.
10
+ cap?: { claimed: number; of: number } | null;
11
+ }
12
+
13
+ export interface ProductOffer {
14
+ "@type": "Offer";
15
+ price: number;
16
+ priceCurrency: string;
17
+ priceValidUntil?: string;
18
+ availability: string;
19
+ url: string;
20
+ }
21
+
22
+ /**
23
+ * schema.org Offer for a product page. Indexes the price a shopper actually
24
+ * sees: a scheduled/time sale carries the sale price plus a validity window; a
25
+ * capped (claims-driven) sale keeps the base price out of the index, since its
26
+ * number moves on every purchase and would mismatch the product feed.
27
+ */
28
+ export function buildProductOffer(
29
+ product: ProductWithDetails & { sale?: SaleOverlay | null },
30
+ opts: { url: string; currency: string },
31
+ ): ProductOffer {
32
+ const inStock = product.inventory_status?.in_stock !== false;
33
+ const sale = product.sale;
34
+ const indexableSale = sale && !sale.cap ? sale : null;
35
+ const offer: ProductOffer = {
36
+ "@type": "Offer",
37
+ price: parsePrice(indexableSale ? indexableSale.price : product.default_price),
38
+ priceCurrency: opts.currency,
39
+ availability: inStock ? IN_STOCK : OUT_OF_STOCK,
40
+ url: opts.url,
41
+ };
42
+ if (indexableSale) {
43
+ offer.priceValidUntil = indexableSale.ends_at;
44
+ }
45
+ return offer;
46
+ }
@@ -19,7 +19,7 @@
19
19
  "check": "bun run typecheck && bun run test:run"
20
20
  },
21
21
  "dependencies": {
22
- "@cimplify/sdk": "^0.83.0",
22
+ "@cimplify/sdk": "^0.86.0",
23
23
  "next": "^16.2.6",
24
24
  "react": "^19.0.0",
25
25
  "react-dom": "^19.0.0"
@@ -5,7 +5,7 @@
5
5
  "": {
6
6
  "name": "__STOREFRONT_NAME__",
7
7
  "dependencies": {
8
- "@cimplify/sdk": "^0.64.1",
8
+ "@cimplify/sdk": "^0.84.0",
9
9
  "next": "^16.2.6",
10
10
  "react": "^19.0.0",
11
11
  "react-dom": "^19.0.0",
@@ -31,7 +31,7 @@
31
31
 
32
32
  "@base-ui/utils": ["@base-ui/utils@0.2.9", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@floating-ui/utils": "^0.2.11", "reselect": "^5.1.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-x/PDDCYzoqPpjrdyb3VcyylTI2IjUXEtYDGi5foh7KsnmNJIIaVwA2GLgDH1dps1GgXiJbA60hM+AyuTfQzIvw=="],
33
33
 
34
- "@cimplify/sdk": ["@cimplify/sdk@0.64.1", "", { "dependencies": { "@base-ui/react": "^1.4.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", "jose": "^6.2.3", "libphonenumber-js": "1.12.41", "react-day-picker": "^9.14.0", "tailwind-merge": "^3.5.0", "xss": "^1.0.15", "zod": "^4.4.3" }, "peerDependencies": { "@paystack/inline-js": "^2.22.8", "msw": ">=2.0.0", "react": ">=17.0.0", "vitest": ">=2.0.0" }, "optionalPeers": ["@paystack/inline-js", "msw", "react", "vitest"], "bin": { "cimplify-mock": "dist/mock/cli.mjs" } }, "sha512-qY3ngAxAyeRwHR5jJZ7eeeNKgvayZlVUuNglp779bJiwtwL3cZ8Ux4L+k3SgRzgAxtKz9YU2mmVBZxEu14frUw=="],
34
+ "@cimplify/sdk": ["@cimplify/sdk@0.84.0", "", { "dependencies": { "@base-ui/react": "^1.4.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", "jose": "^6.2.3", "libphonenumber-js": "1.12.41", "react-day-picker": "^9.14.0", "tailwind-merge": "^3.5.0", "xss": "^1.0.15", "zod": "^4.4.3" }, "peerDependencies": { "@paystack/inline-js": "^2.22.8", "msw": ">=2.0.0", "react": ">=17.0.0", "react-dom": ">=17.0.0", "vitest": ">=2.0.0" }, "optionalPeers": ["@paystack/inline-js", "msw", "react", "react-dom", "vitest"], "bin": { "cimplify-mock": "dist/mock/cli.mjs" } }, "sha512-0fhHjNgBrxLt3pjI51gVfAj8lrd81NCw0DzsTdLIiM59UqOwCWCt166NqPOZg6VxpMITeE+z+fDKJn1tJpV/0Q=="],
35
35
 
36
36
  "@date-fns/tz": ["@date-fns/tz@1.4.1", "", {}, "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA=="],
37
37
 
@@ -23,7 +23,7 @@ export function AccountPill() {
23
23
  });
24
24
  }, [session, refresh]);
25
25
 
26
- if (loading || !CLIENT_ID) return <span className="h-9 w-24" aria-hidden />;
26
+ if (loading) return <span className="h-9 w-24" aria-hidden />;
27
27
 
28
28
  return (
29
29
  <CimplifyUserButton
@@ -17,7 +17,7 @@
17
17
  "check": "bun run typecheck && bun run test:run"
18
18
  },
19
19
  "dependencies": {
20
- "@cimplify/sdk": "^0.83.0",
20
+ "@cimplify/sdk": "^0.86.0",
21
21
  "next": "^16.2.6",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
@@ -11,6 +11,7 @@ import {
11
11
  import { ProductDetail } from "./product-detail";
12
12
  import { brand } from "@/lib/brand";
13
13
  import { getSiteUrl } from "@/lib/site-url";
14
+ import { buildProductOffer } from "@/lib/product-offer";
14
15
 
15
16
  // Pre-enumerate every product slug at build time so Next can emit cacheable
16
17
  // responses (s-maxage instead of no-store) for the route. Without this, every
@@ -29,7 +30,6 @@ export const revalidate = 3600;
29
30
 
30
31
  function productLd(product: ProductWithDetails, SITE_URL: string) {
31
32
  const image = product.image_url ?? product.images?.[0];
32
- const inStock = product.inventory_status?.in_stock !== false;
33
33
  return {
34
34
  "@context": "https://schema.org",
35
35
  "@type": "Product",
@@ -38,15 +38,10 @@ function productLd(product: ProductWithDetails, SITE_URL: string) {
38
38
  image: image ? [image] : undefined,
39
39
  sku: product.id,
40
40
  brand: { "@type": "Brand", name: brand.name },
41
- offers: {
42
- "@type": "Offer",
43
- price: product.default_price,
44
- priceCurrency: brand.currency,
45
- availability: inStock
46
- ? "https://schema.org/InStock"
47
- : "https://schema.org/OutOfStock",
41
+ offers: buildProductOffer(product, {
48
42
  url: `${SITE_URL}/products/${product.slug ?? product.id}`,
49
- },
43
+ currency: brand.currency,
44
+ }),
50
45
  };
51
46
  }
52
47
 
@@ -5,7 +5,7 @@
5
5
  "": {
6
6
  "name": "__STOREFRONT_NAME__",
7
7
  "dependencies": {
8
- "@cimplify/sdk": "^0.64.1",
8
+ "@cimplify/sdk": "^0.84.0",
9
9
  "next": "^16.2.6",
10
10
  "react": "^19.0.0",
11
11
  "react-dom": "^19.0.0",
@@ -31,7 +31,7 @@
31
31
 
32
32
  "@base-ui/utils": ["@base-ui/utils@0.2.9", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@floating-ui/utils": "^0.2.11", "reselect": "^5.1.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-x/PDDCYzoqPpjrdyb3VcyylTI2IjUXEtYDGi5foh7KsnmNJIIaVwA2GLgDH1dps1GgXiJbA60hM+AyuTfQzIvw=="],
33
33
 
34
- "@cimplify/sdk": ["@cimplify/sdk@0.64.1", "", { "dependencies": { "@base-ui/react": "^1.4.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", "jose": "^6.2.3", "libphonenumber-js": "1.12.41", "react-day-picker": "^9.14.0", "tailwind-merge": "^3.5.0", "xss": "^1.0.15", "zod": "^4.4.3" }, "peerDependencies": { "@paystack/inline-js": "^2.22.8", "msw": ">=2.0.0", "react": ">=17.0.0", "vitest": ">=2.0.0" }, "optionalPeers": ["@paystack/inline-js", "msw", "react", "vitest"], "bin": { "cimplify-mock": "dist/mock/cli.mjs" } }, "sha512-qY3ngAxAyeRwHR5jJZ7eeeNKgvayZlVUuNglp779bJiwtwL3cZ8Ux4L+k3SgRzgAxtKz9YU2mmVBZxEu14frUw=="],
34
+ "@cimplify/sdk": ["@cimplify/sdk@0.84.0", "", { "dependencies": { "@base-ui/react": "^1.4.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", "jose": "^6.2.3", "libphonenumber-js": "1.12.41", "react-day-picker": "^9.14.0", "tailwind-merge": "^3.5.0", "xss": "^1.0.15", "zod": "^4.4.3" }, "peerDependencies": { "@paystack/inline-js": "^2.22.8", "msw": ">=2.0.0", "react": ">=17.0.0", "react-dom": ">=17.0.0", "vitest": ">=2.0.0" }, "optionalPeers": ["@paystack/inline-js", "msw", "react", "react-dom", "vitest"], "bin": { "cimplify-mock": "dist/mock/cli.mjs" } }, "sha512-0fhHjNgBrxLt3pjI51gVfAj8lrd81NCw0DzsTdLIiM59UqOwCWCt166NqPOZg6VxpMITeE+z+fDKJn1tJpV/0Q=="],
35
35
 
36
36
  "@date-fns/tz": ["@date-fns/tz@1.4.1", "", {}, "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA=="],
37
37
 
@@ -23,7 +23,7 @@ export function AccountPill() {
23
23
  });
24
24
  }, [session, refresh]);
25
25
 
26
- if (loading || !CLIENT_ID) return <span className="h-9 w-24" aria-hidden />;
26
+ if (loading) return <span className="h-9 w-24" aria-hidden />;
27
27
 
28
28
  return (
29
29
  <CimplifyUserButton
@@ -0,0 +1,46 @@
1
+ import { parsePrice, type ProductWithDetails } from "@cimplify/sdk";
2
+
3
+ const IN_STOCK = "https://schema.org/InStock";
4
+ const OUT_OF_STOCK = "https://schema.org/OutOfStock";
5
+
6
+ interface SaleOverlay {
7
+ price: string | number;
8
+ ends_at: string;
9
+ // Present only for capped, claims-driven sales, whose price moves per purchase.
10
+ cap?: { claimed: number; of: number } | null;
11
+ }
12
+
13
+ export interface ProductOffer {
14
+ "@type": "Offer";
15
+ price: number;
16
+ priceCurrency: string;
17
+ priceValidUntil?: string;
18
+ availability: string;
19
+ url: string;
20
+ }
21
+
22
+ /**
23
+ * schema.org Offer for a product page. Indexes the price a shopper actually
24
+ * sees: a scheduled/time sale carries the sale price plus a validity window; a
25
+ * capped (claims-driven) sale keeps the base price out of the index, since its
26
+ * number moves on every purchase and would mismatch the product feed.
27
+ */
28
+ export function buildProductOffer(
29
+ product: ProductWithDetails & { sale?: SaleOverlay | null },
30
+ opts: { url: string; currency: string },
31
+ ): ProductOffer {
32
+ const inStock = product.inventory_status?.in_stock !== false;
33
+ const sale = product.sale;
34
+ const indexableSale = sale && !sale.cap ? sale : null;
35
+ const offer: ProductOffer = {
36
+ "@type": "Offer",
37
+ price: parsePrice(indexableSale ? indexableSale.price : product.default_price),
38
+ priceCurrency: opts.currency,
39
+ availability: inStock ? IN_STOCK : OUT_OF_STOCK,
40
+ url: opts.url,
41
+ };
42
+ if (indexableSale) {
43
+ offer.priceValidUntil = indexableSale.ends_at;
44
+ }
45
+ return offer;
46
+ }
@@ -17,7 +17,7 @@
17
17
  "check": "bun run typecheck && bun run test:run"
18
18
  },
19
19
  "dependencies": {
20
- "@cimplify/sdk": "^0.83.0",
20
+ "@cimplify/sdk": "^0.86.0",
21
21
  "next": "^16.2.6",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
@@ -5,7 +5,7 @@
5
5
  "": {
6
6
  "name": "__STOREFRONT_NAME__",
7
7
  "dependencies": {
8
- "@cimplify/sdk": "^0.64.1",
8
+ "@cimplify/sdk": "^0.84.0",
9
9
  "next": "^16.2.6",
10
10
  "react": "^19.0.0",
11
11
  "react-dom": "^19.0.0",
@@ -31,7 +31,7 @@
31
31
 
32
32
  "@base-ui/utils": ["@base-ui/utils@0.2.9", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@floating-ui/utils": "^0.2.11", "reselect": "^5.1.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-x/PDDCYzoqPpjrdyb3VcyylTI2IjUXEtYDGi5foh7KsnmNJIIaVwA2GLgDH1dps1GgXiJbA60hM+AyuTfQzIvw=="],
33
33
 
34
- "@cimplify/sdk": ["@cimplify/sdk@0.64.1", "", { "dependencies": { "@base-ui/react": "^1.4.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", "jose": "^6.2.3", "libphonenumber-js": "1.12.41", "react-day-picker": "^9.14.0", "tailwind-merge": "^3.5.0", "xss": "^1.0.15", "zod": "^4.4.3" }, "peerDependencies": { "@paystack/inline-js": "^2.22.8", "msw": ">=2.0.0", "react": ">=17.0.0", "vitest": ">=2.0.0" }, "optionalPeers": ["@paystack/inline-js", "msw", "react", "vitest"], "bin": { "cimplify-mock": "dist/mock/cli.mjs" } }, "sha512-qY3ngAxAyeRwHR5jJZ7eeeNKgvayZlVUuNglp779bJiwtwL3cZ8Ux4L+k3SgRzgAxtKz9YU2mmVBZxEu14frUw=="],
34
+ "@cimplify/sdk": ["@cimplify/sdk@0.84.0", "", { "dependencies": { "@base-ui/react": "^1.4.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", "jose": "^6.2.3", "libphonenumber-js": "1.12.41", "react-day-picker": "^9.14.0", "tailwind-merge": "^3.5.0", "xss": "^1.0.15", "zod": "^4.4.3" }, "peerDependencies": { "@paystack/inline-js": "^2.22.8", "msw": ">=2.0.0", "react": ">=17.0.0", "react-dom": ">=17.0.0", "vitest": ">=2.0.0" }, "optionalPeers": ["@paystack/inline-js", "msw", "react", "react-dom", "vitest"], "bin": { "cimplify-mock": "dist/mock/cli.mjs" } }, "sha512-0fhHjNgBrxLt3pjI51gVfAj8lrd81NCw0DzsTdLIiM59UqOwCWCt166NqPOZg6VxpMITeE+z+fDKJn1tJpV/0Q=="],
35
35
 
36
36
  "@date-fns/tz": ["@date-fns/tz@1.4.1", "", {}, "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA=="],
37
37