@cimplify/cli 0.7.2 → 0.7.4

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 (76) hide show
  1. package/dist/{add-5NW7N5CJ.mjs → add-4PBCFGM3.mjs} +1 -1
  2. package/dist/{chunk-26ETGFW6.mjs → chunk-LRTPNNQG.mjs} +1 -1
  3. package/dist/{chunk-ZVEQGQ2Z.mjs → chunk-NTY7JESF.mjs} +2 -2
  4. package/dist/chunk-QOPMMTVI.mjs +5763 -0
  5. package/dist/dispatcher.mjs +9 -9
  6. package/dist/{doctor-XLBDC6NO.mjs → doctor-ZVYWEE53.mjs} +2 -2
  7. package/dist/{explain-PKTL7ZR6.mjs → explain-S6S33HPN.mjs} +1 -1
  8. package/dist/{introspect-FPBV3O4N.mjs → introspect-FFB7TA2E.mjs} +2 -2
  9. package/dist/{list-N7FKBQ3Y.mjs → list-RLWFTEP4.mjs} +1 -1
  10. package/dist/{update-ZC3JGKLI.mjs → update-WH6NDWIM.mjs} +1 -1
  11. package/package.json +2 -2
  12. package/templates/storefront-auto/app/auth/callback/route.ts +16 -0
  13. package/templates/storefront-auto/app/auth/session/route.ts +11 -0
  14. package/templates/storefront-auto/app/auth/signout/route.ts +11 -0
  15. package/templates/storefront-auto/components/account-iframe.tsx +7 -1
  16. package/templates/storefront-auto/components/account-pill.tsx +49 -0
  17. package/templates/storefront-auto/components/header.tsx +2 -0
  18. package/templates/storefront-auto/lib/auth.ts +35 -0
  19. package/templates/storefront-auto/package.json +1 -1
  20. package/templates/storefront-bakery/app/auth/callback/route.ts +16 -0
  21. package/templates/storefront-bakery/app/auth/session/route.ts +11 -0
  22. package/templates/storefront-bakery/app/auth/signout/route.ts +11 -0
  23. package/templates/storefront-bakery/components/account-iframe.tsx +7 -1
  24. package/templates/storefront-bakery/components/account-pill.tsx +49 -0
  25. package/templates/storefront-bakery/components/header.tsx +2 -0
  26. package/templates/storefront-bakery/lib/auth.ts +35 -0
  27. package/templates/storefront-bakery/package.json +1 -1
  28. package/templates/storefront-fashion/app/auth/callback/route.ts +16 -0
  29. package/templates/storefront-fashion/app/auth/session/route.ts +11 -0
  30. package/templates/storefront-fashion/app/auth/signout/route.ts +11 -0
  31. package/templates/storefront-fashion/components/account-iframe.tsx +7 -1
  32. package/templates/storefront-fashion/components/account-pill.tsx +49 -0
  33. package/templates/storefront-fashion/components/header.tsx +2 -0
  34. package/templates/storefront-fashion/lib/auth.ts +35 -0
  35. package/templates/storefront-fashion/package.json +1 -1
  36. package/templates/storefront-grocery/app/auth/callback/route.ts +16 -0
  37. package/templates/storefront-grocery/app/auth/session/route.ts +11 -0
  38. package/templates/storefront-grocery/app/auth/signout/route.ts +11 -0
  39. package/templates/storefront-grocery/components/account-iframe.tsx +7 -1
  40. package/templates/storefront-grocery/components/account-pill.tsx +49 -0
  41. package/templates/storefront-grocery/components/header.tsx +2 -0
  42. package/templates/storefront-grocery/lib/auth.ts +35 -0
  43. package/templates/storefront-grocery/package.json +1 -1
  44. package/templates/storefront-pharmacy/app/auth/callback/route.ts +16 -0
  45. package/templates/storefront-pharmacy/app/auth/session/route.ts +11 -0
  46. package/templates/storefront-pharmacy/app/auth/signout/route.ts +11 -0
  47. package/templates/storefront-pharmacy/components/account-iframe.tsx +7 -1
  48. package/templates/storefront-pharmacy/components/account-pill.tsx +49 -0
  49. package/templates/storefront-pharmacy/components/header.tsx +2 -0
  50. package/templates/storefront-pharmacy/lib/auth.ts +35 -0
  51. package/templates/storefront-pharmacy/package.json +1 -1
  52. package/templates/storefront-restaurant/app/auth/callback/route.ts +16 -0
  53. package/templates/storefront-restaurant/app/auth/session/route.ts +11 -0
  54. package/templates/storefront-restaurant/app/auth/signout/route.ts +11 -0
  55. package/templates/storefront-restaurant/components/account-iframe.tsx +7 -1
  56. package/templates/storefront-restaurant/components/account-pill.tsx +49 -0
  57. package/templates/storefront-restaurant/components/header.tsx +2 -0
  58. package/templates/storefront-restaurant/lib/auth.ts +35 -0
  59. package/templates/storefront-restaurant/package.json +1 -1
  60. package/templates/storefront-retail/app/auth/callback/route.ts +16 -0
  61. package/templates/storefront-retail/app/auth/session/route.ts +11 -0
  62. package/templates/storefront-retail/app/auth/signout/route.ts +11 -0
  63. package/templates/storefront-retail/components/account-iframe.tsx +7 -1
  64. package/templates/storefront-retail/components/account-pill.tsx +49 -0
  65. package/templates/storefront-retail/components/header.tsx +2 -0
  66. package/templates/storefront-retail/lib/auth.ts +35 -0
  67. package/templates/storefront-retail/package.json +1 -1
  68. package/templates/storefront-services/app/auth/callback/route.ts +16 -0
  69. package/templates/storefront-services/app/auth/session/route.ts +11 -0
  70. package/templates/storefront-services/app/auth/signout/route.ts +11 -0
  71. package/templates/storefront-services/components/account-iframe.tsx +7 -1
  72. package/templates/storefront-services/components/account-pill.tsx +49 -0
  73. package/templates/storefront-services/components/header.tsx +2 -0
  74. package/templates/storefront-services/lib/auth.ts +35 -0
  75. package/templates/storefront-services/package.json +1 -1
  76. package/dist/chunk-CVQRL2VH.mjs +0 -5763
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { TEMPLATES } from './chunk-CVQRL2VH.mjs';
3
- import { package_default } from './chunk-ZVEQGQ2Z.mjs';
2
+ import { TEMPLATES } from './chunk-QOPMMTVI.mjs';
3
+ import { package_default } from './chunk-NTY7JESF.mjs';
4
4
 
