aemdm 0.2.3 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import { fileURLToPath } from "node:url";
3
3
  import { realpathSync } from "node:fs";
4
4
  import { Command, CommanderError, Option } from "commander";
5
5
  import { z } from "zod";
6
- import { CliError, request, requestJson, resolveBucket, resolveOptionalImsToken, resolveSearchAuth, } from "./lib/client.js";
6
+ import { CliError, normalizeBucket, request, requestJson, resolveBucket, resolveOptionalImsToken, resolveSearchAuth, } from "./lib/client.js";
7
7
  import { readProfileConfig, writeProfileConfig, } from "./lib/config.js";
8
8
  import { buildAssetUrl, buildMetadataUrl, deliveryFormatSchema, resolveDimensions, } from "./lib/delivery.js";
9
9
  import { buildSearchRequest, getAssetIdFromHit, loadRawQuery, } from "./lib/search.js";
@@ -167,7 +167,7 @@ async function handleAssetGet(assetId, options, runtime) {
167
167
  const parsed = parseWithSchema(assetGetSchema, options);
168
168
  const profile = await readProfileConfig(runtime.env);
169
169
  const baseUrl = resolveBucket(parsed.bucket, runtime.env, profile.bucket);
170
- const imsToken = resolveOptionalImsToken(parsed.imsToken, runtime.env);
170
+ const imsToken = resolveOptionalImsToken(parsed.imsToken, runtime.env, profile.imsToken);
171
171
  const dimensions = resolveDimensions(parsed.size, parsed.width, parsed.height);
172
172
  verbose(runtime, `bucket: ${baseUrl}`);
173
173
  verbose(runtime, `asset: ${assetId}`);
@@ -227,7 +227,7 @@ async function handleSearch(options, runtime) {
227
227
  const parsed = parseWithSchema(searchSchema, options);
228
228
  const profile = await readProfileConfig(runtime.env);
229
229
  const baseUrl = resolveBucket(parsed.bucket, runtime.env, profile.bucket);
230
- const { imsToken, apiKey } = resolveSearchAuth(parsed.imsToken, parsed.apiKey, runtime.env);
230
+ const { imsToken, apiKey } = resolveSearchAuth(parsed.imsToken, parsed.apiKey, runtime.env, profile.imsToken);
231
231
  const searchBody = await buildSearchBody(parsed);
232
232
  const searchUrl = `${baseUrl}/search`;
233
233
  verbose(runtime, `bucket: ${baseUrl}`);
@@ -347,7 +347,7 @@ function buildProgram(runtime) {
347
347
  program
348
348
  .name("aemdm")
349
349
  .description("CLI for Adobe Dynamic Media with OpenAPI")
350
- .version("0.2.3")
350
+ .version("0.4.0")
351
351
  .showHelpAfterError()
352
352
  .option("-v, --verbose", "Show additional diagnostic output")
353
353
  .configureOutput({
@@ -401,7 +401,8 @@ Core commands:
401
401
  Important defaults:
402
402
  - Bucket comes from --bucket or AEMDM_BUCKET.
403
403
  - A standalone call like aemdm --bucket delivery-p123-e456.adobeaemcloud.com saves the default bucket to the local aemdm profile config.
404
- - Search auth comes from --ims-token/AEMDM_IMS_TOKEN and --api-key/AEMDM_API_KEY.
404
+ - aemdm --ims-token <token> saves the IMS token to the profile config. Both can be saved together.
405
+ - Search auth comes from --ims-token/AEMDM_IMS_TOKEN/profile config and --api-key/AEMDM_API_KEY.
405
406
  - asset get prints a URL by default.
406
407
  - asset get --metadata prints full JSON metadata when authenticated, or basic public JSON metadata when no token is supplied.
407
408
  - asset get --binary downloads the asset and requires --output.
@@ -444,14 +445,31 @@ LLM usage guidance:
444
445
  - Prefer --first-binary with --output when the user wants the downloaded file.
445
446
  `;
446
447
  }
447
- function getStandaloneBucketValue(argv) {
448
- if (argv.length === 1 && argv[0].startsWith("--bucket=")) {
449
- return argv[0].slice("--bucket=".length);
448
+ function parseStandaloneConfig(argv) {
449
+ const config = {};
450
+ const args = [...argv];
451
+ while (args.length > 0) {
452
+ const arg = args.shift();
453
+ if (arg.startsWith("--bucket=")) {
454
+ config.bucket = arg.slice("--bucket=".length);
455
+ }
456
+ else if (arg === "--bucket" && args.length > 0) {
457
+ config.bucket = args.shift();
458
+ }
459
+ else if (arg.startsWith("--ims-token=")) {
460
+ config.imsToken = arg.slice("--ims-token=".length);
461
+ }
462
+ else if (arg === "--ims-token" && args.length > 0) {
463
+ config.imsToken = args.shift();
464
+ }
465
+ else {
466
+ return undefined;
467
+ }
450
468
  }
451
- if (argv.length === 2 && argv[0] === "--bucket") {
452
- return argv[1];
469
+ if (!config.bucket && !config.imsToken) {
470
+ return undefined;
453
471
  }
454
- return undefined;
472
+ return config;
455
473
  }
456
474
  function toCliError(error) {
457
475
  if (error instanceof CliError) {
@@ -482,11 +500,21 @@ export async function runCli(argv, runtimeOverrides = {}) {
482
500
  ...runtimeOverrides,
483
501
  };
484
502
  try {
485
- const standaloneBucket = getStandaloneBucketValue(argv);
486
- if (standaloneBucket !== undefined) {
487
- const bucket = resolveBucket(standaloneBucket, runtime.env);
488
- const configPath = await writeProfileConfig(runtime.env, { bucket });
489
- writeLine(runtime.stdout, `Saved bucket to profile config: ${bucket}`);
503
+ const standaloneConfig = parseStandaloneConfig(argv);
504
+ if (standaloneConfig !== undefined) {
505
+ const existing = await readProfileConfig(runtime.env);
506
+ const merged = { ...existing };
507
+ if (standaloneConfig.bucket) {
508
+ merged.bucket = normalizeBucket(standaloneConfig.bucket);
509
+ }
510
+ if (standaloneConfig.imsToken) {
511
+ merged.imsToken = standaloneConfig.imsToken;
512
+ }
513
+ const configPath = await writeProfileConfig(runtime.env, merged);
514
+ if (merged.bucket)
515
+ writeLine(runtime.stdout, `Saved bucket: ${merged.bucket}`);
516
+ if (standaloneConfig.imsToken)
517
+ writeLine(runtime.stdout, `Saved IMS token to profile config`);
490
518
  writeLine(runtime.stdout, `Config file: ${configPath}`);
491
519
  return 0;
492
520
  }
@@ -40,11 +40,11 @@ export function resolveBucket(explicitValue, env, profileBucket) {
40
40
  }
41
41
  return normalizeBucket(bucket);
42
42
  }
43
- export function resolveOptionalImsToken(explicitValue, env) {
44
- return explicitValue ?? env.AEMDM_IMS_TOKEN;
43
+ export function resolveOptionalImsToken(explicitValue, env, profileImsToken) {
44
+ return explicitValue ?? env.AEMDM_IMS_TOKEN ?? profileImsToken;
45
45
  }
46
- export function resolveSearchAuth(imsToken, apiKey, env) {
47
- const resolvedImsToken = imsToken ?? env.AEMDM_IMS_TOKEN;
46
+ export function resolveSearchAuth(imsToken, apiKey, env, profileImsToken) {
47
+ const resolvedImsToken = imsToken ?? env.AEMDM_IMS_TOKEN ?? profileImsToken;
48
48
  const resolvedApiKey = apiKey ?? env.AEMDM_API_KEY ?? "asset_search_service";
49
49
  if (!resolvedImsToken) {
50
50
  throw new CliError("Missing IMS token. Use --ims-token or set AEMDM_IMS_TOKEN.");
@@ -3,6 +3,7 @@ import path from "node:path";
3
3
  import { z } from "zod";
4
4
  const profileSchema = z.object({
5
5
  bucket: z.string().optional(),
6
+ imsToken: z.string().optional(),
6
7
  });
7
8
  export function resolveConfigPath(env) {
8
9
  if (env.AEMDM_CONFIG_PATH) {
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { CliError } from "./client.js";
3
3
  export const DEFAULT_SEO_NAME = "asset";
4
- export const TRANSFORM_FALLBACK_FORMAT = "avif";
4
+ export const TRANSFORM_FALLBACK_FORMAT = "png";
5
5
  export const deliveryFormatSchema = z.enum(["gif", "png", "jpg", "jpeg", "webp", "avif"]);
6
6
  const qualitySchema = z.number().int().min(1).max(100);
7
7
  const dimensionSchema = z.number().int().min(1);
@@ -39,12 +39,12 @@ export function resolveDimensions(size, width, height) {
39
39
  };
40
40
  }
41
41
  export function buildMetadataUrl(baseUrl, assetId) {
42
- return `${baseUrl.replace(/\/+$/, "")}/${encodeURIComponent(assetId)}/metadata`;
42
+ return `${baseUrl.replace(/\/+$/, "")}/${assetId}/metadata`;
43
43
  }
44
44
  export function buildAssetUrl(baseUrl, options) {
45
45
  const seoName = options.seoName ?? DEFAULT_SEO_NAME;
46
46
  const normalizedBase = baseUrl.replace(/\/+$/, "");
47
- const encodedAssetId = encodeURIComponent(options.assetId);
47
+ const encodedAssetId = options.assetId;
48
48
  const encodedSeoName = encodeURIComponent(seoName);
49
49
  if (options.quality !== undefined) {
50
50
  qualitySchema.parse(options.quality);
@@ -66,22 +66,23 @@ export function buildAssetUrl(baseUrl, options) {
66
66
  options.height !== undefined ||
67
67
  options.quality !== undefined ||
68
68
  options.maxQuality !== undefined;
69
+ const format = options.format ?? TRANSFORM_FALLBACK_FORMAT;
69
70
  if (!shouldUseTransformRoute) {
70
- return `${normalizedBase}/${encodedAssetId}`;
71
+ return `${normalizedBase}/${encodedAssetId}/as/${encodedSeoName}.${format}`;
71
72
  }
72
- const format = options.format ?? TRANSFORM_FALLBACK_FORMAT;
73
- const url = new URL(`${normalizedBase}/${encodedAssetId}/as/${encodedSeoName}.${format}`);
73
+ const base = `${normalizedBase}/${encodedAssetId}/as/${encodedSeoName}.${format}`;
74
+ const params = [];
74
75
  if (options.width !== undefined) {
75
- url.searchParams.set("width", String(options.width));
76
+ params.push(`width=${options.width}`);
76
77
  }
77
78
  if (options.height !== undefined) {
78
- url.searchParams.set("height", String(options.height));
79
+ params.push(`height=${options.height}`);
79
80
  }
80
81
  if (options.quality !== undefined) {
81
- url.searchParams.set("quality", String(options.quality));
82
+ params.push(`quality=${options.quality}`);
82
83
  }
83
84
  if (options.maxQuality !== undefined) {
84
- url.searchParams.set("max-quality", String(options.maxQuality));
85
+ params.push(`max-quality=${options.maxQuality}`);
85
86
  }
86
- return url.toString();
87
+ return params.length > 0 ? `${base}?${params.join("&")}` : base;
87
88
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aemdm",
3
- "version": "0.2.3",
3
+ "version": "0.4.0",
4
4
  "description": "CLI for Adobe Dynamic Media with OpenAPI",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Chris Pilsworth",