@omega.js/backend 0.1.0

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 (993) hide show
  1. package/LICENSE +98 -0
  2. package/README.md +1053 -0
  3. package/bin/omega +2 -0
  4. package/bin/omega-backend +2 -0
  5. package/bin/omg +2 -0
  6. package/cli.js +3 -0
  7. package/dist/cli/command-table.js +268 -0
  8. package/dist/cli/commands/auth.js +259 -0
  9. package/dist/cli/commands/base-command.js +451 -0
  10. package/dist/cli/commands/build.js +27 -0
  11. package/dist/cli/commands/clean.js +13 -0
  12. package/dist/cli/commands/clear.js +11 -0
  13. package/dist/cli/commands/cwd.js +9 -0
  14. package/dist/cli/commands/deploy.js +205 -0
  15. package/dist/cli/commands/emulator-orphans.js +440 -0
  16. package/dist/cli/commands/emulator.js +1829 -0
  17. package/dist/cli/commands/firebase-init.js +101 -0
  18. package/dist/cli/commands/firestore.js +261 -0
  19. package/dist/cli/commands/indexes.js +51 -0
  20. package/dist/cli/commands/install.js +151 -0
  21. package/dist/cli/commands/logs.js +358 -0
  22. package/dist/cli/commands/mcp.js +41 -0
  23. package/dist/cli/commands/migrate-markers.js +176 -0
  24. package/dist/cli/commands/migrate-rules.js +90 -0
  25. package/dist/cli/commands/migrate.js +59 -0
  26. package/dist/cli/commands/serve.js +208 -0
  27. package/dist/cli/commands/setup-tests/base-test.js +174 -0
  28. package/dist/cli/commands/setup-tests/emulator-config.js +109 -0
  29. package/dist/cli/commands/setup-tests/env-runtime-config-deprecated.js +47 -0
  30. package/dist/cli/commands/setup-tests/firebase-admin.js +49 -0
  31. package/dist/cli/commands/setup-tests/firebase-auth.js +26 -0
  32. package/dist/cli/commands/setup-tests/firebase-cli.js +26 -0
  33. package/dist/cli/commands/setup-tests/firebase-functions.js +49 -0
  34. package/dist/cli/commands/setup-tests/firestore-indexes-file.js +72 -0
  35. package/dist/cli/commands/setup-tests/firestore-indexes-in-json.js +20 -0
  36. package/dist/cli/commands/setup-tests/firestore-indexes-required.js +108 -0
  37. package/dist/cli/commands/setup-tests/firestore-indexes-synced.js +236 -0
  38. package/dist/cli/commands/setup-tests/firestore-rules-file.js +115 -0
  39. package/dist/cli/commands/setup-tests/firestore-rules-in-json.js +49 -0
  40. package/dist/cli/commands/setup-tests/functions-package.js +44 -0
  41. package/dist/cli/commands/setup-tests/gcloud-cli.js +26 -0
  42. package/dist/cli/commands/setup-tests/gitignore.js +122 -0
  43. package/dist/cli/commands/setup-tests/helpers/merge-line-files.js +28 -0
  44. package/dist/cli/commands/setup-tests/helpers/required-indexes.js +109 -0
  45. package/dist/cli/commands/setup-tests/helpers/seed-campaigns.js +246 -0
  46. package/dist/cli/commands/setup-tests/helpers.js +72 -0
  47. package/dist/cli/commands/setup-tests/hosting-folder.js +23 -0
  48. package/dist/cli/commands/setup-tests/hosting-rewrites.js +52 -0
  49. package/dist/cli/commands/setup-tests/index.js +111 -0
  50. package/dist/cli/commands/setup-tests/is-firebase-project.js +21 -0
  51. package/dist/cli/commands/setup-tests/java-installed.js +27 -0
  52. package/dist/cli/commands/setup-tests/legacy-tests-cleanup.js +43 -0
  53. package/dist/cli/commands/setup-tests/marketing-campaigns-seeded.js +230 -0
  54. package/dist/cli/commands/setup-tests/node-version.js +60 -0
  55. package/dist/cli/commands/setup-tests/npm-project-scripts.js +42 -0
  56. package/dist/cli/commands/setup-tests/nvmrc-version.js +36 -0
  57. package/dist/cli/commands/setup-tests/omega-backend.js +42 -0
  58. package/dist/cli/commands/setup-tests/omega-config.js +84 -0
  59. package/dist/cli/commands/setup-tests/project-directories.js +34 -0
  60. package/dist/cli/commands/setup-tests/project-id-consistency.js +201 -0
  61. package/dist/cli/commands/setup-tests/public-html-files.js +22 -0
  62. package/dist/cli/commands/setup-tests/realtime-rules-file.js +69 -0
  63. package/dist/cli/commands/setup-tests/realtime-rules-in-json.js +20 -0
  64. package/dist/cli/commands/setup-tests/remoteconfig-template-file.js +32 -0
  65. package/dist/cli/commands/setup-tests/remoteconfig-template-in-json.js +31 -0
  66. package/dist/cli/commands/setup-tests/service-account.js +86 -0
  67. package/dist/cli/commands/setup-tests/storage-lifecycle-policy.js +81 -0
  68. package/dist/cli/commands/setup-tests/storage-rules-file.js +32 -0
  69. package/dist/cli/commands/setup-tests/storage-rules-in-json.js +20 -0
  70. package/dist/cli/commands/stripe.js +14 -0
  71. package/dist/cli/commands/test-lanes/stripe-live.js +440 -0
  72. package/dist/cli/commands/test.js +976 -0
  73. package/dist/cli/commands/update.js +27 -0
  74. package/dist/cli/commands/version.js +10 -0
  75. package/dist/cli/commands/watch.js +196 -0
  76. package/dist/cli/flags.js +13 -0
  77. package/dist/cli/index.js +173 -0
  78. package/dist/cli/run.js +33 -0
  79. package/dist/cli/utils/attach-log-file.js +4 -0
  80. package/dist/cli/utils/compile-rules.js +1155 -0
  81. package/dist/cli/utils/ensure-target.js +314 -0
  82. package/dist/cli/utils/project-type.js +140 -0
  83. package/dist/cli/utils/public-files.js +49 -0
  84. package/dist/cli/utils/safe-install.js +4 -0
  85. package/dist/cli/utils/spawn-shell.js +94 -0
  86. package/dist/cli/utils/stage-functions.js +338 -0
  87. package/dist/cli/utils/stage-local-packages.js +210 -0
  88. package/dist/cli/utils/target-checks.js +161 -0
  89. package/dist/cli/utils/target.js +24 -0
  90. package/dist/cli/utils/ui.js +292 -0
  91. package/dist/defaults/AGENTS.md +125 -0
  92. package/dist/defaults/CHANGELOG.md +15 -0
  93. package/dist/defaults/CLAUDE.md +1 -0
  94. package/dist/defaults/_.gitignore +65 -0
  95. package/dist/defaults/docs/README.md +17 -0
  96. package/dist/defaults/test/README.md +65 -0
  97. package/dist/defaults/test/_init.js +14 -0
  98. package/dist/defaults/test/helpers/connect-trap.js +72 -0
  99. package/dist/defaults/test/unit/registration.test.js +143 -0
  100. package/dist/defaults/test/unit/rules-posture.test.js +101 -0
  101. package/dist/defaults/test/unit/socket-free.test.js +43 -0
  102. package/dist/manager/events/auth/before-create.js +89 -0
  103. package/dist/manager/events/auth/before-signin.js +76 -0
  104. package/dist/manager/events/auth/on-create.js +112 -0
  105. package/dist/manager/events/auth/on-delete.js +141 -0
  106. package/dist/manager/events/auth/utils.js +93 -0
  107. package/dist/manager/events/cron/daily/blog-auto-publisher.js +287 -0
  108. package/dist/manager/events/cron/daily/data-requests.js +57 -0
  109. package/dist/manager/events/cron/daily/expire-paypal-cancellations.js +191 -0
  110. package/dist/manager/events/cron/daily/marketing-prune.js +486 -0
  111. package/dist/manager/events/cron/daily/reset-usage.js +188 -0
  112. package/dist/manager/events/cron/daily/trial-lapse-sweep.js +355 -0
  113. package/dist/manager/events/cron/daily.js +3 -0
  114. package/dist/manager/events/cron/frequent/abandoned-carts.js +204 -0
  115. package/dist/manager/events/cron/frequent/email-queue.js +71 -0
  116. package/dist/manager/events/cron/frequent/marketing-campaigns.js +463 -0
  117. package/dist/manager/events/cron/frequent/retry-failed-webhooks.js +70 -0
  118. package/dist/manager/events/cron/frequent.js +3 -0
  119. package/dist/manager/events/cron/runner.js +64 -0
  120. package/dist/manager/events/firestore/notifications/on-write.js +123 -0
  121. package/dist/manager/events/firestore/payments-disputes/on-write.js +256 -0
  122. package/dist/manager/events/firestore/payments-disputes/providers/stripe.js +218 -0
  123. package/dist/manager/events/firestore/payments-disputes/providers/test.js +231 -0
  124. package/dist/manager/events/firestore/payments-webhooks/analytics.js +687 -0
  125. package/dist/manager/events/firestore/payments-webhooks/on-write.js +1224 -0
  126. package/dist/manager/events/firestore/payments-webhooks/transitions/index.js +395 -0
  127. package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-completed.js +58 -0
  128. package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-failed.js +28 -0
  129. package/dist/manager/events/firestore/payments-webhooks/transitions/one-time/purchase-refunded.js +30 -0
  130. package/dist/manager/events/firestore/payments-webhooks/transitions/send-email.js +74 -0
  131. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/cancellation-removed.js +14 -0
  132. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/cancellation-requested.js +28 -0
  133. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/checkout-declined.js +18 -0
  134. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/new-subscription.js +67 -0
  135. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-failed.js +25 -0
  136. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-recovered.js +26 -0
  137. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/payment-refunded.js +34 -0
  138. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/plan-changed.js +42 -0
  139. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/subscription-cancelled.js +33 -0
  140. package/dist/manager/events/firestore/payments-webhooks/transitions/subscription/subscription-winback.js +17 -0
  141. package/dist/manager/functions/_legacy/actions/create-post-handler.js +188 -0
  142. package/dist/manager/functions/_legacy/actions/generate-uuid.js +63 -0
  143. package/dist/manager/functions/_legacy/actions/sign-up-handler.js +205 -0
  144. package/dist/manager/functions/_legacy/admin/create-post.js +207 -0
  145. package/dist/manager/functions/_legacy/admin/firestore-write.js +72 -0
  146. package/dist/manager/functions/_legacy/admin/get-stats.js +218 -0
  147. package/dist/manager/functions/_legacy/admin/query.js +198 -0
  148. package/dist/manager/functions/_legacy/admin/send-notification.js +206 -0
  149. package/dist/manager/functions/_legacy/template.js +33 -0
  150. package/dist/manager/functions/_legacy/test/authenticate.js +40 -0
  151. package/dist/manager/functions/_legacy/test/webhook.js +37 -0
  152. package/dist/manager/functions/wrappers/mailchimp/addToList.js +25 -0
  153. package/dist/manager/helpers/analytics.js +498 -0
  154. package/dist/manager/helpers/api-manager.js +313 -0
  155. package/dist/manager/helpers/backend-router.js +37 -0
  156. package/dist/manager/helpers/context/authenticate.js +215 -0
  157. package/dist/manager/helpers/context/client-info.js +91 -0
  158. package/dist/manager/helpers/context/index.js +228 -0
  159. package/dist/manager/helpers/context/logging.js +150 -0
  160. package/dist/manager/helpers/context/parse.js +160 -0
  161. package/dist/manager/helpers/context/respond.js +247 -0
  162. package/dist/manager/helpers/event-middleware.js +109 -0
  163. package/dist/manager/helpers/metadata.js +32 -0
  164. package/dist/manager/helpers/middleware.js +541 -0
  165. package/dist/manager/helpers/redact-secret.js +25 -0
  166. package/dist/manager/helpers/resolved-config.js +34 -0
  167. package/dist/manager/helpers/roles.js +69 -0
  168. package/dist/manager/helpers/safe-compare.js +31 -0
  169. package/dist/manager/helpers/schema-engine.js +314 -0
  170. package/dist/manager/helpers/schema-zod.js +203 -0
  171. package/dist/manager/helpers/settings.js +215 -0
  172. package/dist/manager/helpers/usage.js +541 -0
  173. package/dist/manager/helpers/user.js +57 -0
  174. package/dist/manager/helpers/utilities.js +565 -0
  175. package/dist/manager/index.js +1404 -0
  176. package/dist/manager/libraries/abandoned-cart-config.js +12 -0
  177. package/dist/manager/libraries/ai/index.js +321 -0
  178. package/dist/manager/libraries/ai/prompt.js +85 -0
  179. package/dist/manager/libraries/ai/providers/anthropic-format.js +297 -0
  180. package/dist/manager/libraries/ai/providers/anthropic.js +169 -0
  181. package/dist/manager/libraries/ai/providers/claude-code.js +183 -0
  182. package/dist/manager/libraries/ai/providers/openai.js +1272 -0
  183. package/dist/manager/libraries/ai/providers/test.js +257 -0
  184. package/dist/manager/libraries/ai/tokens.js +54 -0
  185. package/dist/manager/libraries/analytics/conversions.js +478 -0
  186. package/dist/manager/libraries/analytics/match-data.js +573 -0
  187. package/dist/manager/libraries/analytics/signup.js +120 -0
  188. package/dist/manager/libraries/auth-user.js +46 -0
  189. package/dist/manager/libraries/content/feed-parser.js +198 -0
  190. package/dist/manager/libraries/content/ghostii.js +191 -0
  191. package/dist/manager/libraries/content/source-resolver.js +629 -0
  192. package/dist/manager/libraries/email/constants.js +532 -0
  193. package/dist/manager/libraries/email/data/blocked-local-parts.json +55 -0
  194. package/dist/manager/libraries/email/data/blocked-local-patterns.js +14 -0
  195. package/dist/manager/libraries/email/data/corporate-domains.json +23 -0
  196. package/dist/manager/libraries/email/data/custom-disposable-domains.json +58 -0
  197. package/dist/manager/libraries/email/data/disposable-domains.json +8184 -0
  198. package/dist/manager/libraries/email/data/typo-domains.js +83 -0
  199. package/dist/manager/libraries/email/disposable-domains.js +92 -0
  200. package/dist/manager/libraries/email/generators/lib/filter.js +179 -0
  201. package/dist/manager/libraries/email/generators/lib/image-host.js +344 -0
  202. package/dist/manager/libraries/email/generators/lib/image-illustrator.js +154 -0
  203. package/dist/manager/libraries/email/generators/lib/markdown-renderer.js +294 -0
  204. package/dist/manager/libraries/email/generators/lib/mjml-template.js +120 -0
  205. package/dist/manager/libraries/email/generators/lib/structure.js +299 -0
  206. package/dist/manager/libraries/email/generators/lib/svg-illustrator.js +197 -0
  207. package/dist/manager/libraries/email/generators/lib/templates/base.js +229 -0
  208. package/dist/manager/libraries/email/generators/lib/templates/card.js +33 -0
  209. package/dist/manager/libraries/email/generators/lib/templates/classic-schema.js +59 -0
  210. package/dist/manager/libraries/email/generators/lib/templates/clean.js +82 -0
  211. package/dist/manager/libraries/email/generators/lib/templates/editorial/helpers.js +100 -0
  212. package/dist/manager/libraries/email/generators/lib/templates/editorial/index.js +318 -0
  213. package/dist/manager/libraries/email/generators/lib/templates/feedback.js +101 -0
  214. package/dist/manager/libraries/email/generators/lib/templates/field-report/helpers.js +138 -0
  215. package/dist/manager/libraries/email/generators/lib/templates/field-report/index.js +494 -0
  216. package/dist/manager/libraries/email/generators/lib/templates/index.js +47 -0
  217. package/dist/manager/libraries/email/generators/lib/templates/newsletter-shared.js +610 -0
  218. package/dist/manager/libraries/email/generators/lib/templates/order.js +461 -0
  219. package/dist/manager/libraries/email/generators/lib/templates/plain.js +83 -0
  220. package/dist/manager/libraries/email/generators/lib/templates/shared-campaign.js +64 -0
  221. package/dist/manager/libraries/email/generators/newsletter.js +899 -0
  222. package/dist/manager/libraries/email/index.js +146 -0
  223. package/dist/manager/libraries/email/marketing/index.js +754 -0
  224. package/dist/manager/libraries/email/prepare.js +365 -0
  225. package/dist/manager/libraries/email/providers/beehiiv.js +589 -0
  226. package/dist/manager/libraries/email/providers/sendgrid.js +845 -0
  227. package/dist/manager/libraries/email/transactional/index.js +510 -0
  228. package/dist/manager/libraries/email/utm.js +86 -0
  229. package/dist/manager/libraries/email/validation-provider-neverbounce.js +70 -0
  230. package/dist/manager/libraries/email/validation-provider-zerobounce.js +42 -0
  231. package/dist/manager/libraries/email/validation.js +255 -0
  232. package/dist/manager/libraries/env.js +198 -0
  233. package/dist/manager/libraries/infer-contact.js +101 -0
  234. package/dist/manager/libraries/load-provider.js +65 -0
  235. package/dist/manager/libraries/notification.js +256 -0
  236. package/dist/manager/libraries/openai.js +13 -0
  237. package/dist/manager/libraries/payment/discount-codes.js +204 -0
  238. package/dist/manager/libraries/payment/fetch-failure.js +56 -0
  239. package/dist/manager/libraries/payment/license.js +44 -0
  240. package/dist/manager/libraries/payment/order-id.js +57 -0
  241. package/dist/manager/libraries/payment/provider-errors.js +71 -0
  242. package/dist/manager/libraries/payment/providers/chargebee.js +817 -0
  243. package/dist/manager/libraries/payment/providers/coinbase.js +330 -0
  244. package/dist/manager/libraries/payment/providers/paypal.js +987 -0
  245. package/dist/manager/libraries/payment/providers/stripe.js +755 -0
  246. package/dist/manager/libraries/payment/providers/test.js +250 -0
  247. package/dist/manager/libraries/payment/refund-linkage.js +78 -0
  248. package/dist/manager/libraries/payment/refund-policy.js +113 -0
  249. package/dist/manager/libraries/payment/winback.js +55 -0
  250. package/dist/manager/libraries/prompts/infer-contact.md +78 -0
  251. package/dist/manager/libraries/rate-limits.js +17 -0
  252. package/dist/manager/libraries/recaptcha.js +58 -0
  253. package/dist/manager/libraries/user-doc.js +234 -0
  254. package/dist/manager/routes/admin/backup/post.js +106 -0
  255. package/dist/manager/routes/admin/cron/post.js +37 -0
  256. package/dist/manager/routes/admin/database/get.js +35 -0
  257. package/dist/manager/routes/admin/database/post.js +34 -0
  258. package/dist/manager/routes/admin/email/post.js +74 -0
  259. package/dist/manager/routes/admin/firestore/get.js +35 -0
  260. package/dist/manager/routes/admin/firestore/post.js +56 -0
  261. package/dist/manager/routes/admin/firestore/query/post.js +128 -0
  262. package/dist/manager/routes/admin/hook/post.js +104 -0
  263. package/dist/manager/routes/admin/infer-contact/post.js +35 -0
  264. package/dist/manager/routes/admin/notification/post.js +37 -0
  265. package/dist/manager/routes/admin/payment/post.js +56 -0
  266. package/dist/manager/routes/admin/post/deduplicate-image-alts.js +52 -0
  267. package/dist/manager/routes/admin/post/dispatch-deploy.js +36 -0
  268. package/dist/manager/routes/admin/post/post.js +487 -0
  269. package/dist/manager/routes/admin/post/put.js +146 -0
  270. package/dist/manager/routes/admin/post/templates/post.html +14 -0
  271. package/dist/manager/routes/admin/repo/content/post.js +106 -0
  272. package/dist/manager/routes/admin/stats/get.js +222 -0
  273. package/dist/manager/routes/admin/users/disable/post.js +50 -0
  274. package/dist/manager/routes/admin/users/list/get.js +110 -0
  275. package/dist/manager/routes/admin/users/sync/post.js +119 -0
  276. package/dist/manager/routes/brand/get.js +26 -0
  277. package/dist/manager/routes/content/post/get.js +110 -0
  278. package/dist/manager/routes/general/email/post.js +89 -0
  279. package/dist/manager/routes/general/email/templates/general/download-app-link.js +42 -0
  280. package/dist/manager/routes/general/uuid/post.js +31 -0
  281. package/dist/manager/routes/handler/post/post.js +142 -0
  282. package/dist/manager/routes/health/get.js +43 -0
  283. package/dist/manager/routes/index.js +11 -0
  284. package/dist/manager/routes/marketing/campaign/delete.js +45 -0
  285. package/dist/manager/routes/marketing/campaign/get.js +69 -0
  286. package/dist/manager/routes/marketing/campaign/post.js +124 -0
  287. package/dist/manager/routes/marketing/campaign/put.js +86 -0
  288. package/dist/manager/routes/marketing/campaign/utils.js +59 -0
  289. package/dist/manager/routes/marketing/contact/delete.js +100 -0
  290. package/dist/manager/routes/marketing/contact/post.js +149 -0
  291. package/dist/manager/routes/marketing/contact/put.js +37 -0
  292. package/dist/manager/routes/marketing/email-preferences/post.js +275 -0
  293. package/dist/manager/routes/marketing/webhook/forward/post.js +170 -0
  294. package/dist/manager/routes/marketing/webhook/post.js +123 -0
  295. package/dist/manager/routes/marketing/webhook/providers/beehiiv.js +197 -0
  296. package/dist/manager/routes/marketing/webhook/providers/sendgrid.js +194 -0
  297. package/dist/manager/routes/payments/cancel/_is-trialing.js +40 -0
  298. package/dist/manager/routes/payments/cancel/post.js +149 -0
  299. package/dist/manager/routes/payments/cancel/providers/chargebee.js +41 -0
  300. package/dist/manager/routes/payments/cancel/providers/paypal.js +56 -0
  301. package/dist/manager/routes/payments/cancel/providers/stripe.js +33 -0
  302. package/dist/manager/routes/payments/cancel/providers/test.js +118 -0
  303. package/dist/manager/routes/payments/discount/get.js +25 -0
  304. package/dist/manager/routes/payments/dispute-alert/post.js +117 -0
  305. package/dist/manager/routes/payments/dispute-alert/providers/chargeblast.js +55 -0
  306. package/dist/manager/routes/payments/intent/post.js +341 -0
  307. package/dist/manager/routes/payments/intent/providers/chargebee.js +197 -0
  308. package/dist/manager/routes/payments/intent/providers/coinbase.js +94 -0
  309. package/dist/manager/routes/payments/intent/providers/paypal.js +244 -0
  310. package/dist/manager/routes/payments/intent/providers/stripe.js +141 -0
  311. package/dist/manager/routes/payments/intent/providers/test.js +341 -0
  312. package/dist/manager/routes/payments/plan/post.js +147 -0
  313. package/dist/manager/routes/payments/plan/providers/chargebee.js +62 -0
  314. package/dist/manager/routes/payments/plan/providers/paypal.js +62 -0
  315. package/dist/manager/routes/payments/plan/providers/stripe.js +59 -0
  316. package/dist/manager/routes/payments/plan/providers/test.js +113 -0
  317. package/dist/manager/routes/payments/portal/post.js +113 -0
  318. package/dist/manager/routes/payments/portal/providers/chargebee.js +62 -0
  319. package/dist/manager/routes/payments/portal/providers/paypal.js +25 -0
  320. package/dist/manager/routes/payments/portal/providers/stripe.js +62 -0
  321. package/dist/manager/routes/payments/portal/providers/test.js +18 -0
  322. package/dist/manager/routes/payments/refund/post.js +207 -0
  323. package/dist/manager/routes/payments/refund/providers/chargebee.js +147 -0
  324. package/dist/manager/routes/payments/refund/providers/coinbase.js +40 -0
  325. package/dist/manager/routes/payments/refund/providers/paypal.js +239 -0
  326. package/dist/manager/routes/payments/refund/providers/stripe.js +160 -0
  327. package/dist/manager/routes/payments/refund/providers/test.js +204 -0
  328. package/dist/manager/routes/payments/trial-eligibility/get.js +29 -0
  329. package/dist/manager/routes/payments/uncancel/post.js +106 -0
  330. package/dist/manager/routes/payments/uncancel/providers/chargebee.js +30 -0
  331. package/dist/manager/routes/payments/uncancel/providers/paypal.js +25 -0
  332. package/dist/manager/routes/payments/uncancel/providers/stripe.js +27 -0
  333. package/dist/manager/routes/payments/uncancel/providers/test.js +114 -0
  334. package/dist/manager/routes/payments/webhook/post.js +157 -0
  335. package/dist/manager/routes/payments/webhook/providers/chargebee.js +218 -0
  336. package/dist/manager/routes/payments/webhook/providers/coinbase.js +91 -0
  337. package/dist/manager/routes/payments/webhook/providers/paypal.js +214 -0
  338. package/dist/manager/routes/payments/webhook/providers/stripe.js +182 -0
  339. package/dist/manager/routes/payments/webhook/providers/test.js +18 -0
  340. package/dist/manager/routes/payments/winback/post.js +276 -0
  341. package/dist/manager/routes/payments/winback/providers/chargebee.js +24 -0
  342. package/dist/manager/routes/payments/winback/providers/paypal.js +22 -0
  343. package/dist/manager/routes/payments/winback/providers/stripe.js +41 -0
  344. package/dist/manager/routes/payments/winback/providers/test.js +42 -0
  345. package/dist/manager/routes/restart/index.js +37 -0
  346. package/dist/manager/routes/special/electron-client/post.js +59 -0
  347. package/dist/manager/routes/test/authenticate/get.js +11 -0
  348. package/dist/manager/routes/test/health/get.js +8 -0
  349. package/dist/manager/routes/test/index.js +16 -0
  350. package/dist/manager/routes/test/lab/post.js +17 -0
  351. package/dist/manager/routes/test/redirect/get.js +27 -0
  352. package/dist/manager/routes/test/reset-account/post.js +79 -0
  353. package/dist/manager/routes/test/roster/get.js +40 -0
  354. package/dist/manager/routes/test/schema/post.js +20 -0
  355. package/dist/manager/routes/test/usage/post.js +44 -0
  356. package/dist/manager/routes/test/webhook/post.js +21 -0
  357. package/dist/manager/routes/user/api-keys/post.js +51 -0
  358. package/dist/manager/routes/user/connections/_context.js +194 -0
  359. package/dist/manager/routes/user/connections/_grant.js +247 -0
  360. package/dist/manager/routes/user/connections/_lease.js +184 -0
  361. package/dist/manager/routes/user/connections/_providers.js +191 -0
  362. package/dist/manager/routes/user/connections/_state.js +117 -0
  363. package/dist/manager/routes/user/connections/delete.js +55 -0
  364. package/dist/manager/routes/user/connections/get.js +167 -0
  365. package/dist/manager/routes/user/connections/post.js +370 -0
  366. package/dist/manager/routes/user/connections/providers/discord.js +41 -0
  367. package/dist/manager/routes/user/connections/providers/google.js +55 -0
  368. package/dist/manager/routes/user/connections/providers/kick.js +68 -0
  369. package/dist/manager/routes/user/connections/providers/spotify.js +40 -0
  370. package/dist/manager/routes/user/connections/providers/twitch.js +52 -0
  371. package/dist/manager/routes/user/data-request/delete.js +84 -0
  372. package/dist/manager/routes/user/data-request/get.js +229 -0
  373. package/dist/manager/routes/user/data-request/post.js +136 -0
  374. package/dist/manager/routes/user/delete.js +149 -0
  375. package/dist/manager/routes/user/feedback/post.js +81 -0
  376. package/dist/manager/routes/user/get.js +14 -0
  377. package/dist/manager/routes/user/orders/get.js +95 -0
  378. package/dist/manager/routes/user/sessions/delete.js +95 -0
  379. package/dist/manager/routes/user/sessions/get.js +40 -0
  380. package/dist/manager/routes/user/settings/validate/post.js +86 -0
  381. package/dist/manager/routes/user/signup/post.js +662 -0
  382. package/dist/manager/routes/user/subscription/get.js +77 -0
  383. package/dist/manager/routes/user/token/post.js +30 -0
  384. package/dist/manager/routes/verts/delete.js +43 -0
  385. package/dist/manager/routes/verts/get.js +49 -0
  386. package/dist/manager/routes/verts/post.js +61 -0
  387. package/dist/manager/routes/verts/put.js +75 -0
  388. package/dist/manager/routes/verts/redirect/get.js +36 -0
  389. package/dist/manager/routes/verts/serve/get.js +67 -0
  390. package/dist/manager/routes/verts/utils.js +436 -0
  391. package/dist/manager/schemas/admin/backup/post.js +8 -0
  392. package/dist/manager/schemas/admin/cron/post.js +8 -0
  393. package/dist/manager/schemas/admin/database/get.js +5 -0
  394. package/dist/manager/schemas/admin/database/post.js +6 -0
  395. package/dist/manager/schemas/admin/email/post.js +31 -0
  396. package/dist/manager/schemas/admin/firestore/get.js +5 -0
  397. package/dist/manager/schemas/admin/firestore/post.js +8 -0
  398. package/dist/manager/schemas/admin/firestore/query/post.js +5 -0
  399. package/dist/manager/schemas/admin/hook/post.js +8 -0
  400. package/dist/manager/schemas/admin/infer-contact/post.js +6 -0
  401. package/dist/manager/schemas/admin/notification/post.js +23 -0
  402. package/dist/manager/schemas/admin/payment/post.js +8 -0
  403. package/dist/manager/schemas/admin/post/post.js +23 -0
  404. package/dist/manager/schemas/admin/post/put.js +13 -0
  405. package/dist/manager/schemas/admin/repo/content/post.js +10 -0
  406. package/dist/manager/schemas/admin/stats/get.js +5 -0
  407. package/dist/manager/schemas/admin/users/disable/post.js +9 -0
  408. package/dist/manager/schemas/admin/users/list/get.js +10 -0
  409. package/dist/manager/schemas/admin/users/sync/post.js +7 -0
  410. package/dist/manager/schemas/brand/get.js +3 -0
  411. package/dist/manager/schemas/content/post/get.js +8 -0
  412. package/dist/manager/schemas/general/email/post.js +10 -0
  413. package/dist/manager/schemas/general/uuid/post.js +9 -0
  414. package/dist/manager/schemas/handler/post/post.js +13 -0
  415. package/dist/manager/schemas/health/get.js +3 -0
  416. package/dist/manager/schemas/marketing/campaign/delete.js +8 -0
  417. package/dist/manager/schemas/marketing/campaign/get.js +13 -0
  418. package/dist/manager/schemas/marketing/campaign/post.js +43 -0
  419. package/dist/manager/schemas/marketing/campaign/put.js +37 -0
  420. package/dist/manager/schemas/marketing/contact/delete.js +8 -0
  421. package/dist/manager/schemas/marketing/contact/post.js +14 -0
  422. package/dist/manager/schemas/marketing/contact/put.js +8 -0
  423. package/dist/manager/schemas/marketing/email-preferences/post.js +15 -0
  424. package/dist/manager/schemas/marketing/webhook/forward/post.js +8 -0
  425. package/dist/manager/schemas/marketing/webhook/post.js +7 -0
  426. package/dist/manager/schemas/payments/cancel/post.js +15 -0
  427. package/dist/manager/schemas/payments/discount/get.js +8 -0
  428. package/dist/manager/schemas/payments/dispute-alert/post.js +6 -0
  429. package/dist/manager/schemas/payments/intent/post.js +28 -0
  430. package/dist/manager/schemas/payments/plan/post.js +11 -0
  431. package/dist/manager/schemas/payments/portal/post.js +12 -0
  432. package/dist/manager/schemas/payments/refund/post.js +14 -0
  433. package/dist/manager/schemas/payments/trial-eligibility/get.js +7 -0
  434. package/dist/manager/schemas/payments/uncancel/post.js +9 -0
  435. package/dist/manager/schemas/payments/webhook/post.js +6 -0
  436. package/dist/manager/schemas/payments/winback/post.js +12 -0
  437. package/dist/manager/schemas/restart/index.js +5 -0
  438. package/dist/manager/schemas/special/electron-client/post.js +11 -0
  439. package/dist/manager/schemas/test/authenticate/get.js +3 -0
  440. package/dist/manager/schemas/test/health/get.js +3 -0
  441. package/dist/manager/schemas/test/index.js +5 -0
  442. package/dist/manager/schemas/test/lab/post.js +3 -0
  443. package/dist/manager/schemas/test/redirect/get.js +7 -0
  444. package/dist/manager/schemas/test/reset-account/post.js +3 -0
  445. package/dist/manager/schemas/test/roster/get.js +3 -0
  446. package/dist/manager/schemas/test/schema/post.js +70 -0
  447. package/dist/manager/schemas/test/usage/post.js +8 -0
  448. package/dist/manager/schemas/test/webhook/post.js +7 -0
  449. package/dist/manager/schemas/user/api-keys/post.js +6 -0
  450. package/dist/manager/schemas/user/connections/delete.js +8 -0
  451. package/dist/manager/schemas/user/connections/get.js +21 -0
  452. package/dist/manager/schemas/user/connections/post.js +12 -0
  453. package/dist/manager/schemas/user/data-request/delete.js +3 -0
  454. package/dist/manager/schemas/user/data-request/get.js +5 -0
  455. package/dist/manager/schemas/user/data-request/post.js +6 -0
  456. package/dist/manager/schemas/user/delete.js +6 -0
  457. package/dist/manager/schemas/user/feedback/post.js +8 -0
  458. package/dist/manager/schemas/user/get.js +3 -0
  459. package/dist/manager/schemas/user/orders/get.js +6 -0
  460. package/dist/manager/schemas/user/sessions/delete.js +6 -0
  461. package/dist/manager/schemas/user/sessions/get.js +6 -0
  462. package/dist/manager/schemas/user/settings/validate/post.js +8 -0
  463. package/dist/manager/schemas/user/signup/post.js +27 -0
  464. package/dist/manager/schemas/user/subscription/get.js +5 -0
  465. package/dist/manager/schemas/user/token/post.js +5 -0
  466. package/dist/manager/schemas/verts/delete.js +8 -0
  467. package/dist/manager/schemas/verts/get.js +9 -0
  468. package/dist/manager/schemas/verts/post.js +23 -0
  469. package/dist/manager/schemas/verts/put.js +20 -0
  470. package/dist/manager/schemas/verts/redirect/get.js +10 -0
  471. package/dist/manager/schemas/verts/serve/get.js +14 -0
  472. package/dist/manager/server-manager.js +69 -0
  473. package/dist/mcp/client.js +98 -0
  474. package/dist/mcp/handler.js +521 -0
  475. package/dist/mcp/index.js +157 -0
  476. package/dist/mcp/tools.js +555 -0
  477. package/dist/mcp/utils.js +108 -0
  478. package/dist/omega-bin.js +6 -0
  479. package/dist/require.js +3 -0
  480. package/dist/test/fixtures/firebase-project/.firebaserc +5 -0
  481. package/dist/test/fixtures/firebase-project/config/omega.json5 +131 -0
  482. package/dist/test/fixtures/firebase-project/database.rules.json +6 -0
  483. package/dist/test/fixtures/firebase-project/firebase.json +49 -0
  484. package/dist/test/fixtures/firebase-project/firestore.indexes.json +4 -0
  485. package/dist/test/fixtures/firebase-project/package.json +14 -0
  486. package/dist/test/fixtures/firebase-project/src/index.js +11 -0
  487. package/dist/test/fixtures/firebase-project/storage.rules +8 -0
  488. package/dist/test/parse-audit.js +25 -0
  489. package/dist/test/run-tests.js +90 -0
  490. package/dist/test/runner.js +982 -0
  491. package/dist/test/seed.js +242 -0
  492. package/dist/test/test-accounts.js +2660 -0
  493. package/dist/test/utils/assertions.js +189 -0
  494. package/dist/test/utils/email-capture.js +252 -0
  495. package/dist/test/utils/extended-mode-warning.js +11 -0
  496. package/dist/test/utils/firestore-rules-client.js +277 -0
  497. package/dist/test/utils/http-client.js +228 -0
  498. package/dist/test/utils/test-mode-file.js +192 -0
  499. package/dist/utils/merge-line-files.js +6 -0
  500. package/dist/utils/scaffold-defaults.js +71 -0
  501. package/dist/utils/test-lanes.js +62 -0
  502. package/dist/vendor/account/engine.js +182 -0
  503. package/dist/vendor/account/features.js +220 -0
  504. package/dist/vendor/account/index.js +53 -0
  505. package/dist/vendor/account/schema.js +272 -0
  506. package/dist/vendor/account/subscription.js +38 -0
  507. package/dist/vendor/analytics/adapters/ga4.js +26 -0
  508. package/dist/vendor/analytics/adapters/meta.js +26 -0
  509. package/dist/vendor/analytics/adapters/resolve.js +130 -0
  510. package/dist/vendor/analytics/adapters/tiktok.js +27 -0
  511. package/dist/vendor/analytics/catalog.js +908 -0
  512. package/dist/vendor/analytics/identity.js +136 -0
  513. package/dist/vendor/config/company.js +31 -0
  514. package/dist/vendor/config/defaults.js +173 -0
  515. package/dist/vendor/config/demo.js +18 -0
  516. package/dist/vendor/config/desktop-artifacts.js +110 -0
  517. package/dist/vendor/config/edit.js +769 -0
  518. package/dist/vendor/config/env-delivery.js +145 -0
  519. package/dist/vendor/config/env-rules.js +93 -0
  520. package/dist/vendor/config/env-schema.js +1078 -0
  521. package/dist/vendor/config/env.js +445 -0
  522. package/dist/vendor/config/hooks.js +97 -0
  523. package/dist/vendor/config/index.js +237 -0
  524. package/dist/vendor/config/instances.js +208 -0
  525. package/dist/vendor/config/load.js +490 -0
  526. package/dist/vendor/config/merge.js +68 -0
  527. package/dist/vendor/config/order.js +139 -0
  528. package/dist/vendor/config/ports.js +374 -0
  529. package/dist/vendor/config/providers.js +32 -0
  530. package/dist/vendor/config/repo.js +142 -0
  531. package/dist/vendor/config/retired-keys.js +430 -0
  532. package/dist/vendor/config/schema.js +1610 -0
  533. package/dist/vendor/config/secrets.js +50 -0
  534. package/dist/vendor/config/seed.js +34 -0
  535. package/dist/vendor/config/site-global.js +205 -0
  536. package/dist/vendor/config/validate.js +554 -0
  537. package/dist/vendor/config/winback.js +61 -0
  538. package/dist/vendor/devkit/attach-log-file.js +262 -0
  539. package/dist/vendor/devkit/bare-requires.js +153 -0
  540. package/dist/vendor/devkit/command-path.js +46 -0
  541. package/dist/vendor/devkit/defaults-engine.js +372 -0
  542. package/dist/vendor/devkit/deploy-record.js +180 -0
  543. package/dist/vendor/devkit/deploy.js +286 -0
  544. package/dist/vendor/devkit/license.js +155 -0
  545. package/dist/vendor/devkit/local-https.js +360 -0
  546. package/dist/vendor/devkit/local.js +1905 -0
  547. package/dist/vendor/devkit/logger.js +128 -0
  548. package/dist/vendor/devkit/merge-line-files.js +296 -0
  549. package/dist/vendor/devkit/npm-registry.js +52 -0
  550. package/dist/vendor/devkit/omega-bin.js +345 -0
  551. package/dist/vendor/devkit/parse-audit.js +74 -0
  552. package/dist/vendor/devkit/safe-install.js +18 -0
  553. package/dist/vendor/devkit/scaffold-guard.js +96 -0
  554. package/dist/vendor/devkit/stop-signals.js +28 -0
  555. package/dist/vendor/devkit/test/assert.js +120 -0
  556. package/dist/vendor/devkit/test/define-cases.js +104 -0
  557. package/dist/vendor/devkit/test/runner-core.js +554 -0
  558. package/dist/vendor/devkit/test/scope.js +162 -0
  559. package/dist/vendor/devkit/update.js +569 -0
  560. package/dist/vendor/monitoring/core.js +180 -0
  561. package/dist/vendor/monitoring/env.js +49 -0
  562. package/dist/vendor/monitoring/logger.js +39 -0
  563. package/dist/vendor/monitoring/node.js +72 -0
  564. package/docs/admin-post-route.md +57 -0
  565. package/docs/ai-library.md +171 -0
  566. package/docs/architecture.md +62 -0
  567. package/docs/audit.md +70 -0
  568. package/docs/auth-hooks.md +74 -0
  569. package/docs/build-system.md +65 -0
  570. package/docs/cdp-debugging.md +29 -0
  571. package/docs/cli-firestore-auth.md +85 -0
  572. package/docs/cli-logs.md +67 -0
  573. package/docs/cli-output.md +146 -0
  574. package/docs/code-patterns.md +77 -0
  575. package/docs/common-mistakes.md +12 -0
  576. package/docs/common-operations.md +60 -0
  577. package/docs/connections.md +212 -0
  578. package/docs/consent.md +362 -0
  579. package/docs/directory-structure.md +150 -0
  580. package/docs/email-system.md +459 -0
  581. package/docs/environment-detection.md +93 -0
  582. package/docs/file-naming.md +10 -0
  583. package/docs/firestore.md +134 -0
  584. package/docs/ghostii.md +240 -0
  585. package/docs/index.md +299 -0
  586. package/docs/key-files.md +37 -0
  587. package/docs/logging.md +60 -0
  588. package/docs/marketing-campaigns.md +407 -0
  589. package/docs/marketing-fields.md +25 -0
  590. package/docs/mcp.md +222 -0
  591. package/docs/migration.md +129 -0
  592. package/docs/payment-system.md +856 -0
  593. package/docs/paypal-sandbox-qa.md +105 -0
  594. package/docs/response-headers.md +9 -0
  595. package/docs/routes.md +211 -0
  596. package/docs/sanitization.md +71 -0
  597. package/docs/schemas.md +163 -0
  598. package/docs/shared/agent-docs.md +89 -0
  599. package/docs/shared/analytics.md +612 -0
  600. package/docs/shared/brands.md +51 -0
  601. package/docs/shared/breaking-changes.md +497 -0
  602. package/docs/shared/config.md +1387 -0
  603. package/docs/shared/deploys.md +215 -0
  604. package/docs/shared/icons.md +201 -0
  605. package/docs/shared/local-dev.md +147 -0
  606. package/docs/shared/logging.md +202 -0
  607. package/docs/shared/monitoring.md +153 -0
  608. package/docs/shared/publishing.md +183 -0
  609. package/docs/shared/rulings.md +34 -0
  610. package/docs/shared/testing.md +147 -0
  611. package/docs/shared/theming.md +604 -0
  612. package/docs/shared/translation.md +291 -0
  613. package/docs/shared/updates.md +61 -0
  614. package/docs/stripe-webhook-forwarding.md +20 -0
  615. package/docs/test-boot-layer.md +67 -0
  616. package/docs/test-framework.md +583 -0
  617. package/docs/usage-rate-limiting.md +121 -0
  618. package/docs/verts.md +29 -0
  619. package/package.json +143 -0
  620. package/templates/config/omega.json5 +322 -0
  621. package/templates/database.rules.json +82 -0
  622. package/templates/firebase.json +67 -0
  623. package/templates/firestore.framework.rules +191 -0
  624. package/templates/firestore.indexes.json +4 -0
  625. package/templates/firestore.rules +59 -0
  626. package/templates/index.js +12 -0
  627. package/templates/public/404.html +26 -0
  628. package/templates/public/index.html +24 -0
  629. package/templates/remoteconfig.template.json +1 -0
  630. package/templates/storage-lifecycle-config-1-day.json +9 -0
  631. package/templates/storage-lifecycle-config-30-days.json +9 -0
  632. package/templates/storage.rules +11 -0
  633. package/test/_init/accounts-validation.js +58 -0
  634. package/test/ai/tools-live.test.js +171 -0
  635. package/test/analytics/conversion-delivery.test.js +1013 -0
  636. package/test/analytics/match-normalization.test.js +238 -0
  637. package/test/analytics/signup-conversion.test.js +135 -0
  638. package/test/boot/cli-dispatch.test.js +142 -0
  639. package/test/boot/defaults-scaffold.test.js +248 -0
  640. package/test/boot/deploy-staging.test.js +446 -0
  641. package/test/boot/disposable-domains.test.js +154 -0
  642. package/test/boot/emulator-boots.test.js +39 -0
  643. package/test/boot/emulator-port-preflight.test.js +126 -0
  644. package/test/boot/emulator-ready-timeout.test.js +46 -0
  645. package/test/boot/project-id-refresh.test.js +73 -0
  646. package/test/boot/suite-portability.test.js +278 -0
  647. package/test/boot/update-command.test.js +63 -0
  648. package/test/cli/cross-platform.test.js +168 -0
  649. package/test/cli/custom-project-type.test.js +426 -0
  650. package/test/cli/deploy-license-env.test.js +100 -0
  651. package/test/cli/emulator-adoption.test.js +254 -0
  652. package/test/cli/emulator-orphans.test.js +658 -0
  653. package/test/cli/emulator-ownership.test.js +177 -0
  654. package/test/cli/emulator-port-retry.test.js +395 -0
  655. package/test/cli/emulator-shutdown.test.js +788 -0
  656. package/test/cli/emulator-stale-reap.test.js +574 -0
  657. package/test/cli/ensure-target.test.js +325 -0
  658. package/test/cli/flags.test.js +64 -0
  659. package/test/cli/https-trust.test.js +38 -0
  660. package/test/cli/install-update-dispatch.test.js +47 -0
  661. package/test/cli/lane-environments.test.js +159 -0
  662. package/test/cli/marketing-campaigns-seeded.test.js +194 -0
  663. package/test/cli/migrate-bare-requires.test.js +128 -0
  664. package/test/cli/migrate-markers.test.js +530 -0
  665. package/test/cli/required-indexes.test.js +64 -0
  666. package/test/cli/rules-compile.test.js +622 -0
  667. package/test/cli/rules-migration-deferral.test.js +259 -0
  668. package/test/cli/rules-version.test.js +104 -0
  669. package/test/cli/setup-load-files.test.js +66 -0
  670. package/test/cli/setup-offline-mode.test.js +276 -0
  671. package/test/cli/setup-retired.test.js +64 -0
  672. package/test/cli/setup-shared-project-indexes.test.js +262 -0
  673. package/test/cli/setup-tests-requires.test.js +45 -0
  674. package/test/cli/stage-env-compose.test.js +316 -0
  675. package/test/cli/stripe-live-lane.test.js +303 -0
  676. package/test/cli/target.test.js +182 -0
  677. package/test/cli/templates.test.js +66 -0
  678. package/test/cli/test-runner-env.test.js +115 -0
  679. package/test/cli/test-stack-shutdown.test.js +275 -0
  680. package/test/cli/test-target-no-match.test.js +128 -0
  681. package/test/cli/verb-logs.test.js +118 -0
  682. package/test/cli/version-dispatch.test.js +39 -0
  683. package/test/content/blog-generate.test.js +164 -0
  684. package/test/email/campaign-config-fault.test.js +376 -0
  685. package/test/email/campaign-cron-pipeline.test.js +536 -0
  686. package/test/email/campaign-send.test.js +47 -0
  687. package/test/email/consent-lifecycle.test.js +258 -0
  688. package/test/email/content-html-policy.test.js +270 -0
  689. package/test/email/feedback-and-plain-send.test.js +54 -0
  690. package/test/email/fixtures/clean.json +30 -0
  691. package/test/email/fixtures/editorial.json +30 -0
  692. package/test/email/fixtures/field-report.json +53 -0
  693. package/test/email/identity.test.js +321 -0
  694. package/test/email/marketing/consent-gate.test.js +265 -0
  695. package/test/email/marketing/custom-fields-catalog.test.js +259 -0
  696. package/test/email/marketing/prune-per-provider.test.js +762 -0
  697. package/test/email/marketing/remove-log-privacy.test.js +225 -0
  698. package/test/email/marketing-lifecycle.test.js +139 -0
  699. package/test/email/newsletter-generate.test.js +853 -0
  700. package/test/email/newsletter-svg-tokens.test.js +51 -0
  701. package/test/email/newsletter-templates.test.js +492 -0
  702. package/test/email/order-one-time-cta.test.js +97 -0
  703. package/test/email/render-content.test.js +248 -0
  704. package/test/email/safe-url.test.js +0 -0
  705. package/test/email/sanitize-images.test.js +66 -0
  706. package/test/email/send-log-privacy.test.js +107 -0
  707. package/test/email/templates.test.js +288 -0
  708. package/test/email/testing-capture.test.js +260 -0
  709. package/test/email/transactional-send.test.js +36 -0
  710. package/test/email/transactional.test.js +562 -0
  711. package/test/email/unsubscribe-groups.test.js +155 -0
  712. package/test/email/unsubscribe-key.test.js +91 -0
  713. package/test/email/validation-cases.test.js +152 -0
  714. package/test/email/validation.test.js +746 -0
  715. package/test/events/auth-delete-conversion.test.js +138 -0
  716. package/test/events/auth-delete-race.test.js +211 -0
  717. package/test/events/auth-on-create-log.test.js +164 -0
  718. package/test/events/auth-signup-conversion.test.js +141 -0
  719. package/test/events/auth-signup-limit.test.js +142 -0
  720. package/test/events/auth-trigger-log-privacy.test.js +256 -0
  721. package/test/events/cron-job-doc-shape.test.js +101 -0
  722. package/test/events/cron-reset-usage.test.js +116 -0
  723. package/test/events/notification-conversion.test.js +182 -0
  724. package/test/events/payments/_webhook-harness.js +307 -0
  725. package/test/events/payments/abandoned-cart-activity.test.js +135 -0
  726. package/test/events/payments/analytics-payment-events.test.js +945 -0
  727. package/test/events/payments/dispute-email-status.test.js +55 -0
  728. package/test/events/payments/journey-payments-abandoned.test.js +126 -0
  729. package/test/events/payments/journey-payments-cancel-endpoint.test.js +102 -0
  730. package/test/events/payments/journey-payments-cancel-no-order.test.js +76 -0
  731. package/test/events/payments/journey-payments-cancel.test.js +184 -0
  732. package/test/events/payments/journey-payments-decline.test.js +225 -0
  733. package/test/events/payments/journey-payments-discount.test.js +91 -0
  734. package/test/events/payments/journey-payments-dispute.test.js +190 -0
  735. package/test/events/payments/journey-payments-failure.test.js +148 -0
  736. package/test/events/payments/journey-payments-legacy-product.test.js +151 -0
  737. package/test/events/payments/journey-payments-one-time-decline.test.js +127 -0
  738. package/test/events/payments/journey-payments-one-time-failure.test.js +113 -0
  739. package/test/events/payments/journey-payments-one-time-refund.test.js +186 -0
  740. package/test/events/payments/journey-payments-one-time.test.js +175 -0
  741. package/test/events/payments/journey-payments-plan-change.test.js +146 -0
  742. package/test/events/payments/journey-payments-plan-switch-trial.test.js +161 -0
  743. package/test/events/payments/journey-payments-plan-switch.test.js +182 -0
  744. package/test/events/payments/journey-payments-refund-no-order.test.js +76 -0
  745. package/test/events/payments/journey-payments-refund-webhook.test.js +225 -0
  746. package/test/events/payments/journey-payments-suspend.test.js +183 -0
  747. package/test/events/payments/journey-payments-trial-cancel.test.js +132 -0
  748. package/test/events/payments/journey-payments-trial.test.js +173 -0
  749. package/test/events/payments/journey-payments-uid-resolution.test.js +134 -0
  750. package/test/events/payments/journey-payments-uncancel.test.js +157 -0
  751. package/test/events/payments/journey-payments-upgrade.test.js +137 -0
  752. package/test/events/payments/journey-payments-winback-decline.test.js +171 -0
  753. package/test/events/payments/journey-payments-winback.test.js +195 -0
  754. package/test/events/payments/paypal-expiry-cron.test.js +166 -0
  755. package/test/events/payments/purchase-failed-handler.test.js +80 -0
  756. package/test/events/payments/purchase-refunded-handler.test.js +108 -0
  757. package/test/events/payments/test-processor-doc-shape.test.js +162 -0
  758. package/test/events/payments/transition-order-emails.test.js +185 -0
  759. package/test/events/payments/transition-promo-lines.test.js +192 -0
  760. package/test/events/payments/transitions-detect.test.js +767 -0
  761. package/test/events/payments/trial-lapse-sweep-staleness.test.js +173 -0
  762. package/test/events/payments/trial-lapse-sweep.test.js +359 -0
  763. package/test/events/payments/webhook-atomic-writes.test.js +69 -0
  764. package/test/events/payments/webhook-chargebee-unreachable.test.js +106 -0
  765. package/test/events/payments/webhook-discount-clear.test.js +189 -0
  766. package/test/events/payments/webhook-failure-intent.test.js +93 -0
  767. package/test/events/payments/webhook-hosted-page-uid-trust.test.js +212 -0
  768. package/test/events/payments/webhook-ordering.test.js +326 -0
  769. package/test/events/payments/webhook-provider-lookup-trust.test.js +206 -0
  770. package/test/events/payments/webhook-refund-amount-trust.test.js +223 -0
  771. package/test/events/payments/webhook-refund-envelope-shape.test.js +148 -0
  772. package/test/events/payments/webhook-refund-linkage-trust.test.js +404 -0
  773. package/test/events/payments/webhook-refund-transaction-fallback.test.js +184 -0
  774. package/test/events/payments/webhook-refund-without-order.test.js +170 -0
  775. package/test/events/payments/webhook-refusal-reporting.test.js +194 -0
  776. package/test/events/payments/webhook-refused-intent.test.js +142 -0
  777. package/test/events/payments/webhook-retry-sweep.test.js +252 -0
  778. package/test/events/payments/webhook-transition-claim.test.js +389 -0
  779. package/test/events/payments/webhook-uid-trust.test.js +203 -0
  780. package/test/events/payments/webhook-user-without-auth.test.js +161 -0
  781. package/test/fixtures/chargebee/invoice-one-time.json +27 -0
  782. package/test/fixtures/chargebee/subscription-active.json +44 -0
  783. package/test/fixtures/chargebee/subscription-cancelled.json +42 -0
  784. package/test/fixtures/chargebee/subscription-in-trial.json +41 -0
  785. package/test/fixtures/chargebee/subscription-legacy-plan.json +41 -0
  786. package/test/fixtures/chargebee/subscription-non-renewing.json +41 -0
  787. package/test/fixtures/chargebee/subscription-paused.json +42 -0
  788. package/test/fixtures/chargebee/webhook-payment-failed.json +51 -0
  789. package/test/fixtures/chargebee/webhook-subscription-created.json +47 -0
  790. package/test/fixtures/coinbase/charge-confirmed.json +50 -0
  791. package/test/fixtures/coinbase/charge-failed.json +34 -0
  792. package/test/fixtures/coinbase/charge-pending.json +34 -0
  793. package/test/fixtures/migrate/ported-route.js.txt +11 -0
  794. package/test/fixtures/paypal/capture-completed.json +51 -0
  795. package/test/fixtures/paypal/capture-refunded.json +43 -0
  796. package/test/fixtures/paypal/order-approved.json +62 -0
  797. package/test/fixtures/paypal/order-completed.json +110 -0
  798. package/test/fixtures/paypal/sale-refunded.json +38 -0
  799. package/test/fixtures/paypal/subscription-active.json +76 -0
  800. package/test/fixtures/paypal/subscription-cancelled.json +50 -0
  801. package/test/fixtures/paypal/subscription-suspended.json +65 -0
  802. package/test/fixtures/stripe/checkout-session-completed.json +130 -0
  803. package/test/fixtures/stripe/invoice-payment-failed.json +148 -0
  804. package/test/fixtures/stripe/invoice-subscription-payment-failed.json +28 -0
  805. package/test/fixtures/stripe/invoice-subscription-payment-succeeded.json +28 -0
  806. package/test/fixtures/stripe/subscription-active.json +161 -0
  807. package/test/fixtures/stripe/subscription-canceled.json +161 -0
  808. package/test/fixtures/stripe/subscription-trialing.json +161 -0
  809. package/test/helpers/_shared-config.js +15 -0
  810. package/test/helpers/ai-request-payload.test.js +619 -0
  811. package/test/helpers/ai-schema-resolve.test.js +125 -0
  812. package/test/helpers/ai-test-provider.test.js +227 -0
  813. package/test/helpers/ai-token-accounting.test.js +150 -0
  814. package/test/helpers/ai-tools-format.test.js +384 -0
  815. package/test/helpers/analytics-no-id-notice.test.js +146 -0
  816. package/test/helpers/analytics-user-data.test.js +194 -0
  817. package/test/helpers/api-manager.test.js +311 -0
  818. package/test/helpers/backend-router.test.js +136 -0
  819. package/test/helpers/content/blog-auto-publisher.test.js +482 -0
  820. package/test/helpers/content/feed-parser.test.js +529 -0
  821. package/test/helpers/content/ghostii-blocks.test.js +135 -0
  822. package/test/helpers/content/ghostii-feed-integration.test.js +405 -0
  823. package/test/helpers/content/ghostii-write-article.test.js +244 -0
  824. package/test/helpers/dev-only-routes.test.js +225 -0
  825. package/test/helpers/env-reader.test.js +362 -0
  826. package/test/helpers/environment.test.js +263 -0
  827. package/test/helpers/event-middleware.test.js +407 -0
  828. package/test/helpers/infer-contact.test.js +157 -0
  829. package/test/helpers/lane-url.test.js +90 -0
  830. package/test/helpers/merge-line-files.test.js +272 -0
  831. package/test/helpers/metadata.test.js +122 -0
  832. package/test/helpers/middleware-request-log.test.js +134 -0
  833. package/test/helpers/middleware-user-log.test.js +124 -0
  834. package/test/helpers/payment/chargebee/parse-webhook.test.js +490 -0
  835. package/test/helpers/payment/chargebee/refund-details.test.js +198 -0
  836. package/test/helpers/payment/chargebee/to-unified-one-time.test.js +148 -0
  837. package/test/helpers/payment/chargebee/to-unified-subscription.test.js +649 -0
  838. package/test/helpers/payment/coinbase/create-intent.test.js +247 -0
  839. package/test/helpers/payment/coinbase/parse-webhook.test.js +195 -0
  840. package/test/helpers/payment/coinbase/refund-unsupported.test.js +141 -0
  841. package/test/helpers/payment/coinbase/to-unified-one-time.test.js +201 -0
  842. package/test/helpers/payment/discount-codes.test.js +141 -0
  843. package/test/helpers/payment/extract-resource.test.js +113 -0
  844. package/test/helpers/payment/fetch-failure.test.js +215 -0
  845. package/test/helpers/payment/license-gate.test.js +108 -0
  846. package/test/helpers/payment/order-id.test.js +99 -0
  847. package/test/helpers/payment/paypal/create-intent.test.js +382 -0
  848. package/test/helpers/payment/paypal/fetch-capture.test.js +123 -0
  849. package/test/helpers/payment/paypal/fetch-sale.test.js +183 -0
  850. package/test/helpers/payment/paypal/parse-webhook.test.js +678 -0
  851. package/test/helpers/payment/paypal/refund-details.test.js +202 -0
  852. package/test/helpers/payment/paypal/resolve-plan-id.test.js +108 -0
  853. package/test/helpers/payment/paypal/switch-plan.test.js +92 -0
  854. package/test/helpers/payment/paypal/to-unified-one-time.test.js +383 -0
  855. package/test/helpers/payment/paypal/to-unified-subscription.test.js +884 -0
  856. package/test/helpers/payment/stripe/fetch-charge.test.js +147 -0
  857. package/test/helpers/payment/stripe/parse-webhook.test.js +448 -0
  858. package/test/helpers/payment/stripe/refund-details.test.js +147 -0
  859. package/test/helpers/payment/stripe/to-unified-one-time.test.js +307 -0
  860. package/test/helpers/payment/stripe/to-unified-subscription.test.js +709 -0
  861. package/test/helpers/persona-domain.test.js +87 -0
  862. package/test/helpers/recaptcha.test.js +166 -0
  863. package/test/helpers/resolved-config.test.js +106 -0
  864. package/test/helpers/response-log-redaction.test.js +262 -0
  865. package/test/helpers/roles.test.js +155 -0
  866. package/test/helpers/route-context-debug-gate.test.js +118 -0
  867. package/test/helpers/route-context-logging.test.js +214 -0
  868. package/test/helpers/route-context.test.js +129 -0
  869. package/test/helpers/safe-compare.test.js +114 -0
  870. package/test/helpers/sanitize.test.js +223 -0
  871. package/test/helpers/schema-engine.test.js +537 -0
  872. package/test/helpers/schema-zod.test.js +554 -0
  873. package/test/helpers/seed-accounts-load.test.js +163 -0
  874. package/test/helpers/seed-google-personas.test.js +114 -0
  875. package/test/helpers/seeded-personas.test.js +823 -0
  876. package/test/helpers/settings.test.js +292 -0
  877. package/test/helpers/setup-engines-pin.test.js +68 -0
  878. package/test/helpers/setup-manifest-sync.test.js +116 -0
  879. package/test/helpers/slugify.test.js +395 -0
  880. package/test/helpers/storage.test.js +195 -0
  881. package/test/helpers/test-banner-latch.test.js +139 -0
  882. package/test/helpers/usage-consume.test.js +460 -0
  883. package/test/helpers/usage-log-privacy.test.js +121 -0
  884. package/test/helpers/user-doc-heal.test.js +540 -0
  885. package/test/helpers/user.test.js +771 -0
  886. package/test/helpers/webhook-forward.test.js +419 -0
  887. package/test/helpers/wipe-auth-project.test.js +143 -0
  888. package/test/mcp/discovery.test.js +52 -0
  889. package/test/mcp/oauth.test.js +160 -0
  890. package/test/mcp/protocol.test.js +278 -0
  891. package/test/mcp/roles.test.js +203 -0
  892. package/test/mcp/utils.test.js +245 -0
  893. package/test/notification/identity.test.js +155 -0
  894. package/test/routes/admin/create-post.test.js +362 -0
  895. package/test/routes/admin/database.test.js +133 -0
  896. package/test/routes/admin/deduplicate-image-alts.test.js +191 -0
  897. package/test/routes/admin/email-content-html.test.js +58 -0
  898. package/test/routes/admin/email-request-log.test.js +86 -0
  899. package/test/routes/admin/email.test.js +116 -0
  900. package/test/routes/admin/firestore-query.test.js +206 -0
  901. package/test/routes/admin/firestore.test.js +129 -0
  902. package/test/routes/admin/infer-contact.test.js +220 -0
  903. package/test/routes/admin/notification.test.js +199 -0
  904. package/test/routes/admin/post-convert-image.test.js +159 -0
  905. package/test/routes/admin/post-deploy-flag.test.js +81 -0
  906. package/test/routes/admin/post-download-error.test.js +90 -0
  907. package/test/routes/admin/post-resize-image.test.js +185 -0
  908. package/test/routes/admin/post.test.js +369 -0
  909. package/test/routes/admin/repo-content.test.js +223 -0
  910. package/test/routes/admin/stats.test.js +114 -0
  911. package/test/routes/admin/users-disable.test.js +63 -0
  912. package/test/routes/admin/users-list.test.js +71 -0
  913. package/test/routes/content/post.test.js +60 -0
  914. package/test/routes/general/uuid.test.js +133 -0
  915. package/test/routes/health.test.js +110 -0
  916. package/test/routes/marketing/campaign.test.js +184 -0
  917. package/test/routes/marketing/contact.test.js +416 -0
  918. package/test/routes/marketing/email-preferences.test.js +293 -0
  919. package/test/routes/marketing/push-send.test.js +34 -0
  920. package/test/routes/marketing/webhook-forward.test.js +63 -0
  921. package/test/routes/marketing/webhook.test.js +641 -0
  922. package/test/routes/payments/_route-harness.js +145 -0
  923. package/test/routes/payments/cancel-provider-errors.test.js +227 -0
  924. package/test/routes/payments/cancel-skip-guards.test.js +126 -0
  925. package/test/routes/payments/cancel-trialing.test.js +428 -0
  926. package/test/routes/payments/cancel.test.js +165 -0
  927. package/test/routes/payments/dedup-race.test.js +186 -0
  928. package/test/routes/payments/discount.test.js +82 -0
  929. package/test/routes/payments/dispute-alert.test.js +324 -0
  930. package/test/routes/payments/intent-discount-amounts.test.js +550 -0
  931. package/test/routes/payments/intent-discount-coupons.test.js +293 -0
  932. package/test/routes/payments/intent-one-time-metadata.test.js +122 -0
  933. package/test/routes/payments/intent-purchaser-guard.test.js +139 -0
  934. package/test/routes/payments/intent-zero-total.test.js +260 -0
  935. package/test/routes/payments/intent.test.js +413 -0
  936. package/test/routes/payments/plan.test.js +367 -0
  937. package/test/routes/payments/portal-return-url.test.js +107 -0
  938. package/test/routes/payments/portal.test.js +94 -0
  939. package/test/routes/payments/refund-one-time.test.js +254 -0
  940. package/test/routes/payments/refund-paypal-proration.test.js +190 -0
  941. package/test/routes/payments/refund.test.js +181 -0
  942. package/test/routes/payments/trial-eligibility.test.js +73 -0
  943. package/test/routes/payments/uncancel.test.js +270 -0
  944. package/test/routes/payments/webhook-stripe-invoice.test.js +129 -0
  945. package/test/routes/payments/webhook-stripe-refund-one-time.test.js +130 -0
  946. package/test/routes/payments/webhook-test-provider.test.js +156 -0
  947. package/test/routes/payments/webhook.test.js +115 -0
  948. package/test/routes/payments/winback-stripe-coupon.test.js +126 -0
  949. package/test/routes/payments/winback.test.js +597 -0
  950. package/test/routes/test/authenticate.test.js +79 -0
  951. package/test/routes/test/redirect.test.js +66 -0
  952. package/test/routes/test/reset-account.test.js +126 -0
  953. package/test/routes/test/roster.test.js +101 -0
  954. package/test/routes/test/schema.test.js +556 -0
  955. package/test/routes/test/usage.test.js +374 -0
  956. package/test/routes/user/api-keys.test.js +158 -0
  957. package/test/routes/user/connections-grant.test.js +759 -0
  958. package/test/routes/user/connections-identity.test.js +260 -0
  959. package/test/routes/user/connections-log-privacy.test.js +206 -0
  960. package/test/routes/user/connections-refresh-lease.test.js +696 -0
  961. package/test/routes/user/connections-return.test.js +261 -0
  962. package/test/routes/user/connections-uid.test.js +516 -0
  963. package/test/routes/user/delete.test.js +138 -0
  964. package/test/routes/user/feedback.test.js +106 -0
  965. package/test/routes/user/orders.test.js +233 -0
  966. package/test/routes/user/sessions.test.js +231 -0
  967. package/test/routes/user/settings-validate.test.js +84 -0
  968. package/test/routes/user/signup-emails.test.js +113 -0
  969. package/test/routes/user/signup-location.test.js +84 -0
  970. package/test/routes/user/signup-log-privacy.test.js +112 -0
  971. package/test/routes/user/signup.test.js +579 -0
  972. package/test/routes/user/subscription.test.js +101 -0
  973. package/test/routes/user/token.test.js +111 -0
  974. package/test/routes/user/user.test.js +158 -0
  975. package/test/routes/verts/cache.test.js +78 -0
  976. package/test/routes/verts/click-destination.test.js +76 -0
  977. package/test/routes/verts/crud.test.js +194 -0
  978. package/test/routes/verts/redirect.test.js +147 -0
  979. package/test/routes/verts/selection.test.js +237 -0
  980. package/test/routes/verts/serve.test.js +185 -0
  981. package/test/routes/verts/unit-document.test.js +117 -0
  982. package/test/rules/_environment.js +66 -0
  983. package/test/rules/brand-merge.test.js +195 -0
  984. package/test/rules/field-helpers.test.js +309 -0
  985. package/test/rules/notifications.test.js +577 -0
  986. package/test/rules/payments-carts.test.js +406 -0
  987. package/test/rules/sessions.test.js +170 -0
  988. package/test/rules/user.test.js +517 -0
  989. package/test/rules/verts.test.js +101 -0
  990. package/test/security/fetch-log-secrets.test.js +124 -0
  991. package/test/security/primitives.test.js +105 -0
  992. package/test/security/repo-pinning.test.js +77 -0
  993. package/test/stripe-live/subscription-lifecycle.test.js +141 -0
