@solana-mobile/dapp-store-cli 0.15.0 → 0.16.1

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 (105) hide show
  1. package/bin/dapp-store.js +3 -1
  2. package/lib/CliSetup.js +304 -505
  3. package/lib/CliUtils.js +6 -376
  4. package/lib/__tests__/CliSetupTest.js +484 -74
  5. package/lib/cli/__tests__/parseErrors.test.js +25 -0
  6. package/lib/cli/__tests__/signer.test.js +436 -0
  7. package/lib/cli/constants.js +23 -0
  8. package/lib/cli/messages.js +21 -0
  9. package/lib/cli/parseErrors.js +41 -0
  10. package/lib/{commands/publish/PublishCliSupport.js → cli/selfUpdate.js} +72 -38
  11. package/lib/{commands/publish/PublishCliRemove.js → cli/signer.js} +35 -56
  12. package/lib/index.js +96 -5
  13. package/lib/package.json +5 -24
  14. package/lib/portal/__tests__/releaseMetadata.test.js +647 -0
  15. package/lib/portal/__tests__/translators.test.js +76 -0
  16. package/lib/portal/__tests__/workflowClient.test.js +457 -0
  17. package/lib/portal/attestationClient.js +143 -0
  18. package/lib/portal/files.js +64 -0
  19. package/lib/portal/http.js +364 -0
  20. package/lib/portal/records.js +64 -0
  21. package/lib/portal/releaseMetadata.js +748 -0
  22. package/lib/portal/translators.js +460 -0
  23. package/lib/portal/types.js +1 -0
  24. package/lib/portal/workflowClient.js +704 -0
  25. package/lib/publication/PublicationProgressReporter.js +1051 -0
  26. package/lib/publication/__tests__/PublicationProgressReporter.test.js +174 -0
  27. package/lib/{commands/ValidateCommand.js → publication/__tests__/fundingPreflight.test.js} +90 -66
  28. package/lib/publication/__tests__/publicationSummary.test.js +26 -0
  29. package/lib/publication/cliValidation.js +482 -0
  30. package/lib/publication/fundingPreflight.js +246 -0
  31. package/lib/publication/publicationSummary.js +99 -0
  32. package/lib/{commands/utils.js → publication/runPublicationWorkflow.js} +16 -46
  33. package/package.json +5 -24
  34. package/src/CliSetup.ts +370 -505
  35. package/src/CliUtils.ts +9 -233
  36. package/src/__tests__/CliSetupTest.ts +272 -120
  37. package/src/cli/__tests__/parseErrors.test.ts +34 -0
  38. package/src/cli/__tests__/signer.test.ts +359 -0
  39. package/src/cli/constants.ts +3 -0
  40. package/src/cli/messages.ts +27 -0
  41. package/src/cli/parseErrors.ts +62 -0
  42. package/src/cli/selfUpdate.ts +59 -0
  43. package/src/cli/signer.ts +38 -0
  44. package/src/index.ts +31 -4
  45. package/src/portal/__tests__/releaseMetadata.test.ts +508 -0
  46. package/src/portal/__tests__/translators.test.ts +82 -0
  47. package/src/portal/__tests__/workflowClient.test.ts +278 -0
  48. package/src/portal/attestationClient.ts +19 -0
  49. package/src/portal/files.ts +73 -0
  50. package/src/portal/http.ts +170 -0
  51. package/src/portal/records.ts +38 -0
  52. package/src/portal/releaseMetadata.ts +489 -0
  53. package/src/portal/translators.ts +750 -0
  54. package/src/portal/types.ts +27 -0
  55. package/src/portal/workflowClient.ts +575 -0
  56. package/src/publication/PublicationProgressReporter.ts +1026 -0
  57. package/src/publication/__tests__/PublicationProgressReporter.test.ts +210 -0
  58. package/src/publication/__tests__/fundingPreflight.test.ts +78 -0
  59. package/src/publication/__tests__/publicationSummary.test.ts +30 -0
  60. package/src/publication/cliValidation.ts +264 -0
  61. package/src/publication/fundingPreflight.ts +123 -0
  62. package/src/publication/publicationSummary.ts +26 -0
  63. package/src/publication/runPublicationWorkflow.ts +46 -0
  64. package/lib/commands/create/CreateCliApp.js +0 -223
  65. package/lib/commands/create/CreateCliRelease.js +0 -290
  66. package/lib/commands/create/index.js +0 -40
  67. package/lib/commands/index.js +0 -3
  68. package/lib/commands/publish/PublishCliSubmit.js +0 -208
  69. package/lib/commands/publish/PublishCliUpdate.js +0 -211
  70. package/lib/commands/publish/index.js +0 -22
  71. package/lib/commands/scaffolding/ScaffoldInit.js +0 -15
  72. package/lib/commands/scaffolding/index.js +0 -1
  73. package/lib/config/EnvVariables.js +0 -59
  74. package/lib/config/PublishDetails.js +0 -915
  75. package/lib/config/S3StorageManager.js +0 -93
  76. package/lib/config/index.js +0 -2
  77. package/lib/generated/config_obj.json +0 -1
  78. package/lib/generated/config_schema.json +0 -1
  79. package/lib/prebuild_schema/publishing_source.yaml +0 -64
  80. package/lib/prebuild_schema/schemagen.js +0 -25
  81. package/lib/upload/CachedStorageDriver.js +0 -293
  82. package/lib/upload/TurboStorageDriver.js +0 -718
  83. package/lib/upload/index.js +0 -2
  84. package/src/commands/ValidateCommand.ts +0 -82
  85. package/src/commands/create/CreateCliApp.ts +0 -93
  86. package/src/commands/create/CreateCliRelease.ts +0 -149
  87. package/src/commands/create/index.ts +0 -47
  88. package/src/commands/index.ts +0 -3
  89. package/src/commands/publish/PublishCliRemove.ts +0 -66
  90. package/src/commands/publish/PublishCliSubmit.ts +0 -93
  91. package/src/commands/publish/PublishCliSupport.ts +0 -66
  92. package/src/commands/publish/PublishCliUpdate.ts +0 -101
  93. package/src/commands/publish/index.ts +0 -29
  94. package/src/commands/scaffolding/ScaffoldInit.ts +0 -20
  95. package/src/commands/scaffolding/index.ts +0 -1
  96. package/src/commands/utils.ts +0 -33
  97. package/src/config/EnvVariables.ts +0 -39
  98. package/src/config/PublishDetails.ts +0 -456
  99. package/src/config/S3StorageManager.ts +0 -47
  100. package/src/config/index.ts +0 -2
  101. package/src/prebuild_schema/publishing_source.yaml +0 -64
  102. package/src/prebuild_schema/schemagen.js +0 -31
  103. package/src/upload/CachedStorageDriver.ts +0 -99
  104. package/src/upload/TurboStorageDriver.ts +0 -277
  105. package/src/upload/index.ts +0 -2
