TreeMap
A free, open-source disk-space visualizer. Squarified treemap, duplicate hunting, disk-full forecasting, and cleanup that only ever moves files to the Trash.
Calculate your Canada Express Entry CRS score, track live IRCC draws, and get push alerts for new rounds. Free, private, and on-device.
ProductivityTypeScript · JavaScriptGitHub @BalwinderCaOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
IRCC Tracker for Canada immigration applicants. Calculate your CRS score, follow live IRCC draws, view trends, and get push alerts when new draws are published.
No accounts. No self-hosted backend. Profile data stays on your device.Read the full README ↗IRCC Tracker for Canada immigration applicants. Calculate your CRS score, follow live IRCC draws, view trends, and get push alerts when new draws are published.
No accounts. No self-hosted backend. Profile data stays on your device.
All calculators are estimates for planning — each screen points to the official IRCC/provincial tool. Profile data stays on-device.
| Feature | Source |
|---|---|
| Draws (app) | Mobile app → IRCC JSON feed |
| Draws (worker) | Cloudflare Worker → GitHub Actions mirror (data/latest-draw.json)¹ |
| Processing times | Mobile app → GitHub Actions mirror (data/processing-times.json), 7-day cache |
| EE pool / levels | Mobile app → GitHub Actions mirror (data/ee-pool.json), 7-day cache |
| Trends & analytics | Computed on-device from cached draws + EE pool data |
| Push alerts | Cloudflare Worker → Expo Push API |
| Premium unlock | Google Play Billing (one-time managed product: crs_pulse.analytics_unlock) |
¹ canada.ca (Akamai) rejects Cloudflare Worker egress with HTTP 520, so the worker reads the latest draw from a GitHub-hosted mirror instead of IRCC directly.
mobile/ Expo React Native app
workers/push/ Cloudflare Worker (KV + cron)
data/ GitHub Actions mirrors (draws, processing times, EE pool)
docs/ Privacy policy
npm install -g eas-clicd mobile
npm install
cp .env.example .env.local
npm start
Set in mobile/.env.local:
EXPO_PUBLIC_PUSH_URL=https://crs-pulse-push.balwinderxcode.workers.dev
Set your EAS project ID in mobile/.env.local (or EAS secrets):
EAS_PROJECT_ID=your-eas-project-id
Privacy policy — rendered from docs/PRIVACY_POLICY.md and hosted on Vercel (see web/). Use this URL in the stores:
https://www.crspulse.com/privacy
Run on a device or simulator:
npm run ios
Push notifications require a physical iPhone (not simulator).
The worker stores anonymous Expo push tokens and notifies subscribers when IRCC publishes a new draw.
cd workers/push
npm install
npx wrangler login
# Create KV namespaces (one-time)
npx wrangler kv namespace create TOKENS_KV
npx wrangler kv namespace create TOKENS_KV --preview
Paste the returned IDs into workers/push/wrangler.toml, then:
npm run deploy
# Required production secrets (one-time)
npx wrangler secret put PUSH_API_SECRET
npx wrangler secret put SYNC_SECRET
# Optional: ops alerting — the cron emails ALERT_EMAIL once if the draw mirror
# goes >30 days stale (e.g. the GitHub Action stopped). Needs RESEND_API_KEY +
# EMAIL_FROM set too; no-op if unset.
npx wrangler secret put ALERT_EMAIL
Set the same PUSH_API_SECRET value as EXPO_PUBLIC_PUSH_API_KEY in EAS secrets / mobile/.env.local.
Verify:
curl https://crs-pulse-push.balwinderxcode.workers.dev/health
| Method | Path | Auth | Body |
|---|---|---|---|
| GET | /health | None | — |
| POST | /register | Bearer PUSH_API_SECRET | { "token": "ExponentPushToken[...]", "platform": "ios" | "android" } |
| DELETE | /revoke | Bearer PUSH_API_SECRET | { "token": "ExponentPushToken[...]" } |
| POST | /sync | Bearer SYNC_SECRET | — |
Cron runs every 15 minutes after deploy.
Local dev: npm run dev → http://localhost:8787
mobile/)| Command | Description |
|---|---|
npm start | Start Expo dev server |
npm run ios | Run on iOS simulator |
npm run android | Run on Android emulator |
npm test | Run Jest tests |
npm run test:coverage | Run Jest with coverage thresholds |
npm run type-check | TypeScript check |
npm run lint | ESLint |
workers/push/)| Command | Description |
|---|---|
npm run dev | Local worker at :8787 |
npm run deploy | Deploy to Cloudflare |
npm run type-check | TypeScript check |
npm install -g eas-clihttps://www.crspulse.com/privacycd mobile
npm install
cp .env.example .env.local
eas login
eas init # links project, sets EAS_PROJECT_ID
Set in mobile/.env.local:
EAS_PROJECT_ID=your-eas-project-id
EXPO_PUBLIC_PUSH_URL=https://crs-pulse-push.balwinderxcode.workers.dev
com.crspulse.apphttps://www.crspulse.com/privacyITSAppUsesNonExemptEncryption is false)EAS manages APNs credentials. On first iOS production build:
eas credentials --platform ios
Choose Push Notifications: Manage your Apple Push Notifications Key and let EAS create/upload the key.
cd mobile
npm run build:ios # eas build --platform ios --profile production
npm run submit:ios # eas submit --platform ios --profile production --latest
First eas submit will prompt for ascAppId and appleTeamId if not in eas.json.
After the app is live, add to .env.local for the in-app review link:
EXPO_PUBLIC_APP_STORE_ID=1234567890
Production builds with distribution: store go to App Store Connect automatically after submit. Add internal/external testers in TestFlight before submitting for App Review.
EXPO_PUBLIC_PUSH_URL is set in mobile/eas.json for preview and production profiles.
See docs/RELEASE_CHECKLIST.md for the full pre-flight list (push key, FCM V1, data-safety form). Highlights:
Production Android builds are app bundles signed with the upload keystore from local credentials (mobile/credentials.json → credentials/upload-keystore.jks, both gitignored). EAS injects that signing config — a plain gradlew bundleRelease is debug-signed and Play rejects it, so always build through EAS:
cd mobile
npm run build:android # cloud build (recommended)
# or a local build (needs the Android SDK on PATH):
export ANDROID_HOME=$HOME/Library/Android/sdk
eas build --local --platform android --profile production --output build/app.aab
versionCode uses appVersionSource: "remote" + autoIncrement — EAS owns the counter and ignores the value in app.config.js; each build bumps it.
The free tier ships Google AdMob, so the app declares com.google.android.gms.permission.AD_ID (in app.config.js) and the Play Console Advertising ID declaration must be "Yes." ⚠️ The "an active artifact doesn't include AD_ID" error checks every active artifact across all tracks — an old build made before the permission was added keeps tripping it until you roll a compliant build over it / retire that version code. It is not about your newest bundle.
eas submit --platform android --profile production --latest # cloud build
eas submit --platform android --profile production --path build/app.aab # local build
eas submit uses mobile/google-play-service-account.json (gitignored, track internal). That service account must be granted access in Play Console → Users and permissions with at least "Release apps to testing tracks", or submit fails with "the service account is missing the necessary permissions." The first release of a new app must be created manually in the console UI. With permission granted you can also drive a track rollout directly via the Google Play Developer API (edits.insert → tracks.update → edits.commit).
PUSH_API_SECRET must exactly equal the build's EXPO_PUBLIC_PUSH_API_KEY (sent as a Bearer token; mismatch → HTTP 401, surfaced as this message). The key comes from the EAS production environment at build time — keep it in sync with the worker secret.npm run android) to confirm with Google test ads, which always fill.express-entry-calculator/
├── mobile/
│ ├── src/
│ │ ├── features/ # home, dashboard, calculators, fsw, bcpnp, sinp,
│ │ │ # draws, analytics, timeline, tracker, checklist,
│ │ │ # notifications, paywall, profile, onboarding, faq, support
│ │ ├── store/ # Zustand stores (profile, draws, timeline, application,
│ │ │ # premium, processingTimes, eePool)
│ │ ├── services/ # pushService, iapService, errorReporter
│ │ ├── components/ # shared UI (Card, Button, Badge, Input, SkeletonCard, etc.)
│ │ ├── hooks/ # useColors, useAccentColor, useTabBarLayout, etc.
│ │ ├── theme/ # colors, spacing, typography, shadows
│ │ ├── navigation/ # RootNavigator, MainNavigator
│ │ ├── types/ # TypeScript types (Draw, Category, RootStackParamList, etc.)
│ │ └── constants/ # STORAGE_KEYS, IAP_PRODUCTS, CATEGORY_LABELS, etc.
│ └── app.config.js
├── workers/push/
│ └── src/
│ ├── index.ts # HTTP handlers + scheduled cron entry point
│ ├── tokenStore.ts # KV token storage (register/revoke/list/migrate)
│ ├── expoValidate.ts # Token validation against Expo API
│ └── expoReceipts.ts # Async delivery receipt polling
├── data/
│ ├── latest-draw.json # GitHub Actions mirror → worker reads this
│ ├── processing-times.json # IRCC processing times mirror
│ └── ee-pool.json # Express Entry pool + levels plan mirror
└── docs/
└── PRIVACY_POLICY.md
All CRS inputs and profile settings are stored locally on the device. The push worker only stores anonymous Expo device tokens — no personal immigration data is sent to any server. The in-app purchase is processed entirely by Google Play; no payment data reaches us. The free version shows Google AdMob banner ads (which may collect device/ad identifiers); the one-time Premium unlock removes them.
Private project.