Cloudsteading
Calends overview: monthly subscription spend, subscription bubbles, renewal timeline and upcoming charges (fictional demo data)
Calends overview: monthly subscription spend, subscription bubbles, renewal timeline and upcoming charges (fictional demo data)

Calends

A private ledger for subscriptions, renewals and shared payments

Calends is a self-hosted Rocket Money alternative built on Cloudflare (D1, Workers). Free tier eligible within limits. Inspect the source and license in the linked repository.

Source & license

Upstream license: MIT

License TL;DR

You can use it, change it, self-host it and sell it. Keep the original copyright and license notice with copies of the code. You don’t have to publish your changes. The authors don’t promise it will work.

Explain MIT in plain English →

Summary of the main license. Separate packages and assets can have different terms.

Inspect repository ↗Read this project’s actual license ↗

Repository owner

@imetn

See the upstream repository for the original creator and contributors.

Maintain this project? Maintainer verification →

Cloudflare hosting

Free tier eligible within limits

One Worker with D1 and a 15-minute cron fits the Workers Free plan for a single owner. Optional email reminders need Cloudflare Email Sending, which requires Workers Paid for arbitrary recipients; webhook and chat-app reminders do not.

Hosting requirements
  • Email reminders are commented out by default; enabling them needs a verified sending domain and Workers Paid.
  • A separate docker/ Wrangler file supports a self-hosted container path; the catalog describes the Cloudflare path.
  • The public demo resets daily and disables external notifications.
  • Source and configuration review establishes a deployment path and conditional costs; this candidate was not executed or load-tested.
  • Official Cloudflare pricing pages saved 31 Oct 2026 (Markdown editions of developers.cloudflare.com pricing documentation).
Check current pricing ↗
Review findings & limitations
  • Email reminders are commented out by default; enabling them needs a verified sending domain and Workers Paid.
  • A separate docker/ Wrangler file supports a self-hosted container path; the catalog describes the Cloudflare path.
  • The public demo resets daily and disables external notifications.
  • Competitor links describe the stated workflow overlap, not feature parity or a verified price saving.
  • Source review at the pinned commit; deployment, workload CPU and application security have not been independently tested.
Sources checked 31/10/2026

Repository snapshot: e794985. Hosting eligibility reflects the deployment documentation and listed assumptions.

  • rocket-money ↗

    - **Flexible billing** — monthly, yearly, custom cycles and lifetime purchases, with trials, intro prices, bonus periods, pauses, advance charging and cancellation deadlines. - **Billing history** — keep price, plan and region changes, gaps and overlapping subscriptions. See total spent, actual monthly cost and every charge. - **Transfers and split payments** — count second-hand takeovers and resales in net cost, and split a charge across multiple payment methods. - **Multiple currencies** — historical exchange rates for past charges, current rates for forecasts, and your choice of display currency. - **Balances and shared plans** — track prepaid credit and gift cards, estimate when balances run out, and manage payments for friends. Shared plans have dated collection rules, member overrides, per-period receipts with skipped-period collection, prepayments and waivers, overdue tracking and exit refunds; the member sheet uses a monthly period grid with receipt details on click, prepayments and waivers, while people have private avatars, custom colors and a separate account for each plan. - **Reports and calendar** — spending trends, cash vs. accrual, prepaid value, cancellation savings, subscription checkups and a shareable year in review. Includes month/year calendars and a private ICS feed. - **Search** — find Chinese names by full pinyin, initials or mixed syllables in the subscription list, command palette and pickers. - **Renewal reminders** — email, Telegram, Lark/Feishu, DingTalk, WeCom, Slack, Discord, Bark, ntfy, ServerChan and custom webhooks. - **AI access** — query

  • workers ↗

    "main": "worker/index.ts", "compatibility_date": "2026-10-01", "compatibility_flags": ["nodejs_compat"], "assets": { "directory": "./dist", "binding": "ASSETS", "not_found_handling": "single-page-application", "run_worker_first": true, }, "d1_databases": [{ "binding": "DB",

  • d1 ↗

    "d1_databases": [{ "binding": "DB", "database_name": "calends", "migrations_dir": "worker/migrations" }], // 每 15 分钟检查本地提醒时刻;维护任务仍只在整点执行 "triggers": { "crons": ["*/15 * * * *"] }, // 可选:邮件提醒需要 Cloudflare Email Sending,取消注释并换成你在 Cloudflare 验证过的域名 // "send_email": [{ "name": "EMAIL", "allowed_

  • free-tier-eligible ↗

    | **Free** | 100,000 per day | No charge for duration | 10 milliseconds of CPU time per invocation |

  • free-tier-eligible ↗

    | Rows read | 5 million / day | First 25 billion / month included + $0.001 / million rows |

  • MIT ↗

    MIT License Copyright (c) 2026 Calends contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR I

  • architecture ↗

    "triggers": { "crons": ["*/15 * * * *"] }

  • architecture ↗

    "d1_databases": [{ "binding": "DB", "database_name": "calends", "migrations_dir": "worker/migrations" }]

  • architecture ↗

    "main": "worker/index.ts", "compatibility_date": "2026-10-01", "compatibility_flags": ["nodejs_compat"], "assets": { "directory": "./dist", "binding": "ASSETS", "not_found_handling": "single-page-application", "run_worker_first": true, }, "d1_databases": [{ "binding": "DB",

  • architecture ↗

    "d1_databases": [{ "binding": "DB", "database_name": "calends", "migrations_dir": "worker/migrations" }], // 每 15 分钟检查本地提醒时刻;维护任务仍只在整点执行 "triggers": { "crons": ["*/15 * * * *"] }, // 可选:邮件提醒需要 Cloudflare Email Sending,取消注释并换成你在 Cloudflare 验证过的域名 // "send_email": [{ "name": "EMAIL", "allowed_

Upstream screenshot · imetn/calends repository contributors ↗. Depicts the upstream project. We have not deployed and tested a fresh installation here.

What it can replace

Compare the workflow you need. These mappings describe overlap; full feature parity requires a separate comparison.

Rocket Money logoRocket Money ↗

Manual subscription tracking, renewal reminders and shared-payment records for one owner; bank-account syncing, bill negotiation and automatic cancellation are not included

See supporting source ↗
Workflow overlap
Pricing not verified
→ D1 + Workers

How it works

The shape of Calends on Cloudflare, and how it stacks up against the rented tools it replaces.

Architecture

Diagram of deployment declarations at the reviewed commit. Each app has its own entrypoint; declared resources do not prove runtime calls. Follow file and line sources below.

View upstream source ↗
Public interface
Configured entry points2
calends
docker/wrangler.docker.jsonc
calends
wrangler.jsonc
↓
App
calends
entry
Cloudflare Workers
Entrypoint: worker/index.tsConfigured cron (UTC): */15 * * * *
calends
entry
Cloudflare Workers
Entrypoint: worker/index.tsConfigured cron (UTC): */15 * * * *
↓

Configuration and workflow sources

Reviewed commit e794985ae8d3. This configuration evidence comes from reading source files. Execution checks, when available, appear in the project's runtime review.

Partial source coverage: 3 files outside collection bounds; 0 collection or parsing issues. Dynamic imports and generated entrypoints may need manual review.

Deployment configuration · 2 files
docker/wrangler.docker.jsonc ↗

Cloudflare Workers · compatibility 2026-10-01

calends · default

Entrypoint: worker/index.ts

Static assets: ./dist · single-page-application · Worker first: true

Cron triggers (UTC): */15 * * * *

  • DB → D1
  • ASSETS → Static assets
wrangler.jsonc ↗

Cloudflare Workers · compatibility 2026-10-01

calends · default

Entrypoint: worker/index.ts

Static assets: ./dist · single-page-application · Worker first: true

Cron triggers (UTC): */15 * * * *

  • DB → D1
  • ASSETS → Static assets

Named environments are separate deployments. Bindings are shown only where declared. Configured routes are URL patterns, not verified application endpoints.

Runtime source · handlers, binding usage and workflow steps

Observed TypeScript/JavaScript declarations from Worker entrypoints and resolved relative imports. Calls and workflow steps may run conditionally; their listed order is not a proven end-to-end request flow. Router declarations may be mounted under a prefix or may not be registered. This shows code wiring, not a successful deployment or runtime test. Dynamic wiring, aliases and generated code may not resolve.

worker/index.ts ↗
  • L479 · fetch handler exported · calls withLocaleScope, handleFetch
  • L483 · scheduled handler exported · calls withLocaleScope, handleScheduled
  • L87 · invalidIn calls (conditional paths may differ): validSub, t
  • L96 · cleanPerson calls (conditional paths may differ): raw.name.trim, v.trim, s, validateAvatar, personColor, toISOString
  • L117 · methodError calls (conditional paths may differ): t, validateMethod
  • L127 · api calls (conditional paths may differ): login, logout, authStatus, path.startsWith, sameOrigin, bad, setup, passkeyLoginOptions, passkeyLoginVerify, isAuthed, getSettings, t, json, maskForPublic, loadState, getRev, fetchPublic, searchParams.get, res.headers.get, type.startsWith
  • L469 · scheduledNotifications calls (conditional paths may differ): loadState, applyLocale, settingsLocale, siteUrl, runScheduled, console.log, log.join
  • L488 · handleFetch calls (conditional paths may differ): normalizeBasePath, stripBase, some, env.OLD_HOSTS.split, h.trim, url.pathname.startsWith, Response.redirect, siteUrl, ensureMigrated, getSettings, applyLocale, settingsLocale, requestLocale, api, req.headers.get, mcp, url.pathname.match, constantEqual, buildIcs, loadState
  • L543 · handleScheduled calls (conditional paths may differ): ensureMigrated, getUTCMinutes, scheduledNotifications, getFx, Date.now, Date.parse, ctx.waitUntil, catch, refreshFx, console.error

Environment references: env.DEV_BYPASS_AUTH · env.DB · env.SITE_URL · env.BASE_PATH · env.OLD_HOSTS · env.ASSETS

shared/sub-write.ts ↗
  • L9 · validSub calls (conditional paths may differ): t, subError
  • L18 · prepareSubWrite calls (conditional paths may differ): reconcileRenewalRules, sub.share.members.map, updateMemberLeave, validSub, subRefsError, statusChangeError
shared/avatar.ts ↗
  • L12 · avatarCrop calls (conditional paths may differ): Number.isInteger, fail, Math.min
  • L19 · validateAvatar calls (conditional paths may differ): Math.ceil, fail, exec, atob, btoa, Uint8Array.from, c.charCodeAt, raw.slice, str, view.getUint32, includes, view.getUint16
  • L105 · validatePeopleAvatars calls (conditional paths may differ): validateAvatar, encode, JSON.stringify, t
  • L116 · prepareAvatarThumbnail calls (conditional paths may differ): value.split, Uint8Array.from, atob, c.charCodeAt, String.fromCharCode, bytes.subarray, chunks.push, bytes.slice, view.getUint32, fail, kind, includes, chunks.reduce, out.set, setUint32, validateAvatar, btoa
shared/base.ts ↗
  • L9 · withBase calls (conditional paths may differ): normalizeBasePath, path.startsWith
  • L16 · stripBase calls (conditional paths may differ): normalizeBasePath, path.startsWith, path.slice
  • L22 · siteUrl calls (conditional paths may differ): value.replace, normalizeBasePath, site.endsWith
shared/billing.ts ↗
  • L32 · parseDay calls (conditional paths may differ): map, s.split, Date.UTC
  • L37 · fmtDay calls (conditional paths may differ): slice, toISOString
  • L41 · addDays calls (conditional paths may differ): fmtDay, parseDay
  • L45 · diffDays calls (conditional paths may differ): Math.round, parseDay
  • L51 · calendarSpan calls (conditional paths may differ): addMonths, Math.floor, Math.max, diffDays
  • L57 · addMonths calls (conditional paths may differ): map, s.split, Math.floor, getUTCDate, Date.UTC, fmtDay, Math.min
  • L66 · todayIn calls (conditional paths may differ): Date.now, format, dateFormat, fmtDay
  • L75 · nthCharge calls (conditional paths may differ): addDays, addMonths
  • L106 · cycleLabel calls (conditional paths may differ): t
  • L116 · perLabel calls (conditional paths may differ): t
  • L126 · cycleShort calls (conditional paths may differ): t
  • L133 · receivableError calls (conditional paths may differ): Array.isArray, list, t, validDay, String, x.periods.every, bad, Number.isFinite, CURRENCY_CODE.test
  • L144 · subError calls (conditional paths may differ): t, Number.isFinite, validDay, transferError, Array.isArray, m.feeHistory.some, CURRENCY_CODE.test, Number.isInteger, memberSince, includes, m.periodDues.every, receivableError, m.restarts.every, s.proxy.charged.some, phaseSubs, some, stopError, paymentError
  • L223 · shapeIssues calls (conditional paths may differ): out.push, JSON.stringify, validDay, isDay, add, t, day, Array.isArray, s.tags.some, Number.isFinite, CURRENCY_CODE.test, String, includes, Number.isInteger
  • L269 · newShapeError calls (conditional paths may differ): map, shapeIssues, find, known.has
  • L280 · phaseSubs calls (conditional paths may differ): sort, s.phases.slice, a.start.localeCompare, filter, sorted.filter, kept.map, addDays
  • L326 · isGap calls (conditional paths may differ): diffDays
  • L331 · phaseLanes calls (conditional paths may differ): vs.map, laneEnd.forEach
  • L346 · coveredUntil calls (conditional paths may differ): vs.slice
  • L356 · servedTime calls (conditional paths may differ): phaseSubs, vs.forEach, coveredUntil, addDays, gaps.push, diffDays
  • L377 · activePhases calls (conditional paths may differ): filter, phaseSubs
  • L383 · phaseAt calls (conditional paths may differ): pop, activePhases
  • L388 · currentPhase calls (conditional paths may differ): phaseSubs, phaseAt, pop, vs.filter
  • L406 · upcomingPhase calls (conditional paths may differ): find, phaseSubs
  • L411 · effectiveStatus calls (conditional paths may differ): phaseSubs, phaseAt, effectiveStatus, vs.findIndex, vs.some, isLive
  • L440 · priceAt calls (conditional paths may differ): priceAt, currentPhase, sort, slice, a.from.localeCompare
  • L476 · chargeSources calls (conditional paths may differ): chargeMethodId, c.funding.reduce, c.funding.map
  • L483 · fundingError calls (conditional paths may differ): Array.isArray, parts.some, Number.isFinite, Math.sign, parts.map, t, Math.abs, parts.reduce
  • L502 · renewalDate calls (conditional paths may differ): addDays, history.slice
  • L532 · maxChargeLead calls (conditional paths may differ): Math.max, map
  • L537 · firstCycle calls (conditional paths may differ): Math.floor, diffDays, cycleDays
  • L542 · renewalAnchor calls (conditional paths may differ): billingAnchor, firstCycle, Math.max, Math.round, diffDays, cycleDays, Math.ceil, maxChargeLead, nthCharge, renewalDate, addDays, renewalRuleAt
  • L554 · schedule calls (conditional paths may differ): billingAnchor, renewalAnchor, firstCycle, Math.max, Math.floor, diffDays, addDays, cycleDays, maxChargeLead, nthCharge, renewalDate, out.push, priceAt
  • L589 · chargesBetween calls (conditional paths may differ): migrateRenewalRules, sort, flatMap, phaseSubs, chargesBetween, a.date.localeCompare, map, recorded.has, filter, rawCharges, reduce, raw.filter, paid.has, schedule, renewalDate, addDays, keep.concat, after.filter, known, x.date.localeCompare
  • L609 · rawCharges calls (conditional paths may differ): sort, slice, a.date.localeCompare, pays.filter, map, renewals.at, schedule, filter, addDays, covered.has, earlier.concat, priceAt, billingAnchor, diffDays, cycleDays, before.concat
  • L652 · migrateRenewalRules calls (conditional paths may differ): validDay, filter, chargesBetween, addDays, candidates.at, diffDays, s.phases.map
  • L665 · preserveRenewalHistory calls (conditional paths may differ): currentPhase, migrateRenewalRules, filter
  • L685 · renewalDates calls (conditional paths may differ): migrateRenewalRules, phaseSubs, Math.max, phases.map, cycleDays, addDays, Math.ceil, find, chargesBetween, renewalRuleAt, chargeServicePeriod
  • L702 · nonrenewalDate calls (conditional paths may differ): renewalDates, billingPeriod, addDays
  • L710 · chargeServicePeriod calls (conditional paths may differ): renewalAnchor, firstCycle, Math.max, Math.round, diffDays, cycleDays, maxChargeLead, billingAnchor, Math.ceil, nthCharge, renewalDate, addDays, extend, renewalRuleAt
shared/types.ts ↗
  • L483 · channelsOf calls (conditional paths may differ): t
shared/people.ts ↗
  • L17 · normContact calls (conditional paths may differ): toLowerCase, trim, normalize, raw.replace
  • L52 · refsOf calls (conditional paths may differ): out.push
  • L73 · rewrite calls (conditional paths may differ): s.share.members.map, fn, members.some, JSON.stringify, changed.push
  • L105 · linkPeople calls (conditional paths may differ): people.map, byName.has, byName.set, add, byName.get, personKey, byKey.has, byKey.set, addName, normName, toString, hash, ids.has, ids.add, created.push, info.name.trim, colorFor, filter, refsOf, refInfo
  • L168 · personLink calls (conditional paths may differ): ref.name.trim, people.find, colorFor, create, personKey, people.filter, normName, normContact, same.find, people.map, toString, hash, ids.has
  • L200 · syncPerson calls (conditional paths may differ): rewrite, refInfo
  • L211 · mergePeople calls (conditional paths may differ): people.find, rewrite, refInfo, subs.map, moved.find, syncPerson, map, people.filter, filter, all.map, synced.find, ids.has
  • L222 · personOf calls (conditional paths may differ): refInfo
  • L256 · shareItem calls (conditional paths may differ): dues.find, memberCashFlows, Number.isFinite, toBaseOrNaN, flows.reduce, memberFlowValue, reduce, flows.filter, memberPaidThrough, collectCurrencyOf, memberAboard, includes, effectiveStatus
  • L273 · proxyItem calls (conditional paths may differ): proxyRows, rows.filter, open.reduce, done.reduce, proxyTotals, isStopped, effectiveStatus
  • L293 · peopleLedger calls (conditional paths may differ): book.set, refsOf, personOf, normName, personKey, book.has, dues.has, dues.set, memberDues, collectCurrencyOf, shareItem, dues.get, proxyItem, book.get, l.items.push, book.values, l.items.sort, Number, a.sub.name.localeCompare, sort
  • L320 · peopleListRows calls (conditional paths may differ): a.person.name.localeCompare, sort, book.filter, Number.isFinite, byName, Number, a.lastPaid.localeCompare, localeCompare
  • L345 · personAmounts calls (conditional paths may differ): ledger.items.map, proxyTotals, proxyRows, find, memberDues, collectCurrencyOf, dueTotal, emptyTotal, sumTotals, map, memberCashFlows, convertedTotal, parts.map
shared/ledger-edit.ts ↗
  • L67 · applyFields calls (conditional paths may differ): Object.entries
  • L76 · requireMigration calls (conditional paths may differ): t, JSON.stringify
  • L83 · methodReferences calls (conditional paths may differ): st.subscriptions.flatMap, map, filter, flatMap, usesMethod
  • L103 · deleteMethodData calls (conditional paths may differ): methodReferences, st.methods.some, t, requireMigration, usesMethod, merged.set, merged.get, map, Math.round, moveFunding, st.subscriptions.filter, uses, move
  • L140 · deleteSubscriptionSettings calls (conditional paths may differ): settings.publicHidden.filter
  • L147 · pruneSettingsRefs calls (conditional paths may differ): subs.some, methods.some
  • L156 · categoryEdit calls (conditional paths may differ): categoryList, categories.find, t, referenceSummary, st.subscriptions.filter, Object.create, order.some, categories.some, order.map, requireMigration, categories.filter, applyFields, categories.map, cleanCategories, renameInSubs
  • L183 · personReferences calls (conditional paths may differ): map, refsFor
  • L191 · personEdit calls (conditional paths may differ): personReferences, some, t, requireMigration, mergePeople, filter, person.name.trim, validatePeopleAvatars, syncPerson
  • L206 · validateCurrency calls (conditional paths may differ): CURRENCY_CODE.test, t
  • L209 · validateMethod calls (conditional paths may differ): t, validateCurrency
  • L213 · safeSettings calls (conditional paths may differ): Object.fromEntries, map, filter, Object.keys, channelsOf, PREF_KEYS.filter
  • L228 · checkSettings calls (conditional paths may differ): JSON.stringify, changed, validateCurrency, st.methods.some, t, format, isLocale, START_PAGE_PATHS.includes, added.some, st.subscriptions.some, test
  • L248 · settingsEdit calls (conditional paths may differ): applyFields, checkSettings, pruneSettingsRefs, channelsOf, switches.map, switches.some, channels.some, t, channels.map, switches.find, mergePrefs
  • L269 · methodIdsOf calls (conditional paths may differ): ids.add, payMethodIds
  • L283 · subRefsError calls (conditional paths may differ): newShapeError, methodIdsOf, knownMethods.has, st.methods.some, t, join, st.methods.map, currenciesOf, knownCurrencies.has, validateCurrency
  • L302 · editRecord calls (conditional paths may differ): Number.isInteger, t, JSON.stringify, records.flatMap
  • L312 · mergeEdits calls (conditional paths may differ): t, Object.keys, JSON.stringify
worker/auth.ts ↗
  • L40 · sha256 calls (conditional paths may differ): crypto.subtle.digest, encode, hex
  • L45 · randomToken calls (conditional paths may differ): hex, crypto.getRandomValues
  • L49 · pbkdf2 calls (conditional paths may differ): crypto.subtle.importKey, encode, crypto.subtle.deriveBits, hex
  • L56 · constantEqual calls (conditional paths may differ): a.charCodeAt, b.charCodeAt
  • L63 · hashPassword calls (conditional paths may differ): randomToken, pbkdf2, toISOString
  • L68 · checkPassword calls (conditional paths may differ): getKv, constantEqual, pbkdf2, sha256, putKv, hashPassword
  • L80 · needsSetup calls (conditional paths may differ): getKv
  • L84 · readCookie calls (conditional paths may differ): req.headers.get, raw.split, part.indexOf, part.slice, decodeURIComponent
  • L93 · isAuthed calls (conditional paths may differ): readCookie, sha256, first, bind, env.DB.prepare, Date.now, run, clientIp
  • L108 · sessionHash calls (conditional paths may differ): readCookie, sha256
  • L121 · locationOf calls (conditional paths may differ): join, filter
  • L127 · startSession calls (conditional paths may differ): Date.now, randomToken, Math.max, getSettings, env.DB.batch, bind, env.DB.prepare, sha256, slice, req.headers.get, clientIp, locationOf, Response.json, normalizeBasePath, secure
  • L162 · throttle calls (conditional paths may differ): clientIp, setTimeout, Date.now, first, bind, env.DB.prepare, run
  • L185 · authStatus calls (conditional paths may differ): first, env.DB.prepare, Response.json, needsSetup, getSettings
  • L192 · setup calls (conditional paths may differ): needsSetup, err, t, throttle, catch, req.json, constantEqual, sha256, th.fail, putKv, hashPassword, th.clear, startSession
  • L208 · login calls (conditional paths may differ): throttle, err, t, catch, req.json, checkPassword, th.fail, th.clear, startSession
  • L221 · changePassword calls (conditional paths may differ): catch, req.json, checkPassword, err, t, putKv, hashPassword, removeOtherSessions, Response.json
  • L231 · removeOtherSessions calls (conditional paths may differ): run, bind, env.DB.prepare, sessionHash
  • L238 · logout calls (conditional paths may differ): readCookie, run, bind, env.DB.prepare, sha256, Response.json, normalizeBasePath, secure
  • L251 · logoutOthers calls (conditional paths may differ): Response.json, removeOtherSessions
  • L256 · listSessions calls (conditional paths may differ): sessionHash, all, bind, env.DB.prepare, Date.now, rows.results.map, r.token_hash.slice, r.created_at.replace, toISOString
  • L284 · pruneSessions calls (conditional paths may differ): run, bind, env.DB.prepare, sessionHash, Math.max
  • L293 · revokeSession calls (conditional paths may differ): test, err, t, sessionHash, mine.startsWith, run, bind, env.DB.prepare, Response.json
  • L302 · sameOrigin calls (conditional paths may differ): req.headers.get
  • L332 · saveChallenge calls (conditional paths may differ): randomToken, env.DB.batch, bind, env.DB.prepare, Date.now
  • L342 · takeChallenge calls (conditional paths may differ): first, bind, env.DB.prepare, Date.now
  • L366 · listPasskeys calls (conditional paths may differ): all, env.DB.prepare, rows.results.map
  • L373 · deletePasskey calls (conditional paths may differ): run, bind, env.DB.prepare
  • L378 · passkeyRegisterOptions calls (conditional paths may differ): all, env.DB.prepare, rp, getSettings, generateRegistrationOptions, displayName.trim, existing.results.map, JSON.parse, Response.json, saveChallenge
  • L395 · passkeyRegisterVerify calls (conditional paths may differ): readJson, takeChallenge, err, t, rp, catch, verifyRegistrationResponse, logPasskeyError, run, bind, env.DB.prepare, b64url, JSON.stringify, slice, trim, Response.json
  • L422 · passkeyLoginOptions calls (conditional paths may differ): generateAuthenticationOptions, rp, Response.json, saveChallenge
  • L428 · passkeyLoginVerify calls (conditional paths may differ): throttle, err, t, readJson, takeChallenge, Response.json, first, bind, env.DB.prepare, th.fail, rp, catch, verifyAuthenticationResponse, unb64url, JSON.parse, logPasskeyError, run, th.clear, startSession

Environment references: env.OWNER_PASSCODE · env.DB · env.TRUST_PROXY · env.BASE_PATH · env.SETUP_TOKEN · env.DEV_BYPASS_AUTH · env.SITE_URL

shared/mask.ts ↗
  • L10 · maskDomain calls (conditional paths may differ): text.replace, split, labels.slice, parts.map, repeat, Math.min, masked.join
  • L19 · isDomainSub calls (conditional paths may differ): isDomainCategory, presetOf, test, s.tags.join
  • L25 · publicMethod calls (conditional paths may differ): t
  • L40 · originOf calls (conditional paths may differ): url.startsWith
  • L50 · publicPayment calls (conditional paths may differ): pay.funding.map
  • L65 · publicPhase calls (conditional paths may differ): publicStop
  • L89 · publicSub calls (conditional paths may differ): migrateRenewalRules, isDomainSub, shortHash, maskDomain, originOf, publicStop, s.share.members.map, m.feeHistory.map
  • L149 · maskForPublic calls (conditional paths may differ): categoryList, state.subscriptions.filter, hidden.has, subs.map, map, cats.filter, used.has, state.methods.map, publicSub
worker/fx.ts ↗
  • L10 · keepSet calls (conditional paths may differ): state.subscriptions.map, state.methods.map
  • L22 · neededDates calls (conditional paths may differ): todayIn, chargesBetween, set.add, first.slice, addMonths, addDays, sort
  • L38 · slimFxHistory calls (conditional paths may differ): JSON.parse
  • L50 · putFxDayStmt calls (conditional paths may differ): bind, env.DB.prepare, JSON.stringify
  • L56 · getJson calls (conditional paths may differ): fetch, res.json
  • L63 · fetchDay calls (conditional paths may differ): FX_SOURCES.currencyApi, parseCurrencyApi, getJson, Object.keys, parseFrankfurter, FX_SOURCES.frankfurter
  • L83 · fxStatus calls (conditional paths may differ): neededDates, all, env.DB.prepare, rows.results.map, need.filter, have.has, have.get
  • L97 · backfill calls (conditional paths may differ): fxStatus, keepSet, slice, reverse, st.missing.slice, todo.slice, Promise.all, chunk.map, then, fetchDay, failed.push, stmts.push, putFxDayStmt, env.DB.batch
  • L122 · recordToday calls (conditional paths may differ): run, bind, env.DB.prepare, todayIn, JSON.stringify
  • L129 · refreshFx calls (conditional paths may differ): fetch, t, catch, res.json, toISOString, putKv, recordToday, getSettings

Environment references: env.DB

worker/ics.ts ↗
  • L7 · icsText calls (conditional paths may differ): replace, s.replace
  • L17 · fold calls (conditional paths may differ): encode, out.push, out.join
  • L37 · buildIcs calls (conditional paths may differ): todayIn, addMonths, replace, toISOString, fold, icsText, t, chargesBetween, c.date.replace, seen.get, seen.set, state.methods.find, chargeMethodId, lines.push, addDays, isImageIcon, money, join, filter, renewalRuleAt
worker/notify.ts ↗
  • L70 · iconOf calls (conditional paths may differ): isDataIcon
  • L82 · buildDaily calls (conditional paths may differ): effectiveStatus, diffDays, trials.push, t, relDays, cnDate, money, iconOf, headline.push, nextCharge, state.methods.find, priceAt, convert, renew.push, join, filter, includes, renewalDates, cancellation.push, ending.push
  • L262 · buildMonthly calls (conditional paths may differ): today.endsWith, addMonths, sort, flatMap, state.subscriptions.filter, filter, chargesBetween, a.date.localeCompare, charges.reduce, convert, totals, map, today.split, today.slice, monthLabel, money, t, String, charges.map, state.methods.find
  • L325 · sendDigest calls (conditional paths may differ): filter, channelsOf, t, Date.now, run, bind, env.DB.prepare, JSON.stringify, Promise.all, targets.map, sendTo, String, console.error, results.filter, Math.floor, join, results.map
  • L382 · localHour calls (conditional paths may differ): Date.now, Number, format, getUTCHours
  • L390 · runScheduled calls (conditional paths may differ): Date.now, all, bind, env.DB.prepare, JSON.parse, sendDigest, log.push, todayIn, localHour, buildMonthly, buildDaily

Environment references: env.DB

worker/email.ts ↗
  • L73 · darkCss calls (conditional paths may differ): join, DARK_RULES.map, rules, replace
  • L100 · iconSrc calls (conditional paths may differ): test, icon.startsWith
  • L108 · iconCell calls (conditional paths may differ): iconSrc, esc, test, toUpperCase, r.title.trim
  • L120 · daysChip calls (conditional paths may differ): esc, relDays
  • L130 · rowHtml calls (conditional paths may differ): tone, iconCell, esc, daysChip
  • L150 · noteBox calls (conditional paths may differ): esc
  • L154 · sectionHtml calls (conditional paths may differ): noteBox, t, esc, join, s.rows.map, rowHtml
  • L175 · statsHtml calls (conditional paths may differ): join, stats.map, esc
  • L189 · renderText calls (conditional paths may differ): lines.push, join, d.stats.map, t, filter, relDays, d.sections.some, lines.join
  • L214 · renderEmail calls (conditional paths may differ): siteUrl.replace, t, cnDateFull, test, site.replace, d.sections.some, esc, join, statsHtml, d.sections.map, sectionHtml, noteBox, locale, repeat, renderText
worker/store.ts ↗
  • L13 · getRev calls (conditional paths may differ): first, env.DB.prepare
  • L25 · mutate calls (conditional paths may differ): env.DB.batch, env.DB.prepare
  • L30 · getKv calls (conditional paths may differ): first, bind, env.DB.prepare, JSON.parse
  • L35 · putKvStmt calls (conditional paths may differ): validatePeopleAvatars, bind, env.DB.prepare, JSON.stringify
  • L42 · putKv calls (conditional paths may differ): includes, mutate, putKvStmt, run
  • L47 · getSettings calls (conditional paths may differ): getKv
  • L51 · getFx calls (conditional paths may differ): getKv
  • L56 · getPeople calls (conditional paths may differ): getKv
  • L69 · writePeople calls (conditional paths may differ): validatePeopleAvatars, toISOString, putPeopleStmt, subs.map, upsertStmt, stamp, mutate
  • L80 · ensurePeople calls (conditional paths may differ): linkPeople, writePeople
  • L92 · ensureReceivables calls (conditional paths may differ): state.subscriptions.map, migrateReceivables, changed.push, writePeople
  • L104 · loadState calls (conditional paths may differ): env.DB.batch, env.DB.prepare, Object.fromEntries, results.map, JSON.parse, subs.results.map, migrateRenewalRules, map, sort, methods.results.map, pruneSettingsRefs, ensureReceivables, ensurePeople, loadState, slimFxHistory, keepSet
  • L148 · upsertStmt calls (conditional paths may differ): bind, env.DB.prepare, JSON.stringify
  • L154 · upsert calls (conditional paths may differ): stamp, toISOString, mutate, upsertStmt
  • L159 · remove calls (conditional paths may differ): mutate, bind, env.DB.prepare
  • L163 · replaceAll calls (conditional paths may differ): stmts.push, env.DB.prepare, upsertStmt, mutate
  • L174 · loadMethods calls (conditional paths may differ): results.map, all, env.DB.prepare, JSON.parse
  • L179 · loadSubs calls (conditional paths may differ): results.map, all, env.DB.prepare, migrateRenewalRules, JSON.parse
  • L189 · restoreLedger calls (conditional paths may differ): validatePeopleAvatars, mutate, env.DB.prepare, b.methods.map, upsertStmt, b.subscriptions.map, putKvStmt, b.fxDaily.map, putFxDayStmt
  • L214 · saveSettingsWithSubs calls (conditional paths may differ): toISOString, mutate, putKvStmt, subs.map, upsertStmt, stamp
  • L220 · saveLedgerEdit calls (conditional paths may differ): toISOString, map, upsertStmt, stamp, deletedMethods.map, bind, env.DB.prepare, deletedSubscriptions.map, filter, putKvStmt, mutate

Environment references: env.DB

shared/prefs.ts ↗
  • L41 · mergePrefs calls (conditional paths may differ): Array.isArray, Object.entries, KEY_RE.test, Object.keys
shared/categories.ts ↗
  • L63 · presetOf calls (conditional paths may differ): PRESETS.find, nameOf
  • L76 · categoryList calls (conditional paths may differ): filter, subs.map, seen.has, seen.add, out.push, add, fromPreset, used.find, presetOf, nameOf, out.some
  • L101 · findCategory calls (conditional paths may differ): list.find
  • L106 · presetCategory calls (conditional paths may differ): list.find, nameOf
  • L111 · isDomainCategory calls (conditional paths may differ): findCategory, presetOf
  • L120 · categoryColor calls (conditional paths may differ): test, ch.charCodeAt
  • L132 · cleanCategories calls (conditional paths may differ): Array.isArray, t, x.name.trim, names.has, names.add, x.icon.trim, test, list.push
  • L151 · renameInSubs calls (conditional paths may differ): Object.hasOwn, out.push
  • L161 · categoryCounts calls (conditional paths may differ): n.set, n.get
worker/ai/mcp.ts ↗
  • L32 · listTokens calls (conditional paths may differ): getKv, map, storedTokens
  • L38 · createToken calls (conditional paths may differ): randomToken, slice, name.trim, t, sha256, token.slice, toISOString, putKv, storedTokens
  • L52 · revokeToken calls (conditional paths may differ): putKv, filter, storedTokens
  • L67 · authenticate calls (conditional paths may differ): req.headers.get, sha256, find, listTokens, toISOString, now.slice, tk.lastUsedAt.slice, run, touchStmt
  • L88 · handle calls (conditional paths may differ): String, getSettings, ok, SUPPORTED.includes, t, JSON.stringify, connectionInfo, todayIn, map, TOOLS.filter, toolTitle, TOOLS.find, fail, tool.run
  • L134 · mcp calls (conditional paths may differ): getSettings, Response.json, fail, t, authenticate, req.json, Array.isArray, handle, out.push

Environment references: env.DB

worker/url-guard.ts ↗
  • L19 · expandV6 calls (conditional paths may differ): s.match, map, v4.slice, b.some, s.slice, toString, s.split, x.split, part, fill, Array, words.some, test, words.map, parseInt
  • L48 · privateIp calls (conditional paths may differ): toLowerCase, host.replace, h.includes, expandV6, every, w.slice, zeros, privateV4, embeddedPrivate, h.match, Number
  • L69 · isPublicHttpsUrl calls (conditional paths may differ): PRIVATE_HOST.test, privateIp
  • L84 · fetchPublic calls (conditional paths may differ): isPublicHttpsUrl, fetch, res.headers.get
worker/ai/mcp-tools.ts ↗
  • L37 · connectionInfo calls (conditional paths may differ): map, TOOLS.filter

Environment references: ctx.env.SITE_URL

worker/appstore.ts ↗
  • L12 · searchAppStore calls (conditional paths may differ): query.match, slice, query.trim, storeCountry, encodeURIComponent, fetch, catch, res.json, map, String
  • L37 · appStorePlans calls (conditional paths may differ): test, storeCountry, Promise.all, catch, then, fetch, r.text, r.json, slice, parseAppStorePlans
worker/backup.ts ↗
  • L29 · exportAll calls (conditional paths may differ): Promise.all, all, env.DB.prepare, toISOString, subs.results.map, JSON.parse, methods.results.map, getPeople, getSettings, getFx, fxDaily.results.map
  • L49 · parseBackup calls (conditional paths may differ): Array.isArray, t, LEGACY_FORMATS.includes, String, b.people.filter, validatePeopleAvatars, b.subscriptions.map
  • L71 · restoreAll calls (conditional paths may differ): restoreLedger, Object.keys

Environment references: env.DB

worker/ai/assistant.ts ↗
  • L45 · viewConfig calls (conditional paths may differ): configView, getConfig
  • L50 · saveConfig calls (conditional paths may differ): getConfig, normBaseUrl, b.model.trim, putKv, configView
  • L63 · testConfig calls (conditional paths may differ): getConfig, t, normBaseUrl, fetch, catch, res.json, errorKind, map, String
  • L79 · systemPrompt calls (conditional paths may differ): loadState, todayIn, map, categoryList, st.methods.map, join, locale, cats.join, methods.join
  • L130 · toolContent calls (conditional paths may differ): JSON.stringify, content.slice
  • L146 · enrichDraft calls (conditional paths may differ): decodeEntities, trim, String, fixed.icon.replace, toLowerCase, catch, searchAppStore, norm, name.split, hits.find, startsWith, stem.startsWith, slice, Math.max, hit.icon.replace, filled.push
  • L173 · complete calls (conditional paths may differ): fetch, normBaseUrl, JSON.stringify, ASSISTANT_TOOLS.map, catch, res.json, Object.assign, errorKind, emptyTurn, getReader, res.body.pipeThrough, reader.read, buf.indexOf, replace, buf.slice, applyLine, emit
  • L220 · chat calls (conditional paths may differ): stream.writable.getWriter, catch, writer.write, enc.encode, JSON.stringify, getConfig, Object.assign, t, slice, filter, u.startsWith, userMessage, String, added.push, dropOldImages, Array.isArray, systemPrompt, complete, assistantMessage, emit
  • L355 · apply calls (conditional paths may differ): ASSISTANT_TOOLS.find, t, loadState, tool.run, confirmationArgs, state, writeMismatches
  • L376 · confirmationArgs calls (conditional paths may differ): Object.assign
  • L404 · listChats calls (conditional paths may differ): all, env.DB.prepare, sort, rows.map, JSON.parse, localeCompare, String
  • L412 · saveChat calls (conditional paths may differ): Array.isArray, JSON.stringify, toISOString, encode, run, bind, env.DB.prepare
  • L424 · clearChats calls (conditional paths may differ): run, env.DB.prepare

Environment references: env.DB

worker/i18n.ts ↗
  • L19 · pack calls (conditional paths may differ): cache.get, Object.entries, flatten, cache.set
  • L34 · withLocaleScope calls (conditional paths may differ): scope.run, freshLocaleState
  • L38 · applyLocale calls (conditional paths may differ): setLocale, map, fallbackChain
  • L43 · settingsLocale calls (conditional paths may differ): isLocale
  • L48 · requestLocale calls (conditional paths may differ): req.headers.get, isLocale, matchLocale, accept.split, settingsLocale
shared/i18n.ts ↗
  • L35 · fallbackChain calls (conditional paths may differ): chain.push
  • L44 · matchLocale calls (conditional paths may differ): split, raw.trim, p.toLowerCase, lower.startsWith, test, LOCALES.find, l.code.toLowerCase, l.code.split, lower.split
  • L65 · flatten calls (conditional paths may differ): Object.entries, every, Object.keys, PLURAL_KEYS.has, flatten
  • L97 · setLocale calls (conditional paths may differ): active
  • L109 · t calls (conditional paths may differ): active, Number, plural.select, s.replace, String
worker/migrate.ts ↗
  • L7 · ensureMigrated calls (conditional paths may differ): catch, run
  • L15 · run calls (conditional paths may differ): run, env.DB.prepare, results.map, all, applied.has, env.DB.batch, m.statements.map, bind, console.log

Environment references: env.DB

worker/http.ts ↗
  • L14 · readJson calls (conditional paths may differ): req.json, t
shared/carpool.ts ↗
  • L33 · memberCashFlows calls (conditional paths may differ): map, filter
  • L41 · memberFlowValue calls (conditional paths may differ): convertOn
  • L46 · firstPeriodCut calls (conditional paths may differ): memberSince, memberCollection
  • L54 · memberPaidThrough calls (conditional paths may differ): paidThrough, memberLedger
  • L68 · suggestedLeaveRefund calls (conditional paths may differ): firstPeriodCut, collectCurrencyOf, periods.flatMap, memberLedger, diffDays, convertOn, Math.max, Math.min, parts.reduce, currencyDecimals, Math.round
  • L97 · carLedger calls (conditional paths may differ): currentPhase, toBaseOrNaN, monthlyGross, members.reduce, r.date.slice, today.slice, memberFlowValue, isStopped, effectiveStatus, memberFee, memberAboard, memberBillMonths, reduce, filter, members.flatMap, members.map, chargesBetween, chargeBaseOrNaN, chargesTotal, sumTotals
  • L172 · carYear calls (conditional paths may differ): today.slice, collectCurrencyOf, yearCost, reduce, filter, members.flatMap, p.date.slice, memberFlowValue, flatMap, members.filter, map, periods.filter, memberLedger, isSettled, p.from.slice, toBaseOrNaN, chargesTotal, chargesBetween, sumTotals, convertedTotal
  • L210 · carpoolYear calls (conditional paths may differ): today.slice, filter, map, state.subscriptions.filter, carYear, isStopped, effectiveStatus, cars.map, cars.reduce, sumTotals
  • L230 · cycleMonths calls (conditional paths may differ): Math.max, cycleDays
  • L237 · memberFee calls (conditional paths may differ): memberSince, collectCurrencyOf, memberRate
  • L243 · changeMemberFee calls (conditional paths may differ): memberRate, memberSince, collectCurrencyOf, sort, history.filter, shareFromFee, a.from.localeCompare
  • L272 · shareFromFee calls (conditional paths may differ): Math.round, toBaseAt, cycleMonths
  • L280 · resyncShares calls (conditional paths may differ): cycleMonths, collectCurrencyOf, next.share.members.map, shareFromFee, memberBillMonths
  • L307 · carRule calls (conditional paths may differ): collectCurrencyOf, memberRate, counts.set, counts.get, sort, counts.values, Math.max, Math.round, cycleMonths, toBaseAt
  • L333 · costChange calls (conditional paths may differ): currentPhase, priceAt, flatMap, map, find, reverse, sort, filter, addDays, key, cost, payers.some, some
  • L359 · offCarRhythm calls (conditional paths may differ): some, onCarRhythm, memberBillMonths
  • L364 · followsCar calls (conditional paths may differ): offCarRhythm, memberRate, collectCurrencyOf, sameRule, carRule
  • L375 · putPeriodDues calls (conditional paths may differ): filter, flatMap, Object.entries, sort, a.from.localeCompare
  • L386 · waiveExemptSpan calls (conditional paths may differ): map, periods.filter, memberLedger, isSettled, putRecord, uid, memberPeriods
  • L398 · moveCollectFrom calls (conditional paths may differ): collectionAnchor, s.share.members.map, memberSince, map, memberPeriods, starts.includes, includes, sort
  • L416 · applyCarRule calls (conditional paths may differ): s.share.members.map, followsCar, memberRate, collectCurrencyOf, sameRule, changeMemberFee
  • L433 · followCarRule calls (conditional paths may differ): carRule, memberRate, collectCurrencyOf, filter, onCarRhythm, sameRule, changeMemberFee, nextCollectionDay, addDays, memberSince, some, memberPeriods, sort, dropReshaped
  • L453 · dropReshaped calls (conditional paths may differ): flatMap, at, keys.sort, map, memberPeriods, span, was.has, was.get, now.get, keys.some, r.periods.filter, reshaped, keep
  • L472 · moveMemberJoin calls (conditional paths may differ): memberSince, memberLedger, was.filter, removeRecordPeriod, dropped.push, was.find, find, memberPeriods, x.periods.map, resolveKey
  • L506 · periodsTotal calls (conditional paths may differ): collectCurrencyOf, periods.reduce, convertOn, currencyDecimals, Math.round
  • L517 · nextCollectionDay calls (conditional paths may differ): collectionPeriod
  • L522 · collectionPeriod calls (conditional paths may differ): collectionAnchor, Math.max, Math.ceil, Number, today.slice, anchor.slice, addMonths
  • L535 · memberStatus calls (conditional paths may differ): collectCurrencyOf, find, memberDues, memberCollection, memberUnpaidFrom, periods.find, memberLedger, isSettled, diffDays
  • L565 · memberSegments calls (conditional paths may differ): addDays, sort, out.push, memberLedger, visibleStripPeriods, periodCellKind, push, periods.filter, firstPeriodCut, at, paid.at
shared/phases.ts ↗
  • L80 · phasesOf calls (conditional paths may differ): sort, s.phases.map, a.start.localeCompare, fromTop
  • L85 · mirror calls (conditional paths may differ): endOf
  • L113 · endOf calls (conditional paths may differ): phases.reduce
  • L123 · switchPlan calls (conditional paths may differ): phasesOf, t, addDays, phases.push, mirror
  • L138 · prependPlan calls (conditional paths may differ): phasesOf, t, phases.unshift, addDays, mirror
  • L148 · confirmPlan calls (conditional paths may differ): mirror, map, phasesOf
  • L156 · dropPlan calls (conditional paths may differ): filter, phasesOf, addDays, phaseToTop, mirror
  • L165 · resume calls (conditional paths may differ): phasesOf, addDays, t, phases.push, mirror
  • L175 · changePrice calls (conditional paths may differ): findLast, phaseSubs, t, pricePoints, replacePricePoints, concat, hist.filter
  • L183 · pricePoints calls (conditional paths may differ): sort, slice, a.from.localeCompare
  • L188 · replacePricePoints calls (conditional paths may differ): phasesOf, phases.find, find, phaseSubs, t, pricePoints, validDay, old.some, Number.isFinite, points.map, sort, a.from.localeCompare, p.priceHistory.at, mirror
  • L208 · editPricePoint calls (conditional paths may differ): find, phaseSubs, some, pricePoints, t, validDay, replacePricePoints, map
  • L220 · removePricePoint calls (conditional paths may differ): find, phaseSubs, t, pricePoints, hist.some, replacePricePoints, hist.filter
  • L234 · affectedPriceCharges calls (conditional paths may differ): phaseSubs, reduce, map, chargesBetween, filter, charges.get
  • L258 · editIndex calls (conditional paths may differ): phases.findLastIndex, endAt, Math.max
  • L269 · toEditForm calls (conditional paths may differ): phasesOf, editIndex
  • L293 · syncPhase calls (conditional paths may differ): phasesOf, Object.assign, editIndex, endOf, mirror
  • L318 · setPhaseEnd calls (conditional paths may differ): t, phasesOf, phases.find, mirror
  • L334 · movePhaseStart calls (conditional paths may differ): validDay, t, phasesOf, phases.findIndex, find, phaseSubs, hist.some, priceAt, hist.map, collectionAnchor, mirror, subError
  • L368 · paymentsOutsideMovedPhase calls (conditional paths may differ): phaseSubs, flatMap, filter, outside
  • L379 · removePhase calls (conditional paths may differ): phasesOf, phases.splice, addDays, phaseToTop, mirror
  • L396 · phaseIndexAt calls (conditional paths may differ): phases.findIndex, phases.flatMap, endAt, live.find, some, chargesBetween, filter, phases.map
  • L417 · materialize calls (conditional paths may differ): recorded.some, chargesBetween, addDays, find, past.filter, sort, inferred.map, a.date.localeCompare
  • L433 · savePayment calls (conditional paths may differ): phasesOf, phaseIndexAt, materialize, filter, t, phases.findIndex, endAt, sort, a.date.localeCompare, fromPhases, subError
  • L471 · setPhaseMethod calls (conditional paths may differ): clear, map, phasesOf, mirror, subError
  • L482 · deletePayment calls (conditional paths may differ): phasesOf, phaseIndexAt, materialize, filter, t, payments.some, addDays, fromPhases
  • L498 · fromPhases calls (conditional paths may differ): mirror
  • L514 · stopSubscription calls (conditional paths may differ): validDay, t, isStopped, effectiveStatus, currentPhase, billingPeriod, filter, phasesOf, phases.find, mirror, Number.isFinite, savePayment, Math.abs, subError
  • L565 · reactivate calls (conditional paths may differ): effectiveStatus, t, reopenStopped
  • L571 · statusChangeError calls (conditional paths may differ): effectiveStatus, t, billingPeriod, currentPhase, at, phaseSubs
  • L596 · stopUndated calls (conditional paths may differ): isStopped, effectiveStatus, t, filter, phasesOf, mirror
  • L605 · cancelAtEnd calls (conditional paths may differ): isStopped, effectiveStatus, t, validDay, sort, filter, b.start.localeCompare, dropPlan, syncPhase, toEditForm
  • L615 · editPhase calls (conditional paths may differ): t, phasesOf, phases.findIndex, applyFields, phases.sort, a.start.localeCompare, phases.map, validDay, mirror, phases.find, pricePoints, find, patch.priceHistory.some, phaseSubs, replacePricePoints, editPricePoint, hist.at
shared/fx-total.ts ↗
  • L15 · amountTotal calls (conditional paths may differ): emptyTotal, Number.isFinite
  • L21 · sumTotals calls (conditional paths may differ): emptyTotal, Object.entries
  • L31 · scaleTotal calls (conditional paths may differ): Object.fromEntries, map, Object.entries
  • L34 · convertedTotal calls (conditional paths may differ): amountTotal, toBaseOrNaN
  • L38 · chargeTotal calls (conditional paths may differ): emptyTotal, amountTotal, chargeBaseOrNaN
  • L60 · formatUnconverted calls (conditional paths may differ): flow, money, sort, Object.entries, Math.abs, localeCompare, join, map, rows.slice, fmt, t
  • L70 · totalNote calls (conditional paths may differ): Object.keys, t, formatUnconverted
  • L77 · formatTotal calls (conditional paths may differ): formatUnconverted, Object.keys, flow, money
  • L88 · totalsNote calls (conditional paths may differ): rows.flatMap, JSON.stringify, seen.has, seen.add, formatUnconverted, t, parts.join
  • L101 · dueTotal calls (conditional paths may differ): sumTotals, map, Object.entries, convertedTotal, amountTotal, toBaseOrNaN
shared/intl.ts ↗
  • L10 · key calls (conditional paths may differ): JSON.stringify
  • L14 · dateFormat calls (conditional paths may differ): key, dateCache.get, dateCache.set
  • L21 · numberFormat calls (conditional paths may differ): key, numberCache.get, numberCache.set
  • L28 · relativeFormat calls (conditional paths may differ): key, relativeCache.get, relativeCache.set
shared/fx.ts ↗
  • L23 · index calls (conditional paths may differ): cache.get, sort, Object.keys, Object.entries, byCcy.get, byCcy.set, arr.push, cache.set
  • L41 · rateOn calls (conditional paths may differ): byCcy.get, index
  • L58 · convertOn calls (conditional paths may differ): rateOn
  • L67 · parseCurrencyApi calls (conditional paths may differ): Object.entries, k.toUpperCase, keep.has
  • L78 · parseFrankfurter calls (conditional paths may differ): Object.entries, keep.has
  • L87 · toBaseOrNaN calls (conditional paths may differ): convertOn
  • L94 · chargeBaseOrNaN calls (conditional paths may differ): toBaseOrNaN
  • L101 · toBaseAt calls (conditional paths may differ): toBaseOrNaN, Number.isFinite
  • L107 · chargeFxMissing calls (conditional paths may differ): Number.isFinite, chargeBaseOrNaN
  • L112 · chargeValue calls (conditional paths may differ): chargeBaseOrNaN, Number.isFinite
  • L118 · settlementCurrencyOf calls (conditional paths may differ): state.methods.find
shared/receivables.ts ↗
  • L45 · receiptShares calls (conditional paths may differ): receipt.periods.map, periods.get, listed.filter, known.reduce, known.every, weights.reduce, currencyDecimals, Math.round, weights.map, raw.map, sort, Math.floor, out.reduce
  • L67 · resolveKey calls (conditional paths may differ): periods.some, periods.find
  • L81 · settle calls (conditional paths may differ): periods.map, sort, a.date.localeCompare, resolvedPeriods, unmatched.push, receiptShares, paid.has, paid.set, shares.get, waived.has, waived.set, paid.get, waived.get
  • L110 · paidThrough calls (conditional paths may differ): isSettled
  • L120 · takenPeriods calls (conditional paths may differ): flatMap, filter, resolvedPeriods
  • L128 · putRecord calls (conditional paths may differ): t, rec.periods.find, resolveKey, resolvedPeriods, takenPeriods, keys.find, taken.has, keys.sort, filter, sort, put, a.date.localeCompare
  • L147 · removeRecord calls (conditional paths may differ): some, t, filter
  • L156 · removeRecordPeriod calls (conditional paths may differ): find, resolveKey, t, removeRecord, rec.periods.filter, hit.includes, map, currencyDecimals, Math.round
  • L177 · editRecordPeriod calls (conditional paths may differ): find, resolveKey, t, putRecord, removeRecordPeriod
  • L201 · setOpening calls (conditional paths may differ): filter, takenPeriods, map, periods.filter, taken.has, periods.find
  • L216 · togglePeriodRange calls (conditional paths may differ): order.indexOf, picked.includes, picked.filter, sort, order.includes, filter, picked.map, idx.filter, Math.min, Math.max, idx.at, Array.from, keep.map
shared/transfers.ts ↗
  • L11 · transfersOf calls (conditional paths may differ): map, sort, a.tr.date.localeCompare
  • L22 · transferValue calls (conditional paths may differ): convertOn
  • L27 · soldOn calls (conditional paths may differ): at, transfersOf
  • L36 · holdingPeriod calls (conditional paths may differ): find, transfersOf, soldOn, Math.max, diffDays, servedTime
  • L66 · netCost calls (conditional paths may differ): filter, chargesBetween, history.reduce, chargeBaseOrNaN, transfersOf, transferValue, holdingPeriod, Math.max, sumTotals, chargesTotal, map, scaleTotal, amountTotal, transferSign, history.filter, Number.isFinite
  • L104 · transferredStop calls (conditional paths may differ): find, transfersOf
  • L118 · transferRows calls (conditional paths may differ): sort, flatMap, state.subscriptions.filter, map, filter, transfersOf, transferSign, transferValue, a.date.localeCompare, a.sub.id.localeCompare
  • L131 · transferError calls (conditional paths may differ): Array.isArray, t, validDay, Number.isFinite, CURRENCY_CODE.test
  • L153 · cleanTransfers calls (conditional paths may differ): map, currency.toUpperCase, note.trim
shared/proxy.ts ↗
  • L12 · proxyPeriods calls (conditional paths may differ): map, chargesBetween, addMonths, all.flatMap, charged.get, addDays
  • L34 · proxyLedger calls (conditional paths may differ): flatMap, keys.reduce, settle, proxyPeriods
  • L57 · row calls (conditional paths may differ): toBaseAt, convertedTotal, emptyTotal
  • L86 · proxyRows calls (conditional paths may differ): reverse, map, periods.filter, proxyLedger, row
  • L96 · proxyAhead calls (conditional paths may differ): reverse, map, periods.filter, proxyLedger, row
  • L104 · proxyOpenPeriods calls (conditional paths may differ): map, periods.filter, proxyLedger, addMonths, row
  • L114 · proxyTotals calls (conditional paths may differ): rows.filter, sumTotals, rows.map, amountTotal, done.map, open.map, done.flatMap, scaleTotal, rows.reduce, done.reduce, open.reduce, reduce
  • L145 · proxyYear calls (conditional paths may differ): today.slice, map, state.subscriptions.filter, filter, proxyRows, r.date.slice, bySub.filter, used.map, proxyTotals, used.flatMap
shared/tool-schema.ts ↗
  • L35 · validateToolInput calls (conditional paths may differ): t, Array.isArray, types.some, Number.isInteger, fail, schema.enum.includes, Number.isFinite, validDay, test, value.forEach, validateToolInput, Object.hasOwn, Object.entries, includes
shared/format.ts ↗
  • L73 · currencyDecimals calls (conditional paths may differ): includes
  • L80 · moneyDecimals calls (conditional paths may differ): currencyDecimals, Math.abs, Number.isInteger
  • L86 · moneyTick calls (conditional paths may differ): Math.abs, money
  • L91 · money calls (conditional paths may differ): Number.isFinite, moneyDecimals, format, numberFormat, intlLocale, Math.abs, symbol
  • L107 · flowSign calls (conditional paths may differ): Number.isFinite
  • L112 · flow calls (conditional paths may differ): flowSign, money, Math.abs
  • L117 · fmtDate calls (conditional paths may differ): map, d.split, format, dateFormat, intlLocale, Date.UTC
  • L123 · cnDate calls (conditional paths may differ): fmtDate
  • L129 · weekdayOf calls (conditional paths may differ): fmtDate
  • L134 · cnDateAuto calls (conditional paths may differ): d.slice, today.slice, cnDate, cnDateFull
  • L139 · cnDateFull calls (conditional paths may differ): fmtDate
  • L144 · monthLabel calls (conditional paths may differ): fmtDate
  • L152 · monthTicks calls (conditional paths may differ): months.flatMap, Number, m.slice, at
  • L162 · weekdayShort calls (conditional paths may differ): fmtDate
  • L168 · relDays calls (conditional paths may differ): relativeFormat, intlLocale, Math.abs, rtf.format, Math.round
  • L177 · billLabel calls (conditional paths may differ): t, includes
  • L184 · esc calls (conditional paths may differ): s.replace
  • L189 · localTime calls (conditional paths may differ): test, raw.replace, Number.isNaN, d.getTime, d.toLocaleString, s.slice
shared/icon.ts ↗
  • L23 · iconInput calls (conditional paths may differ): trim, String, t, icon.slice, test, icon.startsWith, bad, icon.indexOf, isDataIcon, Math.floor, graphemes
worker/channels.ts ↗
  • L18 · toText calls (conditional paths may differ): lines.push, join, d.stats.map, t, headingOf, metaOf, amountOf, trim, lines.join
  • L44 · sectionMarkdown calls (conditional paths may differ): headingOf, metaOf, out.push, grey, out.join
  • L55 · toMarkdown calls (conditional paths may differ): out.push, join, d.stats.map, sectionMarkdown, t, trim, out.join
  • L64 · toLarkCard calls (conditional paths may differ): blocks.push, join, d.stats.map, sectionMarkdown, t
  • L80 · toTelegram calls (conditional paths may differ): esc, out.push, join, d.stats.map, headingOf, metaOf, t, slice, trim, out.join
  • L98 · hmacBase64 calls (conditional paths may differ): crypto.subtle.importKey, encode, crypto.subtle.sign, btoa, String.fromCharCode
  • L104 · post calls (conditional paths may differ): fetch, JSON.stringify, slice, res.text
  • L111 · expectOk calls (conditional paths may differ): catch, res.json, okValues.includes, trim, String
  • L125 · sendTo calls (conditional paths may differ): need, t, renderEmail, env.EMAIL.send, post, toTelegram, expectOk, toString, Math.floor, Date.now, hmacBase64, toLarkCard, url.includes, encodeURIComponent, toMarkdown, slice, replace, toText, c.config.token.trim, key.match

Environment references: env.SITE_URL · env.EMAIL · env.MAIL_FROM · env.MAIL_FROM_NAME

shared/receivables-legacy.ts ↗
  • L35 · migrateReceivables calls (conditional paths may differ): alignDriftedKeys, convertLegacy
  • L44 · alignDriftedKeys calls (conditional paths may differ): Number, s.share.members.map, some, collectionAnchor, day, addDays, at, sort, records.flatMap, memberPeriods, now.map, starts.has, old.findIndex, diffDays, r.periods.map, periods.every, fix
  • L79 · convertLegacy calls (conditional paths may differ): isLegacyProxy, legacyAnchor, map, isLegacyMember, memberFrom, proxyFrom
  • L95 · legacyAnchor calls (conditional paths may differ): flatMap, addMonths, addDays, memberBillMonths, m.payments.map, fromUntil, starts.sort
  • L105 · memberFrom calls (conditional paths may differ): memberSince, at, sort, payments.map, filter, payments.flatMap, addDays, some, memberPeriods, until.localeCompare, date.localeCompare, addMonths, memberBillMonths, receipts.push, claim, map, periods.filter, taken.has, receipts.unshift, receipts.sort
  • L147 · claim calls (conditional paths may differ): periods.filter, inside.filter, taken.has, overlap.find, Array.isArray, pick.map, taken.add
  • L157 · proxyFrom calls (conditional paths may differ): entries.reduce, map, chargesBetween, matchEntries, entries.forEach, keyOf.get, charged.push, batches.get, b.periods.push, batches.set, receipts.push, batches.values, sort, all.sort, a.date.localeCompare, charged.sort, a.charge.localeCompare
  • L197 · matchEntries calls (conditional paths may differ): entries.find, out.has, out.set, usedDates.add, usedDates.has, sort, entries.filter, Math.abs, diffDays
worker/ai/tool-kit.ts ↗
  • L37 · state calls (conditional paths may differ): loadState, concat, st.subscriptions.filter, has, filter, ctx.overlay.values, todayIn
  • L51 · findSub calls (conditional paths may differ): trim, String, t, st.subscriptions.find, q.toLowerCase, st.subscriptions.filter, s.name.toLowerCase, includes, subCandidates
  • L69 · findMember calls (conditional paths may differ): trim, String, members.find, raw.toLowerCase, members.filter, m.name.toLowerCase, includes, t, candidates
  • L82 · findPerson calls (conditional paths may differ): trim, String, people.find, normName, people.filter, includes, t, candidates
  • L96 · rangeOf calls (conditional paths may differ): String, today.slice, addDays, addMonths, re.test, t
  • L130 · chargesIn calls (conditional paths may differ): sort, flatMap, st.subscriptions.filter, chargesBetween, a.date.localeCompare
  • L137 · groupOf calls (conditional paths may differ): c.date.slice, t
  • L216 · commit calls (conditional paths may differ): resyncShares, st.subscriptions.find, Number.isFinite, t, prepareSubWrite, persist
  • L244 · persist calls (conditional paths may differ): row, st.subscriptions.find, t, personSummary, map, slice, chargesBetween, addMonths, round, writeLedger
  • L297 · writeLedger calls (conditional paths may differ): st.methods.filter, deletedMethods.includes, saveLedgerEdit
  • L332 · refundInput calls (conditional paths may differ): num, date, str, toUpperCase, String
  • L342 · assertNotDuplicate calls (conditional paths may differ): st.subscriptions.some, s.name.toLowerCase, name.toLowerCase, account.toLowerCase, toLowerCase, s.account.trim, t
  • L376 · categoryMatches calls (conditional paths may differ): list.filter, q.toLowerCase, c.name.toLowerCase, some, PRESETS.find, n.toLowerCase
  • L399 · subscriptionCategory calls (conditional paths may differ): trim, String, categoryList, categoryMatches, categoryEdit, str, categories.find, categoryRef
  • L410 · dropNulls calls (conditional paths may differ): Array.isArray, v.map, Object.fromEntries, flatMap, Object.entries, dropNulls
  • L416 · personTarget calls (conditional paths may differ): findPerson, findSub, filter, find, peopleLedger, items.filter, t, candidates, pick.map
shared/appstore-plans.ts ↗
  • L14 · parseAppStorePlans calls (conditional paths may differ): exec, JSON.parse, Array.isArray, v.forEach, walk, o.items.filter, p.leadingText.trim, trim, p.trailingText.replace, seen.has, seen.add, found.push, parsePrice, guessCycle, Object.values
  • L55 · parsePrice calls (conditional paths may differ): text.replace, test, Math.max, s.lastIndexOf, Number, s.replace, s.slice, s.split, replace, Number.isFinite
  • L82 · guessCycle calls (conditional paths may differ): Number, N_MONTHS.exec, CYCLE_WORDS.find, re.test
shared/deepseek.ts ↗
  • L26 · configView calls (conditional paths may differ): c.apiKey.slice
  • L37 · normBaseUrl calls (conditional paths may differ): replace, trim, u.replace
  • L59 · userMessage calls (conditional paths may differ): text.trim, images.map
  • L69 · dropOldImages calls (conditional paths may differ): reverse, map, history.slice, join, m.content.flatMap
  • L104 · applyLine calls (conditional paths may differ): line.startsWith, trim, line.slice, JSON.parse
  • L138 · assistantMessage calls (conditional paths may differ): turn.toolCalls.filter
  • L149 · parseArgs calls (conditional paths may differ): raw.trim, JSON.parse, Array.isArray, String
shared/assistant-result.ts ↗
  • L20 · draftSignature calls (conditional paths may differ): JSON.stringify, Array.isArray, Object.fromEntries, sort, Object.entries, a.localeCompare
  • L26 · canonical calls (conditional paths may differ): JSON.stringify, Array.isArray, Object.fromEntries, sort, filter, Object.entries, a.localeCompare
  • L41 · writeMismatches calls (conditional paths may differ): map, a.keys, b.keys, canonical, a.get, b.get, c.get, fields.push, Object.keys
  • L61 · remainingFailures calls (conditional paths may differ): filter, refs, Object.fromEntries, map, sort, Object.keys, failures.filter, canonical, selector, targets.some, identities.has
  • L94 · confirmInOrder calls (conditional paths may differ): confirm
shared/metrics.ts ↗
  • L35 · contributionOn calls (conditional paths may differ): reduce, activePhases, contributionOn, toBaseOrNaN, monthlyGross, myShareFactor
  • L50 · contributionTotal calls (conditional paths may differ): sumTotals, map, activePhases, contributionTotal, contributionOn, amountTotal
  • L58 · mrrOn calls (conditional paths may differ): state.subscriptions.reduce, contributionOn
  • L62 · monthEnd calls (conditional paths may differ): addDays, addMonths
  • L67 · monthMovement calls (conditional paths may differ): movement, addDays, day.slice
  • L71 · mrrChangeSinceLastMonth calls (conditional paths may differ): monthMovement
  • L80 · chartMonths calls (conditional paths may differ): Number, today.slice, first.slice, Math.max, Math.min
  • L92 · mrrSeries calls (conditional paths may differ): slice, addMonths, today.slice, monthEnd, out.push, mrrOn, mrrTotal
  • L122 · movement calls (conditional paths may differ): mrrTotal, contributionOn, Number.isFinite, Math.abs, r.items.push, r.items.sort
  • L164 · eventsOf calls (conditional paths may differ): out.push, toBaseOrNaN, contributionOn, after, addDays, cycleDays, myShareFactor, sort, slice, a.from.localeCompare
  • L229 · events calls (conditional paths may differ): state.subscriptions.filter, phaseSubs, out.push, eventsOf, vs.forEach, isGap, coveredUntil, label, contiguous, contributionOn, addDays, find, amountTotal, out.sort, b.date.localeCompare
  • L274 · coverage calls (conditional paths may differ): addDays, chargeServicePeriod, Math.round, cycleDays, find, chargesBetween, nthCharge
  • L305 · cashVsAccrual calls (conditional paths may differ): addMonths, today.slice, monthEnd, slice, rows.set, emptyTotal, flatMap, state.subscriptions.filter, chargesBetween, chargeValue, myShareFactor, rows.get, c.date.slice, scaleTotal, chargeTotal, sumTotals, coverage, Math.max, diffDays, addDays
  • L367 · stopHistory calls (conditional paths may differ): forEach, phaseSubs, transferredStop, stopSummary, myShareFactor, convertOn, items.push, Number.isFinite, items.sort, b.date.localeCompare
  • L408 · stopLosses calls (conditional paths may differ): filter, stopHistory, sumTotals, items.map, amountTotal, items.reduce, items.some
  • L422 · prepaidValue calls (conditional paths may differ): stopLosses, losses.items.map, emptyTotal, phaseSubs, filter, chargesBetween, addMonths, Math.max, coverage, addDays, myShareFactor, diffDays, chargeValue, sumTotals, scaleTotal, chargeTotal, items.push, items.sort, items.map, items.reduce
  • L469 · cancelSavings calls (conditional paths may differ): toBaseAt, priceAt, cycleDays, myShareFactor, Math.max, diffDays, amountTotal, toBaseOrNaN, items.push, items.sort, items.reduce, sumTotals, items.map
  • L501 · pickCards calls (conditional paths may differ): sort, cards.push, find, reverse, ds.slice
  • L539 · topUpPlan calls (conditional paths may differ): runwayFor, Math.max, addMonths, reduce, r.forecast.filter, includes, Math.pow, Math.round, Math.log10, sumTotals, map, scaleTotal, out.push, pickCards, out.sort, localeCompare
  • L597 · sharedName calls (conditional paths may differ): subs.map, s.name.trim, all.reduce, a.slice, all.some, sep.test, head.at, Math.max, map, head.slice, trim, head.replace
  • L613 · baseName calls (conditional paths may differ): trim, replace, name.toLowerCase
  • L625 · ccyLabel calls (conditional paths may differ): of, intlLocale
  • L633 · insights calls (conditional paths may differ): stopLosses, out.push, t, formatTotal, values, unused.items.map, state.subscriptions.filter, includes, effectiveStatus, join, ss.map, sort, filter, map, live.filter, cycleDays, nextCharge, diffDays, localeCompare, sumTotals
  • L795 · yearReview calls (conditional paths may differ): today.slice, reduce, state.subscriptions.filter, filter, flatMap, chargesBetween, emptyTotal, stopLosses, sumTotals, date.slice, note, scaleTotal, chargeTotal, myShareFactor, chargeBaseOrNaN, ccys.add, bySub.get, bySub.set, byMonth.set, c.date.slice
  • L865 · yearCost calls (conditional paths may differ): today.slice, reduce, filter, chargesBetween, chargeBaseOrNaN
shared/period-grid.ts ↗
  • L25 · periodGridMode calls (conditional paths may differ): periods.map, addDays, spans.some, monthIndex, addMonths, spans.every, diffDays
  • L38 · visibleStripPeriods calls (conditional paths may differ): sort, a.from.localeCompare, sorted.findIndex, isSettled, sorted.slice
  • L45 · periodCellKind calls (conditional paths may differ): isSettled
  • L50 · periodGrid calls (conditional paths may differ): periodGridMode, sort, a.from.localeCompare, visibleStripPeriods, rows.find, rows.push, all.forEach, Math.max, monthIndex, visible.includes, Math.floor, Math.min, segments.push, rowFor, String, Number, first.slice, padStart, diffDays, Math.round
worker/ai/tool-views.ts ↗
  • L31 · money calls (conditional paths may differ): Number.isFinite, toBaseOrNaN, fin, partialOut, amountTotal
  • L45 · fxGaps calls (conditional paths may differ): Number.isFinite, missing.add, sort
  • L69 · personRow calls (conditional paths may differ): fin, toBaseOrNaN, personAmounts, partialOut, contactLine, personSummary, conv, l.items.map
  • L103 · periodsOut calls (conditional paths may differ): periods.map
  • L116 · collectionSummary calls (conditional paths may differ): carRule, collectCcy, memberCollection, memberUnpaidFrom, followsCar, collectionAnchor, memberPaidThrough, diffDays, money, partialOut, dueTotal
  • L135 · ruleOut calls (conditional paths may differ): carRule
  • L145 · row calls (conditional paths may differ): currentPhase, nextCharge, monthlyGross, filter, activePhases, effectiveStatus, kindOf, money, priceAt, cycleLabel, partialOut, monthlyPriceTotal, round, myShareFactor, renewalDates, lastCharge, st.methods.find, stopSummary, parallel.map
  • L190 · chargeRow calls (conditional paths may differ): st.subscriptions.find, chargeBaseOrNaN, round, fin, myShareFactor, Number.isFinite, st.methods.find, chargeMethodId, c.funding.map
  • L219 · fieldChanges calls (conditional paths may differ): JSON.stringify, leaf, Array.isArray, map, keys, Array, Math.max, Object.keys, keys.flatMap, fieldChanges
  • L234 · computedOf calls (conditional paths may differ): filter, chargesBetween, fxGaps, all.reduce, gaps.note, chargeBaseOrNaN, chargeCcy, effectiveStatus, nextCharge, lastCharge, fin, gaps.out, partialOut, chargesTotal, stopSummary, currentPhase
  • L247 · roundAll calls (conditional paths may differ): Object.fromEntries, map, filter, Object.entries, round
shared/scope-report.ts ↗
  • L131 · rowTotal calls (conditional paths may differ): sumTotals, rows.map, amountTotal
  • L134 · cashMonth calls (conditional paths may differ): cashSpan
  • L137 · cashSpan calls (conditional paths may differ): sum, rows.filter, rowTotal
  • L152 · cashRows calls (conditional paths may differ): charges, map, transferRows, amountTotal
  • L162 · charges calls (conditional paths may differ): sort, state.subscriptions.flatMap, map, filter, chargesBetween, chargeValue, myShareFactor, scaleTotal, chargeTotal, a.date.localeCompare, a.sub.id.localeCompare
  • L179 · compactComposition calls (conditional paths may differ): rows.slice, sum, rest.reduce, rest.some, rowTotal
  • L194 · composition calls (conditional paths may differ): groups.set, groups.get, rowTotal, compactComposition, sort, filter, map, totalRatio, a.key.localeCompare
  • L209 · addComposition calls (conditional paths may differ): blocks.push
  • L215 · details calls (conditional paths may differ): state.subscriptions.map, map, filter, events, addDays, roots.get, rows.push, rows.sort, b.date.localeCompare, a.sub.id.localeCompare, a.kind.localeCompare, blocks.push, stopLosses, charges, sum, scaleTotal, rowTotal, state.subscriptions.filter, reduce, members.flatMap
  • L316 · selectionCash calls (conditional paths may differ): map, cashVsAccrual, today.slice, charges, sum, rowTotal
  • L325 · scopeReport calls (conditional paths may differ): subs.filter, scoped.subscriptions.reduce, charges, stopHistory, scoped.subscriptions.map, rowTotal, rows.filter, spentBySub.sort, a.sub.id.localeCompare, scoped.subscriptions.filter, isStopped, effectiveStatus, sumTotals, stopped.map, at, filter, phaseSubs, addDays, contributionTotal, Math.max
  • L379 · legacyScopeReport calls (conditional paths may differ): subs.filter, today.slice, mrrOn, totalReport, mrrTotal, scaleTotal, rowTotal, charges, stopLosses, totals, sum, mrrChangeSinceLastMonth, selectionCash, cash.some, blocks.push, filter, scoped.subscriptions.map, contributionOn, contributionTotal, addComposition
  • L441 · totalReport calls (conditional paths may differ): scoped.subscriptions.reduce, charges, sum, bySub.get, emptyTotal, sumTotals, bySub.set, filter, bySub.values, rowTotal, scaleTotal, Math.max, rows.filter, selectionCash, cash.some, blocks.push, addComposition, composition, spent.map, rows.flatMap
  • L497 · monthReport calls (conditional paths may differ): state.subscriptions.filter, monthEnd, charges, cashRows, cashOf, slice, addMonths, rows.filter, cashMonth, rowTotal, compare, sum, yearlyCharges.some, addDays, movement, Math.abs, filter, events, mv.items.map, sort
  • L607 · cumulativeTrend calls (conditional paths may differ): Number.isFinite, months.reduce, fin, months.findIndex, today.slice, Number, slice, monthEnd, Math.max
  • L622 · yearOutlook calls (conditional paths may differ): Number, today.slice, state.subscriptions.filter, through.slice, cashRows, rowTotal, bySub.get, bySub.set, sort, filter, map, bySub.values, Math.abs, isStopped, effectiveStatus, Number.isFinite, a.sub.id.localeCompare, slice, addMonths, nextMonth.slice
  • L726 · allOutlook calls (conditional paths may differ): state.subscriptions.filter, scoped.subscriptions.reduce, cashRows, rows.reduce, since.slice, today.slice, slice, addMonths, months.push, cashMonth, rows.filter, r.date.startsWith, map, months.map, m.month.slice, cashSpan, rowTotal, rankBySub, spendPieces
  • L759 · spendPiecesBetween calls (conditional paths may differ): spendPieces, cashRows, state.subscriptions.filter
  • L763 · spendPieces calls (conditional paths may differ): out.get, emptyTotal, sumTotals, scaleTotal, rowTotal, out.set, rows.filter, chargeSources, add, sort, filter, out.values, out.delete
  • L791 · rankBySub calls (conditional paths may differ): bySub.get, emptyTotal, sumTotals, rowTotal, bySub.set, sort, filter, bySub.values, a.sub.id.localeCompare
  • L822 · asDated calls (conditional paths may differ): addDays, lastCharge
  • L830 · subscriptionTenure calls (conditional paths may differ): state.subscriptions.filter, firstDay, subs.map, asDated, servedTime, dated.get, isStopped, effectiveStatus, xs.reduce, rows.filter, current, rows.reduce, start.slice, today.slice, slice, addMonths, addDays, byMonth.push, subs.filter, avg
worker/ai/report-tools.ts ↗
  • L41 · listFilter calls (conditional paths may differ): String, str, categoryRef, method.toLowerCase, methodRef, listWords, Array.isArray, f.subscriptions.map, findSub
  • L70 · fxGaps calls (conditional paths may differ): chargesBetween, chargeFxMissing, g.months.add, c.date.slice, g.currencies.add, g.subs.add, g.rows.push, ref, round, transfersOf, Number.isFinite, transferValue, tr.date.slice, phaseSubs, convertOn
  • L106 · chargeOut calls (conditional paths may differ): chargeFxMissing, ref, round, partialOut, num
  • L123 · compOut calls (conditional paths may differ): rows.map, st.methods.find, t, partialOut, num, pct
  • L143 · blockOut calls (conditional paths may differ): blockValues, Object.assign, partialOut, Object.fromEntries, map, Object.entries
  • L152 · blockValues calls (conditional paths may differ): m.subs.has, num, h, cmp, b.rows.map, v, mv.items.map, ref, sub, compOut, chargeOut, round, b.refunds.map
  • L266 · monthReportOut calls (conditional paths may differ): test, t, monthReport, st.subscriptions.filter, monthEnd, fxGaps, addMonths, g.months.has, d.slice, bad, transferRows, partialOut, num, r.blocks.find, emptyTotal, gapsOut, r.blocks.map, blockOut, r.months.map, monthOut
  • L318 · scopeReportOut calls (conditional paths may differ): filteredSubs, fxGaps, monthEnd, slice, addMonths, scopeReport, report.blocks.find, emptyTotal, num, sort, Object.keys, partialOut, lifecycleTotal, report.lifecycle.stops.map, ref, Math.max, diffDays, report.lifecycle.timeline.map, report.lifecycle.spending.map, subs.map
shared/stop-form.ts ↗
  • L22 · stopForm calls (conditional paths may differ): currentPhase, nonrenewalDate, String, settlementCurrencyOf
  • L42 · stopPreview calls (conditional paths may differ): currentPhase, billingPeriod, stopSummary
  • L53 · applyStopForm calls (conditional paths may differ): JSON.stringify, stopForm, t, reopenStopped, reactivate, cancelAtEnd, stopPreview, Number.isFinite, Number, currentPhase, indexOf, filter, open.phases.map, stopSubscription, subError, statusChangeError
  • L112 · editStop calls (conditional paths may differ): effectiveStatus, t, stopForm, Object.assign, currentPhase, String, applyStopForm
shared/subscription-schema.ts ↗
  • L11 · record calls (conditional paths may differ): Object.fromEntries, map, Object.entries, required.includes, String
shared/receivable-edit.ts ↗
  • L18 · receivableOf calls (conditional paths may differ): addMonths, collectCurrencyOf, t, proxyLedger, memberLedger, collectionAnchor, share.members.map
  • L43 · openPeriods calls (conditional paths may differ): periods.filter, isSettled, t, open.slice
  • L60 · recordReceipt calls (conditional paths may differ): validDay, t, receivableOf, filter, input.periods.map, r.periods.find, resolveKey, periodsTotal, toUpperCase, Math.round, convertOn, Number.isFinite, r.save, putRecord
  • L77 · recordWaiver calls (conditional paths may differ): validDay, t, receivableOf, r.save, putRecord
  • L86 · deleteRecord calls (conditional paths may differ): receivableOf, find, r.save, removeRecord
  • L93 · recordProxyCharge calls (conditional paths may differ): t, validDay, Number.isFinite, periods.some, proxyLedger, filter, sort, a.charge.localeCompare
Build and deployment pipeline · 2 GitHub Actions workflows

Repository CI declarations, separate from runtime request processing. Job dependencies and conditions are shown as written; long commands are shortened with an ellipsis; a workflow file does not prove a recent successful run.

CI · .github/workflows/ci.yml ↗

Triggers: push, pull_request

check · no job dependencies declared

  1. actions/checkout@v4actions/checkout@v4
  2. actions/setup-node@v4actions/setup-node@v4
  3. Shell commandnpm ci
  4. Shell commandnpm run lint
  5. Shell commandnpm run format:check
  6. Shell commandnpm run i18n:check
  7. Shell commandnpm test
  8. Shell commandnpm run build
Docker image · .github/workflows/docker.yml ↗

Triggers: push, workflow_dispatch

image · no job dependencies declared

  1. actions/checkout@v4actions/checkout@v4
  2. docker/setup-qemu-action@v3docker/setup-qemu-action@v3
  3. docker/setup-buildx-action@v3docker/setup-buildx-action@v3
  4. docker/login-action@v3docker/login-action@v3
  5. Shell commandecho "enabled=${{ secrets.DOCKERHUB_TOKEN != '' }}" >> "$GITHUB_OUTPUT"
  6. docker/login-action@v3docker/login-action@v3Condition: steps.hub.outputs.enabled == 'true'
  7. docker/metadata-action@v5docker/metadata-action@v5
  8. docker/build-push-action@v6docker/build-push-action@v6
package.json ↗
  • build: node scripts/migrations.mjs && node scripts/i18n.mjs && tsc --noEmit && vite build app
  • deploy: npm run build && node scripts/wrangler.mjs deploy && node scripts/wrangler.mjs d1 migrations apply DB --remote

Full upstream document by @imetn · README.md · snapshot e794985

Calends logo

Calends

Track subscription charges, shared payments and prepaid balances on your own server.

Website · Live demo · Quick start · AI & MCP · 简体中文

Calends overview

Overview · Fictional demo data


Billing and payment history

Track what you pay, when it renews, and who owes you. Calends brings subscriptions, shared plans and prepaid balances into one private, single-owner ledger, with an optional read-only public page and AI access through MCP.

  • Flexible billing — monthly, yearly, custom cycles and lifetime purchases, with trials, intro prices, bonus periods, pauses, advance charging and cancellation deadlines.
  • Billing history — keep price, plan and region changes, gaps and overlapping subscriptions. See total spent, actual monthly cost and every charge.
  • Transfers and split payments — count second-hand takeovers and resales in net cost, and split a charge across multiple payment methods.
  • Multiple currencies — historical exchange rates for past charges, current rates for forecasts, and your choice of display currency.
  • Balances and shared plans — track prepaid credit and gift cards, estimate when balances run out, and manage payments for friends. Shared plans have dated collection rules, member overrides, per-period receipts with skipped-period collection, prepayments and waivers, overdue tracking and exit refunds; the member sheet uses a monthly period grid with receipt details on click, prepayments and waivers, while people have private avatars, custom colors and a separate account for each plan.
  • Reports and calendar — spending trends, cash vs. accrual, prepaid value, cancellation savings, subscription checkups and a shareable year in review. Includes month/year calendars and a private ICS feed.
  • Search — find Chinese names by full pinyin, initials or mixed syllables in the subscription list, command palette and pickers.
  • Renewal reminders — email, Telegram, Lark/Feishu, DingTalk, WeCom, Slack, Discord, Bark, ntfy, ServerChan and custom webhooks.
  • AI access — query and update your ledger from an MCP-compatible assistant, with scoped tokens and previews for write operations.
  • Import external ledgers — preview and import CSV files, ZIP backups and databases from Wallos and other services in your browser.
  • Storage and access — password and passkey sign-in, remote device sign-out, optional public sharing with server-side masking, JSON backup/restore and CSV import/export. Available in 14 languages.
Explore the subscription timeline

Calends subscription timeline with billing periods, plan changes and payment methods

Billing periods and subscription history · Fictional demo data

Quick start

Try the live demo to explore before deploying. It is shared by all visitors and resets daily; external notifications are disabled.

Open Calends at / and sign in at /login. After signing in, / redirects to your configured start page, defaulting to /overview. Public sharing is optional: enable it in Settings to make the read-only page available at /public. See routes and login redirects for details.

Cloudflare Workers

Use one-click deployment with Workers and D1, or deploy from source:

git clone https://github.com/imetn/calends
cd calends
npm ci
npx wrangler login
node scripts/wrangler.mjs secret put SETUP_TOKEN
npm run deploy

Use a long random string for SETUP_TOKEN. The deploy command builds the app, provisions D1 on first deployment and applies migrations. Open https://<your-worker>.workers.dev/, enter the token on the login page and set your password.

Docker

Run the prebuilt image directly:

docker run -d --name calends -p 8787:8787 -v calends-data:/data xizozhe/calends:latest
docker logs calends

Open http://localhost:8787/ and use the setup token printed in the logs on the login page. The named volume calends-data persists SQLite data at /data. You can also use ghcr.io/imetn/calends:latest. For production, pin a version tag such as 1.17.0 instead of latest.

Alternatively, build from source with Compose. From a source checkout, Compose (in docker/) builds the included Dockerfile and starts the app:

cd docker
docker compose up -d --build
docker compose logs calends

The Compose configuration persists SQLite data in a named volume mounted at /data. Building locally does not require a published Calends image or a Cloudflare account. See the Docker guide for image registries, upgrades and Compose options.

For public access, enable HTTPS through a reverse proxy and set TRUST_PROXY=1. Add a passkey after setup in Settings → Security.

See the self-hosting guide for custom domains, configuration, email reminders, subpath deployment and a prompt to delegate deployment to your AI agent.

AI & MCP

Ask “What renews next week?”, “How much did I spend this year?” or “Netflix went up to $17.99 from today.” Calends provides 34 tools over a stateless MCP endpoint at /mcp using Streamable HTTP.

On the AI access page (/connect), create a read-only or read/write token, then connect your client. Opening /mcp in a browser takes you to this page; MCP clients use the /mcp endpoint:

Claude Code

claude mcp add --transport http calends https://your-domain/mcp --header "Authorization: Bearer <token>"

Codex

export CALENDS_TOKEN='<token>'
codex mcp add calends --url https://your-domain/mcp --bearer-token-env-var CALENDS_TOKEN

Keep CALENDS_TOKEN available in the environment that launches Codex. Tools cover subscriptions, spending, trends, receivables and insights, plus changes to prices, plans, status and payments. Every write tool supports dry_run; AI access can be turned off in Settings.

Contributing

Bug reports, translations and pull requests are welcome. See CONTRIBUTING.md for local setup, checks and contribution guidelines.

Start with the architecture and code standards before changing a subsystem. Coding agents use AGENTS.md; CLAUDE.md imports the same instructions.

Built with React 19, Vite, HeroUI / React Aria, shadcn/ui and Cloudflare Workers + D1 (SQLite). Billing, exchange rates and other domain logic are shared between the browser and Worker in shared/.

Translations live in shared/locales/<locale>/<namespace>.json, with English and Simplified Chinese as the source languages.

About the name

Calends were the first day of every Roman month, when accounts were settled and debts called in. It is also where the word calendar comes from.

License

Calends is open source under the MIT License; see LICENSE. You may use, modify and redistribute it, including commercially, provided you retain the copyright and license notices in copies or substantial portions of the software. If you fork Calends, please use a distinct name and credit Calends as the upstream source so users can distinguish your version from this project.

Frequently asked about Calends

What is Calends?+

Calends is a self-hosted Rocket Money alternative built on the Cloudflare developer platform. A private ledger for subscriptions, renewals and shared payments

What does Calends replace?+

Calends is listed as an alternative to Rocket Money. Compare the features and tradeoffs before migrating.

What Cloudflare primitives does Calends use?+

Calends is built on D1, Workers.

How much does Calends cost to run?+

One Worker with D1 and a 15-minute cron fits the Workers Free plan for a single owner. Optional email reminders need Cloudflare Email Sending, which requires Workers Paid for arbitrary recipients; webhook and chat-app reminders do not. Email reminders are commented out by default; enabling them needs a verified sending domain and Workers Paid. A separate docker/ Wrangler file supports a self-hosted container path; the catalog describes the Cloudflare path. The public demo resets daily and disables external notifications. Source and configuration review establishes a deployment path and conditional costs; this candidate was not executed or load-tested. Official Cloudflare pricing pages saved 31 Oct 2026 (Markdown editions of developers.cloudflare.com pricing documentation). Check current Cloudflare pricing before deploying.

Is Calends open source?+

The upstream repository declares the MIT license. Read its terms at https://raw.githubusercontent.com/imetn/calends/e794985ae8d3cc6e6d25e692bbc0c2cb60c4fb6c/LICENSE. Source code and contributor credit are available at https://github.com/imetn/calends.

Community rating

No ratings yet. Tried this project? Share your experience.

One rating per verified account. You can change or remove yours. Accounts are email verified; use of the software is self-reported.

Discuss your experience ↓
Sign in to rate

Discussion · 0

sign in to comment →
No comments yet — be the first.