npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@abto-app/event

v1.1.0

Published

ABTO Browser SDK — typed custom events, identity, sessions, AI trace correlation, and opt-in autocapture.

Readme

@abto-app/event

ABTO SDK는 고객사가 선택해 직접 기록한 사용자 행동을 수집하고, Gateway가 반환한 request_id를 통해 서버의 LLM 비용·지연 데이터와 연결한다.

@abto-app/event는 공개키를 사용하는 Browser 전용 패키지다. 비밀키와 provider credential을 사용하는 Node 환경에는 별도 @abto-app/calling 패키지를 설치한다.

PostHog의 autocapture, ingestion, session, schema/discovery는 ABTO 이벤트 설계의 참고 모델이다. ABTO 이벤트를 PostHog로 보내는 연동이 아니라, 검증된 수집 철학을 ABTO 독립 수집 구조에 적용한다.

설치

pnpm add @abto-app/event

이벤트 경계

Browser SDK가 보내는 이벤트는 두 종류다.

| 종류 | SDK 이름 | Analytics wire 이름 | 정의 주체 | 발생 방식 | |---|---|---|---|---| | 시스템 이벤트 | $로 시작 | ABTO canonical 이름 | ABTO | 명시적으로 켠 autocapture 또는 전용 API | | 커스텀 이벤트 | $ 없이 제품 도메인 이름 사용 | 같은 이름 | 고객 저장소 | client.capture() |

사용자는 $ 이벤트나 $ 속성을 등록할 수 없다. 아래 canonical wire 이름도 ABTO가 소유하므로 커스텀 이벤트로 등록할 수 없다. ABTO 시스템 이벤트는 일반 capture()로 보낼 수 없으며 SDK 내부 경로와 AI trace 전용 메서드만 발생시킨다.

Browser SDK 시스템 이벤트

| SDK 이벤트 | Analytics event_name | 의미 | 발생 조건 | |---|---|---|---| | $pageview | pageview | 페이지/SPA route 진입 | autocapture opt-in 시 초기 로드, history 변경, bfcache 복원 | | $pageleave | pageleave | 페이지/route 이탈 | autocapture opt-in 시 SPA 이동, pagehide | | $autocapture | interaction_autocaptured | DOM 상호작용 원시 사실 | autocapture opt-in 시 click, change, submit, copy | | $rageclick | interaction_rageclick | 짧은 시간의 반복 클릭 | autocapture opt-in 시 SDK 휴리스틱 | | $dead_click | interaction_deadclick | 반응이 관측되지 않은 클릭 | autocapture opt-in 시 SDK 휴리스틱 | | $ai_prompt_submitted | llm_prompt_submitted | 프롬프트 제출을 앱이 확인 | trace.submitPrompt() | | $ai_response_rendered | llm_response_rendered | 응답이 UI에 렌더됨을 앱이 확인 | trace.markResponseRendered() | | $ai_response_interacted | llm_response_interacted | 응답에 대한 명시적 행동 | trace.captureResponseInteraction() |

$session_start$session_end는 보내지 않는다. 모든 이벤트의 session_id와 timestamp의 최솟값·최댓값을 분석 계층에서 사용해 세션 시작, 종료, duration을 파생한다. 브라우저 종료 신호는 유실될 수 있으므로 $session_end를 확정 사실로 기록하지 않는다.

커스텀 이벤트 정본: abto.events.ts

제품 이벤트는 고객 애플리케이션 저장소의 abto.events.ts에 사전 등록한다. 이 파일을 코드 리뷰와 향후 CLI/CI schema push의 정본으로 사용한다.

// abto.events.ts
import { defineEvents } from '@abto-app/event';

export const events = defineEvents({
  checkout_completed: { description: '결제가 완료됨' },
});
import { initAbto } from '@abto-app/event';
import { events } from './abto.events';

const abto = initAbto({
  projectKey: 'public_project_key',
  environment: 'development',
  events,
});

abto.capture('checkout_completed', {
  value: 49000,
  scale: 'KRW',
});

valuescale은 모두 선택이다. 이름만 보내거나 추가 속성만 보낼 수 있다. 생략한 valuescale은 전송하지 않고, 빈 문자열 scale은 그대로 전송한다. 단순 행동 건수는 { value: 1, scale: 'count' }로 기록한다. Success Metric은 이벤트 이름과 metric을 집계하며, 같은 객체에 추가한 나머지 속성은 자동으로 extra_json에 들어간다.