5
5
  // src/dispatcher.ts
6
6
  var VERSION = package_default.version ?? "unknown";
@@ -138,16 +138,16 @@ var COMMANDS = {
138
138
  logs: () => import('./logs-YNN2PQ24.mjs'),
139
139
  status: () => import('./status-JSYXM5RT.mjs'),
140
140
  dev: () => import('./dev-ONW2S77K.mjs'),
141
- introspect: () => import('./introspect-FPBV3O4N.mjs'),
141
+ introspect: () => import('./introspect-FFB7TA2E.mjs'),
142
142
  inspect: () => import('./inspect-CGYX4DDF.mjs'),
143
- doctor: () => import('./doctor-XLBDC6NO.mjs'),
144
- explain: () => import('./explain-PKTL7ZR6.mjs'),
143
+ doctor: () => import('./doctor-ZVYWEE53.mjs'),
144
+ explain: () => import('./explain-S6S33HPN.mjs'),
145
145
  assets: () => import('./assets-74SK63TR.mjs'),
146
146
  repo: () => import('./repo-KNQMSPVV.mjs'),
147
- list: () => import('./list-N7FKBQ3Y.mjs'),
148
- add: () => import('./add-5NW7N5CJ.mjs'),
149
- update: () => import('./update-ZC3JGKLI.mjs'),
150
- upgrade: () => import('./update-ZC3JGKLI.mjs'),
147
+ list: () => import('./list-RLWFTEP4.mjs'),
148
+ add: () => import('./add-4PBCFGM3.mjs'),
149
+ update: () => import('./update-WH6NDWIM.mjs'),
150
+ upgrade: () => import('./update-WH6NDWIM.mjs'),
151
151
  "auth-step-up": () => import('./auth-step-up-BIUYQJP6.mjs')
152
152
  };
