Already verified on the branch
These passed against commit 265d424b74. Re-run them only if the branch changes.
build-qaandbuild-prodcompile. Initial bundle 12.44 MB raw, 2.00 MB transfer. Angular 20 was 12.34 MB and 1.99 MB. The 10 MB budget warning is pre-existing.prerender-all-qacompletes. 1025 routes prerendered, sitemap and robots.txt written, exit 0.- Express SSR renders real markup.
/,/creators?key=x,/topfan-vs-patreon,/pricing,/creators/loginreturn 200 with correct titles./topfan/?key=xis 301, garbage paths are 404. No runtime error in server output. - Serverless target compiles.
ng run TopFans:serverless:qaexits 0. Nothing inbuildspec-*.ymlordeployment_script/uses this target. - White-label generation and one tenant build.
generate-white-label-qaproduced 25 projects;perfectcrewbuilt and served through SSR with its own title. - Clean
npm ciapplies the patch.postinstallrunspatch-packageand ngx-intl-tel-input gets the ngx-bootstrap 21 fix.
Use the QA artifact
Verify against the optimized QA build served by the Express SSR process, the same path pm2 runs in production.
npm ci node --max_old_space_size=10048 ./node_modules/@angular/cli/bin/ng build --configuration=qa --prerender=false PORT=4000 node dist/TopFans/server/server.mjs
Local Node 24.5 prints an npm 12 warning and cannot run ng update; neither matters for this checklist. CI builds on Node 20 with npm 10.8.1 and .npmrc already sets legacy-peer-deps=true.
Release fingerprint
- AngularCore 21.2.23, CLI and SSR 21.2.24, Material and CDK 21.2.14
- TypeScript5.9.3, zone.js 0.15.1
- Firebase
firebase11.10.0 Web SDK,@angular/fireremoved - ngx-bootstrap21.2.2, no more
Module.forRoot() - PrimeNG21.1.10, ng-select 21.8.2, ng-bootstrap 20.0.0, ngrx 21.1.1
- FontAwesomeangular-fontawesome 4.0.0, svg-core and icon packs 7.3.1
- Patchedngx-intl-tel-input 15.0.0 via
patches/ - Architecture heldNgModules, zone.js via
provideZoneChangeDetection()inAppModule,*ngIftemplates
Firebase without AngularFire
FirebaseService now calls the Web SDK directly. Initialization is lazy, browser-only, and gated on each product's isSupported(). Remote Config updates run inside NgZone.run(). A returning user with a stored token previously never attached onMessage; that is fixed and must be checked, not assumed.
- Grant notification permission on a fresh profile. A token is issued, the register-token API call succeeds, and the console shows no Firebase initialization error.
- Send a test push while the tab is in the foreground. Exactly one toast or in-app notification appears. Two means the listener attached twice.
- Reload as the same user. The stored token is reused, no second registration call fires, and a foreground push still surfaces once.
- Background push. With the tab hidden, the service worker (
firebase-messaging-sw.js) shows the system notification and clicking it opens the app. - Remote Config drives the UI. On a page that reads
iosPayButtonsConfig, changing the remote value and reloading changes what renders. No manual click should be needed for the view to update. - Unsupported browser path. In Safari private mode or with notifications blocked, the app loads and no unhandled rejection appears.
- SSR stays clean. Server output has no
IndexedDB,window is not defined, or Firebase provider errors while requesting pages.
ngx-bootstrap 21 and the phone input
Two things changed under the application. Module.forRoot() is gone, so every module is imported bare. BsDropdownDirective.isOpen is a getter now, so code that assigned it uses toggle() or hide(). ngx-intl-tel-input still calls forRoot() and is patched.
Dropdowns toggled from code
- Home discover filter. Choosing a filter closes the dropdown and reloads the feed.
- Forum topic filter. Same behaviour on the forum page.
- Collaborators filter. Selecting a filter closes the menu and the list resets to page one.
- Post detail actions menu. After saving an edit the menu closes.
- Post-submit type menu. Choosing a post type closes the menu.
- Info icon popovers. Opening one closes the previously open one.
Phone number input
- Signup and personal info. The country dropdown opens, searches, and selects; the dial code updates.
- Pro contact form. Same on the marketing pro contact form layout.
- Lazy vendor CSS. Flags and the dropdown are styled after the first render; there is no unstyled flash on a cold load.
Modals, datepickers, tabs, accordions
- Alert modal variants. Confirm, cancel, cross button, and dynamic class all behave;
page-hiddenis added on open and removed on close. - Datepicker and daterange picker. Open, choose, and clear on the dynamic-form datepicker and any scheduling flow.
- Tabs and accordion. Insights tabs and pricing accordions switch content.
- Typeahead. The one typeahead usage still filters and selects.
Host listeners that lost their $event argument
Angular 21 type-checks host bindings. 49 @HostListener('window:scroll' | 'window:resize', ['$event']) decorators sat on handlers that took no parameters and were changed to pass nothing. Behaviour should be identical; confirm the listeners still fire.
- Infinite scroll loads more. Home feed, creators list, collaborators, block users, basic see-more, merchandise store, VIP offers.
- Resize handlers. Home, merchandise store, portfolio, sponsored ad, skyscraper preview modal, VIP offer list, advertisement details switch layout when the window crosses the mobile breakpoint.
- Table sticky behaviour. The shared table component reacts to window scroll.
- Click-outside handlers. Any component that closes a menu on document click still does so.
Library and product smoke matrix
| Area | Pass when |
|---|---|
| Public pages | Home, creator login, creator profile, a comparison page, and a marketing page paint without an empty layout or console exception, and stay populated after hydration. |
| FontAwesome 7 | Brand and solid icons render across header, footer, social links, and buttons. A missing icon renders as a blank box and is easy to miss on dark backgrounds. |
| PrimeNG 21 | Custom MultiSelect opens, filters, selects, removes chips, and uses its footer. Image overlay and reels carousels navigate. Checkboxes and radio styling hold. |
| ng-select 21 | A representative select opens, filters, chooses, and clears with the existing theme. |
| ng-bootstrap 20 | Any NgbModal and NgbActiveModal usage opens and closes without an injector error. |
| Voice circle recorder | Recording starts, the visualizer animates with volume, and stopping produces a playable clip. This component's buffer type changed for TypeScript 5.9. |
| ngx-mask, ngx-toastr | Masked inputs format as typed; toasts appear and dismiss. |
| Owl carousel | Marketing carousels move both directions and respect breakpoints. |
| QR scanner, device detector | The zxing scanner opens the camera where used; mobile and desktop detection still branch correctly. |
| Audio player | An audio post loads, plays, pauses, seeks, and closes. This package still peers on Material 17. |
| IVS live video | A live stream page loads the player. ng-ivs still peers on Angular 12. |
| Wallet and Stripe | Balance and payout forms render. Test checkout and Connect complete against a safe environment. |
| Messages | Conversation list and thread load; sending text and opening attachment UI do not throw. |
| ngrx store | State-driven views update after actions; no Store provider error at startup. |
| HMR | A template or style edit updates under ng serve. |
Build and render one white-label tenant
- Generate in a disposable working tree.
generate-white-label-qarewritesangular.json,package.json,src/index.html, and the white-label constants. Do not commit any of it. - Build a tenant with the QA configuration.
WEBSITE=<tenant url> ng build <tenant> --configuration=qa --prerender=falsewith the 10 GB heap. - Serve
dist/<tenant>/server/server.mjs. The home page title is the tenant's, not TopFan's, and server output has no runtime error. - Repeat the Firebase and phone input checks on the tenant. Tenant Firebase config and the patched phone input are the two places a tenant can diverge from TopFans.
- Branding survives hydration. Logo, colors, login, and cover-page behaviour stay tenant-specific after JavaScript starts.
Known noise and deliberate holds
- Lint still fails, as it did before. 890 findings remain from rules already in the v19 recommended set:
prefer-standalone541, templateeqeqeq67,no-output-on-prefix50, and others. Two rules new in v20 recommended,prefer-injectandtemplate/prefer-control-flow, are turned off with a comment because they are separate migrations. - Serverless
main.jscannot be required in bare Node. It fails inside video.js's auto-setup, which needswindow.setTimeout. This is unrelated to Angular and the target is not deployed anywhere. - Peer warnings from
ng update.ng-ivspeers Angular 12,ngx-audio-playerpeers Material 17,primengpeered CDK 20 before its own bump. All compiled; both players are on the smoke matrix. - Control-flow migration was reverted.
ng updaterewrote 659 templates to@ifand introduced four template errors. Templates stay on*ngIf. generate-static-routeslogs 400s. The QA backend rejects VIP-offer and store lookups for some creators. This is backend data, not a build failure, and the run still exits 0.- Sass deprecation warnings dropped from 288 to 226. Still noisy, still not blocking.
- Local
environment.tsand regenerated marketing constants are not committed. They are the developer's local QA switch and prefetch output respectively.
Stop the release if any of these fail
- A foreground push produces zero or two notifications, or a fresh user cannot register a token.
- The phone country dropdown does not open on signup, or the browser console shows
forRoot is not a function. - A dropdown toggled from code stays open, or infinite scroll stops loading on the home feed.
- A permitted host receives an empty
<app-root>or server output reports an Angular runtime error. - Wallet checkout, Stripe Connect, audio playback, live video, or a representative white-label tenant regresses.