shop-cli 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/cli/approvalRequired.d.ts +30 -0
  2. package/dist/cli/approvalRequired.js +87 -0
  3. package/dist/cli/errors.d.ts +4 -1
  4. package/dist/cli/errors.js +3 -1
  5. package/dist/cli/gid.d.ts +1 -1
  6. package/dist/cli/help/registry.js +1013 -221
  7. package/dist/cli/help/render.d.ts +1 -0
  8. package/dist/cli/help/render.js +47 -0
  9. package/dist/cli/parse-command.d.ts +18 -0
  10. package/dist/cli/parse-command.js +109 -0
  11. package/dist/cli/router.js +3 -0
  12. package/dist/cli/suggest.d.ts +5 -0
  13. package/dist/cli/suggest.js +88 -0
  14. package/dist/cli/verbs/_shared.d.ts +1 -1
  15. package/dist/cli/verbs/_shared.js +4 -3
  16. package/dist/cli/verbs/catalogs.js +1 -1
  17. package/dist/cli/verbs/checkout-branding.js +2 -2
  18. package/dist/cli/verbs/collections.js +147 -12
  19. package/dist/cli/verbs/companies.js +2 -2
  20. package/dist/cli/verbs/company-contacts.js +8 -4
  21. package/dist/cli/verbs/company-locations.js +1 -1
  22. package/dist/cli/verbs/customers.js +156 -19
  23. package/dist/cli/verbs/discounts-automatic.js +36 -10
  24. package/dist/cli/verbs/discounts-code.js +27 -10
  25. package/dist/cli/verbs/draft-orders.js +49 -3
  26. package/dist/cli/verbs/files.js +171 -55
  27. package/dist/cli/verbs/fulfillment-orders.js +19 -4
  28. package/dist/cli/verbs/fulfillments.js +8 -2
  29. package/dist/cli/verbs/graphql.js +2 -0
  30. package/dist/cli/verbs/inventory.js +7 -1
  31. package/dist/cli/verbs/marketing-activities.js +1 -1
  32. package/dist/cli/verbs/markets.js +37 -4
  33. package/dist/cli/verbs/metaobjects.js +10 -1
  34. package/dist/cli/verbs/orders.js +80 -8
  35. package/dist/cli/verbs/product-variants.js +67 -16
  36. package/dist/cli/verbs/products.js +1263 -211
  37. package/dist/cli/verbs/selling-plan-groups.js +32 -10
  38. package/dist/cli/verbs/subscription-contracts.js +1 -0
  39. package/dist/cli/verbs/themes.js +18 -0
  40. package/dist/cli/verbs/url-redirects.js +64 -6
  41. package/dist/cli/workflows/files/stagedUploads.d.ts +3 -3
  42. package/dist/cli/workflows/files/stagedUploads.js +71 -19
  43. package/dist/cli/workflows/files/stdinFile.d.ts +7 -0
  44. package/dist/cli/workflows/files/stdinFile.js +65 -0
  45. package/dist/cli/workflows/files/urlDownloads.d.ts +14 -0
  46. package/dist/cli/workflows/files/urlDownloads.js +114 -0
  47. package/dist/cli/workflows/files/waitForReady.d.ts +20 -0
  48. package/dist/cli/workflows/files/waitForReady.js +114 -0
  49. package/dist/cli.js +115 -29
  50. package/package.json +3 -2
@@ -181,22 +181,44 @@ const runSellingPlanGroups = async ({
181
181
  });
182
182
  const id = (0, import_shared.requireId)(args.id, "SellingPlanGroup");
183
183
  const variantIds = (0, import_shared.parseIds)(args["variant-ids"], "ProductVariant");