153
153
  var COMMAND_PREFIXES = {
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { gatherIntrospection } from './chunk-26ETGFW6.mjs';
2
+ import { gatherIntrospection } from './chunk-LRTPNNQG.mjs';
3
3
  import './chunk-K5464A3L.mjs';
4
4
  import './chunk-DBZ3UOQ2.mjs';
5
- import './chunk-ZVEQGQ2Z.mjs';
5
+ import './chunk-NTY7JESF.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-ZVEQGQ2Z.mjs';
2
+ import { package_default } from './chunk-NTY7JESF.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-26ETGFW6.mjs';
2
+ export { run as default, extractMockSeed, gatherIntrospection, renderIntrospection } from './chunk-LRTPNNQG.mjs';
3
3
  import './chunk-K5464A3L.mjs';
4
4
  import './chunk-DBZ3UOQ2.mjs';
5
- import './chunk-ZVEQGQ2Z.mjs';
5
+ import './chunk-NTY7JESF.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-CVQRL2VH.mjs';
2
+ import { REGISTRY_INDEX } from './chunk-QOPMMTVI.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-ZVEQGQ2Z.mjs';
2
+ import { package_default } from './chunk-NTY7JESF.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.2",
3
+ "version": "0.7.4",
4
4
  "description": "Cimplify CLI — deploy, manage env vars, link projects, and scaffold storefronts",
5
5
  "keywords": [
6
6
  "cimplify",
@@ -45,6 +45,6 @@
45
45
  "vitest": "^4.1.5"
46
46
  },
47
47
  "dependencies": {
48
- "@cimplify/sdk": "^0.54.0"
48
+ "@cimplify/sdk": "^0.56.0"
49
49
  }
50
50
  }
@@ -0,0 +1,16 @@
1
+ import { handleOidcCallback } from "@cimplify/sdk/server";
2
+
3
+ const CLIENT_ID = process.env.CIMPLIFY_CLIENT_ID ?? "";
4
+ const AUTH_URL = process.env.CIMPLIFY_AUTH_URL;
5
+ const REDIRECT_URI = process.env.CIMPLIFY_REDIRECT_URI ?? "";
6
+
7
+ export async function POST(req: Request): Promise<Response> {
8
+ if (!CLIENT_ID || !REDIRECT_URI) {
9
+ return Response.json({ error: "oidc_not_configured" }, { status: 500 });
10
+ }
11
+ return handleOidcCallback(req, {
12
+ clientId: CLIENT_ID,
13
+ authUrl: AUTH_URL,
14
+ redirectUri: REDIRECT_URI,
15
+ });
16
+ }
@@ -0,0 +1,11 @@
1
+ import { handleSessionRequest } from "@cimplify/sdk/server";
2
+
3
+ const CLIENT_ID = process.env.CIMPLIFY_CLIENT_ID ?? "";
4
+ const AUTH_URL = process.env.CIMPLIFY_AUTH_URL;
5
+
6
+ export async function GET(req: Request): Promise<Response> {
7
+ if (!CLIENT_ID) {
8
+ return Response.json({ sub: null }, { status: 200 });
9
+ }
10
+ return handleSessionRequest(req, { clientId: CLIENT_ID, authUrl: AUTH_URL });
11
+ }
@@ -0,0 +1,11 @@
1
+ import { buildSignoutCookies } from "@cimplify/sdk/server";
2
+
3
+ const CLIENT_ID = process.env.CIMPLIFY_CLIENT_ID ?? "";
4
+
5
+ export async function POST(): Promise<Response> {
6
+ const headers = new Headers({ "Content-Type": "application/json" });
7
+ for (const cookie of buildSignoutCookies({ clientId: CLIENT_ID })) {
8
+ headers.append("Set-Cookie", cookie);
9
+ }
10
+ return new Response(JSON.stringify({ ok: true }), { status: 200, headers });
11
+ }
@@ -1,13 +1,19 @@
1
1
  "use client";
2
2
 
3
3
  import { CimplifyAccount } from "@cimplify/sdk/react";
4
+ import { brand } from "@/lib/brand";
4
5
 
5
6
  /**
6
7
  * Cimplify Account portal — iframe-mounted UI hosted by Cimplify Link.
7
8
  * Handles sign-in, sign-up, OTP, addresses, payment methods, sessions,
8
9
  * and order history. The iframe owns auth state; we just choose which
9
10
  * `section` to land on.
11
+ *
12
+ * `merchantName` is passed so the embedded widget can render scoping
13
+ * affordances ("Showing your account with {merchant} only.") and
14
+ * enforce the per-merchant data contract — orders/subs filter to this
15
+ * business, cross-merchant surfaces are hidden.
10
16
  */
