@cimplify/cli 0.6.11 → 0.6.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{add-5G7UZ6DA.mjs → add-6SPMUMSJ.mjs} +1 -1
- package/dist/{assets-EBEMMENZ.mjs → assets-74SK63TR.mjs} +73 -24
- package/dist/{chunk-SNDMYYOK.mjs → chunk-CNIFXKZV.mjs} +1 -1
- package/dist/{chunk-KOJZB3S4.mjs → chunk-RGF2RSSR.mjs} +4 -1
- package/dist/chunk-RIOQDUQO.mjs +5707 -0
- package/dist/dispatcher.mjs +12 -10
- package/dist/{doctor-3UOTPOJ6.mjs → doctor-726TVOXT.mjs} +2 -2
- package/dist/{explain-DXJ3R52F.mjs → explain-6BWR5MCK.mjs} +29 -1
- package/dist/inspect-CGYX4DDF.mjs +411 -0
- package/dist/{introspect-77SUZ5X5.mjs → introspect-4LD27NVX.mjs} +2 -2
- package/dist/{list-QX7RM3P3.mjs → list-2YRVKBXY.mjs} +1 -1
- package/dist/{update-UHKPFT6F.mjs → update-J65Z6Q7E.mjs} +1 -1
- package/package.json +4 -1
- package/templates/storefront-auto/app/api/revalidate/route.ts +5 -0
- package/templates/storefront-auto/bun.lock +745 -6
- package/templates/storefront-auto/package.json +2 -2
- package/templates/storefront-auto/tsconfig.json +24 -6
- package/templates/storefront-bakery/app/api/revalidate/route.ts +5 -0
- package/templates/storefront-bakery/bun.lock +1167 -0
- package/templates/storefront-bakery/package.json +2 -2
- package/templates/storefront-bakery/tsconfig.json +24 -6
- package/templates/storefront-fashion/app/api/revalidate/route.ts +5 -0
- package/templates/storefront-fashion/bun.lock +747 -8
- package/templates/storefront-fashion/package.json +2 -2
- package/templates/storefront-fashion/tsconfig.json +24 -6
- package/templates/storefront-grocery/app/api/revalidate/route.ts +5 -0
- package/templates/storefront-grocery/bun.lock +745 -6
- package/templates/storefront-grocery/package.json +2 -2
- package/templates/storefront-grocery/tsconfig.json +24 -6
- package/templates/storefront-pharmacy/app/api/revalidate/route.ts +5 -0
- package/templates/storefront-pharmacy/bun.lock +745 -6
- package/templates/storefront-pharmacy/package.json +2 -2
- package/templates/storefront-pharmacy/tsconfig.json +24 -6
- package/templates/storefront-restaurant/app/api/revalidate/route.ts +5 -0
- package/templates/storefront-restaurant/bun.lock +745 -6
- package/templates/storefront-restaurant/package.json +2 -2
- package/templates/storefront-restaurant/tsconfig.json +24 -6
- package/templates/storefront-retail/app/api/revalidate/route.ts +5 -0
- package/templates/storefront-retail/bun.lock +745 -6
- package/templates/storefront-retail/package.json +2 -2
- package/templates/storefront-retail/tsconfig.json +24 -6
- package/templates/storefront-services/app/api/revalidate/route.ts +5 -0
- package/templates/storefront-services/bun.lock +745 -6
- package/templates/storefront-services/package.json +2 -2
- package/templates/storefront-services/tsconfig.json +24 -6
- package/dist/chunk-OLZMA2TM.mjs +0 -5707
package/dist/dispatcher.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { TEMPLATES } from './chunk-
|
|
3
|
-
import { package_default } from './chunk-
|
|
2
|
+
import { TEMPLATES } from './chunk-RIOQDUQO.mjs';
|
|
3
|
+
import { package_default } from './chunk-RGF2RSSR.mjs';
|
|
4
4
|
|
|
5
5
|
// src/dispatcher.ts
|
|
6
6
|
var VERSION = package_default.version ?? "unknown";
|
|
@@ -19,6 +19,7 @@ Project:
|
|
|
19
19
|
cimplify link <project-id> Link CWD to a Cimplify project (writes .cimplify/project.json)
|
|
20
20
|
cimplify unlink Remove the local project link
|
|
21
21
|
cimplify introspect Snapshot the current storefront: auth, link, brand, mock, env, git
|
|
22
|
+
cimplify inspect [catalogue] Read-only snapshot of the live catalogue (products + collections + gaps)
|
|
22
23
|
cimplify doctor [--offline] Run pre-flight checks; verdicts + fix hints (exit 1 on any fail)
|
|
23
24
|
cimplify explain [topic] Print canonical guidance on cart, products, bundles, errors, \u2026
|
|
24
25
|
cimplify assets upload <dir> Upload storefront brand assets to the Cimplify CDN
|
|
@@ -137,15 +138,16 @@ var COMMANDS = {
|
|
|
137
138
|
logs: () => import('./logs-YNN2PQ24.mjs'),
|
|
138
139
|
status: () => import('./status-JSYXM5RT.mjs'),
|
|
139
140
|
dev: () => import('./dev-ONW2S77K.mjs'),
|
|
140
|
-
introspect: () => import('./introspect-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
introspect: () => import('./introspect-4LD27NVX.mjs'),
|
|
142
|
+
inspect: () => import('./inspect-CGYX4DDF.mjs'),
|
|
143
|
+
doctor: () => import('./doctor-726TVOXT.mjs'),
|
|
144
|
+
explain: () => import('./explain-6BWR5MCK.mjs'),
|
|
145
|
+
assets: () => import('./assets-74SK63TR.mjs'),
|
|
144
146
|
repo: () => import('./repo-KNQMSPVV.mjs'),
|
|
145
|
-
list: () => import('./list-
|
|
146
|
-
add: () => import('./add-
|
|
147
|
-
update: () => import('./update-
|
|
148
|
-
upgrade: () => import('./update-
|
|
147
|
+
list: () => import('./list-2YRVKBXY.mjs'),
|
|
148
|
+
add: () => import('./add-6SPMUMSJ.mjs'),
|
|
149
|
+
update: () => import('./update-J65Z6Q7E.mjs'),
|
|
150
|
+
upgrade: () => import('./update-J65Z6Q7E.mjs'),
|
|
149
151
|
"auth-step-up": () => import('./auth-step-up-BIUYQJP6.mjs')
|
|
150
152
|
};
|
|
151
153
|
var COMMAND_PREFIXES = {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { gatherIntrospection } from './chunk-
|
|
2
|
+
import { gatherIntrospection } from './chunk-CNIFXKZV.mjs';
|
|
3
3
|
import './chunk-K5464A3L.mjs';
|
|
4
4
|
import './chunk-DBZ3UOQ2.mjs';
|
|
5
|
-
import './chunk-
|
|
5
|
+
import './chunk-RGF2RSSR.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-
|
|
2
|
+
import { package_default } from './chunk-RGF2RSSR.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
|
|
|
@@ -138,6 +138,34 @@ var TOPICS = [
|
|
|
138
138
|
"source_url": "https://cimplify.dev/docs/cli#exit-codes",
|
|
139
139
|
"body": "## Exit codes\nDistinct per error class so callers can branch on `$?` without parsing output:\n\n| Code | Name | Meaning |\n| --- | --- | --- |\n| `0` | OK | Success. For `deploy`, deployment is `active`. |\n| `1` | FAILED | Generic failure (build failed, deploy errored). |\n| `2` | SUPERSEDED | A newer push raced this deploy; usually fine. |\n| `3` | ABORTED | User canceled (Ctrl-C) or `cimplify_cancel_deployment`. |\n| `4` | NOT_LINKED | `.cimplify/project.json` missing. Run `cimplify link <id>`. |\n| `7` | INTERACTIVE_REQUIRED | A prompt was needed but `--yes` not passed and stdin is non-TTY. |\n| `9` | NOT_FOUND | Resource (project, domain, deploy) does not exist. |\n| `10` | NETWORK_ERROR | Could not reach `api.cimplify.io`. |\n| `12` | TIMEOUT | Long-poll exceeded its window; the underlying job may still be running. |\n| `20` | NOT_LOGGED_IN | No saved token. Run `cimplify login` or pass `--api-key`. |\n| `21` | AUTH_FAILED | Token expired or revoked. Re-login. |\n| `30` | INVALID_INPUT | Argument/flag validation failed. |\n\nFull list in `@cimplify/cli/errors`.\n"
|
|
140
140
|
},
|
|
141
|
+
{
|
|
142
|
+
"name": "inspect-json",
|
|
143
|
+
"title": 'Inspect \u2014 The JSON envelope (locked contract, `schema_version: "1"`)',
|
|
144
|
+
"description": "The locked JSON envelope cimplify inspect emits",
|
|
145
|
+
"source_url": "https://cimplify.dev/docs/cli/inspect#the-json-envelope-locked-contract-schema_version-1",
|
|
146
|
+
"body": '## The JSON envelope (locked contract, `schema_version: "1"`)\n```jsonc\n{\n "ok": true,\n "schema_version": "1",\n "data": {\n "business": {\n "handle": "bakeryco",\n "name": "Bakery & Co.",\n "currencies": ["USD", "GHS"],\n "locations": 2\n },\n "totals": {\n "products": 142,\n "collections": 8,\n "categories": 12,\n "distinct_tags": 24\n },\n "gaps": {\n "no_image": 11,\n "no_description": 7,\n "no_price": 0,\n "no_collection": 23,\n "no_tags": 56,\n "empty_collections": [{ "id": "col_seasonal", "name": "Seasonal" }],\n "empty_categories": [{ "id": "cat_specials", "name": "Specials" }]\n },\n "products": {\n "sample": [\n {\n "id": "prod_abc1",\n "name": "Croissant",\n "slug": "croissant",\n "price": { "amount": "3.50", "currency": "USD" },\n "has_image": true,\n "has_description": true,\n "tags": ["vegan", "bestseller"],\n "collection_ids": ["col_featured"],\n "category_ids": ["cat_breakfast"],\n "variant_count": 3\n }\n ],\n "next_cursor": "eyJpZCI6IjE5In0",\n "returned": 20\n },\n "collections": [\n {\n "id": "col_featured",\n "name": "Featured",\n "slug": "featured",\n "product_count": 12,\n "sample_product_ids": ["prod_abc1", "prod_abc2", "prod_abc3"]\n }\n ],\n "categories": {\n "tree": [\n { "id": "cat_breakfast", "name": "Breakfast", "slug": "breakfast", "product_count": 24, "children": [] }\n ]\n },\n "tags": {\n "top": [{ "tag": "vegan", "count": 18 }, { "tag": "bestseller", "count": 12 }]\n }\n },\n "meta": {\n "fetched_at": "2026-05-23T10:15:32Z",\n "source": "hosted",\n "sampled": true,\n "sample_size": 20,\n "gaps_method": "sample",\n "shape_hints": ["medium_catalogue", "uses_collections", "has_empty_collection", "untagged_majority", "has_image_gaps"],\n "suggestions": [\n { "kind": "fix_no_image", "count": 11 },\n { "kind": "fix_no_collection", "count": 23 },\n { "kind": "merchandise_empty_collection", "collection_id": "col_seasonal", "name": "Seasonal" }\n ],\n "suggested_commands": ["cimplify inspect catalogue --all"]\n }\n}\n```\n\n### Error envelope\n\n```jsonc\n{\n "ok": false,\n "schema_version": "1",\n "error": {\n "code": "NO_PUBLIC_KEY",\n "message": "Set NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY in .env or pass --public-key.",\n "remediation": "Get one at desk.cimplify.io/settings/api."\n }\n}\n```\n\n### Field rules\n\n- `ok` is always present.\n- `schema_version` is a contract over the whole document; bump on **removal**, **rename**, or **type change** of any field, `shape_hints` token, or `suggestions[].kind`. **Adding** is non-breaking.\n- `data.gaps.empty_collections` / `empty_categories` are always arrays (return `[]`, never omit).\n- `data.products.sample` is **always page-ordered** (never randomized).\n- `next_cursor` is `null` when the walk is exhausted.\n- `meta.source` is `"hosted"` or `"mock"`. Locked.\n- `meta.gaps_method` is `"sample"` or `"exact"`. Locked.\n- `meta.shape_hints` is **sorted alphabetically** (deterministic, diffable).\n- `meta.suggestions` is ordered by impact (most actionable first).\n- `meta.fetched_at` is the only timestamp; nothing in `data` carries time.\n'
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"name": "shape-hints",
|
|
150
|
+
"title": "Inspect \u2014 `shape_hints` vocabulary",
|
|
151
|
+
"description": "The canonical shape_hints vocabulary for cimplify inspect",
|
|
152
|
+
"source_url": "https://cimplify.dev/docs/cli/inspect#shape_hints-vocabulary",
|
|
153
|
+
"body": "## `shape_hints` vocabulary\nFlat string tokens for agents to branch on. Adding tokens is non-breaking; removing/renaming bumps `schema_version`.\n\n### Catalogue size\n\n| Token | Meaning |\n| --- | --- |\n| `empty_catalogue` | 0 products. Whole storefront needs an empty-state. |\n| `tiny_catalogue` | 1\u20139 products. Skip search, deep filters. |\n| `small_catalogue` | 10\u201399 products. Default UX. |\n| `medium_catalogue` | 100\u2013999 products. Search nice-to-have. |\n| `large_catalogue` | 1000+ products. Search + facets required. |\n\n### Feature presence\n\n| Token | Meaning |\n| --- | --- |\n| `has_variants` | At least one product has variants \u2192 variant picker UI. |\n| `has_addons` | At least one product has add-on options \u2192 modifier UI. |\n| `has_subscriptions` | Billing plans exist \u2192 recurring pricing toggle. |\n| `has_composites` | Bundle / composite products exist. |\n| `has_recipes` | Products linked to `ProductComponent` (restaurant / manufacturing). |\n| `has_time_profiles` | Products have time-of-day windows \u2192 hours-aware UI. |\n| `has_input_fields` | Buyer-supplied inputs (engraving, message). |\n| `has_custom_attributes` | Custom attribute schemas defined. |\n| `per_location_pricing` | Prices vary by location. |\n| `multi_currency` | >1 currency configured. |\n| `multi_location` | >1 location configured. |\n\n### Merchandising state\n\n| Token | Meaning |\n| --- | --- |\n| `uses_collections` | \u22651 non-empty collection exists. |\n| `uses_categories` | \u22651 non-empty category. |\n| `has_empty_collection` | \u22651 collection has 0 products. |\n| `has_empty_category` | \u22651 category has 0 products. |\n| `untagged_majority` | >50% of products are untagged. |\n| `unmerchandised_majority` | >50% of products aren't in any collection. |\n\n### Data quality\n\n| Token | Meaning |\n| --- | --- |\n| `has_image_gaps` | \u22651 product missing image. |\n| `has_description_gaps` | \u22651 product missing description. |\n| `has_price_gaps` | \u22651 product missing price. |\n\n### Environment\n\n| Token | Meaning |\n| --- | --- |\n| `mock_data` | Reading from `cimplify-mock` (key isn't `cpk_live_*` / `cpk_test_*`). |\n"
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"name": "revalidation-tags",
|
|
157
|
+
"title": "Revalidation",
|
|
158
|
+
"description": "Canonical cimplify:* cache-tag vocabulary + revalidate helpers",
|
|
159
|
+
"source_url": "https://cimplify.dev/docs/sdk/revalidation",
|
|
160
|
+
"body": 'Cimplify storefronts cache catalogue reads with Next 16\'s `\'use cache\'` + `cacheTag`. When the merchant edits a product, collection, or brand asset, Cimplify POSTs to your storefront\'s `/api/revalidate` with the canonical tag set, and your handler calls `revalidateTag(tag)` for each.\n\nThis page is the canonical contract between Cimplify and your storefront. Use the typed helpers \u2014 never hand-write tag strings \u2014 so the scheme stays in one place.\n\n## Tags\n\nAll tags are namespaced under `cimplify:` so they can\'t collide with consumer tags. Build them via `tags` from `@cimplify/sdk/server`:\n\n```ts\n\n// In a cached server function:\nasync function getProducts() {\n "use cache";\n cacheTag(tags.products());\n cacheLife("hours");\n // \u2026\n}\n```\n\n### Catalogue\n\n| Helper | Tag |\n| --- | --- |\n| `tags.products()` | `cimplify:products` |\n| `tags.product(id)` | `cimplify:product:{id}` |\n| `tags.categories()` | `cimplify:categories` |\n| `tags.category(id)` | `cimplify:category:{id}` |\n| `tags.categoryProducts(id)` | `cimplify:category:{id}:products` |\n| `tags.collections()` | `cimplify:collections` |\n| `tags.collection(id)` | `cimplify:collection:{id}` |\n| `tags.collectionProducts(id)` | `cimplify:collection:{id}:products` |\n| `tags.tag(name)` | `cimplify:tag:{name}` (product tag, e.g. "vegan") |\n| `tags.addons()` | `cimplify:addons` |\n| `tags.addon(id)` | `cimplify:addon:{id}` |\n| `tags.stock()` | `cimplify:stock` |\n| `tags.stockFor(productId)` | `cimplify:stock:{productId}` |\n\n### Brand / business\n\n| Helper | Tag |\n| --- | --- |\n| `tags.business()` | `cimplify:business` |\n| `tags.brand()` | `cimplify:brand` |\n| `tags.locations()` | `cimplify:locations` |\n| `tags.location(id)` | `cimplify:location:{id}` |\n| `tags.locale()` | `cimplify:locale` |\n\n### Pricing / subscriptions\n\n| Helper | Tag |\n| --- | --- |\n| `tags.pricing()` | `cimplify:pricing` |\n| `tags.subscriptions()` | `cimplify:subscriptions` |\n| `tags.subscription(id)` | `cimplify:subscription:{id}` |\n\n### Customer-scoped (Server Actions only)\n\n| Helper | Tag |\n| --- | --- |\n| `tags.orders(customerId)` | `cimplify:orders:{customerId}` |\n| `tags.order(id)` | `cimplify:order:{id}` |\n\n## Granularity\n\nTag with **both** a broad and a precise tag on every read so either-flavour invalidation works:\n\n```ts\ncacheTag(tags.products(), tags.product(id));\n```\n\nThe broad tag (`products`) catches "I changed something product-related, invalidate everything." The precise tag (`product:{id}`) catches "I changed exactly this one product, invalidate only its entries."\n\n## Revalidating from a Server Action\n\n```ts\n\nexport async function saveProduct(id: string, data: ProductInput) {\n await client.catalogue.updateProduct(id, data);\n await revalidateProduct(id);\n}\n```\n\nHelpers:\n\n| Helper | Invalidates |\n| --- | --- |\n| `revalidateProducts()` | `products` |\n| `revalidateProduct(id)` | `product:{id}` + `products` |\n| `revalidateCategories()` | `categories` |\n| `revalidateCategory(id)` | `category:{id}` + `category:{id}:products` + `categories` |\n| `revalidateCollections()` | `collections` |\n| `revalidateCollection(id)` | `collection:{id}` + `collection:{id}:products` + `collections` |\n| `revalidateBusiness()` | `business` |\n| `revalidateBrand()` | `brand` |\n| `revalidateLocations()` | `locations` |\n| `revalidateLocation(id)` | `location:{id}` + `locations` |\n| `revalidatePricing()` | `pricing` + `products` |\n| `revalidateAddOns()` | `addons` |\n| `revalidateAddOn(id)` | `addon:{id}` + `addons` |\n| `revalidateSubscriptions()` | `subscriptions` |\n| `revalidateSubscription(id)` | `subscription:{id}` + `subscriptions` |\n| `revalidateStock(productId?)` | `stock:{productId}` + `stock` (or just `stock` if no id) |\n| `revalidateByTag(tag)` | escape hatch for a raw tag |\n\n## The Cimplify \u2192 storefront contract\n\nWhen a merchant edits something in the dashboard, Cimplify POSTs to your storefront\'s `/api/revalidate` with the matching tag set:\n\n```http\nPOST /api/revalidate\nx-cimplify-timestamp: 1716480000000\nx-cimplify-signature: sha256=<hex>\ncontent-type: application/json\n\n{ "tags": ["cimplify:products", "cimplify:product:p_abc123"] }\n```\n\nYour handler verifies the HMAC (shared secret in `CIMPLIFY_REVALIDATE_SECRET`), iterates `revalidateTag(tag)` for each, and returns 200. The route handler ships in every Cimplify storefront template \u2014 you don\'t write it.\n\nThe tags Cimplify sends use the **exact strings** from this table. If you\'ve tagged your caches with the helpers above, your storefront stays in sync automatically.\n\n## Tag taxonomy is locked\n\nThe vocabulary above is the v1 contract. Adding new tag helpers is non-breaking; removing or renaming any of them bumps the SDK major version. Agents that bake these strings into prompts can pin to v0.x of `@cimplify/sdk`.\n'
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"name": "inspect-suggestions",
|
|
164
|
+
"title": "Inspect \u2014 `suggestions[].kind` vocabulary",
|
|
165
|
+
"description": "The canonical suggestions[].kind vocabulary for cimplify inspect",
|
|
166
|
+
"source_url": "https://cimplify.dev/docs/cli/inspect#suggestionskind-vocabulary",
|
|
167
|
+
"body": '## `suggestions[].kind` vocabulary\nEach entry is a stable `kind` + typed payload. Copy lives here (and in `cimplify explain inspect-suggestions`) so agents don\'t invent phrasing.\n\n| Kind | Payload | Implied prompt |\n| --- | --- | --- |\n| `add_first_product` | `{}` | Catalogue is empty \u2014 seed or add a product. |\n| `add_first_collection` | `{}` | Products exist but no collections \u2014 group some. |\n| `fix_no_image` | `{ count }` | N products without images \u2014 add placeholders. |\n| `fix_no_description` | `{ count }` | N products lack descriptions \u2014 generate copy. |\n| `fix_no_price` | `{ count }` | N products have no price \u2014 open the editor. |\n| `fix_no_collection` | `{ count }` | N products not in any collection \u2014 suggest "Featured". |\n| `fix_no_tags` | `{ count }` | N untagged products \u2014 suggest tags from name/category. |\n| `merchandise_empty_collection` | `{ collection_id, name }` | Collection "X" is empty \u2014 pick products to feature. |\n| `merchandise_empty_category` | `{ category_id, name }` | Category "X" has no products \u2014 fill or remove. |\n| `tag_uncategorized_products` | `{ count }` | Majority untagged \u2014 bulk-tag from category. |\n| `enable_search_ui` | `{ product_count }` | Large catalogue \u2014 add search + filters. |\n'
|
|
168
|
+
},
|
|
141
169
|
{
|
|
142
170
|
"name": "agent-prompts",
|
|
143
171
|
"title": "Agent prompts",
|
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { parseEnvFile } from './chunk-DBZ3UOQ2.mjs';
|
|
3
|
+
import { parseArgs, flagString, flagBool } from './chunk-C4M3DXKC.mjs';
|
|
4
|
+
import { CliError, CLI_ERROR_CODE, isJsonMode, result, info, bold, dim, yellow, green, red } from './chunk-E2T2SBP5.mjs';
|
|
5
|
+
import { promises } from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { createCimplifyClient } from '@cimplify/sdk';
|
|
8
|
+
|
|
9
|
+
var SCHEMA_VERSION = "1";
|
|
10
|
+
var HOSTED_URL = "https://storefronts.cimplify.io";
|
|
11
|
+
var MOCK_URL = "http://127.0.0.1:8787";
|
|
12
|
+
var DEFAULT_LIMIT = 20;
|
|
13
|
+
var COLLECTION_SAMPLE_SIZE = 3;
|
|
14
|
+
var TAG_TOP_N = 10;
|
|
15
|
+
var ENV_FILE_CANDIDATES = [".env.local", ".env", ".env.production", ".env.example"];
|
|
16
|
+
var PUBLIC_KEY_ENV = "NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY";
|
|
17
|
+
var FLAG_ALL = "all";
|
|
18
|
+
var FLAG_LIMIT = "limit";
|
|
19
|
+
var FLAG_CURSOR = "cursor";
|
|
20
|
+
var FLAG_PUBLIC_KEY = "public-key";
|
|
21
|
+
async function run(argv) {
|
|
22
|
+
const args = parseArgs(argv);
|
|
23
|
+
const sub = args.positional[0] ?? "catalogue";
|
|
24
|
+
if (sub !== "catalogue") {
|
|
25
|
+
throw new CliError(
|
|
26
|
+
CLI_ERROR_CODE.INVALID_INPUT,
|
|
27
|
+
`Unknown subcommand: ${sub}. Try \`cimplify inspect catalogue\`.`
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
await runCatalogue(args);
|
|
31
|
+
}
|
|
32
|
+
async function runCatalogue(args) {
|
|
33
|
+
const publicKey = await resolvePublicKey(args);
|
|
34
|
+
const source = isHostedKey(publicKey) ? "hosted" : "mock";
|
|
35
|
+
const baseUrl = source === "hosted" ? HOSTED_URL : MOCK_URL;
|
|
36
|
+
const limit = parseLimit(flagString(args, FLAG_LIMIT));
|
|
37
|
+
const walkAll = flagBool(args, FLAG_ALL);
|
|
38
|
+
const cursor = flagString(args, FLAG_CURSOR);
|
|
39
|
+
const client = createCimplifyClient({
|
|
40
|
+
baseUrl,
|
|
41
|
+
publicKey,
|
|
42
|
+
suppressPublicKeyWarning: true
|
|
43
|
+
});
|
|
44
|
+
const data = await fetchCatalogue(client, { limit, walkAll, cursor });
|
|
45
|
+
const meta = buildMeta({
|
|
46
|
+
data,
|
|
47
|
+
source,
|
|
48
|
+
sampled: !walkAll,
|
|
49
|
+
sampleSize: data.products.returned,
|
|
50
|
+
gapsMethod: walkAll ? "exact" : "sample"
|
|
51
|
+
});
|
|
52
|
+
if (isJsonMode()) {
|
|
53
|
+
result({ schema_version: SCHEMA_VERSION, data, meta });
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
renderHuman(data, meta);
|
|
57
|
+
}
|
|
58
|
+
async function resolvePublicKey(args) {
|
|
59
|
+
const fromFlag = flagString(args, FLAG_PUBLIC_KEY);
|
|
60
|
+
if (fromFlag) return fromFlag;
|
|
61
|
+
const fromProc = process.env[PUBLIC_KEY_ENV]?.trim();
|
|
62
|
+
if (fromProc) return fromProc;
|
|
63
|
+
for (const name of ENV_FILE_CANDIDATES) {
|
|
64
|
+
const filePath = path.join(process.cwd(), name);
|
|
65
|
+
try {
|
|
66
|
+
const text = await promises.readFile(filePath, "utf8");
|
|
67
|
+
const entries = parseEnvFile(text);
|
|
68
|
+
const hit = entries.find((e) => e.key === PUBLIC_KEY_ENV);
|
|
69
|
+
const value = hit?.value.trim();
|
|
70
|
+
if (value) return value;
|
|
71
|
+
} catch {
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
throw new CliError(
|
|
75
|
+
CLI_ERROR_CODE.INVALID_INPUT,
|
|
76
|
+
`Set ${PUBLIC_KEY_ENV} in .env or pass --public-key. Get one at desk.cimplify.io/settings/api.`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
function isHostedKey(key) {
|
|
80
|
+
return key.startsWith("cpk_live_") || key.startsWith("cpk_test_");
|
|
81
|
+
}
|
|
82
|
+
function parseLimit(raw) {
|
|
83
|
+
if (!raw) return DEFAULT_LIMIT;
|
|
84
|
+
const n = Number.parseInt(raw, 10);
|
|
85
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
86
|
+
throw new CliError(CLI_ERROR_CODE.INVALID_INPUT, `--limit must be a positive integer.`);
|
|
87
|
+
}
|
|
88
|
+
return n;
|
|
89
|
+
}
|
|
90
|
+
async function fetchCatalogue(client, opts) {
|
|
91
|
+
const [productsPage, collectionsList, categoriesList] = await Promise.all([
|
|
92
|
+
fetchProducts(client, opts),
|
|
93
|
+
unwrap(client.catalogue.getCollections(), "list collections"),
|
|
94
|
+
unwrap(client.catalogue.getCategories(), "list categories")
|
|
95
|
+
]);
|
|
96
|
+
const collections = await Promise.all(
|
|
97
|
+
collectionsList.map(async (c) => buildCollectionSummary(client, c))
|
|
98
|
+
);
|
|
99
|
+
const collectionMembers = /* @__PURE__ */ new Set();
|
|
100
|
+
for (const c of collections) {
|
|
101
|
+
for (const pid of c.sample_product_ids) collectionMembers.add(pid);
|
|
102
|
+
}
|
|
103
|
+
const tagFreq = countTags(productsPage.items);
|
|
104
|
+
const distinctTags = tagFreq.size;
|
|
105
|
+
const topTags = [...tagFreq.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, TAG_TOP_N).map(([tag, count]) => ({ tag, count }));
|
|
106
|
+
const sample = productsPage.items.map((p) => productToSampleRow(p));
|
|
107
|
+
const gaps = computeGaps(productsPage.items, collections, categoriesList, collectionMembers);
|
|
108
|
+
return {
|
|
109
|
+
business: await fetchBusiness(client),
|
|
110
|
+
totals: {
|
|
111
|
+
products: productsPage.total ?? productsPage.items.length,
|
|
112
|
+
collections: collectionsList.length,
|
|
113
|
+
categories: categoriesList.length,
|
|
114
|
+
distinct_tags: distinctTags
|
|
115
|
+
},
|
|
116
|
+
gaps,
|
|
117
|
+
products: {
|
|
118
|
+
sample,
|
|
119
|
+
next_cursor: productsPage.nextCursor,
|
|
120
|
+
returned: productsPage.items.length
|
|
121
|
+
},
|
|
122
|
+
collections,
|
|
123
|
+
categories: { tree: buildCategoryTree(categoriesList) },
|
|
124
|
+
tags: { top: topTags }
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async function fetchProducts(client, opts) {
|
|
128
|
+
const first = await unwrap(
|
|
129
|
+
client.catalogue.getProducts({ limit: opts.limit, cursor: opts.cursor }),
|
|
130
|
+
"list products"
|
|
131
|
+
);
|
|
132
|
+
if (!opts.walkAll) {
|
|
133
|
+
return {
|
|
134
|
+
items: first.items,
|
|
135
|
+
total: first.total_available ?? null,
|
|
136
|
+
nextCursor: extractCursor(first)
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const all = [...first.items];
|
|
140
|
+
let cursor = extractCursor(first);
|
|
141
|
+
while (cursor) {
|
|
142
|
+
const page = await unwrap(
|
|
143
|
+
client.catalogue.getProducts({ limit: opts.limit, cursor }),
|
|
144
|
+
"walk products"
|
|
145
|
+
);
|
|
146
|
+
all.push(...page.items);
|
|
147
|
+
cursor = extractCursor(page);
|
|
148
|
+
}
|
|
149
|
+
return { items: all, total: first.total_available ?? all.length, nextCursor: null };
|
|
150
|
+
}
|
|
151
|
+
function extractCursor(page) {
|
|
152
|
+
return page.pagination?.next_cursor ?? null;
|
|
153
|
+
}
|
|
154
|
+
async function buildCollectionSummary(client, c) {
|
|
155
|
+
const r = await client.catalogue.getCollectionProducts(c.id);
|
|
156
|
+
const products = r.ok ? Array.isArray(r.value) ? r.value : r.value.items ?? [] : [];
|
|
157
|
+
return {
|
|
158
|
+
id: c.id,
|
|
159
|
+
name: c.name,
|
|
160
|
+
slug: c.slug ?? "",
|
|
161
|
+
product_count: products.length,
|
|
162
|
+
sample_product_ids: products.slice(0, COLLECTION_SAMPLE_SIZE).map((p) => p.id)
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
async function fetchBusiness(client) {
|
|
166
|
+
const maybe = client.business.getBusiness;
|
|
167
|
+
if (typeof maybe !== "function") {
|
|
168
|
+
return { handle: null, name: null, currencies: [], locations: 0 };
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
const r = await maybe.call(client.business);
|
|
172
|
+
if (!r?.ok || !r.value) return { handle: null, name: null, currencies: [], locations: 0 };
|
|
173
|
+
return {
|
|
174
|
+
handle: r.value.handle ?? null,
|
|
175
|
+
name: r.value.name ?? null,
|
|
176
|
+
currencies: r.value.currencies ?? [],
|
|
177
|
+
locations: r.value.locations?.length ?? 0
|
|
178
|
+
};
|
|
179
|
+
} catch {
|
|
180
|
+
return { handle: null, name: null, currencies: [], locations: 0 };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function productToSampleRow(p) {
|
|
184
|
+
return {
|
|
185
|
+
id: p.id,
|
|
186
|
+
name: p.name,
|
|
187
|
+
slug: p.slug,
|
|
188
|
+
price: priceShape(p),
|
|
189
|
+
has_image: hasImage(p),
|
|
190
|
+
has_description: !!p.description?.trim(),
|
|
191
|
+
tags: p.tags ?? [],
|
|
192
|
+
collection_ids: [],
|
|
193
|
+
category_ids: p.category_id ? [p.category_id] : [],
|
|
194
|
+
variant_count: 0
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function hasImage(p) {
|
|
198
|
+
if (p.image_url && p.image_url.trim().length > 0) return true;
|
|
199
|
+
if (p.images && p.images.length > 0) return true;
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
function hasPrice(p) {
|
|
203
|
+
const price = p.default_price;
|
|
204
|
+
if (!price) return false;
|
|
205
|
+
const amt = price.amount;
|
|
206
|
+
if (amt == null) return false;
|
|
207
|
+
const n = typeof amt === "number" ? amt : Number.parseFloat(String(amt));
|
|
208
|
+
return Number.isFinite(n) && n > 0;
|
|
209
|
+
}
|
|
210
|
+
function priceShape(p) {
|
|
211
|
+
const price = p.default_price;
|
|
212
|
+
if (!price || price.amount == null) return null;
|
|
213
|
+
return { amount: String(price.amount), currency: price.currency ?? "" };
|
|
214
|
+
}
|
|
215
|
+
function countTags(products) {
|
|
216
|
+
const m = /* @__PURE__ */ new Map();
|
|
217
|
+
for (const p of products) {
|
|
218
|
+
for (const t of p.tags ?? []) {
|
|
219
|
+
m.set(t, (m.get(t) ?? 0) + 1);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return m;
|
|
223
|
+
}
|
|
224
|
+
function computeGaps(products, collections, categories, collectionMembers) {
|
|
225
|
+
let no_image = 0;
|
|
226
|
+
let no_description = 0;
|
|
227
|
+
let no_price = 0;
|
|
228
|
+
let no_collection = 0;
|
|
229
|
+
let no_tags = 0;
|
|
230
|
+
for (const p of products) {
|
|
231
|
+
if (!hasImage(p)) no_image++;
|
|
232
|
+
if (!p.description?.trim()) no_description++;
|
|
233
|
+
if (!hasPrice(p)) no_price++;
|
|
234
|
+
if (!collectionMembers.has(p.id)) no_collection++;
|
|
235
|
+
if (!p.tags?.length) no_tags++;
|
|
236
|
+
}
|
|
237
|
+
const empty_collections = collections.filter((c) => c.product_count === 0).map((c) => ({ id: c.id, name: c.name }));
|
|
238
|
+
const productCountsByCategory = /* @__PURE__ */ new Map();
|
|
239
|
+
for (const p of products) {
|
|
240
|
+
if (p.category_id) {
|
|
241
|
+
productCountsByCategory.set(
|
|
242
|
+
p.category_id,
|
|
243
|
+
(productCountsByCategory.get(p.category_id) ?? 0) + 1
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const empty_categories = categories.filter((c) => (productCountsByCategory.get(c.id) ?? 0) === 0).map((c) => ({ id: c.id, name: c.name }));
|
|
248
|
+
return { no_image, no_description, no_price, no_collection, no_tags, empty_collections, empty_categories };
|
|
249
|
+
}
|
|
250
|
+
function buildCategoryTree(categories) {
|
|
251
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
252
|
+
for (const c of categories) {
|
|
253
|
+
const key = c.parent_id ?? null;
|
|
254
|
+
if (!byParent.has(key)) byParent.set(key, []);
|
|
255
|
+
byParent.get(key).push(c);
|
|
256
|
+
}
|
|
257
|
+
const build = (parent) => (byParent.get(parent) ?? []).map((c) => ({
|
|
258
|
+
id: c.id,
|
|
259
|
+
name: c.name,
|
|
260
|
+
slug: c.slug ?? "",
|
|
261
|
+
product_count: 0,
|
|
262
|
+
children: build(c.id)
|
|
263
|
+
}));
|
|
264
|
+
return build(null);
|
|
265
|
+
}
|
|
266
|
+
function buildMeta(input) {
|
|
267
|
+
return {
|
|
268
|
+
fetched_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
269
|
+
source: input.source,
|
|
270
|
+
sampled: input.sampled,
|
|
271
|
+
sample_size: input.sampleSize,
|
|
272
|
+
gaps_method: input.gapsMethod,
|
|
273
|
+
shape_hints: shapeHints(input.data, input.source).sort(),
|
|
274
|
+
suggestions: suggestions(input.data),
|
|
275
|
+
suggested_commands: suggestedCommands(input.sampled)
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function shapeHints(d, source) {
|
|
279
|
+
const hints = [];
|
|
280
|
+
const total = d.totals.products;
|
|
281
|
+
if (total === 0) hints.push("empty_catalogue");
|
|
282
|
+
else if (total < 10) hints.push("tiny_catalogue");
|
|
283
|
+
else if (total < 100) hints.push("small_catalogue");
|
|
284
|
+
else if (total < 1e3) hints.push("medium_catalogue");
|
|
285
|
+
else hints.push("large_catalogue");
|
|
286
|
+
if (d.collections.some((c) => c.product_count > 0)) hints.push("uses_collections");
|
|
287
|
+
if (d.categories.tree.length > 0) hints.push("uses_categories");
|
|
288
|
+
if (d.gaps.empty_collections.length > 0) hints.push("has_empty_collection");
|
|
289
|
+
if (d.gaps.empty_categories.length > 0) hints.push("has_empty_category");
|
|
290
|
+
const sampleSize = d.products.sample.length;
|
|
291
|
+
if (sampleSize > 0 && d.gaps.no_tags / sampleSize > 0.5) hints.push("untagged_majority");
|
|
292
|
+
if (sampleSize > 0 && d.gaps.no_collection / sampleSize > 0.5) hints.push("unmerchandised_majority");
|
|
293
|
+
if (d.gaps.no_image > 0) hints.push("has_image_gaps");
|
|
294
|
+
if (d.gaps.no_description > 0) hints.push("has_description_gaps");
|
|
295
|
+
if (d.gaps.no_price > 0) hints.push("has_price_gaps");
|
|
296
|
+
if (d.business.currencies.length > 1) hints.push("multi_currency");
|
|
297
|
+
if (d.business.locations > 1) hints.push("multi_location");
|
|
298
|
+
if (source === "mock") hints.push("mock_data");
|
|
299
|
+
return hints;
|
|
300
|
+
}
|
|
301
|
+
function suggestions(d) {
|
|
302
|
+
const out = [];
|
|
303
|
+
if (d.totals.products === 0) {
|
|
304
|
+
out.push({ kind: "add_first_product" });
|
|
305
|
+
return out;
|
|
306
|
+
}
|
|
307
|
+
if (d.totals.collections === 0) out.push({ kind: "add_first_collection" });
|
|
308
|
+
if (d.gaps.no_image > 0) out.push({ kind: "fix_no_image", count: d.gaps.no_image });
|
|
309
|
+
if (d.gaps.no_description > 0)
|
|
310
|
+
out.push({ kind: "fix_no_description", count: d.gaps.no_description });
|
|
311
|
+
if (d.gaps.no_price > 0) out.push({ kind: "fix_no_price", count: d.gaps.no_price });
|
|
312
|
+
if (d.gaps.no_collection > 0)
|
|
313
|
+
out.push({ kind: "fix_no_collection", count: d.gaps.no_collection });
|
|
314
|
+
if (d.gaps.no_tags > 0) out.push({ kind: "fix_no_tags", count: d.gaps.no_tags });
|
|
315
|
+
for (const c of d.gaps.empty_collections) {
|
|
316
|
+
out.push({ kind: "merchandise_empty_collection", collection_id: c.id, name: c.name });
|
|
317
|
+
}
|
|
318
|
+
for (const c of d.gaps.empty_categories) {
|
|
319
|
+
out.push({ kind: "merchandise_empty_category", category_id: c.id, name: c.name });
|
|
320
|
+
}
|
|
321
|
+
if (d.totals.products >= 1e3) {
|
|
322
|
+
out.push({ kind: "enable_search_ui", product_count: d.totals.products });
|
|
323
|
+
}
|
|
324
|
+
return out;
|
|
325
|
+
}
|
|
326
|
+
function suggestedCommands(sampled) {
|
|
327
|
+
return sampled ? ["cimplify inspect catalogue --all", "cimplify inspect catalogue --json"] : ["cimplify inspect catalogue --json"];
|
|
328
|
+
}
|
|
329
|
+
function renderHuman(d, m) {
|
|
330
|
+
info("");
|
|
331
|
+
info(
|
|
332
|
+
` ${bold(d.business.name ?? "(unnamed business)")}` + (d.business.handle ? dim(` \xB7 @${d.business.handle}`) : "") + (d.business.locations ? dim(` \xB7 ${d.business.locations} location${d.business.locations === 1 ? "" : "s"}`) : "") + (d.business.currencies.length ? dim(` \xB7 ${d.business.currencies.join(", ")}`) : "")
|
|
333
|
+
);
|
|
334
|
+
info("");
|
|
335
|
+
info(` Products ${pad(d.totals.products, 5)} ${gapBadges(d.gaps)}`);
|
|
336
|
+
info(` Collections ${pad(d.totals.collections, 5)} ${emptyBadge(d.gaps.empty_collections, "empty")}`);
|
|
337
|
+
info(` Categories ${pad(d.totals.categories, 5)} ${emptyBadge(d.gaps.empty_categories, "empty")}`);
|
|
338
|
+
info(` Tags ${pad(d.totals.distinct_tags, 5)} ${tagBadge(d.gaps.no_tags)}`);
|
|
339
|
+
info("");
|
|
340
|
+
if (d.tags.top.length > 0) {
|
|
341
|
+
info(` ${dim("Top tags")} ${d.tags.top.map((t) => `${t.tag} (${t.count})`).join(" \xB7 ")}`);
|
|
342
|
+
info("");
|
|
343
|
+
}
|
|
344
|
+
if (d.collections.length > 0) {
|
|
345
|
+
info(` ${dim("Collections")}`);
|
|
346
|
+
for (const c of d.collections.slice(0, 5)) {
|
|
347
|
+
const tag = c.product_count === 0 ? ` ${yellow("\u26A0 empty")}` : "";
|
|
348
|
+
info(` ${green("\u25B8")} ${c.name.padEnd(20)} ${dim(`${c.product_count} items`)}${tag}`);
|
|
349
|
+
}
|
|
350
|
+
if (d.collections.length > 5) info(` ${dim(`\u2026${d.collections.length - 5} more`)}`);
|
|
351
|
+
info("");
|
|
352
|
+
}
|
|
353
|
+
if (d.products.sample.length > 0) {
|
|
354
|
+
info(` ${dim(`Sample products (first ${d.products.sample.length} of ${d.totals.products})`)}`);
|
|
355
|
+
for (const p of d.products.sample.slice(0, 10)) {
|
|
356
|
+
const id = p.id.slice(0, 8).padEnd(10);
|
|
357
|
+
const name = truncate(p.name, 24).padEnd(24);
|
|
358
|
+
const price = p.price ? `${p.price.currency} ${p.price.amount}` : dim("\u2014");
|
|
359
|
+
const img = p.has_image ? green("img \u2713") : red("img \u2717");
|
|
360
|
+
const tags = p.tags.length ? p.tags.slice(0, 2).join(", ") : dim("\u2014");
|
|
361
|
+
info(` ${dim(id)}${name} ${price.padEnd(12)} ${img} ${tags}`);
|
|
362
|
+
}
|
|
363
|
+
info("");
|
|
364
|
+
}
|
|
365
|
+
if (m.sampled) {
|
|
366
|
+
info(` ${dim(`Gaps computed from first ${m.sample_size} products. Add --all for exact counts.`)}`);
|
|
367
|
+
} else {
|
|
368
|
+
info(` ${dim("Gaps are exact (walked the full catalogue).")}`);
|
|
369
|
+
}
|
|
370
|
+
info("");
|
|
371
|
+
if (m.suggested_commands.length > 0) {
|
|
372
|
+
info(` ${dim("Try")}`);
|
|
373
|
+
for (const c of m.suggested_commands) info(` ${c}`);
|
|
374
|
+
info("");
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function gapBadges(g, _kind) {
|
|
378
|
+
const bits = [];
|
|
379
|
+
if (g.no_image > 0) bits.push(`${g.no_image} no image`);
|
|
380
|
+
if (g.no_description > 0) bits.push(`${g.no_description} no description`);
|
|
381
|
+
if (g.no_collection > 0) bits.push(`${g.no_collection} not in any collection`);
|
|
382
|
+
if (bits.length === 0) return dim("no gaps");
|
|
383
|
+
return yellow(`\u26A0 ${bits.join(" \xB7 ")}`);
|
|
384
|
+
}
|
|
385
|
+
function emptyBadge(items, label) {
|
|
386
|
+
if (items.length === 0) return dim("no gaps");
|
|
387
|
+
const names = items.slice(0, 3).map((i) => i.name).join(", ");
|
|
388
|
+
const more = items.length > 3 ? `, \u2026${items.length - 3} more` : "";
|
|
389
|
+
return yellow(`\u26A0 ${items.length} ${label} (${names}${more})`);
|
|
390
|
+
}
|
|
391
|
+
function tagBadge(untagged) {
|
|
392
|
+
if (untagged === 0) return dim("all tagged");
|
|
393
|
+
return yellow(`\u26A0 ${untagged} products untagged`);
|
|
394
|
+
}
|
|
395
|
+
function pad(n, width) {
|
|
396
|
+
return String(n).padStart(width);
|
|
397
|
+
}
|
|
398
|
+
function truncate(s, width) {
|
|
399
|
+
if (s.length <= width) return s;
|
|
400
|
+
return s.slice(0, width - 1) + "\u2026";
|
|
401
|
+
}
|
|
402
|
+
async function unwrap(p, label) {
|
|
403
|
+
const r = await p;
|
|
404
|
+
if (!r.ok || r.value == null) {
|
|
405
|
+
const msg = r.error?.message ?? `failed to ${label}`;
|
|
406
|
+
throw new CliError(CLI_ERROR_CODE.NETWORK_ERROR, msg);
|
|
407
|
+
}
|
|
408
|
+
return r.value;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export { computeGaps, run as default, isHostedKey, parseLimit, shapeHints, suggestions };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
export { run as default, extractMockSeed, gatherIntrospection, renderIntrospection } from './chunk-
|
|
2
|
+
export { run as default, extractMockSeed, gatherIntrospection, renderIntrospection } from './chunk-CNIFXKZV.mjs';
|
|
3
3
|
import './chunk-K5464A3L.mjs';
|
|
4
4
|
import './chunk-DBZ3UOQ2.mjs';
|
|
5
|
-
import './chunk-
|
|
5
|
+
import './chunk-RGF2RSSR.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-
|
|
2
|
+
import { REGISTRY_INDEX } from './chunk-RIOQDUQO.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-
|
|
2
|
+
import { package_default } from './chunk-RGF2RSSR.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.6.
|
|
3
|
+
"version": "0.6.14",
|
|
4
4
|
"description": "Cimplify CLI — deploy, manage env vars, link projects, and scaffold storefronts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cimplify",
|
|
@@ -43,5 +43,8 @@
|
|
|
43
43
|
"tsup": "^8.5.1",
|
|
44
44
|
"typescript": "5.9.3",
|
|
45
45
|
"vitest": "^4.1.5"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@cimplify/sdk": "^0.49.0"
|
|
46
49
|
}
|
|
47
50
|
}
|