@cimplify/sdk 0.36.0 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+ // src/dispatcher.ts
3
+ var HELP = `cimplify \u2014 Cimplify SDK CLI
4
+
5
+ Usage:
6
+ bunx @cimplify/sdk Boot the local mock (alias for \`mock\`)
7
+ bunx @cimplify/sdk init [dir] Scaffold a working storefront (uses SDK components)
8
+ bunx @cimplify/sdk mock [flags] Boot the local Cimplify API mock
9
+ bunx @cimplify/sdk add <component> Install a UI component from the registry
10
+ bunx @cimplify/sdk list List available registry components
11
+ bunx @cimplify/sdk --help, -h Show this help
12
+ bunx @cimplify/sdk --version, -v Show SDK version
13
+
14
+ Mock flags:
15
+ --port <num> Port (default 8787)
16
+ --host <host> Host (default 127.0.0.1)
17
+ --seed <name> default | restaurant | retail | services | grocery | reesa-storefront | empty
18
+ --auth-mode <mode> permissive (default) | strict
19
+ --otp <code> Default OTP code (default 123456)
20
+ --persist <file> Persist state to JSON file
21
+ --cors <origins> CORS origins (comma-separated, or '*')
22
+ --webhook-url <url> Send mock events to this URL
23
+ --webhook-secret <key> HMAC secret for webhook signatures
24
+ --frozen-at <iso> Freeze clock at given ISO timestamp
25
+ --quiet Suppress request logs
26
+ `;
27
+ async function main() {
28
+ const [, , maybeSub, ...rest] = process.argv;
29
+ const TOP_LEVEL_FLAGS = /* @__PURE__ */ new Set(["--help", "-h", "help", "--version", "-v", "version"]);
30
+ if (maybeSub && TOP_LEVEL_FLAGS.has(maybeSub)) {
31
+ return await runDispatcherFlag(maybeSub);
32
+ }
33
+ const isFlag = (s) => s?.startsWith("-");
34
+ const sub = !maybeSub || isFlag(maybeSub) ? "mock" : maybeSub;
35
+ const args = !maybeSub || isFlag(maybeSub) ? process.argv.slice(2) : rest;
36
+ switch (sub) {
37
+ case "mock": {
38
+ process.argv = [process.argv[0], process.argv[1], ...args];
39
+ const { fileURLToPath } = await import('url');
40
+ const { dirname, join } = await import('path');
41
+ const here = dirname(fileURLToPath(import.meta.url));
42
+ await import(join(here, "mock", "cli.mjs"));
43
+ return;
44
+ }
45
+ case "init":
46
+ await runInit(args);
47
+ return;
48
+ case "add":
49
+ case "list": {
50
+ process.argv = [process.argv[0], process.argv[1], sub, ...args];
51
+ const { fileURLToPath } = await import('url');
52
+ const { dirname, join } = await import('path');
53
+ const here = dirname(fileURLToPath(import.meta.url));
54
+ await import(join(here, "cli.js"));
55
+ return;
56
+ }
57
+ default:
58
+ console.error(`cimplify: unknown command "${sub}"`);
59
+ console.error(`Run \`bunx @cimplify/sdk --help\` for usage.`);
60
+ process.exit(1);
61
+ }
62
+ }
63
+ async function runInit(args) {
64
+ const { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync, copyFileSync } = await import('fs');
65
+ const { fileURLToPath } = await import('url');
66
+ const { dirname, join, resolve } = await import('path');
67
+ const { spawnSync } = await import('child_process');
68
+ const dir = args.find((a) => !a.startsWith("-")) ?? "my-storefront";
69
+ const target = resolve(process.cwd(), dir);
70
+ if (existsSync(target) && readdirSync(target).length > 0) {
71
+ console.error(`cimplify init: directory ${target} is not empty`);
72
+ process.exit(1);
73
+ }
74
+ mkdirSync(target, { recursive: true });
75
+ const here = dirname(fileURLToPath(import.meta.url));
76
+ const candidates = [
77
+ resolve(here, "..", "templates", "storefront"),
78
+ resolve(here, "..", "..", "templates", "storefront"),
79
+ resolve(here, "templates", "storefront")
80
+ ];
81
+ const source = candidates.find((p) => existsSync(p));
82
+ if (!source) {
83
+ console.error("cimplify init: template not found in any of:");
84
+ for (const c of candidates) console.error(" " + c);
85
+ process.exit(1);
86
+ }
87
+ const placeholderName = dir.replace(/[^a-z0-9-]/gi, "-").toLowerCase();
88
+ const SUBSTITUTABLE = /* @__PURE__ */ new Set([".json", ".tsx", ".ts", ".html", ".md", ".css", ".env.example"]);
89
+ function copyTree(srcDir, dstDir) {
90
+ mkdirSync(dstDir, { recursive: true });
91
+ for (const entry of readdirSync(srcDir)) {
92
+ const s = join(srcDir, entry);
93
+ const d = join(dstDir, entry);
94
+ const stat = statSync(s);
95
+ if (stat.isDirectory()) {
96
+ copyTree(s, d);
97
+ } else {
98
+ const ext = entry.includes(".env") ? ".env.example" : entry.slice(entry.lastIndexOf("."));
99
+ if (SUBSTITUTABLE.has(ext)) {
100
+ const content = readFileSync(s, "utf8").replaceAll("__STOREFRONT_NAME__", placeholderName);
101
+ writeFileSync(d, content);
102
+ } else {
103
+ copyFileSync(s, d);
104
+ }
105
+ }
106
+ }
107
+ }
108
+ copyTree(source, target);
109
+ const envExample = join(target, ".env.example");
110
+ if (existsSync(envExample)) {
111
+ copyFileSync(envExample, join(target, ".env.local"));
112
+ }
113
+ console.log(`\u2713 Scaffolded ${placeholderName} in ${target}`);
114
+ console.log("");
115
+ console.log("Installing dependencies...");
116
+ const installer = spawnSync(process.env.npm_execpath ? "bun" : "bun", ["install"], {
117
+ cwd: target,
118
+ stdio: "inherit"
119
+ });
120
+ if (installer.status !== 0) {
121
+ console.warn("(bun install failed; run it manually)");
122
+ }
123
+ console.log("");
124
+ console.log("Next steps:");
125
+ console.log(` cd ${dir}`);
126
+ console.log(" bun dev # boots both the mock API and the storefront");
127
+ console.log("");
128
+ console.log("Then open http://localhost:5173");
129
+ }
130
+ async function runDispatcherFlag(flag) {
131
+ if (flag === "-h" || flag === "--help" || flag === "help") {
132
+ console.log(HELP);
133
+ return;
134
+ }
135
+ const { readFileSync } = await import('fs');
136
+ const { fileURLToPath } = await import('url');
137
+ const { dirname, join, resolve } = await import('path');
138
+ const here = dirname(fileURLToPath(import.meta.url));
139
+ const candidates = [join(here, "..", "package.json"), join(here, "..", "..", "package.json"), resolve(here, "package.json")];
140
+ for (const path of candidates) {
141
+ try {
142
+ const pkg = JSON.parse(readFileSync(path, "utf8"));
143
+ if (pkg.name === "@cimplify/sdk" && pkg.version) {
144
+ console.log(pkg.version);
145
+ return;
146
+ }
147
+ } catch {
148
+ }
149
+ }
150
+ console.log("unknown");
151
+ }
152
+ main().catch((err) => {
153
+ console.error("cimplify:", err);
154
+ process.exit(1);
155
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cimplify/sdk",
3
- "version": "0.36.0",
3
+ "version": "0.38.0",
4
4
  "description": "Cimplify Commerce SDK for storefronts",
5
5
  "keywords": [
6
6
  "cimplify",
@@ -11,12 +11,15 @@
11
11
  ],
12
12
  "license": "MIT",
13
13
  "bin": {
14
+ "sdk": "dist/dispatcher.mjs",
15
+ "cimplify": "dist/dispatcher.mjs",
14
16
  "cimplify-sdk": "dist/cli.js",
15
- "cimplify-mock": "dist/mock/cli.js"
17
+ "cimplify-mock": "dist/mock/cli.mjs"
16
18
  },
17
19
  "files": [
18
20
  "dist",
19
- "registry"
21
+ "registry",
22
+ "templates"
20
23
  ],
21
24
  "main": "./dist/index.js",
22
25
  "module": "./dist/index.mjs",
@@ -0,0 +1,2 @@
1
+ VITE_CIMPLIFY_API_URL=http://127.0.0.1:8787
2
+ VITE_CIMPLIFY_BUSINESS_ID=bus_default_akua_bakery
@@ -0,0 +1,49 @@
1
+ # __STOREFRONT_NAME__
2
+
3
+ A Cimplify storefront scaffolded from the SDK template. Talks to the Cimplify mock API by default — swap one env var to point at production.
4
+
5
+ ## Run
6
+
7
+ ```bash
8
+ bun install
9
+ bun dev
10
+ ```
11
+
12
+ Two things start in parallel:
13
+
14
+ - `bunx @cimplify/sdk` — the Cimplify mock API on `http://127.0.0.1:8787` (Akua's Bakery seeded by default).
15
+ - `vite` — this storefront on `http://localhost:5173`.
16
+
17
+ Open the storefront in your browser. Edit `src/App.tsx` to start customising. The SDK ships full pages (`<CataloguePage>`, `<ProductPage>`, `<CartPage>`, `<CheckoutPage>`) and layouts (`<DefaultProductLayout>`, `<FoodProductLayout>`, etc.) that you can swap in.
18
+
19
+ ## Switch seed
20
+
21
+ ```bash
22
+ bunx @cimplify/sdk --seed restaurant # Mama's Kitchen
23
+ bunx @cimplify/sdk --seed retail # Currents Electronics
24
+ bunx @cimplify/sdk --seed services # Serene Spa
25
+ bunx @cimplify/sdk --seed grocery # FreshMart
26
+ ```
27
+
28
+ Update `VITE_CIMPLIFY_BUSINESS_ID` in `.env.local` to match the seeded business.
29
+
30
+ ## Go live
31
+
32
+ ```diff
33
+ # .env.local
34
+ - VITE_CIMPLIFY_API_URL=http://127.0.0.1:8787
35
+ + VITE_CIMPLIFY_API_URL=https://api.cimplify.io
36
+ + VITE_CIMPLIFY_API_KEY=<your tenant key>
37
+ - VITE_CIMPLIFY_BUSINESS_ID=bus_default_akua_bakery
38
+ + VITE_CIMPLIFY_BUSINESS_ID=<your business id>
39
+ ```
40
+
41
+ ## Add components
42
+
43
+ ```bash
44
+ bunx @cimplify/sdk add product-card
45
+ bunx @cimplify/sdk add cart-summary
46
+ bunx @cimplify/sdk list
47
+ ```
48
+
49
+ These copy component sources directly into `src/components/` — yours to edit.
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>__STOREFRONT_NAME__</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.tsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "__STOREFRONT_NAME__",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "concurrently -k -n mock,vite -c blue,magenta \"bunx @cimplify/sdk --quiet\" \"vite\"",
8
+ "dev:storefront": "vite",
9
+ "dev:mock": "bunx @cimplify/sdk",
10
+ "build": "tsc -b && vite build",
11
+ "preview": "vite preview"
12
+ },
13
+ "dependencies": {
14
+ "@cimplify/sdk": "^0.37.0",
15
+ "react": "^19.2.5",
16
+ "react-dom": "^19.2.5",
17
+ "react-router-dom": "^7.9.0"
18
+ },
19
+ "devDependencies": {
20
+ "@types/react": "^19.2.14",
21
+ "@types/react-dom": "^19.2.3",
22
+ "@vitejs/plugin-react": "^5.0.0",
23
+ "concurrently": "^9.0.0",
24
+ "typescript": "^5.9.3",
25
+ "vite": "^7.0.0"
26
+ }
27
+ }
@@ -0,0 +1,82 @@
1
+ import { Routes, Route, NavLink, useNavigate } from "react-router-dom";
2
+ import { CataloguePage, CartPage, CheckoutPage, useCimplify } from "@cimplify/sdk/react";
3
+ import type { Product } from "@cimplify/sdk";
4
+
5
+ function CartPill() {
6
+ const cim = useCimplify();
7
+ const total = cim?.cart?.totalItems ?? 0;
8
+ return <span className="cimplify-storefront__cart-pill">Cart · {total}</span>;
9
+ }
10
+
11
+ function Catalogue() {
12
+ const navigate = useNavigate();
13
+ return (
14
+ <CataloguePage
15
+ title="Shop"
16
+ onProductClick={(p: Product) => navigate(`/products/${p.id}`)}
17
+ />
18
+ );
19
+ }
20
+
21
+ function ProductDetail() {
22
+ return (
23
+ <div>
24
+ <p>Product detail page — wire up your favourite ProductPage layout here.</p>
25
+ <p>The SDK ships <code>&lt;ProductPage&gt;</code>, <code>&lt;DefaultProductLayout&gt;</code>, <code>&lt;FoodProductLayout&gt;</code> etc. — pick one.</p>
26
+ </div>
27
+ );
28
+ }
29
+
30
+ function Cart() {
31
+ const navigate = useNavigate();
32
+ return <CartPage onCheckout={() => navigate("/checkout")} />;
33
+ }
34
+
35
+ function Checkout() {
36
+ const navigate = useNavigate();
37
+ return (
38
+ <CheckoutPage
39
+ onSuccess={(orderId: string) => navigate(`/orders/${orderId}`)}
40
+ onCancel={() => navigate("/cart")}
41
+ />
42
+ );
43
+ }
44
+
45
+ function OrderConfirmation() {
46
+ return (
47
+ <div>
48
+ <h2>Thanks — your order is confirmed.</h2>
49
+ <p>This is the page shoppers land on after checkout.</p>
50
+ </div>
51
+ );
52
+ }
53
+
54
+ export function App() {
55
+ return (
56
+ <div className="cimplify-storefront">
57
+ <header className="cimplify-storefront__nav">
58
+ <NavLink to="/"><h1>__STOREFRONT_NAME__</h1></NavLink>
59
+ <nav>
60
+ <NavLink to="/" end>Shop</NavLink>
61
+ <NavLink to="/cart">Cart</NavLink>
62
+ <CartPill />
63
+ </nav>
64
+ </header>
65
+
66
+ <main className="cimplify-storefront__main">
67
+ <Routes>
68
+ <Route path="/" element={<Catalogue />} />
69
+ <Route path="/products/:productId" element={<ProductDetail />} />
70
+ <Route path="/cart" element={<Cart />} />
71
+ <Route path="/checkout" element={<Checkout />} />
72
+ <Route path="/orders/:orderId" element={<OrderConfirmation />} />
73
+ </Routes>
74
+ </main>
75
+
76
+ <footer className="cimplify-storefront__footer">
77
+ Built with <a href="https://github.com/cimplify/cimplify">@cimplify/sdk</a> ·{" "}
78
+ Mock API at <code>{import.meta.env.VITE_CIMPLIFY_API_URL ?? "http://127.0.0.1:8787"}</code>
79
+ </footer>
80
+ </div>
81
+ );
82
+ }
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ import ReactDOM from "react-dom/client";
3
+ import { BrowserRouter } from "react-router-dom";
4
+ import { CimplifyProvider } from "@cimplify/sdk/react";
5
+ import { App } from "./App";
6
+
7
+ import "@cimplify/sdk/styles.css";
8
+ import "./styles.css";
9
+
10
+ const baseUrl = import.meta.env.VITE_CIMPLIFY_API_URL ?? "http://127.0.0.1:8787";
11
+ const businessId = import.meta.env.VITE_CIMPLIFY_BUSINESS_ID ?? "bus_default_akua_bakery";
12
+
13
+ ReactDOM.createRoot(document.getElementById("root")!).render(
14
+ <React.StrictMode>
15
+ <BrowserRouter>
16
+ <CimplifyProvider config={{ baseUrl, apiKey: "mock-dev", businessId }}>
17
+ <App />
18
+ </CimplifyProvider>
19
+ </BrowserRouter>
20
+ </React.StrictMode>,
21
+ );
@@ -0,0 +1,59 @@
1
+ html, body {
2
+ margin: 0;
3
+ padding: 0;
4
+ font-family: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", Roboto, sans-serif;
5
+ background: #fafaf9;
6
+ color: #0f172a;
7
+ }
8
+
9
+ * { box-sizing: border-box; }
10
+
11
+ a { color: inherit; text-decoration: none; }
12
+
13
+ .cimplify-storefront {
14
+ min-height: 100vh;
15
+ display: flex;
16
+ flex-direction: column;
17
+ }
18
+
19
+ .cimplify-storefront__nav {
20
+ display: flex;
21
+ align-items: center;
22
+ justify-content: space-between;
23
+ padding: 16px 24px;
24
+ background: white;
25
+ border-bottom: 1px solid #e2e8f0;
26
+ position: sticky;
27
+ top: 0;
28
+ z-index: 10;
29
+ }
30
+
31
+ .cimplify-storefront__nav h1 { font-size: 18px; margin: 0; }
32
+ .cimplify-storefront__nav nav a { margin-left: 16px; font-size: 14px; }
33
+ .cimplify-storefront__nav nav a.active { color: #2563eb; font-weight: 600; }
34
+ .cimplify-storefront__cart-pill {
35
+ margin-left: 16px;
36
+ padding: 6px 12px;
37
+ border-radius: 999px;
38
+ background: #0f172a;
39
+ color: white;
40
+ font-size: 13px;
41
+ font-weight: 600;
42
+ }
43
+
44
+ .cimplify-storefront__main {
45
+ flex: 1;
46
+ padding: 24px;
47
+ max-width: 1200px;
48
+ margin: 0 auto;
49
+ width: 100%;
50
+ }
51
+
52
+ .cimplify-storefront__footer {
53
+ padding: 24px;
54
+ text-align: center;
55
+ font-size: 12px;
56
+ color: #64748b;
57
+ border-top: 1px solid #e2e8f0;
58
+ background: white;
59
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
5
+ "module": "ESNext",
6
+ "skipLibCheck": true,
7
+ "moduleResolution": "Bundler",
8
+ "allowImportingTsExtensions": true,
9
+ "resolveJsonModule": true,
10
+ "isolatedModules": true,
11
+ "noEmit": true,
12
+ "jsx": "react-jsx",
13
+ "strict": true,
14
+ "noUnusedLocals": true,
15
+ "noUnusedParameters": true,
16
+ "noFallthroughCasesInSwitch": true
17
+ },
18
+ "include": ["src"]
19
+ }
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ server: {
7
+ port: 5173,
8
+ open: true,
9
+ },
10
+ });