backend-manager 5.11.5 → 5.11.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 (33) hide show
  1. package/package.json +1 -1
  2. package/src/cli/commands/serve.js +49 -2
  3. package/src/manager/functions/core/actions/api/general/add-marketing-contact.js +4 -1
  4. package/src/manager/libraries/prompts/infer-contact.md +5 -0
  5. package/src/manager/routes/marketing/contact/post.js +4 -1
  6. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-24-21/metadata.json +14 -0
  7. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-24-21/newsletter.html +486 -0
  8. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-24-21/newsletter.md +41 -0
  9. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-24-21/newsletter.mjml +97 -0
  10. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-24-21/structure.json +42 -0
  11. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-24-21/summary.md +1 -0
  12. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-25-51/metadata.json +14 -0
  13. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-25-51/newsletter.html +486 -0
  14. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-25-51/newsletter.md +41 -0
  15. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-25-51/newsletter.mjml +97 -0
  16. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-25-51/structure.json +42 -0
  17. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-25-51/summary.md +1 -0
  18. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-27-47/metadata.json +14 -0
  19. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-27-47/newsletter.html +486 -0
  20. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-27-47/newsletter.md +41 -0
  21. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-27-47/newsletter.mjml +97 -0
  22. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-27-47/structure.json +42 -0
  23. package/src/test/fixtures/firebase-project/.temp/newsletter/run-2026-07-03T23-27-47/summary.md +1 -0
  24. package/src/test/fixtures/firebase-project/.temp/test-mode.json +1 -1
  25. package/src/test/fixtures/firebase-project/database-debug.log +8 -8
  26. package/src/test/fixtures/firebase-project/firestore-debug.log +56 -54
  27. package/src/test/fixtures/firebase-project/pubsub-debug.log +4 -4
  28. package/test/email/marketing-lifecycle.js +5 -0
  29. package/test/email/validation.js +42 -7
  30. package/test/functions/admin/edit-post.js +14 -10
  31. package/test/functions/general/add-marketing-contact.js +56 -16
  32. package/test/routes/admin/post.js +14 -10
  33. package/test/routes/marketing/contact.js +43 -10
@@ -2,5 +2,5 @@
2
2
  "env": {
3
3
  "TEST_EXTENDED_MODE": ""
4
4
  },
5
- "updatedAt": "2026-07-02T08:47:59.846Z"
5
+ "updatedAt": "2026-07-04T00:56:35.608Z"
6
6
  }
@@ -2,11 +2,11 @@ WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
2
2
  WARNING: sun.misc.Unsafe::objectFieldOffset has been called by akka.util.Unsafe (file:/Users/ian/.cache/firebase/emulators/firebase-database-emulator-v4.11.2.jar)
3
3
  WARNING: Please consider reporting this to the maintainers of class akka.util.Unsafe
4
4
  WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
