Goal
We want completely pure functions wherever possible. This issue is the full inventory of impure functions across the monorepo, produced by a systematic audit of all ~2,700 non-test TypeScript source files in apps/* and packages/* (excluding apps/e2e, apps/ios, apps/android, tests, and generated migrations).
Definition used. A function is impure if it: (a) performs I/O (database, network, filesystem, process spawning, browser storage, sockets/IPC); (b) is nondeterministic (Date.now(), new Date() with no argument, Math.random(), crypto.randomUUID/randomBytes, nanoid/createId); (c) reads mutable external state (process.env at call time, window/document/navigator, module-level mutable variables); (d) mutates external state (module-level caches/maps, singletons, its arguments); or (e) has observable side effects (console.*, event emission, timers). Deterministic helpers that only transform their inputs (including new Date(value) parsing a supplied value, and functions that take now/env/fetch as parameters) are counted as pure and excluded.
Headline counts (named functions; boundary layers like API routes and repositories grouped per file):
| Area |
Approx. impure functions |
Dominant impurity |
packages/lib |
~420 |
Database I/O (~300 across ~70 files) |
packages/db |
~35 + ~55 schema files with createId/new Date default closures |
DB singletons, schema defaults |
packages/sdk |
~9 |
Network (impurity is constructor-injected — closest to pure) |
packages/cli |
~125 |
Network + filesystem (command handlers) |
apps/web |
~250 named in src/lib/stores/hooks + 356 API route files + component-layer markers (227 nondeterminism / 230 browser-global / 157 console occurrences) |
DB/network I/O, wall-clock reads |
apps/processor |
~95 |
S3/DB I/O, module state |
apps/realtime |
~55 |
Socket emission, module registries |
apps/control-plane |
~40 |
Shell/docker exec, Stripe |
apps/desktop (main) |
~45 |
IPC, fs, module singletons |
apps/admin |
~55 named + 26 route files |
DB analytics queries |
apps/marketing |
~8 named + 1 route |
Env reads |
apps/atlas |
3 |
Browser globals |
Total: roughly 1,100+ impure functions (plus per-file-grouped boundary handlers).
Highest-value purification targets
These are pure computations contaminated by avoidable impurity — fix by injecting now/rng/env/ports as parameters:
evaluateFunction — packages/lib/src/sheets/functions.ts:96 — the spreadsheet formula evaluator calls new Date() (NOW/TODAY) and Math.random() (RAND/RANDBETWEEN) inline; inject a clock/rng.
getTodayUTC/getTomorrowMidnightUTC/getSecondsUntilMidnightUTC — packages/lib/src/services/date-utils.ts:13,28,44 — pure date math reading the wall clock; take now as a parameter.
isOnPrem/isTenantMode/isCloud/isBillingEnabled — packages/lib/src/deployment-mode.ts:34-46 — read process.env at call time (areCloudIntegrationsAllowed already shows the pure pattern).
csrf-utils / broadcast-auth — packages/lib/src/auth/csrf-utils.ts:24, packages/lib/src/auth/broadcast-auth.ts:28,88 — token generation/validation mixes randomBytes, Date.now(), env reads, and console.error.
currentWindowStart — packages/lib/src/security/distributed-rate-limit.ts:218 — now = Date.now() default parameter bakes the clock into the signature (same anti-pattern in calculateVersionExpiresAt — packages/db/src/schema/versioning.ts:80, isDormant — apps/admin/src/lib/dormancy.ts:9, and the sdk/cli default-deps constructors).
components/calendar/calendar-types.ts:219 (apps/web) — exported date-bucketing helpers read new Date() for "today" instead of accepting it.
- Workflow/trigger executors —
apps/web/src/lib/workflows/workflow-executor.ts, calendar-trigger-executor.ts, zoom/webhook-trigger-executor.ts — Date.now() embedded in computation and console.* in logic paths.
- In-memory rate limiters & module caches —
packages/lib/src/auth/rate-limit-utils.ts:22, packages/lib/src/security/distributed-rate-limit.ts:66, apps/web/src/lib/auth/cron-auth.ts:18-34 (usedNonces), apps/web/src/lib/ai/core/model-capabilities.ts:29-118, apps/web/src/lib/ai/core/stream-abort-registry.ts:30-117 — module-level mutable Maps + setInterval at import time.
credit-pricing module-init env reads — packages/lib/src/billing/credit-pricing.ts:12-39 — pricing constants frozen from process.env at import.
- Sandbox env gates —
packages/lib/src/services/sandbox/{can-run-code,checkpoint-policy,containment,egress-ip,quota}.ts — call-time process.env reads inside decision logic.
Good existing patterns to replicate: packages/sdk (all impurity constructor-injected; operations layer fully pure), packages/cli's pure modules (argv/parse.ts, config/resolve.ts, auth/resolve.ts, keys/logic.ts), packages/lib pure decision modules (step-up-decisions.ts, credit-core.ts, compute-reorder-plan.ts, breach-assessment.ts), and apps/web's machine-workspace/workspace-reducer.ts + sheet/core/touch.ts (injected clock).
Note: boundary layers (API route handlers, DB repositories, socket handlers, CLI entrypoints) are inherently effectful — the goal there is not to make them pure but to keep them thin, pushing decisions into pure functions. They are inventoried below for completeness.
Full inventory
packages/lib — Impure Function Audit
Scope: packages/lib/src (non-test .ts). For inherently-effectful boundary modules (DB repositories, network/sandbox clients) exported functions are grouped per file; pure-computation-contaminated-by-avoidable-impurity cases are called out individually. A function with several impurities is listed once under its most severe category with all reasons.
Nondeterminism (time/randomness)
Otherwise-pure computations that read the clock / RNG inline instead of taking now/entropy as a parameter:
evaluateFunction — packages/lib/src/sheets/functions.ts:96 — a pure spreadsheet-formula evaluator, but the NOW/TODAY cases call new Date() (337, 333) and RAND/RANDBETWEEN call Math.random() (451, 462); no clock/rng injected.
getTodayUTC / getTomorrowMidnightUTC / getSecondsUntilMidnightUTC — packages/lib/src/services/date-utils.ts:13,28,44 — pure date math contaminated by new Date() / Date.now() read at call time (parseDateUTC:56 is pure).
generateCsrfToken — packages/lib/src/auth/csrf-utils.ts:24 — randomBytes(32) (25) + Date.now() (26); also reads process.env.CSRF_SECRET.
validateCsrfToken — packages/lib/src/auth/csrf-utils.ts — Date.now() (60) for expiry, console.error (74), reads CSRF_SECRET (5).
generateBroadcastSignature / verifyBroadcastSignature — packages/lib/src/auth/broadcast-auth.ts:28,88 — Date.now() timestamps; verify also console.error (98) and reads REALTIME_BROADCAST_SECRET (9).
currentWindowStart — packages/lib/src/security/distributed-rate-limit.ts:218 — default param now = Date.now() bakes the clock into the signature.
getMonitoringCutoff — packages/lib/src/compliance/retention/monitoring-retention.ts:47 — new Date(Date.now() - ...).
generateVerificationToken — packages/lib/src/auth/verification-utils.ts:28 — randomBytes(32) + new Date(Date.now()+...) (29).
generateOpaqueToken — packages/lib/src/auth/opaque-tokens.ts:18 — randomBytes(32).
- token minter —
packages/lib/src/auth/token-utils.ts:17 — cuid2 seeded from randomBytes.
generateUnsubToken — packages/lib/src/services/notification-email-service.ts:57 — randomBytes + new Date expiry (67).
generateSessionRowId — packages/lib/src/monitoring/activity-logger.ts:118 — randomBytes(32).
Note: incidents/breach-assessment.ts, compliance/dsr/sla.ts, services/machines/branch-session.ts, auth/invite-token.ts, auth/oauth/issue-tokens.ts, compliance/retention/chat-retention.ts correctly take now/detectedAt as a parameter and are PURE.
Environment reads
isOnPrem / isTenantMode / isCloud / isBillingEnabled — packages/lib/src/deployment-mode.ts:34,38,42,46 — read process.env.DEPLOYMENT_MODE at call time (areCloudIntegrationsAllowed:30 takes mode and is pure).
isFlyEnvironment — packages/lib/src/security/client-ip.ts:72 — reads process.env.FLY_APP_NAME.
envInt/envBool/envFloat + module-level constants — packages/lib/src/billing/credit-pricing.ts:12,22,31,39… — read process.env at module-init; openRouterBaseUrl — :343 — call time.
resolveMonitoringRetentionConfig / resolveAiUsageRetentionDays — packages/lib/src/compliance/retention/monitoring-retention.ts:29-32,43.
resolveArchivalConfig — packages/lib/src/compliance/retention/activity-log-archival.ts:63-65.
- retention-engine env read —
packages/lib/src/compliance/retention/retention-engine.ts:144.
validateEnv / getEnvErrors / isEnvValid / getValidatedEnv — packages/lib/src/config/env-validation.ts:190,210,226,238 (getValidatedEnv also caches).
getResendConfig / resolveAppUrl — packages/lib/src/services/email-service.ts:6,27.
resolveBaseUrl / resolveMarketingBase — packages/lib/src/services/broadcast/core.ts:152,169 — default param env = process.env.
getUserIndexKey / isPiiEncryptionEnabled — packages/lib/src/auth/user-repository.ts:41,52.
- master-key readers —
packages/lib/src/encryption/encryption-utils.ts:20, packages/lib/src/encryption/user-crypto.ts:49, packages/lib/src/audit/audit-query.ts:58, packages/lib/src/audit/audit-ingest-writer.ts:46, packages/lib/src/audit/security-audit-repository.ts:32 — read ENCRYPTION_KEY at call time.
- anchor-gate env —
packages/lib/src/audit/full-audit-verification.ts:241-242.
getSessionIdleTimeout — packages/lib/src/auth/constants.ts:28 — env + console.warn (32).
verifyGoogleToken / verifyAppleToken — packages/lib/src/auth/oauth-utils.ts:37-101 — env (also network).
- passkey
PASSKEY_CONFIG module constants — packages/lib/src/auth/passkey-service.ts:30-32.
- onprem link base —
packages/lib/src/auth/onprem-setup-link.ts:44-46.
- NODE_ENV gating —
packages/lib/src/security/auth-handoff-sweep.ts:28, security/jti-revocation.ts:42,80,116,139, security/distributed-rate-limit.ts:338,424,485,698, auth/exchange-codes.ts:69,116, auth/passkey-register-handoff.ts:80.
- ClickHouse config —
packages/lib/src/observability/clickhouse-client.ts:107-152.
- Logger config —
packages/lib/src/logging/logger.ts:82-100,287,301,305,309,330; logging/logger-config.ts:306; logging/logger-database.ts:265.
sanitizeCss — packages/lib/src/canvas/sanitize-css.ts:62,83 — NODE_ENV gate + console.warn.
- Sandbox env gates:
isCodeExecutionEnabled — services/sandbox/can-run-code.ts:85; isCheckpointBeforeAgentBatchEnabled — checkpoint-policy.ts:51; isContainmentVerified — containment.ts:167; getConfiguredEgressIpTag — egress-ip.ts:34; getMachineMaxActiveSeconds — quota.ts:140; resolveSpritesToken — sandbox-client/sprites.ts:354; getSandboxSessionSecret — machine-session-manager.ts:88; machine-storage-measure.ts:30.
credit-emit — packages/lib/src/billing/credit-emit.ts:40; notifications — notifications/notifications.ts:40; APNs config notifications/push-notifications.ts:40-42,226-227; cost-reconcile — billing/cost-reconcile.ts:68; Resend suppression — compliance/erasure/resend-suppression-client.ts:50-51,108-109.
page-content-store — packages/lib/src/services/page-content-store.ts:42-56,154-155.
voice-pricing — packages/lib/src/monitoring/voice-pricing.ts:30.
- UploadSemaphore ctor —
packages/lib/src/services/upload-semaphore.ts:54.
- quota readers —
packages/lib/src/services/sandbox/quota.ts:24,141.
Module-level mutable state
attempts Map + module-load setInterval — packages/lib/src/auth/rate-limit-utils.ts:22,25; mutated by checkRateLimit:39, resetRateLimit:132, getRateLimitStatus:140.
inMemoryAttempts Map / cleanupIntervalId / postgresAvailableLogged — packages/lib/src/security/distributed-rate-limit.ts:66,67,206; startCleanupInterval:73 (module-load setInterval, 102), shutdownRateLimiting:93, inMemoryCheckRateLimit:106.
UploadSemaphore singleton — packages/lib/src/services/upload-semaphore.ts:43-60; acquireUploadSlot:75, releaseUploadSlot:112, cleanupStaleSlots (acquire also Date.now+Math.random, 98).
cachedEnv memo — packages/lib/src/config/env-validation.ts:232.
cached binding — packages/lib/src/audit/audit-db-binding.ts:39; resolveAuditDbBinding:45, resetAuditDbBindingForTests:65.
defaultAppendPath / defaultService singletons — packages/lib/src/audit/security-audit.ts:427,445,161.
alertHandler / periodicTimer / verificationInProgress — packages/lib/src/audit/security-audit-alerting.ts:72,545,546 (periodic setInterval 562).
incidentNotifier — packages/lib/src/incidents/incident-service.ts:51; setIncidentNotifier:54.
hookFn — packages/lib/src/logging/siem-error-hook.ts:16; setSiemErrorHook:18, fireSiemErrorHook:26.
activityBroadcastHook / workflowTriggerHook — packages/lib/src/monitoring/activity-logger.ts:374,400.
- APNs JWT cache —
packages/lib/src/notifications/push-notifications.ts:29,30; getApnsJwtToken:32.
resendInstance memo — packages/lib/src/services/email-service.ts:17.
_s3 singleton — packages/lib/src/services/page-content-store.ts:37.
- concurrency/activity maps —
packages/lib/src/services/sandbox/quota.ts; acquireCodeExecutionSlot:58, releaseCodeExecutionSlot:70, recordMachineActivity:206, resets :80,220,225.
- checkpoint state map —
packages/lib/src/services/sandbox/checkpoint-policy.ts:143-185.
- sprite handle cache —
packages/lib/src/services/sandbox/sandbox-client/sprites.ts:1375.
Logger buffers + flushTimer — packages/lib/src/logging/logger.ts:79,359, logging/logger-browser.ts:84,208.
- ClickHouse buffer —
packages/lib/src/observability/clickhouse-buffer.ts:102.
Database I/O (grouped per file; most also stamp new Date() and/or console.error)
- Repositories:
repositories/account-repository.ts:32, activity-log-repository.ts:17, agent-repository.ts:47, broadcast-repository.ts:64, data-subject-request-repository.ts:41, drive-repository.ts:28, page-repository.ts:79 — all exported CRUD.
- Permissions:
permissions/permissions.ts — getDriveIdsForUser, getUserAccessLevel, canUserViewPage/EditPage/SharePage/DeletePage, isDriveOwnerOrAdmin, isUserDriveMember, getUserAccessiblePagesInDrive(+WithDetails), getUserDriveAccess, getUserDrivePermissions, usersShareDrive, getBatchPagePermissions; permission-mutations.ts:224,359; share-link-service.ts:94-453 (9 fns); accessible-page-ids.ts:12; agent-permissions.ts:30,64,73; app-permissions.ts:60-463 (10 fns; resolveExplicitAppRoleAccess:89, getScopedDriveMembership:428 pure); membership-queries.ts:23,36,53,63 (resolveCustomRolePermissions:14 pure); file-access.ts:18; rollback-permissions.ts:53.
pages/circular-reference-guard.ts — DB reads.
- Integrations:
integrations/repositories/{audit,connection,provider,config,grant}-repository.ts — all exported fns; integrations/saga/execute-tool.ts:77 (Date.now timing); integrations/resolution/resolve-agent-integrations.ts.
- Auth:
auth/account-lockout.ts:62-148, device-auth-utils.ts (console 122-384), exchange-codes.ts:53 (randomBytes+env), magic-link-service.ts:107,126, oauth-utils.ts (network+env, new Date 233,276), passkey-service.ts, passkey-register-handoff.ts:59, pkce.ts:41,47, session-repository.ts (new Date 55-139), session-service.ts:68, step-up-service.ts (pure decisions in step-up-decisions.ts), token-lookup.ts, user-repository.ts, verification-utils.ts:28, onprem-setup-link.ts:32.
- Security:
security/jti-revocation.ts:36,75, distributed-rate-limit.ts (checkDistributedRateLimit), auth-handoff-sweep.ts.
- Audit:
audit/audit-ingest-writer.ts:82, security-audit-repository.ts:83, security-audit-chain-verifier.ts:178,207,318, security-audit.ts:160, security-audit-alerting.ts:97, audit-log.ts, audit-query.ts, co-stream-reconciliation.ts, full-audit-verification.ts, admin-read-audit.ts, co-stream.ts.
- Billing:
billing/cost-reconcile.ts, credit-backfill.ts:68, credit-balance.ts:108, credit-consume.ts, credit-funding.ts, credit-gate.ts:144, live-concurrency-query.ts:26, machine-payer.ts (credit-core.ts is pure).
- Compliance:
compliance/retention/{retention-engine.ts:25-143,activity-log-archival.ts:96,monitoring-retention.ts,chat-retention.ts}, compliance/erasure/{pseudonymize-repository,pseudonymize-targets,run-erasure,revoke-integration-tokens.ts:37,drive-disposition,email-suppression,ai-provider-erasure}.ts, compliance/export/gdpr-export.ts, compliance/file-cleanup/orphan-detector.ts.
- Monitoring/observability:
monitoring/activity-logger.ts (logActivity + helpers; randomBytes, console 48-587, module hooks), ai-monitoring.ts, hash-chain-verifier.ts:146 (console 329-573), observability/analytics-inserts.ts:76, analytics-reads.ts, analytics-read-core.ts, analytics-gdpr.ts, error-resolutions.ts:100, logging/logger-database.ts (write* fns), logging/monitoring-purge.ts:42, logging/ai-usage-purge.ts.
incidents/incident-service.ts:71 — createIncident.
- Notifications:
notifications/notifications.ts (DB+fetch+env+console), notifications/push-notifications.ts (DB+APNs network+env+cache).
content/version-resolver.ts.
- Services:
services/storage-limits.ts:34-382 (11 fns; pure helpers excluded), storage-repository.ts, drive-service.ts, drive-member-service.ts, drive-role-service.ts:83-393 (7 fns; validators pure), drive-agent-service.ts:99-496 (7 fns), drive-search-service.ts:117,162,350, calendar-event-drive-service.ts:130-356 (6 fns), channel-message-repository.ts, dm-message-repository.ts, app-shell-service.ts:264, page-payload-service.ts:250, page-version-service.ts, quote-enrichment.ts, notification-email-service.ts, attachment-upload.ts:51, attachment-upload-repository.ts, validated-service-token.ts:178-669 (7 fns), subdomain-allocation.ts, reorder/locked-batch-reorder.ts:47 (compute-reorder-plan.ts:19 pure), broadcast/{audience.ts:70,record-adapter.ts,engine.ts,transactional-engine.ts}.
- Machines:
services/machines/{machine-branches-store.ts:102,machine-projects-store.ts:63,machine-workspaces-store.ts:99,agent-terminals-store.ts:90,machine-settings.ts:88-136,machine-projects.ts:187-291,machine-access.ts:29,39,machine-list.ts:57,87,machine-workspaces.ts:104-229,agent-terminals.ts:265-536,machine-branches.ts:314-717,machine-orphan-reconcile.ts:184} — DB (+ sandbox process I/O for spawn/kill).
- Sandbox:
services/sandbox/{machine-session-manager.ts:392,555,575,machine-session.ts:93,machine-billing.ts,machine-storage-billing.ts:110,212,machine-storage-reconcile.ts:159,github-token.ts:6,tool-gate.ts:100,audit.ts:230,quota.ts:107}.
Network I/O
assertSafeUrl/safe-fetch — packages/lib/src/security/url-validator.ts:482 — SSRF-guarded fetch.
fetchWithTimeout — packages/lib/src/utils/fetch-with-timeout.ts:38.
buildWebhookSiemErrorHook — packages/lib/src/logging/siem-error-hook.ts:35 — POST (44), console (56), new Date (49).
exchangeCodeForTokens / refreshTokens — packages/lib/src/integrations/oauth/oauth-handler.ts:125,178 — fetch + PKCE randomBytes (217).
executeHttpRequest — packages/lib/src/integrations/execution/http-executor.ts — fetch (91), Date.now timing.
billing/cost-reconcile.ts:71, billing/credit-emit.ts:47, notifications/notifications.ts:47, notifications/push-notifications.ts (HTTP/2 APNs), compliance/erasure/revoke-integration-tokens.ts:37, compliance/erasure/resend-suppression-client.ts:50-109, services/email-service.ts:71 (Resend SDK).
Note: fetch(...) at packages/lib/src/forms/form-html.ts:55 is inside a generated client-side HTML string — not a runtime call in this package.
Filesystem/process I/O
writePageContent / readPageContent / readPageContentFromFilesystem / isContentCompressed / getContentMetadata — packages/lib/src/services/page-content-store.ts:80,132,153,166,178.
validatePath — packages/lib/src/security/path-validator.ts:17 — realpath/stat.
- Sprite sandbox client —
packages/lib/src/services/sandbox/sandbox-client/sprites.ts — runSpawned:379, killSpriteSession:873, createSpritesSandboxClient:1406 (+ internal spawn/fs at 282,700,940-956,1329); sprite-tasks.ts:330,453; sprite-machine-host.ts:244.
- Sandbox tool runners —
packages/lib/src/services/sandbox/tool-runners.ts:605,744,801,878 — runBashInSandbox, writeSandboxFile, readSandboxFile, editSandboxFile.
- Machine remote FS —
packages/lib/src/services/sandbox/machine-fs.ts:68-343 — 8 fns.
runGitInSandbox — packages/lib/src/services/sandbox/git-tool-runners.ts:90.
resolveMachineMergeBase / listMachineDiffFiles / readMachineDiffPair — packages/lib/src/services/sandbox/machine-diff.ts:68,113,214; readMachineGitBlob — machine-git-blob.ts:78.
refreshStorageMeasurement — packages/lib/src/services/sandbox/machine-storage-measure.ts:144 — du spawn.
setupMemoryProtection — packages/lib/src/services/memory-monitor.ts:50 — setInterval; getProcessMemory:34 — process.memoryUsage().
Console/logging side effects (only impurity)
generateDocx / generateExcel — packages/lib/src/content/export-utils.ts:30,135 — console.error; new Date() (100) in workbook metadata.
sanitizeCss — packages/lib/src/canvas/sanitize-css.ts:63,84.
getSessionIdleTimeout — packages/lib/src/auth/constants.ts:32.
- Logger console writers —
packages/lib/src/logging/logger.ts:316-417, logger-browser.ts:251-257.
graceful-shutdown default logError — packages/lib/src/logging/graceful-shutdown.ts:23.
- clickhouse-buffer / analytics-inserts default
logError — observability/clickhouse-buffer.ts:52, analytics-inserts.ts:77.
Count
Approximately 420 impure exported/significant functions in packages/lib/src: Nondeterminism ~14 highlighted; Environment reads ~55; Module-level mutable state ~24; Database I/O ~300+ (across ~70 files); Network I/O ~13; Filesystem/process I/O ~30; Console/logging ~12. Most notable avoidable contaminations: evaluateFunction (sheets), date-utils helpers, csrf-utils/broadcast-auth, currentWindowStart default param, call-time process.env readers in deployment-mode/credit-pricing/sandbox gates.
packages/db, packages/sdk, packages/cli — Impure Function Audit
packages/db
Nondeterminism (time/randomness)
Schema default closures — anonymous callbacks on exported Drizzle table objects. Two markers recur: $defaultFn(() => createId()) (cuid2, random) on primary keys, and $onUpdate(() => new Date()) (wall clock) on updatedAt. Present across ~55 schema files:
auth.ts:11,53,61,106,129,157,210,236, automation-preferences.ts:16, calendar-triggers.ts:22,44, calendar.ts:31,90,116,149,186,203, chat.ts:30,93, commands.ts:27,57, contact.ts:5, conversations.ts:11,18,32, core.ts:12,20,38,86,103,105,132,147,165,182,196, credits.ts:34,63,111, custom-domains.ts:9, dashboard.ts:8,12,32, data-subject-requests.ts:69, display-preferences.ts:13, drafts.ts:7, email-broadcasts.ts:102,119,191, email-notifications.ts:9,22, feedback.ts:14, form-targets.ts:27,59, hotkeys.ts:7, incidents.ts:38, integrations.ts:59,86,102,149,178,218,250,261, machine-agent-terminals.ts:57,77, machine-branches.ts:30,97, machine-projects.ts:30,45, machine-sessions.ts:25,122, machine-workspaces.ts:92, members.ts:12,26,44,53,74,90,181, monitoring.ts:31,78,116,147,233,325,469, notifications.ts:42, oauth.ts:13,29,56,82,108, pending-connection-invites.ts:7, pending-invites.ts:9, pending-page-invites.ts:14, permissions.ts:9, personalization.ts:11, published-pages.ts:8,21, push-notifications.ts:9,31, security-audit-anchors.ts:28, security-audit-ingest.ts:29, security-audit.ts:90, sessions.ts:7, share-links.ts:11,38, social.ts:12,37,45,61,116, storage.ts:22, subscriptions.ts:7,21, task-triggers.ts:19,32, tasks.ts:12,21,44,52,73,85,105, toast-notification-preferences.ts:16, versioning.ts:147,174,307, webhook-triggers.ts:18,31, workflow-runs.ts:38, workflows.ts:15,34, zoom.ts:19,48, ai-compaction.ts:21 (all under packages/db/src/schema/)
calculateVersionExpiresAt — packages/db/src/schema/versioning.ts:80 — default param createdAt: Date = new Date(); reads wall clock when called without an argument
Database I/O
pool (module singleton) — packages/db/src/db.ts:37 — constructs new Pool() at import time
db (module singleton) — packages/db/src/db.ts:49 — drizzle(pool) bound at import
getAdvisoryLockPool — packages/db/src/db.ts:76 — lazily constructs a second pg.Pool, mutates module-level advisoryLockPool
withAdvisoryLock — packages/db/src/advisory-lock.ts:113 — pool.connect() + pg_try_advisory_lock/pg_advisory_unlock queries
getAdminDb — packages/db/src/admin-db.ts:131 — lazy connect via memoized registry singleton
createAdminDbRegistry — packages/db/src/admin-db.ts:69 — closure holds mutable instance, calls createPool/drizzle/alert
getAdminEraserDb — packages/db/src/admin-eraser-db.ts:126 — lazy connect, throws when unconfigured
createAdminEraserDbRegistry — packages/db/src/admin-eraser-db.ts:81 — closure instance, createPool/drizzle
createAdminAuditDbClient — packages/db/src/admin-eraser-db.ts:142 — new Pool() + drizzle on arbitrary creds
runMigrations — packages/db/src/migration-runner.ts:34 — executes CREATE SCHEMA/TABLE, per-migration transactions
migrateAdminDb — packages/db/src/migrate-admin.ts:23 — new Pool(), reads migration files, runs migrations
provisionAdminLoginUsers — packages/db/src/provision-admin-users.ts:25 — new Pool(), pool.query for each login user
atomicDeviceTokenRotation — packages/db/src/transactions/auth-transactions.ts:71 — db.transaction SELECT FOR UPDATE / INSERT / UPDATE; also Date.now() (145), new Date() (188,198), createId() (203)
atomicValidateOrCreateDeviceToken — packages/db/src/transactions/auth-transactions.ts:281 — db.transaction locking + writes; also new Date() (340,383,413), createId() (416)
calculateInitialStorage — packages/db/scripts/calculate-initial-storage.ts:20 — SELECT/UPDATE/INSERT over all users; new Date() (48,92)
Environment reads
basePoolConfig — packages/db/src/db.ts:10 — reads process.env.DATABASE_URL/DATABASE_SSL at call time
pool init — packages/db/src/db.ts:39 — reads process.env.DB_POOL_MAX
getEnv closure (adminDb registry) — packages/db/src/admin-db.ts:112 — reads 5 ADMIN_*/AUDIT_* env vars each call
getAdminDbMode — packages/db/src/admin-db.ts:137 — re-reads env via getEnv
getEnv closure (eraser registry) — packages/db/src/admin-eraser-db.ts:117 — reads ADMIN_ERASER_DATABASE_URL/ADMIN_DATABASE_SSL
- module-scope
dryRun — packages/db/src/migrate-pending-invites.ts:29 — reads process.argv at import
Module-level mutable state
advisoryLockPool (module let) — packages/db/src/db.ts:74 — mutated by getAdvisoryLockPool
pools (module new Map) — packages/db/src/pool-stats.ts:5
registerPool — packages/db/src/pool-stats.ts:13 — mutates the module pools map
getPoolStats — packages/db/src/pool-stats.ts:17 — reads the module pools map
registry singletons — packages/db/src/admin-db.ts:110, packages/db/src/admin-eraser-db.ts:115 — module-level instances holding memoized connection state
Console/logging side effects
releaseQuietly — packages/db/src/advisory-lock.ts:76 — console.error on release failure
unlockAndRelease — packages/db/src/advisory-lock.ts:95 — console.error on unlock failure
alert closure (adminDb) — packages/db/src/admin-db.ts:124 — console.error break-glass banner
Filesystem/process I/O
main — packages/db/src/migrate.ts:11 — readMigrationFiles, console.log, process.exit(0/1)
main — packages/db/src/migrate-admin.ts:47 — console.log, process.exit
main — packages/db/src/provision-admin-users.ts:62 — console.log, process.exit
main — packages/db/src/migrate-pending-invites.ts:31 — DB deletes, console.log/error, process.exit
calculateInitialStorage / bottom runner — packages/db/scripts/calculate-initial-storage.ts:20,164 — config() env load, console.*, process.exit
Count (db): ~15 DB-I/O functions, ~6 env-read functions, 5 module-mutable-state functions, 3 console-side-effect functions, 5 filesystem/process entrypoints, 1 nondeterministic helper, plus ~55 schema files carrying createId/new Date default closures.
packages/sdk
Network I/O
executeRequest — packages/sdk/src/transport/execute.ts:22 — calls injected fetch; setTimeout timeout timer/AbortController
PageSpaceClient.#invoke / invoke — packages/sdk/src/client.ts:344 — drives the fetch pipeline (network), retry backoff via delay
delay — packages/sdk/src/client.ts:265 — setTimeout-based sleep
OAuthTokenProvider.getAccessToken / #performRefresh — packages/sdk/src/auth/oauth.ts:83,138 — awaits injected refresh HTTP call, persists via onTokensUpdated
Nondeterminism (time/randomness)
PageSpaceClient constructor — packages/sdk/src/client.ts:324 — defaults #fetch = fetch.bind(globalThis) (331) and #jitter = Math.random (331) when not injected
OAuthTokenProvider constructor — packages/sdk/src/auth/oauth.ts:75 — defaults #now = Date.now (78) when now not injected
Module-level mutable state
PageSpaceClient instance field #compatibilityVerified — packages/sdk/src/client.ts:322 — memoized flag mutated during #invoke
OAuthTokenProvider fields #tokens/#status/#inFlightRefresh — packages/sdk/src/auth/oauth.ts:67-73 — mutated across refresh calls
StaticTokenProvider field #invalidated — packages/sdk/src/auth/static.ts:23 — toggled by invalidate/getAccessToken
Note: pkce.generateCodeVerifier (pkce.ts:54), retry.ts, registry.ts, version.ts, build-request.ts, parse-response.ts, decide.ts, and all operations/* (the new Date(value) in calendar.ts/activity.ts are deterministic parses of given input) are pure — impurity is constructor-injected.
Count (sdk): 4 network-I/O functions, 2 nondeterministic-default constructors, 3 stateful classes. No filesystem/process, env-read, or standalone console impurities.
packages/cli
Nondeterminism (time/randomness)
FileCredentialStore.writeFile — packages/cli/src/credentials/file-store.ts:104 — Math.random() temp-file suffix (110); also process.pid
FileActiveKeyStore.writeFile — packages/cli/src/credentials/active-key.ts:88 — Math.random() temp-file suffix (91); also process.pid
createRefreshAccessToken — packages/cli/src/auth/silent-refresh.ts:35 — defaults now = Date.now (39); computes token expiries from clock
- default-deps wiring:
loginHandler — packages/cli/src/commands/login.ts:151 — deps default randomBytes: nodeRandomBytes (148/153) + now: Date.now (160)
tokensCreateHandler — packages/cli/src/commands/keys/create.ts:409 — deps default randomBytes (406/411) + now: Date.now (418)
keysUseHandler — packages/cli/src/commands/keys/use.ts:278 — deps default randomBytes (275/280) + now: Date.now (287)
keysHandler — packages/cli/src/commands/keys/wizard.ts:645 — deps default randomBytes (642/647) + now: Date.now (654)
loginDeviceHandler — packages/cli/src/commands/login-device.ts:137 — deps default now: Date.now (147)
whoamiHandler — packages/cli/src/commands/whoami.ts:136 — deps default now: Date.now (141); new Date(deps.now()) (102)
Environment reads
- module-scope process wiring in
bin.ts — packages/cli/src/bin.ts:46,50,55 — reads process.argv/process.env/process.stdin.isTTY
- module-scope process wiring in
bin-pagespace-mcp.ts — packages/cli/src/bin-pagespace-mcp.ts:17-24 — reads process.argv/process.env
(resolveConfig, resolveEnvToken, resolveEnvKeyName, resolveAuth, resolveKeyName, hasExplicitCredential all take env as a parameter and are pure.)
Network I/O
Auth flow factories/functions:
confirmIdentity — packages/cli/src/auth/confirm-identity.ts:36 — constructs a client and performs GET /api/auth/me
createDiscoverMetadata — packages/cli/src/auth/discover.ts:29 — returned fn defaults to real fetch, hits .well-known
createExchangeCode — packages/cli/src/auth/exchange-code.ts:70 — default fetch, token exchange
createPollDeviceToken — packages/cli/src/auth/poll-device-token.ts:30 — default fetch, device-token poll
createRequestDeviceAuthorization — packages/cli/src/auth/request-device-authorization.ts:35 — default fetch
createRevokeToken — packages/cli/src/auth/revoke-token.ts:29 — default fetch
runDeviceLogin — packages/cli/src/auth/device-flow.ts:150 — polling network loop; new Date(deps.now()) (222)
runLoopbackLogin — packages/cli/src/auth/loopback-flow.ts:299 — discovery/exchange network + browser + loopback server; deps.randomBytes (313,315), new Date(deps.now()) (405)
buildAuthProvider — packages/cli/src/auth/auth-context.ts:53 — wires OAuth refresher (network) + credential-store writes; new Date(deps.now()) (103)
enforceAuth — packages/cli/src/auth/auth-context.ts:130 — auth.getAccessToken() (network refresh), credentialStore.delete, stderr writes
run — packages/cli/src/run.ts:128 — composition root: constructs PageSpaceClient, network auth, now: Date.now (200), reads active-key/credential stores, writes stderr
runPagespaceMcpBin — packages/cli/src/pagespace-mcp-bin.ts:19 — delegates to run
createMcpServer — packages/cli/src/mcp/serve.ts:217 — wires tools/call handler that performs sdk.invoke (network)
Command handlers — each writes to ctx.stdout/ctx.stderr and calls ctx.sdk.* (network): ~70 handlers across commands/activity.ts:22, agents.ts:68,142,223,271, channels.ts:13, content.ts:101,140,197, drives.ts:42,54,72,90,108,129,170, export.ts:53,124, pages.ts:53,76,105,125,161,182,207,235, roles.ts:155,177,202,276,351,386,438,486, search.ts:98,145,193, sheets.ts:44,104, tasks.ts:50,74,195,242,284,312,336,390, trash.ts:25, keys/list.ts:19,51, keys/revoke.ts:23,56, keys/create.ts:254,409, keys/use.ts:136,197,278, keys/wizard.ts:580,645, login.ts:58,151, login-device.ts:44,137, logout.ts:102,139, whoami.ts:40,136, mcp.ts:41,68
Filesystem/process I/O
bin.ts top-level + prompt/flushAndExit — packages/cli/src/bin.ts:14-75 — process.stdout/stderr/stdin, readline, process.exit
bin-pagespace-mcp.ts top-level — packages/cli/src/bin-pagespace-mcp.ts:14-36 — process.stdout/stderr, process.exitCode
openBrowser — packages/cli/src/auth/open-browser.ts:45 — spawn detached child (reads process.platform)
createLoopbackServer — packages/cli/src/auth/create-loopback-server.ts:22 — node:http createServer/listen
waitMs/unrefWaitMs — packages/cli/src/auth/wait.ts:15,28 — setTimeout
FileCredentialStore get/set/delete/list/readFile/writeFile — packages/cli/src/credentials/file-store.ts:52-120 — node:fs open/read/write/chmod/rename; defaultCredentialsPath reads homedir() (33)
FileActiveKeyStore getActiveKey/setActiveKey/clearActiveKey/readFile/writeFile — packages/cli/src/credentials/active-key.ts:60-99 — node:fs; defaultActiveKeysPath reads homedir() (28)
createNativeKeychainAdapter (getSecret/setSecret/deleteSecret/listSecrets) — packages/cli/src/credentials/keychain.ts:91 — lazy native @napi-rs/keyring binding, OS keychain I/O
readStdinToString / createPagesReplaceLinesHandler deps — packages/cli/src/commands/content.ts:191,199 — process.stdin async iteration, readFile
pagesExportHandler default deps — packages/cli/src/commands/export.ts:100,126 — writeFile/access to disk
sheetsEditCellsHandler stdin reader — packages/cli/src/commands/sheets.ts:98 — process.stdin async iteration
Console/logging side effects
versionHandler — packages/cli/src/commands/version.ts:12 — writes to ctx.stdout only (no network)
helpHandler/createHelpHandler — packages/cli/src/router/routes.ts:162, packages/cli/src/commands/help.ts:104 — prints help text to ctx.stdout
Module-level mutable state
CompositeCredentialStore fields degraded/noticeShown — packages/cli/src/credentials/store.ts:42,43 — mutated on first keychain failure (degrade)
createNativeKeychainAdapter closure modulePromise — packages/cli/src/credentials/keychain.ts:92 — memoized lazy import
createCredentialStore — packages/cli/src/credentials/store.ts:204 — defaults stderr to a sink writing process.stderr (209)
Count (cli): ~90 network-I/O functions, ~20 filesystem/process-I/O functions, ~9 nondeterminism sites, 2 env-reading module entrypoints, 2 console-only handlers, 3 module-mutable-state sites. Pure modules: argv/parse.ts, router/router.ts, config/resolve.ts, auth/resolve.ts, auth/legacy-token-env.ts, credentials/serialize.ts, mcp/tool-convert.ts, commands/keys/args.ts, commands/keys/logic.ts, commands/keys/guidance.ts, exit-codes.ts, and pure helpers in keys/create.ts (buildTokenScope, buildKeyUpdateScope, buildKeyActivateScope, resolveNewKeyName).
The apps/web and remaining apps inventories continue in the comments below (GitHub body size limit).
Goal
We want completely pure functions wherever possible. This issue is the full inventory of impure functions across the monorepo, produced by a systematic audit of all ~2,700 non-test TypeScript source files in
apps/*andpackages/*(excludingapps/e2e,apps/ios,apps/android, tests, and generated migrations).Definition used. A function is impure if it: (a) performs I/O (database, network, filesystem, process spawning, browser storage, sockets/IPC); (b) is nondeterministic (
Date.now(),new Date()with no argument,Math.random(),crypto.randomUUID/randomBytes,nanoid/createId); (c) reads mutable external state (process.envat call time,window/document/navigator, module-level mutable variables); (d) mutates external state (module-level caches/maps, singletons, its arguments); or (e) has observable side effects (console.*, event emission, timers). Deterministic helpers that only transform their inputs (includingnew Date(value)parsing a supplied value, and functions that takenow/env/fetchas parameters) are counted as pure and excluded.Headline counts (named functions; boundary layers like API routes and repositories grouped per file):
packages/libpackages/dbcreateId/new Datedefault closurespackages/sdkpackages/cliapps/websrc/lib/stores/hooks+ 356 API route files + component-layer markers (227 nondeterminism / 230 browser-global / 157 console occurrences)apps/processorapps/realtimeapps/control-planeapps/desktop(main)apps/adminapps/marketingapps/atlasTotal: roughly 1,100+ impure functions (plus per-file-grouped boundary handlers).
Highest-value purification targets
These are pure computations contaminated by avoidable impurity — fix by injecting
now/rng/env/ports as parameters:evaluateFunction—packages/lib/src/sheets/functions.ts:96— the spreadsheet formula evaluator callsnew Date()(NOW/TODAY) andMath.random()(RAND/RANDBETWEEN) inline; inject a clock/rng.getTodayUTC/getTomorrowMidnightUTC/getSecondsUntilMidnightUTC—packages/lib/src/services/date-utils.ts:13,28,44— pure date math reading the wall clock; takenowas a parameter.isOnPrem/isTenantMode/isCloud/isBillingEnabled—packages/lib/src/deployment-mode.ts:34-46— readprocess.envat call time (areCloudIntegrationsAllowedalready shows the pure pattern).csrf-utils/broadcast-auth—packages/lib/src/auth/csrf-utils.ts:24,packages/lib/src/auth/broadcast-auth.ts:28,88— token generation/validation mixesrandomBytes,Date.now(), env reads, andconsole.error.currentWindowStart—packages/lib/src/security/distributed-rate-limit.ts:218—now = Date.now()default parameter bakes the clock into the signature (same anti-pattern incalculateVersionExpiresAt—packages/db/src/schema/versioning.ts:80,isDormant—apps/admin/src/lib/dormancy.ts:9, and the sdk/cli default-deps constructors).components/calendar/calendar-types.ts:219(apps/web) — exported date-bucketing helpers readnew Date()for "today" instead of accepting it.apps/web/src/lib/workflows/workflow-executor.ts,calendar-trigger-executor.ts,zoom/webhook-trigger-executor.ts—Date.now()embedded in computation andconsole.*in logic paths.packages/lib/src/auth/rate-limit-utils.ts:22,packages/lib/src/security/distributed-rate-limit.ts:66,apps/web/src/lib/auth/cron-auth.ts:18-34(usedNonces),apps/web/src/lib/ai/core/model-capabilities.ts:29-118,apps/web/src/lib/ai/core/stream-abort-registry.ts:30-117— module-level mutable Maps +setIntervalat import time.credit-pricingmodule-init env reads —packages/lib/src/billing/credit-pricing.ts:12-39— pricing constants frozen fromprocess.envat import.packages/lib/src/services/sandbox/{can-run-code,checkpoint-policy,containment,egress-ip,quota}.ts— call-timeprocess.envreads inside decision logic.Good existing patterns to replicate:
packages/sdk(all impurity constructor-injected; operations layer fully pure),packages/cli's pure modules (argv/parse.ts,config/resolve.ts,auth/resolve.ts,keys/logic.ts),packages/libpure decision modules (step-up-decisions.ts,credit-core.ts,compute-reorder-plan.ts,breach-assessment.ts), and apps/web'smachine-workspace/workspace-reducer.ts+sheet/core/touch.ts(injected clock).Note: boundary layers (API route handlers, DB repositories, socket handlers, CLI entrypoints) are inherently effectful — the goal there is not to make them pure but to keep them thin, pushing decisions into pure functions. They are inventoried below for completeness.
Full inventory
packages/lib — Impure Function Audit
Scope:
packages/lib/src(non-test.ts). For inherently-effectful boundary modules (DB repositories, network/sandbox clients) exported functions are grouped per file; pure-computation-contaminated-by-avoidable-impurity cases are called out individually. A function with several impurities is listed once under its most severe category with all reasons.Nondeterminism (time/randomness)
Otherwise-pure computations that read the clock / RNG inline instead of taking
now/entropy as a parameter:evaluateFunction—packages/lib/src/sheets/functions.ts:96— a pure spreadsheet-formula evaluator, but theNOW/TODAYcases callnew Date()(337, 333) andRAND/RANDBETWEENcallMath.random()(451, 462); no clock/rng injected.getTodayUTC/getTomorrowMidnightUTC/getSecondsUntilMidnightUTC—packages/lib/src/services/date-utils.ts:13,28,44— pure date math contaminated bynew Date()/Date.now()read at call time (parseDateUTC:56is pure).generateCsrfToken—packages/lib/src/auth/csrf-utils.ts:24—randomBytes(32)(25) +Date.now()(26); also readsprocess.env.CSRF_SECRET.validateCsrfToken—packages/lib/src/auth/csrf-utils.ts—Date.now()(60) for expiry,console.error(74), readsCSRF_SECRET(5).generateBroadcastSignature/verifyBroadcastSignature—packages/lib/src/auth/broadcast-auth.ts:28,88—Date.now()timestamps; verify alsoconsole.error(98) and readsREALTIME_BROADCAST_SECRET(9).currentWindowStart—packages/lib/src/security/distributed-rate-limit.ts:218— default paramnow = Date.now()bakes the clock into the signature.getMonitoringCutoff—packages/lib/src/compliance/retention/monitoring-retention.ts:47—new Date(Date.now() - ...).generateVerificationToken—packages/lib/src/auth/verification-utils.ts:28—randomBytes(32)+new Date(Date.now()+...)(29).generateOpaqueToken—packages/lib/src/auth/opaque-tokens.ts:18—randomBytes(32).packages/lib/src/auth/token-utils.ts:17— cuid2 seeded fromrandomBytes.generateUnsubToken—packages/lib/src/services/notification-email-service.ts:57—randomBytes+new Dateexpiry (67).generateSessionRowId—packages/lib/src/monitoring/activity-logger.ts:118—randomBytes(32).Note:
incidents/breach-assessment.ts,compliance/dsr/sla.ts,services/machines/branch-session.ts,auth/invite-token.ts,auth/oauth/issue-tokens.ts,compliance/retention/chat-retention.tscorrectly takenow/detectedAtas a parameter and are PURE.Environment reads
isOnPrem/isTenantMode/isCloud/isBillingEnabled—packages/lib/src/deployment-mode.ts:34,38,42,46— readprocess.env.DEPLOYMENT_MODEat call time (areCloudIntegrationsAllowed:30takesmodeand is pure).isFlyEnvironment—packages/lib/src/security/client-ip.ts:72— readsprocess.env.FLY_APP_NAME.envInt/envBool/envFloat+ module-level constants —packages/lib/src/billing/credit-pricing.ts:12,22,31,39…— readprocess.envat module-init;openRouterBaseUrl—:343— call time.resolveMonitoringRetentionConfig/resolveAiUsageRetentionDays—packages/lib/src/compliance/retention/monitoring-retention.ts:29-32,43.resolveArchivalConfig—packages/lib/src/compliance/retention/activity-log-archival.ts:63-65.packages/lib/src/compliance/retention/retention-engine.ts:144.validateEnv/getEnvErrors/isEnvValid/getValidatedEnv—packages/lib/src/config/env-validation.ts:190,210,226,238(getValidatedEnv also caches).getResendConfig/resolveAppUrl—packages/lib/src/services/email-service.ts:6,27.resolveBaseUrl/resolveMarketingBase—packages/lib/src/services/broadcast/core.ts:152,169— default paramenv = process.env.getUserIndexKey/isPiiEncryptionEnabled—packages/lib/src/auth/user-repository.ts:41,52.packages/lib/src/encryption/encryption-utils.ts:20,packages/lib/src/encryption/user-crypto.ts:49,packages/lib/src/audit/audit-query.ts:58,packages/lib/src/audit/audit-ingest-writer.ts:46,packages/lib/src/audit/security-audit-repository.ts:32— readENCRYPTION_KEYat call time.packages/lib/src/audit/full-audit-verification.ts:241-242.getSessionIdleTimeout—packages/lib/src/auth/constants.ts:28— env +console.warn(32).verifyGoogleToken/verifyAppleToken—packages/lib/src/auth/oauth-utils.ts:37-101— env (also network).PASSKEY_CONFIGmodule constants —packages/lib/src/auth/passkey-service.ts:30-32.packages/lib/src/auth/onprem-setup-link.ts:44-46.packages/lib/src/security/auth-handoff-sweep.ts:28,security/jti-revocation.ts:42,80,116,139,security/distributed-rate-limit.ts:338,424,485,698,auth/exchange-codes.ts:69,116,auth/passkey-register-handoff.ts:80.packages/lib/src/observability/clickhouse-client.ts:107-152.packages/lib/src/logging/logger.ts:82-100,287,301,305,309,330;logging/logger-config.ts:306;logging/logger-database.ts:265.sanitizeCss—packages/lib/src/canvas/sanitize-css.ts:62,83—NODE_ENVgate +console.warn.isCodeExecutionEnabled—services/sandbox/can-run-code.ts:85;isCheckpointBeforeAgentBatchEnabled—checkpoint-policy.ts:51;isContainmentVerified—containment.ts:167;getConfiguredEgressIpTag—egress-ip.ts:34;getMachineMaxActiveSeconds—quota.ts:140;resolveSpritesToken—sandbox-client/sprites.ts:354;getSandboxSessionSecret—machine-session-manager.ts:88;machine-storage-measure.ts:30.credit-emit—packages/lib/src/billing/credit-emit.ts:40;notifications—notifications/notifications.ts:40; APNs confignotifications/push-notifications.ts:40-42,226-227;cost-reconcile—billing/cost-reconcile.ts:68; Resend suppression —compliance/erasure/resend-suppression-client.ts:50-51,108-109.page-content-store—packages/lib/src/services/page-content-store.ts:42-56,154-155.voice-pricing—packages/lib/src/monitoring/voice-pricing.ts:30.packages/lib/src/services/upload-semaphore.ts:54.packages/lib/src/services/sandbox/quota.ts:24,141.Module-level mutable state
attemptsMap + module-loadsetInterval—packages/lib/src/auth/rate-limit-utils.ts:22,25; mutated bycheckRateLimit:39,resetRateLimit:132,getRateLimitStatus:140.inMemoryAttemptsMap /cleanupIntervalId/postgresAvailableLogged—packages/lib/src/security/distributed-rate-limit.ts:66,67,206;startCleanupInterval:73(module-loadsetInterval, 102),shutdownRateLimiting:93,inMemoryCheckRateLimit:106.UploadSemaphoresingleton —packages/lib/src/services/upload-semaphore.ts:43-60;acquireUploadSlot:75,releaseUploadSlot:112,cleanupStaleSlots(acquire alsoDate.now+Math.random, 98).cachedEnvmemo —packages/lib/src/config/env-validation.ts:232.cachedbinding —packages/lib/src/audit/audit-db-binding.ts:39;resolveAuditDbBinding:45,resetAuditDbBindingForTests:65.defaultAppendPath/defaultServicesingletons —packages/lib/src/audit/security-audit.ts:427,445,161.alertHandler/periodicTimer/verificationInProgress—packages/lib/src/audit/security-audit-alerting.ts:72,545,546(periodicsetInterval562).incidentNotifier—packages/lib/src/incidents/incident-service.ts:51;setIncidentNotifier:54.hookFn—packages/lib/src/logging/siem-error-hook.ts:16;setSiemErrorHook:18,fireSiemErrorHook:26.activityBroadcastHook/workflowTriggerHook—packages/lib/src/monitoring/activity-logger.ts:374,400.packages/lib/src/notifications/push-notifications.ts:29,30;getApnsJwtToken:32.resendInstancememo —packages/lib/src/services/email-service.ts:17._s3singleton —packages/lib/src/services/page-content-store.ts:37.packages/lib/src/services/sandbox/quota.ts;acquireCodeExecutionSlot:58,releaseCodeExecutionSlot:70,recordMachineActivity:206, resets:80,220,225.packages/lib/src/services/sandbox/checkpoint-policy.ts:143-185.packages/lib/src/services/sandbox/sandbox-client/sprites.ts:1375.Loggerbuffers +flushTimer—packages/lib/src/logging/logger.ts:79,359,logging/logger-browser.ts:84,208.packages/lib/src/observability/clickhouse-buffer.ts:102.Database I/O (grouped per file; most also stamp
new Date()and/orconsole.error)repositories/account-repository.ts:32,activity-log-repository.ts:17,agent-repository.ts:47,broadcast-repository.ts:64,data-subject-request-repository.ts:41,drive-repository.ts:28,page-repository.ts:79— all exported CRUD.permissions/permissions.ts—getDriveIdsForUser,getUserAccessLevel,canUserViewPage/EditPage/SharePage/DeletePage,isDriveOwnerOrAdmin,isUserDriveMember,getUserAccessiblePagesInDrive(+WithDetails),getUserDriveAccess,getUserDrivePermissions,usersShareDrive,getBatchPagePermissions;permission-mutations.ts:224,359;share-link-service.ts:94-453(9 fns);accessible-page-ids.ts:12;agent-permissions.ts:30,64,73;app-permissions.ts:60-463(10 fns;resolveExplicitAppRoleAccess:89,getScopedDriveMembership:428pure);membership-queries.ts:23,36,53,63(resolveCustomRolePermissions:14pure);file-access.ts:18;rollback-permissions.ts:53.pages/circular-reference-guard.ts— DB reads.integrations/repositories/{audit,connection,provider,config,grant}-repository.ts— all exported fns;integrations/saga/execute-tool.ts:77(Date.nowtiming);integrations/resolution/resolve-agent-integrations.ts.auth/account-lockout.ts:62-148,device-auth-utils.ts(console 122-384),exchange-codes.ts:53(randomBytes+env),magic-link-service.ts:107,126,oauth-utils.ts(network+env,new Date233,276),passkey-service.ts,passkey-register-handoff.ts:59,pkce.ts:41,47,session-repository.ts(new Date55-139),session-service.ts:68,step-up-service.ts(pure decisions instep-up-decisions.ts),token-lookup.ts,user-repository.ts,verification-utils.ts:28,onprem-setup-link.ts:32.security/jti-revocation.ts:36,75,distributed-rate-limit.ts(checkDistributedRateLimit),auth-handoff-sweep.ts.audit/audit-ingest-writer.ts:82,security-audit-repository.ts:83,security-audit-chain-verifier.ts:178,207,318,security-audit.ts:160,security-audit-alerting.ts:97,audit-log.ts,audit-query.ts,co-stream-reconciliation.ts,full-audit-verification.ts,admin-read-audit.ts,co-stream.ts.billing/cost-reconcile.ts,credit-backfill.ts:68,credit-balance.ts:108,credit-consume.ts,credit-funding.ts,credit-gate.ts:144,live-concurrency-query.ts:26,machine-payer.ts(credit-core.tsis pure).compliance/retention/{retention-engine.ts:25-143,activity-log-archival.ts:96,monitoring-retention.ts,chat-retention.ts},compliance/erasure/{pseudonymize-repository,pseudonymize-targets,run-erasure,revoke-integration-tokens.ts:37,drive-disposition,email-suppression,ai-provider-erasure}.ts,compliance/export/gdpr-export.ts,compliance/file-cleanup/orphan-detector.ts.monitoring/activity-logger.ts(logActivity+ helpers; randomBytes, console 48-587, module hooks),ai-monitoring.ts,hash-chain-verifier.ts:146(console 329-573),observability/analytics-inserts.ts:76,analytics-reads.ts,analytics-read-core.ts,analytics-gdpr.ts,error-resolutions.ts:100,logging/logger-database.ts(write* fns),logging/monitoring-purge.ts:42,logging/ai-usage-purge.ts.incidents/incident-service.ts:71—createIncident.notifications/notifications.ts(DB+fetch+env+console),notifications/push-notifications.ts(DB+APNs network+env+cache).content/version-resolver.ts.services/storage-limits.ts:34-382(11 fns; pure helpers excluded),storage-repository.ts,drive-service.ts,drive-member-service.ts,drive-role-service.ts:83-393(7 fns; validators pure),drive-agent-service.ts:99-496(7 fns),drive-search-service.ts:117,162,350,calendar-event-drive-service.ts:130-356(6 fns),channel-message-repository.ts,dm-message-repository.ts,app-shell-service.ts:264,page-payload-service.ts:250,page-version-service.ts,quote-enrichment.ts,notification-email-service.ts,attachment-upload.ts:51,attachment-upload-repository.ts,validated-service-token.ts:178-669(7 fns),subdomain-allocation.ts,reorder/locked-batch-reorder.ts:47(compute-reorder-plan.ts:19pure),broadcast/{audience.ts:70,record-adapter.ts,engine.ts,transactional-engine.ts}.services/machines/{machine-branches-store.ts:102,machine-projects-store.ts:63,machine-workspaces-store.ts:99,agent-terminals-store.ts:90,machine-settings.ts:88-136,machine-projects.ts:187-291,machine-access.ts:29,39,machine-list.ts:57,87,machine-workspaces.ts:104-229,agent-terminals.ts:265-536,machine-branches.ts:314-717,machine-orphan-reconcile.ts:184}— DB (+ sandbox process I/O for spawn/kill).services/sandbox/{machine-session-manager.ts:392,555,575,machine-session.ts:93,machine-billing.ts,machine-storage-billing.ts:110,212,machine-storage-reconcile.ts:159,github-token.ts:6,tool-gate.ts:100,audit.ts:230,quota.ts:107}.Network I/O
assertSafeUrl/safe-fetch —packages/lib/src/security/url-validator.ts:482— SSRF-guardedfetch.fetchWithTimeout—packages/lib/src/utils/fetch-with-timeout.ts:38.buildWebhookSiemErrorHook—packages/lib/src/logging/siem-error-hook.ts:35— POST (44), console (56),new Date(49).exchangeCodeForTokens/refreshTokens—packages/lib/src/integrations/oauth/oauth-handler.ts:125,178— fetch + PKCE randomBytes (217).executeHttpRequest—packages/lib/src/integrations/execution/http-executor.ts— fetch (91),Date.nowtiming.billing/cost-reconcile.ts:71,billing/credit-emit.ts:47,notifications/notifications.ts:47,notifications/push-notifications.ts(HTTP/2 APNs),compliance/erasure/revoke-integration-tokens.ts:37,compliance/erasure/resend-suppression-client.ts:50-109,services/email-service.ts:71(Resend SDK).Note:
fetch(...)atpackages/lib/src/forms/form-html.ts:55is inside a generated client-side HTML string — not a runtime call in this package.Filesystem/process I/O
writePageContent/readPageContent/readPageContentFromFilesystem/isContentCompressed/getContentMetadata—packages/lib/src/services/page-content-store.ts:80,132,153,166,178.validatePath—packages/lib/src/security/path-validator.ts:17—realpath/stat.packages/lib/src/services/sandbox/sandbox-client/sprites.ts—runSpawned:379,killSpriteSession:873,createSpritesSandboxClient:1406(+ internal spawn/fs at 282,700,940-956,1329);sprite-tasks.ts:330,453;sprite-machine-host.ts:244.packages/lib/src/services/sandbox/tool-runners.ts:605,744,801,878—runBashInSandbox,writeSandboxFile,readSandboxFile,editSandboxFile.packages/lib/src/services/sandbox/machine-fs.ts:68-343— 8 fns.runGitInSandbox—packages/lib/src/services/sandbox/git-tool-runners.ts:90.resolveMachineMergeBase/listMachineDiffFiles/readMachineDiffPair—packages/lib/src/services/sandbox/machine-diff.ts:68,113,214;readMachineGitBlob—machine-git-blob.ts:78.refreshStorageMeasurement—packages/lib/src/services/sandbox/machine-storage-measure.ts:144—duspawn.setupMemoryProtection—packages/lib/src/services/memory-monitor.ts:50—setInterval;getProcessMemory:34—process.memoryUsage().Console/logging side effects (only impurity)
generateDocx/generateExcel—packages/lib/src/content/export-utils.ts:30,135—console.error;new Date()(100) in workbook metadata.sanitizeCss—packages/lib/src/canvas/sanitize-css.ts:63,84.getSessionIdleTimeout—packages/lib/src/auth/constants.ts:32.packages/lib/src/logging/logger.ts:316-417,logger-browser.ts:251-257.graceful-shutdowndefaultlogError—packages/lib/src/logging/graceful-shutdown.ts:23.logError—observability/clickhouse-buffer.ts:52,analytics-inserts.ts:77.Count
Approximately 420 impure exported/significant functions in
packages/lib/src: Nondeterminism ~14 highlighted; Environment reads ~55; Module-level mutable state ~24; Database I/O ~300+ (across ~70 files); Network I/O ~13; Filesystem/process I/O ~30; Console/logging ~12. Most notable avoidable contaminations:evaluateFunction(sheets),date-utilshelpers,csrf-utils/broadcast-auth,currentWindowStartdefault param, call-timeprocess.envreaders indeployment-mode/credit-pricing/sandbox gates.packages/db, packages/sdk, packages/cli — Impure Function Audit
packages/db
Nondeterminism (time/randomness)
Schema default closures — anonymous callbacks on exported Drizzle table objects. Two markers recur:
$defaultFn(() => createId())(cuid2, random) on primary keys, and$onUpdate(() => new Date())(wall clock) onupdatedAt. Present across ~55 schema files:auth.ts:11,53,61,106,129,157,210,236,automation-preferences.ts:16,calendar-triggers.ts:22,44,calendar.ts:31,90,116,149,186,203,chat.ts:30,93,commands.ts:27,57,contact.ts:5,conversations.ts:11,18,32,core.ts:12,20,38,86,103,105,132,147,165,182,196,credits.ts:34,63,111,custom-domains.ts:9,dashboard.ts:8,12,32,data-subject-requests.ts:69,display-preferences.ts:13,drafts.ts:7,email-broadcasts.ts:102,119,191,email-notifications.ts:9,22,feedback.ts:14,form-targets.ts:27,59,hotkeys.ts:7,incidents.ts:38,integrations.ts:59,86,102,149,178,218,250,261,machine-agent-terminals.ts:57,77,machine-branches.ts:30,97,machine-projects.ts:30,45,machine-sessions.ts:25,122,machine-workspaces.ts:92,members.ts:12,26,44,53,74,90,181,monitoring.ts:31,78,116,147,233,325,469,notifications.ts:42,oauth.ts:13,29,56,82,108,pending-connection-invites.ts:7,pending-invites.ts:9,pending-page-invites.ts:14,permissions.ts:9,personalization.ts:11,published-pages.ts:8,21,push-notifications.ts:9,31,security-audit-anchors.ts:28,security-audit-ingest.ts:29,security-audit.ts:90,sessions.ts:7,share-links.ts:11,38,social.ts:12,37,45,61,116,storage.ts:22,subscriptions.ts:7,21,task-triggers.ts:19,32,tasks.ts:12,21,44,52,73,85,105,toast-notification-preferences.ts:16,versioning.ts:147,174,307,webhook-triggers.ts:18,31,workflow-runs.ts:38,workflows.ts:15,34,zoom.ts:19,48,ai-compaction.ts:21(all underpackages/db/src/schema/)calculateVersionExpiresAt—packages/db/src/schema/versioning.ts:80— default paramcreatedAt: Date = new Date(); reads wall clock when called without an argumentDatabase I/O
pool(module singleton) —packages/db/src/db.ts:37— constructsnew Pool()at import timedb(module singleton) —packages/db/src/db.ts:49—drizzle(pool)bound at importgetAdvisoryLockPool—packages/db/src/db.ts:76— lazily constructs a secondpg.Pool, mutates module-leveladvisoryLockPoolwithAdvisoryLock—packages/db/src/advisory-lock.ts:113—pool.connect()+pg_try_advisory_lock/pg_advisory_unlockqueriesgetAdminDb—packages/db/src/admin-db.ts:131— lazy connect via memoized registry singletoncreateAdminDbRegistry—packages/db/src/admin-db.ts:69— closure holds mutableinstance, callscreatePool/drizzle/alertgetAdminEraserDb—packages/db/src/admin-eraser-db.ts:126— lazy connect, throws when unconfiguredcreateAdminEraserDbRegistry—packages/db/src/admin-eraser-db.ts:81— closureinstance,createPool/drizzlecreateAdminAuditDbClient—packages/db/src/admin-eraser-db.ts:142—new Pool()+drizzleon arbitrary credsrunMigrations—packages/db/src/migration-runner.ts:34— executes CREATE SCHEMA/TABLE, per-migration transactionsmigrateAdminDb—packages/db/src/migrate-admin.ts:23—new Pool(), reads migration files, runs migrationsprovisionAdminLoginUsers—packages/db/src/provision-admin-users.ts:25—new Pool(),pool.queryfor each login useratomicDeviceTokenRotation—packages/db/src/transactions/auth-transactions.ts:71—db.transactionSELECT FOR UPDATE / INSERT / UPDATE; alsoDate.now()(145),new Date()(188,198),createId()(203)atomicValidateOrCreateDeviceToken—packages/db/src/transactions/auth-transactions.ts:281—db.transactionlocking + writes; alsonew Date()(340,383,413),createId()(416)calculateInitialStorage—packages/db/scripts/calculate-initial-storage.ts:20— SELECT/UPDATE/INSERT over all users;new Date()(48,92)Environment reads
basePoolConfig—packages/db/src/db.ts:10— readsprocess.env.DATABASE_URL/DATABASE_SSLat call timepoolinit —packages/db/src/db.ts:39— readsprocess.env.DB_POOL_MAXgetEnvclosure (adminDb registry) —packages/db/src/admin-db.ts:112— reads 5ADMIN_*/AUDIT_*env vars each callgetAdminDbMode—packages/db/src/admin-db.ts:137— re-reads env viagetEnvgetEnvclosure (eraser registry) —packages/db/src/admin-eraser-db.ts:117— readsADMIN_ERASER_DATABASE_URL/ADMIN_DATABASE_SSLdryRun—packages/db/src/migrate-pending-invites.ts:29— readsprocess.argvat importModule-level mutable state
advisoryLockPool(modulelet) —packages/db/src/db.ts:74— mutated bygetAdvisoryLockPoolpools(modulenew Map) —packages/db/src/pool-stats.ts:5registerPool—packages/db/src/pool-stats.ts:13— mutates the modulepoolsmapgetPoolStats—packages/db/src/pool-stats.ts:17— reads the modulepoolsmapregistrysingletons —packages/db/src/admin-db.ts:110,packages/db/src/admin-eraser-db.ts:115— module-level instances holding memoized connection stateConsole/logging side effects
releaseQuietly—packages/db/src/advisory-lock.ts:76—console.erroron release failureunlockAndRelease—packages/db/src/advisory-lock.ts:95—console.erroron unlock failurealertclosure (adminDb) —packages/db/src/admin-db.ts:124—console.errorbreak-glass bannerFilesystem/process I/O
main—packages/db/src/migrate.ts:11—readMigrationFiles,console.log,process.exit(0/1)main—packages/db/src/migrate-admin.ts:47—console.log,process.exitmain—packages/db/src/provision-admin-users.ts:62—console.log,process.exitmain—packages/db/src/migrate-pending-invites.ts:31— DB deletes,console.log/error,process.exitcalculateInitialStorage/ bottom runner —packages/db/scripts/calculate-initial-storage.ts:20,164—config()env load,console.*,process.exitCount (db): ~15 DB-I/O functions, ~6 env-read functions, 5 module-mutable-state functions, 3 console-side-effect functions, 5 filesystem/process entrypoints, 1 nondeterministic helper, plus ~55 schema files carrying
createId/new Datedefault closures.packages/sdk
Network I/O
executeRequest—packages/sdk/src/transport/execute.ts:22— calls injectedfetch;setTimeouttimeout timer/AbortControllerPageSpaceClient.#invoke/invoke—packages/sdk/src/client.ts:344— drives the fetch pipeline (network), retry backoff viadelaydelay—packages/sdk/src/client.ts:265—setTimeout-based sleepOAuthTokenProvider.getAccessToken/#performRefresh—packages/sdk/src/auth/oauth.ts:83,138— awaits injected refresh HTTP call, persists viaonTokensUpdatedNondeterminism (time/randomness)
PageSpaceClientconstructor —packages/sdk/src/client.ts:324— defaults#fetch = fetch.bind(globalThis)(331) and#jitter = Math.random(331) when not injectedOAuthTokenProviderconstructor —packages/sdk/src/auth/oauth.ts:75— defaults#now = Date.now(78) whennownot injectedModule-level mutable state
PageSpaceClientinstance field#compatibilityVerified—packages/sdk/src/client.ts:322— memoized flag mutated during#invokeOAuthTokenProviderfields#tokens/#status/#inFlightRefresh—packages/sdk/src/auth/oauth.ts:67-73— mutated across refresh callsStaticTokenProviderfield#invalidated—packages/sdk/src/auth/static.ts:23— toggled byinvalidate/getAccessTokenNote:
pkce.generateCodeVerifier(pkce.ts:54),retry.ts,registry.ts,version.ts,build-request.ts,parse-response.ts,decide.ts, and alloperations/*(thenew Date(value)incalendar.ts/activity.tsare deterministic parses of given input) are pure — impurity is constructor-injected.Count (sdk): 4 network-I/O functions, 2 nondeterministic-default constructors, 3 stateful classes. No filesystem/process, env-read, or standalone console impurities.
packages/cli
Nondeterminism (time/randomness)
FileCredentialStore.writeFile—packages/cli/src/credentials/file-store.ts:104—Math.random()temp-file suffix (110); alsoprocess.pidFileActiveKeyStore.writeFile—packages/cli/src/credentials/active-key.ts:88—Math.random()temp-file suffix (91); alsoprocess.pidcreateRefreshAccessToken—packages/cli/src/auth/silent-refresh.ts:35— defaultsnow = Date.now(39); computes token expiries from clockloginHandler—packages/cli/src/commands/login.ts:151— deps defaultrandomBytes: nodeRandomBytes(148/153) +now: Date.now(160)tokensCreateHandler—packages/cli/src/commands/keys/create.ts:409— deps defaultrandomBytes(406/411) +now: Date.now(418)keysUseHandler—packages/cli/src/commands/keys/use.ts:278— deps defaultrandomBytes(275/280) +now: Date.now(287)keysHandler—packages/cli/src/commands/keys/wizard.ts:645— deps defaultrandomBytes(642/647) +now: Date.now(654)loginDeviceHandler—packages/cli/src/commands/login-device.ts:137— deps defaultnow: Date.now(147)whoamiHandler—packages/cli/src/commands/whoami.ts:136— deps defaultnow: Date.now(141);new Date(deps.now())(102)Environment reads
bin.ts—packages/cli/src/bin.ts:46,50,55— readsprocess.argv/process.env/process.stdin.isTTYbin-pagespace-mcp.ts—packages/cli/src/bin-pagespace-mcp.ts:17-24— readsprocess.argv/process.env(
resolveConfig,resolveEnvToken,resolveEnvKeyName,resolveAuth,resolveKeyName,hasExplicitCredentialall take env as a parameter and are pure.)Network I/O
Auth flow factories/functions:
confirmIdentity—packages/cli/src/auth/confirm-identity.ts:36— constructs a client and performs GET /api/auth/mecreateDiscoverMetadata—packages/cli/src/auth/discover.ts:29— returned fn defaults to realfetch, hits.well-knowncreateExchangeCode—packages/cli/src/auth/exchange-code.ts:70— defaultfetch, token exchangecreatePollDeviceToken—packages/cli/src/auth/poll-device-token.ts:30— defaultfetch, device-token pollcreateRequestDeviceAuthorization—packages/cli/src/auth/request-device-authorization.ts:35— defaultfetchcreateRevokeToken—packages/cli/src/auth/revoke-token.ts:29— defaultfetchrunDeviceLogin—packages/cli/src/auth/device-flow.ts:150— polling network loop;new Date(deps.now())(222)runLoopbackLogin—packages/cli/src/auth/loopback-flow.ts:299— discovery/exchange network + browser + loopback server;deps.randomBytes(313,315),new Date(deps.now())(405)buildAuthProvider—packages/cli/src/auth/auth-context.ts:53— wires OAuth refresher (network) + credential-store writes;new Date(deps.now())(103)enforceAuth—packages/cli/src/auth/auth-context.ts:130—auth.getAccessToken()(network refresh),credentialStore.delete, stderr writesrun—packages/cli/src/run.ts:128— composition root: constructsPageSpaceClient, network auth,now: Date.now(200), reads active-key/credential stores, writes stderrrunPagespaceMcpBin—packages/cli/src/pagespace-mcp-bin.ts:19— delegates toruncreateMcpServer—packages/cli/src/mcp/serve.ts:217— wirestools/callhandler that performssdk.invoke(network)Command handlers — each writes to
ctx.stdout/ctx.stderrand callsctx.sdk.*(network): ~70 handlers acrosscommands/activity.ts:22,agents.ts:68,142,223,271,channels.ts:13,content.ts:101,140,197,drives.ts:42,54,72,90,108,129,170,export.ts:53,124,pages.ts:53,76,105,125,161,182,207,235,roles.ts:155,177,202,276,351,386,438,486,search.ts:98,145,193,sheets.ts:44,104,tasks.ts:50,74,195,242,284,312,336,390,trash.ts:25,keys/list.ts:19,51,keys/revoke.ts:23,56,keys/create.ts:254,409,keys/use.ts:136,197,278,keys/wizard.ts:580,645,login.ts:58,151,login-device.ts:44,137,logout.ts:102,139,whoami.ts:40,136,mcp.ts:41,68Filesystem/process I/O
bin.tstop-level +prompt/flushAndExit—packages/cli/src/bin.ts:14-75—process.stdout/stderr/stdin,readline,process.exitbin-pagespace-mcp.tstop-level —packages/cli/src/bin-pagespace-mcp.ts:14-36—process.stdout/stderr,process.exitCodeopenBrowser—packages/cli/src/auth/open-browser.ts:45—spawndetached child (readsprocess.platform)createLoopbackServer—packages/cli/src/auth/create-loopback-server.ts:22—node:httpcreateServer/listenwaitMs/unrefWaitMs—packages/cli/src/auth/wait.ts:15,28—setTimeoutFileCredentialStoreget/set/delete/list/readFile/writeFile —packages/cli/src/credentials/file-store.ts:52-120—node:fsopen/read/write/chmod/rename;defaultCredentialsPathreadshomedir()(33)FileActiveKeyStoregetActiveKey/setActiveKey/clearActiveKey/readFile/writeFile —packages/cli/src/credentials/active-key.ts:60-99—node:fs;defaultActiveKeysPathreadshomedir()(28)createNativeKeychainAdapter(getSecret/setSecret/deleteSecret/listSecrets) —packages/cli/src/credentials/keychain.ts:91— lazy native@napi-rs/keyringbinding, OS keychain I/OreadStdinToString/createPagesReplaceLinesHandlerdeps —packages/cli/src/commands/content.ts:191,199—process.stdinasync iteration,readFilepagesExportHandlerdefault deps —packages/cli/src/commands/export.ts:100,126—writeFile/accessto disksheetsEditCellsHandlerstdin reader —packages/cli/src/commands/sheets.ts:98—process.stdinasync iterationConsole/logging side effects
versionHandler—packages/cli/src/commands/version.ts:12— writes toctx.stdoutonly (no network)helpHandler/createHelpHandler—packages/cli/src/router/routes.ts:162,packages/cli/src/commands/help.ts:104— prints help text toctx.stdoutModule-level mutable state
CompositeCredentialStorefieldsdegraded/noticeShown—packages/cli/src/credentials/store.ts:42,43— mutated on first keychain failure (degrade)createNativeKeychainAdapterclosuremodulePromise—packages/cli/src/credentials/keychain.ts:92— memoized lazy importcreateCredentialStore—packages/cli/src/credentials/store.ts:204— defaultsstderrto a sink writingprocess.stderr(209)Count (cli): ~90 network-I/O functions, ~20 filesystem/process-I/O functions, ~9 nondeterminism sites, 2 env-reading module entrypoints, 2 console-only handlers, 3 module-mutable-state sites. Pure modules:
argv/parse.ts,router/router.ts,config/resolve.ts,auth/resolve.ts,auth/legacy-token-env.ts,credentials/serialize.ts,mcp/tool-convert.ts,commands/keys/args.ts,commands/keys/logic.ts,commands/keys/guidance.ts,exit-codes.ts, and pure helpers inkeys/create.ts(buildTokenScope,buildKeyUpdateScope,buildKeyActivateScope,resolveNewKeyName).The apps/web and remaining apps inventories continue in the comments below (GitHub body size limit).