defineEvents()에서 이름을 추론하므로 잘못된 이벤트 이름을 개발 시점에 확인할 수 있다. 런타임 정책은 환경별로 다르다.

| 환경 | 미등록 이벤트 | |---|---| | development | 전송하고 Discovered 경고 | | production | drop |

필수 metric이 없거나 계약을 벗어나면 경고 후 해당 커스텀 이벤트를 보내지 않는다.

추가 속성은 같은 객체에 넣는다. value·scale은 최상위 metric으로, tierextra_json.tier로 전송된다.

abto.capture('checkout_completed', { value: 49000, scale: 'KRW', tier: 'pro' });

초기화와 autocapture

앱 루트에서 한 번 초기화한다. 설정을 생략한 기본 초기화는 identity와 trace context만 준비하며 이벤트를 만들지 않는다. 고객사가 필요하다고 정한 Custom Event와 LLM trace event만 해당 제품 동작에서 직접 호출한다.

const abto = initAbto({
  projectKey: 'public_project_key',
  apiHost: 'https://api.abto.app',
  environment: 'production',
  events,
});

| 설정 | 기본값 | 역할 | |---|---|---| | projectKey | 필수 | 브라우저에 둘 수 있는 공개 Event Key | | apiHost | https://api.abto.app | Event API host. SDK가 /v1/collect/events를 붙인다 | | environment | production | development에서는 미등록 event와 schema drift를 경고 후 전송한다 | | appVersion | 미설정 | 지정하면 event context의 $app_version으로 전송한다 | | events | {} | defineEvents()로 만든 Custom Event registry | | capture.prompt | metadata_only | off, hash, metadata_only, full 중 prompt 수집 정책 | | capture.response | metadata_only | off, metadata_only, full 중 response 수집 정책 | | capture.mask | all | off, sensitive, all 중 autocapture DOM text/value 마스킹 정책 | | autocapture.enabled | false | 페이지와 DOM 자동 수집의 명시적 opt-in |

페이지와 DOM 상호작용을 넓게 수집해야 하고 데이터 정책 검토를 마친 경우에만 autocapture를 명시적으로 켠다.

const abto = initAbto({
  projectKey: 'public_project_key',
  autocapture: { enabled: true },
});

이전 기본값에서 마이그레이션

0.1.4 이하에서 autocapture를 생략하면 자동 수집이 켜졌다. 그 동작에 의존한 애플리케이션은 업그레이드할 때 autocapture: { enabled: true }를 추가해야 한다. 자동 수집이 필요하지 않은 애플리케이션은 설정을 생략하고 선택한 capture()LlmTrace 호출만 유지한다.

기본 endpoint는 ${apiHost}/v1/collect/events다. SDK 내부의 Browser 이벤트는 전송 직전에 현재 Analytics 수신 계약으로 변환된다.

{
  "batch": [
    {
      "event_id": "019b...",
      "device_id": "019b...",
      "session_id": "019b...",
      "event_name": "checkout_started",
      "value": 3000,
      "scale": "KRW",
      "occurred_at": "2026-07-15T04:10:00.000Z",
      "extra_json": {
        "value": 3000,
        "scale": "KRW",
        "$lib": "web",
        "$lib_version": "1.1.0"
      }
    }
  ]
}

서버의 이벤트별 응답은 event UUID를 key로 사용한다.

{
  "results": {
    "019b5b74-11d0-7000-8000-000000000001": {
      "result": "drop",
      "code": "schema_type_mismatch"
    }
  }
}

모든 collector 요청은 public project key를 Bearer header에 싣는다. 페이지 이탈도 응답을 읽을 수 있는 fetch(..., { keepalive: true })를 사용하며, 서버가 이 key에서 project_idorganization_id를 결정한다. $tenant_id를 포함한 client property는 분석 문맥이며 인증·project 귀속 값이 아니다.

수신 계약의 상한은 요청당 100 events다. SDK 기본값은 20이며 약 60 KiB 이하 payload만 keepalive로 전송한다. malformed request와 인증 실패는 요청 단위 4xx, 개별 validation/storage 실패는 2xx 응답의 UUID별 warning, drop, retry로 처리한다. 커스텀 event_name은 Backend와 같은 UTF-16 기준 최대 200자이며, defineEvents()와 runtime capture가 enqueue 전에 검증한다. metric scale은 최대 16자이며, 빈 값이나 초과한 값은 해당 커스텀 이벤트를 경고 후 보내지 않는다.