5
- 01:48:07.812 [NamespaceSystem-akka.actor.default-dispatcher-4] INFO akka.event.slf4j.Slf4jLogger - Slf4jLogger started
6
- 01:48:07.913 [main] INFO com.firebase.server.forge.App$ - Listening at 127.0.0.1:9000
7
- 01:48:18.289 [NamespaceSystem-akka.actor.default-dispatcher-4] INFO com.firebase.core.namespace.NamespaceActor - demo-backend-manager-default-rtdb successfully activated FBKV (SurveyIdle(0)) wait: 73ms, init: 0ms
8
- 01:48:18.324 [NamespaceSystem-blocking-namespace-operation-dispatcher-6] INFO com.firebase.core.namespace.StateManager - Namespace demo-backend-manager-default-rtdb status Active to Active
9
- 01:48:29.496 [Thread-0] INFO com.firebase.server.forge.App$ - Attempting graceful shutdown.
10
- 01:48:29.562 [NamespaceSystem-akka.actor.default-dispatcher-5] INFO com.firebase.core.namespace.Terminator$Terminator - 1 actors left to terminate: demo-backend-manager-default-rtdb
11
- 01:48:29.581 [NamespaceSystem-akka.actor.default-dispatcher-7] INFO com.firebase.core.namespace.NamespaceActor - stopped namespace actor for demo-backend-manager-default-rtdb
12
- 01:48:29.604 [Thread-0] INFO com.firebase.server.forge.App$ - Graceful shutdown complete.
5
+ 17:56:43.904 [NamespaceSystem-akka.actor.default-dispatcher-4] INFO akka.event.slf4j.Slf4jLogger - Slf4jLogger started
6
+ 17:56:44.025 [main] INFO com.firebase.server.forge.App$ - Listening at 127.0.0.1:9000
7
+ 17:56:55.055 [NamespaceSystem-akka.actor.default-dispatcher-4] INFO com.firebase.core.namespace.NamespaceActor - demo-backend-manager-default-rtdb successfully activated FBKV (SurveyIdle(0)) wait: 84ms, init: 0ms
8
+ 17:56:55.099 [NamespaceSystem-blocking-namespace-operation-dispatcher-6] INFO com.firebase.core.namespace.StateManager - Namespace demo-backend-manager-default-rtdb status Active to Active
9
+ 17:57:09.904 [Thread-0] INFO com.firebase.server.forge.App$ - Attempting graceful shutdown.
10
+ 17:57:09.909 [NamespaceSystem-akka.actor.default-dispatcher-4] INFO com.firebase.core.namespace.Terminator$Terminator - 1 actors left to terminate: demo-backend-manager-default-rtdb
11
+ 17:57:09.913 [NamespaceSystem-akka.actor.default-dispatcher-4] INFO com.firebase.core.namespace.NamespaceActor - stopped namespace actor for demo-backend-manager-default-rtdb
12
+ 17:57:09.917 [Thread-0] INFO com.firebase.server.forge.App$ - Graceful shutdown complete.
@@ -2,7 +2,7 @@ WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
2
2
  WARNING: sun.misc.Unsafe::allocateMemory has been called by io.netty.util.internal.PlatformDependent0$2 (file:/Users/ian/.cache/firebase/emulators/cloud-firestore-emulator-v1.21.0.jar)
3
3
  WARNING: Please consider reporting this to the maintainers of class io.netty.util.internal.PlatformDependent0$2
4
4
  WARNING: sun.misc.Unsafe::allocateMemory will be removed in a future release
5
- Jul 02, 2026 1:48:06 AM com.google.cloud.datastore.emulator.firestore.websocket.WebSocketServer start
5
+ Jul 03, 2026 5:56:42 PM com.google.cloud.datastore.emulator.firestore.websocket.WebSocketServer start
6
6
  INFO: Started WebSocket server on ws://127.0.0.1:9150
7
7
 
8
8
  API endpoint: http://127.0.0.1:8080
@@ -20,111 +20,113 @@ If you are running a Firestore in Datastore Mode project, run:
20
20
  Note: Support for Datastore Mode is in preview. If you encounter any bugs please file at https://github.com/firebase/firebase-tools/issues.
21
21
  Dev App Server is now running.
22
22
 
23
- Jul 02, 2026 1:48:17 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
23
+ Jul 03, 2026 5:56:54 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
24
24
  INFO: Detected non-HTTP/2 connection.
25
- Jul 02, 2026 1:48:17 AM io.grpc.netty.TcpMetrics loadEpollInfo
25
+ Jul 03, 2026 5:56:54 PM io.grpc.netty.TcpMetrics loadEpollInfo
26
26
  INFO: Epoll available during static init of TcpMetrics:false
27
- Jul 02, 2026 1:48:17 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
27
+ Jul 03, 2026 5:56:54 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
28
28
  INFO: Detected HTTP/2 connection.
29
- Jul 02, 2026 1:48:19 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
29
+ Jul 03, 2026 5:56:56 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
30
30
  INFO: Detected HTTP/2 connection.
31
- Jul 02, 2026 1:48:19 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
31
+ Jul 03, 2026 5:56:56 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
32
32
  INFO: Detected HTTP/2 connection.
33
- Jul 02, 2026 1:48:27 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
33
+ Jul 03, 2026 5:57:07 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
34
34
  INFO: Detected HTTP/2 connection.
35
- Jul 02, 2026 1:48:27 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
35
+ Jul 03, 2026 5:57:07 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
36
36
  INFO: Detected HTTP/2 connection.
