Skip to content

Audit: full inventory of impure functions across the monorepo (goal: completely pure functions) #2162

Description

@2witstudios

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:

  1. 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.
  2. 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.
  3. isOnPrem/isTenantMode/isCloud/isBillingEnabled — packages/lib/src/deployment-mode.ts:34-46 — read process.env at call time (areCloudIntegrationsAllowed already shows the pure pattern).
  4. 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.
  5. 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).
  6. components/calendar/calendar-types.ts:219 (apps/web) — exported date-bucketing helpers read new Date() for "today" instead of accepting it.
  7. 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.
  8. 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.
  9. credit-pricing module-init env reads — packages/lib/src/billing/credit-pricing.ts:12-39 — pricing constants frozen from process.env at import.
  10. 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).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions