paymongo-cli 1.4.4 → 1.4.7

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 (155) hide show
  1. package/.github/copilot-instructions.md +95 -95
  2. package/CHANGELOG.md +85 -1
  3. package/LICENSE +20 -20
  4. package/dist/.tsbuildinfo +1 -1
  5. package/dist/commands/config.js +30 -15
  6. package/dist/commands/dev/logs.js +3 -3
  7. package/dist/commands/dev/status.js +2 -2
  8. package/dist/commands/dev/stop.js +3 -3
  9. package/dist/commands/dev.js +9 -8
  10. package/dist/commands/env.js +6 -6
  11. package/dist/commands/generate/templates/checkout-page/index.js +520 -520
  12. package/dist/commands/generate/templates/payment-intent/javascript.js +68 -68
  13. package/dist/commands/generate/templates/payment-intent/typescript.js +92 -92
  14. package/dist/commands/generate/templates/webhook-handler/javascript.js +192 -147
  15. package/dist/commands/generate/templates/webhook-handler/typescript.js +147 -117
  16. package/dist/commands/generate.js +43 -37
  17. package/dist/commands/init.js +25 -8
  18. package/dist/commands/login.js +56 -19
  19. package/dist/commands/payments.js +9 -8
  20. package/dist/commands/team/index.js +11 -9
  21. package/dist/commands/trigger.js +58 -18
  22. package/dist/commands/webhooks.js +8 -7
  23. package/dist/index.js +9 -2
  24. package/dist/services/analytics/service.js +24 -19
  25. package/dist/services/api/client.js +16 -16
  26. package/dist/services/config/manager.js +6 -8
  27. package/dist/services/dev/process-manager.js +30 -32
  28. package/dist/services/dev/server.js +45 -39
  29. package/dist/services/team/service.js +4 -1
  30. package/dist/types/schemas.js +38 -9
  31. package/dist/utils/bulk.js +36 -4
  32. package/dist/utils/constants.js +11 -1
  33. package/dist/utils/errors.js +6 -0
  34. package/dist/utils/validator.js +10 -9
  35. package/dist/utils/webhook-store.js +18 -15
  36. package/eslint.config.ts +70 -70
  37. package/package.json +2 -2
  38. package/coverage/base.css +0 -224
  39. package/coverage/block-navigation.js +0 -87
  40. package/coverage/favicon.png +0 -0
  41. package/coverage/index.html +0 -281
  42. package/coverage/lcov-report/base.css +0 -224
  43. package/coverage/lcov-report/block-navigation.js +0 -87
  44. package/coverage/lcov-report/favicon.png +0 -0
  45. package/coverage/lcov-report/index.html +0 -281
  46. package/coverage/lcov-report/prettify.css +0 -1
  47. package/coverage/lcov-report/prettify.js +0 -2
  48. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  49. package/coverage/lcov-report/sorter.js +0 -210
  50. package/coverage/lcov.info +0 -5053
  51. package/coverage/prettify.css +0 -1
  52. package/coverage/prettify.js +0 -2
  53. package/coverage/sort-arrow-sprite.png +0 -0
  54. package/coverage/sorter.js +0 -210
  55. package/dist/commands/config.d.ts +0 -21
  56. package/dist/commands/config.d.ts.map +0 -1
  57. package/dist/commands/config.js.map +0 -1
  58. package/dist/commands/dev.d.ts +0 -16
  59. package/dist/commands/dev.d.ts.map +0 -1
  60. package/dist/commands/dev.js.map +0 -1
  61. package/dist/commands/env.d.ts +0 -4
  62. package/dist/commands/env.d.ts.map +0 -1
  63. package/dist/commands/env.js.map +0 -1
  64. package/dist/commands/init.d.ts +0 -15
  65. package/dist/commands/init.d.ts.map +0 -1
  66. package/dist/commands/init.js.map +0 -1
  67. package/dist/commands/login.d.ts +0 -20
  68. package/dist/commands/login.d.ts.map +0 -1
  69. package/dist/commands/login.js.map +0 -1
  70. package/dist/commands/payments.d.ts +0 -41
  71. package/dist/commands/payments.d.ts.map +0 -1
  72. package/dist/commands/payments.js.map +0 -1
  73. package/dist/commands/team/index.d.ts +0 -4
  74. package/dist/commands/team/index.d.ts.map +0 -1
  75. package/dist/commands/team/index.js.map +0 -1
  76. package/dist/commands/trigger.d.ts +0 -4
  77. package/dist/commands/trigger.d.ts.map +0 -1
  78. package/dist/commands/trigger.js.map +0 -1
  79. package/dist/commands/webhooks.d.ts +0 -23
  80. package/dist/commands/webhooks.d.ts.map +0 -1
  81. package/dist/commands/webhooks.js.map +0 -1
  82. package/dist/index.d.ts +0 -3
  83. package/dist/index.d.ts.map +0 -1
  84. package/dist/index.js.map +0 -1
  85. package/dist/services/analytics/service.d.ts +0 -35
  86. package/dist/services/analytics/service.d.ts.map +0 -1
  87. package/dist/services/analytics/service.js.map +0 -1
  88. package/dist/services/api/client.d.ts +0 -26
  89. package/dist/services/api/client.d.ts.map +0 -1
  90. package/dist/services/api/client.js.map +0 -1
  91. package/dist/services/api/rate-limiter.d.ts +0 -64
  92. package/dist/services/api/rate-limiter.d.ts.map +0 -1
  93. package/dist/services/api/rate-limiter.js.map +0 -1
  94. package/dist/services/api/undici-client.d.ts +0 -39
  95. package/dist/services/api/undici-client.d.ts.map +0 -1
  96. package/dist/services/api/undici-client.js +0 -288
  97. package/dist/services/api/undici-client.js.map +0 -1
  98. package/dist/services/config/manager.d.ts +0 -16
  99. package/dist/services/config/manager.d.ts.map +0 -1
  100. package/dist/services/config/manager.js.map +0 -1
  101. package/dist/services/dev/process-manager.d.ts +0 -50
  102. package/dist/services/dev/process-manager.d.ts.map +0 -1
  103. package/dist/services/dev/process-manager.js.map +0 -1
  104. package/dist/services/github/auth.d.ts +0 -15
  105. package/dist/services/github/auth.d.ts.map +0 -1
  106. package/dist/services/github/auth.js +0 -79
  107. package/dist/services/github/auth.js.map +0 -1
  108. package/dist/services/github/client.d.ts +0 -95
  109. package/dist/services/github/client.d.ts.map +0 -1
  110. package/dist/services/github/client.js +0 -130
  111. package/dist/services/github/client.js.map +0 -1
  112. package/dist/services/github/sync.d.ts +0 -26
  113. package/dist/services/github/sync.d.ts.map +0 -1
  114. package/dist/services/github/sync.js +0 -203
  115. package/dist/services/github/sync.js.map +0 -1
  116. package/dist/services/payments/simulator.d.ts +0 -28
  117. package/dist/services/payments/simulator.d.ts.map +0 -1
  118. package/dist/services/payments/simulator.js.map +0 -1
  119. package/dist/services/team/service.d.ts +0 -44
  120. package/dist/services/team/service.d.ts.map +0 -1
  121. package/dist/services/team/service.js.map +0 -1
  122. package/dist/services/web/server.d.ts +0 -31
  123. package/dist/services/web/server.d.ts.map +0 -1
  124. package/dist/services/web/server.js +0 -206
  125. package/dist/services/web/server.js.map +0 -1
  126. package/dist/types/paymongo.d.ts +0 -204
  127. package/dist/types/paymongo.d.ts.map +0 -1
  128. package/dist/types/paymongo.js.map +0 -1
  129. package/dist/types/schemas.d.ts +0 -80
  130. package/dist/types/schemas.d.ts.map +0 -1
  131. package/dist/types/schemas.js.map +0 -1
  132. package/dist/utils/bulk.d.ts +0 -62
  133. package/dist/utils/bulk.d.ts.map +0 -1
  134. package/dist/utils/bulk.js.map +0 -1
  135. package/dist/utils/cache.d.ts +0 -22
  136. package/dist/utils/cache.d.ts.map +0 -1
  137. package/dist/utils/cache.js.map +0 -1
  138. package/dist/utils/constants.d.ts +0 -32
  139. package/dist/utils/constants.d.ts.map +0 -1
  140. package/dist/utils/constants.js.map +0 -1
  141. package/dist/utils/errors.d.ts +0 -34
  142. package/dist/utils/errors.d.ts.map +0 -1
  143. package/dist/utils/errors.js.map +0 -1
  144. package/dist/utils/logger.d.ts +0 -20
  145. package/dist/utils/logger.d.ts.map +0 -1
  146. package/dist/utils/logger.js.map +0 -1
  147. package/dist/utils/spinner.d.ts +0 -17
  148. package/dist/utils/spinner.d.ts.map +0 -1
  149. package/dist/utils/spinner.js.map +0 -1
  150. package/dist/utils/validator.d.ts +0 -10
  151. package/dist/utils/validator.d.ts.map +0 -1
  152. package/dist/utils/validator.js.map +0 -1
  153. package/dist/utils/webhook-store.d.ts +0 -22
  154. package/dist/utils/webhook-store.d.ts.map +0 -1
  155. package/dist/utils/webhook-store.js.map +0 -1
@@ -2,15 +2,18 @@ import * as http from 'http';
2
2
  import * as crypto from 'crypto';
3
3
  import chalk from 'chalk';
4
4
  import { AnalyticsService } from '../analytics/service.js';
5
+ import Logger from '../../utils/logger.js';
5
6
  export class DevServer {
6
7
  server;
7
8
  port;
8
9
  config;
9
10
  analytics;
11
+ logger;
10
12
  constructor(port, config) {
11
13
  this.port = port;
12
14
  this.config = config;
13
15
  this.analytics = new AnalyticsService(config);
16
+ this.logger = new Logger();
14
17
  this.server = http.createServer((req, res) => {
15
18
  this.handleWebhookRequest(req, res);
16
19
  });
@@ -18,7 +21,7 @@ export class DevServer {
18
21
  async start() {
19
22
  return new Promise((resolve, reject) => {
20
23
  this.server.listen(this.port, () => {
21
- console.log(chalk.green('✓'), `Webhook server listening on http://localhost:${this.port}`);
24
+ this.logger.success(`Webhook server listening on http://localhost:${this.port}`);
22
25
  resolve();
23
26
  });
24
27
  this.server.on('error', (error) => {
@@ -29,7 +32,7 @@ export class DevServer {
29
32
  async stop() {
30
33
  return new Promise((resolve) => {
31
34
  this.server.close(() => {
32
- console.log(chalk.yellow('✓'), 'Webhook server stopped');
35
+ this.logger.warning('Webhook server stopped');
33
36
  resolve();
34
37
  });
35
38
  });
@@ -46,42 +49,45 @@ export class DevServer {
46
49
  body += chunk.toString();
47
50
  });
48
51
  req.on('end', () => {
49
- try {
50
- const event = JSON.parse(body);
51
- const signatureValid = this.verifyWebhookSignature(req, body);
52
- if (!signatureValid) {
53
- console.log(chalk.red('⚠️'), 'Webhook signature verification failed');
54
- res.writeHead(401, { 'Content-Type': 'application/json' });
55
- res.end(JSON.stringify({ error: 'Invalid signature' }));
56
- this.analytics.recordEvent({
57
- type: event.data?.type || 'unknown',
58
- success: false,
59
- error: 'Invalid signature',
60
- data: event.data?.attributes,
61
- });
62
- return;
63
- }
64
- this.logWebhookEvent(event);
65
- res.writeHead(200, { 'Content-Type': 'application/json' });
66
- res.end(JSON.stringify({ success: true }));
67
- }
68
- catch (error) {
69
- console.error(chalk.red('✗'), 'Failed to process webhook:', error.message);
70
- res.writeHead(400, { 'Content-Type': 'application/json' });
71
- res.end(JSON.stringify({ error: 'Invalid JSON' }));
72
- this.analytics.recordEvent({
73
- type: 'unknown',
52
+ void this.processWebhookBody(body, req, res);
53
+ });
54
+ }
55
+ async processWebhookBody(body, req, res) {
56
+ try {
57
+ const event = JSON.parse(body);
58
+ const signatureValid = this.verifyWebhookSignature(req, body);
59
+ if (!signatureValid) {
60
+ this.logger.failure('Webhook signature verification failed');
61
+ res.writeHead(401, { 'Content-Type': 'application/json' });
62
+ res.end(JSON.stringify({ error: 'Invalid signature' }));
63
+ await this.analytics.recordEvent({
64
+ type: event.data?.type || 'unknown',
74
65
  success: false,
75
- error: 'Invalid JSON',
66
+ error: 'Invalid signature',
67
+ data: event.data?.attributes,
76
68
  });
69
+ return;
77
70
  }
78
- });
71
+ await this.logWebhookEvent(event);
72
+ res.writeHead(200, { 'Content-Type': 'application/json' });
73
+ res.end(JSON.stringify({ success: true }));
74
+ }
75
+ catch (error) {
76
+ this.logger.error('Failed to process webhook:', error.message);
77
+ res.writeHead(400, { 'Content-Type': 'application/json' });
78
+ res.end(JSON.stringify({ error: 'Invalid JSON' }));
79
+ await this.analytics.recordEvent({
80
+ type: 'unknown',
81
+ success: false,
82
+ error: 'Invalid JSON',
83
+ });
84
+ }
79
85
  }
80
- logWebhookEvent(event) {
86
+ async logWebhookEvent(event) {
81
87
  const timestamp = new Date().toLocaleTimeString();
82
88
  const eventType = event.data?.type || 'unknown';
83
89
  const eventId = event.data?.id || 'unknown';
84
- this.analytics.recordEvent({
90
+ await this.analytics.recordEvent({
85
91
  type: eventType,
86
92
  success: true,
87
93
  data: event.data?.attributes,
@@ -101,23 +107,23 @@ export class DevServer {
101
107
  }
102
108
  verifyWebhookSignature(req, body) {
103
109
  if (!this.config.dev.verifyWebhookSignatures) {
104
- console.log(chalk.yellow('ℹ️'), 'Webhook signature verification disabled in config');
110
+ this.logger.warn('Webhook signature verification disabled in config');
105
111
  return true;
106
112
  }
107
113
  const signatureHeader = req.headers['paymongo-signature'];
108
114
  if (!signatureHeader) {
109
- console.log(chalk.red('⚠️'), 'Signature verification required but no signature header found');
115
+ this.logger.failure('Signature verification required but no signature header found');
110
116
  return false;
111
117
  }
112
118
  const signatureParts = signatureHeader.split(',');
113
119
  if (signatureParts.length < 2) {
114
- console.log(chalk.red('⚠️'), 'Invalid signature format');
120
+ this.logger.failure('Invalid signature format');
115
121
  return false;
116
122
  }
117
123
  const timestamp = signatureParts.find((part) => part.startsWith('t='))?.split('=')[1];
118
124
  const signature = signatureParts.find((part) => part.startsWith('te='))?.split('=')[1];
119
125
  if (!timestamp || !signature) {
120
- console.log(chalk.red('⚠️'), 'Missing timestamp or signature in header');
126
+ this.logger.failure('Missing timestamp or signature in header');
121
127
  return false;
122
128
  }
123
129
  const webhookId = signatureParts.find((part) => part.startsWith('li='))?.split('=')[1];
@@ -130,12 +136,12 @@ export class DevServer {
130
136
  else {
131
137
  secretKeys = Object.values(webhookSecrets).filter((secret) => typeof secret === 'string' && secret.length > 0);
132
138
  if (webhookId && secretKeys.length > 0) {
133
- console.log(chalk.yellow('⚠️'), `No webhook secret found for id ${webhookId}. Update your configuration.`);
139
+ this.logger.warning(`No webhook secret found for id ${webhookId}. Update your configuration.`);
134
140
  return false;
135
141
  }
136
142
  }
137
143
  if (secretKeys.length === 0) {
138
- console.log(chalk.yellow('⚠️'), 'Signature verification enabled but no webhook secrets configured');
144
+ this.logger.warning('Signature verification enabled but no webhook secrets configured');
139
145
  return true;
140
146
  }
141
147
  let isValid = false;
@@ -155,11 +161,11 @@ export class DevServer {
155
161
  }
156
162
  }
157
163
  if (isValid) {
158
- console.log(chalk.green('✓'), 'Signature verified successfully');
164
+ this.logger.success('Signature verified successfully');
159
165
  return true;
160
166
  }
161
167
  else {
162
- console.log(chalk.red('✗'), 'Signature verification failed');
168
+ this.logger.failure('Signature verification failed');
163
169
  return false;
164
170
  }
165
171
  }
@@ -41,7 +41,7 @@ export class TeamService {
41
41
  await this.config.save(config);
42
42
  return bundle;
43
43
  }
44
- async importKeyBundle(bundle, memberName) {
44
+ async importKeyBundle(bundle, memberName, options = {}) {
45
45
  const config = await this.config.load();
46
46
  if (!config) {
47
47
  throw new PayMongoError('No configuration found. Run "paymongo init" first.', 'CONFIG_NOT_FOUND', 400);
@@ -72,6 +72,9 @@ export class TeamService {
72
72
  if (!config.apiKeys[env]) {
73
73
  config.apiKeys[env] = bundle.keys[env];
74
74
  }
75
+ else if (options.force) {
76
+ config.apiKeys[env] = bundle.keys[env];
77
+ }
75
78
  else {
76
79
  console.log(`⚠️ ${env.toUpperCase()} keys already exist. Use --force to overwrite.`);
77
80
  }
@@ -12,23 +12,52 @@ const DevConfigSchema = z.object({
12
12
  autoRegisterWebhook: z.boolean(),
13
13
  verifyWebhookSignatures: z.boolean(),
14
14
  });
15
- const TeamConfigSchema = z.object({
16
- githubToken: z.string().optional(),
17
- repo: z.string().optional(),
18
- branch: z.string().optional(),
19
- }).optional();
20
15
  export const PayMongoConfigSchema = z.object({
21
16
  version: z.string().min(1, 'Version is required'),
22
17
  projectName: z.string().min(1, 'Project name is required'),
23
18
  environment: z.enum(['test', 'live']),
24
- apiKeys: z.object({
19
+ apiKeys: z
20
+ .object({
25
21
  test: ApiKeysSchema.optional(),
26
22
  live: ApiKeysSchema.optional(),
27
- }).refine((keys) => keys.test !== undefined || keys.live !== undefined, { message: 'At least one environment API keys must be configured' }),
23
+ })
24
+ .optional(),
28
25
  webhooks: WebhooksConfigSchema,
29
- webhookSecrets: z.record(z.string(), z.string()),
26
+ webhookSecrets: z.record(z.string(), z.string()).optional(),
30
27
  dev: DevConfigSchema,
31
- team: TeamConfigSchema,
28
+ team: z
29
+ .object({
30
+ name: z.string().optional(),
31
+ members: z
32
+ .array(z.object({
33
+ name: z.string(),
34
+ email: z.string().optional(),
35
+ addedAt: z.number(),
36
+ sharedKeys: z.array(z.string()).optional(),
37
+ }))
38
+ .optional(),
39
+ sharedKeyBundles: z
40
+ .array(z.object({
41
+ id: z.string(),
42
+ createdAt: z.number(),
43
+ environments: z.array(z.enum(['test', 'live'])),
44
+ sharedWith: z.array(z.string()),
45
+ }))
46
+ .optional(),
47
+ })
48
+ .optional(),
49
+ registeredWebhooks: z
50
+ .array(z.object({
51
+ id: z.string(),
52
+ url: z.string(),
53
+ createdAt: z.number(),
54
+ }))
55
+ .optional(),
56
+ analytics: z
57
+ .object({
58
+ enabled: z.boolean(),
59
+ })
60
+ .optional(),
32
61
  });
33
62
  export function validateConfig(config) {
34
63
  const result = PayMongoConfigSchema.safeParse(config);
@@ -32,8 +32,24 @@ export class BulkOperations {
32
32
  return filename;
33
33
  }
34
34
  static async importWebhooks(filename) {
35
- const content = await fs.readFile(filename, 'utf-8');
36
- const data = JSON.parse(content);
35
+ let content;
36
+ try {
37
+ content = await fs.readFile(filename, 'utf-8');
38
+ }
39
+ catch (error) {
40
+ const code = error.code;
41
+ if (code === 'ENOENT') {
42
+ throw new PayMongoError(`File not found: ${filename}`, 'FILE_NOT_FOUND', 404);
43
+ }
44
+ throw new PayMongoError(`Cannot read file: ${filename}`, 'FILE_READ_ERROR', 400);
45
+ }
46
+ let data;
47
+ try {
48
+ data = JSON.parse(content);
49
+ }
50
+ catch {
51
+ throw new PayMongoError(`Invalid JSON in ${filename}`, 'INVALID_JSON', 400);
52
+ }
37
53
  this.validateImportData(data, 'webhooks');
38
54
  return {
39
55
  webhooks: data.data,
@@ -41,8 +57,24 @@ export class BulkOperations {
41
57
  };
42
58
  }
43
59
  static async importPayments(filename) {
44
- const content = await fs.readFile(filename, 'utf-8');
45
- const data = JSON.parse(content);
60
+ let content;
61
+ try {
62
+ content = await fs.readFile(filename, 'utf-8');
63
+ }
64
+ catch (error) {
65
+ const code = error.code;
66
+ if (code === 'ENOENT') {
67
+ throw new PayMongoError(`File not found: ${filename}`, 'FILE_NOT_FOUND', 404);
68
+ }
69
+ throw new PayMongoError(`Cannot read file: ${filename}`, 'FILE_READ_ERROR', 400);
70
+ }
71
+ let data;
72
+ try {
73
+ data = JSON.parse(content);
74
+ }
75
+ catch {
76
+ throw new PayMongoError(`Invalid JSON in ${filename}`, 'INVALID_JSON', 400);
77
+ }
46
78
  this.validateImportData(data, 'payments');
47
79
  return {
48
80
  payments: data.data,
@@ -12,10 +12,20 @@ export const ENVIRONMENTS = ['test', 'live'];
12
12
  export const CONFIG_FILE_NAME = '.paymongo';
13
13
  export const ENV_FILE_NAME = '.env';
14
14
  export const CLI_NAME = 'paymongo';
15
- export const CLI_VERSION = '1.0.0';
15
+ import { createRequire } from 'module';
16
+ const _require = createRequire(import.meta.url);
17
+ const _pkg = _require('../../package.json');
18
+ export const CLI_VERSION = _pkg.version;
16
19
  export const REQUEST_TIMEOUT = 30000;
17
20
  export const MAX_RETRIES = 3;
18
21
  export const RETRY_DELAY = 1000;
22
+ export const CACHE_TTL = 2 * 60 * 1000;
23
+ export const RATE_LIMIT_WINDOW_MS = 60 * 1000;
24
+ export const RATE_LIMIT_DEFAULT_MAX = 100;
25
+ export const RATE_LIMIT_WEBHOOKS_MAX = 30;
26
+ export const RATE_LIMIT_PAYMENTS_MAX = 60;
27
+ export const RATE_LIMIT_REFUNDS_MAX = 20;
28
+ export const RATE_LIMIT_ENV_MULTIPLIER = 0.5;
19
29
  export const DEFAULT_DEV_PORT = 3000;
20
30
  export const DEFAULT_WEBHOOK_PATH = '/webhook';
21
31
  export const LOG_LEVELS = ['error', 'warn', 'info', 'debug'];
@@ -56,6 +56,12 @@ export class WebhookError extends Error {
56
56
  this.name = 'WebhookError';
57
57
  }
58
58
  }
59
+ export class CommandError extends Error {
60
+ constructor(message) {
61
+ super(message || 'Command failed');
62
+ this.name = 'CommandError';
63
+ }
64
+ }
59
65
  export async function withRetry(operation, options = {}) {
60
66
  const { maxRetries = 3, delayMs = 1000, backoffMultiplier = 2, silent = false, retryCondition = (error) => {
61
67
  return (error.name === 'NetworkError' ||
@@ -1,11 +1,5 @@
1
- export class ValidationError extends Error {
2
- field;
3
- constructor(message, field) {
4
- super(message);
5
- this.field = field;
6
- this.name = 'ValidationError';
7
- }
8
- }
1
+ import { ValidationError } from './errors.js';
2
+ export { ValidationError };
9
3
  export function validateApiKey(key, type) {
10
4
  if (!key || typeof key !== 'string') {
11
5
  return false;
@@ -16,7 +10,14 @@ export function validateApiKey(key, type) {
16
10
  }
17
11
  export function validateWebhookUrl(url) {
18
12
  try {
19
- const parsedUrl = new URL(url);
13
+ const trimmed = url.trim();
14
+ if (trimmed.length > 2048) {
15
+ return false;
16
+ }
17
+ const parsedUrl = new URL(trimmed);
18
+ if (parsedUrl.username || parsedUrl.password) {
19
+ return false;
20
+ }
20
21
  return (parsedUrl.protocol === 'https:' ||
21
22
  parsedUrl.hostname === 'localhost' ||
22
23
  parsedUrl.hostname === '127.0.0.1');
@@ -1,23 +1,25 @@
1
- import * as fs from 'fs';
1
+ import fs from 'fs/promises';
2
2
  import * as path from 'path';
3
3
  import * as os from 'os';
4
4
  class WebhookEventStore {
5
5
  storePath;
6
+ storeDir;
6
7
  constructor() {
7
- const paymongoDir = path.join(os.homedir(), '.paymongo');
8
- this.storePath = path.join(paymongoDir, 'webhook-events.json');
9
- if (!fs.existsSync(paymongoDir)) {
10
- fs.mkdirSync(paymongoDir, { recursive: true });
11
- }
8
+ this.storeDir = path.join(os.homedir(), '.paymongo');
9
+ this.storePath = path.join(this.storeDir, 'webhook-events.json');
10
+ }
11
+ async ensureDir() {
12
+ await fs.mkdir(this.storeDir, { recursive: true });
12
13
  }
13
14
  async storeEvent(event) {
14
15
  try {
16
+ await this.ensureDir();
15
17
  const events = await this.loadEvents();
16
18
  events.push(event);
17
19
  if (events.length > 1000) {
18
20
  events.splice(0, events.length - 1000);
19
21
  }
20
- fs.writeFileSync(this.storePath, JSON.stringify(events, null, 2));
22
+ await fs.writeFile(this.storePath, JSON.stringify(events, null, 2));
21
23
  }
22
24
  catch (error) {
23
25
  console.warn('Failed to store webhook event:', error);
@@ -25,13 +27,13 @@ class WebhookEventStore {
25
27
  }
26
28
  async loadEvents() {
27
29
  try {
28
- if (!fs.existsSync(this.storePath)) {
29
- return [];
30
- }
31
- const data = fs.readFileSync(this.storePath, 'utf-8');
30
+ const data = await fs.readFile(this.storePath, 'utf-8');
32
31
  return JSON.parse(data);
33
32
  }
34
- catch (_error) {
33
+ catch (error) {
34
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
35
+ return [];
36
+ }
35
37
  return [];
36
38
  }
37
39
  }
@@ -45,11 +47,12 @@ class WebhookEventStore {
45
47
  }
46
48
  async clearEvents() {
47
49
  try {
48
- if (fs.existsSync(this.storePath)) {
49
- fs.unlinkSync(this.storePath);
50
- }
50
+ await fs.unlink(this.storePath);
51
51
  }
52
52
  catch (error) {
53
+ if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
54
+ return;
55
+ }
53
56
  console.warn('Failed to clear webhook events:', error);
54
57
  }
55
58
  }
package/eslint.config.ts CHANGED
@@ -1,70 +1,70 @@
1
- import eslint from '@eslint/js';
2
- import tseslint from 'typescript-eslint';
3
- import globals from 'globals';
4
-
5
- export default tseslint.config(
6
- eslint.configs.recommended,
7
- ...tseslint.configs.recommended,
8
- {
9
- files: ['src/**/*.ts'],
10
- languageOptions: {
11
- ecmaVersion: 2022,
12
- sourceType: 'module',
13
- globals: {
14
- ...globals.node,
15
- ...globals.es2022,
16
- },
17
- parserOptions: {
18
- project: './tsconfig.json',
19
- },
20
- },
21
- rules: {
22
- 'no-unused-vars': 'off',
23
- '@typescript-eslint/no-unused-vars': ['warn', {
24
- argsIgnorePattern: '^_',
25
- varsIgnorePattern: '^_',
26
- caughtErrorsIgnorePattern: '^_'
27
- }],
28
- 'no-console': 'off',
29
- 'no-redeclare': 'off',
30
- '@typescript-eslint/no-redeclare': 'error',
31
- 'no-shadow': 'off',
32
- '@typescript-eslint/no-shadow': 'error',
33
- 'no-undef': 'off',
34
- 'no-empty': 'error',
35
- '@typescript-eslint/explicit-function-return-type': 'off',
36
- '@typescript-eslint/no-explicit-any': 'warn',
37
- '@typescript-eslint/no-non-null-assertion': 'warn',
38
- eqeqeq: ['error', 'always'],
39
- curly: ['error', 'all'],
40
- 'no-var': 'error',
41
- 'prefer-const': 'error',
42
- },
43
- },
44
- {
45
- files: ['tests/**/*.ts'],
46
- languageOptions: {
47
- ecmaVersion: 2022,
48
- sourceType: 'module',
49
- globals: {
50
- ...globals.node,
51
- ...globals.es2022,
52
- ...globals.jest,
53
- },
54
- },
55
- rules: {
56
- 'no-unused-vars': 'off',
57
- '@typescript-eslint/no-unused-vars': ['warn', {
58
- argsIgnorePattern: '^_',
59
- varsIgnorePattern: '^_',
60
- caughtErrorsIgnorePattern: '^_'
61
- }],
62
- '@typescript-eslint/no-explicit-any': 'off', // Allow any in tests for mocking
63
- 'no-console': 'off',
64
- 'no-undef': 'off',
65
- },
66
- },
67
- {
68
- ignores: ['dist/', 'node_modules/', '*.js', '*.cjs', 'coverage/'],
69
- }
70
- );
1
+ import eslint from '@eslint/js';
2
+ import tseslint from 'typescript-eslint';
3
+ import globals from 'globals';
4
+
5
+ export default tseslint.config(
6
+ eslint.configs.recommended,
7
+ ...tseslint.configs.recommended,
8
+ {
9
+ files: ['src/**/*.ts'],
10
+ languageOptions: {
11
+ ecmaVersion: 2022,
12
+ sourceType: 'module',
13
+ globals: {
14
+ ...globals.node,
15
+ ...globals.es2022,
16
+ },
17
+ parserOptions: {
18
+ project: './tsconfig.json',
19
+ },
20
+ },
21
+ rules: {
22
+ 'no-unused-vars': 'off',
23
+ '@typescript-eslint/no-unused-vars': ['warn', {
24
+ argsIgnorePattern: '^_',
25
+ varsIgnorePattern: '^_',
26
+ caughtErrorsIgnorePattern: '^_'
27
+ }],
28
+ 'no-console': 'off',
29
+ 'no-redeclare': 'off',
30
+ '@typescript-eslint/no-redeclare': 'error',
31
+ 'no-shadow': 'off',
32
+ '@typescript-eslint/no-shadow': 'error',
33
+ 'no-undef': 'off',
34
+ 'no-empty': 'error',
35
+ '@typescript-eslint/explicit-function-return-type': 'off',
36
+ '@typescript-eslint/no-explicit-any': 'warn',
37
+ '@typescript-eslint/no-non-null-assertion': 'warn',
38
+ eqeqeq: ['error', 'always'],
39
+ curly: ['error', 'all'],
40
+ 'no-var': 'error',
41
+ 'prefer-const': 'error',
42
+ },
43
+ },
44
+ {
45
+ files: ['tests/**/*.ts'],
46
+ languageOptions: {
47
+ ecmaVersion: 2022,
48
+ sourceType: 'module',
49
+ globals: {
50
+ ...globals.node,
51
+ ...globals.es2022,
52
+ ...globals.jest,
53
+ },
54
+ },
55
+ rules: {
56
+ 'no-unused-vars': 'off',
57
+ '@typescript-eslint/no-unused-vars': ['warn', {
58
+ argsIgnorePattern: '^_',
59
+ varsIgnorePattern: '^_',
60
+ caughtErrorsIgnorePattern: '^_'
61
+ }],
62
+ '@typescript-eslint/no-explicit-any': 'off', // Allow any in tests for mocking
63
+ 'no-console': 'off',
64
+ 'no-undef': 'off',
65
+ },
66
+ },
67
+ {
68
+ ignores: ['dist/', 'node_modules/', '*.js', '*.cjs', 'coverage/'],
69
+ }
70
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paymongo-cli",
3
- "version": "1.4.4",
3
+ "version": "1.4.7",
4
4
  "description": "Developer-first CLI tool for PayMongo integration development with local webhook forwarding, payment testing, and team collaboration features. See USER_GUIDE.md for comprehensive documentation.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -72,4 +72,4 @@
72
72
  "publishConfig": {
73
73
  "access": "public"
74
74
  }
75
- }
75
+ }