Skip to content

feat: add memory adapter#351

Merged
HugoRCD merged 3 commits into
mainfrom
EVL-170
May 24, 2026
Merged

feat: add memory adapter#351
HugoRCD merged 3 commits into
mainfrom
EVL-170

Conversation

@HugoRCD
Copy link
Copy Markdown
Owner

@HugoRCD HugoRCD commented May 24, 2026

🔗 Linked issue

📚 Description

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

Summary by CodeRabbit

  • New Features

    • Added a Memory adapter for in-memory event logging across JS runtimes (default bounded store: 1000), with named stores, read/filter/query, and clearing helpers; includes HTTP-query coercion helper for read options.
  • Documentation

    • New Memory adapter docs and integration examples (framework wiring, dev-only JSON endpoint pattern, config and behavior guidance).
  • Tests

    • Comprehensive tests covering write/read/filter/eviction/clear and query parsing.
  • Chores

    • Package exports and build entries updated to expose the adapter.

Review Change Stack

@HugoRCD HugoRCD self-assigned this May 24, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented May 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
evlog-docs Skipped Skipped May 24, 2026 4:39pm
just-use-evlog Skipped Skipped May 24, 2026 4:39pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 24, 2026

📝 Walkthrough

Walkthrough

Adds an in-memory ring-buffer drain for evlog with write/read/clear APIs, a drain factory, query-string parsing, tests, package export/build wiring, docs, and a changeset declaring a minor release.

Changes

Memory Adapter

Layer / File(s) Summary
Memory adapter core implementation
packages/evlog/src/adapters/memory.ts
Module-level Map<storeName, WideEvent[]>. writeToMemory appends and enforces maxEvents. createMemoryDrain persists drained events. readMemoryLogs snapshots and filters by level, since/until, filter, and limit. clearMemoryLogs truncates a named buffer. parseReadMemoryLogsQuery coerces query params.
Memory adapter test suite
packages/evlog/test/adapters/memory.test.ts
Vitest suite covering write behavior (default & named stores, max-events eviction, empty arrays), drain creation/batching, read filtering (level, timestamps as string/Date, custom predicate, combined ranges), limit semantics, snapshot isolation, clearMemoryLogs, and parseReadMemoryLogsQuery coercion and end-to-end filtering.
Package exports and build
packages/evlog/package.json, packages/evlog/tsdown.config.ts
Adds ./memory public subpath export (ESM + types), updates typesVersions for the memory adapter, and adds tsdown entry for adapters/memory.
Documentation and release notes
.changeset/add-memory-drain-adapter.md, apps/docs/content/3.integrate/adapters/01.overview.md, apps/docs/content/3.integrate/adapters/self-hosted/03.memory.md
Changeset declares minor bump. Adds Memory adapter overview link/card and a full self-hosted page with quick start, framework examples, dev HTTP endpoint pattern, config (maxEvents, store), named stores, querying API and coercion rules, combining with network drains, ring-buffer eviction notes, and direct API usage examples.

Sequence Diagram

sequenceDiagram
  participant App
  participant MemoryDrain
  participant MemoryStore
  participant ReadAPI
  App->>MemoryDrain: send(context) / send(batch)
  MemoryDrain->>MemoryStore: writeToMemory(events, {store,maxEvents})
  MemoryStore->>MemoryStore: enforce maxEvents (evict oldest)
  ReadAPI->>MemoryStore: readMemoryLogs(options)
  MemoryStore->>ReadAPI: snapshot -> filtered & limited results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels: documentation

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is empty except for the template placeholders; the author did not fill in the linked issue, description, or checklist sections required by the template. Complete the PR description by filling in the linked issue (e.g., 'Resolves #349'), a brief summary of the changes, and check the documentation checklist items.
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add memory adapter' clearly and concisely describes the main change: introducing a new in-memory adapter for event logging.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch EVL-170

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 24, 2026

Thank you for following the naming conventions! 🙏

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 24, 2026

Benchmark report

Bundle size

Threshold: 5% · 🔴 larger · 🟡 warning · 🟢 smaller · ⚪ unchanged · 🆕 new