11
17
  export function AccountIframe({ section }: { section?: string }) {
12
- return <CimplifyAccount section={section} />;
18
+ return <CimplifyAccount section={section} merchantName={brand.name} />;
13
19
  }
@@ -0,0 +1,49 @@
1
+ "use client";
2
+
3
+ import { useEffect, useRef } from "react";
4
+ import Link from "next/link";
5
+ import { CimplifySignInButton, useCimplifySession } from "@cimplify/sdk/react";
6
+ import { signInSilent } from "@cimplify/sdk";
7
+
8
+ const CLIENT_ID = process.env.NEXT_PUBLIC_CIMPLIFY_CLIENT_ID ?? "";
9
+
10
+ // Attempts silent SSO on mount before showing the button so returning
11
+ // shoppers don't see a flash of "Sign in" before landing as signed in.
12
+ export function AccountPill() {
13
+ const { session, loading, refresh } = useCimplifySession();
14
+ const triedSilent = useRef(false);
15
+
16
+ useEffect(() => {
17
+ if (triedSilent.current || session || !CLIENT_ID) return;
18
+ triedSilent.current = true;
19
+ void signInSilent({
20
+ clientId: CLIENT_ID,
21
+ redirectUri: `${window.location.origin}/auth/callback`,
22
+ }).then((r) => {
23
+ if (r.ok) refresh();
24
+ });
25
+ }, [session, refresh]);
26
+
27
+ if (loading || !CLIENT_ID) return <span className="h-9 w-24" aria-hidden />;
28
+
29
+ if (session) {
30
+ const first = session.name?.split(/\s+/)[0] ?? "Account";
31
+ return (
32
+ <Link
33
+ href="/account"
34
+ className="text-[13px] font-medium tracking-wide text-foreground hover:text-primary transition-colors"
35
+ >
36
+ Hi, {first}
37
+ </Link>
38
+ );
39
+ }
40
+
41
+ return (
42
+ <CimplifySignInButton
43
+ clientId={CLIENT_ID}
44
+ redirectUri={`${typeof window !== "undefined" ? window.location.origin : ""}/auth/callback`}
45
+ variant="text"
46
+ onSuccess={() => refresh()}
47
+ />
48
+ );
49
+ }
@@ -3,6 +3,7 @@ import { Suspense } from "react";
3
3
  import { NavLink } from "./nav-link";
4
4
  import { CartPill, CartPillSkeleton } from "./cart-pill";
5
5
  import { MobileNav } from "./mobile-nav";
6
+ import { AccountPill } from "./account-pill";
6
7
  import { brand } from "@/lib/brand";
7
8
 
