@koda-sl/baker-cli 0.370.0-dev.1981aa822 → 0.370.0-dev.886bf7ec3
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/README.md +1 -1
- package/dist/cli.js +122 -8
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3704,7 +3704,7 @@ baker shopify stock --limit 20 # every product with every op
|
|
|
3704
3704
|
**`meta.store`** is on every answer: `connection` (`connected` · `needs_reconnect` · `disconnected`), `catalog` (`starting` · `importing` · `ready` · `failed`, with `importProgress` while importing), `unreadable` (products Baker couldn't read; `unreadableCapped` past 100), `checksFailing` (Baker couldn't check the store for changes lately), `storefront` (`online_store`, or `own` for a store that sells through its own site) and `lastActivity` (when Baker last heard from the store — not a promise the copy is current). `hints[]` turn each of these into what to tell the user.
|
|
3705
3705
|
**`store`:** `--full` adds the admin link and every country. `capped: true` means a count stopped at its ceiling — `outOfStock` and `partlyOut` at 100, `collections` at 500 — so that count reads "that many or more"; a hint says which.
|
|
3706
3706
|
**`products`:** `--search <words>`, `--collection <handle|id>`, `--stock`, `--limit <1-100>`, `--cursor`; `--full` adds vendor, type, option count, photo link, visibility and status. Every price, here and in `product` (`price`, `compareAt`, each option's price, `pricesByCountry`), is one string with its currency beside each amount: `"USD 785.95"`, `"USD 9.95 – USD 49.95"`. A row's `availability` is absent when the product is on the store, `link_only` when it is buyable only through its link, `not_for_sale` (with `status`) when it can't be bought on the online store.
|
|
3707
|
-
**`product`:** positional `<ref>` — the numeric id, the handle, a store product link (`…/products/<handle>`, even an all-digit handle), a Baker Products link, a Shopify admin product link (`admin.shopify.com/store/<shop>/products/<n>`), or `gid://shopify/Product/<n>`. Returns options (50 per page, `--variants-cursor` for more; 250 with `--full`), `outOptions` (up to 50, `outOptionsMore` for the rest), `pricesByCountry` when the store sells at more than one price, `photos[]` (`imageId` + a Baker-hosted `url`, never a Shopify link), `url` (the product's page in Baker) and `storeUrl
|
|
3707
|
+
**`product`:** positional `<ref>` — the numeric id, the handle, a store product link (`…/products/<handle>`, even an all-digit handle), a Baker Products link, a Shopify admin product link (`admin.shopify.com/store/<shop>/products/<n>`), or `gid://shopify/Product/<n>`. Returns options (50 per page, `--variants-cursor` for more; 250 with `--full`), `outOptions` (up to 50, `outOptionsMore` for the rest), `pricesByCountry` when the store sells at more than one price, `photos[]` (`imageId` + a Baker-hosted `url`, never a Shopify link), `url` (the product's page in Baker) and `storeUrl` (the product's page on the store — never a buy button's link). `--full` adds the whole description, SEO, category, the admin link and each option's SKU, barcode, feed id and prices by country. `NOT_FOUND` carries a hint to search instead.
|
|
3708
3708
|
|
|
3709
3709
|
**Details and buy links (`product`):** every option carries `buyUrl` — the link for a page's buy button, following the store's own setting (`buyDestination`: `checkout`, `store_cart` or `product_page`) — with `buyUrlKind`, `unavailable` when it can't be bought, and `notes` for what a link can't do (`password_protected`, `subscription_only`, `subscription_optional`, `own_storefront`, `details_pending`). `--quantity <1-99>` sets how many it adds. `videos[]` lists the product's videos in the library (`videoId`, `playbackId`); `contentSummary` names what else Baker keeps. `--content` adds `content`: the formatted description, every custom field by its name (long values previewed, `truncated: true`), the rating, subscriptions, bundle, YouTube/Vimeo and 3D links, translations, and `unreadable` for content the store owner hasn't let Baker read yet. `--field <name|namespace.key>` returns one field whole. `--full` adds `--content`, all three links per option and each option's weight, unit price, shipping and tax. `store` adds `buyDestination`, `passwordProtected`, `languages`, `policies` (`--full`: their text, and the store's brand).
|
|
3710
3710
|
**`collections`:** `--search <words>`; each row has `handle`, Shopify's product count and, on a store with an online store, `onStore`.
|
package/dist/cli.js
CHANGED
|
@@ -5897,7 +5897,11 @@ var REJECTION_CODES = [
|
|
|
5897
5897
|
"missing_identity",
|
|
5898
5898
|
"invalid_timestamp",
|
|
5899
5899
|
"invalid_value",
|
|
5900
|
-
"batch_too_large"
|
|
5900
|
+
"batch_too_large",
|
|
5901
|
+
// Appended, never inserted: the set a client branches on grows, and no code
|
|
5902
|
+
// it already handles changes meaning.
|
|
5903
|
+
"invalid_items",
|
|
5904
|
+
"invalid_consent"
|
|
5901
5905
|
];
|
|
5902
5906
|
var EVENT_INGEST_BATCH_MAX = 500;
|
|
5903
5907
|
var EVENT_INGEST_BODY_MAX_BYTES = 8 * 1024 * 1024;
|
|
@@ -5910,12 +5914,15 @@ function validateEventName(name) {
|
|
|
5910
5914
|
if (value === "" || value.length > MAX_EVENT_NAME || !EVENT_NAME.test(value)) return "invalid_name";
|
|
5911
5915
|
return null;
|
|
5912
5916
|
}
|
|
5917
|
+
var MAX_ITEM_QUANTITY = 1e6;
|
|
5913
5918
|
var REJECTION_MESSAGES = {
|
|
5914
5919
|
invalid_event_id: "event_id, when sent, must be 1-128 characters of A-Z a-z 0-9 . _ : -",
|
|
5915
5920
|
invalid_name: "event_name is required: up to 80 characters of letters (any language), digits, spaces, . _ and -, starting with a letter or a digit",
|
|
5916
5921
|
missing_identity: "user_data named nobody, and no url named a page",
|
|
5917
5922
|
invalid_timestamp: "event_time must be an ISO-8601 instant with an offset or Unix seconds, and not in the future",
|
|
5918
5923
|
invalid_value: "value and currency go together: a non-negative decimal and an ISO-4217 code",
|
|
5924
|
+
invalid_items: "each item names a product_id, variant_id or feed_id; quantity is a whole number from 1 to 1000000; price is a non-negative decimal sent with a currency",
|
|
5925
|
+
invalid_consent: "consent.analytics and consent.marketing, when sent, are granted or denied",
|
|
5919
5926
|
batch_too_large: `a request carries at most ${EVENT_INGEST_BATCH_MAX} events and ${EVENT_INGEST_BODY_MAX_BYTES / (1024 * 1024)} MB`
|
|
5920
5927
|
};
|
|
5921
5928
|
|
|
@@ -7294,7 +7301,8 @@ var TRIGGER_KINDS = {
|
|
|
7294
7301
|
"utm_source",
|
|
7295
7302
|
"utm_medium",
|
|
7296
7303
|
"utm_campaign",
|
|
7297
|
-
"properties.<name>"
|
|
7304
|
+
"properties.<name>",
|
|
7305
|
+
"items"
|
|
7298
7306
|
],
|
|
7299
7307
|
keywords: ["purchase", "signup", "conversion", "analytics", "tracking", "hubspot", "crm", "deal", "funnel", "lead"]
|
|
7300
7308
|
},
|
|
@@ -14906,8 +14914,22 @@ var ANALYTICS_EVENT_TYPES = [
|
|
|
14906
14914
|
* page, `ingest` for a third party's server — and every report that needs to
|
|
14907
14915
|
* tell them apart filters on that.
|
|
14908
14916
|
*/
|
|
14909
|
-
"custom"
|
|
14917
|
+
"custom",
|
|
14918
|
+
/**
|
|
14919
|
+
* A fact about the business that names no person — a product changed, a
|
|
14920
|
+
* variant went out of stock.
|
|
14921
|
+
*
|
|
14922
|
+
* Only a commerce door writes it (`ingestShopifyFacts`), and it is a type of
|
|
14923
|
+
* its own rather than a `custom` event with no identity for one reason:
|
|
14924
|
+
* every report that lists named events whitelists `custom`, so an
|
|
14925
|
+
* operational fact would otherwise appear in the conversion picker, the
|
|
14926
|
+
* funnel and the sends. Here it reaches the raw feed and the agents that
|
|
14927
|
+
* listen for its name, and no person-level report. The row builder blanks
|
|
14928
|
+
* every identity column on it, whatever it carried.
|
|
14929
|
+
*/
|
|
14930
|
+
"operational"
|
|
14910
14931
|
];
|
|
14932
|
+
var EVENT_ITEMS_MAX = 250;
|
|
14911
14933
|
var ANALYTICS_DEVICE_TYPES = ["desktop", "mobile", "tablet", "bot", "unknown"];
|
|
14912
14934
|
var ANALYTICS_BEACON_BATCH_MAX = 50;
|
|
14913
14935
|
|
|
@@ -30920,6 +30942,48 @@ var EVENT_FIELDS = [
|
|
|
30920
30942
|
name: "properties",
|
|
30921
30943
|
need: "optional",
|
|
30922
30944
|
takes: "A flat object of strings \u2014 anything you want to break the event down by later."
|
|
30945
|
+
},
|
|
30946
|
+
{
|
|
30947
|
+
name: "items",
|
|
30948
|
+
need: "optional",
|
|
30949
|
+
takes: "What was bought, one entry per product line \u2014 for a purchase, a cart or a refund.",
|
|
30950
|
+
note: `Each line names its product by at least one of the ids below. value still carries the whole event's money. An order with more than ${EVENT_ITEMS_MAX} lines keeps the first ${EVENT_ITEMS_MAX}.`
|
|
30951
|
+
},
|
|
30952
|
+
{
|
|
30953
|
+
name: "product_id, variant_id, feed_id",
|
|
30954
|
+
need: "conditional",
|
|
30955
|
+
under: true,
|
|
30956
|
+
takes: "Your store's product id, its variant id, or the id the product has in your ads catalog. At least one per line, up to 255 characters each."
|
|
30957
|
+
},
|
|
30958
|
+
{
|
|
30959
|
+
name: "quantity",
|
|
30960
|
+
need: "optional",
|
|
30961
|
+
under: true,
|
|
30962
|
+
takes: `How many: a whole number from 1 to ${MAX_ITEM_QUANTITY.toLocaleString("en-US")}.`
|
|
30963
|
+
},
|
|
30964
|
+
{
|
|
30965
|
+
name: "price",
|
|
30966
|
+
need: "optional",
|
|
30967
|
+
under: true,
|
|
30968
|
+
takes: "The price of one unit, in the event's currency \u2014 so it needs currency. A decimal like value."
|
|
30969
|
+
},
|
|
30970
|
+
{
|
|
30971
|
+
name: "consent",
|
|
30972
|
+
need: "optional",
|
|
30973
|
+
takes: "What the person allowed, if your site asked them.",
|
|
30974
|
+
note: "Send it whenever you know it. A person who said no to marketing is still counted in your reports, and is never matched or sent to an ad platform."
|
|
30975
|
+
},
|
|
30976
|
+
{
|
|
30977
|
+
name: "analytics",
|
|
30978
|
+
need: "optional",
|
|
30979
|
+
under: true,
|
|
30980
|
+
takes: "granted or denied \u2014 whether they allowed being measured. Denied keeps the event and forgets who it was."
|
|
30981
|
+
},
|
|
30982
|
+
{
|
|
30983
|
+
name: "marketing",
|
|
30984
|
+
need: "optional",
|
|
30985
|
+
under: true,
|
|
30986
|
+
takes: "granted or denied \u2014 whether they allowed their visit to be used for advertising."
|
|
30923
30987
|
}
|
|
30924
30988
|
];
|
|
30925
30989
|
var RESPONSE_FIELDS = [
|
|
@@ -31249,7 +31313,37 @@ var ingestEventSchema = z39.strictObject({
|
|
|
31249
31313
|
* left alone: masking one would rename the field and could collide with a
|
|
31250
31314
|
* sibling.
|
|
31251
31315
|
*/
|
|
31252
|
-
properties: z39.record(z39.string(), z39.string()).nullish()
|
|
31316
|
+
properties: z39.record(z39.string(), z39.string()).nullish(),
|
|
31317
|
+
/**
|
|
31318
|
+
* What was bought, one entry per line — for a purchase, a cart, a refund.
|
|
31319
|
+
*
|
|
31320
|
+
* Flat and Meta-like for the same reason as the rest of this body: it is
|
|
31321
|
+
* Meta's `contents` (`id`, `quantity`, `item_price`) and GA4's `items`
|
|
31322
|
+
* (`item_id`, `quantity`, `price`) under one spelling a sender can map from
|
|
31323
|
+
* either. A line names what it is by any of three ids — the store's product,
|
|
31324
|
+
* its variant, or the id the product has in an ads catalog. `price` is the
|
|
31325
|
+
* *unit* price in the event's `currency`; `value` stays the whole event's
|
|
31326
|
+
* money. Structure only here; `validateItems` judges each line.
|
|
31327
|
+
*/
|
|
31328
|
+
items: z39.array(
|
|
31329
|
+
z39.strictObject({
|
|
31330
|
+
product_id: z39.string().nullish(),
|
|
31331
|
+
variant_id: z39.string().nullish(),
|
|
31332
|
+
feed_id: z39.string().nullish(),
|
|
31333
|
+
quantity: z39.union([z39.string(), z39.number()]).nullish(),
|
|
31334
|
+
price: z39.union([z39.string(), z39.number()]).nullish()
|
|
31335
|
+
})
|
|
31336
|
+
).nullish(),
|
|
31337
|
+
/**
|
|
31338
|
+
* What the person allowed, each `granted` or `denied`: `analytics` (being
|
|
31339
|
+
* measured at all) and `marketing` (being matched and sent to an ad
|
|
31340
|
+
* platform). Absent means the sender did not say. Enforced before anything
|
|
31341
|
+
* is stored — see `./consent`. `validateConsent` judges the values.
|
|
31342
|
+
*/
|
|
31343
|
+
consent: z39.strictObject({
|
|
31344
|
+
analytics: z39.string().nullish(),
|
|
31345
|
+
marketing: z39.string().nullish()
|
|
31346
|
+
}).nullish()
|
|
31253
31347
|
});
|
|
31254
31348
|
var ingestBatchSchema = z39.strictObject({
|
|
31255
31349
|
events: z39.array(ingestEventSchema).min(1).max(EVENT_INGEST_BATCH_MAX)
|
|
@@ -64584,6 +64678,8 @@ var shopifyProductRequestSchema = z50.object({
|
|
|
64584
64678
|
content: flag.optional(),
|
|
64585
64679
|
/** One custom field whole, by its name or `namespace.key`. */
|
|
64586
64680
|
field: z50.string().min(1).max(200).optional(),
|
|
64681
|
+
/** With `field`: the option (its `variantId`) whose field to return, when options share one. */
|
|
64682
|
+
variant: z50.string().regex(/^\d{1,30}$/, "an option's variantId").optional(),
|
|
64587
64683
|
/** How many of the option each buy link puts in the cart (1-99, default 1). */
|
|
64588
64684
|
quantity: z50.coerce.number().int().min(1).max(SHOPIFY_MAX_BUY_QUANTITY).optional()
|
|
64589
64685
|
});
|
|
@@ -64727,6 +64823,8 @@ var shopifyFieldRefSchema = z50.object({
|
|
|
64727
64823
|
url: z50.string().optional(),
|
|
64728
64824
|
mimeType: z50.string().optional(),
|
|
64729
64825
|
alt: z50.string().optional(),
|
|
64826
|
+
/** A video's preview image; its `url` is the video file itself. */
|
|
64827
|
+
previewUrl: z50.string().optional(),
|
|
64730
64828
|
/** A product's or a collection's title, from Baker's copy. */
|
|
64731
64829
|
title: z50.string().optional(),
|
|
64732
64830
|
/** An image brought into the library: its imageId, to place like the product's photos. */
|
|
@@ -64806,6 +64904,7 @@ var shopifyProductDetailSchema = z50.object({
|
|
|
64806
64904
|
handle: z50.string(),
|
|
64807
64905
|
/** The product's page in Baker — the link to share with the user. */
|
|
64808
64906
|
url: z50.string(),
|
|
64907
|
+
/** The product's page on the online store: to look at, never a buy button's link (that is each option's `buyUrl`). */
|
|
64809
64908
|
storeUrl: z50.string().optional(),
|
|
64810
64909
|
availability: shopifyAvailabilitySchema.optional(),
|
|
64811
64910
|
status: shopifyProductStatusSchema.optional(),
|
|
@@ -65023,7 +65122,8 @@ function buyHints(product) {
|
|
|
65023
65122
|
const hints2 = [];
|
|
65024
65123
|
if (product.variants.some((variant) => variant.buyUrl)) {
|
|
65025
65124
|
hints2.push(
|
|
65026
|
-
`Use each option's \`buyUrl\` for the page's buy button \u2014 never build a Shopify link yourself (it follows the store's setting: ${product.buyDestination.replace("_", " ")}; \`--quantity <n>\` sets how many)
|
|
65125
|
+
`Use each option's \`buyUrl\` for the page's buy button \u2014 never build a Shopify link yourself (it follows the store's setting: ${product.buyDestination.replace("_", " ")}; \`--quantity <n>\` sets how many).`,
|
|
65126
|
+
"Keep that `buyUrl` on the button: never `storeUrl` (a page to look at, not a buy link), and never drop or replace the buy button to get a page past its checks. The options to sell are `variants[]` alone \u2014 one buy button per option, each with its own `buyUrl`, never a size or colour picker made up from a size chart or other details. When the shopper must choose on the store, link the option's `productPage` (from `--full`)."
|
|
65027
65127
|
);
|
|
65028
65128
|
}
|
|
65029
65129
|
const notes = new Set(product.variants.flatMap((variant) => variant.notes ?? []));
|
|
@@ -65051,7 +65151,8 @@ function contentHints(product) {
|
|
|
65051
65151
|
}
|
|
65052
65152
|
const truncated = product.content?.fields.find((field) => field.truncated);
|
|
65053
65153
|
if (truncated) {
|
|
65054
|
-
|
|
65154
|
+
const option = truncated.variantId ? ` --variant ${truncated.variantId}` : "";
|
|
65155
|
+
hints2.push(`Some fields are shown in part: read one whole with \`--field ${truncated.key}${option}\`.`);
|
|
65055
65156
|
}
|
|
65056
65157
|
if (product.content?.unreadable?.length || product.content?.fields.some((field) => field.unreadable)) {
|
|
65057
65158
|
hints2.push(
|
|
@@ -65229,7 +65330,7 @@ registerSchema({
|
|
|
65229
65330
|
});
|
|
65230
65331
|
registerSchema({
|
|
65231
65332
|
command: "shopify.product",
|
|
65232
|
-
description: "One product of the connected store \u2014 accepts its id, its handle, a link to it on the store, in Baker or in Shopify admin. Returns its options with price, stock and `buyUrl` (the link for a page's buy button \u2014 never build a Shopify link yourself), prices by country, the options out of stock, its collections, its photos' library imageIds to place on a landing (`baker images download <imageIds> --out src/pages/<page>/_images/`) or pass to a creative, its videos' playbackIds (`<Video playbackId=\u2026/>`), and `contentSummary`: what else Baker keeps about it. Start here for a landing from a product: add `--content` for the formatted description, every custom field by its name (a size chart, care instructions), the rating, subscriptions and translations; `--field <name>` returns one field whole. `--out-of-stock` lists only the options out of stock.",
|
|
65333
|
+
description: "One product of the connected store \u2014 accepts its id, its handle, a link to it on the store, in Baker or in Shopify admin. Returns its options with price, stock and `buyUrl` (the link for a page's buy button, one per option \u2014 never build a Shopify link yourself, never use `storeUrl` for it), prices by country, the options out of stock, its collections, its photos' library imageIds to place on a landing (`baker images download <imageIds> --out src/pages/<page>/_images/`) or pass to a creative, its videos' playbackIds (`<Video playbackId=\u2026/>`), and `contentSummary`: what else Baker keeps about it. Start here for a landing from a product: add `--content` for the formatted description, every custom field by its name (a size chart, care instructions), the rating, subscriptions and translations; `--field <name>` returns one field whole. `--out-of-stock` lists only the options out of stock.",
|
|
65233
65334
|
args: {
|
|
65234
65335
|
ref: { type: "positional", description: "Product id, handle, or a link to the product", required: true },
|
|
65235
65336
|
"variants-cursor": {
|
|
@@ -65254,6 +65355,11 @@ registerSchema({
|
|
|
65254
65355
|
description: 'One custom field whole, by its name or its key (e.g. "Size chart" or custom.size_chart)',
|
|
65255
65356
|
required: false
|
|
65256
65357
|
},
|
|
65358
|
+
variant: {
|
|
65359
|
+
type: "string",
|
|
65360
|
+
description: "With --field: the option whose field to return, by its variantId (options share their fields, e.g. --field custom.fit --variant 102)",
|
|
65361
|
+
required: false
|
|
65362
|
+
},
|
|
65257
65363
|
quantity: {
|
|
65258
65364
|
type: "number",
|
|
65259
65365
|
description: "How many of the option each buy link puts in the cart, 1-99 (default 1)",
|
|
@@ -65365,6 +65471,11 @@ var productCommand = defineCommand209({
|
|
|
65365
65471
|
default: false
|
|
65366
65472
|
},
|
|
65367
65473
|
field: { type: "string", description: "One custom field whole, by its name or key", required: false },
|
|
65474
|
+
variant: {
|
|
65475
|
+
type: "string",
|
|
65476
|
+
description: "With --field: the option whose field it is, by its variantId",
|
|
65477
|
+
required: false
|
|
65478
|
+
},
|
|
65368
65479
|
quantity: {
|
|
65369
65480
|
type: "string",
|
|
65370
65481
|
description: "How many each buy link puts in the cart, 1-99 (default 1)",
|
|
@@ -65389,6 +65500,7 @@ var productCommand = defineCommand209({
|
|
|
65389
65500
|
outOfStock: args5["out-of-stock"],
|
|
65390
65501
|
content: args5.content,
|
|
65391
65502
|
field: args5.field,
|
|
65503
|
+
variant: args5.variant,
|
|
65392
65504
|
quantity: args5.quantity,
|
|
65393
65505
|
full: args5.full
|
|
65394
65506
|
})
|
|
@@ -65464,6 +65576,7 @@ Examples:
|
|
|
65464
65576
|
baker shopify product the-complete-snowboard # one product: options with buyUrl, prices, photos, videos
|
|
65465
65577
|
baker shopify product <handle> --content # its whole details: description, custom fields, subscriptions
|
|
65466
65578
|
baker shopify product <handle> --field "Size chart" # one custom field, whole
|
|
65579
|
+
baker shopify product <handle> --field custom.fit --variant <variantId> # one option's field, whole
|
|
65467
65580
|
baker shopify product <handle> --quantity 2 # buy links that put 2 in the cart
|
|
65468
65581
|
baker shopify product <id> --out-of-stock # only its options that are out of stock
|
|
65469
65582
|
baker shopify stock # every product with options out of stock
|
|
@@ -65472,7 +65585,8 @@ Examples:
|
|
|
65472
65585
|
Product photos are already in the image library: place them by imageId
|
|
65473
65586
|
(baker images download <imageIds> --out src/pages/<page>/_images/), never by a cdn.shopify.com link.
|
|
65474
65587
|
Product videos are in the video library: place them with <Video playbackId="\u2026" />.
|
|
65475
|
-
A page's buy button uses the option's buyUrl \u2014 never build a Shopify link
|
|
65588
|
+
A page's buy button uses the option's buyUrl, one button per option \u2014 never build a Shopify link
|
|
65589
|
+
yourself, and never use storeUrl (a page to look at) for it.
|
|
65476
65590
|
NO_SHOPIFY_CONNECTION means no store is connected: follow error.fix. SHOPIFY_NOT_AVAILABLE means it cannot be
|
|
65477
65591
|
connected here yet: carry on without it.
|
|
65478
65592
|
Full guide: __tooling__/docs/tools/baker/shopify.md`
|