Status Entry Base (gzip) Current (gzip) Change Raw delta
🆕 adapter/memory 2.04 kB new +5.13 kB
toolkit 781 B 794 B +1.7% +40 B
adapter/hyperdx 1.18 kB 1.19 kB +1.3% +41 B
adapter/better-stack 1.26 kB 1.27 kB +1.2% +41 B
framework/express 727 B 735 B +1.1% +49 B
framework/nitro 7.16 kB 7.23 kB +0.9% +196 B
adapter/posthog 1.47 kB 1.48 kB +0.9% +41 B
adapter/axiom 1.50 kB 1.51 kB +0.8% +41 B
adapter/datadog 2.48 kB 2.50 kB +0.6% +41 B
adapter/otlp 2.14 kB 2.15 kB +0.5% +41 B
adapter/sentry 2.40 kB 2.42 kB +0.4% +41 B
adapter/fs 3.35 kB 3.36 kB +0.4% +41 B
framework/nestjs 1.25 kB 1.26 kB +0.4% +41 B
core (index) 2.15 kB 2.16 kB +0.1% 0 B
workers 1.30 kB 1.31 kB +0.1% 0 B
framework/next 5.20 kB 5.20 kB 0.0% 0 B
framework/ai 4.67 kB 4.67 kB 0.0% 0 B
framework/vite 2.40 kB 2.40 kB 0.0% 0 B
enrichers 1.99 kB 1.99 kB 0.0% 0 B
utils 1.58 kB 1.58 kB 0.0% 0 B
error 1.57 kB 1.57 kB 0.0% 0 B
pipeline 1.35 kB 1.35 kB 0.0% 0 B
http 1.24 kB 1.24 kB 0.0% 0 B
browser 289 B 289 B 0.0% 0 B
client 128 B 128 B 0.0% 0 B
types 31 B 31 B 0.0% 0 B
framework/sveltekit 1.59 kB 1.59 kB -0.1% 0 B
framework/fastify 1.02 kB 1.01 kB -0.1% 0 B
framework/elysia 1.33 kB 1.33 kB -0.2% 0 B
framework/hono 615 B 613 B -0.3% 0 B
logger 229 B 225 B -1.7% 0 B
🟡 Total 54.33 kB 56.55 kB +4.1% +5.77 kB

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/docs/content/3.integrate/adapters/self-hosted/03.memory.md`:
- Line 166: The variable name apiBrain is a typo in the named-store example;
rename it to a clear, consistent identifier (e.g., apiStore or apiMemory)
wherever it's declared and referenced so it matches createMemoryDrain({ store:
'api' }) and avoids confusion; update the declaration using createMemoryDrain
and any subsequent usages of apiBrain to the new name (search for apiBrain and
replace with apiStore/apiMemory in the example).

In `@packages/evlog/src/adapters/memory.ts`:
- Line 115: The read path is mutating state by calling
getOrCreateStore(storeName) when building events (e.g., the line creating const
events = [...getOrCreateStore(storeName)]) and in the other uses around 151-154;
change these reads to use a non-creating accessor (e.g., getStore or a new
readOnlyGetStore) that returns undefined or an empty array without inserting a
new entry into the in-memory map, and handle the undefined/empty result
accordingly so reads do not create persistent empty stores or grow memory with
arbitrary storeName values.
- Around line 136-137: The current check uses filtered.slice(-options.limit)
which treats 0 and negative limits incorrectly; update the logic around
options.limit to explicitly handle non-positive values: when options.limit !==
undefined and options.limit <= 0 return an empty array, otherwise if
options.limit < filtered.length return filtered.slice(-options.limit), else
return filtered. Refer to the options.limit check and the filtered.slice call in
the memory adapter (the block that currently does "if (options.limit !==
undefined && filtered.length > options.limit) { return
filtered.slice(-options.limit) }") and implement the explicit <= 0 guard before
calling slice.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 20e309d0-e0f9-40e5-b886-6402e3172053

📥 Commits

Reviewing files that changed from the base of the PR and between aa3ae0d and d5db66d.

⛔ Files ignored due to path filters (1)
  • packages/evlog/test/toolkit/__snapshots__/api-surface.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (7)
  • .changeset/add-memory-drain-adapter.md
  • apps/docs/content/3.integrate/adapters/01.overview.md
  • apps/docs/content/3.integrate/adapters/self-hosted/03.memory.md
  • packages/evlog/package.json
  • packages/evlog/src/adapters/memory.ts
  • packages/evlog/test/adapters/memory.test.ts
  • packages/evlog/tsdown.config.ts

Comment thread apps/docs/content/3.integrate/adapters/self-hosted/03.memory.md Outdated
Comment thread packages/evlog/src/adapters/memory.ts Outdated
Comment thread packages/evlog/src/adapters/memory.ts Outdated
- add parseReadMemoryLogsQuery() to coerce HTTP query-string params into
  ReadMemoryLogsOptions (works with Hono, h3/Nitro, Express, Fastify,
  Next.js, Elysia, NestJS)
- fix readMemoryLogs: reads no longer mutate state via getOrCreateStore
- fix readMemoryLogs: limit <= 0 now correctly returns an empty array
- update changeset to document new helper
- extend tests: 12 new cases covering all parseReadMemoryLogsQuery paths
- nitro.config.ts: defineConfig is not exported in nitro-nightly v4;
  switch to `import type { NitroConfig } from 'nitro/types'` with a
  `satisfies NitroConfig` annotation on the default export
- vite.config.ts: rollupConfig is not a valid NitroPluginConfig key
  (the plugin accepts `config?: NitroConfig` but NitroConfig explicitly
  omits rollupConfig); remove the unused Sentry externals config since
  no @sentry package is present in this example
@vercel vercel Bot temporarily deployed to Preview – evlog-docs May 24, 2026 16:39 Inactive
@vercel vercel Bot temporarily deployed to Preview – just-use-evlog May 24, 2026 16:39 Inactive
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 24, 2026

npm i https://pkg.pr.new/evlog@351
npm i https://pkg.pr.new/@evlog/nuxthub@351

commit: 84734f2

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/tanstack-start/nitro.config.ts`:
- Line 15: The double-cast "as unknown as NitroModuleSlot" bypasses type safety
for the evlog module registration; replace it with a single "as NitroModuleSlot"
cast (or better, declare a local typed adapter/interface matching
NitroModuleSlot and cast to that) and add a short comment documenting the exact
incompatibility between "evlog/nitro/v3" and "nitro-nightly" if one prevents a
direct type match, so the intent and remaining risk are explicit; update the
expression where the module is registered (the evlog registration expression
currently using the double-cast) to use the single cast or the new adapter type.
- Line 1: Replace the incorrect import of NitroConfig from 'nitro/types' with
the NitroConfig export from 'nitropack' so the type used in the NitroModuleSlot
derivation and the file-level "satisfies NitroConfig" expression is correct;
update the import statement that currently references NitroConfig and ensure any
references to NitroModuleSlot and the line using "satisfies NitroConfig"
continue to compile against the nitropack NitroConfig type, and do not remove or
change usage of defineConfig (keep using defineConfig from 'nitro' if present)
since defineConfig is exported in Nitro v4/nightly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0dfbb546-c92b-4396-9fa6-4d46a4e19629