8
9
  /**
@@ -31,6 +32,7 @@ export function Header() {
31
32
  </Suspense>
32
33
  ))}
33
34
  </nav>
35
+ <AccountPill />
34
36
  <Suspense fallback={<CartPillSkeleton />}>
35
37
  <CartPill />
36
38
  </Suspense>
@@ -0,0 +1,35 @@
1
+ import { headers } from "next/headers";
2
+ import {
3
+ getAccessTokenFromCookieHeader,
4
+ getServerClient,
5
+ getSessionFromCookieHeader,
6
+ type CimplifyClient,
7
+ type CimplifySession,
8
+ } from "@cimplify/sdk/server";
9
+
10
+ const CLIENT_ID = process.env.CIMPLIFY_CLIENT_ID ?? "";
11
+ const AUTH_URL = process.env.CIMPLIFY_AUTH_URL;
12
+
13
+ const oidcConfig = { clientId: CLIENT_ID, authUrl: AUTH_URL };
14
+
15
+ export async function getSession(): Promise<CimplifySession | null> {
16
+ if (!CLIENT_ID) return null;
17
+ const cookieHeader = (await headers()).get("cookie");
18
+ return getSessionFromCookieHeader(oidcConfig, cookieHeader);
19
+ }
20
+
21
+ // Returns a Cimplify server client with the signed-in customer's access
22
+ // token attached (if any). Use this instead of getServerClient() on pages
23
+ // that need personalized data: orders, subscriptions, price-list pricing.
24
+ // Pages without `revalidate: 0` will share a cache across customers, so
25
+ // only use this on routes that opt out of static caching.
26
+ export async function getAuthenticatedServerClient(): Promise<CimplifyClient> {
27
+ const cookieHeader = (await headers()).get("cookie");
28
+ const accessToken =
29
+ CLIENT_ID && cookieHeader
30
+ ? getAccessTokenFromCookieHeader(oidcConfig, cookieHeader) ?? undefined
31
+ : undefined;
32
+ return getServerClient({ accessToken });
33
+ }
34
+
35
+ export type { CimplifySession } from "@cimplify/sdk/server";
@@ -17,7 +17,7 @@
17
17
  "check": "bun run typecheck && bun run test:run"
18
18
  },
19
19
  "dependencies": {
20
- "@cimplify/sdk": "^0.54.0",
20
+ "@cimplify/sdk": "^0.56.0",
21
21
  "next": "^16.2.6",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
@@ -0,0 +1,16 @@
1
+ import { handleOidcCallback } from "@cimplify/sdk/server";
2
+
3
+ const CLIENT_ID = process.env.CIMPLIFY_CLIENT_ID ?? "";
4
+ const AUTH_URL = process.env.CIMPLIFY_AUTH_URL;
5
+ const REDIRECT_URI = process.env.CIMPLIFY_REDIRECT_URI ?? "";
6
+
7
+ export async function POST(req: Request): Promise<Response> {
8
+ if (!CLIENT_ID || !REDIRECT_URI) {
9
+ return Response.json({ error: "oidc_not_configured" }, { status: 500 });
10
+ }
11
+ return handleOidcCallback(req, {
12
+ clientId: CLIENT_ID,
13
+ authUrl: AUTH_URL,
14
+ redirectUri: REDIRECT_URI,
15
+ });
16
+ }
@@ -0,0 +1,11 @@
1
+ import { handleSessionRequest } from "@cimplify/sdk/server";
2
+
3
+ const CLIENT_ID = process.env.CIMPLIFY_CLIENT_ID ?? "";
4
+ const AUTH_URL = process.env.CIMPLIFY_AUTH_URL;
5
+
6
+ export async function GET(req: Request): Promise<Response> {
7
+ if (!CLIENT_ID) {
8
+ return Response.json({ sub: null }, { status: 200 });
9
+ }
10
+ return handleSessionRequest(req, { clientId: CLIENT_ID, authUrl: AUTH_URL });
11
+ }
@@ -0,0 +1,11 @@
1
+ import { buildSignoutCookies } from "@cimplify/sdk/server";
2
+
3
+ const CLIENT_ID = process.env.CIMPLIFY_CLIENT_ID ?? "";
4
+
5
+ export async function POST(): Promise<Response> {
6
+ const headers = new Headers({ "Content-Type": "application/json" });
7
+ for (const cookie of buildSignoutCookies({ clientId: CLIENT_ID })) {
8
+ headers.append("Set-Cookie", cookie);
9
+ }
10
+ return new Response(JSON.stringify({ ok: true }), { status: 200, headers });
11
+ }
@@ -1,13 +1,19 @@
1
1
  "use client";
2
2
 
3
3
  import { CimplifyAccount } from "@cimplify/sdk/react";
4
+ import { brand } from "@/lib/brand";
4
5
 
5
6
  /**
6
7
  * Cimplify Account portal — iframe-mounted UI hosted by Cimplify Link.
7
8
  * Handles sign-in, sign-up, OTP, addresses, payment methods, sessions,
8
9
  * and order history. The iframe owns auth state; we just choose which
9
10
  * `section` to land on.
11
+ *
12
+ * `merchantName` is passed so the embedded widget can render scoping
13
+ * affordances ("Showing your account with Akua's Bakery only.") and
14
+ * enforce the per-merchant data contract — orders/subs filter to this
15
+ * business, cross-merchant surfaces are hidden.
10
16
  */
11
17
  export function AccountIframe({ section }: { section?: string }) {
12
- return <CimplifyAccount section={section} />;
18
+ return <CimplifyAccount section={section} merchantName={brand.name} />;
13
19
  }