@@ -0,0 +1,658 @@
1
+ /**
2
+ * Test: every emulator boot reaps the machine's ORPHANED emulator-family
3
+ * processes before it claims a port
4
+ * ([#781](https://github.com/Omega-JS-Stack/omega/issues/781)).
5
+ *
6
+ * Found on a real machine: ~100 `functionsEmulatorRuntime` workers from a
7
+ * legacy node v22 firebase-tools, plus a hub on a bumped port with three java
8
+ * jars, all reparented to PID 1 with their parents gone for hours. Nothing in
9
+ * this framework could see any of it: the port-driven reaper only looked at
10
+ * the ports THIS run wanted and only signalled what it could prove was this
11
+ * project's, and the record-driven reap only reaches pids a record still names.
12
+ *
13
+ * Run: npx omega test backend:cli/emulator-orphans
14
+ *
15
+ * The verdict is pure (a ps row in, a boolean out) and the sweep reads the
16
+ * world through seams, so every case below states a process table and reads
17
+ * back exactly what would have been signalled — nothing real is touched. The
18
+ * rows are copied from `ps -A -o pid=,ppid=,uid=,command=` on the machine that
19
+ * reported this, except the rows marked BUILT below: shapes firebase-tools
20
+ * launches that were not running when the snapshot was taken.
21
+ *
22
+ * The one exception is the real-process drill at the bottom, which spawns its
23
+ * own idle node, orphans it, and runs the REAL sweep against the REAL `ps`.
24
+ * Its `kill` seam forwards the signal only for the pids this file spawned and
25
+ * merely RECORDS anything else the sweep matched: the sweep is machine-wide by
26
+ * design, and a test run is not the place to exercise that half.
27
+ */
28
+
29
+ // Libraries
30
+ const os = require('os');
31
+ const path = require('path');
32
+ const childProcess = require('child_process');
33
+ const jetpack = require('fs-jetpack');
34
+
35
+ const EmulatorCommand = require('../../dist/cli/commands/emulator.js');
36
+ const orphans = require('../../dist/cli/commands/emulator-orphans.js');
37
+ const defineCases = require('../../dist/vendor/devkit/test/define-cases.js');
38
+
39
+ const { parseProcessTable, isOrphanedEmulatorFamily, describeFamilyMember, reapMachineOrphans } = orphans;
40
+
41
+ // This user, and somebody else (root owns half the rows in any snapshot).
42
+ const UID = 501;
43
+ const OTHER_UID = 0;
44
+
45
+ // Where an orphan lands, and two live parents: this session's own boot, and a
46
+ // terminal a developer typed `firebase emulators:start` into.
47
+ const ORPHANED = 1;
48
+
49
+ // Every temp root the drill writes carries this, and it is the ONLY thing the
50
+ // drill will send a real signal to.
51
+ const DRILL_MARKER = 'omega-orphan-drill-';
52
+ const OUR_BOOT = 34965;
53
+ const A_TERMINAL = 51900;
54
+
55
+ // ─── REAL rows (2026-09-03, ppid varies per case) ───
56
+
57
+ // The hub, both shapes: the bin on PATH, and (BUILT) firebase-tools' own entry
58
+ // file, the form an `emulators:exec` lane runs.
59
+ const HUB = 'node /Users/ian/.nvm/versions/node/v24.15.0/bin/firebase emulators:start --only functions,firestore,auth,database,hosting,pubsub';
60
+ const HUB_TOOLS_ENTRY = 'node /Users/ian/.nvm/versions/node/v22.22.1/lib/node_modules/firebase-tools/lib/bin/firebase.js emulators:exec --project demo-other-brand npm test';
61
+
62
+ // The functions worker — the shape that piled up ~100 deep, from the LEGACY
63
+ // node v22 install, which is why no path or version can be part of the match.
64
+ const WORKER = '/Users/ian/.nvm/versions/node/v22.22.1/bin/node /Users/ian/.nvm/versions/node/v22.22.1/lib/node_modules/firebase-tools/lib/emulator/functionsEmulatorRuntime';
65
+
66
+ // The functions DISCOVERY server: firebase-tools spawns the functions source's
67
+ // own `firebase-functions` bin with a random 8000-8999 PORT, reads the manifest
68
+ // off it, then asks it to quit. A hub killed inside that window leaves it with
69
+ // no parent and no timer. Real row, found on switchboard 2026-09-06: started
70
+ // two days earlier, ppid 1, still holding port 8615
71
+ // ([#805](https://github.com/Omega-JS-Stack/omega/issues/805)).
72
+ const DISCOVERY = 'node targets/backend/node_modules/.bin/firebase-functions targets/backend/dist';
73
+ // BUILT: the file that bin is a symlink to, the form a run through the entry
74
+ // file itself prints.
75
+ const DISCOVERY_ENTRY = '/Users/ian/.nvm/versions/node/v24.15.0/bin/node /Users/ian/Developer/Repositories/StreamForge-App/switchboard/targets/backend/node_modules/firebase-functions/lib/bin/firebase-functions.js /Users/ian/Developer/Repositories/StreamForge-App/switchboard/targets/backend/dist';
76
+ // BUILT: the same spawn from a backend target that IS the cwd, so firebase-tools
77
+ // hands it a bare-relative source dir and nothing precedes `node_modules`.
78
+ const DISCOVERY_RELATIVE = 'node node_modules/.bin/firebase-functions dist';
79
+
80
+ // The jars, from the hub that survived on port 4402. The firestore one carries
81
+ // `--database-edition standard`, so a naive name match calls it the database.
82
+ const FIRESTORE_JAR = 'java -Dgoogle.cloud_firestore.debug_log_level=FINE -Duser.language=en -jar /Users/ian/.cache/firebase/emulators/cloud-firestore-emulator-v1.21.0.jar --host 127.0.0.1 --port 9152 --websocket_port 9150 --database-edition standard --project_id demo-other-brand --single_project_mode true';
83
+ const DATABASE_JAR = 'java -Duser.language=en -jar /Users/ian/.cache/firebase/emulators/firebase-database-emulator-v4.11.2.jar --host 127.0.0.1 --port 9153 --functions_emulator_port 9155';
84
+ const PUBSUB_JAR = 'java -jar /Users/ian/.cache/firebase/emulators/pubsub-emulator-0.8.34/pubsub-emulator/lib/cloud-pubsub-emulator-0.8.34-all.jar --host=127.0.0.1 --port=9154';
85
+ // BUILT: the storage rules runtime, and a jar the emulator cache grows later.
86
+ const STORAGE_JAR = 'java -jar /Users/ian/.cache/firebase/emulators/cloud-storage-rules-runtime-v1.1.3.jar';
87
+ const UNKNOWN_JAR = 'java -jar /Users/ian/.cache/firebase/emulators/some-future-emulator-v1.0.0.jar --port 9160';
88
+
89
+ // Puppeteer's browser: the temp profile name is the whole signal — only
90
+ // puppeteer ever creates a `puppeteer_dev_chrome_profile-` directory.
91
+ const PUPPETEER_CHROME = '/Users/ian/.cache/puppeteer/chrome/mac_arm-140.0.7339.80/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing --allow-pre-commit-input --disable-background-networking --headless=new --user-data-dir=/var/folders/gx/7g25kfls0c96yfb6nt3bsg1m0000gn/T/puppeteer_dev_chrome_profile-a1B2c3 --remote-debugging-port=0';
92
+
93
+ // ─── REAL rows that must survive, even at PID 1 ───
94
+
95
+ const USER_CHROME = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
96
+ // BUILT. devkit's port-hold and boot-child are LIBRARY modules a lane requires
97
+ // in-process — they are never a spawned argv, so a command line naming one is
98
+ // always somebody else reading the file (an editor, a grep, a node -e).
99
+ const EDITOR_ON_PORT_HOLD = '/Applications/Visual Studio Code.app/Contents/MacOS/Electron /Users/ian/Developer/Repositories/Omega/omega/packages/devkit/src/test/port-hold.js';
100
+ // BUILT. An editor sitting on the discovery server's own bin file, and one on a
101
+ // source file of the same package: a command line that NAMES `firebase-functions`
102
+ // is not a node running it.
103
+ const EDITOR_ON_DISCOVERY_BIN = '/Applications/Visual Studio Code.app/Contents/MacOS/Electron /Users/ian/Developer/Repositories/StreamForge-App/switchboard/targets/backend/node_modules/firebase-functions/lib/bin/firebase-functions.js';
104
+ const EDITOR_ON_FUNCTIONS_SOURCE = '/Applications/Visual Studio Code.app/Contents/MacOS/Electron /Users/ian/Developer/Repositories/StreamForge-App/switchboard/targets/backend/node_modules/firebase-functions/lib/common/providers/https.js';
105
+ const OMEGA_CLI = 'node /Users/ian/Developer/Repositories/Omega/omega/node_modules/.bin/omega emulator';
106
+ const MGR_CLI = 'node /Users/ian/Developer/Repositories/Omega/omega/node_modules/.bin/mgr dev';
107
+ const FIREBASE_DEPLOY = 'node /Users/ian/.nvm/versions/node/v24.15.0/bin/firebase deploy --only functions --project omegajs';
108
+ // The reload watcher: it runs with PPID 1 while its session is very much alive,
109
+ // and its argv names firebase paths — the row the loose name regex killed
110
+ // ([#293](https://github.com/Omega-JS-Stack/omega/issues/293)).
111
+ const RELOAD_WATCHER = 'node /Users/ian/.nvm/versions/node/v22.22.1/bin/nodemon --on-change-only --delay 1 --watch /Users/ian/Developer/Repositories/Omega/omega/packages/backend/src --ext js,json --exec node -e "fs.writeFileSync(\'/Users/ian/Developer/Repositories/Omega/omega/brands/sandbox-brand/targets/backend/.temp/emulator.log.reset\',\'\')"';
112
+
113
+ /**
114
+ * A fixture process table plus the seams the sweep reads the world through.
115
+ *
116
+ * `readTable` renders the rows exactly as `ps -A -o pid=,ppid=,uid=,command=`
117
+ * does, `readProcess` is the per-pid re-read the escalation proves itself with,
118
+ * `kill` is the signal, and `isFree` is the port probe — so a case states what
119
+ * is running and reads back what would have been signalled. Nothing leaves this
120
+ * process.
121
+ * @param {object} table - pid → { ppid, uid, command } as ps would report it.
122
+ * @param {object} [options]
123
+ * @param {Function} [options.afterTerm] - What SIGTERM does (default: it exits).
124
+ * @param {number[]} [options.busyPorts] - Ports the probe reports as still held.
125
+ */
126
+ function processTable(table, { afterTerm, busyPorts = [] } = {}) {
127
+ const live = new Map(Object.entries(table).map(([pid, row]) => [Number(pid), { uid: UID, ...row }]));
128
+ const signals = [];
129
+ const probed = [];
130
+
131
+ return {
132
+ signals: signals,
133
+ probed: probed,
134
+ live: live,
135
+ signalled: (pid) => signals.filter((entry) => entry.pid === Number(pid)).map((entry) => entry.signal),
136
+ seams: {
137
+ uid: UID,
138
+ graceMs: 50,
139
+ readTable: () => [...live.entries()].map(([pid, row]) => ` ${pid} ${row.ppid} ${row.uid} ${row.command}`).join('\n'),
140
+ readProcess: (pid) => {
141
+ const row = live.get(Number(pid));
142
+
143
+ return row ? { pid: Number(pid), ppid: row.ppid, uid: row.uid, command: row.command } : null;
144
+ },
145
+ kill: (pid, signal) => {
146
+ signals.push({ pid: Number(pid), signal: signal });
147
+
148
+ if (signal === 'SIGTERM') {
149
+ (afterTerm || ((target, rows) => rows.delete(target)))(Number(pid), live);
150
+ } else {
151
+ live.delete(Number(pid));
152
+ }
153
+ },
154
+ isFree: async (port) => {
155
+ probed.push(port);
156
+
157
+ return !busyPorts.includes(port);
158
+ },
159
+ },
160
+ };
161
+ }
162
+
163
+ /** A row as ps prints it, for the pure-verdict cases. */
164
+ function row(pid, ppid, command, uid = UID) {
165
+ return { pid: pid, ppid: ppid, uid: uid, command: command };
166
+ }
167
+
168
+ /**
169
+ * The idle stand-in for a functions worker: a real node process whose argv path
170
+ * is the family shape, so the REAL matcher sees a REAL row for it.
171
+ * @param {string} dir - The temp root to write it under.
172
+ * @returns {string} The script path.
173
+ */
174
+ function writeWorkerScript(dir) {
175
+ const script = path.join(dir, 'firebase-tools', 'lib', 'emulator', 'functionsEmulatorRuntime.js');
176
+
177
+ jetpack.write(script, 'setInterval(() => {}, 1000);\n');
178
+
179
+ return script;
180
+ }
181
+
182
+ /**
183
+ * Poll `probe` until it answers truthy or the window closes.
184
+ * @param {Function} probe - Returns the answer, or a falsy value to keep waiting.
185
+ * @param {number} timeoutMs - How long to keep asking.
186
+ * @returns {Promise<*>} The answer, or null when the window closed.
187
+ */
188
+ async function until(probe, timeoutMs) {
189
+ const deadline = Date.now() + timeoutMs;
190
+
191
+ while (Date.now() < deadline) {
192
+ const answer = probe();
193
+
194
+ if (answer) {
195
+ return answer;
196
+ }
197
+
198
+ await new Promise((resolve) => setTimeout(resolve, 50));
199
+ }
200
+
201
+ return null;
202
+ }
203
+
204
+ /** Every live pid whose command line contains `needle`, from the real `ps`. */
205
+ function livePidsRunning(needle) {
206
+ return parseProcessTable(orphans.readProcessTable())
207
+ .filter((entry) => entry.command.includes(needle))
208
+ .map((entry) => entry.pid);
209
+ }
210
+
211
+ module.exports = defineCases({
212
+ description: 'machine-wide reap of orphaned emulator-family processes (#781)',
213
+ type: 'group',
214
+ timeout: 60000,
215
+
216
+ tests: [
217
+ // ─── the snapshot ───
218
+
219
+ {
220
+ name: 'a-real-ps-snapshot-parses-into-rows',
221
+ async run({ assert }) {
222
+ const text = [
223
+ ` 1 0 0 /sbin/launchd`,
224
+ `73529 1 501 ${FIRESTORE_JAR}`,
225
+ `74155 ${OUR_BOOT} 501 ${PUBSUB_JAR}`,
226
+ ``,
227
+ `garbage line with no numbers`,
228
+ ].join('\n');
229
+
230
+ const rows = parseProcessTable(text);
231
+
232
+ assert.equal(rows.length, 3, 'blank and unparseable lines are dropped, never guessed at');
233
+ assert.deepEqual(rows[0], { pid: 1, ppid: 0, uid: 0, command: '/sbin/launchd' });
234
+ assert.equal(rows[1].pid, 73529);
235
+ assert.equal(rows[1].ppid, 1);
236
+ assert.equal(rows[1].uid, 501);
237
+ assert.equal(rows[1].command, FIRESTORE_JAR, 'the command keeps every flag and space it was printed with');
238
+ assert.equal(rows[2].ppid, OUR_BOOT);
239
+ assert.deepEqual(parseProcessTable(''), []);
240
+ assert.deepEqual(parseProcessTable(null), []);
241
+ },
242
+ },
243
+
244
+ // ─── the two proofs: orphaned AND family ───
245
+
246
+ {
247
+ name: 'this-sessions-live-stack-is-untouched',
248
+ async run({ assert }) {
249
+ // Every member of a LIVE stack hangs off the boot that spawned it, so
250
+ // the sweep never considers it — the boot cannot eat its own tail.
251
+ assert.equal(isOrphanedEmulatorFamily(row(37088, OUR_BOOT, FIRESTORE_JAR), UID), false);
252
+ assert.equal(isOrphanedEmulatorFamily(row(37360, OUR_BOOT, PUBSUB_JAR), UID), false);
253
+ assert.equal(isOrphanedEmulatorFamily(row(37361, OUR_BOOT, WORKER), UID), false);
254
+ },
255
+ },
256
+
257
+ {
258
+ name: 'a-hand-run-hub-in-a-terminal-is-untouched',
259
+ async run({ assert }) {
260
+ // A developer typed `firebase emulators:start` in a shell that is still
261
+ // open: the shell is its parent, so it is nobody's leftover.
262
+ assert.equal(isOrphanedEmulatorFamily(row(51901, A_TERMINAL, HUB), UID), false);
263
+ },
264
+ },
265
+
266
+ {
267
+ name: 'another-uid-is-never-a-candidate',
268
+ async run({ assert }) {
269
+ // Same row, same PID 1, a different owner. Signalling it would fail
270
+ // anyway — but the verdict is the gate, not the OS's refusal.
271
+ assert.equal(isOrphanedEmulatorFamily(row(73529, ORPHANED, FIRESTORE_JAR, OTHER_UID), UID), false);
272
+ assert.equal(isOrphanedEmulatorFamily(row(73529, ORPHANED, FIRESTORE_JAR), undefined), false, 'no uid of our own proves nothing');
273
+ },
274
+ },
275
+
276
+ {
277
+ name: 'a-legacy-workers-orphan-is-reaped-whatever-node-installed-it',
278
+ async run({ assert }) {
279
+ // The ~100-deep pile: a firebase-tools from a node version this machine
280
+ // no longer even runs. No path and no version is part of the match.
281
+ assert.equal(isOrphanedEmulatorFamily(row(38483, ORPHANED, WORKER), UID), true);
282
+ },
283
+ },
284
+
285
+ {
286
+ name: 'the-discovery-servers-orphan-is-reaped-in-both-its-shapes',
287
+ async run({ assert }) {
288
+ // The row that outlived two boots on switchboard: the hub that spawned
289
+ // it went down inside its 10s quit window, so nothing was left to ask
290
+ // it to leave and nothing but this sweep can reach it (#805).
291
+ assert.equal(isOrphanedEmulatorFamily(row(38500, ORPHANED, DISCOVERY), UID), true);
292
+ assert.equal(isOrphanedEmulatorFamily(row(38501, ORPHANED, DISCOVERY_ENTRY), UID), true);
293
+ assert.equal(isOrphanedEmulatorFamily(row(38504, ORPHANED, DISCOVERY_RELATIVE), UID), true, 'a bare-relative source dir puts nothing at all before `node_modules`');
294
+ },
295
+ },
296
+
297
+ {
298
+ name: 'a-discovery-server-under-a-living-hub-is-untouched',
299
+ async run({ assert }) {
300
+ // Same parentage rule as the worker: while the hub that spawned it is
301
+ // alive, the hub owns its teardown and the sweep never considers it.
302
+ assert.equal(isOrphanedEmulatorFamily(row(38502, OUR_BOOT, DISCOVERY), UID), false);
303
+ assert.equal(isOrphanedEmulatorFamily(row(38503, A_TERMINAL, DISCOVERY_ENTRY), UID), false);
304
+ },
305
+ },
306
+
307
+ {
308
+ name: 'a-command-that-merely-names-firebase-functions-survives-the-sweep',
309
+ async run({ assert }) {
310
+ // The #293 lesson on the newest shape: the match is a node RUNNING the
311
+ // bin, so an editor holding that very file open is not family, and
312
+ // neither is anything else naming the package.
313
+ assert.equal(isOrphanedEmulatorFamily(row(60009, ORPHANED, EDITOR_ON_DISCOVERY_BIN), UID), false);
314
+ assert.equal(isOrphanedEmulatorFamily(row(60010, ORPHANED, EDITOR_ON_FUNCTIONS_SOURCE), UID), false);
315
+ assert.equal(describeFamilyMember(EDITOR_ON_DISCOVERY_BIN), null);
316
+ assert.equal(describeFamilyMember(EDITOR_ON_FUNCTIONS_SOURCE), null);
317
+ },
318
+ },
319
+
320
+ {
321
+ name: 'a-bumped-port-hub-and-every-jar-under-it-are-reaped',
322
+ async run({ assert }) {
323
+ // The hub sat on 4402, so no port this run wants could ever reach it,
324
+ // and it named another brand's project — which stops mattering the
325
+ // moment its parent is gone (#781 supersedes #293 on that point).
326
+ assert.equal(isOrphanedEmulatorFamily(row(38400, ORPHANED, HUB), UID), true);
327
+ assert.equal(isOrphanedEmulatorFamily(row(38401, ORPHANED, HUB_TOOLS_ENTRY), UID), true);
328
+ assert.equal(isOrphanedEmulatorFamily(row(73529, ORPHANED, FIRESTORE_JAR), UID), true);
329
+ assert.equal(isOrphanedEmulatorFamily(row(73530, ORPHANED, DATABASE_JAR), UID), true);
330
+ assert.equal(isOrphanedEmulatorFamily(row(74155, ORPHANED, PUBSUB_JAR), UID), true);
331
+ assert.equal(isOrphanedEmulatorFamily(row(74156, ORPHANED, STORAGE_JAR), UID), true);
332
+ },
333
+ },
334
+
335
+ {
336
+ name: 'puppeteers-headless-browser-is-family',
337
+ async run({ assert }) {
338
+ // A lane's chrome outlives the lane exactly as the jars do, and its
339
+ // throwaway profile directory is the only thing that says so.
340
+ assert.equal(isOrphanedEmulatorFamily(row(41003, ORPHANED, PUPPETEER_CHROME), UID), true);
341
+ },
342
+ },
343
+
344
+ {
345
+ name: 'the-reload-watcher-survives-the-sweep',
346
+ async run({ assert }) {
347
+ // nodemon runs at PID 1 with a LIVE session behind it, and its argv
348
+ // names firebase paths. The strict family match is the whole reason it
349
+ // lives: it is not a hub, a worker, a jar, a helper or a browser.
350
+ assert.equal(isOrphanedEmulatorFamily(row(60001, ORPHANED, RELOAD_WATCHER), UID), false);
351
+ assert.equal(describeFamilyMember(RELOAD_WATCHER), null);
352
+ },
353
+ },
354
+
355
+ {
356
+ name: 'the-cli-a-deploy-and-a-users-browser-survive-the-sweep',
357
+ async run({ assert }) {
358
+ // `omega emulator` is the process running this very sweep; `firebase
359
+ // deploy` is a live publish; a Chrome launched from Finder has PID 1
360
+ // for a parent and no puppeteer profile.
361
+ assert.equal(isOrphanedEmulatorFamily(row(60002, ORPHANED, OMEGA_CLI), UID), false);
362
+ assert.equal(isOrphanedEmulatorFamily(row(60003, ORPHANED, MGR_CLI), UID), false);
363
+ assert.equal(isOrphanedEmulatorFamily(row(60004, ORPHANED, FIREBASE_DEPLOY), UID), false);
364
+ assert.equal(isOrphanedEmulatorFamily(row(60005, ORPHANED, USER_CHROME), UID), false);
365
+ assert.equal(isOrphanedEmulatorFamily(row(60006, ORPHANED, 'node /Users/ian/some-other-tool/server.js'), UID), false);
366
+ assert.equal(isOrphanedEmulatorFamily(row(60007, ORPHANED, 'java -jar /Users/ian/Developer/tools/some-app.jar'), UID), false, 'a jar outside the firebase emulator cache is somebody else\'s');
367
+ assert.equal(isOrphanedEmulatorFamily(row(60008, ORPHANED, EDITOR_ON_PORT_HOLD), UID), false, "a devkit lane helper is a library module, so a process NAMING one is never running one");
368
+ },
369
+ },
370
+
371
+ {
372
+ name: 'init-itself-is-never-a-candidate',
373
+ async run({ assert }) {
374
+ assert.equal(isOrphanedEmulatorFamily(row(1, 0, '/sbin/launchd'), UID), false);
375
+ assert.equal(isOrphanedEmulatorFamily(row(NaN, ORPHANED, WORKER), UID), false);
376
+ assert.equal(isOrphanedEmulatorFamily(undefined, UID), false);
377
+ },
378
+ },
379
+
380
+ // ─── the short label the log prints ───
381
+
382
+ {
383
+ name: 'every-family-member-has-a-short-name',
384
+ async run({ assert }) {
385
+ assert.equal(describeFamilyMember(HUB), 'firebase hub');
386
+ assert.equal(describeFamilyMember(HUB_TOOLS_ENTRY), 'firebase hub');
387
+ assert.equal(describeFamilyMember(WORKER), 'functions worker');
388
+ assert.equal(describeFamilyMember(DISCOVERY), 'functions discovery server');
389
+ assert.equal(describeFamilyMember(DISCOVERY_ENTRY), 'functions discovery server');
390
+ assert.equal(describeFamilyMember(FIRESTORE_JAR), 'firestore jar', 'the firestore jar names a database edition — the JAR decides, not the argv');
391
+ assert.equal(describeFamilyMember(DATABASE_JAR), 'database jar');
392
+ assert.equal(describeFamilyMember(PUBSUB_JAR), 'pubsub jar');
393
+ assert.equal(describeFamilyMember(STORAGE_JAR), 'storage jar');
394
+ assert.equal(describeFamilyMember(UNKNOWN_JAR), 'emulator jar', 'a jar the cache grows tomorrow is still an emulator jar');
395
+ assert.equal(describeFamilyMember(PUPPETEER_CHROME), 'puppeteer chrome');
396
+ assert.equal(describeFamilyMember(USER_CHROME), null);
397
+ assert.equal(describeFamilyMember(EDITOR_ON_PORT_HOLD), null);
398
+ },
399
+ },
400
+
401
+ // ─── the sweep ───
402
+
403
+ {
404
+ name: 'the-sweep-signals-every-orphan-and-nothing-else',
405
+ async run({ assert }) {
406
+ const table = processTable({
407
+ 73529: { ppid: ORPHANED, command: FIRESTORE_JAR },
408
+ 74155: { ppid: ORPHANED, command: PUBSUB_JAR },
409
+ 38483: { ppid: ORPHANED, command: WORKER },
410
+ 37088: { ppid: OUR_BOOT, command: FIRESTORE_JAR },
411
+ 60001: { ppid: ORPHANED, command: RELOAD_WATCHER },
412
+ 60002: { ppid: ORPHANED, command: FIRESTORE_JAR, uid: OTHER_UID },
413
+ });
414
+
415
+ const result = await reapMachineOrphans(table.seams);
416
+
417
+ assert.deepEqual(result.reaped.map((entry) => entry.pid).sort(), [38483, 73529, 74155]);
418
+ assert.deepEqual(result.killed, [], 'everything left on the first signal');
419
+ assert.deepEqual(table.signals.map((entry) => entry.signal), ['SIGTERM', 'SIGTERM', 'SIGTERM']);
420
+ assert.deepEqual(table.signalled(37088), [], "a live stack's jar is never signalled");
421
+ assert.deepEqual(table.signalled(60001), [], 'the watcher is never signalled');
422
+ assert.deepEqual(table.signalled(60002), [], "another user's jar is never signalled");
423
+ },
424
+ },
425
+
426
+ {
427
+ name: 'only-the-survivors-of-sigterm-are-sigkilled',
428
+ async run({ assert }) {
429
+ // Escalation order is the safety story: SIGTERM to everything, and
430
+ // SIGKILL only to what is still standing after the grace window.
431
+ const table = processTable({
432
+ 73529: { ppid: ORPHANED, command: FIRESTORE_JAR },
433
+ 74155: { ppid: ORPHANED, command: PUBSUB_JAR },
434
+ }, {
435
+ // The firestore jar ignores SIGTERM, as a JVM mid-write does.
436
+ afterTerm: (pid, rows) => { if (pid !== 73529) rows.delete(pid); },
437
+ });
438
+
439
+ const result = await reapMachineOrphans(table.seams);
440
+
441
+ assert.deepEqual(table.signalled(74155), ['SIGTERM'], 'a process that left needs nothing more');
442
+ assert.deepEqual(table.signalled(73529), ['SIGTERM', 'SIGKILL'], 'the survivor is escalated, in that order');
443
+ assert.deepEqual(result.killed, [73529]);
444
+ assert.deepEqual(result.reaped.map((entry) => entry.pid).sort(), [73529, 74155]);
445
+ },
446
+ },
447
+
448
+ {
449
+ name: 'a-recycled-pid-is-spared-on-the-re-read',
450
+ async run({ assert }) {
451
+ // The grace window is long enough for a number to be freed and handed
452
+ // out again, and there is no apologising to a SIGKILL: the row is read
453
+ // again and must STILL be an orphaned family member.
454
+ const table = processTable({
455
+ 73529: { ppid: ORPHANED, command: FIRESTORE_JAR },
456
+ }, {
457
+ afterTerm: (pid, rows) => rows.set(pid, { ppid: A_TERMINAL, uid: UID, command: 'node /Users/ian/some-other-tool/server.js' }),
458
+ });
459
+
460
+ const result = await reapMachineOrphans(table.seams);
461
+
462
+ assert.deepEqual(table.signalled(73529), ['SIGTERM'], 'the number came back as a stranger — no SIGKILL');
463
+ assert.deepEqual(result.killed, []);
464
+ assert.deepEqual(result.reaped, [], 'nothing of ours went down, so nothing is reported reaped');
465
+ },
466
+ },
467
+
468
+ {
469
+ name: 'the-ports-the-orphans-held-are-waited-on-before-the-boot-continues',
470
+ async run({ assert }) {
471
+ // A SIGKILLed JVM holds its listener after kill() returns, and the
472
+ // allocator probes these very ports moments later.
473
+ const table = processTable({
474
+ 73529: { ppid: ORPHANED, command: FIRESTORE_JAR },
475
+ 74155: { ppid: ORPHANED, command: PUBSUB_JAR },
476
+ });
477
+
478
+ await reapMachineOrphans(table.seams);
479
+
480
+ assert.deepEqual([...new Set(table.probed)].sort((a, b) => a - b), [9150, 9152, 9154], 'every port the reaped rows published, websocket included');
481
+ },
482
+ },
483
+
484
+ {
485
+ name: 'the-sweep-never-signals-itself-or-its-parent',
486
+ async run({ assert }) {
487
+ // No ownership proof stands behind this sweep, so the two numbers it
488
+ // must never signal are named outright: a family-shaped row wearing
489
+ // this very pid would otherwise make the boot kill itself mid-reap.
490
+ const table = processTable({
491
+ [process.pid]: { ppid: ORPHANED, command: FIRESTORE_JAR },
492
+ [process.ppid]: { ppid: ORPHANED, command: WORKER },
493
+ 73529: { ppid: ORPHANED, command: PUBSUB_JAR },
494
+ });
495
+
496
+ // Not vacuous: both rows clear the verdict, so the guard is the only
497
+ // thing standing between this boot and its own SIGTERM.
498
+ assert.equal(isOrphanedEmulatorFamily(row(process.pid, ORPHANED, FIRESTORE_JAR), UID), true);
499
+
500
+ const result = await reapMachineOrphans(table.seams);
501
+
502
+ assert.deepEqual(table.signalled(process.pid), [], 'the running boot is never a candidate');
503
+ assert.deepEqual(table.signalled(process.ppid), [], 'nor is whatever launched it');
504
+ assert.deepEqual(result.reaped.map((entry) => entry.pid), [73529], 'the real orphan beside them still goes');
505
+ },
506
+ },
507
+
508
+ {
509
+ name: 'nothing-orphaned-means-no-signal-and-no-probe',
510
+ async run({ assert }) {
511
+ const table = processTable({
512
+ 37088: { ppid: OUR_BOOT, command: FIRESTORE_JAR },
513
+ 60001: { ppid: ORPHANED, command: RELOAD_WATCHER },
514
+ });
515
+
516
+ const result = await reapMachineOrphans(table.seams);
517
+
518
+ assert.deepEqual(result, { reaped: [], killed: [] });
519
+ assert.deepEqual(table.signals, []);
520
+ assert.deepEqual(table.probed, [], 'a boot with nothing to reap pays for nothing');
521
+ },
522
+ },
523
+
524
+ // ─── what the boot prints ───
525
+
526
+ {
527
+ name: 'the-boot-names-every-pid-it-reaped-and-what-it-was',
528
+ async run({ assert }) {
529
+ const command = new EmulatorCommand({ firebaseProjectPath: os.tmpdir(), argv: {}, options: {} });
530
+ const lines = [];
531
+
532
+ command.log = (message) => lines.push(String(message));
533
+
534
+ const table = processTable({
535
+ 73529: { ppid: ORPHANED, command: FIRESTORE_JAR },
536
+ 74155: { ppid: ORPHANED, command: PUBSUB_JAR },
537
+ 38483: { ppid: ORPHANED, command: WORKER },
538
+ });
539
+
540
+ await command.reapMachineOrphans(table.seams);
541
+
542
+ assert.equal(lines.length, 1, 'one line per boot, not one per pid');
543
+ // Named in table order, which `ps -A` prints by pid.
544
+ assert.match(lines[0], /Reaped 3 orphaned emulator processes left by earlier runs: 38483 functions worker, 73529 firestore jar, 74155 pubsub jar\./);
545
+ },
546
+ },
547
+
548
+ {
549
+ name: 'the-boot-is-silent-when-there-is-nothing-to-reap',
550
+ async run({ assert }) {
551
+ const command = new EmulatorCommand({ firebaseProjectPath: os.tmpdir(), argv: {}, options: {} });
552
+ const lines = [];
553
+
554
+ command.log = (message) => lines.push(String(message));
555
+
556
+ const table = processTable({ 37088: { ppid: OUR_BOOT, command: FIRESTORE_JAR } });
557
+
558
+ await command.reapMachineOrphans(table.seams);
559
+
560
+ assert.deepEqual(lines, [], 'a clean machine says nothing at all');
561
+ },
562
+ },
563
+
564
+ {
565
+ name: 'a-single-reap-and-a-sigkill-read-in-plain-english',
566
+ async run({ assert }) {
567
+ const command = new EmulatorCommand({ firebaseProjectPath: os.tmpdir(), argv: {}, options: {} });
568
+ const lines = [];
569
+
570
+ command.log = (message) => lines.push(String(message));
571
+
572
+ const table = processTable({ 73529: { ppid: ORPHANED, command: FIRESTORE_JAR } }, { afterTerm: () => {} });
573
+
574
+ await command.reapMachineOrphans(table.seams);
575
+
576
+ // The escalation note sits where every sibling reap line puts it, so
577
+ // the pid list is never read as "this one needed SIGKILL".
578
+ assert.match(lines[0], /Reaped 1 orphaned emulator process left by earlier runs \(1 needed SIGKILL\): 73529 firestore jar\./);
579
+ },
580
+ },
581
+
582
+ // ─── the real thing ───
583
+
584
+ {
585
+ name: 'a-real-orphan-is-reaped-and-a-parented-one-survives',
586
+ async run({ assert }) {
587
+ // The whole chain against the real OS: a real `ps -A`, a real row for a
588
+ // real process, and a real SIGTERM. The script is a plain idling node
589
+ // whose path IS the functions-worker shape.
590
+ const dir = jetpack.tmpDir({ prefix: 'omega-orphan-drill-' }).cwd();
591
+ const script = writeWorkerScript(dir);
592
+ const spared = [];
593
+ let live = null;
594
+
595
+ try {
596
+ // The orphan: `sh` exits the moment it has forked, so the node it
597
+ // started reparents to PID 1 with nobody left to tear it down. Its
598
+ // stdio is redirected because execSync waits for the child's pipes to
599
+ // close, and an idling node never closes an inherited one.
600
+ childProcess.execSync(`sh -c 'node ${script} > /dev/null 2>&1 &'`, { stdio: 'ignore' });
601
+
602
+ const orphanPid = await until(() => {
603
+ const found = parseProcessTable(orphans.readProcessTable())
604
+ .filter((entry) => entry.command.includes(script) && entry.ppid === 1)
605
+ .map((entry) => entry.pid);
606
+
607
+ return found.length > 0 ? found[0] : null;
608
+ }, 10000);
609
+
610
+ assert.ok(orphanPid, 'the drill needs a real orphan to reap');
611
+
612
+ // The one it must NOT touch: same command line, live parent (this
613
+ // very process), so the parentage half of the proof spares it.
614
+ live = childProcess.spawn(process.execPath, [script], { stdio: 'ignore' });
615
+
616
+ await until(() => livePidsRunning(script).includes(live.pid), 10000);
617
+
618
+ const result = await reapMachineOrphans({
619
+ graceMs: 2000,
620
+ // Machine-wide is the DESIGN, but a test run signals only what
621
+ // THIS FILE spawned — every one of those runs out of a temp root
622
+ // carrying the drill marker. Anything else the sweep matched on
623
+ // this machine is recorded and left standing.
624
+ kill: (pid, signal) => {
625
+ const live = orphans.readProcessRow(pid);
626
+
627
+ if (!live || !live.command.includes(DRILL_MARKER)) {
628
+ spared.push({ pid: Number(pid), signal: signal });
629
+
630
+ return;
631
+ }
632
+
633
+ process.kill(pid, signal);
634
+ },
635
+ // Our idle node binds nothing, and probing real ports is not this
636
+ // drill's business.
637
+ isFree: async () => true,
638
+ });
639
+
640
+ assert.ok(result.reaped.some((entry) => entry.pid === orphanPid), 'the real orphan is reported reaped');
641
+ assert.equal(result.reaped.find((entry) => entry.pid === orphanPid).label, 'functions worker');
642
+
643
+ const gone = await until(() => !livePidsRunning(script).includes(orphanPid), 5000);
644
+
645
+ assert.ok(gone, 'the real orphan is actually gone');
646
+ assert.ok(livePidsRunning(script).includes(live.pid), 'the same shape with a live parent survives the sweep');
647
+ assert.deepEqual(spared.filter((entry) => entry.pid === live.pid), [], 'the parented one was never even signalled');
648
+ } finally {
649
+ if (live) {
650
+ live.kill();
651
+ }
652
+
653
+ jetpack.remove(dir);
654
+ }
655
+ },
656
+ },
657
+ ],
658
+ });