@tenderprompt/cli 0.1.30 → 0.1.32

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 (2) hide show
  1. package/dist/index.js +84 -17
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2037,8 +2037,38 @@ function normalizeStoredProfile(value) {
2037
2037
  permissions: Array.isArray(record.permissions)
2038
2038
  ? record.permissions.filter((permission) => typeof permission === 'string')
2039
2039
  : undefined,
2040
+ ...normalizeStoredShopifyContext(record),
2040
2041
  };
2041
2042
  }
2043
+ function normalizeStoredShopifyContext(record) {
2044
+ const shopifyAppUrl = typeof record.shopifyAppUrl === 'string' ? record.shopifyAppUrl : null;
2045
+ if (shopifyAppUrl) {
2046
+ try {
2047
+ const parsed = parseShopifyAppUrl(shopifyAppUrl, 'profile.shopifyAppUrl');
2048
+ return {
2049
+ shopifyAppUrl: parsed.appUrl,
2050
+ shopifyStore: parsed.store,
2051
+ };
2052
+ }
2053
+ catch {
2054
+ return {};
2055
+ }
2056
+ }
2057
+ const shopifyStore = typeof record.shopifyStore === 'string' ? record.shopifyStore : null;
2058
+ if (shopifyStore) {
2059
+ try {
2060
+ const store = parseShopifyStoreHandle(shopifyStore, 'profile.shopifyStore');
2061
+ return {
2062
+ shopifyAppUrl: shopifyAppUrlForStore(store),
2063
+ shopifyStore: store,
2064
+ };
2065
+ }
2066
+ catch {
2067
+ return {};
2068
+ }
2069
+ }
2070
+ return {};
2071
+ }
2042
2072
  function normalizeConfig(value) {
2043
2073
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
2044
2074
  return {};
@@ -2148,20 +2178,19 @@ function parseShopifyStoreHandle(value, flag) {
2148
2178
  function shopifyAppUrlForStore(store) {
2149
2179
  return `https://admin.shopify.com/store/${store}/apps/tender-prompt/shopify`;
2150
2180
  }
2151
- function parseShopifyAppUrlFlag(args, index) {
2152
- const value = args[index + 1];
2181
+ function parseShopifyAppUrl(value, flag) {
2153
2182
  if (!value || value.startsWith('-')) {
2154
- throw new TenderCliUsageError('--shopify-app-url requires a Shopify Admin App Home URL.');
2183
+ throw new TenderCliUsageError(`${flag} requires a Shopify Admin App Home URL.`);
2155
2184
  }
2156
2185
  let url;
2157
2186
  try {
2158
2187
  url = new URL(value);
2159
2188
  }
2160
2189
  catch {
2161
- throw new TenderCliUsageError('--shopify-app-url requires a valid Shopify Admin App Home URL.');
2190
+ throw new TenderCliUsageError(`${flag} requires a valid Shopify Admin App Home URL.`);
2162
2191
  }
2163
2192
  if (url.protocol !== 'https:' || url.hostname !== 'admin.shopify.com') {
2164
- throw new TenderCliUsageError('--shopify-app-url must start with https://admin.shopify.com/.');
2193
+ throw new TenderCliUsageError(`${flag} must start with https://admin.shopify.com/.`);
2165
2194
  }
2166
2195
  const parts = url.pathname.split('/').filter(Boolean);
2167
2196
  const deviceSuffix = parts[5] === 'device' ? parts[5] : null;
@@ -2169,20 +2198,26 @@ function parseShopifyAppUrlFlag(args, index) {
2169
2198
  parts[0] !== 'store' ||
2170
2199
  parts[2] !== 'apps' ||
2171
2200
  parts[4] !== 'shopify') {
2172
- throw new TenderCliUsageError('--shopify-app-url must look like https://admin.shopify.com/store/<store>/apps/tender-prompt/shopify.');
2201
+ throw new TenderCliUsageError(`${flag} must look like https://admin.shopify.com/store/<store>/apps/tender-prompt/shopify.`);
2173
2202
  }
2174
2203
  if (url.search || url.hash) {
2175
- throw new TenderCliUsageError('--shopify-app-url should not include a query string or hash.');
2204
+ throw new TenderCliUsageError(`${flag} should not include a query string or hash.`);
2176
2205
  }
2177
- const store = parseShopifyStoreHandle(parts[1], '--shopify-app-url');
2178
- const appHandle = parseShopifyStoreHandle(parts[3], '--shopify-app-url');
2206
+ const store = parseShopifyStoreHandle(parts[1], flag);
2207
+ const appHandle = parseShopifyStoreHandle(parts[3], flag);
2179
2208
  return {
2180
2209
  store,
2181
2210
  appUrl: `https://admin.shopify.com/store/${store}/apps/${appHandle}/shopify`,
2182
2211
  };
2183
2212
  }
2213
+ function parseShopifyAppUrlFlag(args, index) {
2214
+ return parseShopifyAppUrl(args[index + 1], '--shopify-app-url');
2215
+ }
2216
+ function shopifyDeviceVerificationUri(shopifyAppUrl) {
2217
+ return `${shopifyAppUrl.replace(/\/+$/, '')}/device`;
2218
+ }
2184
2219
  function shopifyDeviceVerificationUrl(input) {
2185
- return `${input.shopifyAppUrl.replace(/\/+$/, '')}/device?user_code=${encodeURIComponent(input.userCode)}`;
2220
+ return `${shopifyDeviceVerificationUri(input.shopifyAppUrl)}?user_code=${encodeURIComponent(input.userCode)}`;
2186
2221
  }
2187
2222
  function parseTokenFlag(args, index) {
2188
2223
  const value = args[index + 1];
@@ -8013,6 +8048,11 @@ function readObjectString(value, key) {
8013
8048
  const field = record?.[key];
8014
8049
  return typeof field === 'string' && field.trim() ? field : null;
8015
8050
  }
8051
+ function readObjectArray(value, key) {
8052
+ const record = readObject(value);
8053
+ const field = record?.[key];
8054
+ return Array.isArray(field) ? field : [];
8055
+ }
8016
8056
  function readPreviewUrlFromJob(job) {
8017
8057
  return readObjectString(job?.result, 'previewUrl');
8018
8058
  }
@@ -8037,8 +8077,22 @@ function readReleaseGitRefFromResult(result) {
8037
8077
  function formatPublishResultLines(result) {
8038
8078
  const record = readObject(result);
8039
8079
  const releaseGitRef = readReleaseGitRefFromResult(result);
8080
+ const publishedAssets = readObjectArray(record, 'publishedAssets');
8081
+ const pinnedClientAsset = publishedAssets
8082
+ .map((asset) => readObject(asset))
8083
+ .find((asset) => asset?.sourcePath === 'client.js' && readObjectString(asset, 'pinnedUrl')) ??
8084
+ publishedAssets
8085
+ .map((asset) => readObject(asset))
8086
+ .find((asset) => asset?.assetPath === 'client.js' && readObjectString(asset, 'pinnedUrl'));
8087
+ const integrationSnippets = readObject(record?.integrationSnippets);
8088
+ const pinnedClientUrl = readObjectString(pinnedClientAsset, 'pinnedUrl');
8089
+ const pinnedModuleScript = readObjectString(integrationSnippets, 'moduleScript');
8040
8090
  return [
8041
8091
  ...(readObjectString(record, 'publishedUrl') ? [`published_url: ${readObjectString(record, 'publishedUrl')}`] : []),
8092
+ ...(readObjectString(record, 'versionUrl') ? [`version_url: ${readObjectString(record, 'versionUrl')}`] : []),
8093
+ ...(readObjectString(record, 'releaseId') ? [`release_id: ${readObjectString(record, 'releaseId')}`] : []),
8094
+ ...(pinnedClientUrl ? [`pinned_client_asset: ${pinnedClientUrl}`] : []),
8095
+ ...(pinnedModuleScript ? [`pinned_module_script: ${pinnedModuleScript}`] : []),
8042
8096
  ...(releaseGitRef?.tagName ? [`release_tag: ${releaseGitRef.tagName}`] : []),
8043
8097
  ...(releaseGitRef?.commitSha ? [`source_commit: ${releaseGitRef.commitSha}`] : []),
8044
8098
  ];
@@ -8226,6 +8280,8 @@ async function runAuthStatus(command, io, runtime) {
8226
8280
  artifactId: profile?.artifactId ?? null,
8227
8281
  permissions: profile?.permissions ?? [],
8228
8282
  expiresAt: formatExpiry(profile?.expiresAt),
8283
+ shopifyAppUrl: profile?.shopifyAppUrl ?? null,
8284
+ shopifyStore: profile?.shopifyStore ?? null,
8229
8285
  };
8230
8286
  if (command.json) {
8231
8287
  io.stdout(JSON.stringify(result, null, 2));
@@ -8240,6 +8296,7 @@ async function runAuthStatus(command, io, runtime) {
8240
8296
  `app_id: ${profile.artifactId ?? '(account)'}`,
8241
8297
  `permissions: ${(profile.permissions ?? []).join(' ') || '(unknown)'}`,
8242
8298
  `expires_at: ${formatExpiry(profile.expiresAt) ?? '(unknown)'}`,
8299
+ `shopify_app_url: ${profile.shopifyAppUrl ?? '(none)'}`,
8243
8300
  ].join('\n'));
8244
8301
  }
8245
8302
  else {
@@ -8284,6 +8341,8 @@ async function runAuthStatus(command, io, runtime) {
8284
8341
  artifactId: profile?.artifactId ?? null,
8285
8342
  permissions: profile?.permissions ?? [],
8286
8343
  expiresAt: formatExpiry(profile?.expiresAt),
8344
+ shopifyAppUrl: profile?.shopifyAppUrl ?? null,
8345
+ shopifyStore: profile?.shopifyStore ?? null,
8287
8346
  };
8288
8347
  if (command.json) {
8289
8348
  io.stdout(JSON.stringify(result, null, 2));
@@ -8298,6 +8357,7 @@ async function runAuthStatus(command, io, runtime) {
8298
8357
  `app_id: ${profile.artifactId ?? '(account)'}`,
8299
8358
  `permissions: ${(profile.permissions ?? []).join(' ') || '(unknown)'}`,
8300
8359
  `expires_at: ${formatExpiry(profile.expiresAt) ?? '(unknown)'}`,
8360
+ `shopify_app_url: ${profile.shopifyAppUrl ?? '(none)'}`,
8301
8361
  ].join('\n'));
8302
8362
  }
8303
8363
  else {
@@ -8314,7 +8374,11 @@ async function runAuthLogin(command, io, runtime) {
8314
8374
  const fetcher = runtime.fetcher ?? fetch;
8315
8375
  const sleeper = runtime.sleeper ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
8316
8376
  const now = runtime.now ?? Date.now;
8317
- const baseUrl = command.baseUrl ?? readDefaultBaseUrl(env);
8377
+ const config = await readConfig(env);
8378
+ const existingProfile = config.profiles?.[command.saveProfile] ?? null;
8379
+ const baseUrl = command.baseUrl ?? existingProfile?.baseUrl ?? readDefaultBaseUrl(env);
8380
+ const shopifyAppUrl = command.shopifyAppUrl ?? existingProfile?.shopifyAppUrl ?? null;
8381
+ const shopifyStore = command.shopifyStore ?? existingProfile?.shopifyStore ?? null;
8318
8382
  const deviceResponse = await fetcher(`${baseUrl}/oauth/device/code`, {
8319
8383
  method: 'POST',
8320
8384
  headers: {
@@ -8330,19 +8394,19 @@ async function runAuthLogin(command, io, runtime) {
8330
8394
  if (!deviceResponse.ok || !devicePayload?.device_code || !devicePayload.user_code || !devicePayload.verification_uri_complete) {
8331
8395
  throw new Error(devicePayload?.error_description ?? devicePayload?.error ?? `Device authorization failed with HTTP ${deviceResponse.status}.`);
8332
8396
  }
8333
- const verificationUriComplete = command.shopifyAppUrl
8397
+ const verificationUriComplete = shopifyAppUrl
8334
8398
  ? shopifyDeviceVerificationUrl({
8335
- shopifyAppUrl: command.shopifyAppUrl,
8399
+ shopifyAppUrl,
8336
8400
  userCode: devicePayload.user_code,
8337
8401
  })
8338
8402
  : devicePayload.verification_uri_complete;
8339
- const verificationUri = command.shopifyAppUrl ? `${command.shopifyAppUrl}/device` : devicePayload.verification_uri;
8403
+ const verificationUri = shopifyAppUrl ? shopifyDeviceVerificationUri(shopifyAppUrl) : devicePayload.verification_uri;
8340
8404
  const pendingResult = {
8341
8405
  ok: true,
8342
8406
  command: command.command,
8343
8407
  baseUrl,
8344
- shopifyAppUrl: command.shopifyAppUrl,
8345
- shopifyStore: command.shopifyStore,
8408
+ shopifyAppUrl,
8409
+ shopifyStore,
8346
8410
  profile: command.tokenProfile,
8347
8411
  saveProfile: command.saveProfile,
8348
8412
  userCode: devicePayload.user_code,
@@ -8383,7 +8447,6 @@ async function runAuthLogin(command, io, runtime) {
8383
8447
  });
8384
8448
  const tokenPayload = await parseJsonResponse(tokenResponse);
8385
8449
  if (tokenResponse.ok && tokenPayload?.access_token) {
8386
- const config = await readConfig(env);
8387
8450
  const profiles = config.profiles ?? {};
8388
8451
  profiles[command.saveProfile] = {
8389
8452
  baseUrl,
@@ -8393,6 +8456,8 @@ async function runAuthLogin(command, io, runtime) {
8393
8456
  artifactId: tokenPayload.artifact_id,
8394
8457
  expiresAt: tokenPayload.expires_at,
8395
8458
  permissions: splitScope(tokenPayload.scope),
8459
+ ...(shopifyAppUrl ? { shopifyAppUrl } : {}),
8460
+ ...(shopifyStore ? { shopifyStore } : {}),
8396
8461
  };
8397
8462
  await writeConfig(env, {
8398
8463
  ...config,
@@ -8410,6 +8475,8 @@ async function runAuthLogin(command, io, runtime) {
8410
8475
  artifactId: tokenPayload.artifact_id ?? null,
8411
8476
  permissions: splitScope(tokenPayload.scope),
8412
8477
  expiresAt: formatExpiry(tokenPayload.expires_at),
8478
+ shopifyAppUrl,
8479
+ shopifyStore,
8413
8480
  configPath: readConfigPath(env),
8414
8481
  };
8415
8482
  if (command.json) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenderprompt/cli",
3
- "version": "0.1.30",
3
+ "version": "0.1.32",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "tender": "bin/tender.js"