📥 Commits

Reviewing files that changed from the base of the PR and between a78d8e2 and 84734f2.

📒 Files selected for processing (2)
  • examples/tanstack-start/nitro.config.ts
  • examples/tanstack-start/vite.config.ts

@@ -1,12 +1,11 @@
import { defineConfig } from 'nitro'
import type { NitroConfig } from 'nitro/types'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

Does Nitro or nitro-nightly export NitroConfig from 'nitro/types'? Is defineConfig not exported in Nitro v4?

💡 Result:

NitroConfig is NOT exported from 'nitro/types'. In Nitro v4, you should use defineNitroConfig/defineConfig from Nitro entrypoints (not a nitro/types re-export), and defineConfig is exported under the 'nitro' (or 'nitropack/...') module path—not absent. Evidence: - Nitro’s own configuration docs show importing defineConfig from "nitro": import { defineConfig } from "nitro" and using it to configure Nitro options [1]. - The Nitro configuration raw docs also show the same pattern for defineConfig usage [2]. - Nitro uses a separate defineNitroConfig import from nitropack/config, indicating config helpers live in those packages/paths rather than nitro/types [3]. So, if you’re seeing that “defineConfig not exported in Nitro v4”, that would be due to importing from the wrong module path (e.g., trying to import defineConfig from 'nitro/types' or an incorrect subpath). Use the documented imports: - import { defineConfig } from "nitro" [1] - or import { defineNitroConfig } from "nitropack/config" [3] Note: the searches surfaced Nitro’s type/config source files in the nitrojs/nitro repo, but they did not provide evidence that a public re-export named NitroConfig exists under the 'nitro/types' path. The authoritative docs instead point to nitro / nitropack/config for the supported configuration entrypoints [1][3].

Citations:


🏁 Script executed:

#!/bin/bash
# Verify the Nitro version and check package.json for nitro dependency
fd -t f 'package.json' examples/tanstack-start -x cat {} \; | jq -r '.dependencies + .devDependencies | to_entries[] | select(.key | contains("nitro")) | "\(.key): \(.value)"'

Repository: HugoRCD/evlog

Length of output: 89


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="examples/tanstack-start/nitro.config.ts"
if [ -f "$FILE" ]; then
  echo "== $FILE =="
  sed -n '1,120p' "$FILE" | cat -n
else
  echo "Missing file: $FILE" >&2
  exit 1
fi

Repository: HugoRCD/evlog