@@ -1,20 +1,231 @@
1
- import { beforeEach, expect } from "@jest/globals";
2
- import { createAppCliCmd, createCliCmd, createReleaseCliCmd, initCliCmd, mainCli } from "../CliSetup";
3
- import { Constants } from "../CliUtils";
4
- describe("Cli Setup & Execution", function() {
1
+ function _array_like_to_array(arr, len) {
2
+ if (len == null || len > arr.length) len = arr.length;
3
+ for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
4
+ return arr2;
5
+ }
6
+ function _array_with_holes(arr) {
7
+ if (Array.isArray(arr)) return arr;
8
+ }
9
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
10
+ try {
11
+ var info = gen[key](arg);
12
+ var value = info.value;
13
+ } catch (error) {
14
+ reject(error);
15
+ return;
16
+ }
17
+ if (info.done) {
18
+ resolve(value);
19
+ } else {
20
+ Promise.resolve(value).then(_next, _throw);
21
+ }
22
+ }
23
+ function _async_to_generator(fn) {
24
+ return function() {
25
+ var self = this, args = arguments;
26
+ return new Promise(function(resolve, reject) {
27
+ var gen = fn.apply(self, args);
28
+ function _next(value) {
29
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
30
+ }
31
+ function _throw(err) {
32
+ asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
33
+ }
34
+ _next(undefined);
35
+ });
36
+ };
37
+ }
38
+ function _iterable_to_array_limit(arr, i) {
39
+ var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
40
+ if (_i == null) return;
41
+ var _arr = [];
42
+ var _n = true;
43
+ var _d = false;
44
+ var _s, _e;
45
+ try {
46
+ for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
47
+ _arr.push(_s.value);
48
+ if (i && _arr.length === i) break;
49
+ }
50
+ } catch (err) {
51
+ _d = true;
52
+ _e = err;
53
+ } finally{
54
+ try {
55
+ if (!_n && _i["return"] != null) _i["return"]();
56
+ } finally{
57
+ if (_d) throw _e;
58
+ }
59
+ }
60
+ return _arr;
61
+ }
62
+ function _non_iterable_rest() {
63
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
64
+ }
65
+ function _sliced_to_array(arr, i) {
66
+ return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
67
+ }
68
+ function _unsupported_iterable_to_array(o, minLen) {
69
+ if (!o) return;
70
+ if (typeof o === "string") return _array_like_to_array(o, minLen);
71
+ var n = Object.prototype.toString.call(o).slice(8, -1);
72
+ if (n === "Object" && o.constructor) n = o.constructor.name;
73
+ if (n === "Map" || n === "Set") return Array.from(n);
74
+ if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
75
+ }
76
+ function _ts_generator(thisArg, body) {
77
+ var f, y, t, _ = {
78
+ label: 0,
79
+ sent: function() {
80
+ if (t[0] & 1) throw t[1];
81
+ return t[1];
82
+ },
83
+ trys: [],
84
+ ops: []
85
+ }, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
86
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() {
87
+ return this;
88
+ }), g;
89
+ function verb(n) {
90
+ return function(v) {
91
+ return step([
92
+ n,
93
+ v
94
+ ]);
95
+ };
96
+ }
97
+ function step(op) {
98
+ if (f) throw new TypeError("Generator is already executing.");
99
+ while(g && (g = 0, op[0] && (_ = 0)), _)try {
100
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
101
+ if (y = 0, t) op = [
102
+ op[0] & 2,
103
+ t.value
104
+ ];
105
+ switch(op[0]){
106
+ case 0:
107
+ case 1:
108
+ t = op;
109
+ break;
110
+ case 4:
111
+ _.label++;
112
+ return {
113
+ value: op[1],
114
+ done: false
115
+ };
116
+ case 5:
117
+ _.label++;
118
+ y = op[1];
119
+ op = [
120
+ 0
121
+ ];
122
+ continue;
123
+ case 7:
124
+ op = _.ops.pop();
125
+ _.trys.pop();
126
+ continue;
127
+ default:
128
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
129
+ _ = 0;
130
+ continue;
131
+ }
132
+ if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
133
+ _.label = op[1];
134
+ break;
135
+ }
136
+ if (op[0] === 6 && _.label < t[1]) {
137
+ _.label = t[1];
138
+ t = op;
139
+ break;
140
+ }
141
+ if (t && _.label < t[2]) {
142
+ _.label = t[2];
143
+ _.ops.push(op);
144
+ break;
145
+ }
146
+ if (t[2]) _.ops.pop();
147
+ _.trys.pop();
148
+ continue;
149
+ }
150
+ op = body.call(thisArg, _);
151
+ } catch (e) {
152
+ op = [
153
+ 6,
154
+ e
155
+ ];
156
+ y = 0;
157
+ } finally{
158
+ f = t = 0;
159
+ }
160
+ if (op[0] & 5) throw op[1];
161
+ return {
162
+ value: op[0] ? op[1] : void 0,
163
+ done: true
164
+ };
165
+ }
166
+ }
167
+ import fs from "node:fs";
168
+ import os from "node:os";
169
+ import path from "node:path";
170
+ import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
171
+ import { mainCli } from "../CliSetup";
172
+ import { DEFAULT_API_KEY_ENV, DEFAULT_LOCAL_PORTAL_URL, DEFAULT_PRODUCTION_PORTAL_URL, UPDATED_PUBLISHING_CLI_DOCS_URL, formatUpdatedCliUsageError, resolveApiKey, resolvePortalTargets, validateNewVersionArgs, validateResumeArgs } from "../publication/cliValidation";
173
+ describe("CLI surface", function() {
174
+ var createTempApkFile = function createTempApkFile() {
175
+ var fileName = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "app-release.apk";
176
+ var apkPath = path.join(tempDir, fileName);
177
+ fs.writeFileSync(apkPath, "apk");
178
+ return apkPath;
179
+ };
5
180
  var outputHelpReference = "(outputHelp)";
181
+ var trackedEnvKeys = [
182
+ DEFAULT_API_KEY_ENV,
183
+ "ALT_DAPP_STORE_API_KEY",
184
+ "DAPP_STORE_PORTAL_URL",
185
+ "DAPP_STORE_PORTAL_WEB_URL",
186
+ "DAPP_STORE_PORTAL_API_BASE_URL"
187
+ ];
6
188
  var errorOutput = "";
7
189
  var otherOutput = "";
190
+ var tempDir = "";
191
+ var originalEnv = {};
8
192
  beforeEach(function() {
9
193
  errorOutput = "";
10
194
  otherOutput = "";
195
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "dapp-store-cli-test-"));
196
+ originalEnv = Object.fromEntries(trackedEnvKeys.map(function(key) {
197
+ return [
198
+ key,
199
+ process.env[key]
200
+ ];
201
+ }));
202
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
203
+ try {
204
+ for(var _iterator = trackedEnvKeys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
205
+ var key = _step.value;
206
+ delete process.env[key];
207
+ }
208
+ } catch (err) {
209
+ _didIteratorError = true;
210
+ _iteratorError = err;
211
+ } finally{
212
+ try {
213
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
214
+ _iterator.return();
215
+ }
216
+ } finally{
217
+ if (_didIteratorError) {
218
+ throw _iteratorError;
219
+ }
220
+ }
221
+ }
11
222
  mainCli.exitOverride();
12
223
  mainCli.configureOutput({
13
224
  getOutHelpWidth: function getOutHelpWidth() {
14
- return 250;
225
+ return 200;
15
226
  },
16
227
  getErrHelpWidth: function getErrHelpWidth() {
17
- return 250;
228
+ return 200;
18
229
  },
19
230
  writeOut: function writeOut(str) {
20
231
  otherOutput += str;
@@ -23,95 +234,294 @@ describe("Cli Setup & Execution", function() {
23
234
  errorOutput += str;
24
235
  }
25
236
  });
237
+ process.exitCode = 0;
26
238
  });
27
- test("Cli version argument reports correct version", function() {
239
+ afterEach(function() {
240
+ fs.rmSync(tempDir, {
241
+ recursive: true,
242
+ force: true
243
+ });
244
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
245
+ try {
246
+ for(var _iterator = Object.entries(originalEnv)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
247
+ var _step_value = _sliced_to_array(_step.value, 2), key = _step_value[0], value = _step_value[1];
248
+ if (typeof value === "undefined") {
249
+ delete process.env[key];
250
+ } else {
251
+ process.env[key] = value;
252
+ }
253
+ }
254
+ } catch (err) {
255
+ _didIteratorError = true;
256
+ _iteratorError = err;
257
+ } finally{
258
+ try {
259
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
260
+ _iterator.return();
261
+ }
262
+ } finally{
263
+ if (_didIteratorError) {
264
+ throw _iteratorError;
265
+ }
266
+ }
267
+ }
268
+ });
269
+ test("version reports the package version", function() {
28
270
  expect(function() {
29
271
  mainCli.parse([
30
- "npx",
272
+ "node",
31
273
  "dapp-store",
32
274
  "-V"
33
275
  ]);
34
- }).toThrow(Constants.CLI_VERSION);
276
+ }).toThrow();
35
277
  });