@@ -0,0 +1,49 @@
1
+ "use client";
2
+
3
+ import { useEffect, useRef } from "react";
4
+ import Link from "next/link";
5
+ import { CimplifySignInButton, useCimplifySession } from "@cimplify/sdk/react";
6
+ import { signInSilent } from "@cimplify/sdk";
7
+
8
+ const CLIENT_ID = process.env.NEXT_PUBLIC_CIMPLIFY_CLIENT_ID ?? "";
9
+
10
+ // Attempts silent SSO on mount before showing the button so returning
11
+ // shoppers don't see a flash of "Sign in" before landing as signed in.
12
+ export function AccountPill() {
13
+ const { session, loading, refresh } = useCimplifySession();
14
+ const triedSilent = useRef(false);
15
+
16
+ useEffect(() => {
17
+ if (triedSilent.current || session || !CLIENT_ID) return;
18
+ triedSilent.current = true;
19
+ void signInSilent({
20
+ clientId: CLIENT_ID,
21
+ redirectUri: `${window.location.origin}/auth/callback`,
22
+ }).then((r) => {
23
+ if (r.ok) refresh();
24
+ });
25
+ }, [session, refresh]);
26
+
27
+ if (loading || !CLIENT_ID) return <span className="h-9 w-24" aria-hidden />;
28
+
29
+ if (session) {
30
+ const first = session.name?.split(/\s+/)[0] ?? "Account";
31
+ return (
32
+ <Link
33
+ href="/account"
34
+ className="text-[13px] font-medium tracking-wide text-foreground hover:text-primary transition-colors"
35
+ >
36
+ Hi, {first}
37
+ </Link>
38
+ );
39
+ }
40
+
41
+ return (
42
+ <CimplifySignInButton
43
+ clientId={CLIENT_ID}
44
+ redirectUri={`${typeof window !== "undefined" ? window.location.origin : ""}/auth/callback`}
45
+ variant="text"
46
+ onSuccess={() => refresh()}
47
+ />
48
+ );
49
+ }
@@ -3,6 +3,7 @@ import { Suspense } from "react";
3
3
  import { NavLink } from "./nav-link";
4
4
  import { CartPill, CartPillSkeleton } from "./cart-pill";
5
5
  import { MobileNav } from "./mobile-nav";
6
+ import { AccountPill } from "./account-pill";
6
7
  import { brand } from "@/lib/brand";
7
8
 