184
- const mutationField = verb === "add-variants" ? "sellingPlanGroupAddProductVariants" : "sellingPlanGroupRemoveProductVariants";
185
- const selection = verb === "add-variants" ? { sellingPlanGroup: sellingPlanGroupSummarySelection, userErrors: { field: true, message: true } } : { removedProductVariantIds: true, userErrors: { field: true, message: true } };
184
+ if (verb === "add-variants") {
185
+ const result2 = await (0, import_router.runMutation)(ctx, {
186
+ sellingPlanGroupAddProductVariants: {
187
+ __args: { id, productVariantIds: variantIds },
188
+ sellingPlanGroup: sellingPlanGroupSummarySelection,
189
+ userErrors: { field: true, message: true }
190
+ }
191
+ });
192
+ if (result2 === void 0) return;
193
+ const payload2 = result2.sellingPlanGroupAddProductVariants;
194
+ (0, import_userErrors.maybeFailOnUserErrors)({ payload: payload2, failOnUserErrors: ctx.failOnUserErrors });
195
+ if (ctx.quiet) return console.log(payload2?.sellingPlanGroup?.id ?? "");
196
+ (0, import_output.printJson)(payload2, ctx.format !== "raw");
197
+ return;
198
+ }
186
199
  const result = await (0, import_router.runMutation)(ctx, {
187
- [mutationField]: {
200
+ sellingPlanGroupRemoveProductVariants: {
188
201
  __args: { id, productVariantIds: variantIds },
189
- ...selection
202
+ removedProductVariantIds: true,
203
+ userErrors: { field: true, message: true }
190
204
  }
191
205
  });
192
206
  if (result === void 0) return;
193
- const payload = result[mutationField];
207
+ const payload = result.sellingPlanGroupRemoveProductVariants;
194
208
  (0, import_userErrors.maybeFailOnUserErrors)({ payload, failOnUserErrors: ctx.failOnUserErrors });
195
- if (ctx.quiet) {
196
- if (verb === "add-variants") return console.log(payload?.sellingPlanGroup?.id ?? "");
197
- return;
198
- }
199
- (0, import_output.printJson)(payload, ctx.format !== "raw");
209
+ if (ctx.quiet) return console.log(id ?? "");
210
+ const groupResult = await (0, import_router.runQuery)(ctx, {
211
+ sellingPlanGroup: { __args: { id }, ...sellingPlanGroupSummarySelection }
212
+ });
213
+ if (groupResult === void 0) return;
214
+ (0, import_output.printJson)(
215
+ {
216
+ sellingPlanGroup: groupResult.sellingPlanGroup ?? null,
217
+ removedProductVariantIds: payload?.removedProductVariantIds ?? [],
218
+ userErrors: payload?.userErrors ?? []
219
+ },
220
+ ctx.format !== "raw"
221
+ );
200
222
  return;
201
223
  }
202
224
  throw new import_errors.CliError(`Unknown verb for selling-plan-groups: ${verb}`, 2);
@@ -121,6 +121,7 @@ const subscriptionContractFullSelection = {
121
121
  };
122
122
  const subscriptionDraftSummarySelection = {
123
123
  id: true,
124
+ originalContract: { id: true },
124
125
  customer: { id: true, displayName: true, email: true },
125
126
  currencyCode: true,
126
127
  billingPolicy: {
@@ -269,6 +269,15 @@ const runThemes = async ({
269
269
  });
270
270
  if (result === void 0) return;
271
271
  (0, import_userErrors.maybeFailOnUserErrors)({ payload: result.themeFilesDelete, failOnUserErrors: ctx.failOnUserErrors });
272
+ if (ctx.quiet) {
273
+ const deleted = result.themeFilesDelete?.deletedThemeFiles ?? [];
274
+ for (const f of deleted) {
275
+ const name = typeof f?.filename === "string" ? f.filename : "";
276
+ if (name) process.stdout.write(`${name}
277
+ `);
278
+ }
279
+ return;
280
+ }
272
281
  (0, import_output.printJson)(result.themeFilesDelete, ctx.format !== "raw");
273
282
  return;
274
283
  }
@@ -285,6 +294,15 @@ const runThemes = async ({
285
294
  });
286
295
  if (result === void 0) return;
287
296
  (0, import_userErrors.maybeFailOnUserErrors)({ payload: result.themeFilesCopy, failOnUserErrors: ctx.failOnUserErrors });
297
+ if (ctx.quiet) {
298
+ const copied = result.themeFilesCopy?.copiedThemeFiles ?? [];
299
+ for (const f of copied) {
300
+ const name = typeof f?.filename === "string" ? f.filename : "";
301
+ if (name) process.stdout.write(`${name}
302
+ `);
303
+ }
304
+ return;
305
+ }
288
306
  (0, import_output.printJson)(result.themeFilesCopy, ctx.format !== "raw");
289
307
  return;
290
308
  }
@@ -193,8 +193,26 @@ const runUrlRedirects = async ({
193
193
  });
194
194
  if (result === void 0) return;
