@cimplify/cli 0.13.5 → 0.14.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.
Files changed (25) hide show
  1. package/dist/{add-NZ47GUMD.mjs → add-PPUGTTDA.mjs} +1 -1
  2. package/dist/{chunk-OE7YRDLH.mjs → chunk-7CI5RWNE.mjs} +22 -4
  3. package/dist/{chunk-BDC7XQIL.mjs → chunk-JOF7ESSA.mjs} +9 -9
  4. package/dist/{chunk-5YVMA7A5.mjs → chunk-KAZMZD4X.mjs} +2 -2
  5. package/dist/{chunk-GOTWRZVM.mjs → chunk-SOYZZZVT.mjs} +2 -2
  6. package/dist/{chunk-MLVBE5V2.mjs → chunk-YIBOOKQQ.mjs} +15 -1
  7. package/dist/{deploy-SLRFPGTH.mjs → deploy-64ZVAQ3R.mjs} +36 -3
  8. package/dist/dispatcher.mjs +13 -13
  9. package/dist/{doctor-6NI2XNTD.mjs → doctor-6ZIFM5J5.mjs} +3 -3
  10. package/dist/{explain-HPRJBFHS.mjs → explain-REIDDRKC.mjs} +2 -2
  11. package/dist/{introspect-3OYRCVUF.mjs → introspect-APRI7R7K.mjs} +3 -3
  12. package/dist/{link-KGNQWCSZ.mjs → link-AWC7VJNN.mjs} +18 -1
  13. package/dist/{list-QNEUCCP5.mjs → list-7ZL6FRKT.mjs} +1 -1
  14. package/dist/{projects-U75DOOR7.mjs → projects-P65SUETQ.mjs} +2 -2
  15. package/dist/{repo-IE5IOLR5.mjs → repo-ZPYZK3KW.mjs} +2 -1
  16. package/dist/{update-47HG7LN7.mjs → update-MBEPZANS.mjs} +1 -1
  17. package/package.json +2 -2
  18. package/templates/storefront-auto/package.json +1 -1
  19. package/templates/storefront-bakery/package.json +1 -1
  20. package/templates/storefront-fashion/package.json +1 -1
  21. package/templates/storefront-grocery/package.json +1 -1
  22. package/templates/storefront-pharmacy/package.json +1 -1
  23. package/templates/storefront-restaurant/package.json +1 -1
  24. package/templates/storefront-retail/package.json +1 -1
  25. 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.13.5",
5
+ version: "0.14.0",
6
6
  description: "Cimplify CLI \u2014 deploy, manage env vars, link projects, and scaffold storefronts",
7
7
  keywords: [
8
8
  "cimplify",
@@ -43,7 +43,7 @@ var package_default = {
43
43
  "@babel/parser": "^8.0.4",
44
44
  "@babel/traverse": "^8.0.4",
45
45
  "@babel/types": "^8.0.4",
46
- "@cimplify/sdk": "^0.100.5",
46
+ "@cimplify/sdk": "^0.101.1",
47
47
  re2js: "^1.4.0"
48
48
  },
49
49
  devDependencies: {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { gitDetectRoot, gitCurrentBranch, gitCurrentSha, gitStatusPorcelain } from './chunk-MLVBE5V2.mjs';
3
2
  import { parseEnvFile } from './chunk-RU53TQLZ.mjs';
4
- import { package_default } from './chunk-5YVMA7A5.mjs';
3
+ import { package_default } from './chunk-KAZMZD4X.mjs';
4
+ import { gitDetectRoot, gitCurrentBranch, gitCurrentSha, gitStatusPorcelain } from './chunk-YIBOOKQQ.mjs';
5
5
  import { parseArgs } from './chunk-5KYR6WJB.mjs';
6
6
  import { readAuthOrNull, readProjectLinkOrNull, readProjectState } from './chunk-6CHBU7O4.mjs';
7
7
  import { bold, dim, yellow, green, info, result, red } from './chunk-4O4G2CDE.mjs';
@@ -10,6 +10,8 @@ var ARGS_REV_PARSE_HEAD = ["rev-parse", "HEAD"];
10
10
  var ARGS_REV_PARSE_ABBREV_REF = ["rev-parse", "--abbrev-ref", "HEAD"];
11
11
  var ARGS_REV_PARSE_SHOW_TOPLEVEL = ["rev-parse", "--show-toplevel"];
12
12
  var ARGS_REMOTE_GET_URL = ["remote", "get-url"];
13
+ var ARGS_REMOTE_ADD = ["remote", "add"];
14
+ var ARGS_LS_REMOTE = ["ls-remote"];
13
15
  var ARGS_PUSH = ["push"];
14
16
  async function runGit(args, cwd) {
15
17
  return new Promise((resolve, reject) => {
@@ -59,6 +61,18 @@ async function gitGetRemoteUrl(cwd, name = REMOTE_DEFAULT) {
59
61
  if (code !== 0) return null;
60
62
  return stdout.trimEnd() || null;
61
63
  }
64
+ async function gitAddRemote(cwd, url, name = REMOTE_DEFAULT) {
65
+ await runGitOk([...ARGS_REMOTE_ADD, name, url], cwd);
66
+ }
67
+ async function gitRemoteHead(cwd, url, branch) {
68
+ const { code, stdout } = await runGit(
69
+ [...ARGS_LS_REMOTE, url, `refs/heads/${branch}`],
70
+ cwd
71
+ );
72
+ if (code !== 0) return null;
73
+ const sha = stdout.split(/\s+/)[0]?.trim();
74
+ return sha || null;
75
+ }
62
76
  async function gitPush(cwd, branch) {
63
77
  const args = branch ? [...ARGS_PUSH, REMOTE_DEFAULT, branch] : [...ARGS_PUSH];
64
78
  await runGitOk(args, cwd);
@@ -76,4 +90,4 @@ function isFreestyleRemote(url) {
76
90
  }
77
91
  }
78
92
 
79
- export { gitCurrentBranch, gitCurrentSha, gitDetectRoot, gitGetRemoteUrl, gitPush, gitPushToUrl, gitStatusPorcelain, isFreestyleRemote };
93
+ export { gitAddRemote, gitCurrentBranch, gitCurrentSha, gitDetectRoot, gitGetRemoteUrl, gitPush, gitPushToUrl, gitRemoteHead, gitStatusPorcelain, isFreestyleRemote };
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import { isFreestyleRemote, gitDetectRoot, gitStatusPorcelain, gitCurrentBranch, gitCurrentSha, gitGetRemoteUrl, gitPushToUrl, gitPush } from './chunk-MLVBE5V2.mjs';
3
2
  import { pollDeployment } from './chunk-PDGKBATF.mjs';
4
- import { fetchRepoRecord, fetchCloneToken } from './chunk-OE7YRDLH.mjs';
3
+ import { fetchRepoRecord, fetchCloneToken } from './chunk-7CI5RWNE.mjs';
5
4
  import { promptYesNo } from './chunk-5ZQURRIM.mjs';
6
5
  import { REPO_PROVIDER, ENV_SCOPE, DEPLOY_TRIGGER, DEPLOYMENT_STATUS, TOKEN_PURPOSE } from './chunk-MXYUAJEW.mjs';
6
+ import { isFreestyleRemote, gitDetectRoot, gitStatusPorcelain, gitCurrentBranch, gitCurrentSha, gitGetRemoteUrl, gitPushToUrl, gitPush } from './chunk-YIBOOKQQ.mjs';
7
7
  import { parseArgs, flagBool, flagString } from './chunk-5KYR6WJB.mjs';
8
8
  import { ApiClient } from './chunk-TOQGLBWF.mjs';
9
9
  import { readAuth, readProjectLink, writeProjectState } from './chunk-6CHBU7O4.mjs';
@@ -15,9 +15,39 @@ var FLAG_REF = "ref";
15
15
  var FLAG_NO_PUSH = "no-push";
16
16
  var FLAG_ALLOW_DIRTY = "allow-dirty";
17
17
  var FLAG_NO_POLL = "no-poll";
18
+ var FLAG_SKIP_ENV_CHECK = "skip-env-check";
18
19
  function deployEndpoint(businessId, projectId) {
19
20
  return `/v1/businesses/${encodeURIComponent(businessId)}/projects/${encodeURIComponent(projectId)}/deploy`;
20
21
  }
22
+ function envEndpoint(businessId, projectId) {
23
+ return `/v1/businesses/${encodeURIComponent(businessId)}/projects/${encodeURIComponent(projectId)}/env-vars`;
24
+ }
25
+ function missingProductionKeys(preview, production) {
26
+ const productionKeys = new Set(production.map((record) => record.key));
27
+ return [...new Set(preview.map((record) => record.key))].filter((key) => !productionKeys.has(key)).sort();
28
+ }
29
+ async function checkProductionEnv(client, businessId, projectId) {
30
+ const [preview, production] = await Promise.all([
31
+ client.get(envEndpoint(businessId, projectId), {
32
+ query: { scope: ENV_SCOPE.PREVIEW }
33
+ }),
34
+ client.get(envEndpoint(businessId, projectId), {
35
+ query: { scope: ENV_SCOPE.PRODUCTION }
36
+ })
37
+ ]);
38
+ const missing = missingProductionKeys(preview, production);
39
+ if (missing.length === 0) return;
40
+ const lines = missing.map(
41
+ (key) => ` cimplify env add ${key}=<value> --scope ${ENV_SCOPE.PRODUCTION}`
42
+ );
43
+ throw new CliError(
44
+ CLI_ERROR_CODE.INVALID_INPUT,
45
+ `Production is missing ${missing.length} env var${missing.length === 1 ? "" : "s"} that preview has: ${missing.join(", ")}.
46
+ Set them first:
47
+ ${lines.join("\n")}
48
+ Or deploy anyway with --${FLAG_SKIP_ENV_CHECK}.`
49
+ );
50
+ }
21
51
  function resolvePushStrategy(originUrl, linkRemoteUrl, repo) {
22
52
  if (isFreestyleRemote(originUrl) || isFreestyleRemote(linkRemoteUrl)) {
23
53
  return { kind: "managed" };
@@ -88,6 +118,9 @@ async function run(argv) {
88
118
  const branch = await gitCurrentBranch(root);
89
119
  const localSha = await gitCurrentSha(root);
90
120
  const gitRef = explicitRef ?? localSha;
121
+ if (flagBool(args, FLAG_PROD) && !flagBool(args, FLAG_SKIP_ENV_CHECK)) {
122
+ await checkProductionEnv(client, link.businessId, link.projectId);
123
+ }
91
124
  if (!flagBool(args, FLAG_NO_PUSH)) {
92
125
  await pushForDeploy(client, link.businessId, link.projectId, root, branch, link.remoteUrl);
93
126
  } else {
@@ -158,4 +191,4 @@ async function run(argv) {
158
191
  process.exitCode = EXIT_CODE.ERROR;
159
192
  }
160
193
 
161
- export { run as default, resolvePushStrategy };
194
+ export { run as default, missingProductionKeys, resolvePushStrategy };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { TEMPLATES } from './chunk-BDC7XQIL.mjs';
3
- import { package_default } from './chunk-5YVMA7A5.mjs';
2
+ import { TEMPLATES } from './chunk-JOF7ESSA.mjs';
3
+ import { package_default } from './chunk-KAZMZD4X.mjs';
4
4
 
5
5
  // src/dispatcher.ts
6
6
  var VERSION = package_default.version ?? "unknown";
@@ -137,10 +137,10 @@ var COMMANDS = {
137
137
  login: () => import('./login-NCO4P5G5.mjs'),
138
138
  logout: () => import('./logout-MF6SOLPE.mjs'),
139
139
  whoami: () => import('./whoami-B5Y5KSOZ.mjs'),
140
- projects: () => import('./projects-U75DOOR7.mjs'),
141
- link: () => import('./link-KGNQWCSZ.mjs'),
140
+ projects: () => import('./projects-P65SUETQ.mjs'),
141
+ link: () => import('./link-AWC7VJNN.mjs'),
142
142
  unlink: () => import('./unlink-TCG3NBAR.mjs'),
143
- deploy: () => import('./deploy-SLRFPGTH.mjs'),
143
+ deploy: () => import('./deploy-64ZVAQ3R.mjs'),
144
144
  cancel: () => import('./cancel-FD66LTTS.mjs'),
145
145
  rollback: () => import('./rollback-ZL3V7SYH.mjs'),
146
146
  env: () => import('./env-PAENNABF.mjs'),
@@ -149,17 +149,17 @@ var COMMANDS = {
149
149
  status: () => import('./status-52HG63FE.mjs'),
150
150
  dev: () => import('./dev-L3W6UXYN.mjs'),
151
151
  mock: () => import('./mock-CAWNXSGC.mjs'),
152
- introspect: () => import('./introspect-3OYRCVUF.mjs'),
152
+ introspect: () => import('./introspect-APRI7R7K.mjs'),
153
153
  inspect: () => import('./inspect-MGVG4Z44.mjs'),
154
- doctor: () => import('./doctor-6NI2XNTD.mjs'),
154
+ doctor: () => import('./doctor-6ZIFM5J5.mjs'),
155
155
  lint: () => import('./lint-EYKIQ6RO.mjs'),
156
- explain: () => import('./explain-HPRJBFHS.mjs'),
156
+ explain: () => import('./explain-REIDDRKC.mjs'),
157
157
  assets: () => import('./assets-OEY6FHHZ.mjs'),
158
- repo: () => import('./repo-IE5IOLR5.mjs'),
159
- list: () => import('./list-QNEUCCP5.mjs'),
160
- add: () => import('./add-NZ47GUMD.mjs'),
161
- update: () => import('./update-47HG7LN7.mjs'),
162
- upgrade: () => import('./update-47HG7LN7.mjs'),
158
+ repo: () => import('./repo-ZPYZK3KW.mjs'),
159
+ list: () => import('./list-7ZL6FRKT.mjs'),
160
+ add: () => import('./add-PPUGTTDA.mjs'),
161
+ update: () => import('./update-MBEPZANS.mjs'),
162
+ upgrade: () => import('./update-MBEPZANS.mjs'),
163
163
  "auth-step-up": () => import('./auth-step-up-ALEUQUCE.mjs')
164
164
  };
165
165
  var COMMAND_PREFIXES = {
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { gatherIntrospection } from './chunk-GOTWRZVM.mjs';
3
- import './chunk-MLVBE5V2.mjs';
2
+ import { gatherIntrospection } from './chunk-SOYZZZVT.mjs';
4
3
  import './chunk-RU53TQLZ.mjs';
5
- import './chunk-5YVMA7A5.mjs';
4
+ import './chunk-KAZMZD4X.mjs';
5
+ import './chunk-YIBOOKQQ.mjs';
6
6
  import { parseArgs, flagBool } from './chunk-5KYR6WJB.mjs';
7
7
  import { SDK_ENV_API_URL, DEFAULT_API_BASE_URL, ApiClient, ENV_API_BASE_URL } from './chunk-TOQGLBWF.mjs';
8
8
  import { readAuthOrNull } from './chunk-6CHBU7O4.mjs';
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { package_default } from './chunk-5YVMA7A5.mjs';
2
+ import { package_default } from './chunk-KAZMZD4X.mjs';
3
3
  import { parseArgs } from './chunk-5KYR6WJB.mjs';
4
4
  import { bold, dim, info, result, CliError, CLI_ERROR_CODE } from './chunk-4O4G2CDE.mjs';
5
5
 
@@ -129,7 +129,7 @@ var TOPICS = [
129
129
  "title": "Assets",
130
130
  "description": "Storefront brand assets \u2014 upload to CDN, build URLs, render through next/image",
131
131
  "source_url": "https://cimplify.dev/docs/sdk/assets",
132
- "body": 'The asset surface on the SDK is **read-side only**: building URLs for images you\'ve already uploaded via [`cimplify assets upload`](/docs/cli/assets). Customer-runtime uploads still go through `client.uploads.upload(file)`.\n\n## `assetUrl(path, opts?)`\n\nPure URL builder. Prepends the CDN base for relative paths, passes absolute URLs through with the transform query contract appended.\n\n```ts\n\nassetUrl("hero/main.jpg")\n// "https://storefrontassetscdn.cimplify.io/hero/main.jpg"\n\nassetUrl("hero/main.jpg", { w: 1200, format: "webp", quality: 80 })\n// "https://storefrontassetscdn.cimplify.io/hero/main.jpg?w=1200&format=webp&quality=80"\n\nassetUrl("hero/main.jpg", { base: "http://localhost:8787/storage" })\n// "http://localhost:8787/storage/hero/main.jpg"\n\nassetUrl("https://res.cloudinary.com/foo/bar.jpg")\n// "https://res.cloudinary.com/foo/bar.jpg" (absolute \u2192 passthrough)\n```\n\n### Options\n\n| Field | Type | Notes |\n| --- | --- | --- |\n| `base` | `string` | Override the CDN base. Defaults to `DEFAULT_CDN_BASE_URL`. |\n| `w` / `h` | `number` | Width / height in pixels (on-the-fly resizing) |\n| `quality` | `number` | 0\u2013100 |\n| `format` | `"auto" \\| "webp" \\| "avif" \\| "jpeg"` | `"auto"` lets the CDN pick by `Accept` header |\n\nThe transform query is only meaningful when the URL terminates on the Cimplify storefront CDN (which supports on-the-fly resizing). For external hosts, unknown params are typically ignored; the image still loads at its original size.\n\n## `isCimplifyAsset(src, base?)`\n\nDecides whether `src` is hosted on a Cimplify CDN. Used by the storefront\'s `next/image` loader to skip transforms for external URLs.\n\n```ts\n\nisCimplifyAsset("hero/main.jpg") // true (relative \u2192 ours)\nisCimplifyAsset("/img/seed/bakery/x.jpg") // true (dev mock passthrough)\nisCimplifyAsset("https://storefrontassetscdn.cimplify.io/x.jpg") // true\nisCimplifyAsset("https://cdn.cimplify.io/x.jpg") // true\nisCimplifyAsset("https://static-tmp.cimplify.io/seed/x.jpg") // true\nisCimplifyAsset("https://res.cloudinary.com/demo/upload/x.jpg") // false\nisCimplifyAsset("https://images.unsplash.com/photo-xyz") // false\n```\n\n## Built-in `next/image` loader\n\nEvery storefront template scaffolded by `cimplify init` ships `lib/cimplify-loader.ts` and points `images.loaderFile` at it. The loader:\n\n```ts\n\nconst cdnBase = process.env.NEXT_PUBLIC_CIMPLIFY_CDN_URL?.trim() || undefined;\n\nconst cimplifyImageLoader: ImageLoader = ({ src, width, quality }) => {\n if (isCimplifyAsset(src, cdnBase)) {\n return assetUrl(src, { base: cdnBase, w: width, quality, format: "auto" });\n }\n return src;\n};\n\nexport default cimplifyImageLoader;\n```\n\n**Cloudinary URLs from `client.catalogue.getProducts()` flow through untouched.** Cimplify-hosted URLs get the transform query. The result: every `<Image>` works regardless of where its asset lives, and Cimplify-hosted ones get edge-optimized.\n\nTo point the loader at a different CDN (for testing, multi-CDN setups, etc.) set `NEXT_PUBLIC_CIMPLIFY_CDN_URL` in your `.env.local`:\n\n```\nNEXT_PUBLIC_CIMPLIFY_CDN_URL=http://localhost:8787/storage\n```\n\n## `useImage(path, opts?)` (React)\n\nThin hook that returns `{ src, loader }` for `<Image>`:\n\n```tsx\n\nexport function Hero() {\n const { src, loader } = useImage("hero/main.jpg", { format: "webp", quality: 85 });\n return <Image src={src} loader={loader} alt="..." width={1200} height={800} />;\n}\n```\n\nIf `images.loaderFile` is already set globally in your `next.config.ts`, you typically don\'t need `useImage`; just `<Image src={assetUrl("hero/main.jpg")} />` works. `useImage` is useful when you want per-component transform overrides on a project that doesn\'t use a global loader.\n\n### Options\n\n| Field | Notes |\n| --- | --- |\n| `base` | Per-call CDN base override |\n| `w`, `h` | Default transform values; per-image `width` from `<Image>` still wins |\n| `format`, `quality` | Default transform values |\n\n`useImage` is `useMemo`-stable across renders when the path + opts don\'t change.\n\n## Constants\n\n```ts\n\nDEFAULT_CDN_BASE_URL // "https://storefrontassetscdn.cimplify.io"\nCIMPLIFY_CDN_HOSTS // readonly ["storefrontassetscdn.cimplify.io", "cdn.cimplify.io", "static-tmp.cimplify.io"]\n```\n\n## What\'s not in v1\n\n- **`useUpload` hook**: runtime customer-facing uploads with progress + cancel. Deferred to v1.1. Customer uploads today go through `client.uploads.upload(file)` (no progress, no cancel; just `await`).\n- **`SEED_IDS` typed const tree**: demo-only convenience for referencing seed-image slugs in code. Generate-from-seed-source step deferred to v1.1; today demos use `seedImage(industry, slug)` from the SDK\'s mock module.\n- **Cloudinary / S3 path-transform support**: the loader passes external hosts through unchanged; Cloudinary\'s `c_fill,w_500/` path transforms aren\'t synthesized.\n- **`assets rm` server-side delete**: CLI removes manifest entries; the remote blob stays until v1.1\'s media-manager `DELETE` wiring.\n\n## Where it lives in the platform\n\n- Wire format: `POST /v1/businesses/{id}/assets/{init,confirm}`; see the management API reference\n- Backend service: `crate::uploads::service::UploadService::init_storefront_asset`\n- Served from the Cimplify asset CDN at `storefrontassetscdn.cimplify.io`\n- Storage key: `assets/{business_id}/{folder}/{filename}`: deterministic, idempotent, agent-predictable\n\n## Related\n\n- [`cimplify assets`](/docs/cli/assets): CLI surface for upload/ls/rm\n- [Uploads (customer-runtime)](/docs/sdk/uploads): `client.uploads.upload(file)` for private 7-day-presigned uploads\n'
132
+ "body": 'The asset surface on the SDK is **read-side only**: building URLs for images you\'ve already uploaded via [`cimplify assets upload`](/docs/cli/assets). Customer-runtime uploads still go through `client.uploads.upload(file)`.\n\n## `assetUrl(path, opts?)`\n\nPure URL builder. Prepends the CDN base for relative paths, passes absolute URLs through with the transform query contract appended.\n\n```ts\n\nassetUrl("hero/main.jpg")\n// "https://storefrontassetscdn.cimplify.io/hero/main.jpg"\n\nassetUrl("hero/main.jpg", { w: 1200, format: "webp", quality: 80 })\n// "https://storefrontassetscdn.cimplify.io/hero/main.jpg?w=1200&format=webp&quality=80"\n\nassetUrl("hero/main.jpg", { base: "http://localhost:8787/storage" })\n// "http://localhost:8787/storage/hero/main.jpg"\n\nassetUrl("https://res.cloudinary.com/foo/bar.jpg")\n// "https://res.cloudinary.com/foo/bar.jpg" (absolute \u2192 passthrough)\n```\n\n### Options\n\n| Field | Type | Notes |\n| --- | --- | --- |\n| `base` | `string` | Override the CDN base. Defaults to `DEFAULT_CDN_BASE_URL`. |\n| `w` / `h` | `number` | Width / height in pixels (on-the-fly resizing) |\n| `quality` | `number` | 0\u2013100 |\n| `format` | `"auto" \\| "webp" \\| "avif" \\| "jpeg"` | `"auto"` lets the CDN pick by `Accept` header |\n\nThe transform query is only meaningful when the URL terminates on the Cimplify storefront CDN (which supports on-the-fly resizing). For external hosts, unknown params are typically ignored; the image still loads at its original size.\n\n## `isCimplifyAsset(src, base?)`\n\nDecides whether `src` is hosted on a Cimplify CDN. Used by the storefront\'s `next/image` loader to skip transforms for external URLs.\n\n```ts\n\nisCimplifyAsset("hero/main.jpg") // true (relative \u2192 ours)\nisCimplifyAsset("/img/seed/bakery/x.jpg") // true (dev mock passthrough)\nisCimplifyAsset("https://storefrontassetscdn.cimplify.io/x.jpg") // true\nisCimplifyAsset("https://cdn.cimplify.io/x.jpg") // true\nisCimplifyAsset("https://static-tmp.cimplify.io/seed/x.jpg") // true\nisCimplifyAsset("https://res.cloudinary.com/demo/upload/x.jpg") // false\nisCimplifyAsset("https://images.unsplash.com/photo-xyz") // false\n```\n\n## Built-in `next/image` loader\n\nEvery storefront template scaffolded by `cimplify init` ships `lib/cimplify-loader.ts` and points `images.loaderFile` at it. The loader:\n\n```ts\n\nconst cdnBase = process.env.NEXT_PUBLIC_CIMPLIFY_CDN_URL?.trim() || undefined;\n\nconst cimplifyImageLoader: ImageLoader = ({ src, width, quality }) => {\n if (isCimplifyAsset(src, cdnBase)) {\n return assetUrl(src, { base: cdnBase, w: width, quality, format: "auto" });\n }\n return src;\n};\n\nexport default cimplifyImageLoader;\n```\n\n**Cloudinary URLs from `client.catalogue.getProducts()` flow through untouched.** Cimplify-hosted URLs get the transform query. The result: every `<Image>` works regardless of where its asset lives, and Cimplify-hosted ones get edge-optimized.\n\nTo point the loader at a different CDN (for testing, multi-CDN setups, etc.) set `NEXT_PUBLIC_CIMPLIFY_CDN_URL` in your `.env.local`:\n\n```\nNEXT_PUBLIC_CIMPLIFY_CDN_URL=http://localhost:8787/storage\n```\n\n## `useImage(path, opts?)` (React)\n\nThin hook that returns `{ src, loader }` for `<Image>`:\n\n```tsx\n\nexport function Hero() {\n const { src, loader } = useImage("hero/main.jpg", { format: "webp", quality: 85 });\n return <Image src={src} loader={loader} alt="..." width={1200} height={800} />;\n}\n```\n\nIf `images.loaderFile` is already set globally in your `next.config.ts`, you typically don\'t need `useImage`; just `<Image src={assetUrl("hero/main.jpg")} />` works. `useImage` is useful when you want per-component transform overrides on a project that doesn\'t use a global loader.\n\n### Options\n\n| Field | Notes |\n| --- | --- |\n| `base` | Per-call CDN base override |\n| `w`, `h` | Default transform values; per-image `width` from `<Image>` still wins |\n| `format`, `quality` | Default transform values |\n\n`useImage` is `useMemo`-stable across renders when the path + opts don\'t change.\n\n## Constants\n\n```ts\n\nDEFAULT_CDN_BASE_URL // "https://storefrontassetscdn.cimplify.io"\nCIMPLIFY_CDN_HOSTS // readonly ["storefrontassetscdn.cimplify.io", "cdn.cimplify.io", "static-tmp.cimplify.io"]\n```\n\n## What\'s not in v1\n\n- **`useUpload` hook**: runtime customer-facing uploads with progress + cancel. Deferred to v1.1. Customer uploads today go through `client.uploads.upload(file)` (no progress, no cancel; just `await`).\n- **`SEED_IDS` typed const tree**: demo-only convenience for referencing seed-image slugs in code. Generate-from-seed-source step deferred to v1.1; today demos use `seedImage(industry, slug)` from the SDK\'s mock module.\n- **Cloudinary / S3 path-transform support**: the loader passes external hosts through unchanged; Cloudinary\'s `c_fill,w_500/` path transforms aren\'t synthesized.\n- **`assets rm` server-side delete**: CLI removes manifest entries; the remote blob stays until v1.1\'s media-manager `DELETE` wiring.\n\n## Where it lives in the platform\n\n- Wire format: `POST /v1/businesses/{id}/assets/{init,confirm}`; see the management API reference\n- Backend service: `crate::uploads::service::UploadService::init_storefront_asset`\n- Served from the Cimplify asset CDN at `storefrontassetscdn.cimplify.io`\n- Storage key: `assets/{business_id}/{folder}/{upload_id}/{filename}`: immutable per upload; use the returned preview URL\n\n## Related\n\n- [`cimplify assets`](/docs/cli/assets): CLI surface for upload/ls/rm\n- [Uploads (customer-runtime)](/docs/sdk/uploads): `client.uploads.upload(file)` for private 7-day-presigned uploads\n'
133
133
  },
134
134
  {
135
135
  "name": "exit-codes",
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- export { run as default, extractMockSeed, gatherIntrospection, renderIntrospection } from './chunk-GOTWRZVM.mjs';
3
- import './chunk-MLVBE5V2.mjs';
2
+ export { run as default, extractMockSeed, gatherIntrospection, renderIntrospection } from './chunk-SOYZZZVT.mjs';
4
3
  import './chunk-RU53TQLZ.mjs';
5
- import './chunk-5YVMA7A5.mjs';
4
+ import './chunk-KAZMZD4X.mjs';
5
+ import './chunk-YIBOOKQQ.mjs';
6
6
  import './chunk-5KYR6WJB.mjs';
7
7
  import './chunk-6CHBU7O4.mjs';
8
8
  import './chunk-4O4G2CDE.mjs';
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { gitDetectRoot, gitGetRemoteUrl, gitAddRemote } from './chunk-YIBOOKQQ.mjs';
2
3
  import { parseArgs } from './chunk-5KYR6WJB.mjs';
3
4
  import { ApiClient } from './chunk-TOQGLBWF.mjs';
4
5
  import { readAuth, writeProjectLink } from './chunk-6CHBU7O4.mjs';
@@ -32,14 +33,30 @@ async function run(argv) {
32
33
  linkedAt: (/* @__PURE__ */ new Date()).toISOString()
33
34
  };
34
35
  await writeProjectLink(link);
36
+ const originConfigured = await configureOrigin(link.remoteUrl);
35
37
  success(`Linked to ${project.name} (${project.id})`);
36
38
  if (link.remoteUrl) info(dim(`remote: ${link.remoteUrl}`));
37
39
  result({
38
40
  project: { id: project.id, name: project.name },
39
41
  business: { id: project.business_id },
40
42
  remote_url: link.remoteUrl ?? null,
41
- default_branch: link.defaultBranch ?? null
43
+ default_branch: link.defaultBranch ?? null,
44
+ origin_configured: originConfigured
42
45
  });
43
46
  }
47
+ async function configureOrigin(remoteUrl) {
48
+ if (!remoteUrl) return false;
49
+ const root = await gitDetectRoot(process.cwd());
50
+ if (!root) return false;
51
+ const existing = await gitGetRemoteUrl(root);
52
+ if (existing === remoteUrl) return true;
53
+ if (existing) {
54
+ info(dim(`origin already points at ${existing}; project remote is ${remoteUrl}`));
55
+ return false;
56
+ }
57
+ await gitAddRemote(root, remoteUrl);
58
+ info(dim(`origin \u2192 ${remoteUrl}`));
59
+ return true;
60
+ }
44
61
 
45
62
  export { run as default };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { REGISTRY_INDEX } from './chunk-BDC7XQIL.mjs';
2
+ import { REGISTRY_INDEX } from './chunk-JOF7ESSA.mjs';
3
3
  import { parseArgs, flagBool } from './chunk-5KYR6WJB.mjs';
4
4
  import { CliError, CLI_ERROR_CODE, info, bold, dim, green, result } from './chunk-4O4G2CDE.mjs';
5
5
 
@@ -176,8 +176,8 @@ async function createProject(client, businessId, name, options = {}) {
176
176
  info(dim(` remote: ${repo.remote_url}`));
177
177
  info("");
178
178
  info(dim("Wire your local repo:"));
179
- info(dim(` cimplify link ${project.id}`));
180
- info(dim(` git init && git remote add origin ${repo.remote_url}`));
179
+ info(dim(" git init"));
180
+ info(dim(` cimplify link ${project.id} # configures origin for you`));
181
181
  info(dim(` git add . && git commit -m "Initial commit"`));
182
182
  info(dim(` cimplify deploy`));
183
183
  result({
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
- export { run as default, fetchCloneToken, fetchRepoRecord } from './chunk-OE7YRDLH.mjs';
2
+ export { run as default, fetchCloneToken, fetchRepoRecord } from './chunk-7CI5RWNE.mjs';
3
3
  import './chunk-5ZQURRIM.mjs';
4
4
  import './chunk-MXYUAJEW.mjs';
5
+ import './chunk-YIBOOKQQ.mjs';
5
6
  import './chunk-5KYR6WJB.mjs';
6
7
  import './chunk-TOQGLBWF.mjs';
7
8
  import './chunk-6CHBU7O4.mjs';
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { package_default } from './chunk-5YVMA7A5.mjs';
2
+ import { package_default } from './chunk-KAZMZD4X.mjs';
3
3
  import { promptYesNo } from './chunk-5ZQURRIM.mjs';
4
4
  import { parseArgs, flagBool, flagString } from './chunk-5KYR6WJB.mjs';
5
5
  import { success, bold, info, dim, result, failure, CliError, CLI_ERROR_CODE, step, isJsonMode } from './chunk-4O4G2CDE.mjs';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cimplify/cli",
3
- "version": "0.13.5",
3
+ "version": "0.14.0",
4
4
  "description": "Cimplify CLI — deploy, manage env vars, link projects, and scaffold storefronts",
5
5
  "keywords": [
6
6
  "cimplify",
@@ -41,7 +41,7 @@
41
41
  "@babel/parser": "^8.0.4",
42
42
  "@babel/traverse": "^8.0.4",
43
43
  "@babel/types": "^8.0.4",
44
- "@cimplify/sdk": "^0.100.5",
44
+ "@cimplify/sdk": "^0.101.1",
45
45
  "re2js": "^1.4.0"
46
46
  },
47
47
  "devDependencies": {
@@ -17,7 +17,7 @@
17
17
  "check": "bun run typecheck && bun run test:run"
18
18
  },
19
19
  "dependencies": {
20
- "@cimplify/sdk": "^0.100.5",
20
+ "@cimplify/sdk": "^0.101.1",
21
21
  "next": "^16.2.6",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
@@ -17,7 +17,7 @@
17
17
  "check": "bun run typecheck && bun run test:run"
18
18
  },
19
19
  "dependencies": {
20
- "@cimplify/sdk": "^0.100.5",
20
+ "@cimplify/sdk": "^0.101.1",
21
21
  "next": "^16.2.6",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
@@ -19,7 +19,7 @@
19
19
  "check": "bun run typecheck && bun run test:run"
20
20
  },
21
21
  "dependencies": {
22
- "@cimplify/sdk": "^0.100.5",
22
+ "@cimplify/sdk": "^0.101.1",
23
23
  "next": "^16.2.6",
24
24
  "react": "^19.0.0",
25
25
  "react-dom": "^19.0.0"
@@ -17,7 +17,7 @@
17
17
  "check": "bun run typecheck && bun run test:run"
18
18
  },
19
19
  "dependencies": {
20
- "@cimplify/sdk": "^0.100.5",
20
+ "@cimplify/sdk": "^0.101.1",
21
21
  "next": "^16.2.6",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
@@ -17,7 +17,7 @@
17
17
  "check": "bun run typecheck && bun run test:run"
18
18
  },
19
19
  "dependencies": {
20
- "@cimplify/sdk": "^0.100.5",
20
+ "@cimplify/sdk": "^0.101.1",
21
21
  "next": "^16.2.6",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
@@ -17,7 +17,7 @@
17
17
  "check": "bun run typecheck && bun run test:run"
18
18
  },
19
19
  "dependencies": {
20
- "@cimplify/sdk": "^0.100.5",
20
+ "@cimplify/sdk": "^0.101.1",
21
21
  "next": "^16.2.6",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
@@ -17,7 +17,7 @@
17
17
  "check": "bun run typecheck && bun run test:run"
18
18
  },
19
19
  "dependencies": {
20
- "@cimplify/sdk": "^0.100.5",
20
+ "@cimplify/sdk": "^0.101.1",
21
21
  "next": "^16.2.6",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"
@@ -17,7 +17,7 @@
17
17
  "check": "bun run typecheck && bun run test:run"
18
18
  },
19
19
  "dependencies": {
20
- "@cimplify/sdk": "^0.100.5",
20
+ "@cimplify/sdk": "^0.101.1",
21
21
  "next": "^16.2.6",
22
22
  "react": "^19.0.0",
23
23
  "react-dom": "^19.0.0"