8
9
  /**
@@ -29,6 +30,7 @@ export function Header() {
29
30
  </Suspense>
30
31
  ))}
31
32
  </nav>
33
+ <AccountPill />
32
34
  <Suspense fallback={<CartPillSkeleton />}>
33
35
  <CartPill />
34
36
  </Suspense>
@@ -0,0 +1,35 @@
1
+ import { headers } from "next/headers";
2
+ import {
3
+ getAccessTokenFromCookieHeader,
4
+ getServerClient,
5
+ getSessionFromCookieHeader,
6
+ type CimplifyClient,
7
+ type CimplifySession,
8
+ } from "@cimplify/sdk/server";
9
+
10
+ const CLIENT_ID = process.env.CIMPLIFY_CLIENT_ID ?? "";
11
+ const AUTH_URL = process.env.CIMPLIFY_AUTH_URL;
12
+
13
+ const oidcConfig = { clientId: CLIENT_ID, authUrl: AUTH_URL };
14
+
15
+ export async function getSession(): Promise<CimplifySession | null> {
16
+ if (!CLIENT_ID) return null;
17
+ const cookieHeader = (await headers()).get("cookie");
18
+ return getSessionFromCookieHeader(oidcConfig, cookieHeader);
19
+ }
20
+
21
+ // Returns a Cimplify server client with the signed-in customer's access
22
+ // token attached (if any). Use this instead of getServerClient() on pages
23
+ // that need personalized data: orders, subscriptions, price-list pricing.
24
+ // Pages without `revalidate: 0` will share a cache across customers, so
25
+ // only use this on routes that opt out of static caching.
26
+ export async function getAuthenticatedServerClient(): Promise<CimplifyClient> {
27
+ const cookieHeader = (await headers()).get("cookie");
28
+ const accessToken =
29
+ CLIENT_ID && cookieHeader
30
+ ? getAccessTokenFromCookieHeader(oidcConfig, cookieHeader) ?? undefined
31
+ : undefined;
32
+ return getServerClient({ accessToken });
33
+ }
34
+
35
+ export type { CimplifySession } from "@cimplify/sdk/server";
@@ -17,7 +17,7 @@
17
17
  "check": "bun run typecheck && bun run test:run"
18
18
  },
19
19
  "dependencies": {
20
- "@cimplify/sdk": "^0.54.0",
20
+ "@cimplify/sdk": "^0.56.0",
21
21
  "next": "^16.2.6",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
@@ -0,0 +1,16 @@
1
+ import { handleOidcCallback } from "@cimplify/sdk/server";
2
+
3
+ const CLIENT_ID = process.env.CIMPLIFY_CLIENT_ID ?? "";
4
+ const AUTH_URL = process.env.CIMPLIFY_AUTH_URL;
5
+ const REDIRECT_URI = process.env.CIMPLIFY_REDIRECT_URI ?? "";
6
+
7
+ export async function POST(req: Request): Promise<Response> {
8
+ if (!CLIENT_ID || !REDIRECT_URI) {
9
+ return Response.json({ error: "oidc_not_configured" }, { status: 500 });
10
+ }
11
+ return handleOidcCallback(req, {
12
+ clientId: CLIENT_ID,
13
+ authUrl: AUTH_URL,
14
+ redirectUri: REDIRECT_URI,
15
+ });
16
+ }
@@ -0,0 +1,11 @@
1
+ import { handleSessionRequest } from "@cimplify/sdk/server";
2
+
3
+ const CLIENT_ID = process.env.CIMPLIFY_CLIENT_ID ?? "";
4
+ const AUTH_URL = process.env.CIMPLIFY_AUTH_URL;
5
+
6
+ export async function GET(req: Request): Promise<Response> {
7
+ if (!CLIENT_ID) {
8
+ return Response.json({ sub: null }, { status: 200 });
9
+ }
10
+ return handleSessionRequest(req, { clientId: CLIENT_ID, authUrl: AUTH_URL });
11
+ }
@@ -0,0 +1,11 @@
1
+ import { buildSignoutCookies } from "@cimplify/sdk/server";
2
+
3
+ const CLIENT_ID = process.env.CIMPLIFY_CLIENT_ID ?? "";
4
+
5
+ export async function POST(): Promise<Response> {
6
+ const headers = new Headers({ "Content-Type": "application/json" });
7
+ for (const cookie of buildSignoutCookies({ clientId: CLIENT_ID })) {
8
+ headers.append("Set-Cookie", cookie);
9
+ }
10
+ return new Response(JSON.stringify({ ok: true }), { status: 200, headers });
11
+ }
@@ -1,13 +1,19 @@
1
1
  "use client";
2
2
 
3
3
  import { CimplifyAccount } from "@cimplify/sdk/react";
4
+ import { brand } from "@/lib/brand";
4
5
 
5
6
  /**
6
7
  * Cimplify Account portal — iframe-mounted UI hosted by Cimplify Link.
7
8
  * Handles sign-in, sign-up, OTP, addresses, payment methods, sessions,
8
9
  * and order history. The iframe owns auth state; we just choose which
9
10
  * `section` to land on.
11
+ *
12
+ * `merchantName` is passed so the embedded widget can render scoping
13
+ * affordances ("Showing your account with {merchant} only.") and
14
+ * enforce the per-merchant data contract — orders/subs filter to this
15
+ * business, cross-merchant surfaces are hidden.
10
16
  */
11
17
  export function AccountIframe({ section }: { section?: string }) {
12
- return <CimplifyAccount section={section} />;
18
+ return <CimplifyAccount section={section} merchantName={brand.name} />;
13
19
  }
@@ -0,0 +1,49 @@
1
+ "use client";
2
+
3
+ import { useEffect, useRef } from "react";
4
+ import Link from "next/link";
5
+ import { CimplifySignInButton, useCimplifySession } from "@cimplify/sdk/react";
6
+ import { signInSilent } from "@cimplify/sdk";
7
+
8
+ const CLIENT_ID = process.env.NEXT_PUBLIC_CIMPLIFY_CLIENT_ID ?? "";
9
+
10
+ // Attempts silent SSO on mount before showing the button so returning
11
+ // shoppers don't see a flash of "Sign in" before landing as signed in.
12
+ export function AccountPill() {
13
+ const { session, loading, refresh } = useCimplifySession();
14
+ const triedSilent = useRef(false);
15
+
16
+ useEffect(() => {
17
+ if (triedSilent.current || session || !CLIENT_ID) return;
18
+ triedSilent.current = true;
19
+ void signInSilent({
20
+ clientId: CLIENT_ID,
21
+ redirectUri: `${window.location.origin}/auth/callback`,
22
+ }).then((r) => {
23
+ if (r.ok) refresh();
24
+ });
25
+ }, [session, refresh]);
26
+
27
+ if (loading || !CLIENT_ID) return <span className="h-9 w-24" aria-hidden />;
28
+
29
+ if (session) {
30
+ const first = session.name?.split(/\s+/)[0] ?? "Account";
31
+ return (
32
+ <Link
33
+ href="/account"
34
+ className="text-[13px] font-medium tracking-wide text-foreground hover:text-primary transition-colors"
35
+ >
36
+ Hi, {first}
37
+ </Link>
38
+ );
39
+ }
40
+
41
+ return (
42
+ <CimplifySignInButton
43
+ clientId={CLIENT_ID}
44
+ redirectUri={`${typeof window !== "undefined" ? window.location.origin : ""}/auth/callback`}
45
+ variant="text"
46
+ onSuccess={() => refresh()}
47
+ />
48
+ );
49
+ }
@@ -3,6 +3,7 @@ import { Suspense } from "react";
3
3
  import { NavLink } from "./nav-link";
