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
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).
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.
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 ↗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 ↗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
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→ D1ASSETS→ Static assets
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→ D1ASSETS→ 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.
- 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
- 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
- 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
- L483 · channelsOf calls (conditional paths may differ): t
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- L41 · mergePrefs calls (conditional paths may differ): Array.isArray, Object.entries, KEY_RE.test, Object.keys
- 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
- 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
- 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
- L37 · connectionInfo calls (conditional paths may differ): map, TOOLS.filter
Environment references: ctx.env.SITE_URL
- 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
- 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
- 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
- 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
- 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
- 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
- L14 · readJson calls (conditional paths may differ): req.json, t
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- L23 · iconInput calls (conditional paths may differ): trim, String, t, icon.slice, test, icon.startsWith, bad, icon.indexOf, isDataIcon, Math.floor, graphemes
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- L11 · record calls (conditional paths may differ): Object.fromEntries, map, Object.entries, required.includes, String
- 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.
Triggers: push, pull_request
check · no job dependencies declared
- actions/checkout@v4
actions/checkout@v4 - actions/setup-node@v4
actions/setup-node@v4 - Shell command
npm ci - Shell command
npm run lint - Shell command
npm run format:check - Shell command
npm run i18n:check - Shell command
npm test - Shell command
npm run build
Triggers: push, workflow_dispatch
image · no job dependencies declared
- actions/checkout@v4
actions/checkout@v4 - docker/setup-qemu-action@v3
docker/setup-qemu-action@v3 - docker/setup-buildx-action@v3
docker/setup-buildx-action@v3 - docker/login-action@v3
docker/login-action@v3 - Shell command
echo "enabled=${{ secrets.DOCKERHUB_TOKEN != '' }}" >> "$GITHUB_OUTPUT" - docker/login-action@v3
docker/login-action@v3Condition: steps.hub.outputs.enabled == 'true' - docker/metadata-action@v5
docker/metadata-action@v5 - docker/build-push-action@v6
docker/build-push-action@v6
build: node scripts/migrations.mjs && node scripts/i18n.mjs && tsc --noEmit && vite build appdeploy: npm run build && node scripts/wrangler.mjs deploy && node scripts/wrangler.mjs d1 migrations apply DB --remote
Repository README
View original on GitHub ↗Full upstream document by @imetn · README.md · snapshot e794985
Calends
Track subscription charges, shared payments and prepaid balances on your own server.
Website · Live demo · Quick start · AI & MCP · 简体中文
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
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.

Discussion · 0
sign in to comment →