36
- test("Calling cli with no parameters displays general help", function() {
278
+ test("help advertises the default publication surface", function() {
37
279
  expect(function() {
38
280
  mainCli.parse([
39
- "npx",
40
- "dapp-store"
281
+ "node",
282
+ "dapp-store",
283
+ "--help"
41
284
  ]);
42
285
  }).toThrow(outputHelpReference);
43
- expect(generalHelp).toEqual(errorOutput);
286
+ expect(otherOutput).not.toContain("--new-version");
287
+ expect(otherOutput).toContain("resume");
288
+ expect(otherOutput).toContain("--apk-file");
289
+ expect(otherOutput).toContain("--apk-url");
290
+ expect(otherOutput).toContain("--keypair");
291
+ expect(otherOutput).toContain("--portal-url");
292
+ expect(otherOutput).not.toContain("--dapp-id");
293
+ expect(otherOutput).not.toContain("--fee-payer-keypair");
294
+ expect(otherOutput).not.toContain("--signer-keypair");
295
+ expect(errorOutput).toBe("");
44
296
  });
45
- test("Calling init command with help parameter shows contextual help info", function() {
46
- initCliCmd.exitOverride();
297
+ test("new-version validation rejects ambiguous APK sources", function() {
47
298
  expect(function() {
48
- initCliCmd.parse([
49
- "dapp-store",
50
- "init",
51
- "-h"
52
- ]);
53
- }).toThrow(outputHelpReference);
54
- expect(otherOutput).toEqual(initHelp);
299
+ return validateNewVersionArgs({
300
+ apkFile: createTempApkFile(),
301
+ apkUrl: "https://example.com/app.apk",
302
+ whatsNew: "Fixes",
303
+ keypair: "/tmp/signer.json"
304
+ });
305
+ }).toThrow("exactly one of `--apk-file` or `--apk-url`");
55
306
  });
56
- test("Calling create command with no options lists all options", function() {
57
- createCliCmd.exitOverride();
307
+ test("new-version validation rejects missing APK source", function() {
58
308
  expect(function() {
59
- createCliCmd.parse([
60
- "dapp-store",
61
- "create"
62
- ]);
63
- }).toThrow(outputHelpReference);
64
- expect(errorOutput).toEqual(createHelp);
309
+ return validateNewVersionArgs({
310
+ whatsNew: "Fixes",
311
+ keypair: "/tmp/signer.json"
312
+ });
313
+ }).toThrow("exactly one of `--apk-file` or `--apk-url`");
65
314
  });
66
- test("Calling create app command with no arguments warns about required argument", function() {
67
- createAppCliCmd.exitOverride();
315
+ test("new-version validation rejects blank release notes", function() {
68
316
  expect(function() {
69
- createAppCliCmd.parse([
70
- "dapp-store",
71
- "create",
72
- "app"
73
- ]);
74
- }).toThrow(keyPairArgHelp);
317
+ return validateNewVersionArgs({
318
+ apkUrl: "https://example.com/app.apk",
319
+ whatsNew: " ",
320
+ keypair: "/tmp/signer.json"
321
+ });
322
+ }).toThrow("`--whats-new` is required.");
75
323
  });
76
- test("Calling create app command with help flag shows contextual help", function() {
77
- createAppCliCmd.exitOverride();
324
+ test("new-version validation rejects missing keypair", function() {
78
325
  expect(function() {
79
- createAppCliCmd.parse([
80
- "dapp-store",
81
- "create",
82
- "app",
83
- "-h"
84
- ]);
85
- }).toThrow(outputHelpReference);
86
- expect(otherOutput).toEqual(createAppHelp);
326
+ return validateNewVersionArgs({
327
+ apkUrl: "https://example.com/app.apk",
328
+ whatsNew: "Fixes"
329
+ });
330
+ }).toThrow("`--keypair` is required.");
87
331
  });
88
- test("Calling create release command with no arguments warns about required argument", function() {
89
- createReleaseCliCmd.exitOverride();
332
+ test("new-version validation rejects a missing local APK file", function() {
90
333
  expect(function() {
91
- createReleaseCliCmd.parse([
92
- "dapp-store",
93
- "create",
94
- "release"
95
- ]);
96
- }).toThrow(keyPairArgHelp);
334
+ return validateNewVersionArgs({
335
+ apkFile: path.join(tempDir, "missing.apk"),
336
+ whatsNew: "Fixes",
337
+ keypair: "/tmp/signer.json"
338
+ });
339
+ }).toThrow("APK file not found");
97
340
  });
98
- test("Calling create release command with help flag shows contextual help", function() {
99
- createReleaseCliCmd.exitOverride();
341
+ test("new-version validation rejects non-HTTPS APK URLs", function() {
100
342
  expect(function() {
101
- createReleaseCliCmd.parse([
102
- "dapp-store",
103
- "create",
104
- "release",
105
- "-h"
106
- ]);
107
- }).toThrow(outputHelpReference);
108
- expect(otherOutput).toEqual(createReleaseHelp);
109
- });
110
- //--------------------------------------------------
111
- var generalHelp = "Usage: dapp-store [options] [command]\n\nCLI to assist with publishing to the Saga Dapp Store\n\nOptions:\n -V, --version output the version number\n -h, --help display help for command\n\nCommands:\n init First-time initialization of tooling configuration\n create Create a `app`, or `release`\n validate [options] Validates details prior to publishing\n publish Submit a publishing request (`submit`, `update`, `remove`, or `support`) to the Solana Mobile dApp publisher portal\n help [command] display help for command\n";
112
- var initHelp = "Usage: dapp-store init [options]\n\nFirst-time initialization of tooling configuration\n\nOptions:\n -h, --help display help for command\n";
113
- var keyPairArgHelp = "error: required option '-k, --keypair <path-to-keypair-file>' not specified";
114
- var createHelp = "Usage: dapp-store create [options] [command]\n\nCreate a `app`, or `release`\n\nOptions:\n -h, --help display help for command\n\nCommands:\n app [options] Create a app\n release [options] Create a release\n help [command] display help for command\n\nRelease metadata notes:\n We include publisher.support_email when provided; if omitted we fall back to publisher.email.\n";
115
- var createAppHelp = 'Usage: dapp-store create app [options]\n\nCreate a app\n\nOptions:\n -k, --keypair <path-to-keypair-file> Path to keypair file\n -u, --url <url> RPC URL (default: "https://api.devnet.solana.com")\n -d, --dry-run Flag for dry run. Doesn\'t mint an NFT\n -s, --storage-config <storage-config> Provide alternative storage configuration details\n -p, --priority-fee-lamports <priority-fee-lamports> Priority Fee lamports\n -h, --help display help for command\n';
116
- var createReleaseHelp = 'Usage: dapp-store create release [options]\n\nCreate a release\n\nOptions:\n -k, --keypair <path-to-keypair-file> Path to keypair file\n -a, --app-mint-address <app-mint-address> The mint address of the app NFT\n -u, --url <url> RPC URL (default: "https://api.devnet.solana.com")\n -d, --dry-run Flag for dry run. Doesn\'t mint an NFT\n -b, --build-tools-path <build-tools-path> Path to Android build tools which contains AAPT2\n -s, --storage-config <storage-config> Provide alternative storage configuration details\n -p, --priority-fee-lamports <priority-fee-lamports> Priority Fee lamports\n -h, --help display help for command\n';
343
+ return validateNewVersionArgs({
344
+ apkUrl: "http://example.com/app.apk",
345
+ whatsNew: "Fixes",
346
+ keypair: "/tmp/signer.json"
347
+ });
348
+ }).toThrow("`--apk-url` must use HTTPS.");
349
+ });
350
+ test("new-version validation accepts an existing APK file", function() {
351
+ expect(function() {
352
+ return validateNewVersionArgs({
353
+ apkFile: createTempApkFile(),
354
+ whatsNew: "Fixes",
355
+ keypair: "/tmp/signer.json"
356
+ });
357
+ }).not.toThrow();
358
+ });
359
+ test("new-version validation accepts a single HTTPS APK URL", function() {
360
+ expect(function() {
361
+ return validateNewVersionArgs({
362
+ apkUrl: "https://example.com/app.apk",
363
+ whatsNew: "Fixes",
364
+ keypair: "/tmp/signer.json"
365
+ });
366
+ }).not.toThrow();
367
+ });
368
+ test("resume validation requires a single target", function() {
369
+ expect(function() {
370
+ return validateResumeArgs({
371
+ releaseId: "release-1",
372
+ sessionId: "session-1",
373
+ keypair: "/tmp/signer.json"
374
+ });
375
+ }).toThrow("exactly one of `--release-id` or `--session-id`");
376
+ });
377
+ test("resume validation accepts alias flags", function() {
378
+ expect(function() {
379
+ return validateResumeArgs({
380
+ resumeRelease: "release-1",
381
+ keypair: "/tmp/signer.json"
382
+ });
383
+ }).not.toThrow();
384
+ expect(function() {
385
+ return validateResumeArgs({
386
+ resumeSession: "session-1",
387
+ keypair: "/tmp/signer.json"
388
+ });
389
+ }).not.toThrow();
390
+ });
391
+ test("resume validation rejects conflicting release aliases", function() {
392
+ expect(function() {
393
+ return validateResumeArgs({
394
+ releaseId: "release-1",
395
+ resumeRelease: "release-2",
396
+ keypair: "/tmp/signer.json"
397
+ });
398
+ }).toThrow("Conflicting values were provided for --release-id and --resume-release.");
399
+ });
400
+ test("resume validation accepts a release id", function() {
401
+ expect(function() {
402
+ return validateResumeArgs({
403
+ releaseId: "release-1",
404
+ keypair: "/tmp/signer.json"
405
+ });
406
+ }).not.toThrow();
407
+ });
408
+ test("portal targets default to production when unset", function() {
409
+ var targets = resolvePortalTargets({});
410
+ expect(targets.apiBaseUrl).toBe("".concat(DEFAULT_PRODUCTION_PORTAL_URL, "/api"));
411
+ });
412
+ test("portal targets default to localhost in local-dev mode", function() {
413
+ var targets = resolvePortalTargets({
414
+ localDev: true
415
+ });
416
+ expect(targets.apiBaseUrl).toBe("".concat(DEFAULT_LOCAL_PORTAL_URL, "/api"));
417
+ });
418
+ test("portal targets derive the API base URL from the configured portal URL", function() {
419
+ var targets = resolvePortalTargets({
420
+ portalUrl: "https://staging.publish.solanamobile.com"
421
+ });
422
+ expect(targets.apiBaseUrl).toBe("https://staging.publish.solanamobile.com/api");
423
+ });
424
+ test("portal targets preserve portal subpaths when deriving /api", function() {
425
+ var targets = resolvePortalTargets({
426
+ portalUrl: "https://portal.example.com/publishing"
427
+ });
428
+ expect(targets.apiBaseUrl).toBe("https://portal.example.com/publishing/api");
429
+ });
430
+ test("portal targets honor DAPP_STORE_PORTAL_URL from the environment", function() {
431
+ process.env.DAPP_STORE_PORTAL_URL = "https://env.publish.solanamobile.com";
432
+ var targets = resolvePortalTargets({});
433
+ expect(targets.apiBaseUrl).toBe("https://env.publish.solanamobile.com/api");
434
+ });
435
+ test("portal targets ignore removed legacy portal env vars", function() {
436
+ process.env.DAPP_STORE_PORTAL_WEB_URL = "https://legacy-web.publish.solanamobile.com";
437
+ process.env.DAPP_STORE_PORTAL_API_BASE_URL = "https://legacy.publish.solanamobile.com/root/api";
438
+ var targets = resolvePortalTargets({});
439
+ expect(targets.apiBaseUrl).toBe("".concat(DEFAULT_PRODUCTION_PORTAL_URL, "/api"));
440
+ });
441
+ test("local-dev mode rejects non-local portal URLs", function() {
442
+ expect(function() {
443
+ return resolvePortalTargets({
444
+ localDev: true,
445
+ portalUrl: "https://portal.example.com"
446
+ });
447
+ }).toThrow("only allows localhost portal endpoints");
448
+ });
449
+ test("non-local portal endpoints must use HTTPS", function() {
450
+ expect(function() {
451
+ return resolvePortalTargets({
452
+ portalUrl: "http://portal.example.com"
453
+ });
454
+ }).toThrow("Portal endpoints must use HTTPS unless --local-dev is set.");
455
+ });
456
+ test("portal targets honor the configured API key env name", function() {
457
+ expect(DEFAULT_API_KEY_ENV).toBe("DAPP_STORE_API_KEY");
458
+ });
459
+ test("resolveApiKey reads the default API key env var", function() {
460
+ return _async_to_generator(function() {
461
+ return _ts_generator(this, function(_state) {
462
+ switch(_state.label){
463
+ case 0:
464
+ process.env.DAPP_STORE_API_KEY = "portal-secret";
465
+ return [
466
+ 4,
467
+ expect(resolveApiKey({})).resolves.toBe("portal-secret")
468
+ ];
469
+ case 1:
470
+ _state.sent();
471
+ return [
472
+ 2
473
+ ];
474
+ }
475
+ });
476
+ })();
477
+ });
478
+ test("resolveApiKey reads a custom API key env var", function() {
479
+ return _async_to_generator(function() {
480
+ return _ts_generator(this, function(_state) {
481
+ switch(_state.label){
482
+ case 0:
483
+ process.env.ALT_DAPP_STORE_API_KEY = "alt-secret";
484
+ return [
485
+ 4,
486
+ expect(resolveApiKey({
487
+ apiKeyEnv: "ALT_DAPP_STORE_API_KEY"
488
+ })).resolves.toBe("alt-secret")
489
+ ];
490
+ case 1:
491
+ _state.sent();
492
+ return [
493
+ 2
494
+ ];
495
+ }
496
+ });
497
+ })();
498
+ });
499
+ test("resolveApiKey rejects when no API key is available", function() {
500
+ return _async_to_generator(function() {
501
+ return _ts_generator(this, function(_state) {
502
+ switch(_state.label){
503
+ case 0:
504
+ return [
505
+ 4,
506
+ expect(resolveApiKey({})).rejects.toThrow("Portal API key is required.")
507
+ ];
508
+ case 1:
509
+ _state.sent();
510
+ return [
511
+ 4,
512
+ expect(resolveApiKey({})).rejects.toThrow(UPDATED_PUBLISHING_CLI_DOCS_URL)
513
+ ];
514
+ case 2:
515
+ _state.sent();
516
+ return [
517
+ 2
518
+ ];
519
+ }
520
+ });
521
+ })();
522
+ });
523
+ test("formatUpdatedCliUsageError converts unknown-option errors into docs guidance", function() {
524
+ expect(formatUpdatedCliUsageError("error: unknown option '-k'")).toContain("Unknown option '-k'.");
525
+ expect(formatUpdatedCliUsageError("error: unknown option '-k'")).toContain(UPDATED_PUBLISHING_CLI_DOCS_URL);
526
+ });
117
527
  });
