@solana-mobile/dapp-store-cli 0.16.0 → 1.0.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/bin/dapp-store.js +3 -1
- package/lib/CliSetup.js +304 -505
- package/lib/CliUtils.js +6 -376
- package/lib/__tests__/CliSetupTest.js +484 -74
- package/lib/cli/__tests__/parseErrors.test.js +25 -0
- package/lib/cli/__tests__/signer.test.js +436 -0
- package/lib/cli/constants.js +23 -0
- package/lib/cli/messages.js +21 -0
- package/lib/cli/parseErrors.js +41 -0
- package/lib/{commands/publish/PublishCliSupport.js → cli/selfUpdate.js} +72 -38
- package/lib/{commands/publish/PublishCliRemove.js → cli/signer.js} +35 -56
- package/lib/index.js +96 -5
- package/lib/package.json +5 -24
- package/lib/portal/__tests__/releaseMetadata.test.js +647 -0
- package/lib/portal/__tests__/translators.test.js +76 -0
- package/lib/portal/__tests__/workflowClient.test.js +457 -0
- package/lib/portal/attestationClient.js +143 -0
- package/lib/portal/files.js +64 -0
- package/lib/portal/http.js +364 -0
- package/lib/portal/records.js +64 -0
- package/lib/portal/releaseMetadata.js +748 -0
- package/lib/portal/translators.js +460 -0
- package/lib/portal/types.js +1 -0
- package/lib/portal/workflowClient.js +704 -0
- package/lib/publication/PublicationProgressReporter.js +1051 -0
- package/lib/publication/__tests__/PublicationProgressReporter.test.js +174 -0
- package/lib/{commands/ValidateCommand.js → publication/__tests__/fundingPreflight.test.js} +90 -66
- package/lib/publication/__tests__/publicationSummary.test.js +26 -0
- package/lib/publication/cliValidation.js +482 -0
- package/lib/publication/fundingPreflight.js +246 -0
- package/lib/publication/publicationSummary.js +99 -0
- package/lib/{commands/utils.js → publication/runPublicationWorkflow.js} +16 -46
- package/package.json +5 -24
- package/src/CliSetup.ts +370 -505
- package/src/CliUtils.ts +9 -233
- package/src/__tests__/CliSetupTest.ts +272 -120
- package/src/cli/__tests__/parseErrors.test.ts +34 -0
- package/src/cli/__tests__/signer.test.ts +359 -0
- package/src/cli/constants.ts +3 -0
- package/src/cli/messages.ts +27 -0
- package/src/cli/parseErrors.ts +62 -0
- package/src/cli/selfUpdate.ts +59 -0
- package/src/cli/signer.ts +38 -0
- package/src/index.ts +31 -4
- package/src/portal/__tests__/releaseMetadata.test.ts +508 -0
- package/src/portal/__tests__/translators.test.ts +82 -0
- package/src/portal/__tests__/workflowClient.test.ts +278 -0
- package/src/portal/attestationClient.ts +19 -0
- package/src/portal/files.ts +73 -0
- package/src/portal/http.ts +170 -0
- package/src/portal/records.ts +38 -0
- package/src/portal/releaseMetadata.ts +489 -0
- package/src/portal/translators.ts +750 -0
- package/src/portal/types.ts +27 -0
- package/src/portal/workflowClient.ts +575 -0
- package/src/publication/PublicationProgressReporter.ts +1026 -0
- package/src/publication/__tests__/PublicationProgressReporter.test.ts +210 -0
- package/src/publication/__tests__/fundingPreflight.test.ts +78 -0
- package/src/publication/__tests__/publicationSummary.test.ts +30 -0
- package/src/publication/cliValidation.ts +264 -0
- package/src/publication/fundingPreflight.ts +123 -0
- package/src/publication/publicationSummary.ts +26 -0
- package/src/publication/runPublicationWorkflow.ts +46 -0
- package/lib/commands/create/CreateCliApp.js +0 -223
- package/lib/commands/create/CreateCliRelease.js +0 -290
- package/lib/commands/create/index.js +0 -40
- package/lib/commands/index.js +0 -3
- package/lib/commands/publish/PublishCliSubmit.js +0 -208
- package/lib/commands/publish/PublishCliUpdate.js +0 -211
- package/lib/commands/publish/index.js +0 -22
- package/lib/commands/scaffolding/ScaffoldInit.js +0 -15
- package/lib/commands/scaffolding/index.js +0 -1
- package/lib/config/EnvVariables.js +0 -59
- package/lib/config/PublishDetails.js +0 -915
- package/lib/config/S3StorageManager.js +0 -93
- package/lib/config/index.js +0 -2
- package/lib/generated/config_obj.json +0 -1
- package/lib/generated/config_schema.json +0 -1
- package/lib/prebuild_schema/publishing_source.yaml +0 -64
- package/lib/prebuild_schema/schemagen.js +0 -25
- package/lib/upload/CachedStorageDriver.js +0 -458
- package/lib/upload/TurboStorageDriver.js +0 -718
- package/lib/upload/__tests__/CachedStorageDriver.test.js +0 -437
- package/lib/upload/__tests__/TurboStorageDriver.test.js +0 -17
- package/lib/upload/__tests__/contentGateway.test.js +0 -17
- package/lib/upload/contentGateway.js +0 -23
- package/lib/upload/index.js +0 -2
- package/src/commands/ValidateCommand.ts +0 -82
- package/src/commands/create/CreateCliApp.ts +0 -93
- package/src/commands/create/CreateCliRelease.ts +0 -149
- package/src/commands/create/index.ts +0 -47
- package/src/commands/index.ts +0 -3
- package/src/commands/publish/PublishCliRemove.ts +0 -66
- package/src/commands/publish/PublishCliSubmit.ts +0 -93
- package/src/commands/publish/PublishCliSupport.ts +0 -66
- package/src/commands/publish/PublishCliUpdate.ts +0 -101
- package/src/commands/publish/index.ts +0 -29
- package/src/commands/scaffolding/ScaffoldInit.ts +0 -20
- package/src/commands/scaffolding/index.ts +0 -1
- package/src/commands/utils.ts +0 -33
- package/src/config/EnvVariables.ts +0 -39
- package/src/config/PublishDetails.ts +0 -456
- package/src/config/S3StorageManager.ts +0 -47
- package/src/config/index.ts +0 -2
- package/src/prebuild_schema/publishing_source.yaml +0 -64
- package/src/prebuild_schema/schemagen.js +0 -31
- package/src/upload/CachedStorageDriver.ts +0 -179
- package/src/upload/TurboStorageDriver.ts +0 -283
- package/src/upload/__tests__/CachedStorageDriver.test.ts +0 -246
- package/src/upload/__tests__/TurboStorageDriver.test.ts +0 -15
- package/src/upload/__tests__/contentGateway.test.ts +0 -31
- package/src/upload/contentGateway.ts +0 -37
- package/src/upload/index.ts +0 -2
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { expect, jest, test } from '@jest/globals';
|
|
2
|
+
import { Writable } from 'node:stream';
|
|
3
|
+
import { createPublicationProgressReporter } from '../PublicationProgressReporter';
|
|
4
|
+
function createTestStream() {
|
|
5
|
+
var output = [];
|
|
6
|
+
var stream = new Writable({
|
|
7
|
+
write: function write(chunk, _encoding, callback) {
|
|
8
|
+
output.push(String(chunk));
|
|
9
|
+
callback();
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
stream.isTTY = false;
|
|
13
|
+
stream.columns = 120;
|
|
14
|
+
stream.output = output;
|
|
15
|
+
return stream;
|
|
16
|
+
}
|
|
17
|
+
test('local file upload progress materially advances the bar', function() {
|
|
18
|
+
var logSpy = jest.spyOn(console, 'log').mockImplementation(function() {});
|
|
19
|
+
var stream = createTestStream();
|
|
20
|
+
try {
|
|
21
|
+
var reporter = createPublicationProgressReporter({
|
|
22
|
+
title: 'Publishing version',
|
|
23
|
+
mode: 'new-version',
|
|
24
|
+
stream: stream
|
|
25
|
+
});
|
|
26
|
+
reporter.start({
|
|
27
|
+
metadata: {
|
|
28
|
+
sourceKind: 'apk-file',
|
|
29
|
+
fileName: 'app-release.apk'
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
reporter.logger.debug('Uploading APK to portal storage', {
|
|
33
|
+
step: 'source.upload',
|
|
34
|
+
status: 'running',
|
|
35
|
+
fileName: 'app-release.apk',
|
|
36
|
+
fileSize: 100,
|
|
37
|
+
bytesUploaded: 50,
|
|
38
|
+
bytesTotal: 100,
|
|
39
|
+
stepProgress: 0.5
|
|
40
|
+
});
|
|
41
|
+
var lines = reporter.buildLines();
|
|
42
|
+
expect(reporter.getProgressPercent()).toBeGreaterThan(0.2);
|
|
43
|
+
expect(lines[1]).toContain('23%');
|
|
44
|
+
expect(lines).toContain('Upload: 50 B / 100 B (50%)');
|
|
45
|
+
} finally{
|
|
46
|
+
logSpy.mockRestore();
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
test('ingestion status updates continue advancing overall progress', function() {
|
|
50
|
+
var logSpy = jest.spyOn(console, 'log').mockImplementation(function() {});
|
|
51
|
+
var stream = createTestStream();
|
|
52
|
+
try {
|
|
53
|
+
var reporter = createPublicationProgressReporter({
|
|
54
|
+
title: 'Publishing version',
|
|
55
|
+
mode: 'new-version',
|
|
56
|
+
stream: stream
|
|
57
|
+
});
|
|
58
|
+
reporter.start({
|
|
59
|
+
metadata: {
|
|
60
|
+
sourceKind: 'apk-file',
|
|
61
|
+
fileName: 'app-release.apk'
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
reporter.logger.info('APK uploaded to portal storage', {
|
|
65
|
+
step: 'source.upload',
|
|
66
|
+
status: 'complete',
|
|
67
|
+
fileName: 'app-release.apk'
|
|
68
|
+
});
|
|
69
|
+
reporter.logger.info('Extracting APK metadata', {
|
|
70
|
+
step: 'ingestion.wait',
|
|
71
|
+
status: 'running',
|
|
72
|
+
ingestionStatus: 'processing',
|
|
73
|
+
ingestionStage: 'ExtractingApk',
|
|
74
|
+
ingestionDetail: 'Extracting APK metadata',
|
|
75
|
+
ingestionProgress: 45,
|
|
76
|
+
stepProgress: 0.7
|
|
77
|
+
});
|
|
78
|
+
var lines = reporter.buildLines();
|
|
79
|
+
expect(reporter.getProgressPercent()).toBeGreaterThan(0.55);
|
|
80
|
+
expect(lines[1]).toContain('57%');
|
|
81
|
+
expect(lines).toContain('Ingestion: Extracting APK metadata (45%)');
|
|
82
|
+
} finally{
|
|
83
|
+
logSpy.mockRestore();
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
test('ingestion detail and percentage are rendered while polling', function() {
|
|
87
|
+
var logSpy = jest.spyOn(console, 'log').mockImplementation(function() {});
|
|
88
|
+
var stream = createTestStream();
|
|
89
|
+
try {
|
|
90
|
+
var reporter = createPublicationProgressReporter({
|
|
91
|
+
title: 'Publishing version',
|
|
92
|
+
mode: 'new-version',
|
|
93
|
+
stream: stream
|
|
94
|
+
});
|
|
95
|
+
reporter.start({
|
|
96
|
+
metadata: {
|
|
97
|
+
sourceKind: 'apk-file',
|
|
98
|
+
fileName: 'app-release.apk'
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
reporter.logger.info('Extracting APK metadata', {
|
|
102
|
+
step: 'ingestion.wait',
|
|
103
|
+
status: 'running',
|
|
104
|
+
ingestionStatus: 'processing',
|
|
105
|
+
ingestionProgress: 45,
|
|
106
|
+
ingestionStage: 'ExtractingApk',
|
|
107
|
+
ingestionDetail: 'Extracting APK metadata',
|
|
108
|
+
stepProgress: 0.45
|
|
109
|
+
});
|
|
110
|
+
var lines = reporter.buildLines();
|
|
111
|
+
expect(lines).toContain('Ingestion: Extracting APK metadata (45%)');
|
|
112
|
+
} finally{
|
|
113
|
+
logSpy.mockRestore();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
test('verbose mode prints detailed identifiers as they arrive', function() {
|
|
117
|
+
var logSpy = jest.spyOn(console, 'log').mockImplementation(function() {});
|
|
118
|
+
var stream = createTestStream();
|
|
119
|
+
try {
|
|
120
|
+
var reporter = createPublicationProgressReporter({
|
|
121
|
+
title: 'Publishing version',
|
|
122
|
+
mode: 'new-version',
|
|
123
|
+
verbose: true,
|
|
124
|
+
stream: stream
|
|
125
|
+
});
|
|
126
|
+
reporter.start({
|
|
127
|
+
metadata: {
|
|
128
|
+
sourceKind: 'apk-file',
|
|
129
|
+
fileName: 'app-release.apk'
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
reporter.logger.info('Ingestion session created', {
|
|
133
|
+
step: 'ingestion.create',
|
|
134
|
+
status: 'complete',
|
|
135
|
+
releaseId: 'release-123',
|
|
136
|
+
ingestionSessionId: 'ingestion-456'
|
|
137
|
+
});
|
|
138
|
+
reporter.logger.info('Publication session loaded', {
|
|
139
|
+
step: 'session.load',
|
|
140
|
+
status: 'complete',
|
|
141
|
+
publicationSessionId: 'session-789'
|
|
142
|
+
});
|
|
143
|
+
reporter.logger.info('Release NFT transaction submitted', {
|
|
144
|
+
step: 'mint.submit',
|
|
145
|
+
status: 'complete',
|
|
146
|
+
transactionSignature: 'release-tx-sig'
|
|
147
|
+
});
|
|
148
|
+
reporter.logger.info('Release collection verified', {
|
|
149
|
+
step: 'verify.submit',
|
|
150
|
+
status: 'complete',
|
|
151
|
+
transactionSignature: 'collection-tx-sig'
|
|
152
|
+
});
|
|
153
|
+
reporter.logger.info('Attestation payload created', {
|
|
154
|
+
step: 'attestation.create',
|
|
155
|
+
status: 'complete',
|
|
156
|
+
requestUniqueId: 'attest-111'
|
|
157
|
+
});
|
|
158
|
+
reporter.logger.info('Release submitted to store', {
|
|
159
|
+
step: 'submit.store',
|
|
160
|
+
status: 'complete',
|
|
161
|
+
hubspotTicketId: 'ticket-222'
|
|
162
|
+
});
|
|
163
|
+
var output = stream.output.join('');
|
|
164
|
+
expect(output).toContain('Verbose: Release ID: release-123');
|
|
165
|
+
expect(output).toContain('Verbose: Ingestion session ID: ingestion-456');
|
|
166
|
+
expect(output).toContain('Verbose: Publication session ID: session-789');
|
|
167
|
+
expect(output).toContain('Verbose: Release transaction signature: release-tx-sig');
|
|
168
|
+
expect(output).toContain('Verbose: Collection transaction signature: collection-tx-sig');
|
|
169
|
+
expect(output).toContain('Verbose: Attestation request ID: attest-111');
|
|
170
|
+
expect(output).toContain('Verbose: Ticket ID: ticket-222');
|
|
171
|
+
} finally{
|
|
172
|
+
logSpy.mockRestore();
|
|
173
|
+
}
|
|
174
|
+
});
|
|
@@ -118,78 +118,102 @@ function _ts_generator(thisArg, body) {
|
|
|
118
118
|
};
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
import {
|
|
122
|
-
import {
|
|
123
|
-
import {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
121
|
+
import { describe, expect, it, jest } from "@jest/globals";
|
|
122
|
+
import { PublicKey } from "@solana/web3.js";
|
|
123
|
+
import { ensurePublicationSignerBalance, MIN_PUBLICATION_SIGNER_BALANCE_LAMPORTS, resolveFundingPreflightRpcUrl } from "../fundingPreflight.js";
|
|
124
|
+
describe("resolveFundingPreflightRpcUrl", function() {
|
|
125
|
+
it("defaults to mainnet when local-dev is not enabled", function() {
|
|
126
|
+
expect(resolveFundingPreflightRpcUrl({})).toBe("https://api.mainnet-beta.solana.com");
|
|
127
|
+
});
|
|
128
|
+
it("skips the default RPC in local-dev mode", function() {
|
|
129
|
+
expect(resolveFundingPreflightRpcUrl({
|
|
130
|
+
localDev: true
|
|
131
|
+
})).toBeUndefined();
|
|
132
|
+
});
|
|
133
|
+
it("honors an explicit RPC URL", function() {
|
|
134
|
+
expect(resolveFundingPreflightRpcUrl({
|
|
135
|
+
localDev: true,
|
|
136
|
+
rpcUrl: "https://rpc.example.com"
|
|
137
|
+
})).toBe("https://rpc.example.com");
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
describe("ensurePublicationSignerBalance", function() {
|
|
141
|
+
var publicKey = new PublicKey(new Uint8Array(32).fill(7)).toBase58();
|
|
142
|
+
it("throws before publication when the signer balance is too low", function() {
|
|
143
|
+
return _async_to_generator(function() {
|
|
144
|
+
return _ts_generator(this, function(_state) {
|
|
145
|
+
switch(_state.label){
|
|
146
|
+
case 0:
|
|
147
|
+
return [
|
|
148
|
+
4,
|
|
149
|
+
expect(ensurePublicationSignerBalance({
|
|
150
|
+
publicKey: publicKey
|
|
151
|
+
}, function() {
|
|
152
|
+
return {
|
|
153
|
+
getBalance: jest.fn().mockResolvedValue(MIN_PUBLICATION_SIGNER_BALANCE_LAMPORTS - 1)
|
|
154
|
+
};
|
|
155
|
+
})).rejects.toThrow("publishing needs at least")
|
|
156
|
+
];
|
|
157
|
+
case 1:
|
|
158
|
+
_state.sent();
|
|
156
159
|
return [
|
|
157
160
|
2
|
|
158
161
|
];
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
})();
|
|
165
|
+
});
|
|
166
|
+
it("returns without warning when the signer balance is sufficient", function() {
|
|
167
|
+
return _async_to_generator(function() {
|
|
168
|
+
var result;
|
|
169
|
+
return _ts_generator(this, function(_state) {
|
|
170
|
+
switch(_state.label){
|
|
171
|
+
case 0:
|
|
172
|
+
return [
|
|
173
|
+
4,
|
|
174
|
+
ensurePublicationSignerBalance({
|
|
175
|
+
publicKey: publicKey
|
|
176
|
+
}, function() {
|
|
177
|
+
return {
|
|
178
|
+
getBalance: jest.fn().mockResolvedValue(MIN_PUBLICATION_SIGNER_BALANCE_LAMPORTS + 1)
|
|
179
|
+
};
|
|
180
|
+
})
|
|
181
|
+
];
|
|
182
|
+
case 1:
|
|
183
|
+
result = _state.sent();
|
|
184
|
+
expect(result).toBeUndefined();
|
|
172
185
|
return [
|
|
173
186
|
2
|
|
174
187
|
];
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
})();
|
|
191
|
+
});
|
|
192
|
+
it("returns a warning when the RPC balance check fails", function() {
|
|
193
|
+
return _async_to_generator(function() {
|
|
194
|
+
var result;
|
|
195
|
+
return _ts_generator(this, function(_state) {
|
|
196
|
+
switch(_state.label){
|
|
197
|
+
case 0:
|
|
198
|
+
return [
|
|
199
|
+
4,
|
|
200
|
+
ensurePublicationSignerBalance({
|
|
201
|
+
publicKey: publicKey
|
|
202
|
+
}, function() {
|
|
203
|
+
return {
|
|
204
|
+
getBalance: jest.fn().mockRejectedValue(new Error("RPC timeout"))
|
|
205
|
+
};
|
|
206
|
+
})
|
|
207
|
+
];
|
|
208
|
+
case 1:
|
|
209
|
+
result = _state.sent();
|
|
210
|
+
expect(result).toContain("Continuing without a SOL preflight check");
|
|
211
|
+
expect(result).toContain("RPC timeout");
|
|
184
212
|
return [
|
|
185
213
|
2
|
|
186
214
|
];
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
})();
|
|
195
|
-
};
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
})();
|
|
218
|
+
});
|
|
219
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { expect, test } from '@jest/globals';
|
|
2
|
+
import { extractPublicationSummaryLines } from '../publicationSummary';
|
|
3
|
+
test('summary keeps the completion message and only the compact fields', function() {
|
|
4
|
+
var lines = extractPublicationSummaryLines({
|
|
5
|
+
releaseId: 'release-123',
|
|
6
|
+
publicationSessionId: 'session-123',
|
|
7
|
+
ingestionSessionId: 'ingestion-123',
|
|
8
|
+
releaseMintAddress: 'release-mint-abc',
|
|
9
|
+
collectionMintAddress: 'collection-mint-def',
|
|
10
|
+
releaseTransactionSignature: 'release-tx',
|
|
11
|
+
collectionTransactionSignature: 'collection-tx',
|
|
12
|
+
attestationRequestUniqueId: 'attest-123',
|
|
13
|
+
hubspotTicketId: 'ticket-123'
|
|
14
|
+
});
|
|
15
|
+
expect(lines).toEqual([
|
|
16
|
+
'This app is now in review.',
|
|
17
|
+
'Release mint address: release-mint-abc',
|
|
18
|
+
'Collection mint address: collection-mint-def',
|
|
19
|
+
'Ticket ID: ticket-123'
|
|
20
|
+
]);
|
|
21
|
+
});
|
|
22
|
+
test('summary still shows the in-review message when no fields are available', function() {
|
|
23
|
+
expect(extractPublicationSummaryLines(undefined)).toEqual([
|
|
24
|
+
'This app is now in review.'
|
|
25
|
+
]);
|
|
26
|
+
});
|