Skip to content

@ainative/ai-kit@0.2.1: browser entry bundles server-only deps (ioredis, fs, stream/promises), breaks Next.js client build #206

Description

@urbantech

Summary

@ainative/ai-kit@0.2.1 has no browser-safe entry point. Its single dist/index.mjs (used by both import and ./react in package.json's exports map) bundles server-only code into the same bundle as its client-facing React hooks, so importing anything from the package into a Client Component breaks the build in any bundler that respects "use client" boundaries (confirmed with Next.js 16 / Turbopack).

Repro

npm install @ainative/ai-kit
"use client";
import { useAIStream } from "@ainative/ai-kit";

export default function Chat() {
  const { messages, send, isStreaming } = useAIStream({ endpoint: "/api/chat" });
  // ...
}
next build

Actual result

Build fails with three Module not found errors, all originating from @ainative/ai-kit-core/dist/index.mjs (which @ainative/ai-kit's index.mjs imports from):

./node_modules/@ainative/ai-kit-core/dist/index.mjs:5:1
Error: Module not found: Can't resolve 'fs'
  3 | import { createHash, randomBytes, createCipheriv, createDecipheriv, randomUUID } from 'crypto';
  4 | import Redis from 'ioredis';
> 5 | import { promises, createReadStream, createWriteStream } from 'fs';
  6 | import * as path from 'path';
  7 | import { createGzip } from 'zlib';

./node_modules/@ainative/ai-kit-core/dist/index.mjs:4:1
Error: Module not found: Can't resolve 'ioredis'

./node_modules/@ainative/ai-kit-core/dist/index.mjs:8:1
Error: Module not found: Can't resolve 'stream/promises'

Import trace confirms it's reached purely through the client hook import:

Client Component Browser:
  ./node_modules/@ainative/ai-kit-core/dist/index.mjs [Client Component Browser]
  ./node_modules/@ainative/ai-kit/dist/index.mjs [Client Component Browser]
  ./src/components/AssistantChat.tsx [Client Component Browser]

Root cause (inspected directly)

@ainative/ai-kit-core/dist/index.mjs is a single bundle containing both:

  • Browser-safe primitives (AIStream, useAIStream, useConversation, StreamingMessage, etc.)
  • Node-only persistence code (ZeroDBSessionStore/ZeroDBStorage, which pulls in ioredis, fs, stream/promises)

There's a dist/browser.mjs and dist/server.mjs in @ainative/ai-kit-core that appear to already split these concerns correctly at that layer — but @ainative/ai-kit's own package.json exports map only points at one undifferentiated dist/index.mjs, with no browser condition, so consumers can't reach the already-built browser-safe split.

"exports": {
  ".": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs", "require": "./dist/index.js" },
  "./react": { "types": "./dist/index.d.ts", "import": "./dist/index.mjs", "require": "./dist/index.js" }
}

Secondary issue: missing .d.ts

package.json declares "types": "./dist/index.d.ts", but the published tarball does not contain dist/index.d.ts — only index.js, index.js.map, index.mjs, index.mjs.map. Any TypeScript consumer gets TS7016: Could not find a declaration file for module '@ainative/ai-kit'. Worked around locally with a hand-written ambient module declaration matching the real exports inspected from the compiled JS, but this should ship for real.

Suggested fix

Add a browser export condition (and/or split @ainative/ai-kit itself into a client-safe entry that only re-exports the browser-safe subset of @ainative/ai-kit-core, mirroring the browser.mjs/server.mjs split that already exists one level down), and include the built .d.ts in the published package.

Environment

  • @ainative/ai-kit@0.2.1, @ainative/ai-kit-core@0.2.0
  • Next.js 16.3.5, Turbopack
  • Node 22.19.0

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

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions