TreeMap
무료 오픈소스 디스크 사용량 시각화 도구로, 사각형 트리맵·중복 파일 탐색·디스크 가득 예측·정리 시 파일을 휴지통으로만 이동한다
캐나다 익스프레스 엔트리 CRS 점수 계산·IRCC 추첨 실시간 확인·새 라운드 알림 제공, 무료·프라이버시·디바이스 내 처리
Calculate your Canada Express Entry CRS score, track live IRCC draws, and get push alerts for new rounds. Free, private, and on-device.
생산성TypeScript · JavaScriptGitHub @BalwinderCa온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기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.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.