@@ -0,0 +1,25 @@
1
+ import { describe, expect, test } from "@jest/globals";
2
+ import { getCommanderUserFacingError, isCommanderLifecycleExit } from "../parseErrors";
3
+ import { UPDATED_PUBLISHING_CLI_DOCS_URL } from "../../publication/cliValidation";
4
+ describe("CLI parse error handling", function() {
5
+ test("legacy parse errors point to the updated publishing docs", function() {
6
+ var error = getCommanderUserFacingError({
7
+ code: "commander.unknownOption",
8
+ exitCode: 1,
9
+ message: "error: unknown option '-k'",
10
+ name: "CommanderError"
11
+ });
12
+ expect(error).not.toBeNull();
13
+ expect(error === null || error === void 0 ? void 0 : error.exitCode).toBe(1);
14
+ expect(error === null || error === void 0 ? void 0 : error.message).toContain("Unknown option '-k'.");
15
+ expect(error === null || error === void 0 ? void 0 : error.message).toContain(UPDATED_PUBLISHING_CLI_DOCS_URL);
16
+ });
17
+ test("help exits are ignored", function() {
18
+ expect(isCommanderLifecycleExit({
19
+ code: "commander.helpDisplayed",
20
+ exitCode: 0,
21
+ message: "(outputHelp)",
22
+ name: "CommanderError"
23
+ })).toBe(true);
24
+ });
25
+ });