37
- Jul 02, 2026 1:48:27 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
37
+ Jul 03, 2026 5:57:07 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
38
38
  INFO: Detected HTTP/2 connection.
39
- Jul 02, 2026 1:48:27 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
39
+ Jul 03, 2026 5:57:07 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
40
40
  INFO: Detected HTTP/2 connection.
41
- Jul 02, 2026 1:48:27 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
41
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
42
42
  INFO: Detected HTTP/2 connection.
43
- Jul 02, 2026 1:48:27 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
43
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
44
44
  INFO: Detected HTTP/2 connection.
45
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
45
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
46
46
  INFO: Detected HTTP/2 connection.
47
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
47
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
48
48
  INFO: Detected HTTP/2 connection.
49
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
49
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
50
50
  INFO: Detected HTTP/2 connection.
51
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
51
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
52
52
  INFO: Detected HTTP/2 connection.
53
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
53
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
54
54
  INFO: Detected HTTP/2 connection.
55
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
55
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
56
56
  INFO: Detected HTTP/2 connection.
57
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
57
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
58
58
  INFO: Detected HTTP/2 connection.
59
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
59
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
60
60
  INFO: Detected HTTP/2 connection.
61
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
61
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
62
62
  INFO: Detected HTTP/2 connection.
63
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
63
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
64
64
  INFO: Detected HTTP/2 connection.
65
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
65
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
66
66
  INFO: Detected HTTP/2 connection.
67
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
67
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
68
68
  INFO: Detected HTTP/2 connection.
69
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
69
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
70
70
  INFO: Detected HTTP/2 connection.
71
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
71
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
72
72
  INFO: Detected HTTP/2 connection.
73
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
73
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
74
74
  INFO: Detected HTTP/2 connection.
75
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
75
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
76
76
  INFO: Detected HTTP/2 connection.
77
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
77
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
78
78
  INFO: Detected HTTP/2 connection.
79
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
79
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
80
80
  INFO: Detected HTTP/2 connection.
81
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
81
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
82
82
  INFO: Detected HTTP/2 connection.
83
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
83
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
84
84
  INFO: Detected HTTP/2 connection.
85
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
85
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
86
86
  INFO: Detected HTTP/2 connection.
87
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
87
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
88
88
  INFO: Detected HTTP/2 connection.
89
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
89
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
90
90
  INFO: Detected HTTP/2 connection.
91
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
91
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
92
92
  INFO: Detected HTTP/2 connection.
93
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
93
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
94
94
  INFO: Detected HTTP/2 connection.
95
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
95
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
96
96
  INFO: Detected HTTP/2 connection.
97
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
97
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
98
98
  INFO: Detected HTTP/2 connection.
99
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
99
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
100
100
  INFO: Detected HTTP/2 connection.
101
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
101
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
102
102
  INFO: Detected HTTP/2 connection.
103
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
103
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
104
104
  INFO: Detected HTTP/2 connection.
105
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
105
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
106
106
  INFO: Detected HTTP/2 connection.
107
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
107
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
108
108
  INFO: Detected HTTP/2 connection.
109
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
109
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
110
110
  INFO: Detected HTTP/2 connection.
111
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
111
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
112
112
  INFO: Detected HTTP/2 connection.
113
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
113
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
114
114
  INFO: Detected HTTP/2 connection.
115
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
115
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
116
116
  INFO: Detected HTTP/2 connection.
117
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
117
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
118
118
  INFO: Detected HTTP/2 connection.
119
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
119
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
120
120
  INFO: Detected HTTP/2 connection.
121
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
121
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
122
122
  INFO: Detected HTTP/2 connection.
123
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
123
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
124
124
  INFO: Detected HTTP/2 connection.
125
- Jul 02, 2026 1:48:28 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
125
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
126
126
  INFO: Detected HTTP/2 connection.
127
- Jul 02, 2026 1:48:29 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
127
+ Jul 03, 2026 5:57:08 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
128
+ INFO: Detected HTTP/2 connection.
129
+ Jul 03, 2026 5:57:09 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
128
130
  INFO: Detected HTTP/2 connection.
129
131
  *** shutting down gRPC server since JVM is shutting down
130
132
  *** server shut down
@@ -1,17 +1,17 @@
1
1
  This is the Google Pub/Sub fake.