SDK 자체 전송 진단

Browser SDK는 send_failed, outbox_write_failed, identity_persist_failed, storage_unavailable 네 실패를 메모리 counter로만 모은다. 다음 event batch가 있을 때 고정 스키마의 선택적 diagnostics 필드로 같은 요청에 동승시키며, 이를 위한 별도 네트워크 요청이나 제품 event를 만들지 않는다. 성공 응답을 받은 counter만 차감하고 전송 실패 시 보존한다.

{
  "batch": [{ "event_id": "019b...", "event_name": "checkout_started" }],
  "diagnostics": {
    "sdk_name": "browser-javascript",
    "counters": { "send_failed": 1 }
  }
}

diagnostics는 위 고정 SDK 이름과 counter만 포함하고 사용자 ID, event property, URL, 오류 원문을 복사하지 않는다. 고정된 네 counter만 직렬화하므로 별도 크기 제한이나 첨부 복구 경로가 필요하지 않다. event가 전혀 없으면 진단만 보내는 요청도 생기지 않는다.

SDK 내부 queue와 API에서는 $ 이름을 유지하지만, Analytics의 고정 Event 계약은 $ 접두 event_name을 거절한다. Transport가 위 표의 canonical 이름으로만 변환해 전송한다. device_id·session_id·trace_id는 wire의 1급 필드로 실리고, 담을 필드가 없는 $lib·$user_id·$feature_id 같은 SDK 소유 context만 extra_json에 남는다. Dashboard 이벤트 카탈로그와 Success Metric에서는 canonical wire 이름을 사용한다.

autocapture를 명시적으로 켠 경우 annotation은 원시 $autocapture를 다른 이벤트로 바꾸지 않는다. 원시 상호작용을 보존하면서 분석 차원만 보강한다.

<button
  data-abto-action="accept"
  data-abto-surface="generator"
  data-abto-feature-id="resume.make"
  data-abto-response-id="resp_123"
  data-abto-request-id="req_123">
  적용
</button>

autocapture를 켰다면 위 클릭은 $autocapture로 수집되며 $ai_action, $surface, $feature_id, $response_id, $request_id가 함께 실린다. 업무 의미가 확정된 행동은 앱 코드에서 커스텀 이벤트 또는 AI 전용 메서드로 별도 기록한다.

개인정보 기본값

prompt, response, DOM text/value는 기본적으로 원문을 수집하지 않는다.

위 설정 표의 capture.prompt, capture.response, capture.mask를 생략해도 각각 metadata_only, metadata_only, all의 안전한 기본값이 적용된다.

| annotation | 동작 | |---|---| | data-abto-no-capture | 자신과 하위 트리를 수집하지 않음 | | data-abto-sensitive | 자신과 하위 text/value를 항상 전체 마스킹 | | data-abto-include | 해당 요소의 text/value 수집을 명시적으로 허용 |

password, hidden input과 카드·비밀번호·SSN 계열 필드는 annotation과 무관하게 보호한다. full 원문 수집은 명시적 opt-in이며 고객의 동의·보존·삭제 정책과 함께 사용해야 한다.

브라우저에서 관측 가능한 AI 이벤트

브라우저가 확실히 아는 세 가지 사실만 전용 API로 제공한다.

const trace = abto.startLlmTrace();

await trace.submitPrompt({
  prompt: promptText,
  language: 'ko',
});

const response = await fetch('/api/generate', {
  method: 'POST',
  headers: { 'content-type': 'application/json', ...trace.getHeaders() },
  body: JSON.stringify({ prompt: promptText }),
});
trace.attachRequestId(response);

await trace.markResponseRendered({
  responseId: 'resp_123',
  timeToRenderMs: 1380,
});

await trace.captureResponseInteraction('copied', {
  responseId: 'resp_123',
  source: 'copy_button',
});

captureResponseInteraction()은 계약에 정의된 12개 interaction type만 enqueue한다. plain JavaScript에서 다른 값이 들어와도 경고 후 제외하며, 제품 고유 행동은 커스텀 이벤트로 기록한다.