195
195
  (0, import_userErrors.maybeFailOnUserErrors)({ payload: result.urlRedirectImportSubmit, failOnUserErrors: ctx.failOnUserErrors });
196
- if (ctx.quiet) return console.log(result.urlRedirectImportSubmit?.job?.id ?? "");
197
- (0, import_output.printJson)(result.urlRedirectImportSubmit, ctx.format !== "raw");
196
+ if (ctx.quiet) return console.log(id ?? "");
197
+ const selection = (0, import_select.resolveSelection)({
198
+ typeName: "UrlRedirectImport",
199
+ view: ctx.view,
200
+ baseSelection: getUrlRedirectImportSelection(ctx.view),
201
+ select: args.select,
202
+ selection: args.selection,
203
+ include: args.include,
204
+ ensureId: true
205
+ });
206
+ const importResult = await (0, import_router.runQuery)(ctx, { urlRedirectImport: { __args: { id }, ...selection } });
207
+ if (importResult === void 0) return;
208
+ (0, import_output.printJson)(
209
+ {
210
+ urlRedirectImport: importResult.urlRedirectImport ?? null,
211
+ job: result.urlRedirectImportSubmit?.job ?? null,
212
+ userErrors: result.urlRedirectImportSubmit?.userErrors ?? []
213
+ },
214
+ ctx.format !== "raw"
215
+ );
198
216
  return;
199
217
  }
