@did-btcr2/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 (46) hide show
  1. package/dist/cjs/cli.js +233 -115
  2. package/dist/cjs/cli.js.map +1 -1
  3. package/dist/cjs/command.js +27 -33
  4. package/dist/cjs/command.js.map +1 -1
  5. package/dist/cjs/error.js +10 -0
  6. package/dist/cjs/error.js.map +1 -0
  7. package/dist/cjs/index.js +1 -0
  8. package/dist/cjs/index.js.map +1 -1
  9. package/dist/esm/bin/btcr2.js +5 -0
  10. package/dist/esm/bin/btcr2.js.map +1 -0
  11. package/dist/esm/src/cli.js +294 -0
  12. package/dist/esm/src/cli.js.map +1 -0
  13. package/dist/esm/src/command.js +35 -0
  14. package/dist/esm/src/command.js.map +1 -0
  15. package/dist/esm/src/error.js +10 -0
  16. package/dist/esm/src/error.js.map +1 -0
  17. package/dist/esm/src/index.js +4 -0
  18. package/dist/esm/src/index.js.map +1 -0
  19. package/dist/types/bin/btcr2.d.ts +3 -0
  20. package/dist/types/bin/btcr2.d.ts.map +1 -0
  21. package/dist/types/cli.d.ts +99 -18
  22. package/dist/types/command.d.ts +49 -9
  23. package/dist/types/error.d.ts +7 -0
  24. package/dist/types/index.d.ts +1 -0
  25. package/dist/types/src/cli.d.ts +113 -0
  26. package/dist/types/src/cli.d.ts.map +1 -0
  27. package/dist/types/src/command.d.ts +55 -0
  28. package/dist/types/src/command.d.ts.map +1 -0
  29. package/dist/types/src/error.d.ts +8 -0
  30. package/dist/types/src/error.d.ts.map +1 -0
  31. package/dist/types/src/index.d.ts +4 -0
  32. package/dist/types/src/index.d.ts.map +1 -0
  33. package/package.json +14 -19
  34. package/src/cli.ts +302 -161
  35. package/src/command.ts +66 -38
  36. package/src/error.ts +10 -0
  37. package/src/index.ts +2 -1
  38. package/dist/esm/cli.js +0 -176
  39. package/dist/esm/cli.js.map +0 -1
  40. package/dist/esm/command.js +0 -41
  41. package/dist/esm/command.js.map +0 -1
  42. package/dist/esm/index.js +0 -3
  43. package/dist/esm/index.js.map +0 -1
  44. package/dist/types/cli.d.ts.map +0 -1
  45. package/dist/types/command.d.ts.map +0 -1
  46. package/dist/types/index.d.ts.map +0 -1
package/src/cli.ts CHANGED
@@ -1,44 +1,41 @@
1
- import { DidMethodError } from '@did-btcr2/common';
2
1
  import { Identifier } from '@did-btcr2/method';
3
- import { Command } from 'commander';
2
+ import { Command, CommanderError } from 'commander';
4
3
  import { readFile } from 'fs/promises';
5
- import Btcr2Command from './command.js';
4
+ import Btcr2Command, {
5
+ CommandRequest,
6
+ CommandResult,
7
+ CreateCommandOptions,
8
+ NetworkOption,
9
+ ResolveCommandOptions,
10
+ UpdateCommandOptions,
11
+ } from './command.js';
12
+ import { CLIError } from './error.js';
6
13
 
7
- /**
8
- * Custom CLI Error class extending DidMethodError.
9
- */
10
- export class CLIError extends DidMethodError {
11
- constructor(message: string, type: string = 'CLIError', data?: Record<string, any>) {
12
- super(message, { type, name: type, data });
13
- }
14
- }
14
+ const SUPPORTED_NETWORKS: NetworkOption[] = ['bitcoin', 'testnet3', 'testnet4', 'signet', 'mutinynet', 'regtest'];
15
15
 
16
16
  /**
17
- * A class-based CLI using Commander.
18
- * - No forced process.exit().
19
- * - Configurable by calling `run(argv?)`.
17
+ * CLI tool for the did:btcr2 method.
18
+ * @type {DidBtcr2Cli}
19
+ * @class DidBtcr2Cli
20
20
  */
