
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@shopkit/events
Advanced tools
Event-driven analytics system for e-commerce storefronts.
onCartEvent callback (zero changes to @shopkit/cart)npm install @shopkit/events
# or
bun add @shopkit/events
Peer dependencies: react, zod, next (optional), zustand (optional)
// app/layout.tsx
'use client';
import { EventProvider } from '@shopkit/events/react';
import { usePathname, useSearchParams } from 'next/navigation';
function EventProviderWrapper({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const searchParams = useSearchParams()?.toString();
return (
<EventProvider pathname={pathname} searchParams={searchParams}>
{children}
</EventProvider>
);
}
export default function RootLayout({ children }) {
return (
<html>
<body>
<EventProviderWrapper>{children}</EventProviderWrapper>
</body>
</html>
);
}
// bootstrap/cart.ts
import { eventBus } from '@shopkit/events';
import { createCartMapper } from '@shopkit/events/mappers';
import { createCartEventHandler } from '@shopkit/events/emitters';
import { configureCart } from '@shopkit/cart';
// priceDivisor: 100 converts paise (35500) to rupees (355.00)
const mapper = createCartMapper('INR', 100);
const handleCartEvent = createCartEventHandler(eventBus, mapper);
configureCart({
// ...existing config...
onCartEvent: handleCartEvent,
});
// components/ProductPage.tsx
import { useProductViewEmitter } from '@shopkit/events/emitters';
import { useEventBus } from '@shopkit/events/react';
function ProductPage({ product }) {
const bus = useEventBus();
const standardItem = {
item_id: product.id,
item_name: product.title,
item_brand: product.vendor,
item_category: product.productType,
price: product.price,
quantity: 1,
currency: 'INR',
};
useProductViewEmitter(bus, standardItem);
return <div>{/* product UI */}</div>;
}
import { useTrackEvent } from '@shopkit/events/react';
import { OpenStoreEventType } from '@shopkit/events';
function SearchResults({ term, count }) {
const emit = useTrackEvent();
useEffect(() => {
emit(OpenStoreEventType.SEARCH, {
search_term: term,
results_count: count,
});
}, [term, count]);
return <div>{/* results */}</div>;
}
eventBusSingleton EventBus instance. The single point through which all analytics events flow.
| Method | Signature | Description |
|---|---|---|
subscribe | (type: OpenStoreEventType, handler) => Unsubscribe | Subscribe to a specific event type |
subscribeAll | (handler) => Unsubscribe | Subscribe to all events |
emit | (type, data, overrides?) => OpenStoreEvent | null | Emit an event (null if blocked by middleware) |
use | (middleware: Middleware) => void | Register middleware |
getEventLog | () => ReadonlyArray<OpenStoreEvent> | Get ring buffer of last 100 events |
reset | () => void | Clear all state (testing only) |
import { OpenStoreEventType } from '@shopkit/events';
// 16 client-side events
OpenStoreEventType.PAGE_VIEW
OpenStoreEventType.VIEW_PRODUCT
OpenStoreEventType.ENGAGE_CONTENT
OpenStoreEventType.ADD_TO_CART
OpenStoreEventType.REMOVE_FROM_CART
OpenStoreEventType.VIEW_CART
OpenStoreEventType.BEGIN_CHECKOUT
OpenStoreEventType.ADD_PAYMENT_INFO
OpenStoreEventType.ADD_SHIPPING_INFO
OpenStoreEventType.PURCHASE
OpenStoreEventType.SEARCH
OpenStoreEventType.ADD_TO_WISHLIST
OpenStoreEventType.KWIKPASS_LOGIN_ATTEMPTED
OpenStoreEventType.KWIKPASS_LOGIN_COMPLETED
OpenStoreEventType.VIEW_PROMO
OpenStoreEventType.AB_TEST_VIEWED
// 4 server-side events
OpenStoreEventType.ORDER_FULFILLED
OpenStoreEventType.ORDER_SHIPPED
OpenStoreEventType.ORDER_DELIVERED
OpenStoreEventType.ORDER_CANCELLED
@shopkit/events/middleware)| Middleware | Factory | Description |
|---|---|---|
schemaValidatorMiddleware | createSchemaValidatorMiddleware() | Validates payloads against Zod schemas |
deduplicatorMiddleware | createDeduplicatorMiddleware(windowMs?) | Blocks duplicate events within 2s window |
userEnricherMiddleware | createUserEnricherMiddleware() | Enriches with cookies, affiliate data, PII hashes |
devLoggerMiddleware | createDevLoggerMiddleware(options?) | Console logging (dev default, env/console toggle) |
serverRelayMiddleware | createServerRelayMiddleware(config?) | Batches and sends via sendBeacon |
interface DevLoggerOptions {
enabled?: boolean; // Explicitly enable/disable. Default: auto (dev mode only)
}
Logging is active when ANY of these is true:
enabled: true (e.g., process.env.NEXT_PUBLIC_EVENT_DEBUG === "true")NODE_ENV === "development" (default, unless enabled: false)window.__shopkit_debug = true (ad-hoc toggle from browser console — works in production)interface ServerRelayConfig {
ingestUrl?: string; // Default: /api/events/ingest
flushIntervalMs?: number; // Default: 5000
maxBatchSize?: number; // Default: 10
}
@shopkit/events/emitters)createCartEventHandler(bus, mapper, options?)Creates a handler for @shopkit/cart's onCartEvent callback.
interface CartEmitterOptions {
getCartItems?: () => CartItemLike[];
productIdentifier?: ProductIdentifier; // Override bus.productIdentifier
}
The handler normalizes raw cart store items (snake_case fields, price as number) automatically. productIdentifier is read lazily from bus.productIdentifier at event-fire time, so it respects config set by EventProvider even if the cart is bootstrapped first.
<PageEmitter bus={bus} pathname={pathname} searchParams={searchParams} />React component that emits page_view on route changes. Rendered automatically by EventProvider.
useProductViewEmitter(bus, product, options?)Hook that emits view_product on mount and engage_content after 20s dwell time.
@shopkit/events/mappers)| Mapper | Factory | Description |
|---|---|---|
CartMapper | createCartMapper(currency?, priceDivisor?) | CartItem to ProductFields. priceDivisor converts raw price units (e.g. 100 for paise→rupees) |
ShopifyProductMapper | createShopifyProductMapper(currency?) | Product GraphQL to StandardItem |
ShopifyOrderMapper | createShopifyOrderMapper(currency?) | Order to PurchasePayload |
Note:
ShopifyCartMapperandcreateShopifyCartMapperare deprecated aliases forCartMapperandcreateCartMapper.
@shopkit/events/react)<EventProvider config? pathname? searchParams?>Root provider. Initializes EventBus with middleware pipeline and renders PageEmitter.
interface EventProviderConfig {
enableDevTools?: boolean;
ingestUrl?: string;
disablePageView?: boolean;
disableServerRelay?: boolean;
productIdentifier?: ProductIdentifier; // "product_id" | "sku" | "variant_id"
bus?: EventBus; // For testing
}
| Hook | Return Type | Description |
|---|---|---|
useEventBus() | EventBus | Access EventBus from context |
useTrackEvent() | (type, data) => OpenStoreEvent | null | Stable emit function |
useEventSubscribe(type, handler) | void | Subscribe with auto-cleanup |
useEventSubscribeAll(handler) | void | Subscribe to all events |
useEventLog() | ReadonlyArray<OpenStoreEvent> | Access event ring buffer |
@shopkit/events/schemas)Zod validation schemas for all 20 events plus shared data shapes.
| Export | Description |
|---|---|
StandardItemSchema | 15-field GA4-aligned item schema |
StandardUserDataSchema | PII hashed user data schema |
StandardCookiesSchema | Platform tracking cookies schema |
eventPayloadSchemas | Record mapping event type to Zod schema |
OpenStoreEventEnvelopeSchema | Full event envelope structural schema |
import { eventBus, OpenStoreEventType } from '@shopkit/events';
import { schemaValidatorMiddleware } from '@shopkit/events/middleware';
import { createCartEventHandler } from '@shopkit/events/emitters';
import { createCartMapper } from '@shopkit/events/mappers';
import { EventProvider, useTrackEvent } from '@shopkit/events/react';
import { eventPayloadSchemas } from '@shopkit/events/schemas';
MIT
FAQs
Event-driven analytics system for OpenStore storefronts
We found that @shopkit/events demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.