200
218
  if (verb === "import-get") {
@@ -224,6 +242,9 @@ const runUrlRedirects = async ({
224
242
  });
225
243
  if (!args.yes) throw new import_errors.CliError("Refusing to delete without --yes", 2);
226
244
  if (verb === "bulk-delete-all") {
245
+ const countResult2 = await (0, import_router.runQuery)(ctx, {
246
+ urlRedirectsCount: { count: true, precision: true }
247
+ });
227
248
  const result2 = await (0, import_router.runMutation)(ctx, {
228
249
  urlRedirectBulkDeleteAll: {
229
250
  job: { id: true, done: true },
@@ -233,7 +254,14 @@ const runUrlRedirects = async ({
233
254
  if (result2 === void 0) return;
234
255
  (0, import_userErrors.maybeFailOnUserErrors)({ payload: result2.urlRedirectBulkDeleteAll, failOnUserErrors: ctx.failOnUserErrors });
235
256
  if (ctx.quiet) return console.log(result2.urlRedirectBulkDeleteAll?.job?.id ?? "");
236
- (0, import_output.printJson)(result2.urlRedirectBulkDeleteAll, ctx.format !== "raw");
257
+ (0, import_output.printJson)(
258
+ {
259
+ job: result2.urlRedirectBulkDeleteAll?.job ?? null,
260
+ count: countResult2?.urlRedirectsCount ?? null,
261
+ userErrors: result2.urlRedirectBulkDeleteAll?.userErrors ?? []
262
+ },
263
+ ctx.format !== "raw"
264
+ );
237
265
  return;
238
266
  }
239
267
  if (verb === "bulk-delete-ids") {
@@ -248,13 +276,24 @@ const runUrlRedirects = async ({
248
276
  if (result2 === void 0) return;
249
277
  (0, import_userErrors.maybeFailOnUserErrors)({ payload: result2.urlRedirectBulkDeleteByIds, failOnUserErrors: ctx.failOnUserErrors });
250
278
  if (ctx.quiet) return console.log(result2.urlRedirectBulkDeleteByIds?.job?.id ?? "");
251
- (0, import_output.printJson)(result2.urlRedirectBulkDeleteByIds, ctx.format !== "raw");
279
+ (0, import_output.printJson)(
280
+ {
281
+ job: result2.urlRedirectBulkDeleteByIds?.job ?? null,
282
+ requestedIds: ids,
283
+ requestedCount: ids.length,
284
+ userErrors: result2.urlRedirectBulkDeleteByIds?.userErrors ?? []
285
+ },
286
+ ctx.format !== "raw"
287
+ );
252
288
  return;
253
289
  }
254
290
  if (verb === "bulk-delete-saved-search") {
255
291
  const raw = args["saved-search-id"];
256
292
  if (!raw) throw new import_errors.CliError("Missing --saved-search-id", 2);
257
293
  const savedSearchId = (0, import_gid.coerceGid)(raw, "SavedSearch");
294
+ const countResult2 = await (0, import_router.runQuery)(ctx, {
295
+ urlRedirectsCount: { __args: { savedSearchId }, count: true, precision: true }
296
+ });
258
297
  const result2 = await (0, import_router.runMutation)(ctx, {
259
298
  urlRedirectBulkDeleteBySavedSearch: {
260
299
  __args: { savedSearchId },
@@ -268,11 +307,22 @@ const runUrlRedirects = async ({
268
307
  failOnUserErrors: ctx.failOnUserErrors
269
308
  });
270
309
  if (ctx.quiet) return console.log(result2.urlRedirectBulkDeleteBySavedSearch?.job?.id ?? "");
271
- (0, import_output.printJson)(result2.urlRedirectBulkDeleteBySavedSearch, ctx.format !== "raw");
310
+ (0, import_output.printJson)(
311
+ {
312
+ job: result2.urlRedirectBulkDeleteBySavedSearch?.job ?? null,
313
+ count: countResult2?.urlRedirectsCount ?? null,
314
+ savedSearchId,
315
+ userErrors: result2.urlRedirectBulkDeleteBySavedSearch?.userErrors ?? []
316
+ },
317
+ ctx.format !== "raw"
318
+ );
272
319
  return;
273
320
  }
274
321
  const search = args.search;
275
322
  if (!search) throw new import_errors.CliError("Missing --search", 2);
323
+ const countResult = await (0, import_router.runQuery)(ctx, {
324
+ urlRedirectsCount: { __args: { query: search }, count: true, precision: true }
325
+ });
276
326
  const result = await (0, import_router.runMutation)(ctx, {
277
327
  urlRedirectBulkDeleteBySearch: {
278
328
  __args: { search },
@@ -286,7 +336,15 @@ const runUrlRedirects = async ({
286
336
  failOnUserErrors: ctx.failOnUserErrors
287
337
  });
288
338
  if (ctx.quiet) return console.log(result.urlRedirectBulkDeleteBySearch?.job?.id ?? "");
289
- (0, import_output.printJson)(result.urlRedirectBulkDeleteBySearch, ctx.format !== "raw");
339
+ (0, import_output.printJson)(
340
+ {
341
+ job: result.urlRedirectBulkDeleteBySearch?.job ?? null,
342
+ count: countResult?.urlRedirectsCount ?? null,
343
+ search,
344
+ userErrors: result.urlRedirectBulkDeleteBySearch?.userErrors ?? []
345
+ },
346
+ ctx.format !== "raw"
347
+ );
290
348
  return;
291
349
  }
292
350
  if (verb === "get") {
@@ -15,11 +15,11 @@ export type StagedUploadTarget = {
15
15
  value: string;
16
16
  }>;
17
17
  };
18
- export declare const buildLocalFilesForStagedUpload: ({ filePaths, contentType, resource, }: {
18
+ export declare const buildLocalFilesForStagedUpload: ({ filePaths, mimeType, resource, }: {
19
19
  filePaths: string[];
20
- contentType?: string;
20
+ mimeType?: string;
21
21
  resource?: StagedUploadResource;
22
- }) => LocalFileForStagedUpload[];
22
+ }) => Promise<LocalFileForStagedUpload[]>;
23
23
  export declare const stagedUploadsCreate: (ctx: CommandContext, localFiles: LocalFileForStagedUpload[]) => Promise<StagedUploadTarget[] | undefined>;
24
24
  export declare const uploadToStagedTarget: ({ target, localFile, }: {
25
25
  target: StagedUploadTarget;
@@ -36,14 +36,15 @@ __export(stagedUploads_exports, {
36
36
  module.exports = __toCommonJS(stagedUploads_exports);
37
37
  var import_node_fs = require("node:fs");
38
38
  var import_node_path = __toESM(require("node:path"));
39
+ var import_node_child_process = require("node:child_process");
39
40
  var import_mime_types = require("mime-types");
40
41
  var import_errors = require("../../errors");
41
42
  var import_router = require("../../router");
42
43
  var import_userErrors = require("../../userErrors");
43
- const guessMimeTypeFromFilename = (filename) => {
44
+ const mimeTypeFromFilename = (filename) => {
44
45
  const mimeType = (0, import_mime_types.lookup)(filename);
45
46
  if (typeof mimeType === "string") return mimeType;
46
- return "application/octet-stream";
47
+ return void 0;
47
48
  };
48
49
  const inferStagedUploadResource = ({
49
50
  filename,
@@ -57,26 +58,51 @@ const inferStagedUploadResource = ({
57
58
  }
58
59
  return "FILE";
59
60
  };
60
- const buildLocalFilesForStagedUpload = ({
61
+ const sniffMimeTypeFromFile = async (filePath) => {
62
+ try {
63
+ const mod = await import("file-type");
64
+ const result = await mod.fileTypeFromFile(filePath);
65
+ return result?.mime;
66
+ } catch {
67
+ return void 0;
68
+ }
69
+ };
70
+ const resolveMimeType = async ({
71
+ filePath,
72
+ filename,
73
+ forcedMimeType
74
+ }) => {
75
+ const forced = typeof forcedMimeType === "string" ? forcedMimeType.trim() : "";
76
+ if (forced) return forced;
77
+ const sniffed = await sniffMimeTypeFromFile(filePath);
78
+ if (sniffed) return sniffed;
79
+ const byExt = mimeTypeFromFilename(filename);
80
+ if (byExt) return byExt;
81
+ throw new import_errors.CliError(
82
+ `Unable to determine MIME type for ${filename}. Pass --mime-type <mime>.`,
83
+ 2
84
+ );
85
+ };
86
+ const buildLocalFilesForStagedUpload = async ({
61
87
  filePaths,
62
- contentType,
88
+ mimeType,
63
89
  resource
64
90
  }) => {
65
91
  if (filePaths.length === 0) return [];
66
- return filePaths.map((filePath) => {
92
+ return Promise.all(filePaths.map(async (filePath) => {
67
93
  const stats = (0, import_node_fs.statSync)(filePath);
68
94
  if (!stats.isFile()) throw new import_errors.CliError(`Not a file: ${filePath}`, 2);
69
95
  const filename = import_node_path.default.basename(filePath);
70
- const mimeType = contentType ?? guessMimeTypeFromFilename(filename);
71
- const inferredResource = inferStagedUploadResource({ filename, mimeType });
96
+ const resolvedMimeType = await resolveMimeType({ filePath, filename, forcedMimeType: mimeType });
97
+ const inferredResource = inferStagedUploadResource({ filename, mimeType: resolvedMimeType });
72
98
  return {
73
99
  filePath,
74
100
  filename,
75
- mimeType,
101
+ mimeType: resolvedMimeType,
76
102
  resource: resource ?? inferredResource,
77
103
  fileSize: stats.size
78
104
  };
79
- });
105
+ }));
80
106
  };
81
107
  const stagedUploadsCreate = async (ctx, localFiles) => {
82
108
  const input = localFiles.map((f) => ({
@@ -137,19 +163,45 @@ const uploadToStagedTarget = async ({
137
163
  target,
138
164
  localFile
139
165
  }) => {
140
- const body = (0, import_node_fs.readFileSync)(localFile.filePath);
141
- const form = new FormData();
142
- for (const p of target.parameters) form.set(p.name, p.value);
143
- form.set("file", new Blob([body], { type: localFile.mimeType }), localFile.filename);
144
- const res = await fetch(target.url, { method: "POST", body: form });
145
- if (res.ok) return;
146
- let details = "";
166
+ const args = ["--fail-with-body", "--silent", "--show-error", "-X", "POST"];
167
+ for (const p of target.parameters) {
168
+ args.push("--form-string", `${p.name}=${p.value}`);
169
+ }
170
+ args.push(
171
+ "--form",
172
+ `file=@${localFile.filePath};type=${localFile.mimeType};filename=${localFile.filename}`,
173
+ target.url
174
+ );
175
+ let child;
147
176
  try {
148
- details = await res.text();
149
- } catch {
177
+ child = (0, import_node_child_process.spawn)("curl", args, { stdio: ["ignore", "pipe", "pipe"] });
178
+ } catch (err) {
179
+ throw err;
150
180
  }
181
+ let stdout = "";
182
+ let stderr = "";
183
+ child.stdout?.on("data", (chunk) => {
184
+ stdout += typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
185
+ });
186
+ child.stderr?.on("data", (chunk) => {
187
+ stderr += typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
188
+ });
189
+ const code = await new Promise((resolve, reject) => {
190
+ child.on("error", (err) => {
191
+ if (err && err.code === "ENOENT") {
192
+ reject(new import_errors.CliError("curl is required for streaming staged uploads (curl not found on PATH)", 2));
193
+ return;
194
+ }
195
+ reject(err);
196
+ });
197
+ child.on("close", (c) => resolve(c ?? 1));
198
+ });
199
+ if (code === 0) return;
200
+ const trimmedErr = stderr.trim();
201
+ const trimmedOut = stdout.trim();
202
+ const details = trimmedErr || trimmedOut;
151
203
  throw new import_errors.CliError(
152
- `Staged upload failed for ${localFile.filename}: ${res.status} ${res.statusText}${details ? `
204
+ `Staged upload failed for ${localFile.filename}: curl exited with code ${code}${details ? `
153
205
  ${details}` : ""}`,
154
206
  2
155
207
  );
@@ -0,0 +1,7 @@
1
+ export declare const writeStdinToTempFile: ({ filename, stdin, }: {
2
+ filename: string;
3
+ stdin?: NodeJS.ReadableStream;
4
+ }) => Promise<{
5
+ filePath: string;
6
+ cleanup: () => Promise<void>;
7
+ }>;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var stdinFile_exports = {};
30
+ __export(stdinFile_exports, {
31
+ writeStdinToTempFile: () => writeStdinToTempFile
32
+ });
33
+ module.exports = __toCommonJS(stdinFile_exports);
34
+ var import_node_fs = require("node:fs");
35
+ var import_promises = require("node:fs/promises");
36
+ var import_node_os = __toESM(require("node:os"));
37
+ var import_node_path = __toESM(require("node:path"));
38
+ var import_promises2 = require("node:stream/promises");
39
+ var import_errors = require("../../errors");
40
+ const safeBasename = (name) => name.replace(/[\\/]/g, "_");
41
+ const writeStdinToTempFile = async ({
42
+ filename,
43
+ stdin = process.stdin
44
+ }) => {
45
+ const raw = typeof filename === "string" ? filename.trim() : "";
46
+ if (!raw) throw new import_errors.CliError("Missing --filename (required with --file -)", 2);
47
+ const tempDir = await (0, import_promises.mkdtemp)(import_node_path.default.join(import_node_os.default.tmpdir(), "shop-cli-stdin-"));
48
+ const cleanup = async () => {
49
+ await (0, import_promises.rm)(tempDir, { recursive: true, force: true });
50
+ };
51
+ try {
52
+ const filePath = import_node_path.default.join(tempDir, safeBasename(raw) || "stdin");
53
+ const out = (0, import_node_fs.createWriteStream)(filePath);
54
+ await (0, import_promises2.pipeline)(stdin, out);
55
+ return { filePath, cleanup };
56
+ } catch (err) {
57
+ await cleanup();
58
+ throw err;
59
+ }
60
+ };
61
+ // Annotate the CommonJS export names for ESM import in node:
62
+ 0 && (module.exports = {
63
+ writeStdinToTempFile
64
+ });
65
+ //# sourceMappingURL=stdinFile.js.map
@@ -0,0 +1,14 @@
1
+ export declare const inferFilenameFromUrl: (rawUrl: string) => string;
2
+ export type DownloadedUrlFile = {
3
+ url: string;
4
+ filePath: string;
5
+ filename: string;
6
+ };
7
+ export declare const downloadUrlsToTempDir: ({ urls, filenameOverride, }: {
8
+ urls: string[];
9
+ filenameOverride?: string;
10
+ }) => Promise<{
11
+ tempDir: string;
12
+ files: DownloadedUrlFile[];
13
+ cleanup: () => Promise<void>;
14
+ }>;
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var urlDownloads_exports = {};
30
+ __export(urlDownloads_exports, {
31
+ downloadUrlsToTempDir: () => downloadUrlsToTempDir,
32
+ inferFilenameFromUrl: () => inferFilenameFromUrl
33
+ });
34
+ module.exports = __toCommonJS(urlDownloads_exports);
35
+ var import_node_fs = require("node:fs");
36
+ var import_promises = require("node:fs/promises");
37
+ var import_promises2 = require("node:stream/promises");
38
+ var import_node_stream = require("node:stream");
39
+ var import_node_os = __toESM(require("node:os"));
40
+ var import_node_path = __toESM(require("node:path"));
41
+ var import_errors = require("../../errors");
42
+ const safeBasename = (name) => name.replace(/[\\/]/g, "_");
43
+ const inferFilenameFromUrl = (rawUrl) => {
44
+ let u;
45
+ try {
46
+ u = new URL(rawUrl);
47
+ } catch {
48
+ throw new import_errors.CliError(`Invalid URL: ${rawUrl}`, 2);
49
+ }
50
+ const base = import_node_path.default.posix.basename(u.pathname || "");
51
+ if (base && base !== "/" && base !== ".") {
52
+ try {
53
+ const decoded = decodeURIComponent(base);
54
+ return safeBasename(decoded) || "download";
55
+ } catch {
56
+ return safeBasename(base) || "download";
57
+ }
58
+ }
59
+ return "download";
60
+ };
61
+ const uniqueFilename = (requested, used) => {
62
+ const base = safeBasename(requested) || "download";
63
+ const count = used.get(base) ?? 0;
64
+ used.set(base, count + 1);
65
+ if (count === 0) return base;
66
+ const ext = import_node_path.default.extname(base);
67
+ const stem = ext ? base.slice(0, -ext.length) : base;
68
+ return `${stem}-${count + 1}${ext}`;
69
+ };
70
+ const downloadUrlsToTempDir = async ({
71
+ urls,
72
+ filenameOverride
73
+ }) => {
74
+ if (urls.length === 0) throw new import_errors.CliError("Missing --url (repeatable)", 2);
75
+ if (filenameOverride && urls.length !== 1) {
76
+ throw new import_errors.CliError("--filename is only valid when exactly 1 --url is provided", 2);
77
+ }
78
+ const tempDir = await (0, import_promises.mkdtemp)(import_node_path.default.join(import_node_os.default.tmpdir(), "shop-cli-files-upload-"));
79
+ const cleanup = async () => {
80
+ await (0, import_promises.rm)(tempDir, { recursive: true, force: true });
81
+ };
82
+ try {
83
+ await (0, import_promises.mkdir)(tempDir, { recursive: true });
84
+ const used = /* @__PURE__ */ new Map();
85
+ const files = [];
86
+ for (let i = 0; i < urls.length; i++) {
87
+ const url = urls[i];
88
+ const inferred = filenameOverride ?? inferFilenameFromUrl(url);
89
+ const filename = uniqueFilename(inferred, used);
90
+ const filePath = import_node_path.default.join(tempDir, filename);
91
+ const res = await fetch(url, { redirect: "follow" });
92
+ if (!res.ok) {
93
+ throw new import_errors.CliError(`Failed to download ${url}: ${res.status} ${res.statusText}`, 2);
94
+ }
95
+ if (!res.body) {
96
+ throw new import_errors.CliError(`Failed to download ${url}: empty response body`, 2);
97
+ }
98
+ const nodeStream = import_node_stream.Readable.fromWeb(res.body);
99
+ const out = (0, import_node_fs.createWriteStream)(filePath);
100
+ await (0, import_promises2.pipeline)(nodeStream, out);
101
+ files.push({ url, filePath, filename });
102
+ }
103
+ return { tempDir, files, cleanup };
104
+ } catch (err) {
105
+ await cleanup();
106
+ throw err;
107
+ }
108
+ };
109
+ // Annotate the CommonJS export names for ESM import in node:
110
+ 0 && (module.exports = {
111
+ downloadUrlsToTempDir,
112
+ inferFilenameFromUrl
113
+ });
114
+ //# sourceMappingURL=urlDownloads.js.map
@@ -0,0 +1,20 @@
1
+ import { type CommandContext } from '../../router';
2
+ export type WaitForFilesResult = {
3
+ nodes: any[];
4
+ readyIds: string[];
5
+ failedIds: string[];
6
+ };
7
+ export declare const pollFilesReadyOrFailed: ({ ids, pollIntervalMs, timeoutMs, fetchNodes, now, sleep, }: {
8
+ ids: string[];
9
+ pollIntervalMs: number;
10
+ timeoutMs: number;
11
+ fetchNodes: (ids: string[]) => Promise<any[]>;
12
+ now?: () => number;
13
+ sleep?: (ms: number) => Promise<void>;
14
+ }) => Promise<WaitForFilesResult>;
15
+ export declare const waitForFilesReadyOrFailed: ({ ctx, ids, pollIntervalMs, timeoutMs, }: {
16
+ ctx: CommandContext;
17
+ ids: string[];
18
+ pollIntervalMs: number;
19
+ timeoutMs: number;
20
+ }) => Promise<WaitForFilesResult>;