21
21
  export class DidBtcr2Cli {
22
- private CLI: Command;
22
+ public readonly program: Command;
23
23
 
24
24
  constructor() {
25
- // Create the main Commander program
26
- this.CLI = new Command()
27
- .name('btcr2')
25
+ this.program = new Command('btcr2')
28
26
  .version('btcr2 0.1.0', '-v, --version', 'Output the current version')
29
27
  .description('CLI tool for the did:btcr2 method');
30
28
 
31
- // Configure top-level options and subcommands
32
29
  this.configureCommands();
33
30
  }
34
31
 
35
32
  /**
36
- * Configure the CLI commands and options.
37
- * @private
33
+ * Configures the CLI commands.
34
+ * @returns {void}
38
35
  */
39
36
  private configureCommands(): void {
40
- /* CREATE */
41
- this.CLI
37
+ // Create command
38
+ this.program
42
39
  .command('create')
43
40
  .description('Create an identifier and initial DID document')
44
41
  .requiredOption('-t, --type <type>', 'Identifier type <k|x>', 'k')
@@ -52,36 +49,14 @@ export class DidBtcr2Cli {
52
49
  'If type=k, MUST be secp256k1 public key. ' +
53
50
  'If type=x, MUST be SHA-256 hash of a genesis document'
54
51
  )
55
- .action(
56
- async (options: { type: string; network: string; bytes: string, [key: string]: any }) => {
57
- if(!['k','x'].includes(options.type)) {
58
- throw new CLIError(
59
- 'Invalid type. Must be "k" or "x".',
60
- 'INVALID_ARGUMENT_ERROR',
61
- options
62
- );
63
- }
64
- if(!['bitcoin','testnet3','testnet4','signet','mutinynet','regtest'].includes(options.network)) {
65
- throw new CLIError(
66
- 'Invalid network. Must be one of "bitcoin", "testnet3", ' +
67
- '"testnet4", "signet", "mutinynet", or "regtest".',
68
- 'INVALID_ARGUMENT_ERROR',
69
- options
70
- );
71
- }
72
- if(Buffer.from(options.bytes, 'hex').length === 0) {
73
- throw new CLIError(
74
- 'Invalid bytes. Must be a non-empty hex string.',
75
- 'INVALID_ARGUMENT_ERROR',
76
- options
77
- );
78
- }
79
- await this.invokeCommand({ options, action: 'create', command: new Btcr2Command() });
80
- }
81
- );
52
+ .action(async (options: { type: string; network: string; bytes: string }) => {
53
+ const parsedOptions = this.parseCreateOptions(options);
54
+ const result = await this.invokeCommand({ options: parsedOptions, action: 'create', command: new Btcr2Command() });
55
+ this.printResult(result);
56
+ });
82
57
 
83
- /* READ / RESOLVE */
84
- this.CLI
58
+ // Resolve command
59
+ this.program
85
60
  .command('resolve')
86
61
  .alias('read')
87
62
  .description('Resolve the DID document of the identifier.')
@@ -89,43 +64,13 @@ export class DidBtcr2Cli {
89
64
  .option('-r, --resolutionOptions <resolutionOptions>', 'JSON string containing resolution options')
90
65
  .option('-p, --resolutionOptionsPath <resolutionOptionsPath>', 'Path to a JSON file containing resolution options')
91
66
  .action(async (options: { identifier: string; resolutionOptions?: string; resolutionOptionsPath?: string }) => {
92
- try {
93
- Identifier.decode(options.identifier);
94
- } catch {
95
- throw new CLIError(
96
- 'Invalid identifier. Must be a valid did:btcr2 identifier.',
97
- 'INVALID_ARGUMENT_ERROR',
98
- options
99
- );
100
- }
101
- if(options.resolutionOptions) {
102
- try {
103
- options.resolutionOptions = JSON.parse(options.resolutionOptions);
104
- } catch {
105
- throw new CLIError(
106
- 'Invalid options. Must be a valid JSON string.',
107
- 'INVALID_ARGUMENT_ERROR',
108
- options
109
- );
110
- }
111
- }
112
- if(options.resolutionOptionsPath) {
113
- try {
114
- const data = await readFile(options.resolutionOptionsPath, 'utf-8');
115
- options.resolutionOptions = JSON.parse(data);
116
- } catch {
117
- throw new CLIError(
118
- 'Invalid options path. Must be a valid path to a JSON file.',
119
- 'INVALID_ARGUMENT_ERROR',
120
- options
121
- );
122
- }
123
- }
124
- await this.invokeCommand({ options, action: 'resolve', command: new Btcr2Command() });
67
+ const parsedOptions = await this.parseResolveOptions(options);
68
+ const result = await this.invokeCommand({ options: parsedOptions, action: 'resolve', command: new Btcr2Command() });
69
+ this.printResult(result);
125
70
  });
126
71
 
127
- /* UPDATE */
128
- this.CLI
72
+ // Update command
73
+ this.program
129
74
  .command('update')
130
75
  .description('Update a did:btcr2 document.')
131
76
  .requiredOption('-i, --identifier <identifier>', 'did:btcr2 identifier')
@@ -136,103 +81,299 @@ export class DidBtcr2Cli {
136
81
  .requiredOption('-b, --beaconIds <beaconIds>', 'Beacon IDs as a JSON string array')
137
82
  .action(async (options: {
138
83
  identifier: string;
139
- sourceDocument: string; // stringified DidDocument; e.g. '{ "@context": "...", "id": "did:btcr2:...", ... }'
84
+ sourceDocument: string;
140
85
  sourceVersionId: number;
141
- patch: string; // stringified PatchOperation[]; e.g. '[{ "op": "add", "path": "/foo", "value": "bar" }]'
86
+ patch: string;
142
87
  verificationMethodId: string;
143
- beaconIds: string // stringified string[]; e.g. '["beaconId1","beaconId2"]'
144
- }) => {
145
- // Validate identifier by decoding
146
- try {
147
- Identifier.decode(options.identifier);
148
- } catch {
149
- throw new CLIError(
150
- 'Invalid identifier. Must be a valid did:btcr2 identifier.',
151
- 'INVALID_ARGUMENT_ERROR',
152
- options
153
- );
154
- }
155
- // Validate source document JSON
156
- if(options.sourceDocument) {
157
- try {
158
- options.sourceDocument = JSON.parse(options.sourceDocument);
159
- } catch {
160
- throw new CLIError(
161
- 'Invalid options. Must be a valid JSON string.',
162
- 'INVALID_ARGUMENT_ERROR',
163
- options
164
- );
165
- }
166
- }
167
- // Validate patch JSON
168
- if(options.patch) {
169
- try {
170
- options.patch = JSON.parse(options.patch);
171
- } catch {
172
- throw new CLIError(
173
- 'Invalid options. Must be a valid JSON string.',
174
- 'INVALID_ARGUMENT_ERROR',
175
- options
176
- );
177
- }
178
- }
179
- // Validate beacon IDs JSON
180
- if(options.beaconIds) {
181
- try {
182
- options.beaconIds = JSON.parse(options.beaconIds);
183
- } catch {
184
- throw new CLIError(
185
- 'Invalid options. Must be a valid JSON string.',
186
- 'INVALID_ARGUMENT_ERROR',
187
- options
188
- );
189
- }
190
- }
191
- await this.invokeCommand({ options, action: 'update', command: new Btcr2Command() });
88
+ beaconIds: string;
89
+ }) => {
90
+ const parsedOptions = this.parseUpdateOptions(options);
91
+ const result = await this.invokeCommand({ options: parsedOptions, action: 'update', command: new Btcr2Command() });
92
+ this.printResult(result);
192
93
  });
193
94
 
194
- /* DEACTIVATE / DELETE */
195
- this.CLI
95
+ // Deactivate command
96
+ this.program
196
97
  .command('deactivate')
197
98
  .alias('delete')
198
99
  .description('Deactivate the did:btcr2 identifier permanently.')
199
- .action(async (options) => {
200
- await this.invokeCommand({ options, action: 'deactivate', command: new Btcr2Command() });
100
+ .action(async () => {
101
+ const result = await this.invokeCommand({ options: {}, action: 'deactivate', command: new Btcr2Command() });
102
+ this.printResult(result);
201
103
  });
202
104
  }
203
105
 
204
106
  /**
205
- * A helper to invoke the command logic without forcibly exiting.
107
+ * Invokes a command with the provided request.
108
+ * @param {object} request The command request
109
+ * @param {any} request.options The command options
110
+ * @param {CommandRequest['action']} request.action The command action
111
+ * @param {Btcr2Command} request.command The command instance
112
+ * @returns {Promise<CommandResult>} The command result
206
113
  */
207
- private async invokeCommand({ options, action, command }: {
114
+ private async invokeCommand(request: {
208
115
  options: any;
209
- action: string;
116
+ action: CommandRequest['action'];
210
117
  command: Btcr2Command;
211
- }): Promise<void> {
118
+ }): Promise<CommandResult> {
119
+ return await request.command.execute({ action: request.action, options: request.options } as CommandRequest);
120
+ }
121
+
122
+ /**
123
+ * Runs the CLI with the provided argv or process.argv.
124
+ * @param {string[]} [argv] The argv array to use. Defaults to process.argv.
125
+ */
126
+ public async run(argv?: string[]) {
212
127
  try {
213
- await command.execute({ options, action });
214
- } catch (error) {
215
- console.error('Error executing command:', error);
128
+ const normalized = this.normalizeArgv(argv ?? process.argv);
129
+ await this.program.parseAsync(normalized, { from: 'node' });
130
+ if (!this.program.args.length) this.program.outputHelp();
131
+ } catch (error: any) {
132
+ this.handleError(error);
216
133
  }
217
134
  }
218
135
 
219
136
  /**
220
- * Parse and run the CLI.
137
+ * Normalizes argv to ensure it has at least two elements.
138
+ * @param {string[]} argv The argv array to normalize
139
+ * @returns {string[]} Normalized argv array
140
+ */
141
+ private normalizeArgv(argv: string[]): string[] {
142
+ if (argv.length >= 2) return argv;
143
+ if (argv.length === 1) return ['node', argv[0]];
144
+ return ['node', 'btcr2'];
145
+ }
146
+
147
+ /**
148
+ * Handles errors thrown during CLI execution.
149
+ * @param {unknown} error The error to handle
150
+ * @returns {void}
221
151
  */
222
- public run(argv?: string[]): void {
223
- if (argv) {
224
- this.CLI.parse(argv, { from: 'user' });
225
- } else {
226
- // parse real process.argv
227
- this.CLI.parse();
152
+ private handleError(error: unknown): void {
153
+ if (error instanceof CommanderError && (error.code === 'commander.helpDisplayed' || error.code === 'commander.help')) {
154
+ return;
155
+ }
156
+ if (error instanceof CLIError) {
157
+ console.error(error.message);
158
+ process.exitCode ??= 1;
159
+ return;
228
160
  }
161
+ console.error(error);
162
+ process.exitCode ??= 1;
163
+ }
229
164
 
230
- // If no subcommand was given, show help
231
- if (!this.CLI.args.length) {
232
- this.CLI.help();
165
+ /**
166
+ * Parses create command options and throws CLIError on invalid input.
167
+ * @param {object} options The create command options
168
+ * @param {string} options.type The identifier type
169
+ * @param {string} options.network The bitcoin network
170
+ * @param {string} options.bytes The genesis bytes as a hex string
171
+ * @returns {CreateCommandOptions} The parsed create command options
172
+ */
173
+ private parseCreateOptions(options: { type: string; network: string; bytes: string; }): CreateCommandOptions {
174
+ if (!['k', 'x'].includes(options.type)) {
175
+ throw new CLIError(
176
+ 'Invalid type. Must be "k" or "x".',
177
+ 'INVALID_ARGUMENT_ERROR',
178
+ options
179
+ );
180
+ }
181
+ if (!this.isNetworkValid(options.network)) {
182
+ throw new CLIError(
183
+ 'Invalid network. Must be one of "bitcoin", "testnet3", "testnet4", "signet", "mutinynet", or "regtest".',
184
+ 'INVALID_ARGUMENT_ERROR',
185
+ options
186
+ );
233
187
  }
188
+ if (Buffer.from(options.bytes, 'hex').length === 0) {
189
+ throw new CLIError(
190
+ 'Invalid bytes. Must be a non-empty hex string.',
191
+ 'INVALID_ARGUMENT_ERROR',
192
+ options
193
+ );
194
+ }
195
+ return {
196
+ type : options.type as CreateCommandOptions['type'],
197
+ network : options.network as NetworkOption,
198
+ bytes : options.bytes,
199
+ };
200
+ }
201
+
202
+ /**
203
+ * Parses resolve command options and throws CLIError on invalid input.
204
+ * @param {object} options The resolve command options
205
+ * @param {string} options.identifier The did:btcr2 identifier
206
+ * @param {string} [options.resolutionOptions] JSON string of resolution options
207
+ * @param {string} [options.resolutionOptionsPath] Path to a JSON file of resolution options
208
+ * @returns {Promise<ResolveCommandOptions>} The parsed resolve command options
209
+ */
210
+ private async parseResolveOptions(options: {
211
+ identifier: string;
212
+ resolutionOptions?: string;
213
+ resolutionOptionsPath?: string;
214
+ }): Promise<ResolveCommandOptions> {
215
+ this.validateIdentifier(options.identifier, options);
216
+ const resolutionOptions = await this.parseResolutionOptions(options);
217
+ return {
218
+ identifier : options.identifier,
219
+ ...(resolutionOptions && { options: resolutionOptions }),
220
+ };
221
+ }
222
+
223
+ /**
224
+ * Parses update command options and throws CLIError on invalid input.
225
+ * @param {object} options The update command options
226
+ * @param {string} options.identifier The did:btcr2 identifier
227
+ * @param {string} options.sourceDocument The source DID document as a JSON string
228
+ * @param {number} options.sourceVersionId The source version ID as a number
229
+ * @param {string} options.patch The JSON Patch operations as a JSON string array
230
+ * @param {string} options.verificationMethodId The DID document verification method ID as a string
231
+ * @param {string} options.beaconIds The beacon IDs as a JSON string array
232
+ * @returns {UpdateCommandOptions} The parsed update command options
233
+ */
234
+ private parseUpdateOptions(options: {
235
+ identifier: string;
236
+ sourceDocument: string;
237
+ sourceVersionId: number;
238
+ patch: string;
239
+ verificationMethodId: string;
240
+ beaconIds: string;
241
+ }): UpdateCommandOptions {
242
+ this.validateIdentifier(options.identifier, options);
243
+ const sourceDocument = this.parseJsonOption<UpdateCommandOptions['sourceDocument']>(
244
+ options.sourceDocument,
245
+ 'Invalid options. Must be a valid JSON string.',
246
+ options
247
+ );
248
+ const patch = this.parseJsonOption<UpdateCommandOptions['patch']>(
249
+ options.patch,
250
+ 'Invalid options. Must be a valid JSON string.',
251
+ options
252
+ );
253
+ const beaconIds = this.parseJsonOption<UpdateCommandOptions['beaconIds']>(
254
+ options.beaconIds,
255
+ 'Invalid options. Must be a valid JSON string.',
256
+ options
257
+ );
258
+
259
+ return {
260
+ identifier : options.identifier,
261
+ sourceDocument,
262
+ sourceVersionId : Number(options.sourceVersionId),
263
+ patch,
264
+ verificationMethodId : options.verificationMethodId,
265
+ beaconIds,
266
+ } as UpdateCommandOptions;
234
267
  }
235
- }
236
268
 
269
+ /**
270
+ * Parses a JSON option and throws a CLIError on failure.
271
+ * @param {string} value JSON string to parse
272
+ * @param {string} errorMessage Error message to use on failure
273
+ * @param {Record<string, any>} data Additional data to include in the error
274
+ * @returns {T} Parsed JSON object
275
+ */
276
+ private parseJsonOption<T>(value: string, errorMessage: string, data: Record<string, any>): T {
277
+ try {
278
+ return JSON.parse(value) as T;
279
+ } catch {
280
+ throw new CLIError(
281
+ errorMessage,
282
+ 'INVALID_ARGUMENT_ERROR',
283
+ data
284
+ );
285
+ }
286
+ }
237
287
 
238
- export default new DidBtcr2Cli().run();
288
+ /**
289
+ * Parses resolution options from JSON string or file path.
290
+ * @param {object} options The options containing resolution options
291
+ * @param {string} [options.resolutionOptions] JSON string of resolution options
292
+ * @param {string} [options.resolutionOptionsPath] Path to a JSON file of resolution options
293
+ * @returns {Promise<any>} The parsed resolution options
294
+ */
295
+ private async parseResolutionOptions(options: { resolutionOptions?: string; resolutionOptionsPath?: string; }): Promise<any> {
296
+ if (options.resolutionOptions) {
297
+ return this.parseJsonOption(options.resolutionOptions, 'Invalid options. Must be a valid JSON string.', options);
298
+ }
299
+ if (options.resolutionOptionsPath) {
300
+ try {
301
+ const data = await readFile(options.resolutionOptionsPath, 'utf-8');
302
+ return JSON.parse(data);
303
+ } catch {
304
+ throw new CLIError(
305
+ 'Invalid options path. Must be a valid path to a JSON file.',
306
+ 'INVALID_ARGUMENT_ERROR',
307
+ options
308
+ );
309
+ }
310
+ }
311
+ return undefined;
312
+ }
313
+
314
+ /**
315
+ * Validates the did:btcr2 identifier format.
316
+ * @param {string} identifier The identifier to validate
317
+ * @param {Record<string, any>} data Additional data to include in the error
318
+ * @returns {void}
319
+ */
320
+ private validateIdentifier(identifier: string, data: Record<string, any>): void {
321
+ try {
322
+ Identifier.decode(identifier);
323
+ } catch {
324
+ throw new CLIError(
325
+ 'Invalid identifier. Must be a valid did:btcr2 identifier.',
326
+ 'INVALID_ARGUMENT_ERROR',
327
+ data
328
+ );
329
+ }
330
+ }
331
+
332
+ /**
333
+ * Validates if the provided network is supported.
334
+ * @param {string} network The network to validate
335
+ * @returns {boolean} True if the network is valid
336
+ */
337
+ private isNetworkValid(network: string): network is NetworkOption {
338
+ return SUPPORTED_NETWORKS.includes(network as NetworkOption);
339
+ }
340
+
341
+ /**
342
+ * Prints the command result to the console.
343
+ * @param {CommandResult} result The command result to print
344
+ * @returns {void}
345
+ */
346
+ private printResult(result: CommandResult): void {
347
+ switch (result.action) {
348
+ case 'create':
349
+ this.log(result.did);
350
+ break;
351
+ case 'resolve':
352
+ case 'read':
353
+ this.log(result.resolution);
354
+ break;
355
+ case 'update':
356
+ this.log(result.metadata);
357
+ break;
358
+ case 'deactivate':
359
+ case 'delete':
360
+ this.log(result.message);
361
+ break;
362
+ default:
363
+ this.log(result);
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Logs a payload to the console, formatting objects as JSON.
369
+ * @param {unknown} payload The payload to log
370
+ * @returns {void}
371
+ */
372
+ private log(payload: unknown): void {
373
+ if (typeof payload === 'string') {
374
+ console.log(payload);
375
+ return;
376
+ }
377
+ console.log(JSON.stringify(payload, null, 2));
378
+ }
379
+ }
package/src/command.ts CHANGED
@@ -1,49 +1,77 @@
1
- import { IdentifierTypes, Logger, MethodError } from '@did-btcr2/common';
2
- import { DidBtcr2, DidResolutionOptions } from '@did-btcr2/method';
1
+ import { IdentifierTypes, Logger, MethodError, PatchOperation } from '@did-btcr2/common';
2
+ import { DidBtcr2, DidDocument, DidResolutionOptions, DidResolutionResult, SignalsMetadata } from '@did-btcr2/method';
3
+
4
+ export type NetworkOption = 'bitcoin' | 'testnet3' | 'testnet4' | 'signet' | 'mutinynet' | 'regtest';
3
5
 
4
6
  export interface CommandInterface {
5
- execute(params: { options?: any; action?: string }): Promise<void>;
7
+ execute(request: CommandRequest): Promise<CommandResult>;
8
+ }
9
+
10
+ export interface CreateCommandOptions {
11
+ type: 'k' | 'x';
12
+ bytes: string;
13
+ network: NetworkOption;
14
+ }
15
+
16
+ export interface ResolveCommandOptions {
17
+ identifier: string;
18
+ options?: DidResolutionOptions;
19
+ }
20
+
21
+ export interface UpdateCommandOptions {
22
+ identifier: string;
23
+ sourceDocument: DidDocument;
24
+ sourceVersionId: number;
25
+ patch: PatchOperation[];
26
+ verificationMethodId: string;
27
+ beaconIds: string[];
6
28
  }
7
29
 
8
- interface ExecuteParams {
9
- options?: any;
10
- action?: string
30
+ export interface DeactivateCommandOptions {
31
+ // Placeholder for future deactivate payload once implemented.
11
32
  }
12
33
 
34
+ export type CommandRequest =
35
+ | { action: 'create'; options: CreateCommandOptions; }
36
+ | { action: 'resolve' | 'read'; options: ResolveCommandOptions; }
37
+ | { action: 'update'; options: UpdateCommandOptions; }
38
+ | { action: 'deactivate' | 'delete'; options: DeactivateCommandOptions; };
39
+
40
+ export type CommandResult =
41
+ | { action: 'create'; did: string; }
42
+ | { action: 'resolve' | 'read'; resolution: DidResolutionResult; }
43
+ | { action: 'update'; metadata: SignalsMetadata; }
44
+ | { action: 'deactivate' | 'delete'; message: string; };
45
+
13
46
  export default class Btcr2Command implements CommandInterface {
14
- async execute({ options, action }: ExecuteParams): Promise<void> {
15
- try {
16
- switch (action) {
17
- case 'create':{
18
- const { type, bytes } = options as { type: string; bytes: string };
19
- const idType = type === 'k' ? IdentifierTypes.KEY : IdentifierTypes.EXTERNAL;
20
- const genesisBytes = Buffer.from(bytes, 'hex').toArray().toUint8Array();
21
- const did = await DidBtcr2.create({ idType, genesisBytes });
22
- console.log(did);
23
- break;
24
- }
25
- case 'read':
26
- case 'resolve': {
27
- const { identifier, options: resolutionOptions } = options as DidResolutionOptions;
28
- const resolutionResult = await DidBtcr2.resolve(identifier, resolutionOptions);
29
- console.log(JSON.stringify(resolutionResult, null, 2));
30
- break;
31
- }
32
- case 'update': {
33
- const update = await DidBtcr2.update(options);
34
- console.log(JSON.stringify(update, null, 2));
35
- break;
36
- }
37
- case 'delete':
38
- case 'deactivate':
39
- // await DidBtcr2.deactivate(options);
40
- Logger.warn('// TODO: Update once DidBtcr2.deactivate implemented');
41
- break;
42
- default:
43
- throw new MethodError(`Invalid command: ${action}`, 'INVALID_COMMAND');
47
+ async execute(request: CommandRequest): Promise<CommandResult> {
48
+ const action = request.action;
49
+ switch (action) {
50
+ case 'create': {
51
+ const { type, bytes, network } = request.options;
52
+ const idType = type === 'k' ? IdentifierTypes.KEY : IdentifierTypes.EXTERNAL;
53
+ const genesisBytes = Buffer.from(bytes, 'hex').toArray().toUint8Array();
54
+ const did = await DidBtcr2.create({ idType, genesisBytes, options: { network } });
55
+ return { action: 'create', did };
56
+ }
57
+ case 'read':
58
+ case 'resolve': {
59
+ const { identifier, options } = request.options;
60
+ const resolution = await DidBtcr2.resolve(identifier, options);
61
+ return { action: request.action, resolution };
62
+ }
63
+ case 'update': {
64
+ const metadata = await DidBtcr2.update(request.options as any);
65
+ return { action: 'update', metadata };
66
+ }
67
+ case 'delete':
68
+ case 'deactivate': {
69
+ Logger.warn('// TODO: Update once DidBtcr2.deactivate implemented');
70
+ return { action: 'deactivate', message: 'Deactivate not yet implemented' };
71
+ }
72
+ default:{
73
+ throw new MethodError(`Invalid command: ${action}`, 'INVALID_COMMAND');
44
74
  }
45
- } catch (error: any) {
46
- console.error(error);
47
75
  }
48
76
  }
49
77
  }
package/src/error.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { DidMethodError } from '@did-btcr2/common';
2
+
3
+ /**
4
+ * Custom CLI Error class extending DidMethodError.
5
+ */
6
+ export class CLIError extends DidMethodError {
7
+ constructor(message: string, type: string = 'CLIError', data?: Record<string, any>) {
8
+ super(message, { type, name: type, data });
9
+ }
10
+ }
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './cli.js';
2
- export * from './command.js';
2
+ export * from './command.js';
3
+ export * from './error.js';