backend-manager 5.0.190 → 5.0.192

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/CHANGELOG.md CHANGED
@@ -14,6 +14,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
14
14
  - `Fixed` for any bug fixes.
15
15
  - `Security` in case of vulnerabilities.
16
16
 
17
+ # [5.0.192] - 2026-04-02
18
+ ### Added
19
+ - Setup test to create `hooks/auth/` and `hooks/cron/daily/` directories in consumer projects during `npx bm setup`
20
+
17
21
  # [5.0.186] - 2026-04-01
18
22
  ### Fixed
19
23
  - Move markdown rendering and UTM link tagging to run after `_.merge()` so caller overrides to `body.message` and `email.body` are properly processed
package/CLAUDE.md CHANGED
@@ -65,24 +65,32 @@ src/
65
65
  stripe.js # Stripe SDK init, fetchResource, toUnified*, resolvePriceId
66
66
  paypal.js # PayPal fetchResource, toUnified* (custom_id parsing)
67
67
  test.js # Test processor (delegates to Stripe shapes)
68
- functions/core/ # Built-in functions
68
+ events/ # All event-driven code
69
+ auth/ # Auth event handlers (hookable)
70
+ before-create.js # Disposable email blocking + IP rate limiting
71
+ before-signin.js # Activity update + sign-in analytics
72
+ on-create.js # User doc creation
73
+ on-delete.js # User doc deletion + marketing cleanup
74
+ utils.js # Shared utilities (retryWrite, runAuthHook)
75
+ cron/ # Cron job runners
76
+ runner.js # Shared cron job runner (BEM + consumer hooks)
77
+ daily.js # Daily cron entry point
78
+ daily/{job}.js # Individual daily cron jobs
79
+ frequent.js # Frequent cron entry point
80
+ frequent/{job}.js # Individual frequent cron jobs
81
+ firestore/ # Firestore triggers
82
+ payments-webhooks/ # Webhook processing pipeline
83
+ on-write.js # Orchestrator: fetch→transform→transition→write
84
+ analytics.js # Payment analytics tracking (GA4, Meta, TikTok)
85
+ transitions/ # State transition detection + handlers
86
+ index.js # Transition detection logic
87
+ send-email.js # Shared email helper for handlers
88
+ subscription/ # Subscription transition handlers
89
+ one-time/ # One-time payment transition handlers
90
+ functions/core/ # Built-in functions
69
91
  actions/
70
- api.js # Main bm_api handler
71
- api/{category}/{action}.js # API command handlers
72
- events/
73
- auth/ # Auth event handlers
74
- firestore/ # Firestore triggers
75
- payments-webhooks/ # Webhook processing pipeline
76
- on-write.js # Orchestrator: fetch→transform→transition→write
77
- analytics.js # Payment analytics tracking (GA4, Meta, TikTok)
78
- transitions/ # State transition detection + handlers
79
- index.js # Transition detection logic
80
- send-email.js # Shared email helper for handlers
81
- subscription/ # Subscription transition handlers
82
- one-time/ # One-time payment transition handlers
83
- cron/
84
- daily.js # Daily cron runner
85
- daily/{job}.js # Individual cron jobs
92
+ api.js # Main bm_api handler
93
+ api/{category}/{action}.js # API command handlers
86
94
  routes/ # Built-in routes
87
95
  admin/
88
96
  post/ # POST /admin/post - Create blog posts via GitHub
@@ -142,6 +150,11 @@ functions/
142
150
  {endpoint}/
143
151
  index.js # Schema definition
144
152
  hooks/
153
+ auth/
154
+ before-create.js # Custom pre-signup checks (can block)
155
+ before-signin.js # Custom pre-signin checks (can block)
156
+ on-create.js # Post-signup side effects (non-blocking)
157
+ on-delete.js # Post-deletion side effects (non-blocking)
145
158
  cron/
146
159
  daily/
147
160
  {job}.js # Custom daily jobs
