backend-manager 5.0.192 → 5.0.193

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 (54) hide show
  1. package/package.json +24 -29
  2. package/src/cli/commands/auth.js +4 -6
  3. package/src/cli/commands/base-command.js +5 -7
  4. package/src/cli/commands/emulator.js +1 -1
  5. package/src/cli/commands/firebase-init.js +1 -1
  6. package/src/cli/commands/firestore.js +4 -6
  7. package/src/cli/commands/indexes.js +1 -1
  8. package/src/cli/commands/install.js +1 -1
  9. package/src/cli/commands/logs.js +1 -1
  10. package/src/cli/commands/mcp.js +1 -1
  11. package/src/cli/commands/serve.js +1 -1
  12. package/src/cli/commands/setup-tests/backend-manager.js +1 -1
  13. package/src/cli/commands/setup-tests/bem-config.js +1 -1
  14. package/src/cli/commands/setup-tests/firebase-admin.js +1 -1
  15. package/src/cli/commands/setup-tests/firebase-cli.js +1 -1
  16. package/src/cli/commands/setup-tests/firebase-functions.js +1 -1
  17. package/src/cli/commands/setup-tests/firestore-indexes-file.js +1 -1
  18. package/src/cli/commands/setup-tests/firestore-indexes-required.js +1 -1
  19. package/src/cli/commands/setup-tests/firestore-indexes-synced.js +1 -1
  20. package/src/cli/commands/setup-tests/firestore-rules-file.js +1 -1
  21. package/src/cli/commands/setup-tests/index.js +2 -2
  22. package/src/cli/commands/setup-tests/is-firebase-project.js +1 -1
  23. package/src/cli/commands/setup-tests/legacy-tests-cleanup.js +1 -1
  24. package/src/cli/commands/setup-tests/marketing-campaigns-seeded.js +1 -1
  25. package/src/cli/commands/setup-tests/node-version.js +1 -1
  26. package/src/cli/commands/setup-tests/nvmrc-version.js +1 -1
  27. package/src/cli/commands/setup-tests/{hooks-directories.js → project-directories.js} +7 -5
  28. package/src/cli/commands/setup-tests/project-id-consistency.js +1 -1
  29. package/src/cli/commands/setup-tests/realtime-rules-file.js +1 -1
  30. package/src/cli/commands/setup-tests/remoteconfig-template-file.js +1 -1
  31. package/src/cli/commands/setup-tests/service-account.js +1 -1
  32. package/src/cli/commands/setup-tests/storage-lifecycle-policy.js +1 -1
  33. package/src/cli/commands/setup-tests/storage-rules-file.js +1 -1
  34. package/src/cli/commands/setup.js +2 -2
  35. package/src/cli/commands/test.js +2 -2
  36. package/src/cli/commands/watch.js +1 -1
  37. package/src/cli/index.js +2 -2
  38. package/src/manager/events/cron/daily/expire-paypal-cancellations.js +1 -1
  39. package/src/manager/events/cron/daily/ghostii-auto-publisher.js +1 -1
  40. package/src/manager/events/cron/daily/marketing-newsletter-generate.js +1 -1
  41. package/src/manager/events/cron/daily/marketing-prune.js +2 -2
  42. package/src/manager/events/cron/frequent/abandoned-carts.js +2 -2
  43. package/src/manager/events/cron/frequent/marketing-campaigns.js +1 -1
  44. package/src/manager/functions/_legacy/actions/create-post-handler.js +1 -1
  45. package/src/manager/functions/_legacy/actions/sign-up-handler.js +1 -1
  46. package/src/manager/functions/_legacy/admin/create-post.js +3 -3
  47. package/src/manager/functions/core/actions/api/test/lab.js +0 -1
  48. package/src/manager/index.js +24 -13
  49. package/src/manager/libraries/disposable-domains.json +1 -0
  50. package/src/manager/libraries/payment/processors/stripe.js +1 -1
  51. package/src/manager/routes/payments/webhook/post.js +1 -1
  52. package/src/manager/server-manager.js +1 -1
  53. package/src/test/runner.js +1 -1
  54. package/test/helpers/storage.js +194 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-manager",
3
- "version": "5.0.192",
3
+ "version": "5.0.193",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -43,60 +43,55 @@
43
43
  "preparePackage": {
44
44
  "input": "./src_",
45
45
  "output": "./dist_",
46
- "replace": {}
46
+ "replace": {},
47
+ "type": "copy"
47
48
  },
48
49
  "dependencies": {
49
50
  "@firebase/rules-unit-testing": "^5.0.0",
50
51
  "@google-cloud/pubsub": "^5.3.0",
51
52
  "@google-cloud/storage": "^7.19.0",
53
+ "@inquirer/prompts": "^8.4.1",
52
54
  "@modelcontextprotocol/sdk": "^1.29.0",
53
- "@octokit/rest": "^19.0.13",
55
+ "@octokit/rest": "^22.0.1",
54
56
  "@sendgrid/mail": "^8.1.6",
55
- "@sentry/node": "^6.19.7",
56
- "body-parser": "^1.20.4",
57
+ "@sentry/node": "^10.47.0",
58
+ "body-parser": "^2.2.2",
57
59
  "busboy": "^1.6.0",
58
- "chalk": "^4.1.2",
60
+ "chalk": "^5.6.2",
59
61
  "cors": "^2.8.6",
60
- "dotenv": "^16.6.1",
61
- "express": "^4.22.1",
62
+ "dotenv": "^17.4.1",
63
+ "express": "^5.2.1",
62
64
  "fs-jetpack": "^5.1.0",
63
- "glob": "^11.1.0",
65
+ "glob": "^13.0.6",
64
66
  "hcaptcha": "^0.2.0",
65
- "inquirer": "^8.2.7",
66
67
  "itwcw-package-analytics": "^1.0.8",
67
68
  "json5": "^2.2.3",
68
69
  "jwt-decode": "^4.0.0",
69
- "lodash": "^4.17.23",
70
- "lowdb": "^1.0.0",
70
+ "lodash": "^4.18.1",
71
+ "lowdb": "^7.0.1",
71
72
  "mailchimp-api-v3": "^1.15.0",
72
73
  "markdown-it": "^14.1.1",
73
- "mime-types": "^2.1.35",
74
- "mocha": "^11.7.5",
74
+ "mime-types": "^3.0.2",
75
75
  "moment": "^2.30.1",
76
- "nanoid": "^3.3.11",
77
- "node-fetch": "^2.7.0",
78
- "node-powertools": "^2.3.2",
76
+ "nanoid": "^5.1.7",
77
+ "node-powertools": "^3.0.0",
79
78
  "npm-api": "^1.0.1",
80
- "paypal-server-api": "^2.0.14",
81
79
  "pushid": "^1.0.0",
82
80
  "sanitize-html": "^2.17.2",
83
- "shortid": "^2.2.17",
84
- "stripe": "^20.3.1",
81
+ "stripe": "^22.0.0",
85
82
  "uid-generator": "^2.0.0",
86
- "uuid": "^9.0.1",
87
- "wonderful-fetch": "^1.3.4",
83
+ "uuid": "^13.0.0",
84
+ "wonderful-fetch": "^2.0.5",
88
85
  "wonderful-log": "^1.0.7",
89
86
  "wonderful-version": "^1.3.2",
90
- "yaml": "^2.8.2",
91
- "yargs": "^17.7.2"
87
+ "yaml": "^2.8.3",
88
+ "yargs": "^18.0.0"
92
89
  },
93
90
  "devDependencies": {
94
- "chai": "^5.3.3",
95
- "prepare-package": "^1.2.6",
96
- "sinon": "^21.0.1"
91
+ "prepare-package": "^2.0.8"
97
92
  },
98
93
  "peerDependencies": {
99
- "firebase-admin": "^13.6.1",
100
- "firebase-functions": "^7.0.5"
94
+ "firebase-admin": "^13.7.0",
95
+ "firebase-functions": "^7.2.3"
101
96
  }
102
97
  }
@@ -1,6 +1,6 @@
1
1
  const BaseCommand = require('./base-command');
2
- const chalk = require('chalk');
3
- const inquirer = require('inquirer');
2
+ const chalk = require('chalk').default;
3
+ const { confirm } = require('@inquirer/prompts');
4
4
  const { initFirebase } = require('./firebase-init');
5
5
 
6
6
  class AuthCommand extends BaseCommand {
@@ -148,12 +148,10 @@ class AuthCommand extends BaseCommand {
148
148
 
149
149
  // Require confirmation for production (skip for emulator or --force)
150
150
  if (!isEmulator && !argv.force) {
151
- const { confirmed } = await inquirer.prompt([{
152
- type: 'confirm',
153
- name: 'confirmed',
151
+ const confirmed = await confirm({
154
152
  message: `Delete user "${user.uid}" (${user.email || 'no email'}) from PRODUCTION?`,
155
153
  default: false,
156
- }]);
154
+ });
157
155
 
158
156
  if (!confirmed) {
159
157
  this.log(chalk.gray(' Aborted.'));
@@ -1,5 +1,5 @@
1
- const chalk = require('chalk');
2
- const inquirer = require('inquirer');
1
+ const chalk = require('chalk').default;
2
+ const { confirm } = require('@inquirer/prompts');
3
3
  const { execSync, spawn } = require('child_process');
4
4
  const path = require('path');
5
5
  const jetpack = require('fs-jetpack');
@@ -63,12 +63,10 @@ class BaseCommand {
63
63
  }
64
64
  }
65
65
 
66
- const { shouldKill } = await inquirer.prompt([{
67
- type: 'confirm',
68
- name: 'shouldKill',
66
+ const shouldKill = await confirm({
69
67
  message: 'Kill these processes to free the ports?',
70
68
  default: true,
71
- }]);
69
+ });
72
70
 
73
71
  if (!shouldKill) {
74
72
  this.log(chalk.gray('\n Aborting. Free the ports and try again.\n'));
@@ -174,7 +172,7 @@ class BaseCommand {
174
172
  // Load .env so STRIPE_SECRET_KEY and BACKEND_MANAGER_KEY are available
175
173
  const envPath = path.join(functionsDir, '.env');
176
174
  if (jetpack.exists(envPath)) {
177
- require('dotenv').config({ path: envPath });
175
+ require('dotenv').config({ path: envPath, quiet: true });
178
176
  }
179
177
 
180
178
  // Check for Stripe secret key
@@ -1,7 +1,7 @@
1
1
  const BaseCommand = require('./base-command');
2
2
  const path = require('path');
3
3
  const fs = require('fs');
4
- const chalk = require('chalk');
4
+ const chalk = require('chalk').default;
5
5
  const jetpack = require('fs-jetpack');
6
6
  const JSON5 = require('json5');
7
7
  const powertools = require('node-powertools');
@@ -17,7 +17,7 @@ function initFirebase({ firebaseProjectPath, emulator }) {
17
17
  // Load .env so env vars like GCLOUD_PROJECT are available
18
18
  const envPath = path.join(functionsDir, '.env');
19
19
  if (jetpack.exists(envPath)) {
20
- require('dotenv').config({ path: envPath });
20
+ require('dotenv').config({ path: envPath, quiet: true });
21
21
  }
22
22
 
23
23
  // Resolve firebase-admin from the consumer project's node_modules (peer dep)
@@ -1,6 +1,6 @@
1
1
  const BaseCommand = require('./base-command');
2
- const chalk = require('chalk');
3
- const inquirer = require('inquirer');
2
+ const chalk = require('chalk').default;
3
+ const { confirm } = require('@inquirer/prompts');
4
4
  const { initFirebase } = require('./firebase-init');
5
5
 
6
6
  class FirestoreCommand extends BaseCommand {
@@ -175,12 +175,10 @@ class FirestoreCommand extends BaseCommand {
175
175
 
176
176
  // Require confirmation for production (skip for emulator or --force)
177
177
  if (!isEmulator && !argv.force) {
178
- const { confirmed } = await inquirer.prompt([{
179
- type: 'confirm',
180
- name: 'confirmed',
178
+ const confirmed = await confirm({
181
179
  message: `Delete document "${docPath}" from PRODUCTION?`,
182
180
  default: false,
183
- }]);
181
+ });
184
182
 
185
183
  if (!confirmed) {
186
184
  this.log(chalk.gray(' Aborted.'));
@@ -1,5 +1,5 @@
1
1
  const BaseCommand = require('./base-command');
2
- const chalk = require('chalk');
2
+ const chalk = require('chalk').default;
3
3
  const jetpack = require('fs-jetpack');
4
4
  const powertools = require('node-powertools');
5
5
  const _ = require('lodash');
@@ -1,5 +1,5 @@
1
1
  const BaseCommand = require('./base-command');
2
- const chalk = require('chalk');
2
+ const chalk = require('chalk').default;
3
3
  const os = require('os');
4
4
  const powertools = require('node-powertools');
5
5
  const Npm = require('npm-api');
@@ -1,5 +1,5 @@
1
1
  const BaseCommand = require('./base-command');
2
- const chalk = require('chalk');
2
+ const chalk = require('chalk').default;
3
3
  const fs = require('fs');
4
4
  const { execSync, spawn } = require('child_process');
5
5
  const path = require('path');
@@ -10,7 +10,7 @@ class McpCommand extends BaseCommand {
10
10
  const jetpack = require('fs-jetpack');
11
11
  const envPath = path.join(functionsDir, '.env');
12
12
  if (jetpack.exists(envPath)) {
13
- require('dotenv').config({ path: envPath });
13
+ require('dotenv').config({ path: envPath, quiet: true });
14
14
  }
15
15
 
16
16
  // Resolve the BEM server URL
@@ -1,7 +1,7 @@
1
1
  const BaseCommand = require('./base-command');
2
2
  const path = require('path');
3
3
  const fs = require('fs');
4
- const chalk = require('chalk');
4
+ const chalk = require('chalk').default;
5
5
  const powertools = require('node-powertools');
6
6
  const WatchCommand = require('./watch');
7
7
 
@@ -1,5 +1,5 @@
1
1
  const BaseTest = require('./base-test');
2
- const chalk = require('chalk');
2
+ const chalk = require('chalk').default;
3
3
  const wonderfulVersion = require('wonderful-version');
4
4
  const powertools = require('node-powertools');
5
5
  const Npm = require('npm-api');
@@ -1,6 +1,6 @@
1
1
  const BaseTest = require('./base-test');
2
2
  const jetpack = require('fs-jetpack');
3
- const chalk = require('chalk');
3
+ const chalk = require('chalk').default;
4
4
  const powertools = require('node-powertools');
5
5
  const _ = require('lodash');
6
6
  const helpers = require('./helpers');
@@ -1,5 +1,5 @@
1
1
  const BaseTest = require('./base-test');
2
- const chalk = require('chalk');
2
+ const chalk = require('chalk').default;
3
3
  const wonderfulVersion = require('wonderful-version');
4
4
  const powertools = require('node-powertools');
5
5
  const helpers = require('./helpers');
@@ -1,5 +1,5 @@
1
1
  const BaseTest = require('./base-test');
2
- const chalk = require('chalk');
2
+ const chalk = require('chalk').default;
3
3
  const powertools = require('node-powertools');
4
4
 
5
5
  class FirebaseCliTest extends BaseTest {
@@ -1,5 +1,5 @@
1
1
  const BaseTest = require('./base-test');
2
- const chalk = require('chalk');
2
+ const chalk = require('chalk').default;
3
3
  const wonderfulVersion = require('wonderful-version');
4
4
  const powertools = require('node-powertools');
5
5
  const helpers = require('./helpers');
@@ -1,6 +1,6 @@
1
1
  const BaseTest = require('./base-test');
2
2
  const jetpack = require('fs-jetpack');
3
- const chalk = require('chalk');
3
+ const chalk = require('chalk').default;
4
4
 
5
5
  class FirestoreIndexesFileTest extends BaseTest {
6
6
  getName() {
@@ -1,7 +1,7 @@
1
1
  const BaseTest = require('./base-test');
2
2
  const jetpack = require('fs-jetpack');
3
3
  const _ = require('lodash');
4
- const chalk = require('chalk');
4
+ const chalk = require('chalk').default;
5
5
  const requiredIndexes = require('./helpers/required-indexes');
6
6
 
7
7
  class FirestoreIndexesRequiredTest extends BaseTest {
@@ -1,6 +1,6 @@
1
1
  const BaseTest = require('./base-test');
2
2
  const jetpack = require('fs-jetpack');
3
- const chalk = require('chalk');
3
+ const chalk = require('chalk').default;
4
4
  const _ = require('lodash');
5
5
  const powertools = require('node-powertools');
6
6
 
@@ -1,6 +1,6 @@
1
1
  const BaseTest = require('./base-test');
2
2
  const jetpack = require('fs-jetpack');
3
- const chalk = require('chalk');
3
+ const chalk = require('chalk').default;
4
4
 
5
5
  const bem_allRulesRegex = /(\/\/\/---backend-manager---\/\/\/)(.*?)(\/\/\/---------end---------\/\/\/)/sgm;
6
6
  const bem_allRulesBackupRegex = /({{\s*?backend-manager\s*?}})/sgm;
@@ -36,7 +36,7 @@ const RemoteconfigTemplateFileTest = require('./remoteconfig-template-file');
36
36
  const HostingFolderTest = require('./hosting-folder');
37
37
  const PublicHtmlFilesTest = require('./public-html-files');
38
38
  const FirestoreIndexesRequiredTest = require('./firestore-indexes-required');
39
- const HooksDirectoriesTest = require('./hooks-directories');
39
+ const ProjectDirectoriesTest = require('./project-directories');
40
40
  const LegacyTestsCleanupTest = require('./legacy-tests-cleanup');
41
41
  const MarketingCampaignsSeededTest = require('./marketing-campaigns-seeded');
42
42
 
@@ -79,7 +79,7 @@ function getTests(context) {
79
79
  new RemoteconfigTemplateFileTest(context),
80
80
  new HostingFolderTest(context),
81
81
  new PublicHtmlFilesTest(context),
82
- new HooksDirectoriesTest(context),
82
+ new ProjectDirectoriesTest(context),
83
83
  new LegacyTestsCleanupTest(context),
84
84
  new MarketingCampaignsSeededTest(context),
85
85
  ];
@@ -1,6 +1,6 @@
1
1
  const BaseTest = require('./base-test');
2
2
  const jetpack = require('fs-jetpack');
3
- const chalk = require('chalk');
3
+ const chalk = require('chalk').default;
4
4
 
5
5
  class IsFirebaseProjectTest extends BaseTest {
6
6
  getName() {
@@ -1,6 +1,6 @@
1
1
  const BaseTest = require('./base-test');
2
2
  const jetpack = require('fs-jetpack');
3
- const chalk = require('chalk');
3
+ const chalk = require('chalk').default;
4
4
 
5
5
  // Legacy test files that should be removed from consuming projects
6
6
  const LEGACY_FILES = [
@@ -1,5 +1,5 @@
1
1
  const BaseTest = require('./base-test');
2
- const chalk = require('chalk');
2
+ const chalk = require('chalk').default;
3
3
  const _ = require('lodash');
4
4
  const { buildSeedCampaigns } = require('./helpers/seed-campaigns');
5
5
 
@@ -1,5 +1,5 @@
1
1
  const BaseTest = require('./base-test');
2
- const chalk = require('chalk');
2
+ const chalk = require('chalk').default;
3
3
  const wonderfulVersion = require('wonderful-version');
4
4
 
5
5
  class NodeVersionTest extends BaseTest {
@@ -1,6 +1,6 @@
1
1
  const BaseTest = require('./base-test');
2
2
  const jetpack = require('fs-jetpack');
3
- const chalk = require('chalk');
3
+ const chalk = require('chalk').default;
4
4
  const wonderfulVersion = require('wonderful-version');
5
5
 
6
6
  class NvmrcVersionTest extends BaseTest {
@@ -1,21 +1,23 @@
1
1
  const BaseTest = require('./base-test');
2
2
  const jetpack = require('fs-jetpack');
3
3
 
4
- const HOOKS_DIRS = [
4
+ const DIRS = [
5
+ 'routes',
6
+ 'schemas',
5
7
  'hooks/auth',
6
8
  'hooks/cron/daily',
7
9
  ];
8
10
 
9
- class HooksDirectoriesTest extends BaseTest {
11
+ class ProjectDirectoriesTest extends BaseTest {
10
12
  getName() {
11
- return 'hooks directories exist';
13
+ return 'project directories exist';
12
14
  }
13
15
 
14
16
  async run() {
15
17
  const self = this.self;
16
18
  const functionsDir = `${self.firebaseProjectPath}/functions`;
17
19
 
18
- for (const dir of HOOKS_DIRS) {
20
+ for (const dir of DIRS) {
19
21
  jetpack.dir(`${functionsDir}/${dir}`);
20
22
  }
21
23
 
@@ -27,4 +29,4 @@ class HooksDirectoriesTest extends BaseTest {
27
29
  }
28
30
  }
29
31
 
30
- module.exports = HooksDirectoriesTest;
32
+ module.exports = ProjectDirectoriesTest;
@@ -1,7 +1,7 @@
1
1
  const BaseTest = require('./base-test');
2
2
  const jetpack = require('fs-jetpack');
3
3
  const JSON5 = require('json5');
4
- const chalk = require('chalk');
4
+ const chalk = require('chalk').default;
5
5
 
6
6
  /**
7
7
  * Ensures projectId is consistent across all configuration files:
@@ -1,6 +1,6 @@
1
1
  const BaseTest = require('./base-test');
2
2
  const jetpack = require('fs-jetpack');
3
- const chalk = require('chalk');
3
+ const chalk = require('chalk').default;
4
4
 
5
5
  const bem_allRulesRegex = /(\/\/\/---backend-manager---\/\/\/)(.*?)(\/\/\/---------end---------\/\/\/)/sgm;
6
6
  const bem_allRulesBackupRegex = /({{\s*?backend-manager\s*?}})/sgm;
@@ -1,7 +1,7 @@
1
1
  const BaseTest = require('./base-test');
2
2
  const jetpack = require('fs-jetpack');
3
3
  const path = require('path');
4
- const chalk = require('chalk');
4
+ const chalk = require('chalk').default;
5
5
 
6
6
  class RemoteconfigTemplateFileTest extends BaseTest {
7
7
  getName() {
@@ -1,6 +1,6 @@
1
1
  const BaseTest = require('./base-test');
2
2
  const jetpack = require('fs-jetpack');
3
- const chalk = require('chalk');
3
+ const chalk = require('chalk').default;
4
4
 
5
5
  class ServiceAccountTest extends BaseTest {
6
6
  getName() {
@@ -1,7 +1,7 @@
1
1
  const BaseTest = require('./base-test');
2
2
  const powertools = require('node-powertools');
3
3
  const path = require('path');
4
- const chalk = require('chalk');
4
+ const chalk = require('chalk').default;
5
5
 
6
6
  class StorageLifecyclePolicyTest extends BaseTest {
7
7
  getName() {
@@ -1,7 +1,7 @@
1
1
  const BaseTest = require('./base-test');
2
2
  const jetpack = require('fs-jetpack');
3
3
  const path = require('path');
4
- const chalk = require('chalk');
4
+ const chalk = require('chalk').default;
5
5
 
6
6
  class StorageRulesFileTest extends BaseTest {
7
7
  getName() {
@@ -1,5 +1,5 @@
1
1
  const BaseCommand = require('./base-command');
2
- const chalk = require('chalk');
2
+ const chalk = require('chalk').default;
3
3
  const jetpack = require('fs-jetpack');
4
4
  const path = require('path');
5
5
  const JSON5 = require('json5');
@@ -23,7 +23,7 @@ class SetupCommand extends BaseCommand {
23
23
  // Load environment variables from .env file
24
24
  const envPath = `${self.firebaseProjectPath}/functions/.env`;
25
25
  if (jetpack.exists(envPath)) {
26
- require('dotenv').config({ path: envPath });
26
+ require('dotenv').config({ path: envPath, quiet: true });
27
27
  }
28
28
  }
29
29
 
@@ -1,7 +1,7 @@
1
1
  const BaseCommand = require('./base-command');
2
2
  const path = require('path');
3
3
  const fs = require('fs');
4
- const chalk = require('chalk');
4
+ const chalk = require('chalk').default;
5
5
  const jetpack = require('fs-jetpack');
6
6
  const JSON5 = require('json5');
7
7
  const powertools = require('node-powertools');
@@ -85,7 +85,7 @@ class TestCommand extends BaseCommand {
85
85
  // Load .env first so env vars are available
86
86
  const envPath = path.join(functionsDir, '.env');
87
87
  if (jetpack.exists(envPath)) {
88
- require('dotenv').config({ path: envPath });
88
+ require('dotenv').config({ path: envPath, quiet: true });
89
89
  }
90
90
 
91
91
  // Load backend-manager-config.json
@@ -1,6 +1,6 @@
1
1
  const BaseCommand = require('./base-command');
2
2
  const path = require('path');
3
- const chalk = require('chalk');
3
+ const chalk = require('chalk').default;
4
4
  const jetpack = require('fs-jetpack');
5
5
  const { execSync, spawn } = require('child_process');
6
6
 
package/src/cli/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const os = require('os');
2
2
  const path = require('path');
3
- const argv = require('yargs').argv;
3
+ const argv = require('yargs')(process.argv.slice(2)).argv;
4
4
  const _ = require('lodash');
5
5
 
6
6
  // Abort if running from ~/node_modules (accidental home directory install)
@@ -165,7 +165,7 @@ Main.prototype.process = async function (args) {
165
165
  Main.prototype.test = async function(name, fn, fix, args) {
166
166
  const self = this;
167
167
  let status;
168
- const chalk = require('chalk');
168
+ const chalk = require('chalk').default;
169
169
 
170
170
  return new Promise(async function(resolve, reject) {
171
171
  let passed = await fn();
@@ -16,7 +16,7 @@ const powertools = require('node-powertools');
16
16
  */
17
17
  module.exports = async ({ Manager, assistant, context, libraries }) => {
18
18
  const { admin } = libraries;
19
- const transitions = require('../../events/firestore/payments-webhooks/transitions/index.js');
19
+ const transitions = require('../../firestore/payments-webhooks/transitions/index.js');
20
20
 
21
21
  const now = new Date();
22
22
  const nowStr = powertools.timestamp(now, { output: 'string' });
@@ -93,7 +93,7 @@ module.exports = async ({ Manager, assistant, context, libraries }) => {
93
93
  * Build brand config from Manager.config (same shape as /brand endpoint response)
94
94
  */
95
95
  function buildBrandConfig(config) {
96
- const { buildPublicConfig } = require(require('path').join(__dirname, '..', '..', 'routes', 'brand', 'get.js'));
96
+ const { buildPublicConfig } = require(require('path').join(__dirname, '..', '..', '..', 'routes', 'brand', 'get.js'));
97
97
 
98
98
  return buildPublicConfig(config);
99
99
  }
@@ -17,7 +17,7 @@ const pushid = require('pushid');
17
17
 
18
18
  // Generator modules — keyed by generator field value
19
19
  const generators = {
20
- newsletter: require('../../libraries/email/generators/newsletter.js'),
20
+ newsletter: require('../../../libraries/email/generators/newsletter.js'),
21
21
  };
22
22
 
23
23
  module.exports = async ({ Manager, assistant, libraries }) => {
@@ -17,7 +17,7 @@
17
17
  *
18
18
  * Runs on bm_cronDaily.
19
19
  */
20
- const sendgridProvider = require('../../libraries/email/providers/sendgrid.js');
20
+ const sendgridProvider = require('../../../libraries/email/providers/sendgrid.js');
21
21
 
22
22
  module.exports = async ({ Manager, assistant }) => {
23
23
  // Only run on the 1st of the month
@@ -127,7 +127,7 @@ async function stagePrune(Manager, assistant) {
127
127
 
128
128
  // Also remove from Beehiiv (same emails)
129
129
  if (marketing.beehiiv?.enabled !== false && process.env.BEEHIIV_API_KEY) {
130
- const beehiivProvider = require('../../libraries/email/providers/beehiiv.js');
130
+ const beehiivProvider = require('../../../libraries/email/providers/beehiiv.js');
131
131
  const emails = exportResult.contacts.map(c => c.email).filter(Boolean);
132
132
 
133
133
  assistant.log(`Marketing prune: Removing ${emails.length} contacts from Beehiiv`);
@@ -1,6 +1,6 @@
1
1
  const powertools = require('node-powertools');
2
- const { REMINDER_DELAYS, COLLECTION } = require('../../libraries/abandoned-cart-config.js');
3
- const User = require('../../helpers/user.js');
2
+ const { REMINDER_DELAYS, COLLECTION } = require('../../../libraries/abandoned-cart-config.js');
3
+ const User = require('../../../helpers/user.js');
4
4
 
5
5
  /**
6
6
  * Abandoned cart reminder cron job
@@ -15,7 +15,7 @@
15
15
  */
16
16
  const moment = require('moment');
17
17
  const pushid = require('pushid');
18
- const notification = require('../../libraries/notification.js');
18
+ const notification = require('../../../libraries/notification.js');
19
19
 
20
20
  module.exports = async ({ Manager, assistant, libraries }) => {
21
21
  const { admin } = libraries;
@@ -2,7 +2,7 @@ let Poster;
2
2
  let pathApi;
3
3
  let os;
4
4
  // let JSON5;
5
- const fetch = require('node-fetch');
5
+ // Native fetch (Node 22+)
6
6
  const Mailchimp = require('mailchimp-api-v3');
7
7
 
8
8
  let Module = {
@@ -177,7 +177,7 @@ module.exports = Module;
177
177
  function addToMCList(key, listId, email) {
178
178
  return new Promise((resolve, reject) => {
179
179
  let datacenter = key.split('-')[1];
180
- fetch = fetch || require('node-fetch');
180
+ // Native fetch (Node 22+)
181
181
  fetch(`https://${datacenter}.api.mailchimp.com/3.0/lists/${listId}/members`, {
182
182
  method: 'post',
183
183
  body: JSON.stringify({
@@ -107,7 +107,7 @@ async function createFile(user, repoUser, repoName, key, path, contents) {
107
107
  body: {
108
108
  },
109
109
  timeout: 30000,
110
- json: true,
110
+ response: 'json',
111
111
  headers: {
112
112
  'User-Agent': user,
113
113
  // 'Authorization': `Basic ${user}:${key}`,
@@ -137,7 +137,7 @@ async function createFile(user, repoUser, repoName, key, path, contents) {
137
137
  content: base64Data,
138
138
  },
139
139
  timeout: 30000,
140
- json: true,
140
+ response: 'json',
141
141
  headers: {
142
142
  'User-Agent': user,
143
143
  // 'Authorization': `Basic ${user}:${key}`,
@@ -163,7 +163,7 @@ async function createFile(user, repoUser, repoName, key, path, contents) {
163
163
 
164
164
  function makeRequest(options) {
165
165
  return new Promise(function(resolve, reject) {
166
- fetch = fetch || require('node-fetch');
166
+ // Native fetch (Node 22+)
167
167
  options.headers = options.headers || {};
168
168
  options.headers['Content-Type'] = 'application/json';
169
169
  let hasBody = Object.keys(options.body || {}).length > 0
@@ -1,4 +1,3 @@
1
- const fetch = require('node-fetch');
2
1
  const _ = require('lodash')
3
2
 
4
3
  function Module() {
@@ -123,7 +123,7 @@ Manager.prototype.init = function (exporter, options) {
123
123
 
124
124
  // Set dotenv
125
125
  try {
126
- const env = require('dotenv').config();
126
+ const env = require('dotenv').config({ quiet: true });
127
127
  } catch (e) {
128
128
  self.assistant.error(new Error(`Failed to set up environment variables from .env file: ${e.message}`));
129
129
  }
@@ -619,12 +619,11 @@ Manager.prototype.storage = function (options) {
619
619
  const subfolder = `storage/${self.options.uniqueAppName || 'primary'}/${options.name}`;
620
620
 
621
621
  // Setup lowdb
622
- const low = require('lowdb');
623
- const FileSync = require('lowdb/adapters/FileSync');
622
+ const { LowSync } = require('lowdb');
623
+ const { JSONFileSync } = require('lowdb/node');
624
624
  const location = options.temporary
625
625
  ? `${require('os').tmpdir()}/${subfolder}.json`
626
626
  : `./.data/${subfolder}.json`;
627
- const adapter = new FileSync(location);
628
627
 
629
628
  // Log
630
629
  if (options.log) {
@@ -650,9 +649,22 @@ Manager.prototype.storage = function (options) {
650
649
  if (!jetpack.exists(location)) {
651
650
  jetpack.write(location, {});
652
651
  }
653
- self._internal.storage[options.name] = low(adapter);
654
-
655
- self._internal.storage[options.name].set('_location', location)
652
+ const db = new LowSync(new JSONFileSync(location), {});
653
+ db.read();
654
+
655
+ // Wrap lowdb in a v1-compatible API so consumers don't need lodash
656
+ self._internal.storage[options.name] = {
657
+ _db: db,
658
+ _location: location,
659
+ get(path, defaultValue) {
660
+ const result = _.get(db.data, path, defaultValue);
661
+ return { value() { return result; } };
662
+ },
663
+ set(path, value) { _.set(db.data, path, value); return this; },
664
+ write() { db.write(); return this; },
665
+ getState() { return db.data; },
666
+ setState(data) { db.data = data; return this; },
667
+ };
656
668
  }
657
669
 
658
670
  try {
@@ -1147,15 +1159,14 @@ Manager.prototype.setupCustomServer = function (_library, options) {
1147
1159
  });
1148
1160
 
1149
1161
  // Run the server!
1150
- const server = app.listen({ port: process.env.PORT || 3000, host: '0.0.0.0' }, () => {
1151
- const address = server.address();
1152
-
1153
- // Check if there's an error
1154
- if (server.address() === null) {
1155
- self.assistant.error(e);
1162
+ const server = app.listen({ port: process.env.PORT || 3000, host: '0.0.0.0' }, (error) => {
1163
+ if (error) {
1164
+ self.assistant.error(error);
1156
1165
  process.exit(1);
1157
1166
  }
1158
1167
 
1168
+ const address = server.address();
1169
+
1159
1170
  // Log
1160
1171
  if (options.log) {
1161
1172
  self.assistant.log(`Server listening on ${address.address}:${address.port}`);
@@ -4701,6 +4701,7 @@
4701
4701
  "trangiabao.click",
4702
4702
  "trangzim.uk",
4703
4703
  "translateid.com",
4704
+ "traodoinick.com",
4704
4705
  "trap-mail.de",
4705
4706
  "trash-amil.com",
4706
4707
  "trash-mail.at",
@@ -29,7 +29,7 @@ const Stripe = {
29
29
  throw new Error('STRIPE_SECRET_KEY environment variable is required');
30
30
  }
31
31
 
32
- stripeInstance = require('stripe')(secretKey);
32
+ stripeInstance = new (require('stripe'))(secretKey);
33
33
  }
34
34
 
35
35
  return stripeInstance;
@@ -55,7 +55,7 @@ module.exports = async ({ assistant, Manager, libraries }) => {
55
55
 
56
56
  const { eventId, eventType, category, resourceType, resourceId, raw, uid } = parsed;
57
57
 
58
- assistant.log(`Parsed webhook: eventId=${eventId}, eventType=${eventType}, category=${category || 'null'}, resourceType=${resourceType || 'null'}, uid=${uid || 'null'}`);
58
+ assistant.log(`Parsed webhook: eventId=${eventId}, eventType=${eventType}, category=${category || 'null'}, resourceType=${resourceType || 'null'}, uid=${uid || 'null'}, api_version=${raw?.api_version || 'unknown'}`);
59
59
 
60
60
  // Let the processor decide if this event type is relevant
61
61
  if (processorModule.isSupported && !processorModule.isSupported(eventType)) {
@@ -1,7 +1,7 @@
1
1
  const powertools = require('node-powertools');
2
2
  const yargs = require('yargs/yargs');
3
3
  const { hideBin } = require('yargs/helpers');
4
- const chalk = require('chalk');
4
+ const chalk = require('chalk').default;
5
5
 
6
6
  function ServerManager(command, options) {
7
7
  const self = this;
@@ -2,7 +2,7 @@ const os = require('os');
2
2
  const path = require('path');
3
3
  const Module = require('module');
4
4
  const jetpack = require('fs-jetpack');
5
- const chalk = require('chalk');
5
+ const chalk = require('chalk').default;
6
6
 
7
7
  const HttpClient = require('./utils/http-client.js');
8
8
  const assertions = require('./utils/assertions.js');
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Test: Manager.storage()
3
+ * Unit tests for the lowdb-backed local JSON storage wrapper
4
+ *
5
+ * Run: npx mgr test helpers/storage
6
+ *
7
+ * Covers:
8
+ * - get/set/write operations
9
+ * - Nested path access
10
+ * - Default values
11
+ * - setState/getState
12
+ * - Persistence across reads
13
+ * - Chaining API (.set().write(), .get().value())
14
+ */
15
+ const path = require('path');
16
+ const jetpack = require('fs-jetpack');
17
+ const os = require('os');
18
+
19
+ // Create a minimal Manager mock with storage()
20
+ const STORAGE_DIR = path.join(os.tmpdir(), `bem-storage-test-${Date.now()}`);
21
+
22
+ function createStorage(name) {
23
+ const _ = require('lodash');
24
+ const { LowSync } = require('lowdb');
25
+ const { JSONFileSync } = require('lowdb/node');
26
+ const location = path.join(STORAGE_DIR, `${name}.json`);
27
+
28
+ jetpack.write(location, {});
29
+
30
+ const db = new LowSync(new JSONFileSync(location), {});
31
+ db.read();
32
+
33
+ return {
34
+ _db: db,
35
+ _location: location,
36
+ get(p, defaultValue) {
37
+ const result = _.get(db.data, p, defaultValue);
38
+ return { value() { return result; } };
39
+ },
40
+ set(p, value) { _.set(db.data, p, value); return this; },
41
+ write() { db.write(); return this; },
42
+ getState() { return db.data; },
43
+ setState(data) { db.data = data; return this; },
44
+ };
45
+ }
46
+
47
+ module.exports = {
48
+ description: 'Manager.storage()',
49
+ type: 'group',
50
+
51
+ tests: [
52
+ {
53
+ name: 'set-and-get-simple',
54
+ async run({ assert }) {
55
+ const storage = createStorage('simple');
56
+ storage.set('key', 'value').write();
57
+ const result = storage.get('key').value();
58
+ assert.equal(result, 'value', 'Should get what was set');
59
+ },
60
+ },
61
+
62
+ {
63
+ name: 'get-returns-default-for-missing',
64
+ async run({ assert }) {
65
+ const storage = createStorage('defaults');
66
+ const result = storage.get('nonexistent', 'fallback').value();
67
+ assert.equal(result, 'fallback', 'Should return default for missing key');
68
+ },
69
+ },
70
+
71
+ {
72
+ name: 'get-returns-undefined-without-default',
73
+ async run({ assert }) {
74
+ const storage = createStorage('nodefault');
75
+ const result = storage.get('nonexistent').value();
76
+ assert.equal(result, undefined, 'Should return undefined without default');
77
+ },
78
+ },
79
+
80
+ {
81
+ name: 'set-nested-path',
82
+ async run({ assert }) {
83
+ const storage = createStorage('nested');
84
+ storage.set('a.b.c', 42).write();
85
+ const result = storage.get('a.b.c').value();
86
+ assert.equal(result, 42, 'Should handle nested paths');
87
+ },
88
+ },
89
+
90
+ {
91
+ name: 'set-preserves-existing-data',
92
+ async run({ assert }) {
93
+ const storage = createStorage('preserve');
94
+ storage.set('first', 1).write();
95
+ storage.set('second', 2).write();
96
+ assert.equal(storage.get('first').value(), 1, 'First key should still exist');
97
+ assert.equal(storage.get('second').value(), 2, 'Second key should exist');
98
+ },
99
+ },
100
+
101
+ {
102
+ name: 'set-chaining',
103
+ async run({ assert }) {
104
+ const storage = createStorage('chain');
105
+ storage.set('a', 1).set('b', 2).write();
106
+ assert.equal(storage.get('a').value(), 1, 'First chained set');
107
+ assert.equal(storage.get('b').value(), 2, 'Second chained set');
108
+ },
109
+ },
110
+
111
+ {
112
+ name: 'set-object-value',
113
+ async run({ assert }) {
114
+ const storage = createStorage('object');
115
+ storage.set('user.usage', { daily: 5, monthly: 10 }).write();
116
+ const usage = storage.get('user.usage', {}).value();
117
+ assert.equal(usage.daily, 5, 'Should store object values');
118
+ assert.equal(usage.monthly, 10, 'Should store nested object values');
119
+ },
120
+ },
121
+
122
+ {
123
+ name: 'getState-returns-all-data',
124
+ async run({ assert }) {
125
+ const storage = createStorage('getstate');
126
+ storage.set('x', 1).set('y', 2).write();
127
+ const state = storage.getState();
128
+ assert.equal(state.x, 1, 'getState should include x');
129
+ assert.equal(state.y, 2, 'getState should include y');
130
+ },
131
+ },
132
+
133
+ {
134
+ name: 'setState-replaces-all-data',
135
+ async run({ assert }) {
136
+ const storage = createStorage('setstate');
137
+ storage.set('old', 'data').write();
138
+ storage.setState({}).write();
139
+ const result = storage.get('old').value();
140
+ assert.equal(result, undefined, 'setState({}) should clear all data');
141
+ },
142
+ },
143
+
144
+ {
145
+ name: 'write-persists-to-disk',
146
+ async run({ assert }) {
147
+ const storage = createStorage('persist');
148
+ storage.set('persisted', true).write();
149
+
150
+ // Read the file directly to verify
151
+ const raw = JSON.parse(jetpack.read(storage._location));
152
+ assert.equal(raw.persisted, true, 'Data should be written to disk');
153
+ },
154
+ },
155
+
156
+ {
157
+ name: 'data-survives-re-read',
158
+ async run({ assert }) {
159
+ const _ = require('lodash');
160
+ const { LowSync } = require('lowdb');
161
+ const { JSONFileSync } = require('lowdb/node');
162
+
163
+ // Write with one instance
164
+ const storage1 = createStorage('reread');
165
+ storage1.set('survivor', 'yes').write();
166
+
167
+ // Read with a fresh instance from the same file
168
+ const db2 = new LowSync(new JSONFileSync(storage1._location), {});
169
+ db2.read();
170
+ const result = _.get(db2.data, 'survivor');
171
+ assert.equal(result, 'yes', 'Data should survive a fresh read');
172
+ },
173
+ },
174
+
175
+ {
176
+ name: 'get-default-object-for-usage-pattern',
177
+ async run({ assert }) {
178
+ const storage = createStorage('usage-pattern');
179
+ // Mimics usage.js: storage.get(`${path}.usage`, {}).value()
180
+ const usage = storage.get('nonexistent-user.usage', {}).value();
181
+ assert.ok(typeof usage === 'object', 'Should return empty object default');
182
+ assert.equal(Object.keys(usage).length, 0, 'Default object should be empty');
183
+ },
184
+ },
185
+
186
+ {
187
+ name: 'cleanup',
188
+ async run() {
189
+ // Clean up test files
190
+ jetpack.remove(STORAGE_DIR);
191
+ },
192
+ },
193
+ ],
194
+ };