2
2
  Implementation may be incomplete or differ from the real system.
3
- Jul 02, 2026 1:48:11 AM com.google.cloud.pubsub.testing.v1.Main main
3
+ Jul 03, 2026 5:56:47 PM com.google.cloud.pubsub.testing.v1.Main main
4
4
  INFO: IAM integration is disabled. IAM policy methods and ACL checks are not supported
5
5
  SLF4J(W): No SLF4J providers were found.
6
6
  SLF4J(W): Defaulting to no-operation (NOP) logger implementation
7
7
  SLF4J(W): See https://www.slf4j.org/codes.html#noProviders for further details.
8
8
  WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
9
- WARNING: sun.misc.Unsafe::allocateMemory has been called by io.netty.util.internal.PlatformDependent0$2 (file:/Users/ian/.cache/firebase/emulators/pubsub-emulator-0.8.33/pubsub-emulator/lib/cloud-pubsub-emulator-0.8.33-all.jar)
9
+ WARNING: sun.misc.Unsafe::allocateMemory has been called by io.netty.util.internal.PlatformDependent0$2 (file:/Users/ian/.cache/firebase/emulators/pubsub-emulator-0.8.34/pubsub-emulator/lib/cloud-pubsub-emulator-0.8.34-all.jar)
10
10
  WARNING: Please consider reporting this to the maintainers of class io.netty.util.internal.PlatformDependent0$2
11
11
  WARNING: sun.misc.Unsafe::allocateMemory will be removed in a future release
12
- Jul 02, 2026 1:48:11 AM com.google.cloud.pubsub.testing.v1.Main main
12
+ Jul 03, 2026 5:56:48 PM com.google.cloud.pubsub.testing.v1.Main main
13
13
  INFO: Server started, listening on 8085
14
- Jul 02, 2026 1:48:18 AM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
14
+ Jul 03, 2026 5:56:54 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead
15
15
  INFO: Detected HTTP/2 connection.
16
16
  *** shutting down gRPC server since JVM is shutting down
17
17
  *** server shut down
@@ -37,6 +37,11 @@ module.exports = {
37
37
  firstName: 'Lifecycle',
38
38
  lastName: 'Test',
39
39
  source: 'bem-test-lifecycle',
40
+ // skipValidation bypasses the mailbox verification check — the lifecycle
41
+ // address has no real mailbox so the provider (correctly) marks it
42
+ // undeliverable. This suite tests add → sync → remove against the real
43
+ // providers, not deliverability (covered by email/validation + contact tests).
44
+ skipValidation: true,
40
45
  });
41
46
 
42
47
  assert.isSuccess(response, 'Add contact should succeed');