provider/model/token/cost/retry/fallback, 실제 첫 토큰 시점과 request 성공·실패는 Server SDK/Gateway가 소유한다. AI task 완료·이탈은 제품마다 의미가 다르므로 커스텀 이벤트 또는 분석 파생 지표로 둔다.

trace.getHeaders()는 브라우저가 소유하는 x-abto-device-id만 반환한다. 실제 모델 호출의 featureId는 브라우저 입력을 신뢰하지 않고 백엔드가 Calling SDK context에 설정한다.

식별자와 세션

$ 접두가 붙은 것은 extra_json이 유일한 자리인 context이고, 나머지는 wire의 1급 필드다.

| 속성 | 수명과 역할 | |---|---| | device_id | 프로젝트별 브라우저 설치, localStorage 유지 | | session_id | 탭 사이에서 공유하는 논리 세션, 30분 idle 또는 24시간 max age에 회전 | | trace_id | 한 사용자 행동에서 발생한 브라우저 이벤트를 묶는 값 | | $user_id | identify()로 연결한 제품 사용자 | | $window_id | 탭/window별 ID, sessionStorage 유지 | | $pageview_id | 페이지/SPA route 구간, pageview마다 회전 | | $request_id | Gateway의 실제 provider 호출 PK |

abto.identify('user_123', 'tenant_123');
abto.reset();        // user/tenant 제거, device 유지
abto.forgetDevice(); // outbox와 device identity 제거

identity 저장이 실패해도 새 session/device ID는 현재 인스턴스의 메모리에 유지된다. 저장이 복구되면 현재 값을 다시 보관한다. 활동 시각만 저장하다 실패한 경우에는 다른 탭의 session/device ID 변경을 계속 반영한다.

전송과 재시도

  • 이벤트는 프로젝트별 localStorage outbox에 이벤트별 항목으로 먼저 저장한다. 여러 탭의 enqueue·ack가 다른 이벤트를 덮어쓰지 않는다.
  • 이벤트별 순번으로 보관 순서를 유지한다. 같은 시각의 이벤트도 UUID 정렬에 의존하지 않고 오래된 항목부터 버퍼 상한을 적용한다.
  • 기존 배열 형식 outbox는 읽을 때 이벤트별 항목으로 옮긴다. 저장 실패 시 메모리 큐를 유지하고 다음 flush에서 저장을 재시도한다.
  • 여러 인스턴스가 같은 이벤트를 재시도할 수 있으며 UUID 기반 서버 중복 처리를 따른다.
  • 기본적으로 최대 20개씩 POST /v1/collect/events로 보낸다.
  • 일반 flush와 페이지 이탈 모두 응답 가능한 fetch를 사용하며, 안전 크기의 이탈 payload에만 keepalive를 켠다.
  • keepalive payload는 약 60 KiB 이내로 제한한다. 응답 전에 페이지가 종료되면 localStorage outbox가 다음 SDK 인스턴스에서 재전송한다.
  • 408, 429, 5xx와 이벤트별 retry만 지수 backoff로 재시도한다.
  • 영구 4xx와 이벤트별 drop은 outbox에서 제거한다.
  • 이벤트별 ok, warning, drop, retry 응답을 UUID 기준으로 처리한다.

Public API (browser)

initAbto · defineEvents · client.identify · client.getIdentity · client.reset · client.forgetDevice · client.startLlmTrace · client.capture · client.flush · client.shutdown · trace.getHeaders · trace.submitPrompt · trace.markResponseRendered · trace.captureResponseInteraction.

개발 검증

pnpm test
pnpm typecheck
pnpm build
node ../../examples/browser-smoke/collector.mjs

실브라우저 검증 절차는 examples/browser-smoke/README.md를 따른다.

추가 속성 값은 JSON 스칼라(문자열·유한한 수·불리언·null), 스칼라 배열, 스칼라 값으로 구성된 객체를 받습니다. 더 깊은 중첩, U+0000, 최상위 $ 접두 키는 해당 커스텀 이벤트와 함께 거절합니다. 이벤트 ID·시각·기기/세션 ID·$ 문맥은 SDK가 자동으로 추가합니다.

이 capture API는 1.0.0의 위치 인자 API를 대체합니다. 업그레이드 시 호출부를 바꿔야 하며, 기존 저장 이벤트의 해석은 유지됩니다.