4
4
  import { CartPill, CartPillSkeleton } from "./cart-pill";
5
5
  import { MobileNav } from "./mobile-nav";
6
+ import { AccountPill } from "./account-pill";
6
7
  import { brand } from "@/lib/brand";
7
8
 
8
9
  /**
@@ -29,6 +30,7 @@ export function Header() {
29
30
  </Suspense>
30
31
  ))}
31
32
  </nav>
33
+ <AccountPill />
32
34
  <Suspense fallback={<CartPillSkeleton />}>
33
35
  <CartPill />
34
36
  </Suspense>
@@ -0,0 +1,35 @@
1
+ import { headers } from "next/headers";
2
+ import {
3
+ getAccessTokenFromCookieHeader,
4
+ getServerClient,
5
+ getSessionFromCookieHeader,
6
+ type CimplifyClient,
7
+ type CimplifySession,
8
+ } from "@cimplify/sdk/server";
9
+
10
+ const CLIENT_ID = process.env.CIMPLIFY_CLIENT_ID ?? "";
11
+ const AUTH_URL = process.env.CIMPLIFY_AUTH_URL;
12
+
13
+ const oidcConfig = { clientId: CLIENT_ID, authUrl: AUTH_URL };
14
+
15
+ export async function getSession(): Promise<CimplifySession | null> {
16
+ if (!CLIENT_ID) return null;
17
+ const cookieHeader = (await headers()).get("cookie");
18
+ return getSessionFromCookieHeader(oidcConfig, cookieHeader);
19
+ }
20
+
21
+ // Returns a Cimplify server client with the signed-in customer's access
22
+ // token attached (if any). Use this instead of getServerClient() on pages
23
+ // that need personalized data: orders, subscriptions, price-list pricing.
24
+ // Pages without `revalidate: 0` will share a cache across customers, so
25
+ // only use this on routes that opt out of static caching.
26
+ export async function getAuthenticatedServerClient(): Promise<CimplifyClient> {
27
+ const cookieHeader = (await headers()).get("cookie");
28
+ const accessToken =
29
+ CLIENT_ID && cookieHeader
30
+ ? getAccessTokenFromCookieHeader(oidcConfig, cookieHeader) ?? undefined
31
+ : undefined;
32
+ return getServerClient({ accessToken });
33
+ }
34
+
35
+ export type { CimplifySession } from "@cimplify/sdk/server";
@@ -19,7 +19,7 @@
19
19
  "check": "bun run typecheck && bun run test:run"
20
20
  },
21
21
  "dependencies": {
22
- "@cimplify/sdk": "^0.54.0",
22
+ "@cimplify/sdk": "^0.56.0",
23
23
  "next": "^16.2.6",
24
24
  "react": "^19.0.0",
25
25
  "react-dom": "^19.0.0"
@@ -0,0 +1,16 @@
1
+ import { handleOidcCallback } from "@cimplify/sdk/server";
2
+
3
+ const CLIENT_ID = process.env.CIMPLIFY_CLIENT_ID ?? "";
4
+ const AUTH_URL = process.env.CIMPLIFY_AUTH_URL;
5
+ const REDIRECT_URI = process.env.CIMPLIFY_REDIRECT_URI ?? "";
6
+
7
+ export async function POST(req: Request): Promise<Response> {
8
+ if (!CLIENT_ID || !REDIRECT_URI) {
9
+ return Response.json({ error: "oidc_not_configured" }, { status: 500 });
10
+ }
11
+ return handleOidcCallback(req, {
12
+ clientId: CLIENT_ID,
13
+ authUrl: AUTH_URL,
14
+ redirectUri: REDIRECT_URI,
15
+ });
16
+ }