Length of output: 739


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "nitro/types|NitroConfig|defineConfig|defineNitroConfig" -S .

Repository: HugoRCD/evlog

Length of output: 5840


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '1,130p' packages/evlog/src/types.ts | cat -n

Repository: HugoRCD/evlog

Length of output: 5023


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "interface NitroConfig|type NitroConfig|NitroConfig\s*=" -S . 
rg -n "declare module 'nitro/types'" -S .
rg -n "NitroConfig" examples/tanstack-start -S

Repository: HugoRCD/evlog

Length of output: 39


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "NitroConfig" -S .
rg -n "nitro/types" -S .
sed -n '1,120p' packages/nuxthub/src/module.ts | cat -n
sed -n '1,80p' packages/evlog/src/nuxt/module.ts | cat -n
sed -n '1,120p' examples/tanstack-start/nitro.config.ts | cat -n

Repository: HugoRCD/evlog

Length of output: 10767


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "== examples/tanstack-start/package.json =="
cat examples/tanstack-start/package.json

echo
echo "== examples/tanstack-start/tsconfig.json (if present) =="
ls examples/tanstack-start | rg -n "tsconfig"
if [ -f examples/tanstack-start/tsconfig.json ]; then
  cat examples/tanstack-start/tsconfig.json
fi

echo
echo "== packages/evlog/README.md (Nitro/NitroConfig related section) =="
sed -n '160,240p' packages/evlog/README.md | cat -n

echo
echo "== packages/evlog/tsdown.config.ts (nitro/types mention context) =="
rg -n "nitro/types" packages/evlog/tsdown.config.ts
sed -n '60,100p' packages/evlog/tsdown.config.ts | cat -n

Repository: HugoRCD/evlog

Length of output: 5810


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '1,120p' packages/evlog/src/nitro-v3/module.ts | cat -n
sed -n '1,120p' packages/evlog/src/nitro-v3/plugin.ts | cat -n

Repository: HugoRCD/evlog

Length of output: 7955


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '1,120p' packages/evlog/test/nitro-v3/fixture/nitro.config.ts | cat -n

Repository: HugoRCD/evlog

Length of output: 888


Fix NitroConfig type import in examples/tanstack-start/nitro.config.ts

  • NitroConfig is imported from nitro/types, but this repo uses NitroConfig from nitropack and Nitro’s public config/type exports don’t expose NitroConfig via nitro/types; this will break typing at the NitroModuleSlot derivation and satisfies NitroConfig usage.
  • Replace the NitroConfig import accordingly (line 1; used at lines 6 and 17).
  • The “defineConfig is non-exported” rationale is also incorrect for Nitro v4/nightly: defineConfig is exported from nitro (so switching away from it isn’t necessary).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/tanstack-start/nitro.config.ts` at line 1, Replace the incorrect
import of NitroConfig from 'nitro/types' with the NitroConfig export from
'nitropack' so the type used in the NitroModuleSlot derivation and the
file-level "satisfies NitroConfig" expression is correct; update the import
statement that currently references NitroConfig and ensure any references to
NitroModuleSlot and the line using "satisfies NitroConfig" continue to compile
against the nitropack NitroConfig type, and do not remove or change usage of
defineConfig (keep using defineConfig from 'nitro' if present) since
defineConfig is exported in Nitro v4/nightly.

@@ -15,4 +14,4 @@ export default defineConfig({
env: { service: 'tanstack-start-example' },
}) as unknown as NitroModuleSlot,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Double-cast escape hatch bypasses type safety.

The as unknown as NitroModuleSlot pattern completely disables type checking for the evlog module registration. While the comment explains the version mismatch between evlog/nitro/v3 and nitro-nightly, this loses all compile-time guarantees that the module shape is correct.

Consider whether a single as NitroModuleSlot cast would suffice, or document the specific incompatibility that requires the double-cast.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/tanstack-start/nitro.config.ts` at line 15, The double-cast "as
unknown as NitroModuleSlot" bypasses type safety for the evlog module
registration; replace it with a single "as NitroModuleSlot" cast (or better,
declare a local typed adapter/interface matching NitroModuleSlot and cast to
that) and add a short comment documenting the exact incompatibility between
"evlog/nitro/v3" and "nitro-nightly" if one prevents a direct type match, so the
intent and remaining risk are explicit; update the expression where the module
is registered (the evlog registration expression currently using the
double-cast) to use the single cast or the new adapter type.

@HugoRCD HugoRCD merged commit ee997b3 into main May 24, 2026
20 checks passed
@HugoRCD HugoRCD deleted the EVL-170 branch May 24, 2026 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant