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.
- package/package.json +24 -29
- package/src/cli/commands/auth.js +4 -6
- package/src/cli/commands/base-command.js +5 -7
- package/src/cli/commands/emulator.js +1 -1
- package/src/cli/commands/firebase-init.js +1 -1
- package/src/cli/commands/firestore.js +4 -6
- package/src/cli/commands/indexes.js +1 -1
- package/src/cli/commands/install.js +1 -1
- package/src/cli/commands/logs.js +1 -1
- package/src/cli/commands/mcp.js +1 -1
- package/src/cli/commands/serve.js +1 -1
- package/src/cli/commands/setup-tests/backend-manager.js +1 -1
- package/src/cli/commands/setup-tests/bem-config.js +1 -1
- package/src/cli/commands/setup-tests/firebase-admin.js +1 -1
- package/src/cli/commands/setup-tests/firebase-cli.js +1 -1
- package/src/cli/commands/setup-tests/firebase-functions.js +1 -1
- package/src/cli/commands/setup-tests/firestore-indexes-file.js +1 -1
- package/src/cli/commands/setup-tests/firestore-indexes-required.js +1 -1
- package/src/cli/commands/setup-tests/firestore-indexes-synced.js +1 -1
- package/src/cli/commands/setup-tests/firestore-rules-file.js +1 -1
- package/src/cli/commands/setup-tests/index.js +2 -2
- package/src/cli/commands/setup-tests/is-firebase-project.js +1 -1
- package/src/cli/commands/setup-tests/legacy-tests-cleanup.js +1 -1
- package/src/cli/commands/setup-tests/marketing-campaigns-seeded.js +1 -1
- package/src/cli/commands/setup-tests/node-version.js +1 -1
- package/src/cli/commands/setup-tests/nvmrc-version.js +1 -1
- package/src/cli/commands/setup-tests/{hooks-directories.js → project-directories.js} +7 -5
- package/src/cli/commands/setup-tests/project-id-consistency.js +1 -1
- package/src/cli/commands/setup-tests/realtime-rules-file.js +1 -1
- package/src/cli/commands/setup-tests/remoteconfig-template-file.js +1 -1
- package/src/cli/commands/setup-tests/service-account.js +1 -1
- package/src/cli/commands/setup-tests/storage-lifecycle-policy.js +1 -1
- package/src/cli/commands/setup-tests/storage-rules-file.js +1 -1
- package/src/cli/commands/setup.js +2 -2
- package/src/cli/commands/test.js +2 -2
- package/src/cli/commands/watch.js +1 -1
- package/src/cli/index.js +2 -2
- package/src/manager/events/cron/daily/expire-paypal-cancellations.js +1 -1
- package/src/manager/events/cron/daily/ghostii-auto-publisher.js +1 -1
- package/src/manager/events/cron/daily/marketing-newsletter-generate.js +1 -1
- package/src/manager/events/cron/daily/marketing-prune.js +2 -2
- package/src/manager/events/cron/frequent/abandoned-carts.js +2 -2
- package/src/manager/events/cron/frequent/marketing-campaigns.js +1 -1
- package/src/manager/functions/_legacy/actions/create-post-handler.js +1 -1
- package/src/manager/functions/_legacy/actions/sign-up-handler.js +1 -1
- package/src/manager/functions/_legacy/admin/create-post.js +3 -3
- package/src/manager/functions/core/actions/api/test/lab.js +0 -1
- package/src/manager/index.js +24 -13
- package/src/manager/libraries/disposable-domains.json +1 -0
- package/src/manager/libraries/payment/processors/stripe.js +1 -1
- package/src/manager/routes/payments/webhook/post.js +1 -1
- package/src/manager/server-manager.js +1 -1
- package/src/test/runner.js +1 -1
- 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.
|
|
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": "^
|
|
55
|
+
"@octokit/rest": "^22.0.1",
|
|
54
56
|
"@sendgrid/mail": "^8.1.6",
|
|
55
|
-
"@sentry/node": "^
|
|
56
|
-
"body-parser": "^
|
|
57
|
+
"@sentry/node": "^10.47.0",
|
|
58
|
+
"body-parser": "^2.2.2",
|
|
57
59
|
"busboy": "^1.6.0",
|
|
58
|
-
"chalk": "^
|
|
60
|
+
"chalk": "^5.6.2",
|
|
59
61
|
"cors": "^2.8.6",
|
|
60
|
-
"dotenv": "^
|
|
61
|
-
"express": "^
|
|
62
|
+
"dotenv": "^17.4.1",
|
|
63
|
+
"express": "^5.2.1",
|
|
62
64
|
"fs-jetpack": "^5.1.0",
|
|
63
|
-
"glob": "^
|
|
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.
|
|
70
|
-
"lowdb": "^
|
|
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": "^
|
|
74
|
-
"mocha": "^11.7.5",
|
|
74
|
+
"mime-types": "^3.0.2",
|
|
75
75
|
"moment": "^2.30.1",
|
|
76
|
-
"nanoid": "^
|
|
77
|
-
"node-
|
|
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
|
-
"
|
|
84
|
-
"stripe": "^20.3.1",
|
|
81
|
+
"stripe": "^22.0.0",
|
|
85
82
|
"uid-generator": "^2.0.0",
|
|
86
|
-
"uuid": "^
|
|
87
|
-
"wonderful-fetch": "^
|
|
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.
|
|
91
|
-
"yargs": "^
|
|
87
|
+
"yaml": "^2.8.3",
|
|
88
|
+
"yargs": "^18.0.0"
|
|
92
89
|
},
|
|
93
90
|
"devDependencies": {
|
|
94
|
-
"
|
|
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.
|
|
100
|
-
"firebase-functions": "^7.
|
|
94
|
+
"firebase-admin": "^13.7.0",
|
|
95
|
+
"firebase-functions": "^7.2.3"
|
|
101
96
|
}
|
|
102
97
|
}
|
package/src/cli/commands/auth.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const BaseCommand = require('./base-command');
|
|
2
|
-
const chalk = require('chalk');
|
|
3
|
-
const
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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.'));
|
package/src/cli/commands/logs.js
CHANGED
package/src/cli/commands/mcp.js
CHANGED
|
@@ -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,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,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
|
|
|
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
|
|
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
|
|
82
|
+
new ProjectDirectoriesTest(context),
|
|
83
83
|
new LegacyTestsCleanupTest(context),
|
|
84
84
|
new MarketingCampaignsSeededTest(context),
|
|
85
85
|
];
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
const BaseTest = require('./base-test');
|
|
2
2
|
const jetpack = require('fs-jetpack');
|
|
3
3
|
|
|
4
|
-
const
|
|
4
|
+
const DIRS = [
|
|
5
|
+
'routes',
|
|
6
|
+
'schemas',
|
|
5
7
|
'hooks/auth',
|
|
6
8
|
'hooks/cron/daily',
|
|
7
9
|
];
|
|
8
10
|
|
|
9
|
-
class
|
|
11
|
+
class ProjectDirectoriesTest extends BaseTest {
|
|
10
12
|
getName() {
|
|
11
|
-
return '
|
|
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
|
|
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 =
|
|
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 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,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
|
|
package/src/cli/commands/test.js
CHANGED
|
@@ -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
|
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('../../
|
|
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('
|
|
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('
|
|
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('
|
|
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('
|
|
3
|
-
const User = require('
|
|
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('
|
|
18
|
+
const notification = require('../../../libraries/notification.js');
|
|
19
19
|
|
|
20
20
|
module.exports = async ({ Manager, assistant, libraries }) => {
|
|
21
21
|
const { admin } = libraries;
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/src/manager/index.js
CHANGED
|
@@ -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
|
|
623
|
-
const
|
|
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
|
-
|
|
654
|
-
|
|
655
|
-
|
|
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
|
-
|
|
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}`);
|
|
@@ -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;
|
package/src/test/runner.js
CHANGED
|
@@ -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
|
+
};
|