ecomcoder-cli 1.3.11 → 1.3.13
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 +16 -0
- package/dist/commands/asset/delete.d.ts.map +1 -1
- package/dist/commands/asset/delete.js +14 -0
- package/dist/commands/asset/delete.js.map +1 -1
- package/dist/commands/asset/import.d.ts.map +1 -1
- package/dist/commands/asset/import.js +14 -0
- package/dist/commands/asset/import.js.map +1 -1
- package/dist/commands/asset/upload.d.ts.map +1 -1
- package/dist/commands/asset/upload.js +14 -0
- package/dist/commands/asset/upload.js.map +1 -1
- package/dist/commands/create-dummy-products.d.ts.map +1 -1
- package/dist/commands/create-dummy-products.js +14 -0
- package/dist/commands/create-dummy-products.js.map +1 -1
- package/dist/commands/create-metafield.d.ts.map +1 -1
- package/dist/commands/create-metafield.js +14 -0
- package/dist/commands/create-metafield.js.map +1 -1
- package/dist/commands/discount/create-basic.d.ts.map +1 -1
- package/dist/commands/discount/create-basic.js +14 -0
- package/dist/commands/discount/create-basic.js.map +1 -1
- package/dist/commands/discount/create-bxgy.d.ts.map +1 -1
- package/dist/commands/discount/create-bxgy.js +14 -0
- package/dist/commands/discount/create-bxgy.js.map +1 -1
- package/dist/commands/discount/create-shipping.d.ts.map +1 -1
- package/dist/commands/discount/create-shipping.js +14 -0
- package/dist/commands/discount/create-shipping.js.map +1 -1
- package/dist/commands/discount/delete.d.ts.map +1 -1
- package/dist/commands/discount/delete.js +14 -0
- package/dist/commands/discount/delete.js.map +1 -1
- package/dist/commands/product/add-options.d.ts.map +1 -1
- package/dist/commands/product/add-options.js +14 -0
- package/dist/commands/product/add-options.js.map +1 -1
- package/dist/commands/product/add-tags.d.ts +9 -0
- package/dist/commands/product/add-tags.d.ts.map +1 -0
- package/dist/commands/product/add-tags.js +93 -0
- package/dist/commands/product/add-tags.js.map +1 -0
- package/dist/commands/product/create-variants.d.ts.map +1 -1
- package/dist/commands/product/create-variants.js +14 -0
- package/dist/commands/product/create-variants.js.map +1 -1
- package/dist/commands/product/create.d.ts.map +1 -1
- package/dist/commands/product/create.js +92 -4
- package/dist/commands/product/create.js.map +1 -1
- package/dist/commands/product/delete-variants.d.ts.map +1 -1
- package/dist/commands/product/delete-variants.js +14 -0
- package/dist/commands/product/delete-variants.js.map +1 -1
- package/dist/commands/product/index.d.ts.map +1 -1
- package/dist/commands/product/index.js +32 -0
- package/dist/commands/product/index.js.map +1 -1
- package/dist/commands/product/queries.d.ts +23 -0
- package/dist/commands/product/queries.d.ts.map +1 -1
- package/dist/commands/product/queries.js +80 -0
- package/dist/commands/product/queries.js.map +1 -1
- package/dist/commands/product/remove-tags.d.ts +9 -0
- package/dist/commands/product/remove-tags.d.ts.map +1 -0
- package/dist/commands/product/remove-tags.js +108 -0
- package/dist/commands/product/remove-tags.js.map +1 -0
- package/dist/commands/product/service.d.ts +81 -2
- package/dist/commands/product/service.d.ts.map +1 -1
- package/dist/commands/product/service.js +291 -13
- package/dist/commands/product/service.js.map +1 -1
- package/dist/commands/product/set-metafield.d.ts.map +1 -1
- package/dist/commands/product/set-metafield.js +14 -0
- package/dist/commands/product/set-metafield.js.map +1 -1
- package/dist/commands/product/set-tags.d.ts +9 -0
- package/dist/commands/product/set-tags.d.ts.map +1 -0
- package/dist/commands/product/set-tags.js +107 -0
- package/dist/commands/product/set-tags.js.map +1 -0
- package/dist/commands/product/types.d.ts +58 -0
- package/dist/commands/product/types.d.ts.map +1 -1
- package/dist/commands/product/update-description.d.ts.map +1 -1
- package/dist/commands/product/update-description.js +14 -0
- package/dist/commands/product/update-description.js.map +1 -1
- package/dist/commands/product/update-images.d.ts +8 -0
- package/dist/commands/product/update-images.d.ts.map +1 -0
- package/dist/commands/product/update-images.js +191 -0
- package/dist/commands/product/update-images.js.map +1 -0
- package/dist/commands/product/update-options.d.ts.map +1 -1
- package/dist/commands/product/update-options.js +14 -0
- package/dist/commands/product/update-options.js.map +1 -1
- package/dist/commands/product/update-price.d.ts.map +1 -1
- package/dist/commands/product/update-price.js +14 -0
- package/dist/commands/product/update-price.js.map +1 -1
- package/dist/commands/product/update-status.d.ts.map +1 -1
- package/dist/commands/product/update-status.js +14 -0
- package/dist/commands/product/update-status.js.map +1 -1
- package/dist/commands/product/update-template.d.ts.map +1 -1
- package/dist/commands/product/update-template.js +14 -0
- package/dist/commands/product/update-template.js.map +1 -1
- package/dist/commands/product/update-title.d.ts.map +1 -1
- package/dist/commands/product/update-title.js +14 -0
- package/dist/commands/product/update-title.js.map +1 -1
- package/dist/commands/set-product-rating.d.ts.map +1 -1
- package/dist/commands/set-product-rating.js +14 -0
- package/dist/commands/set-product-rating.js.map +1 -1
- package/dist/commands/shop/set-metafield.d.ts.map +1 -1
- package/dist/commands/shop/set-metafield.js +14 -0
- package/dist/commands/shop/set-metafield.js.map +1 -1
- package/dist/lib/approval.d.ts +28 -0
- package/dist/lib/approval.d.ts.map +1 -0
- package/dist/lib/approval.js +38 -0
- package/dist/lib/approval.js.map +1 -0
- package/dist/lib/image-library.d.ts +65 -0
- package/dist/lib/image-library.d.ts.map +1 -0
- package/dist/lib/image-library.js +143 -0
- package/dist/lib/image-library.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove Product Tags Command
|
|
3
|
+
*
|
|
4
|
+
* Following Command Pattern - thin orchestration layer
|
|
5
|
+
* Delegates business logic to ProductService
|
|
6
|
+
* DESTRUCTIVE - Requires approval flow
|
|
7
|
+
*/
|
|
8
|
+
import { getCredentials, getSessionId } from '../../lib/api-client.js';
|
|
9
|
+
import { parseArgs, hasHelpFlag } from '../../lib/args-parser.js';
|
|
10
|
+
import { ProductService } from './service.js';
|
|
11
|
+
import { requestApproval } from '../../lib/approval.js';
|
|
12
|
+
function showHelp() {
|
|
13
|
+
console.log(`
|
|
14
|
+
Remove Product Tags
|
|
15
|
+
|
|
16
|
+
USAGE:
|
|
17
|
+
ecomcoder product remove-tags --id=<id> --tags=<tag1,tag2,tag3>
|
|
18
|
+
|
|
19
|
+
REQUIRED OPTIONS:
|
|
20
|
+
--id <id> Product ID (numeric or GID)
|
|
21
|
+
--tags <tags> Comma-separated tags to remove
|
|
22
|
+
|
|
23
|
+
EXAMPLES:
|
|
24
|
+
# Remove single tag
|
|
25
|
+
ecomcoder product remove-tags --id=123 --tags="discontinued"
|
|
26
|
+
|
|
27
|
+
# Remove multiple tags
|
|
28
|
+
ecomcoder product remove-tags --id=123 --tags="old-collection,winter-2023"
|
|
29
|
+
|
|
30
|
+
# Using GID
|
|
31
|
+
ecomcoder product remove-tags --id="gid://shopify/Product/123" --tags="clearance"
|
|
32
|
+
|
|
33
|
+
NOTES:
|
|
34
|
+
- Destructive operation (removes tags from product)
|
|
35
|
+
- Requires approval before execution
|
|
36
|
+
- Silently ignores tags that don't exist on product
|
|
37
|
+
- Removed tags can be re-added using add-tags command
|
|
38
|
+
- Tags are case-sensitive
|
|
39
|
+
`);
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
export async function run(argv = process.argv.slice(2)) {
|
|
43
|
+
if (hasHelpFlag(argv)) {
|
|
44
|
+
showHelp();
|
|
45
|
+
}
|
|
46
|
+
const args = parseArgs(argv);
|
|
47
|
+
// Validate required arguments
|
|
48
|
+
if (!args.id) {
|
|
49
|
+
console.error(JSON.stringify({
|
|
50
|
+
success: false,
|
|
51
|
+
error: 'Missing required argument: --id'
|
|
52
|
+
}));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
if (!args.tags) {
|
|
56
|
+
console.error(JSON.stringify({
|
|
57
|
+
success: false,
|
|
58
|
+
error: 'Missing required argument: --tags'
|
|
59
|
+
}));
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
// Parse tags from comma-separated string
|
|
64
|
+
const tags = args.tags.split(',').map((tag) => tag.trim()).filter(Boolean);
|
|
65
|
+
if (tags.length === 0) {
|
|
66
|
+
console.error(JSON.stringify({
|
|
67
|
+
success: false,
|
|
68
|
+
error: 'No valid tags provided'
|
|
69
|
+
}));
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
// Get credentials
|
|
73
|
+
const sessionId = getSessionId(args.sessionId);
|
|
74
|
+
const credentials = await getCredentials(sessionId, args.backendUrl, args.jwt);
|
|
75
|
+
// Request approval before executing
|
|
76
|
+
const tagCount = tags.length;
|
|
77
|
+
const approved = await requestApproval({
|
|
78
|
+
command: `ecomcoder product remove-tags --id=${args.id} --tags=${args.tags}`,
|
|
79
|
+
message: `This will remove ${tagCount} tag(s) from your product on the live Shopify store. Removed tags can be re-added later.`,
|
|
80
|
+
riskLevel: 'moderate'
|
|
81
|
+
});
|
|
82
|
+
if (!approved) {
|
|
83
|
+
console.log(JSON.stringify({
|
|
84
|
+
success: false,
|
|
85
|
+
error: 'Operation cancelled by user'
|
|
86
|
+
}));
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
// Execute remove via service
|
|
90
|
+
const result = await ProductService.removeTags(credentials, {
|
|
91
|
+
id: args.id,
|
|
92
|
+
tags
|
|
93
|
+
});
|
|
94
|
+
// Output result
|
|
95
|
+
console.log(JSON.stringify(result, null, 2));
|
|
96
|
+
if (!result.success) {
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
console.error(JSON.stringify({
|
|
102
|
+
success: false,
|
|
103
|
+
error: error.message || 'Unknown error occurred'
|
|
104
|
+
}));
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=remove-tags.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remove-tags.js","sourceRoot":"","sources":["../../../src/commands/product/remove-tags.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Follows Dependency Injection - accepts dependencies as parameters
|
|
6
6
|
*/
|
|
7
7
|
import type { ShopifyCredentials } from '../../lib/types.js';
|
|
8
|
-
import type { ShopifyProduct, FormattedProduct, UpdateDescriptionInput, UpdateTitleInput, UpdateStatusInput, UpdatePriceInput, UpdateTemplateInput, SetMetafieldInput, GetMetafieldInput, DeleteVariantsInput, CreateVariantsInput, CreateProductInput, AddOptionsInput, UpdateOptionInput, CommandResult } from './types.js';
|
|
8
|
+
import type { ShopifyProduct, FormattedProduct, UpdateDescriptionInput, UpdateTitleInput, UpdateStatusInput, UpdatePriceInput, UpdateTemplateInput, UpdateImagesInput, SetMetafieldInput, GetMetafieldInput, DeleteVariantsInput, CreateVariantsInput, CreateProductInput, AddOptionsInput, UpdateOptionInput, AddTagsInput, RemoveTagsInput, SetTagsInput, CommandResult } from './types.js';
|
|
9
9
|
/**
|
|
10
10
|
* Product Service
|
|
11
11
|
*
|
|
@@ -22,9 +22,16 @@ export declare class ProductService {
|
|
|
22
22
|
/**
|
|
23
23
|
* Create a new product from scratch
|
|
24
24
|
*
|
|
25
|
+
* @param credentials Shopify credentials
|
|
26
|
+
* @param input Product creation input
|
|
27
|
+
* @param media Optional media/images to add (null = no images, undefined/array = add images)
|
|
25
28
|
* @returns Formatted result with created product data
|
|
26
29
|
*/
|
|
27
|
-
static createProduct(credentials: ShopifyCredentials, input: CreateProductInput
|
|
30
|
+
static createProduct(credentials: ShopifyCredentials, input: CreateProductInput, media?: Array<{
|
|
31
|
+
originalSource: string;
|
|
32
|
+
alt?: string;
|
|
33
|
+
mediaContentType: string;
|
|
34
|
+
}> | null): Promise<CommandResult<{
|
|
28
35
|
product: FormattedProduct;
|
|
29
36
|
}>>;
|
|
30
37
|
/**
|
|
@@ -51,6 +58,24 @@ export declare class ProductService {
|
|
|
51
58
|
static updateStatus(credentials: ShopifyCredentials, input: UpdateStatusInput): Promise<CommandResult<{
|
|
52
59
|
product: FormattedProduct;
|
|
53
60
|
}>>;
|
|
61
|
+
/**
|
|
62
|
+
* Update product images
|
|
63
|
+
* Adds new images to the product using smart, context-aware image selection
|
|
64
|
+
*
|
|
65
|
+
* Strategy:
|
|
66
|
+
* 1. If --images provided: Use custom URLs
|
|
67
|
+
* 2. If --search provided: Search Pexels with custom query
|
|
68
|
+
* 3. If --random provided: Generate random placeholders
|
|
69
|
+
* 4. Default: Use product title to search for relevant images
|
|
70
|
+
*
|
|
71
|
+
* Note: Shopify productUpdate with media parameter ADDS images, doesn't replace existing ones
|
|
72
|
+
*
|
|
73
|
+
* @returns Formatted result with updated product and media
|
|
74
|
+
*/
|
|
75
|
+
static updateImages(credentials: ShopifyCredentials, input: UpdateImagesInput): Promise<CommandResult<{
|
|
76
|
+
product: any;
|
|
77
|
+
imagesAdded: number;
|
|
78
|
+
}>>;
|
|
54
79
|
/**
|
|
55
80
|
* Set product metafield value
|
|
56
81
|
*
|
|
@@ -150,5 +175,59 @@ export declare class ProductService {
|
|
|
150
175
|
static updateOption(credentials: ShopifyCredentials, input: UpdateOptionInput): Promise<CommandResult<{
|
|
151
176
|
product: FormattedProduct;
|
|
152
177
|
}>>;
|
|
178
|
+
/**
|
|
179
|
+
* Add tags to product (non-destructive)
|
|
180
|
+
* Uses tagsAdd mutation
|
|
181
|
+
*
|
|
182
|
+
* IMPORTANT:
|
|
183
|
+
* - Preserves existing tags
|
|
184
|
+
* - Duplicate tags are handled by Shopify
|
|
185
|
+
* - No approval required (non-destructive)
|
|
186
|
+
*
|
|
187
|
+
* @returns Formatted result with updated product tags
|
|
188
|
+
*/
|
|
189
|
+
static addTags(credentials: ShopifyCredentials, input: AddTagsInput): Promise<CommandResult<{
|
|
190
|
+
product: {
|
|
191
|
+
id: string;
|
|
192
|
+
title: string;
|
|
193
|
+
tags: string[];
|
|
194
|
+
};
|
|
195
|
+
}>>;
|
|
196
|
+
/**
|
|
197
|
+
* Remove tags from product (destructive)
|
|
198
|
+
* Uses tagsRemove mutation
|
|
199
|
+
*
|
|
200
|
+
* IMPORTANT:
|
|
201
|
+
* - Removes specified tags from product
|
|
202
|
+
* - Requires approval (destructive operation)
|
|
203
|
+
* - Silently ignores tags that don't exist on product
|
|
204
|
+
*
|
|
205
|
+
* @returns Formatted result with updated product tags
|
|
206
|
+
*/
|
|
207
|
+
static removeTags(credentials: ShopifyCredentials, input: RemoveTagsInput): Promise<CommandResult<{
|
|
208
|
+
product: {
|
|
209
|
+
id: string;
|
|
210
|
+
title: string;
|
|
211
|
+
tags: string[];
|
|
212
|
+
};
|
|
213
|
+
}>>;
|
|
214
|
+
/**
|
|
215
|
+
* Set/replace all product tags (destructive)
|
|
216
|
+
* Uses productUpdate mutation
|
|
217
|
+
*
|
|
218
|
+
* IMPORTANT:
|
|
219
|
+
* - REPLACES all existing tags with new tags
|
|
220
|
+
* - Requires approval (destructive operation)
|
|
221
|
+
* - Pass empty array to remove all tags
|
|
222
|
+
*
|
|
223
|
+
* @returns Formatted result with updated product tags
|
|
224
|
+
*/
|
|
225
|
+
static setTags(credentials: ShopifyCredentials, input: SetTagsInput): Promise<CommandResult<{
|
|
226
|
+
product: {
|
|
227
|
+
id: string;
|
|
228
|
+
title: string;
|
|
229
|
+
tags: string[];
|
|
230
|
+
};
|
|
231
|
+
}>>;
|
|
153
232
|
}
|
|
154
233
|
//# sourceMappingURL=service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/commands/product/service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/commands/product/service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAmC7D,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,YAAY,EAeZ,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB;;;;;GAKG;AACH,qBAAa,cAAc;IACzB;;;;OAIG;WACU,cAAc,CACzB,WAAW,EAAE,kBAAkB,EAC/B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,cAAc,CAAC;IAkB1B;;;;;;;OAOG;WACU,aAAa,CACxB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,kBAAkB,EACzB,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI,GACvF,OAAO,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE,gBAAgB,CAAA;KAAE,CAAC,CAAC;IAkLxD;;;;OAIG;WACU,iBAAiB,CAC5B,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,sBAAsB,GAC5B,OAAO,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE,gBAAgB,CAAA;KAAE,CAAC,CAAC;IAkDxD;;;;OAIG;WACU,WAAW,CACtB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE,gBAAgB,CAAA;KAAE,CAAC,CAAC;IA2CxD;;;;OAIG;WACU,YAAY,CACvB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE,gBAAgB,CAAA;KAAE,CAAC,CAAC;IA2CxD;;;;;;;;;;;;;OAaG;WACU,YAAY,CACvB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE,GAAG,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAwGhE;;;;OAIG;WACU,YAAY,CACvB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,aAAa,CAAC;QAAE,SAAS,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;IAmD7C;;;;OAIG;WACU,YAAY,CACvB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,aAAa,CAAC;QAAE,SAAS,CAAC,EAAE,GAAG,CAAC;QAAC,UAAU,CAAC,EAAE,GAAG,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAyElF;;;;;OAKG;WACU,WAAW,CACtB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC,aAAa,CAAC;QAAE,OAAO,CAAC,EAAE,GAAG,CAAC;QAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;QAAC,OAAO,EAAE,gBAAgB,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IA+IhH;;;;OAIG;WACU,cAAc,CACzB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE,gBAAgB,CAAA;KAAE,CAAC,CAAC;IA2CxD;;;;;;;;;;OAUG;WACU,cAAc,CACzB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE,gBAAgB,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAiEzG;;;;;;;;;;OAUG;WACU,cAAc,CACzB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE,gBAAgB,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,GAAG,EAAE,CAAA;KAAE,CAAC,CAAC;IAiEtG;;;;;;;;;;OAUG;WACU,UAAU,CACrB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE,gBAAgB,CAAA;KAAE,CAAC,CAAC;IA4ExD;;;;;;;;;;;OAWG;WACU,YAAY,CACvB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE,gBAAgB,CAAA;KAAE,CAAC,CAAC;IA6FxD;;;;;;;;;;OAUG;WACU,OAAO,CAClB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,EAAE,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;IAsDrF;;;;;;;;;;OAUG;WACU,UAAU,CACrB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,EAAE,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;IAsDrF;;;;;;;;;;OAUG;WACU,OAAO,CAClB,WAAW,EAAE,kBAAkB,EAC/B,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,aAAa,CAAC;QAAE,OAAO,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,EAAE,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;CAuDtF"}
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* Follows Dependency Injection - accepts dependencies as parameters
|
|
6
6
|
*/
|
|
7
7
|
import { shopifyGraphQL } from '../../lib/shopify-client.js';
|
|
8
|
-
import { GET_PRODUCT_BY_ID, UPDATE_PRODUCT, PRODUCT_VARIANTS_BULK_UPDATE, PRODUCT_VARIANTS_BULK_DELETE, PRODUCT_VARIANTS_BULK_CREATE, PRODUCT_CREATE, GET_PRODUCT_METAFIELD, GET_PRODUCT_METAFIELDS, METAFIELDS_SET, PRODUCT_OPTIONS_CREATE, PRODUCT_OPTION_UPDATE, GET_PUBLICATIONS, PUBLISHABLE_PUBLISH } from './queries.js';
|
|
8
|
+
import { GET_PRODUCT_BY_ID, UPDATE_PRODUCT, UPDATE_PRODUCT_MEDIA, PRODUCT_VARIANTS_BULK_UPDATE, PRODUCT_VARIANTS_BULK_DELETE, PRODUCT_VARIANTS_BULK_CREATE, PRODUCT_CREATE, GET_PRODUCT_METAFIELD, GET_PRODUCT_METAFIELDS, METAFIELDS_SET, PRODUCT_OPTIONS_CREATE, PRODUCT_OPTION_UPDATE, GET_PUBLICATIONS, PUBLISHABLE_PUBLISH, TAGS_ADD, TAGS_REMOVE } from './queries.js';
|
|
9
|
+
import { getRandomPlaceholderImages, searchPexelsForProduct } from '../../lib/image-library.js';
|
|
9
10
|
import { normalizeProductId, normalizeVariantId, normalizeTemplateName, formatProduct, isValidPrice, formatUserErrors, hasGraphQLErrors, extractGraphQLErrors } from './utils.js';
|
|
10
11
|
/**
|
|
11
12
|
* Product Service
|
|
@@ -34,9 +35,12 @@ export class ProductService {
|
|
|
34
35
|
/**
|
|
35
36
|
* Create a new product from scratch
|
|
36
37
|
*
|
|
38
|
+
* @param credentials Shopify credentials
|
|
39
|
+
* @param input Product creation input
|
|
40
|
+
* @param media Optional media/images to add (null = no images, undefined/array = add images)
|
|
37
41
|
* @returns Formatted result with created product data
|
|
38
42
|
*/
|
|
39
|
-
static async createProduct(credentials, input) {
|
|
43
|
+
static async createProduct(credentials, input, media) {
|
|
40
44
|
// Title is required
|
|
41
45
|
if (!input.title || input.title.trim().length === 0) {
|
|
42
46
|
return {
|
|
@@ -69,17 +73,15 @@ export class ProductService {
|
|
|
69
73
|
}
|
|
70
74
|
// Note: Shopify's ProductInput does not support variants field
|
|
71
75
|
// Variant price/SKU will be updated after product creation if provided
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
// Execute mutation with default images
|
|
82
|
-
const response = await shopifyGraphQL(credentials, PRODUCT_CREATE, { input: productInput, media: defaultMedia });
|
|
76
|
+
// Use provided media (smart images from command layer) or no images if null
|
|
77
|
+
// media parameter: undefined/array = add images, null = no images
|
|
78
|
+
const mediaToAdd = media !== null ? media : undefined;
|
|
79
|
+
// Execute mutation with optional media
|
|
80
|
+
const variables = { input: productInput };
|
|
81
|
+
if (mediaToAdd && mediaToAdd.length > 0) {
|
|
82
|
+
variables.media = mediaToAdd;
|
|
83
|
+
}
|
|
84
|
+
const response = await shopifyGraphQL(credentials, PRODUCT_CREATE, variables);
|
|
83
85
|
if (hasGraphQLErrors(response)) {
|
|
84
86
|
return {
|
|
85
87
|
success: false,
|
|
@@ -286,6 +288,112 @@ export class ProductService {
|
|
|
286
288
|
}
|
|
287
289
|
};
|
|
288
290
|
}
|
|
291
|
+
/**
|
|
292
|
+
* Update product images
|
|
293
|
+
* Adds new images to the product using smart, context-aware image selection
|
|
294
|
+
*
|
|
295
|
+
* Strategy:
|
|
296
|
+
* 1. If --images provided: Use custom URLs
|
|
297
|
+
* 2. If --search provided: Search Pexels with custom query
|
|
298
|
+
* 3. If --random provided: Generate random placeholders
|
|
299
|
+
* 4. Default: Use product title to search for relevant images
|
|
300
|
+
*
|
|
301
|
+
* Note: Shopify productUpdate with media parameter ADDS images, doesn't replace existing ones
|
|
302
|
+
*
|
|
303
|
+
* @returns Formatted result with updated product and media
|
|
304
|
+
*/
|
|
305
|
+
static async updateImages(credentials, input) {
|
|
306
|
+
const { gid } = normalizeProductId(input.id);
|
|
307
|
+
// Build media array based on input
|
|
308
|
+
let mediaInputs = [];
|
|
309
|
+
if (input.images && input.images.length > 0) {
|
|
310
|
+
// Option 1: Use provided custom images
|
|
311
|
+
mediaInputs = input.images.map((img, index) => {
|
|
312
|
+
if (typeof img === 'string') {
|
|
313
|
+
return {
|
|
314
|
+
originalSource: img,
|
|
315
|
+
alt: `Product Image ${index + 1}`,
|
|
316
|
+
mediaContentType: 'IMAGE'
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
return {
|
|
321
|
+
originalSource: img.url,
|
|
322
|
+
alt: img.alt || `Product Image ${index + 1}`,
|
|
323
|
+
mediaContentType: 'IMAGE'
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
console.error(`ℹ️ Adding ${mediaInputs.length} custom image(s)`);
|
|
328
|
+
}
|
|
329
|
+
else if (input.search || input.random) {
|
|
330
|
+
// Option 2: Generate images (either with search query or random)
|
|
331
|
+
const count = input.random || 6;
|
|
332
|
+
let images;
|
|
333
|
+
if (input.search) {
|
|
334
|
+
// Custom search query provided
|
|
335
|
+
if (input.source === 'picsum') {
|
|
336
|
+
images = getRandomPlaceholderImages(count);
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
// Default to Pexels for search
|
|
340
|
+
images = await searchPexelsForProduct(input.search, count);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
// Just random, no search
|
|
345
|
+
images = getRandomPlaceholderImages(count);
|
|
346
|
+
}
|
|
347
|
+
mediaInputs = images.map(img => ({
|
|
348
|
+
originalSource: img.url,
|
|
349
|
+
alt: img.alt || 'Product Image',
|
|
350
|
+
mediaContentType: 'IMAGE'
|
|
351
|
+
}));
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
return {
|
|
355
|
+
success: false,
|
|
356
|
+
error: 'Either --images, --search, or --random must be provided'
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
if (mediaInputs.length === 0) {
|
|
360
|
+
return {
|
|
361
|
+
success: false,
|
|
362
|
+
error: 'No images to add. Please provide valid image URLs or search parameters.'
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
// Execute mutation to add images
|
|
366
|
+
const response = await shopifyGraphQL(credentials, UPDATE_PRODUCT_MEDIA, {
|
|
367
|
+
product: { id: gid },
|
|
368
|
+
media: mediaInputs
|
|
369
|
+
});
|
|
370
|
+
if (hasGraphQLErrors(response)) {
|
|
371
|
+
return {
|
|
372
|
+
success: false,
|
|
373
|
+
error: `GraphQL error: ${extractGraphQLErrors(response)}`
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
const { product, userErrors } = response.data.productUpdate;
|
|
377
|
+
if (userErrors && userErrors.length > 0) {
|
|
378
|
+
return {
|
|
379
|
+
success: false,
|
|
380
|
+
error: formatUserErrors(userErrors)
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
if (!product) {
|
|
384
|
+
return {
|
|
385
|
+
success: false,
|
|
386
|
+
error: 'Product update failed'
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
return {
|
|
390
|
+
success: true,
|
|
391
|
+
data: {
|
|
392
|
+
product,
|
|
393
|
+
imagesAdded: mediaInputs.length
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
}
|
|
289
397
|
/**
|
|
290
398
|
* Set product metafield value
|
|
291
399
|
*
|
|
@@ -829,5 +937,175 @@ export class ProductService {
|
|
|
829
937
|
}
|
|
830
938
|
};
|
|
831
939
|
}
|
|
940
|
+
/**
|
|
941
|
+
* Add tags to product (non-destructive)
|
|
942
|
+
* Uses tagsAdd mutation
|
|
943
|
+
*
|
|
944
|
+
* IMPORTANT:
|
|
945
|
+
* - Preserves existing tags
|
|
946
|
+
* - Duplicate tags are handled by Shopify
|
|
947
|
+
* - No approval required (non-destructive)
|
|
948
|
+
*
|
|
949
|
+
* @returns Formatted result with updated product tags
|
|
950
|
+
*/
|
|
951
|
+
static async addTags(credentials, input) {
|
|
952
|
+
try {
|
|
953
|
+
const { gid } = normalizeProductId(input.id);
|
|
954
|
+
const response = await shopifyGraphQL(credentials, TAGS_ADD, {
|
|
955
|
+
id: gid,
|
|
956
|
+
tags: input.tags
|
|
957
|
+
});
|
|
958
|
+
if (hasGraphQLErrors(response)) {
|
|
959
|
+
return {
|
|
960
|
+
success: false,
|
|
961
|
+
error: `GraphQL error: ${extractGraphQLErrors(response)}`
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
const mutation = response.data?.tagsAdd;
|
|
965
|
+
if (mutation && mutation.userErrors && mutation.userErrors.length > 0) {
|
|
966
|
+
return {
|
|
967
|
+
success: false,
|
|
968
|
+
error: formatUserErrors(mutation.userErrors)
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
const product = mutation?.node;
|
|
972
|
+
if (!product) {
|
|
973
|
+
return {
|
|
974
|
+
success: false,
|
|
975
|
+
error: 'No product returned from mutation'
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
return {
|
|
979
|
+
success: true,
|
|
980
|
+
data: {
|
|
981
|
+
product: {
|
|
982
|
+
id: product.id,
|
|
983
|
+
title: product.title,
|
|
984
|
+
tags: product.tags || []
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
catch (error) {
|
|
990
|
+
return {
|
|
991
|
+
success: false,
|
|
992
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* Remove tags from product (destructive)
|
|
998
|
+
* Uses tagsRemove mutation
|
|
999
|
+
*
|
|
1000
|
+
* IMPORTANT:
|
|
1001
|
+
* - Removes specified tags from product
|
|
1002
|
+
* - Requires approval (destructive operation)
|
|
1003
|
+
* - Silently ignores tags that don't exist on product
|
|
1004
|
+
*
|
|
1005
|
+
* @returns Formatted result with updated product tags
|
|
1006
|
+
*/
|
|
1007
|
+
static async removeTags(credentials, input) {
|
|
1008
|
+
try {
|
|
1009
|
+
const { gid } = normalizeProductId(input.id);
|
|
1010
|
+
const response = await shopifyGraphQL(credentials, TAGS_REMOVE, {
|
|
1011
|
+
id: gid,
|
|
1012
|
+
tags: input.tags
|
|
1013
|
+
});
|
|
1014
|
+
if (hasGraphQLErrors(response)) {
|
|
1015
|
+
return {
|
|
1016
|
+
success: false,
|
|
1017
|
+
error: `GraphQL error: ${extractGraphQLErrors(response)}`
|
|
1018
|
+
};
|
|
1019
|
+
}
|
|
1020
|
+
const mutation = response.data?.tagsRemove;
|
|
1021
|
+
if (mutation && mutation.userErrors && mutation.userErrors.length > 0) {
|
|
1022
|
+
return {
|
|
1023
|
+
success: false,
|
|
1024
|
+
error: formatUserErrors(mutation.userErrors)
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
const product = mutation?.node;
|
|
1028
|
+
if (!product) {
|
|
1029
|
+
return {
|
|
1030
|
+
success: false,
|
|
1031
|
+
error: 'No product returned from mutation'
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
return {
|
|
1035
|
+
success: true,
|
|
1036
|
+
data: {
|
|
1037
|
+
product: {
|
|
1038
|
+
id: product.id,
|
|
1039
|
+
title: product.title,
|
|
1040
|
+
tags: product.tags || []
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
catch (error) {
|
|
1046
|
+
return {
|
|
1047
|
+
success: false,
|
|
1048
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
1049
|
+
};
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Set/replace all product tags (destructive)
|
|
1054
|
+
* Uses productUpdate mutation
|
|
1055
|
+
*
|
|
1056
|
+
* IMPORTANT:
|
|
1057
|
+
* - REPLACES all existing tags with new tags
|
|
1058
|
+
* - Requires approval (destructive operation)
|
|
1059
|
+
* - Pass empty array to remove all tags
|
|
1060
|
+
*
|
|
1061
|
+
* @returns Formatted result with updated product tags
|
|
1062
|
+
*/
|
|
1063
|
+
static async setTags(credentials, input) {
|
|
1064
|
+
try {
|
|
1065
|
+
const { gid } = normalizeProductId(input.id);
|
|
1066
|
+
const response = await shopifyGraphQL(credentials, UPDATE_PRODUCT, {
|
|
1067
|
+
input: {
|
|
1068
|
+
id: gid,
|
|
1069
|
+
tags: input.tags
|
|
1070
|
+
}
|
|
1071
|
+
});
|
|
1072
|
+
if (hasGraphQLErrors(response)) {
|
|
1073
|
+
return {
|
|
1074
|
+
success: false,
|
|
1075
|
+
error: `GraphQL error: ${extractGraphQLErrors(response)}`
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
const mutation = response.data?.productUpdate;
|
|
1079
|
+
if (mutation && mutation.userErrors && mutation.userErrors.length > 0) {
|
|
1080
|
+
return {
|
|
1081
|
+
success: false,
|
|
1082
|
+
error: formatUserErrors(mutation.userErrors)
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
const product = mutation?.product;
|
|
1086
|
+
if (!product) {
|
|
1087
|
+
return {
|
|
1088
|
+
success: false,
|
|
1089
|
+
error: 'No product returned from update'
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
return {
|
|
1093
|
+
success: true,
|
|
1094
|
+
data: {
|
|
1095
|
+
product: {
|
|
1096
|
+
id: product.id,
|
|
1097
|
+
title: product.title,
|
|
1098
|
+
tags: product.tags || []
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
catch (error) {
|
|
1104
|
+
return {
|
|
1105
|
+
success: false,
|
|
1106
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
832
1110
|
}
|
|
833
1111
|
//# sourceMappingURL=service.js.map
|