@@ -416,6 +429,80 @@ Job.prototype.main = function () {
416
429
  module.exports = Job;
417
430
  ```
418
431
 
432
+ ### Auth Hooks (Consumer Project)
433
+
434
+ Auth hooks let consumer projects inject custom logic into BEM's auth event lifecycle. BEM runs its core handler first, then looks for a matching hook at `hooks/auth/{event-name}.js`.
435
+
436
+ | Hook | File | Behavior |
437
+ |------|------|----------|
438
+ | `before-create` | `hooks/auth/before-create.js` | Runs after BEM's disposable email + rate limit checks. **Can throw `HttpsError` to block signup.** |
439
+ | `before-signin` | `hooks/auth/before-signin.js` | Runs after BEM's activity update. **Can throw `HttpsError` to block sign-in.** |
440
+ | `on-create` | `hooks/auth/on-create.js` | Runs after BEM creates the user doc. **Non-blocking** — errors are caught and logged. |
441
+ | `on-delete` | `hooks/auth/on-delete.js` | Runs after BEM deletes the user doc. **Non-blocking** — errors are caught and logged. |
442
+
443
+ Hook signature (same as BEM's internal handlers):
444
+ ```javascript
445
+ module.exports = async ({ Manager, assistant, user, context, libraries }) => {
446
+ // user: AuthUserRecord (uid, email, providerData, etc.)
447
+ // context: AuthEventContext for blocking functions (ipAddress, userAgent, additionalUserInfo)
448
+ // EventContext for triggers (eventId, eventType, timestamp — no IP/userAgent)
449
+ // libraries: { admin, functions, ... }
450
+ };
451
+ ```
452
+
453
+ #### Blocking hook example (before-create)
454
+
455
+ ```javascript
456
+ // hooks/auth/before-create.js — Only allow Google OAuth signups
457
+ const ENFORCE = true;
458
+
459
+ const ALLOWED_PROVIDERS = ['google.com'];
460
+
461
+ module.exports = async ({ assistant, user, context, libraries }) => {
462
+ if (!ENFORCE) { return; }
463
+
464
+ const { functions } = libraries;
465
+ const provider = context.additionalUserInfo?.providerId;
466
+
467
+ if (!ALLOWED_PROVIDERS.includes(provider)) {
468
+ assistant.error(`hook/before-create: Blocked provider '${provider}' for ${user.email}`);
469
+ throw new functions.auth.HttpsError('permission-denied', 'Please sign up with Google.');
470
+ }
471
+ };
472
+ ```
473
+
474
+ #### Non-blocking hook example (on-create)
475
+
476
+ ```javascript
477
+ // hooks/auth/on-create.js — Auto-delete spam referrals
478
+ const powertools = require('node-powertools');
479
+
480
+ const ENFORCE = true;
481
+ const BLOCKED_AFFILIATE_CODES = ['iLvQjmvm'];
482
+
483
+ module.exports = async ({ Manager, assistant, user, context, libraries }) => {
484
+ if (!ENFORCE) { return; }
485
+
486
+ const { admin } = libraries;
487
+ const uid = user.uid;
488
+
489
+ // Poll until signup route attaches attribution.affiliate.code
490
+ let referredBy = null;
491
+
492
+ await powertools.poll(async () => {
493
+ const userDoc = await admin.firestore().doc(`users/${uid}`).get().catch(() => null);
494
+ if (!userDoc?.exists) { return true; }
495
+ referredBy = userDoc.data()?.attribution?.affiliate?.code;
496
+ return !!referredBy;
497
+ }, { interval: 10000, timeout: 60000 }).catch(() => {});
498
+
499
+ if (!referredBy || !BLOCKED_AFFILIATE_CODES.includes(referredBy)) { return; }
500
+
501
+ // Delete spam account (triggers on-delete for cleanup)
502
+ await admin.auth().deleteUser(uid).catch(e => assistant.error('Delete failed:', e));
503
+ };
504
+ ```
505
+
419
506
  ## Common Operations
420
507
 
421
508
  ### Authenticate User
@@ -482,7 +569,9 @@ Manager.handlers.bm_api = function (mod, position) {
482
569
  | Schemas | `schemas/{name}/` | `index.js` or `{method}.js` |
483
570
  | API Commands | `actions/api/{category}/` | `{action}.js` |
484
571
  | Auth Events | `events/auth/` | `{event}.js` |
485
- | Cron Jobs | `cron/daily/` or `hooks/cron/daily/` | `{job}.js` |
572
+ | Auth Hooks (consumer) | `hooks/auth/` | `{event}.js` |
573
+ | Cron Jobs (BEM) | `events/cron/daily/` | `{job}.js` |
574
+ | Cron Jobs (consumer) | `hooks/cron/daily/` | `{job}.js` |
486
575
 
487
576
  ## Admin Post Route
488
577
 
@@ -1302,6 +1391,12 @@ marketing: {
1302
1391
  | Rate limiting | `src/manager/helpers/usage.js` |
1303
1392
  | User properties + schema | `src/manager/helpers/user.js` |
1304
1393
  | Batch utilities | `src/manager/helpers/utilities.js` |
1394
+ | Auth: before-create | `src/manager/events/auth/before-create.js` |
1395
+ | Auth: before-signin | `src/manager/events/auth/before-signin.js` |
1396
+ | Auth: on-create | `src/manager/events/auth/on-create.js` |
1397
+ | Auth: on-delete | `src/manager/events/auth/on-delete.js` |
1398
+ | Auth: shared utilities | `src/manager/events/auth/utils.js` |
1399
+ | Cron runner | `src/manager/events/cron/runner.js` |
1305
1400
  | Main API handler | `src/manager/functions/core/actions/api.js` |
1306
1401
  | Config template | `templates/backend-manager-config.json` |
1307
1402
  | CLI entry | `src/cli/index.js` |
package/TODO-2.md CHANGED
@@ -23,6 +23,9 @@ waht about when they request a cancel
23
23
  Read cancellation-requested.js
24
24
  The category is order/cancellation-requested (line 13).
25
25
 
26
+ ---
27
+ MIRROR settigns in BEM JSON so that usage reset can properly get MIRRED DOCS liek slapform forms or chatsy agents DOCS
28
+
26
29
  ---
27
30
  GHOSTII REVAMP
28
31
  * better logic for generating posts. better model? claude?
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backend-manager",
3
- "version": "5.0.190",
3
+ "version": "5.0.192",
4
4
  "description": "Quick tools for developing Firebase functions",
5
5
  "main": "src/manager/index.js",
6
6
  "bin": {
@@ -0,0 +1,30 @@
1
+ const BaseTest = require('./base-test');
2
+ const jetpack = require('fs-jetpack');
3
+
4
+ const HOOKS_DIRS = [
5
+ 'hooks/auth',
6
+ 'hooks/cron/daily',
7
+ ];
8
+
9
+ class HooksDirectoriesTest extends BaseTest {
10
+ getName() {
11
+ return 'hooks directories exist';
12
+ }
13
+
14
+ async run() {
15
+ const self = this.self;
16
+ const functionsDir = `${self.firebaseProjectPath}/functions`;
17
+
18
+ for (const dir of HOOKS_DIRS) {
19
+ jetpack.dir(`${functionsDir}/${dir}`);
20
+ }
21
+
22
+ return true;
23
+ }
24
+
25
+ async fix() {
26
+ throw new Error('No automatic fix available for this test');
27
+ }
28
+ }
29
+
30
+ module.exports = HooksDirectoriesTest;
@@ -36,6 +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
40
  const LegacyTestsCleanupTest = require('./legacy-tests-cleanup');
40
41
  const MarketingCampaignsSeededTest = require('./marketing-campaigns-seeded');
41
42
 
@@ -78,6 +79,7 @@ function getTests(context) {
78
79
  new RemoteconfigTemplateFileTest(context),
79
80
  new HostingFolderTest(context),
80
81
  new PublicHtmlFilesTest(context),
82
+ new HooksDirectoriesTest(context),
81
83
  new LegacyTestsCleanupTest(context),
82
84
  new MarketingCampaignsSeededTest(context),
83
85
  ];