@@ -679,8 +679,10 @@ module.exports = {
679
679
  async run({ assert, skip }) {
680
680
  const result = await validate('disposable@gmail.com', { checks: ALL_CHECKS });
681
681
 
682
- if (result.checks.mailbox?.error?.includes('out of credits')) {
683
- skip('Mailbox verification out of credits');
682
+ // Provider errors fail open as { valid: true, error } with NO status field —
683
+ // out of credits, bad key, timeouts. Nothing assertable; skip.
684
+ if (result.checks.mailbox?.error) {
685
+ skip(`Mailbox verification unavailable: ${result.checks.mailbox.error}`);
684
686
  }
685
687
 
686
688
  assert.hasProperty(result, 'checks.mailbox', 'Should have mailbox check');
@@ -689,7 +691,7 @@ module.exports = {
689
691
  },
690
692
 
691
693
  {
692
- name: 'mailbox-fake-domain-fails',
694
+ name: 'mailbox-not-called-when-dns-fails',
693
695
  timeout: 15000,
694
696
  skip: !process.env.TEST_EXTENDED_MODE || !HAS_MAILBOX_API_KEY
695
697
  ? 'TEST_EXTENDED_MODE or mailbox API key not set'
@@ -698,12 +700,45 @@ module.exports = {
698
700
  async run({ assert, skip }) {
699
701
  const result = await validate('rachel.greene@thisfakedomain99999.com', { checks: ALL_CHECKS });
700
702
 
701
- if (result.checks.mailbox?.error?.includes('out of credits')) {
702
- skip('Mailbox verification out of credits');
703
+ // Transient resolver errors fail open (dns: { valid: true, skipped: true })
704
+ // and let mailbox run — the short-circuit can't be observed that run.
705
+ if (result.checks.dns?.skipped) {
706
+ skip(`DNS lookup errored (${result.checks.dns.reason}) — cannot observe short-circuit`);
703
707
  }
704
708
 
705
- assert.hasProperty(result, 'checks.mailbox.status', 'Should have status');
706
- assert.notEqual(result.checks.mailbox.status, 'valid', 'Fake domain should not be valid');
709
+ assert.equal(result.valid, false, 'Fake domain should be invalid');
710
+ assert.propertyEquals(result, 'checks.dns.valid', false, 'DNS check should fail (NXDOMAIN)');
711
+ assert.equal(
712
+ result.checks.mailbox,
713
+ undefined,
714
+ 'Mailbox must NOT run after dns fails — the short-circuit saves a paid API call'
715
+ );
716
+ },
717
+ },
718
+
719
+ {
720
+ name: 'mailbox-nonexistent-mailbox-not-valid',
721
+ timeout: 15000,
722
+ skip: !process.env.TEST_EXTENDED_MODE || !HAS_MAILBOX_API_KEY
723
+ ? 'TEST_EXTENDED_MODE or mailbox API key not set'
724
+ : false,
725
+
726
+ async run({ assert, skip }) {
727
+ // Real-MX domain (gmail passes dns) with a fabricated local part — this is
728
+ // the case that actually reaches the paid mailbox provider.
729
+ const result = await validate('rachel.greene.zk84qx@gmail.com', { checks: ALL_CHECKS });
730
+
731
+ assert.hasProperty(result, 'checks.mailbox', 'Should have mailbox check');
732
+
733
+ const mb = result.checks.mailbox;
734
+
735
+ // Provider errors fail open as { valid: true, error } with NO status; skip.
736
+ if (mb.error) {
737
+ skip(`Mailbox verification unavailable: ${mb.error}`);
738
+ }
739
+
740
+ assert.hasProperty(mb, 'status', 'Should have status');
741
+ assert.notEqual(mb.status, 'valid', 'Fabricated gmail mailbox should not verify as valid');
707
742
  },
708
743
  },
709
744
  ],
@@ -243,10 +243,10 @@ module.exports = {
243
243
  {
244
244
  name: 'verify-edit',
245
245
  auth: 'admin',
246
- timeout: 30000,
246
+ timeout: 90000, // waitFor polls up to 60s for GitHub content propagation
247
247
  skip: !process.env.GH_TOKEN ? 'GH_TOKEN env var not set' : false,
248
248
 
249
- async run({ assert, state }) {
249
+ async run({ assert, state, waitFor }) {
250
250
  if (!state.postPath || state.editSkipped) {
251
251
  assert.ok(true, 'Skipping verify - edit was not performed');
252
252
  return;
@@ -254,15 +254,19 @@ module.exports = {
254
254
 
255
255
  const octokit = new Octokit({ auth: process.env.GH_TOKEN });
256
256
 
257
- // Fetch the file directly from GitHub
258
- const { data: fileData } = await octokit.rest.repos.getContent({
259
- owner: state.owner,
260
- repo: state.repo,
261
- path: state.postPath,
262
- });
257
+ // GitHub's contents API is eventually consistent after createOrUpdateFileContents —
258
+ // an immediate read-back can serve the pre-edit blob. Poll until the edit lands.
259
+ const content = await waitFor(async () => {
260
+ const { data: fileData } = await octokit.rest.repos.getContent({
261
+ owner: state.owner,
262
+ repo: state.repo,
263
+ path: state.postPath,
264
+ });
265
+
266
+ const decoded = Buffer.from(fileData.content, 'base64').toString();
263
267
 
264
- // Decode the content
265
- const content = Buffer.from(fileData.content, 'base64').toString();
268
+ return decoded.includes('EDITED by the BEM test suite') ? decoded : false;
269
+ }, 60000, 3000);
266
270
 
267
271
  assert.ok(
268
272
  content.includes('EDITED by the BEM test suite'),
@@ -7,9 +7,11 @@
7
7
  */
8
8
 
9
9
  // Test email patterns - look like real emails but +bem suffix identifies them for cleanup.
10
- // Fixed `acme.com` test domain — deterministic across brands. Avoids cross-brand
11
- // state contamination in SendGrid/Beehiiv when the same test runs under different brands.
12
- const TEST_DOMAIN = 'acme.com';
10
+ // Fixed `itwcreativeworks.com` test domain — deterministic across brands (avoids cross-brand
11
+ // state contamination in SendGrid/Beehiiv) and we control it: mailbox verdicts are stable
12
+ // and the AI won't flag it as a placeholder (which `acme.com` now is — empty names,
13
+ // confidence 0 — breaking inference).
14
+ const TEST_DOMAIN = 'itwcreativeworks.com';
13
15
  const TEST_EMAILS = {
14
16
  valid: () => `sarah.martinez+bem@${TEST_DOMAIN}`, // Should infer: Sarah Martinez
15
17
  invalid: () => `nonexistent.user+bem@${TEST_DOMAIN}`, // No such mailbox — mailbox verification should flag invalid
@@ -32,7 +34,11 @@ module.exports = {
32
34
  const response = await http.command('general:add-marketing-contact', {
33
35
  email: testEmail,
34
36
  source: 'bem-test',
35
- // No firstName/lastName - should be inferred as "Rachel Greene"
37
+ // No firstName/lastName - should be inferred
38
+ // skipValidation bypasses the mailbox verification check — the test email
39
+ // doesn't have a real mailbox so the provider (correctly) marks it as not
40
+ // deliverable. We're testing the command flow, not the deliverability check.
41
+ skipValidation: true,
36
42
  });
37
43
 
38
44
  assert.isSuccess(response, 'Add marketing contact should succeed for admin');
@@ -146,6 +152,10 @@ module.exports = {
146
152
  email: testEmail,
147
153
  source: 'bem-test',
148
154
  // No firstName/lastName - should be inferred
155
+ // skipValidation bypasses the mailbox verification check — this test's concern
156
+ // is AI name inference (which runs after validation); without it the mailbox
157
+ // check 400s the fabricated address before the inference asserts run.
158
+ skipValidation: true,
149
159
  });
150
160
 
151
161
  assert.isSuccess(response, 'Add marketing contact should succeed');
@@ -227,6 +237,27 @@ module.exports = {
227
237
  source: 'bem-test',
228
238
  });
229
239
 
240
+ // Outcome A: the provider rejected the fabricated mailbox → admin 400. Every
241
+ // earlier check passes deterministically for this address (format/localPart/
242
+ // disposable/corporate return their own dedicated messages, typo can't match,
243
+ // and the domain has real MX), so reaching the generic validation-failed 400
244
+ // proves the paid mailbox pipeline ran and rejected. Nothing was added, so
245
+ // cleanup is a no-op.
246
+ if (response.status === 400) {
247
+ assert.ok(
248
+ response.error?.includes('Email validation failed'),
249
+ `400 should be the validation-failed branch, got: ${response.error}`
250
+ );
251
+
252
+ if (response.error.includes('(dns)')) {
253
+ skip('Transient DNS failure — mailbox check never ran');
254
+ }
255
+
256
+ assert.ok(response.error.includes('(mailbox)'), `Failed check should be mailbox, got: ${response.error}`);
257
+ return;
258
+ }
259
+
260
+ // Outcome B: provider accepted (valid/catch-all/unknown) or failed open.
230
261
  assert.isSuccess(response, 'Add marketing contact should succeed');
231
262
 
232
263
  // Check that validation info is included
@@ -238,9 +269,10 @@ module.exports = {
238
269
 
239
270
  const mbResult = response.data.validation.checks.mailbox;
240
271
 
241
- // If out of credits, skip test - not a failure
242
- if (mbResult.error?.includes('out of credits')) {
243
- skip('Mailbox verification out of credits');
272
+ // Provider errors fail open as { valid: true, error, provider } with NO status
273
+ // field — out of credits, bad key, timeouts. Nothing assertable; skip.
274
+ if (mbResult.error) {
275
+ skip(`Mailbox verification unavailable: ${mbResult.error}`);
244
276
  }
245
277
 
246
278
  assert.hasProperty(mbResult, 'status', 'Mailbox check should return status');
@@ -268,7 +300,8 @@ module.exports = {
268
300
  : false,
269
301
 
270
302
  async run({ http, assert, skip }) {
271
- // Use fake email that mailbox verification should flag as invalid
303
+ // Email that should reach the mailbox provider and be flagged as undeliverable.
304
+ // Must NOT trip earlier checks (localPart blocklist, disposable, corporate).
272
305
  const testEmail = TEST_EMAILS.invalid();
273
306
 
274
307
  const response = await http.command('general:add-marketing-contact', {
@@ -276,20 +309,27 @@ module.exports = {
276
309
  source: 'bem-test',
277
310
  });
278
311
 
279
- // Should still succeed (we fail open) but mailbox should report invalid
280
- assert.isSuccess(response, 'Request should succeed even with invalid email');
281
-
282
312
  const mbResult = response.data?.validation?.checks?.mailbox;
283
313
 
284
- // If out of credits, skip test - not a failure
285
- if (mbResult?.error?.includes('out of credits')) {
286
- skip('Mailbox verification out of credits');
314
+ // Provider errors fail open with no status — the test can't exercise rejection; skip.
315
+ if (mbResult?.error) {
316
+ skip(`Mailbox verification unavailable: ${mbResult.error}`);
317
+ }
318
+
319
+ // If the response was a 400, that's the legitimate rejection path — done.
320
+ if (response.status === 400) {
321
+ if (response.error?.includes('(dns)')) {
322
+ skip('Transient DNS failure — mailbox check never ran');
323
+ }
324
+
325
+ assert.ok(response.error?.includes('(mailbox)'), `Failed check should be mailbox, got: ${response.error}`);
326
+ return;
287
327
  }
288
328
 
289
- // Mailbox should return a status indicating the email is not valid
329
+ // Otherwise expect a 200 with a non-"valid" mailbox status.
330
+ assert.isSuccess(response, 'Request should succeed (fail-open) or error 400');
290
331
  if (mbResult) {
291
332
  assert.hasProperty(mbResult, 'status', 'Should have status');
292
- // Status should NOT be 'valid' for this fake email
293
333
  assert.notEqual(mbResult.status, 'valid', 'Fake email should not be marked valid');
294
334
  }
295
335
  },
@@ -245,10 +245,10 @@ module.exports = {
245
245
  {
246
246
  name: 'verify-edit',
247
247
  auth: 'admin',
248
- timeout: 30000,
248
+ timeout: 90000, // waitFor polls up to 60s for GitHub content propagation
249
249
  skip: !process.env.GH_TOKEN ? 'GH_TOKEN env var not set' : false,
250
250
 
251
- async run({ assert, state }) {
251
+ async run({ assert, state, waitFor }) {
252
252
  if (!state.postPath || state.editSkipped) {
253
253
  assert.ok(true, 'Skipping verify - edit was not performed');
254
254
  return;
@@ -256,15 +256,19 @@ module.exports = {
256
256
 
257
257
  const octokit = new Octokit({ auth: process.env.GH_TOKEN });
258
258
 
259
- // Fetch the file directly from GitHub
260
- const { data: fileData } = await octokit.rest.repos.getContent({
261
- owner: state.owner,
262
- repo: state.repo,
263
- path: state.postPath,
264
- });
259
+ // GitHub's contents API is eventually consistent after createOrUpdateFileContents —
260
+ // an immediate read-back can serve the pre-edit blob. Poll until the edit lands.
261
+ const content = await waitFor(async () => {
262
+ const { data: fileData } = await octokit.rest.repos.getContent({
263
+ owner: state.owner,
264
+ repo: state.repo,
265
+ path: state.postPath,
266
+ });
267
+
268
+ const decoded = Buffer.from(fileData.content, 'base64').toString();
265
269
 
266
- // Decode the content
267
- const content = Buffer.from(fileData.content, 'base64').toString();
270
+ return decoded.includes('EDITED by the BEM test suite') ? decoded : false;
271
+ }, 60000, 3000);
268
272
 
269
273
  assert.ok(
270
274
  content.includes('EDITED by the BEM test suite'),