@coduckai/sdk 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ai/index.cjs CHANGED
@@ -88,7 +88,7 @@ async function anthropicChat(options) {
88
88
  const client = new Anthropic({ apiKey: config.ai?.apiKey || process.env.ANTHROPIC_API_KEY });
89
89
  const messages = options.messages.filter((m) => m.role !== "system").map((m) => ({ role: m.role, content: m.content }));
90
90
  const response = await client.messages.create({
91
- model: options.model || "claude-sonnet-4-5-20250929",
91
+ model: options.model || "claude-sonnet-4-6",
92
92
  max_tokens: options.maxTokens || 4096,
93
93
  ...options.system && { system: options.system },
94
94
  messages,
@@ -109,7 +109,7 @@ async function* anthropicStreamChat(options) {
109
109
  const client = new Anthropic({ apiKey: config.ai?.apiKey || process.env.ANTHROPIC_API_KEY });
110
110
  const messages = options.messages.filter((m) => m.role !== "system").map((m) => ({ role: m.role, content: m.content }));
111
111
  const stream = client.messages.stream({
112
- model: options.model || "claude-sonnet-4-5-20250929",
112
+ model: options.model || "claude-sonnet-4-6",
113
113
  max_tokens: options.maxTokens || 4096,
114
114
  ...options.system && { system: options.system },
115
115
  messages,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/internal/env.ts","../../src/internal/config.ts","../../src/ai/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Environment variable helpers\n */\n\nexport function getEnv(key: string, fallback?: string): string | undefined {\n return process.env[key] || fallback;\n}\n\nexport function requireEnv(key: string): string {\n const val = process.env[key];\n if (!val) {\n throw new Error(`@coduckai/sdk: Missing required environment variable: ${key}`);\n }\n return val;\n}\n\nexport function isServer(): boolean {\n return typeof window === 'undefined';\n}\n\nexport function assertServer(moduleName: string): void {\n if (!isServer()) {\n throw new Error(\n `@coduckai/sdk/${moduleName} is server-only. Use @coduckai/sdk/client for browser code.`\n );\n }\n}\n","/**\n * Global SDK configuration\n *\n * CoDuck apps use env vars automatically — no configure() needed.\n * Standalone usage can call configure() to override.\n */\n\nexport interface SDKConfig {\n auth?: {\n jwtSecret?: string;\n jwtExpiresIn?: string;\n bcryptRounds?: number;\n };\n db?: {\n connectionString?: string;\n };\n client?: {\n baseUrl?: string;\n tokenKey?: string;\n };\n payments?: {\n stripeSecretKey?: string;\n connectedAccountId?: string;\n webhookSecret?: string;\n };\n ai?: {\n provider?: 'openai' | 'anthropic';\n apiKey?: string;\n };\n}\n\nlet globalConfig: SDKConfig = {};\n\nexport function setConfig(config: SDKConfig): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\nexport function getConfig(): SDKConfig {\n return globalConfig;\n}\n","/**\n * @coduckai/sdk/ai — AI Providers (server-only)\n *\n * Unified interface for OpenAI and Anthropic. Auto-detects provider from env vars.\n * Set OPENAI_API_KEY for OpenAI or ANTHROPIC_API_KEY for Anthropic.\n */\n\nimport { assertServer } from '../internal/env';\nimport { getConfig } from '../internal/config';\n\nassertServer('ai');\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system';\n content: string;\n}\n\nexport interface ChatOptions {\n system?: string;\n messages: ChatMessage[];\n model?: string;\n maxTokens?: number;\n temperature?: number;\n}\n\nexport interface ChatResponse {\n text: string;\n usage: { inputTokens: number; outputTokens: number };\n}\n\nexport interface StreamChunk {\n text: string;\n done: boolean;\n}\n\nexport type AIProvider = 'openai' | 'anthropic' | null;\n\n// ---------------------------------------------------------------------------\n// Provider detection\n// ---------------------------------------------------------------------------\n\nfunction detectProvider(): AIProvider {\n const config = getConfig();\n if (config.ai?.provider) return config.ai.provider;\n if (process.env.OPENAI_API_KEY) return 'openai';\n if (process.env.ANTHROPIC_API_KEY) return 'anthropic';\n return null;\n}\n\nfunction requireProvider(): AIProvider & string {\n const provider = detectProvider();\n if (!provider) {\n throw new Error(\n '@coduckai/sdk/ai: No AI provider configured. Set OPENAI_API_KEY or ANTHROPIC_API_KEY.'\n );\n }\n return provider;\n}\n\n// ---------------------------------------------------------------------------\n// OpenAI\n// ---------------------------------------------------------------------------\n\nasync function openaiChat(options: ChatOptions): Promise<ChatResponse> {\n const { default: OpenAI } = await import('openai');\n const config = getConfig();\n const client = new OpenAI({ apiKey: config.ai?.apiKey || process.env.OPENAI_API_KEY });\n\n const messages: Array<{ role: string; content: string }> = [];\n if (options.system) {\n messages.push({ role: 'system', content: options.system });\n }\n messages.push(...options.messages);\n\n const response = await client.chat.completions.create({\n model: options.model || 'gpt-4o',\n messages: messages as any,\n max_tokens: options.maxTokens || 4096,\n temperature: options.temperature ?? 0.7,\n });\n\n return {\n text: response.choices[0]?.message?.content || '',\n usage: {\n inputTokens: response.usage?.prompt_tokens || 0,\n outputTokens: response.usage?.completion_tokens || 0,\n },\n };\n}\n\nasync function* openaiStreamChat(options: ChatOptions): AsyncGenerator<StreamChunk> {\n const { default: OpenAI } = await import('openai');\n const config = getConfig();\n const client = new OpenAI({ apiKey: config.ai?.apiKey || process.env.OPENAI_API_KEY });\n\n const messages: Array<{ role: string; content: string }> = [];\n if (options.system) {\n messages.push({ role: 'system', content: options.system });\n }\n messages.push(...options.messages);\n\n const stream = await client.chat.completions.create({\n model: options.model || 'gpt-4o',\n messages: messages as any,\n max_tokens: options.maxTokens || 4096,\n temperature: options.temperature ?? 0.7,\n stream: true,\n });\n\n for await (const chunk of stream) {\n const text = chunk.choices[0]?.delta?.content || '';\n const done = chunk.choices[0]?.finish_reason !== null && chunk.choices[0]?.finish_reason !== undefined;\n if (text) yield { text, done: false };\n if (done) yield { text: '', done: true };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Anthropic\n// ---------------------------------------------------------------------------\n\nasync function anthropicChat(options: ChatOptions): Promise<ChatResponse> {\n const { default: Anthropic } = await import('@anthropic-ai/sdk');\n const config = getConfig();\n const client = new Anthropic({ apiKey: config.ai?.apiKey || process.env.ANTHROPIC_API_KEY });\n\n const messages = options.messages\n .filter(m => m.role !== 'system')\n .map(m => ({ role: m.role as 'user' | 'assistant', content: m.content }));\n\n const response = await client.messages.create({\n model: options.model || 'claude-sonnet-4-5-20250929',\n max_tokens: options.maxTokens || 4096,\n ...(options.system && { system: options.system }),\n messages,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n });\n\n const textBlock = response.content.find((b: any) => b.type === 'text');\n\n return {\n text: (textBlock as any)?.text || '',\n usage: {\n inputTokens: response.usage?.input_tokens || 0,\n outputTokens: response.usage?.output_tokens || 0,\n },\n };\n}\n\nasync function* anthropicStreamChat(options: ChatOptions): AsyncGenerator<StreamChunk> {\n const { default: Anthropic } = await import('@anthropic-ai/sdk');\n const config = getConfig();\n const client = new Anthropic({ apiKey: config.ai?.apiKey || process.env.ANTHROPIC_API_KEY });\n\n const messages = options.messages\n .filter(m => m.role !== 'system')\n .map(m => ({ role: m.role as 'user' | 'assistant', content: m.content }));\n\n const stream = client.messages.stream({\n model: options.model || 'claude-sonnet-4-5-20250929',\n max_tokens: options.maxTokens || 4096,\n ...(options.system && { system: options.system }),\n messages,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n });\n\n for await (const event of stream) {\n if (event.type === 'content_block_delta' && (event.delta as any).type === 'text_delta') {\n yield { text: (event.delta as any).text, done: false };\n } else if (event.type === 'message_stop') {\n yield { text: '', done: true };\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// AI API\n// ---------------------------------------------------------------------------\n\nexport const ai = {\n /**\n * Send a chat completion request. Auto-detects OpenAI or Anthropic from env vars.\n */\n async chat(options: ChatOptions): Promise<ChatResponse> {\n const provider = requireProvider();\n return provider === 'openai' ? openaiChat(options) : anthropicChat(options);\n },\n\n /**\n * Stream a chat completion. Returns an async iterable of text chunks.\n */\n streamChat(options: ChatOptions): AsyncGenerator<StreamChunk> {\n const provider = requireProvider();\n return provider === 'openai' ? openaiStreamChat(options) : anthropicStreamChat(options);\n },\n\n /**\n * Stream AI response directly to an Express response as SSE.\n * Handles headers, streaming, error handling, and cleanup.\n */\n async streamToSSE(res: any, options: ChatOptions): Promise<void> {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n });\n\n try {\n for await (const chunk of ai.streamChat(options)) {\n if (chunk.text) {\n res.write(`data: ${JSON.stringify({ text: chunk.text })}\\n\\n`);\n }\n if (chunk.done) {\n res.write(`data: ${JSON.stringify({ done: true })}\\n\\n`);\n }\n }\n } catch (error: any) {\n res.write(`data: ${JSON.stringify({ error: error.message })}\\n\\n`);\n } finally {\n res.end();\n }\n },\n\n /**\n * Generate an image (OpenAI DALL-E only).\n */\n async generateImage(options: { prompt: string; size?: string }): Promise<{ url: string }> {\n const provider = detectProvider();\n if (provider !== 'openai') {\n throw new Error('@coduckai/sdk/ai: generateImage() requires OpenAI. Set OPENAI_API_KEY.');\n }\n\n const { default: OpenAI } = await import('openai');\n const config = getConfig();\n const client = new OpenAI({ apiKey: config.ai?.apiKey || process.env.OPENAI_API_KEY });\n\n const response = await client.images.generate({\n model: 'dall-e-3',\n prompt: options.prompt,\n size: (options.size as any) || '1024x1024',\n n: 1,\n });\n\n return { url: response.data?.[0]?.url || '' };\n },\n\n /**\n * Returns the detected AI provider ('openai' | 'anthropic' | null).\n */\n getProvider(): AIProvider {\n return detectProvider();\n },\n};\n"]}
1
+ {"version":3,"sources":["../../src/internal/env.ts","../../src/internal/config.ts","../../src/ai/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Environment variable helpers\n */\n\nexport function getEnv(key: string, fallback?: string): string | undefined {\n return process.env[key] || fallback;\n}\n\nexport function requireEnv(key: string): string {\n const val = process.env[key];\n if (!val) {\n throw new Error(`@coduckai/sdk: Missing required environment variable: ${key}`);\n }\n return val;\n}\n\nexport function isServer(): boolean {\n return typeof window === 'undefined';\n}\n\nexport function assertServer(moduleName: string): void {\n if (!isServer()) {\n throw new Error(\n `@coduckai/sdk/${moduleName} is server-only. Use @coduckai/sdk/client for browser code.`\n );\n }\n}\n","/**\n * Global SDK configuration\n *\n * CoDuck apps use env vars automatically — no configure() needed.\n * Standalone usage can call configure() to override.\n */\n\nexport interface SDKConfig {\n auth?: {\n jwtSecret?: string;\n jwtExpiresIn?: string;\n bcryptRounds?: number;\n };\n db?: {\n connectionString?: string;\n };\n client?: {\n baseUrl?: string;\n tokenKey?: string;\n };\n payments?: {\n stripeSecretKey?: string;\n connectedAccountId?: string;\n webhookSecret?: string;\n };\n ai?: {\n provider?: 'openai' | 'anthropic';\n apiKey?: string;\n };\n}\n\nlet globalConfig: SDKConfig = {};\n\nexport function setConfig(config: SDKConfig): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\nexport function getConfig(): SDKConfig {\n return globalConfig;\n}\n","/**\n * @coduckai/sdk/ai — AI Providers (server-only)\n *\n * Unified interface for OpenAI and Anthropic. Auto-detects provider from env vars.\n * Set OPENAI_API_KEY for OpenAI or ANTHROPIC_API_KEY for Anthropic.\n */\n\nimport { assertServer } from '../internal/env';\nimport { getConfig } from '../internal/config';\n\nassertServer('ai');\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system';\n content: string;\n}\n\nexport interface ChatOptions {\n system?: string;\n messages: ChatMessage[];\n model?: string;\n maxTokens?: number;\n temperature?: number;\n}\n\nexport interface ChatResponse {\n text: string;\n usage: { inputTokens: number; outputTokens: number };\n}\n\nexport interface StreamChunk {\n text: string;\n done: boolean;\n}\n\nexport type AIProvider = 'openai' | 'anthropic' | null;\n\n// ---------------------------------------------------------------------------\n// Provider detection\n// ---------------------------------------------------------------------------\n\nfunction detectProvider(): AIProvider {\n const config = getConfig();\n if (config.ai?.provider) return config.ai.provider;\n if (process.env.OPENAI_API_KEY) return 'openai';\n if (process.env.ANTHROPIC_API_KEY) return 'anthropic';\n return null;\n}\n\nfunction requireProvider(): AIProvider & string {\n const provider = detectProvider();\n if (!provider) {\n throw new Error(\n '@coduckai/sdk/ai: No AI provider configured. Set OPENAI_API_KEY or ANTHROPIC_API_KEY.'\n );\n }\n return provider;\n}\n\n// ---------------------------------------------------------------------------\n// OpenAI\n// ---------------------------------------------------------------------------\n\nasync function openaiChat(options: ChatOptions): Promise<ChatResponse> {\n const { default: OpenAI } = await import('openai');\n const config = getConfig();\n const client = new OpenAI({ apiKey: config.ai?.apiKey || process.env.OPENAI_API_KEY });\n\n const messages: Array<{ role: string; content: string }> = [];\n if (options.system) {\n messages.push({ role: 'system', content: options.system });\n }\n messages.push(...options.messages);\n\n const response = await client.chat.completions.create({\n model: options.model || 'gpt-4o',\n messages: messages as any,\n max_tokens: options.maxTokens || 4096,\n temperature: options.temperature ?? 0.7,\n });\n\n return {\n text: response.choices[0]?.message?.content || '',\n usage: {\n inputTokens: response.usage?.prompt_tokens || 0,\n outputTokens: response.usage?.completion_tokens || 0,\n },\n };\n}\n\nasync function* openaiStreamChat(options: ChatOptions): AsyncGenerator<StreamChunk> {\n const { default: OpenAI } = await import('openai');\n const config = getConfig();\n const client = new OpenAI({ apiKey: config.ai?.apiKey || process.env.OPENAI_API_KEY });\n\n const messages: Array<{ role: string; content: string }> = [];\n if (options.system) {\n messages.push({ role: 'system', content: options.system });\n }\n messages.push(...options.messages);\n\n const stream = await client.chat.completions.create({\n model: options.model || 'gpt-4o',\n messages: messages as any,\n max_tokens: options.maxTokens || 4096,\n temperature: options.temperature ?? 0.7,\n stream: true,\n });\n\n for await (const chunk of stream) {\n const text = chunk.choices[0]?.delta?.content || '';\n const done = chunk.choices[0]?.finish_reason !== null && chunk.choices[0]?.finish_reason !== undefined;\n if (text) yield { text, done: false };\n if (done) yield { text: '', done: true };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Anthropic\n// ---------------------------------------------------------------------------\n\nasync function anthropicChat(options: ChatOptions): Promise<ChatResponse> {\n const { default: Anthropic } = await import('@anthropic-ai/sdk');\n const config = getConfig();\n const client = new Anthropic({ apiKey: config.ai?.apiKey || process.env.ANTHROPIC_API_KEY });\n\n const messages = options.messages\n .filter(m => m.role !== 'system')\n .map(m => ({ role: m.role as 'user' | 'assistant', content: m.content }));\n\n const response = await client.messages.create({\n model: options.model || 'claude-sonnet-4-6',\n max_tokens: options.maxTokens || 4096,\n ...(options.system && { system: options.system }),\n messages,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n });\n\n const textBlock = response.content.find((b: any) => b.type === 'text');\n\n return {\n text: (textBlock as any)?.text || '',\n usage: {\n inputTokens: response.usage?.input_tokens || 0,\n outputTokens: response.usage?.output_tokens || 0,\n },\n };\n}\n\nasync function* anthropicStreamChat(options: ChatOptions): AsyncGenerator<StreamChunk> {\n const { default: Anthropic } = await import('@anthropic-ai/sdk');\n const config = getConfig();\n const client = new Anthropic({ apiKey: config.ai?.apiKey || process.env.ANTHROPIC_API_KEY });\n\n const messages = options.messages\n .filter(m => m.role !== 'system')\n .map(m => ({ role: m.role as 'user' | 'assistant', content: m.content }));\n\n const stream = client.messages.stream({\n model: options.model || 'claude-sonnet-4-6',\n max_tokens: options.maxTokens || 4096,\n ...(options.system && { system: options.system }),\n messages,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n });\n\n for await (const event of stream) {\n if (event.type === 'content_block_delta' && (event.delta as any).type === 'text_delta') {\n yield { text: (event.delta as any).text, done: false };\n } else if (event.type === 'message_stop') {\n yield { text: '', done: true };\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// AI API\n// ---------------------------------------------------------------------------\n\nexport const ai = {\n /**\n * Send a chat completion request. Auto-detects OpenAI or Anthropic from env vars.\n */\n async chat(options: ChatOptions): Promise<ChatResponse> {\n const provider = requireProvider();\n return provider === 'openai' ? openaiChat(options) : anthropicChat(options);\n },\n\n /**\n * Stream a chat completion. Returns an async iterable of text chunks.\n */\n streamChat(options: ChatOptions): AsyncGenerator<StreamChunk> {\n const provider = requireProvider();\n return provider === 'openai' ? openaiStreamChat(options) : anthropicStreamChat(options);\n },\n\n /**\n * Stream AI response directly to an Express response as SSE.\n * Handles headers, streaming, error handling, and cleanup.\n */\n async streamToSSE(res: any, options: ChatOptions): Promise<void> {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n });\n\n try {\n for await (const chunk of ai.streamChat(options)) {\n if (chunk.text) {\n res.write(`data: ${JSON.stringify({ text: chunk.text })}\\n\\n`);\n }\n if (chunk.done) {\n res.write(`data: ${JSON.stringify({ done: true })}\\n\\n`);\n }\n }\n } catch (error: any) {\n res.write(`data: ${JSON.stringify({ error: error.message })}\\n\\n`);\n } finally {\n res.end();\n }\n },\n\n /**\n * Generate an image (OpenAI DALL-E only).\n */\n async generateImage(options: { prompt: string; size?: string }): Promise<{ url: string }> {\n const provider = detectProvider();\n if (provider !== 'openai') {\n throw new Error('@coduckai/sdk/ai: generateImage() requires OpenAI. Set OPENAI_API_KEY.');\n }\n\n const { default: OpenAI } = await import('openai');\n const config = getConfig();\n const client = new OpenAI({ apiKey: config.ai?.apiKey || process.env.OPENAI_API_KEY });\n\n const response = await client.images.generate({\n model: 'dall-e-3',\n prompt: options.prompt,\n size: (options.size as any) || '1024x1024',\n n: 1,\n });\n\n return { url: response.data?.[0]?.url || '' };\n },\n\n /**\n * Returns the detected AI provider ('openai' | 'anthropic' | null).\n */\n getProvider(): AIProvider {\n return detectProvider();\n },\n};\n"]}
package/dist/ai/index.js CHANGED
@@ -86,7 +86,7 @@ async function anthropicChat(options) {
86
86
  const client = new Anthropic({ apiKey: config.ai?.apiKey || process.env.ANTHROPIC_API_KEY });
87
87
  const messages = options.messages.filter((m) => m.role !== "system").map((m) => ({ role: m.role, content: m.content }));
88
88
  const response = await client.messages.create({
89
- model: options.model || "claude-sonnet-4-5-20250929",
89
+ model: options.model || "claude-sonnet-4-6",
90
90
  max_tokens: options.maxTokens || 4096,
91
91
  ...options.system && { system: options.system },
92
92
  messages,
@@ -107,7 +107,7 @@ async function* anthropicStreamChat(options) {
107
107
  const client = new Anthropic({ apiKey: config.ai?.apiKey || process.env.ANTHROPIC_API_KEY });
108
108
  const messages = options.messages.filter((m) => m.role !== "system").map((m) => ({ role: m.role, content: m.content }));
109
109
  const stream = client.messages.stream({
110
- model: options.model || "claude-sonnet-4-5-20250929",
110
+ model: options.model || "claude-sonnet-4-6",
111
111
  max_tokens: options.maxTokens || 4096,
112
112
  ...options.system && { system: options.system },
113
113
  messages,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/internal/env.ts","../../src/internal/config.ts","../../src/ai/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Environment variable helpers\n */\n\nexport function getEnv(key: string, fallback?: string): string | undefined {\n return process.env[key] || fallback;\n}\n\nexport function requireEnv(key: string): string {\n const val = process.env[key];\n if (!val) {\n throw new Error(`@coduckai/sdk: Missing required environment variable: ${key}`);\n }\n return val;\n}\n\nexport function isServer(): boolean {\n return typeof window === 'undefined';\n}\n\nexport function assertServer(moduleName: string): void {\n if (!isServer()) {\n throw new Error(\n `@coduckai/sdk/${moduleName} is server-only. Use @coduckai/sdk/client for browser code.`\n );\n }\n}\n","/**\n * Global SDK configuration\n *\n * CoDuck apps use env vars automatically — no configure() needed.\n * Standalone usage can call configure() to override.\n */\n\nexport interface SDKConfig {\n auth?: {\n jwtSecret?: string;\n jwtExpiresIn?: string;\n bcryptRounds?: number;\n };\n db?: {\n connectionString?: string;\n };\n client?: {\n baseUrl?: string;\n tokenKey?: string;\n };\n payments?: {\n stripeSecretKey?: string;\n connectedAccountId?: string;\n webhookSecret?: string;\n };\n ai?: {\n provider?: 'openai' | 'anthropic';\n apiKey?: string;\n };\n}\n\nlet globalConfig: SDKConfig = {};\n\nexport function setConfig(config: SDKConfig): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\nexport function getConfig(): SDKConfig {\n return globalConfig;\n}\n","/**\n * @coduckai/sdk/ai — AI Providers (server-only)\n *\n * Unified interface for OpenAI and Anthropic. Auto-detects provider from env vars.\n * Set OPENAI_API_KEY for OpenAI or ANTHROPIC_API_KEY for Anthropic.\n */\n\nimport { assertServer } from '../internal/env';\nimport { getConfig } from '../internal/config';\n\nassertServer('ai');\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system';\n content: string;\n}\n\nexport interface ChatOptions {\n system?: string;\n messages: ChatMessage[];\n model?: string;\n maxTokens?: number;\n temperature?: number;\n}\n\nexport interface ChatResponse {\n text: string;\n usage: { inputTokens: number; outputTokens: number };\n}\n\nexport interface StreamChunk {\n text: string;\n done: boolean;\n}\n\nexport type AIProvider = 'openai' | 'anthropic' | null;\n\n// ---------------------------------------------------------------------------\n// Provider detection\n// ---------------------------------------------------------------------------\n\nfunction detectProvider(): AIProvider {\n const config = getConfig();\n if (config.ai?.provider) return config.ai.provider;\n if (process.env.OPENAI_API_KEY) return 'openai';\n if (process.env.ANTHROPIC_API_KEY) return 'anthropic';\n return null;\n}\n\nfunction requireProvider(): AIProvider & string {\n const provider = detectProvider();\n if (!provider) {\n throw new Error(\n '@coduckai/sdk/ai: No AI provider configured. Set OPENAI_API_KEY or ANTHROPIC_API_KEY.'\n );\n }\n return provider;\n}\n\n// ---------------------------------------------------------------------------\n// OpenAI\n// ---------------------------------------------------------------------------\n\nasync function openaiChat(options: ChatOptions): Promise<ChatResponse> {\n const { default: OpenAI } = await import('openai');\n const config = getConfig();\n const client = new OpenAI({ apiKey: config.ai?.apiKey || process.env.OPENAI_API_KEY });\n\n const messages: Array<{ role: string; content: string }> = [];\n if (options.system) {\n messages.push({ role: 'system', content: options.system });\n }\n messages.push(...options.messages);\n\n const response = await client.chat.completions.create({\n model: options.model || 'gpt-4o',\n messages: messages as any,\n max_tokens: options.maxTokens || 4096,\n temperature: options.temperature ?? 0.7,\n });\n\n return {\n text: response.choices[0]?.message?.content || '',\n usage: {\n inputTokens: response.usage?.prompt_tokens || 0,\n outputTokens: response.usage?.completion_tokens || 0,\n },\n };\n}\n\nasync function* openaiStreamChat(options: ChatOptions): AsyncGenerator<StreamChunk> {\n const { default: OpenAI } = await import('openai');\n const config = getConfig();\n const client = new OpenAI({ apiKey: config.ai?.apiKey || process.env.OPENAI_API_KEY });\n\n const messages: Array<{ role: string; content: string }> = [];\n if (options.system) {\n messages.push({ role: 'system', content: options.system });\n }\n messages.push(...options.messages);\n\n const stream = await client.chat.completions.create({\n model: options.model || 'gpt-4o',\n messages: messages as any,\n max_tokens: options.maxTokens || 4096,\n temperature: options.temperature ?? 0.7,\n stream: true,\n });\n\n for await (const chunk of stream) {\n const text = chunk.choices[0]?.delta?.content || '';\n const done = chunk.choices[0]?.finish_reason !== null && chunk.choices[0]?.finish_reason !== undefined;\n if (text) yield { text, done: false };\n if (done) yield { text: '', done: true };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Anthropic\n// ---------------------------------------------------------------------------\n\nasync function anthropicChat(options: ChatOptions): Promise<ChatResponse> {\n const { default: Anthropic } = await import('@anthropic-ai/sdk');\n const config = getConfig();\n const client = new Anthropic({ apiKey: config.ai?.apiKey || process.env.ANTHROPIC_API_KEY });\n\n const messages = options.messages\n .filter(m => m.role !== 'system')\n .map(m => ({ role: m.role as 'user' | 'assistant', content: m.content }));\n\n const response = await client.messages.create({\n model: options.model || 'claude-sonnet-4-5-20250929',\n max_tokens: options.maxTokens || 4096,\n ...(options.system && { system: options.system }),\n messages,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n });\n\n const textBlock = response.content.find((b: any) => b.type === 'text');\n\n return {\n text: (textBlock as any)?.text || '',\n usage: {\n inputTokens: response.usage?.input_tokens || 0,\n outputTokens: response.usage?.output_tokens || 0,\n },\n };\n}\n\nasync function* anthropicStreamChat(options: ChatOptions): AsyncGenerator<StreamChunk> {\n const { default: Anthropic } = await import('@anthropic-ai/sdk');\n const config = getConfig();\n const client = new Anthropic({ apiKey: config.ai?.apiKey || process.env.ANTHROPIC_API_KEY });\n\n const messages = options.messages\n .filter(m => m.role !== 'system')\n .map(m => ({ role: m.role as 'user' | 'assistant', content: m.content }));\n\n const stream = client.messages.stream({\n model: options.model || 'claude-sonnet-4-5-20250929',\n max_tokens: options.maxTokens || 4096,\n ...(options.system && { system: options.system }),\n messages,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n });\n\n for await (const event of stream) {\n if (event.type === 'content_block_delta' && (event.delta as any).type === 'text_delta') {\n yield { text: (event.delta as any).text, done: false };\n } else if (event.type === 'message_stop') {\n yield { text: '', done: true };\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// AI API\n// ---------------------------------------------------------------------------\n\nexport const ai = {\n /**\n * Send a chat completion request. Auto-detects OpenAI or Anthropic from env vars.\n */\n async chat(options: ChatOptions): Promise<ChatResponse> {\n const provider = requireProvider();\n return provider === 'openai' ? openaiChat(options) : anthropicChat(options);\n },\n\n /**\n * Stream a chat completion. Returns an async iterable of text chunks.\n */\n streamChat(options: ChatOptions): AsyncGenerator<StreamChunk> {\n const provider = requireProvider();\n return provider === 'openai' ? openaiStreamChat(options) : anthropicStreamChat(options);\n },\n\n /**\n * Stream AI response directly to an Express response as SSE.\n * Handles headers, streaming, error handling, and cleanup.\n */\n async streamToSSE(res: any, options: ChatOptions): Promise<void> {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n });\n\n try {\n for await (const chunk of ai.streamChat(options)) {\n if (chunk.text) {\n res.write(`data: ${JSON.stringify({ text: chunk.text })}\\n\\n`);\n }\n if (chunk.done) {\n res.write(`data: ${JSON.stringify({ done: true })}\\n\\n`);\n }\n }\n } catch (error: any) {\n res.write(`data: ${JSON.stringify({ error: error.message })}\\n\\n`);\n } finally {\n res.end();\n }\n },\n\n /**\n * Generate an image (OpenAI DALL-E only).\n */\n async generateImage(options: { prompt: string; size?: string }): Promise<{ url: string }> {\n const provider = detectProvider();\n if (provider !== 'openai') {\n throw new Error('@coduckai/sdk/ai: generateImage() requires OpenAI. Set OPENAI_API_KEY.');\n }\n\n const { default: OpenAI } = await import('openai');\n const config = getConfig();\n const client = new OpenAI({ apiKey: config.ai?.apiKey || process.env.OPENAI_API_KEY });\n\n const response = await client.images.generate({\n model: 'dall-e-3',\n prompt: options.prompt,\n size: (options.size as any) || '1024x1024',\n n: 1,\n });\n\n return { url: response.data?.[0]?.url || '' };\n },\n\n /**\n * Returns the detected AI provider ('openai' | 'anthropic' | null).\n */\n getProvider(): AIProvider {\n return detectProvider();\n },\n};\n"]}
1
+ {"version":3,"sources":["../../src/internal/env.ts","../../src/internal/config.ts","../../src/ai/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Environment variable helpers\n */\n\nexport function getEnv(key: string, fallback?: string): string | undefined {\n return process.env[key] || fallback;\n}\n\nexport function requireEnv(key: string): string {\n const val = process.env[key];\n if (!val) {\n throw new Error(`@coduckai/sdk: Missing required environment variable: ${key}`);\n }\n return val;\n}\n\nexport function isServer(): boolean {\n return typeof window === 'undefined';\n}\n\nexport function assertServer(moduleName: string): void {\n if (!isServer()) {\n throw new Error(\n `@coduckai/sdk/${moduleName} is server-only. Use @coduckai/sdk/client for browser code.`\n );\n }\n}\n","/**\n * Global SDK configuration\n *\n * CoDuck apps use env vars automatically — no configure() needed.\n * Standalone usage can call configure() to override.\n */\n\nexport interface SDKConfig {\n auth?: {\n jwtSecret?: string;\n jwtExpiresIn?: string;\n bcryptRounds?: number;\n };\n db?: {\n connectionString?: string;\n };\n client?: {\n baseUrl?: string;\n tokenKey?: string;\n };\n payments?: {\n stripeSecretKey?: string;\n connectedAccountId?: string;\n webhookSecret?: string;\n };\n ai?: {\n provider?: 'openai' | 'anthropic';\n apiKey?: string;\n };\n}\n\nlet globalConfig: SDKConfig = {};\n\nexport function setConfig(config: SDKConfig): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\nexport function getConfig(): SDKConfig {\n return globalConfig;\n}\n","/**\n * @coduckai/sdk/ai — AI Providers (server-only)\n *\n * Unified interface for OpenAI and Anthropic. Auto-detects provider from env vars.\n * Set OPENAI_API_KEY for OpenAI or ANTHROPIC_API_KEY for Anthropic.\n */\n\nimport { assertServer } from '../internal/env';\nimport { getConfig } from '../internal/config';\n\nassertServer('ai');\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system';\n content: string;\n}\n\nexport interface ChatOptions {\n system?: string;\n messages: ChatMessage[];\n model?: string;\n maxTokens?: number;\n temperature?: number;\n}\n\nexport interface ChatResponse {\n text: string;\n usage: { inputTokens: number; outputTokens: number };\n}\n\nexport interface StreamChunk {\n text: string;\n done: boolean;\n}\n\nexport type AIProvider = 'openai' | 'anthropic' | null;\n\n// ---------------------------------------------------------------------------\n// Provider detection\n// ---------------------------------------------------------------------------\n\nfunction detectProvider(): AIProvider {\n const config = getConfig();\n if (config.ai?.provider) return config.ai.provider;\n if (process.env.OPENAI_API_KEY) return 'openai';\n if (process.env.ANTHROPIC_API_KEY) return 'anthropic';\n return null;\n}\n\nfunction requireProvider(): AIProvider & string {\n const provider = detectProvider();\n if (!provider) {\n throw new Error(\n '@coduckai/sdk/ai: No AI provider configured. Set OPENAI_API_KEY or ANTHROPIC_API_KEY.'\n );\n }\n return provider;\n}\n\n// ---------------------------------------------------------------------------\n// OpenAI\n// ---------------------------------------------------------------------------\n\nasync function openaiChat(options: ChatOptions): Promise<ChatResponse> {\n const { default: OpenAI } = await import('openai');\n const config = getConfig();\n const client = new OpenAI({ apiKey: config.ai?.apiKey || process.env.OPENAI_API_KEY });\n\n const messages: Array<{ role: string; content: string }> = [];\n if (options.system) {\n messages.push({ role: 'system', content: options.system });\n }\n messages.push(...options.messages);\n\n const response = await client.chat.completions.create({\n model: options.model || 'gpt-4o',\n messages: messages as any,\n max_tokens: options.maxTokens || 4096,\n temperature: options.temperature ?? 0.7,\n });\n\n return {\n text: response.choices[0]?.message?.content || '',\n usage: {\n inputTokens: response.usage?.prompt_tokens || 0,\n outputTokens: response.usage?.completion_tokens || 0,\n },\n };\n}\n\nasync function* openaiStreamChat(options: ChatOptions): AsyncGenerator<StreamChunk> {\n const { default: OpenAI } = await import('openai');\n const config = getConfig();\n const client = new OpenAI({ apiKey: config.ai?.apiKey || process.env.OPENAI_API_KEY });\n\n const messages: Array<{ role: string; content: string }> = [];\n if (options.system) {\n messages.push({ role: 'system', content: options.system });\n }\n messages.push(...options.messages);\n\n const stream = await client.chat.completions.create({\n model: options.model || 'gpt-4o',\n messages: messages as any,\n max_tokens: options.maxTokens || 4096,\n temperature: options.temperature ?? 0.7,\n stream: true,\n });\n\n for await (const chunk of stream) {\n const text = chunk.choices[0]?.delta?.content || '';\n const done = chunk.choices[0]?.finish_reason !== null && chunk.choices[0]?.finish_reason !== undefined;\n if (text) yield { text, done: false };\n if (done) yield { text: '', done: true };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Anthropic\n// ---------------------------------------------------------------------------\n\nasync function anthropicChat(options: ChatOptions): Promise<ChatResponse> {\n const { default: Anthropic } = await import('@anthropic-ai/sdk');\n const config = getConfig();\n const client = new Anthropic({ apiKey: config.ai?.apiKey || process.env.ANTHROPIC_API_KEY });\n\n const messages = options.messages\n .filter(m => m.role !== 'system')\n .map(m => ({ role: m.role as 'user' | 'assistant', content: m.content }));\n\n const response = await client.messages.create({\n model: options.model || 'claude-sonnet-4-6',\n max_tokens: options.maxTokens || 4096,\n ...(options.system && { system: options.system }),\n messages,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n });\n\n const textBlock = response.content.find((b: any) => b.type === 'text');\n\n return {\n text: (textBlock as any)?.text || '',\n usage: {\n inputTokens: response.usage?.input_tokens || 0,\n outputTokens: response.usage?.output_tokens || 0,\n },\n };\n}\n\nasync function* anthropicStreamChat(options: ChatOptions): AsyncGenerator<StreamChunk> {\n const { default: Anthropic } = await import('@anthropic-ai/sdk');\n const config = getConfig();\n const client = new Anthropic({ apiKey: config.ai?.apiKey || process.env.ANTHROPIC_API_KEY });\n\n const messages = options.messages\n .filter(m => m.role !== 'system')\n .map(m => ({ role: m.role as 'user' | 'assistant', content: m.content }));\n\n const stream = client.messages.stream({\n model: options.model || 'claude-sonnet-4-6',\n max_tokens: options.maxTokens || 4096,\n ...(options.system && { system: options.system }),\n messages,\n ...(options.temperature !== undefined && { temperature: options.temperature }),\n });\n\n for await (const event of stream) {\n if (event.type === 'content_block_delta' && (event.delta as any).type === 'text_delta') {\n yield { text: (event.delta as any).text, done: false };\n } else if (event.type === 'message_stop') {\n yield { text: '', done: true };\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// AI API\n// ---------------------------------------------------------------------------\n\nexport const ai = {\n /**\n * Send a chat completion request. Auto-detects OpenAI or Anthropic from env vars.\n */\n async chat(options: ChatOptions): Promise<ChatResponse> {\n const provider = requireProvider();\n return provider === 'openai' ? openaiChat(options) : anthropicChat(options);\n },\n\n /**\n * Stream a chat completion. Returns an async iterable of text chunks.\n */\n streamChat(options: ChatOptions): AsyncGenerator<StreamChunk> {\n const provider = requireProvider();\n return provider === 'openai' ? openaiStreamChat(options) : anthropicStreamChat(options);\n },\n\n /**\n * Stream AI response directly to an Express response as SSE.\n * Handles headers, streaming, error handling, and cleanup.\n */\n async streamToSSE(res: any, options: ChatOptions): Promise<void> {\n res.writeHead(200, {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n });\n\n try {\n for await (const chunk of ai.streamChat(options)) {\n if (chunk.text) {\n res.write(`data: ${JSON.stringify({ text: chunk.text })}\\n\\n`);\n }\n if (chunk.done) {\n res.write(`data: ${JSON.stringify({ done: true })}\\n\\n`);\n }\n }\n } catch (error: any) {\n res.write(`data: ${JSON.stringify({ error: error.message })}\\n\\n`);\n } finally {\n res.end();\n }\n },\n\n /**\n * Generate an image (OpenAI DALL-E only).\n */\n async generateImage(options: { prompt: string; size?: string }): Promise<{ url: string }> {\n const provider = detectProvider();\n if (provider !== 'openai') {\n throw new Error('@coduckai/sdk/ai: generateImage() requires OpenAI. Set OPENAI_API_KEY.');\n }\n\n const { default: OpenAI } = await import('openai');\n const config = getConfig();\n const client = new OpenAI({ apiKey: config.ai?.apiKey || process.env.OPENAI_API_KEY });\n\n const response = await client.images.generate({\n model: 'dall-e-3',\n prompt: options.prompt,\n size: (options.size as any) || '1024x1024',\n n: 1,\n });\n\n return { url: response.data?.[0]?.url || '' };\n },\n\n /**\n * Returns the detected AI provider ('openai' | 'anthropic' | null).\n */\n getProvider(): AIProvider {\n return detectProvider();\n },\n};\n"]}
@@ -48,7 +48,7 @@ api.interceptors.request.use((config) => {
48
48
  return config;
49
49
  });
50
50
  api.interceptors.response.use(
51
- (response) => response,
51
+ (response) => response.data,
52
52
  (error) => {
53
53
  if (error.response?.status === 401) {
54
54
  clearToken();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/index.ts"],"names":["axios"],"mappings":";;;;;;;;;;;;AAeA,IAAM,SAAA,GAAY,OAAA;AAEX,SAAS,SAAS,KAAA,EAAqB;AAC5C,EAAA,IAAI,OAAO,iBAAiB,WAAA,EAAa;AACvC,IAAA,YAAA,CAAa,OAAA,CAAQ,WAAW,KAAK,CAAA;AAAA,EACvC;AACF;AAEO,SAAS,QAAA,GAA0B;AACxC,EAAA,IAAI,OAAO,iBAAiB,WAAA,EAAa;AACvC,IAAA,OAAO,YAAA,CAAa,QAAQ,SAAS,CAAA;AAAA,EACvC;AACA,EAAA,OAAO,IAAA;AACT;AAEO,SAAS,UAAA,GAAmB;AACjC,EAAA,IAAI,OAAO,iBAAiB,WAAA,EAAa;AACvC,IAAA,YAAA,CAAa,WAAW,SAAS,CAAA;AAAA,EACnC;AACF;AAEO,SAAS,eAAA,GAA2B;AACzC,EAAA,OAAO,CAAC,CAAC,QAAA,EAAS;AACpB;AAIA,SAAS,UAAA,GAAqB;AAE5B,EAAA,IAAI,OAAO,sQAAA,KAAgB,WAAA,IAAgB,WAAyB,YAAA,EAAc;AAChF,IAAA,OAAQ,SAAoB,CAAI,YAAA;AAAA,EAClC;AAEA,EAAA,OAAO,MAAA;AACT;AAEA,IAAM,GAAA,GAAqBA,uBAAM,MAAA,CAAO;AAAA,EACtC,SAAS,UAAA,EAAW;AAAA,EACpB,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA;AAC7B,CAAC,CAAA;AAGD,GAAA,CAAI,YAAA,CAAa,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,KAAW;AACvC,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,MAAA,CAAO,OAAA,CAAQ,aAAA,GAAgB,CAAA,OAAA,EAAU,KAAK,CAAA,CAAA;AAAA,EAChD;AACA,EAAA,OAAO,MAAA;AACT,CAAC,CAAA;AAGD,GAAA,CAAI,aAAa,QAAA,CAAS,GAAA;AAAA,EACxB,CAAC,QAAA,KAAa,QAAA;AAAA,EACd,CAAC,KAAA,KAAU;AACT,IAAA,IAAI,KAAA,CAAM,QAAA,EAAU,MAAA,KAAW,GAAA,EAAK;AAClC,MAAA,UAAA,EAAW;AACX,MAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,QAAA,MAAA,CAAO,SAAS,IAAA,GAAO,QAAA;AAAA,MACzB;AAAA,IACF;AACA,IAAA,OAAO,OAAA,CAAQ,OAAO,KAAK,CAAA;AAAA,EAC7B;AACF,CAAA;AAEA,IAAO,cAAA,GAAQ","file":"index.cjs","sourcesContent":["/**\n * @coduckai/sdk/client — Frontend API Client (browser-only)\n *\n * Pre-configured Axios instance with auth interceptor.\n *\n * Usage:\n * import api, { setToken, clearToken } from '@coduckai/sdk/client';\n * const { data } = await api.get('/recipes');\n * setToken(loginData.token);\n */\n\nimport axios, { type AxiosInstance } from 'axios';\n\n// ── Token Management ───────────────────────────────────\n\nconst TOKEN_KEY = 'token';\n\nexport function setToken(token: string): void {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(TOKEN_KEY, token);\n }\n}\n\nexport function getToken(): string | null {\n if (typeof localStorage !== 'undefined') {\n return localStorage.getItem(TOKEN_KEY);\n }\n return null;\n}\n\nexport function clearToken(): void {\n if (typeof localStorage !== 'undefined') {\n localStorage.removeItem(TOKEN_KEY);\n }\n}\n\nexport function isAuthenticated(): boolean {\n return !!getToken();\n}\n\n// ── Axios Instance ─────────────────────────────────────\n\nfunction getBaseUrl(): string {\n // Vite: VITE_API_URL\n if (typeof import.meta !== 'undefined' && (import.meta as any).env?.VITE_API_URL) {\n return (import.meta as any).env.VITE_API_URL;\n }\n // Fallback: relative /api\n return '/api';\n}\n\nconst api: AxiosInstance = axios.create({\n baseURL: getBaseUrl(),\n headers: { 'Content-Type': 'application/json' },\n});\n\n// Request interceptor — attach Bearer token\napi.interceptors.request.use((config) => {\n const token = getToken();\n if (token) {\n config.headers.Authorization = `Bearer ${token}`;\n }\n return config;\n});\n\n// Response interceptor — handle 401\napi.interceptors.response.use(\n (response) => response,\n (error) => {\n if (error.response?.status === 401) {\n clearToken();\n if (typeof window !== 'undefined') {\n window.location.href = '/login';\n }\n }\n return Promise.reject(error);\n }\n);\n\nexport default api;\n"]}
1
+ {"version":3,"sources":["../../src/client/index.ts"],"names":["axios"],"mappings":";;;;;;;;;;;;AAeA,IAAM,SAAA,GAAY,OAAA;AAEX,SAAS,SAAS,KAAA,EAAqB;AAC5C,EAAA,IAAI,OAAO,iBAAiB,WAAA,EAAa;AACvC,IAAA,YAAA,CAAa,OAAA,CAAQ,WAAW,KAAK,CAAA;AAAA,EACvC;AACF;AAEO,SAAS,QAAA,GAA0B;AACxC,EAAA,IAAI,OAAO,iBAAiB,WAAA,EAAa;AACvC,IAAA,OAAO,YAAA,CAAa,QAAQ,SAAS,CAAA;AAAA,EACvC;AACA,EAAA,OAAO,IAAA;AACT;AAEO,SAAS,UAAA,GAAmB;AACjC,EAAA,IAAI,OAAO,iBAAiB,WAAA,EAAa;AACvC,IAAA,YAAA,CAAa,WAAW,SAAS,CAAA;AAAA,EACnC;AACF;AAEO,SAAS,eAAA,GAA2B;AACzC,EAAA,OAAO,CAAC,CAAC,QAAA,EAAS;AACpB;AAIA,SAAS,UAAA,GAAqB;AAE5B,EAAA,IAAI,OAAO,sQAAA,KAAgB,WAAA,IAAgB,WAAyB,YAAA,EAAc;AAChF,IAAA,OAAQ,SAAoB,CAAI,YAAA;AAAA,EAClC;AAEA,EAAA,OAAO,MAAA;AACT;AAEA,IAAM,GAAA,GAAqBA,uBAAM,MAAA,CAAO;AAAA,EACtC,SAAS,UAAA,EAAW;AAAA,EACpB,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA;AAC7B,CAAC,CAAA;AAGD,GAAA,CAAI,YAAA,CAAa,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,KAAW;AACvC,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,MAAA,CAAO,OAAA,CAAQ,aAAA,GAAgB,CAAA,OAAA,EAAU,KAAK,CAAA,CAAA;AAAA,EAChD;AACA,EAAA,OAAO,MAAA;AACT,CAAC,CAAA;AAGD,GAAA,CAAI,aAAa,QAAA,CAAS,GAAA;AAAA,EACxB,CAAC,aAAa,QAAA,CAAS,IAAA;AAAA,EACvB,CAAC,KAAA,KAAU;AACT,IAAA,IAAI,KAAA,CAAM,QAAA,EAAU,MAAA,KAAW,GAAA,EAAK;AAClC,MAAA,UAAA,EAAW;AACX,MAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,QAAA,MAAA,CAAO,SAAS,IAAA,GAAO,QAAA;AAAA,MACzB;AAAA,IACF;AACA,IAAA,OAAO,OAAA,CAAQ,OAAO,KAAK,CAAA;AAAA,EAC7B;AACF,CAAA;AAEA,IAAO,cAAA,GAAQ","file":"index.cjs","sourcesContent":["/**\n * @coduckai/sdk/client — Frontend API Client (browser-only)\n *\n * Pre-configured Axios instance with auth interceptor.\n *\n * Usage:\n * import api, { setToken, clearToken } from '@coduckai/sdk/client';\n * const recipes = await api.get('/recipes'); // returns response.data directly\n * setToken(loginData.token);\n */\n\nimport axios, { type AxiosInstance } from 'axios';\n\n// ── Token Management ───────────────────────────────────\n\nconst TOKEN_KEY = 'token';\n\nexport function setToken(token: string): void {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(TOKEN_KEY, token);\n }\n}\n\nexport function getToken(): string | null {\n if (typeof localStorage !== 'undefined') {\n return localStorage.getItem(TOKEN_KEY);\n }\n return null;\n}\n\nexport function clearToken(): void {\n if (typeof localStorage !== 'undefined') {\n localStorage.removeItem(TOKEN_KEY);\n }\n}\n\nexport function isAuthenticated(): boolean {\n return !!getToken();\n}\n\n// ── Axios Instance ─────────────────────────────────────\n\nfunction getBaseUrl(): string {\n // Vite: VITE_API_URL\n if (typeof import.meta !== 'undefined' && (import.meta as any).env?.VITE_API_URL) {\n return (import.meta as any).env.VITE_API_URL;\n }\n // Fallback: relative /api\n return '/api';\n}\n\nconst api: AxiosInstance = axios.create({\n baseURL: getBaseUrl(),\n headers: { 'Content-Type': 'application/json' },\n});\n\n// Request interceptor — attach Bearer token\napi.interceptors.request.use((config) => {\n const token = getToken();\n if (token) {\n config.headers.Authorization = `Bearer ${token}`;\n }\n return config;\n});\n\n// Response interceptor — auto-extract .data and handle 401\napi.interceptors.response.use(\n (response) => response.data,\n (error) => {\n if (error.response?.status === 401) {\n clearToken();\n if (typeof window !== 'undefined') {\n window.location.href = '/login';\n }\n }\n return Promise.reject(error);\n }\n);\n\nexport default api;\n"]}
@@ -7,7 +7,7 @@ import { AxiosInstance } from 'axios';
7
7
  *
8
8
  * Usage:
9
9
  * import api, { setToken, clearToken } from '@coduckai/sdk/client';
10
- * const { data } = await api.get('/recipes');
10
+ * const recipes = await api.get('/recipes'); // returns response.data directly
11
11
  * setToken(loginData.token);
12
12
  */
13
13
 
@@ -7,7 +7,7 @@ import { AxiosInstance } from 'axios';
7
7
  *
8
8
  * Usage:
9
9
  * import api, { setToken, clearToken } from '@coduckai/sdk/client';
10
- * const { data } = await api.get('/recipes');
10
+ * const recipes = await api.get('/recipes'); // returns response.data directly
11
11
  * setToken(loginData.token);
12
12
  */
13
13
 
@@ -39,7 +39,7 @@ api.interceptors.request.use((config) => {
39
39
  return config;
40
40
  });
41
41
  api.interceptors.response.use(
42
- (response) => response,
42
+ (response) => response.data,
43
43
  (error) => {
44
44
  if (error.response?.status === 401) {
45
45
  clearToken();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/client/index.ts"],"names":[],"mappings":";;;AAeA,IAAM,SAAA,GAAY,OAAA;AAEX,SAAS,SAAS,KAAA,EAAqB;AAC5C,EAAA,IAAI,OAAO,iBAAiB,WAAA,EAAa;AACvC,IAAA,YAAA,CAAa,OAAA,CAAQ,WAAW,KAAK,CAAA;AAAA,EACvC;AACF;AAEO,SAAS,QAAA,GAA0B;AACxC,EAAA,IAAI,OAAO,iBAAiB,WAAA,EAAa;AACvC,IAAA,OAAO,YAAA,CAAa,QAAQ,SAAS,CAAA;AAAA,EACvC;AACA,EAAA,OAAO,IAAA;AACT;AAEO,SAAS,UAAA,GAAmB;AACjC,EAAA,IAAI,OAAO,iBAAiB,WAAA,EAAa;AACvC,IAAA,YAAA,CAAa,WAAW,SAAS,CAAA;AAAA,EACnC;AACF;AAEO,SAAS,eAAA,GAA2B;AACzC,EAAA,OAAO,CAAC,CAAC,QAAA,EAAS;AACpB;AAIA,SAAS,UAAA,GAAqB;AAE5B,EAAA,IAAI,OAAO,MAAA,CAAA,IAAA,KAAgB,WAAA,IAAgB,MAAA,CAAA,IAAA,CAAoB,KAAK,YAAA,EAAc;AAChF,IAAA,OAAQ,YAAoB,GAAA,CAAI,YAAA;AAAA,EAClC;AAEA,EAAA,OAAO,MAAA;AACT;AAEA,IAAM,GAAA,GAAqB,MAAM,MAAA,CAAO;AAAA,EACtC,SAAS,UAAA,EAAW;AAAA,EACpB,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA;AAC7B,CAAC,CAAA;AAGD,GAAA,CAAI,YAAA,CAAa,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,KAAW;AACvC,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,MAAA,CAAO,OAAA,CAAQ,aAAA,GAAgB,CAAA,OAAA,EAAU,KAAK,CAAA,CAAA;AAAA,EAChD;AACA,EAAA,OAAO,MAAA;AACT,CAAC,CAAA;AAGD,GAAA,CAAI,aAAa,QAAA,CAAS,GAAA;AAAA,EACxB,CAAC,QAAA,KAAa,QAAA;AAAA,EACd,CAAC,KAAA,KAAU;AACT,IAAA,IAAI,KAAA,CAAM,QAAA,EAAU,MAAA,KAAW,GAAA,EAAK;AAClC,MAAA,UAAA,EAAW;AACX,MAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,QAAA,MAAA,CAAO,SAAS,IAAA,GAAO,QAAA;AAAA,MACzB;AAAA,IACF;AACA,IAAA,OAAO,OAAA,CAAQ,OAAO,KAAK,CAAA;AAAA,EAC7B;AACF,CAAA;AAEA,IAAO,cAAA,GAAQ","file":"index.js","sourcesContent":["/**\n * @coduckai/sdk/client — Frontend API Client (browser-only)\n *\n * Pre-configured Axios instance with auth interceptor.\n *\n * Usage:\n * import api, { setToken, clearToken } from '@coduckai/sdk/client';\n * const { data } = await api.get('/recipes');\n * setToken(loginData.token);\n */\n\nimport axios, { type AxiosInstance } from 'axios';\n\n// ── Token Management ───────────────────────────────────\n\nconst TOKEN_KEY = 'token';\n\nexport function setToken(token: string): void {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(TOKEN_KEY, token);\n }\n}\n\nexport function getToken(): string | null {\n if (typeof localStorage !== 'undefined') {\n return localStorage.getItem(TOKEN_KEY);\n }\n return null;\n}\n\nexport function clearToken(): void {\n if (typeof localStorage !== 'undefined') {\n localStorage.removeItem(TOKEN_KEY);\n }\n}\n\nexport function isAuthenticated(): boolean {\n return !!getToken();\n}\n\n// ── Axios Instance ─────────────────────────────────────\n\nfunction getBaseUrl(): string {\n // Vite: VITE_API_URL\n if (typeof import.meta !== 'undefined' && (import.meta as any).env?.VITE_API_URL) {\n return (import.meta as any).env.VITE_API_URL;\n }\n // Fallback: relative /api\n return '/api';\n}\n\nconst api: AxiosInstance = axios.create({\n baseURL: getBaseUrl(),\n headers: { 'Content-Type': 'application/json' },\n});\n\n// Request interceptor — attach Bearer token\napi.interceptors.request.use((config) => {\n const token = getToken();\n if (token) {\n config.headers.Authorization = `Bearer ${token}`;\n }\n return config;\n});\n\n// Response interceptor — handle 401\napi.interceptors.response.use(\n (response) => response,\n (error) => {\n if (error.response?.status === 401) {\n clearToken();\n if (typeof window !== 'undefined') {\n window.location.href = '/login';\n }\n }\n return Promise.reject(error);\n }\n);\n\nexport default api;\n"]}
1
+ {"version":3,"sources":["../../src/client/index.ts"],"names":[],"mappings":";;;AAeA,IAAM,SAAA,GAAY,OAAA;AAEX,SAAS,SAAS,KAAA,EAAqB;AAC5C,EAAA,IAAI,OAAO,iBAAiB,WAAA,EAAa;AACvC,IAAA,YAAA,CAAa,OAAA,CAAQ,WAAW,KAAK,CAAA;AAAA,EACvC;AACF;AAEO,SAAS,QAAA,GAA0B;AACxC,EAAA,IAAI,OAAO,iBAAiB,WAAA,EAAa;AACvC,IAAA,OAAO,YAAA,CAAa,QAAQ,SAAS,CAAA;AAAA,EACvC;AACA,EAAA,OAAO,IAAA;AACT;AAEO,SAAS,UAAA,GAAmB;AACjC,EAAA,IAAI,OAAO,iBAAiB,WAAA,EAAa;AACvC,IAAA,YAAA,CAAa,WAAW,SAAS,CAAA;AAAA,EACnC;AACF;AAEO,SAAS,eAAA,GAA2B;AACzC,EAAA,OAAO,CAAC,CAAC,QAAA,EAAS;AACpB;AAIA,SAAS,UAAA,GAAqB;AAE5B,EAAA,IAAI,OAAO,MAAA,CAAA,IAAA,KAAgB,WAAA,IAAgB,MAAA,CAAA,IAAA,CAAoB,KAAK,YAAA,EAAc;AAChF,IAAA,OAAQ,YAAoB,GAAA,CAAI,YAAA;AAAA,EAClC;AAEA,EAAA,OAAO,MAAA;AACT;AAEA,IAAM,GAAA,GAAqB,MAAM,MAAA,CAAO;AAAA,EACtC,SAAS,UAAA,EAAW;AAAA,EACpB,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA;AAC7B,CAAC,CAAA;AAGD,GAAA,CAAI,YAAA,CAAa,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAA,KAAW;AACvC,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,MAAA,CAAO,OAAA,CAAQ,aAAA,GAAgB,CAAA,OAAA,EAAU,KAAK,CAAA,CAAA;AAAA,EAChD;AACA,EAAA,OAAO,MAAA;AACT,CAAC,CAAA;AAGD,GAAA,CAAI,aAAa,QAAA,CAAS,GAAA;AAAA,EACxB,CAAC,aAAa,QAAA,CAAS,IAAA;AAAA,EACvB,CAAC,KAAA,KAAU;AACT,IAAA,IAAI,KAAA,CAAM,QAAA,EAAU,MAAA,KAAW,GAAA,EAAK;AAClC,MAAA,UAAA,EAAW;AACX,MAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACjC,QAAA,MAAA,CAAO,SAAS,IAAA,GAAO,QAAA;AAAA,MACzB;AAAA,IACF;AACA,IAAA,OAAO,OAAA,CAAQ,OAAO,KAAK,CAAA;AAAA,EAC7B;AACF,CAAA;AAEA,IAAO,cAAA,GAAQ","file":"index.js","sourcesContent":["/**\n * @coduckai/sdk/client — Frontend API Client (browser-only)\n *\n * Pre-configured Axios instance with auth interceptor.\n *\n * Usage:\n * import api, { setToken, clearToken } from '@coduckai/sdk/client';\n * const recipes = await api.get('/recipes'); // returns response.data directly\n * setToken(loginData.token);\n */\n\nimport axios, { type AxiosInstance } from 'axios';\n\n// ── Token Management ───────────────────────────────────\n\nconst TOKEN_KEY = 'token';\n\nexport function setToken(token: string): void {\n if (typeof localStorage !== 'undefined') {\n localStorage.setItem(TOKEN_KEY, token);\n }\n}\n\nexport function getToken(): string | null {\n if (typeof localStorage !== 'undefined') {\n return localStorage.getItem(TOKEN_KEY);\n }\n return null;\n}\n\nexport function clearToken(): void {\n if (typeof localStorage !== 'undefined') {\n localStorage.removeItem(TOKEN_KEY);\n }\n}\n\nexport function isAuthenticated(): boolean {\n return !!getToken();\n}\n\n// ── Axios Instance ─────────────────────────────────────\n\nfunction getBaseUrl(): string {\n // Vite: VITE_API_URL\n if (typeof import.meta !== 'undefined' && (import.meta as any).env?.VITE_API_URL) {\n return (import.meta as any).env.VITE_API_URL;\n }\n // Fallback: relative /api\n return '/api';\n}\n\nconst api: AxiosInstance = axios.create({\n baseURL: getBaseUrl(),\n headers: { 'Content-Type': 'application/json' },\n});\n\n// Request interceptor — attach Bearer token\napi.interceptors.request.use((config) => {\n const token = getToken();\n if (token) {\n config.headers.Authorization = `Bearer ${token}`;\n }\n return config;\n});\n\n// Response interceptor — auto-extract .data and handle 401\napi.interceptors.response.use(\n (response) => response.data,\n (error) => {\n if (error.response?.status === 401) {\n clearToken();\n if (typeof window !== 'undefined') {\n window.location.href = '/login';\n }\n }\n return Promise.reject(error);\n }\n);\n\nexport default api;\n"]}
@@ -61,7 +61,7 @@ function getWebhookSecret() {
61
61
  var payments = {
62
62
  /**
63
63
  * Create a Stripe Checkout Session for one-time payment.
64
- * Automatically includes destination charges when STRIPE_CONNECTED_ACCOUNT_ID is set.
64
+ * Automatically uses direct charges (stripeAccount) when STRIPE_CONNECTED_ACCOUNT_ID is set.
65
65
  */
66
66
  async createCheckout(options) {
67
67
  const stripe = getStripeInstance();
@@ -85,17 +85,18 @@ var payments = {
85
85
  ...options.customerEmail && { customer_email: options.customerEmail },
86
86
  ...options.metadata && { metadata: options.metadata }
87
87
  };
88
- if (connectedAccountId) {
88
+ if (connectedAccountId && options.applicationFeeAmount) {
89
89
  params.payment_intent_data = {
90
- transfer_data: { destination: connectedAccountId }
90
+ application_fee_amount: options.applicationFeeAmount
91
91
  };
92
92
  }
93
- const session = await stripe.checkout.sessions.create(params);
93
+ const requestOptions = connectedAccountId ? { stripeAccount: connectedAccountId } : void 0;
94
+ const session = await stripe.checkout.sessions.create(params, requestOptions);
94
95
  return { url: session.url, sessionId: session.id };
95
96
  },
96
97
  /**
97
98
  * Create a Stripe Checkout Session for subscriptions.
98
- * Automatically includes destination charges when STRIPE_CONNECTED_ACCOUNT_ID is set.
99
+ * Automatically uses direct charges (stripeAccount) when STRIPE_CONNECTED_ACCOUNT_ID is set.
99
100
  */
100
101
  async createSubscription(options) {
101
102
  const stripe = getStripeInstance();
@@ -108,16 +109,17 @@ var payments = {
108
109
  ...options.customerEmail && { customer_email: options.customerEmail },
109
110
  ...options.metadata && { metadata: options.metadata }
110
111
  };
111
- if (options.trialDays || connectedAccountId) {
112
+ if (options.trialDays || connectedAccountId && options.applicationFeePercent) {
112
113
  params.subscription_data = {};
113
114
  if (options.trialDays) {
114
115
  params.subscription_data.trial_period_days = options.trialDays;
115
116
  }
116
- if (connectedAccountId) {
117
- params.subscription_data.transfer_data = { destination: connectedAccountId };
117
+ if (connectedAccountId && options.applicationFeePercent) {
118
+ params.subscription_data.application_fee_percent = options.applicationFeePercent;
118
119
  }
119
120
  }
120
- const session = await stripe.checkout.sessions.create(params);
121
+ const requestOptions = connectedAccountId ? { stripeAccount: connectedAccountId } : void 0;
122
+ const session = await stripe.checkout.sessions.create(params, requestOptions);
121
123
  return { url: session.url, sessionId: session.id };
122
124
  },
123
125
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/internal/env.ts","../../src/internal/config.ts","../../src/payments/index.ts"],"names":["Stripe"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Environment variable helpers\n */\n\nexport function getEnv(key: string, fallback?: string): string | undefined {\n return process.env[key] || fallback;\n}\n\nexport function requireEnv(key: string): string {\n const val = process.env[key];\n if (!val) {\n throw new Error(`@coduckai/sdk: Missing required environment variable: ${key}`);\n }\n return val;\n}\n\nexport function isServer(): boolean {\n return typeof window === 'undefined';\n}\n\nexport function assertServer(moduleName: string): void {\n if (!isServer()) {\n throw new Error(\n `@coduckai/sdk/${moduleName} is server-only. Use @coduckai/sdk/client for browser code.`\n );\n }\n}\n","/**\n * Global SDK configuration\n *\n * CoDuck apps use env vars automatically — no configure() needed.\n * Standalone usage can call configure() to override.\n */\n\nexport interface SDKConfig {\n auth?: {\n jwtSecret?: string;\n jwtExpiresIn?: string;\n bcryptRounds?: number;\n };\n db?: {\n connectionString?: string;\n };\n client?: {\n baseUrl?: string;\n tokenKey?: string;\n };\n payments?: {\n stripeSecretKey?: string;\n connectedAccountId?: string;\n webhookSecret?: string;\n };\n ai?: {\n provider?: 'openai' | 'anthropic';\n apiKey?: string;\n };\n}\n\nlet globalConfig: SDKConfig = {};\n\nexport function setConfig(config: SDKConfig): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\nexport function getConfig(): SDKConfig {\n return globalConfig;\n}\n","/**\n * @coduckai/sdk/payments — Stripe Payments (server-only)\n *\n * Wraps Stripe SDK with automatic Connect destination charges.\n * Auto-reads STRIPE_SECRET_KEY, STRIPE_CONNECTED_ACCOUNT_ID, STRIPE_WEBHOOK_SECRET from env.\n */\n\nimport { assertServer } from '../internal/env';\nimport { getConfig } from '../internal/config';\nimport Stripe from 'stripe';\n\nassertServer('payments');\n\n// ---------------------------------------------------------------------------\n// Lazy singleton Stripe instance\n// ---------------------------------------------------------------------------\n\nlet stripeInstance: Stripe | null = null;\n\nfunction getStripeInstance(): Stripe {\n if (stripeInstance) return stripeInstance;\n\n const config = getConfig();\n const secretKey = config.payments?.stripeSecretKey || process.env.STRIPE_SECRET_KEY;\n\n if (!secretKey) {\n throw new Error(\n '@coduckai/sdk/payments: STRIPE_SECRET_KEY is required. Set the env var or call configure().'\n );\n }\n\n stripeInstance = new Stripe(secretKey);\n return stripeInstance;\n}\n\nfunction getConnectedAccountId(): string | undefined {\n const config = getConfig();\n return config.payments?.connectedAccountId || process.env.STRIPE_CONNECTED_ACCOUNT_ID || undefined;\n}\n\nfunction getWebhookSecret(): string {\n const config = getConfig();\n const secret = config.payments?.webhookSecret || process.env.STRIPE_WEBHOOK_SECRET;\n if (!secret) {\n throw new Error(\n '@coduckai/sdk/payments: STRIPE_WEBHOOK_SECRET is required for webhook handling.'\n );\n }\n return secret;\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface CheckoutItem {\n name: string;\n /** Price in dollars (e.g. 29.99) */\n price: number;\n quantity?: number;\n description?: string;\n image?: string;\n}\n\nexport interface CreateCheckoutOptions {\n items: CheckoutItem[];\n successUrl: string;\n cancelUrl: string;\n customerEmail?: string;\n metadata?: Record<string, string>;\n}\n\nexport interface CreateSubscriptionOptions {\n priceId: string;\n successUrl: string;\n cancelUrl: string;\n customerEmail?: string;\n trialDays?: number;\n metadata?: Record<string, string>;\n}\n\nexport interface CheckoutResult {\n url: string;\n sessionId: string;\n}\n\nexport interface WebhookHandlers {\n onCheckoutComplete?: (session: Stripe.Checkout.Session) => Promise<void> | void;\n onSubscriptionCreated?: (subscription: Stripe.Subscription) => Promise<void> | void;\n onSubscriptionUpdated?: (subscription: Stripe.Subscription) => Promise<void> | void;\n onSubscriptionDeleted?: (subscription: Stripe.Subscription) => Promise<void> | void;\n onPaymentSucceeded?: (invoice: Stripe.Invoice) => Promise<void> | void;\n onPaymentFailed?: (invoice: Stripe.Invoice) => Promise<void> | void;\n onChargeRefunded?: (charge: Stripe.Charge) => Promise<void> | void;\n}\n\n// ---------------------------------------------------------------------------\n// Payments API\n// ---------------------------------------------------------------------------\n\nexport const payments = {\n /**\n * Create a Stripe Checkout Session for one-time payment.\n * Automatically includes destination charges when STRIPE_CONNECTED_ACCOUNT_ID is set.\n */\n async createCheckout(options: CreateCheckoutOptions): Promise<CheckoutResult> {\n const stripe = getStripeInstance();\n const connectedAccountId = getConnectedAccountId();\n\n const params: Stripe.Checkout.SessionCreateParams = {\n mode: 'payment',\n line_items: options.items.map(item => ({\n price_data: {\n currency: 'usd',\n product_data: {\n name: item.name,\n ...(item.description && { description: item.description }),\n ...(item.image && { images: [item.image] }),\n },\n unit_amount: Math.round(item.price * 100),\n },\n quantity: item.quantity || 1,\n })),\n success_url: options.successUrl,\n cancel_url: options.cancelUrl,\n ...(options.customerEmail && { customer_email: options.customerEmail }),\n ...(options.metadata && { metadata: options.metadata }),\n };\n\n if (connectedAccountId) {\n params.payment_intent_data = {\n transfer_data: { destination: connectedAccountId },\n };\n }\n\n const session = await stripe.checkout.sessions.create(params);\n\n return { url: session.url!, sessionId: session.id };\n },\n\n /**\n * Create a Stripe Checkout Session for subscriptions.\n * Automatically includes destination charges when STRIPE_CONNECTED_ACCOUNT_ID is set.\n */\n async createSubscription(options: CreateSubscriptionOptions): Promise<CheckoutResult> {\n const stripe = getStripeInstance();\n const connectedAccountId = getConnectedAccountId();\n\n const params: Stripe.Checkout.SessionCreateParams = {\n mode: 'subscription',\n line_items: [{ price: options.priceId, quantity: 1 }],\n success_url: options.successUrl,\n cancel_url: options.cancelUrl,\n ...(options.customerEmail && { customer_email: options.customerEmail }),\n ...(options.metadata && { metadata: options.metadata }),\n };\n\n if (options.trialDays || connectedAccountId) {\n params.subscription_data = {};\n if (options.trialDays) {\n params.subscription_data.trial_period_days = options.trialDays;\n }\n if (connectedAccountId) {\n params.subscription_data.transfer_data = { destination: connectedAccountId };\n }\n }\n\n const session = await stripe.checkout.sessions.create(params);\n\n return { url: session.url!, sessionId: session.id };\n },\n\n /**\n * Handle Stripe webhook events with typed handlers.\n * Verifies the webhook signature and dispatches to your handler callbacks.\n */\n async handleWebhook(req: any, handlers: WebhookHandlers): Promise<void> {\n const stripe = getStripeInstance();\n const webhookSecret = getWebhookSecret();\n\n const sig = req.headers['stripe-signature'];\n const event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);\n\n switch (event.type) {\n case 'checkout.session.completed':\n await handlers.onCheckoutComplete?.(event.data.object as Stripe.Checkout.Session);\n break;\n case 'customer.subscription.created':\n await handlers.onSubscriptionCreated?.(event.data.object as Stripe.Subscription);\n break;\n case 'customer.subscription.updated':\n await handlers.onSubscriptionUpdated?.(event.data.object as Stripe.Subscription);\n break;\n case 'customer.subscription.deleted':\n await handlers.onSubscriptionDeleted?.(event.data.object as Stripe.Subscription);\n break;\n case 'invoice.payment_succeeded':\n await handlers.onPaymentSucceeded?.(event.data.object as Stripe.Invoice);\n break;\n case 'invoice.payment_failed':\n await handlers.onPaymentFailed?.(event.data.object as Stripe.Invoice);\n break;\n case 'charge.refunded':\n await handlers.onChargeRefunded?.(event.data.object as Stripe.Charge);\n break;\n }\n },\n\n /**\n * Express middleware for webhook routes.\n * Applies express.raw() body parser — mount BEFORE any json() middleware.\n */\n webhookMiddleware(): any {\n try {\n const express = require('express');\n return express.raw({ type: 'application/json' });\n } catch {\n return (_req: any, _res: any, next: any) => next();\n }\n },\n\n /**\n * Get the underlying Stripe instance for advanced usage.\n */\n getStripe(): Stripe {\n return getStripeInstance();\n },\n};\n"]}
1
+ {"version":3,"sources":["../../src/internal/env.ts","../../src/internal/config.ts","../../src/payments/index.ts"],"names":["Stripe"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Environment variable helpers\n */\n\nexport function getEnv(key: string, fallback?: string): string | undefined {\n return process.env[key] || fallback;\n}\n\nexport function requireEnv(key: string): string {\n const val = process.env[key];\n if (!val) {\n throw new Error(`@coduckai/sdk: Missing required environment variable: ${key}`);\n }\n return val;\n}\n\nexport function isServer(): boolean {\n return typeof window === 'undefined';\n}\n\nexport function assertServer(moduleName: string): void {\n if (!isServer()) {\n throw new Error(\n `@coduckai/sdk/${moduleName} is server-only. Use @coduckai/sdk/client for browser code.`\n );\n }\n}\n","/**\n * Global SDK configuration\n *\n * CoDuck apps use env vars automatically — no configure() needed.\n * Standalone usage can call configure() to override.\n */\n\nexport interface SDKConfig {\n auth?: {\n jwtSecret?: string;\n jwtExpiresIn?: string;\n bcryptRounds?: number;\n };\n db?: {\n connectionString?: string;\n };\n client?: {\n baseUrl?: string;\n tokenKey?: string;\n };\n payments?: {\n stripeSecretKey?: string;\n connectedAccountId?: string;\n webhookSecret?: string;\n };\n ai?: {\n provider?: 'openai' | 'anthropic';\n apiKey?: string;\n };\n}\n\nlet globalConfig: SDKConfig = {};\n\nexport function setConfig(config: SDKConfig): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\nexport function getConfig(): SDKConfig {\n return globalConfig;\n}\n","/**\n * @coduckai/sdk/payments — Stripe Payments (server-only)\n *\n * Wraps Stripe SDK with automatic Connect direct charges via stripeAccount header.\n * Auto-reads STRIPE_SECRET_KEY, STRIPE_CONNECTED_ACCOUNT_ID, STRIPE_WEBHOOK_SECRET from env.\n */\n\nimport { assertServer } from '../internal/env';\nimport { getConfig } from '../internal/config';\nimport Stripe from 'stripe';\n\nassertServer('payments');\n\n// ---------------------------------------------------------------------------\n// Lazy singleton Stripe instance\n// ---------------------------------------------------------------------------\n\nlet stripeInstance: Stripe | null = null;\n\nfunction getStripeInstance(): Stripe {\n if (stripeInstance) return stripeInstance;\n\n const config = getConfig();\n const secretKey = config.payments?.stripeSecretKey || process.env.STRIPE_SECRET_KEY;\n\n if (!secretKey) {\n throw new Error(\n '@coduckai/sdk/payments: STRIPE_SECRET_KEY is required. Set the env var or call configure().'\n );\n }\n\n stripeInstance = new Stripe(secretKey);\n return stripeInstance;\n}\n\nfunction getConnectedAccountId(): string | undefined {\n const config = getConfig();\n return config.payments?.connectedAccountId || process.env.STRIPE_CONNECTED_ACCOUNT_ID || undefined;\n}\n\nfunction getWebhookSecret(): string {\n const config = getConfig();\n const secret = config.payments?.webhookSecret || process.env.STRIPE_WEBHOOK_SECRET;\n if (!secret) {\n throw new Error(\n '@coduckai/sdk/payments: STRIPE_WEBHOOK_SECRET is required for webhook handling.'\n );\n }\n return secret;\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface CheckoutItem {\n name: string;\n /** Price in dollars (e.g. 29.99) */\n price: number;\n quantity?: number;\n description?: string;\n image?: string;\n}\n\nexport interface CreateCheckoutOptions {\n items: CheckoutItem[];\n successUrl: string;\n cancelUrl: string;\n customerEmail?: string;\n metadata?: Record<string, string>;\n /** Application fee in cents charged on top of the payment (Connect only) */\n applicationFeeAmount?: number;\n}\n\nexport interface CreateSubscriptionOptions {\n priceId: string;\n successUrl: string;\n cancelUrl: string;\n customerEmail?: string;\n trialDays?: number;\n metadata?: Record<string, string>;\n /** Application fee percentage for recurring charges (0-100, Connect only) */\n applicationFeePercent?: number;\n}\n\nexport interface CheckoutResult {\n url: string;\n sessionId: string;\n}\n\nexport interface WebhookHandlers {\n onCheckoutComplete?: (session: Stripe.Checkout.Session) => Promise<void> | void;\n onSubscriptionCreated?: (subscription: Stripe.Subscription) => Promise<void> | void;\n onSubscriptionUpdated?: (subscription: Stripe.Subscription) => Promise<void> | void;\n onSubscriptionDeleted?: (subscription: Stripe.Subscription) => Promise<void> | void;\n onPaymentSucceeded?: (invoice: Stripe.Invoice) => Promise<void> | void;\n onPaymentFailed?: (invoice: Stripe.Invoice) => Promise<void> | void;\n onChargeRefunded?: (charge: Stripe.Charge) => Promise<void> | void;\n}\n\n// ---------------------------------------------------------------------------\n// Payments API\n// ---------------------------------------------------------------------------\n\nexport const payments = {\n /**\n * Create a Stripe Checkout Session for one-time payment.\n * Automatically uses direct charges (stripeAccount) when STRIPE_CONNECTED_ACCOUNT_ID is set.\n */\n async createCheckout(options: CreateCheckoutOptions): Promise<CheckoutResult> {\n const stripe = getStripeInstance();\n const connectedAccountId = getConnectedAccountId();\n\n const params: Stripe.Checkout.SessionCreateParams = {\n mode: 'payment',\n line_items: options.items.map(item => ({\n price_data: {\n currency: 'usd',\n product_data: {\n name: item.name,\n ...(item.description && { description: item.description }),\n ...(item.image && { images: [item.image] }),\n },\n unit_amount: Math.round(item.price * 100),\n },\n quantity: item.quantity || 1,\n })),\n success_url: options.successUrl,\n cancel_url: options.cancelUrl,\n ...(options.customerEmail && { customer_email: options.customerEmail }),\n ...(options.metadata && { metadata: options.metadata }),\n };\n\n if (connectedAccountId && options.applicationFeeAmount) {\n params.payment_intent_data = {\n application_fee_amount: options.applicationFeeAmount,\n };\n }\n\n const requestOptions: Stripe.RequestOptions | undefined = connectedAccountId\n ? { stripeAccount: connectedAccountId }\n : undefined;\n\n const session = await stripe.checkout.sessions.create(params, requestOptions);\n\n return { url: session.url!, sessionId: session.id };\n },\n\n /**\n * Create a Stripe Checkout Session for subscriptions.\n * Automatically uses direct charges (stripeAccount) when STRIPE_CONNECTED_ACCOUNT_ID is set.\n */\n async createSubscription(options: CreateSubscriptionOptions): Promise<CheckoutResult> {\n const stripe = getStripeInstance();\n const connectedAccountId = getConnectedAccountId();\n\n const params: Stripe.Checkout.SessionCreateParams = {\n mode: 'subscription',\n line_items: [{ price: options.priceId, quantity: 1 }],\n success_url: options.successUrl,\n cancel_url: options.cancelUrl,\n ...(options.customerEmail && { customer_email: options.customerEmail }),\n ...(options.metadata && { metadata: options.metadata }),\n };\n\n if (options.trialDays || (connectedAccountId && options.applicationFeePercent)) {\n params.subscription_data = {};\n if (options.trialDays) {\n params.subscription_data.trial_period_days = options.trialDays;\n }\n if (connectedAccountId && options.applicationFeePercent) {\n params.subscription_data.application_fee_percent = options.applicationFeePercent;\n }\n }\n\n const requestOptions: Stripe.RequestOptions | undefined = connectedAccountId\n ? { stripeAccount: connectedAccountId }\n : undefined;\n\n const session = await stripe.checkout.sessions.create(params, requestOptions);\n\n return { url: session.url!, sessionId: session.id };\n },\n\n /**\n * Handle Stripe webhook events with typed handlers.\n * Verifies the webhook signature and dispatches to your handler callbacks.\n */\n async handleWebhook(req: any, handlers: WebhookHandlers): Promise<void> {\n const stripe = getStripeInstance();\n const webhookSecret = getWebhookSecret();\n\n const sig = req.headers['stripe-signature'];\n const event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);\n\n switch (event.type) {\n case 'checkout.session.completed':\n await handlers.onCheckoutComplete?.(event.data.object as Stripe.Checkout.Session);\n break;\n case 'customer.subscription.created':\n await handlers.onSubscriptionCreated?.(event.data.object as Stripe.Subscription);\n break;\n case 'customer.subscription.updated':\n await handlers.onSubscriptionUpdated?.(event.data.object as Stripe.Subscription);\n break;\n case 'customer.subscription.deleted':\n await handlers.onSubscriptionDeleted?.(event.data.object as Stripe.Subscription);\n break;\n case 'invoice.payment_succeeded':\n await handlers.onPaymentSucceeded?.(event.data.object as Stripe.Invoice);\n break;\n case 'invoice.payment_failed':\n await handlers.onPaymentFailed?.(event.data.object as Stripe.Invoice);\n break;\n case 'charge.refunded':\n await handlers.onChargeRefunded?.(event.data.object as Stripe.Charge);\n break;\n }\n },\n\n /**\n * Express middleware for webhook routes.\n * Applies express.raw() body parser — mount BEFORE any json() middleware.\n */\n webhookMiddleware(): any {\n try {\n const express = require('express');\n return express.raw({ type: 'application/json' });\n } catch {\n return (_req: any, _res: any, next: any) => next();\n }\n },\n\n /**\n * Get the underlying Stripe instance for advanced usage.\n */\n getStripe(): Stripe {\n return getStripeInstance();\n },\n};\n"]}
@@ -3,7 +3,7 @@ import Stripe from 'stripe';
3
3
  /**
4
4
  * @coduckai/sdk/payments — Stripe Payments (server-only)
5
5
  *
6
- * Wraps Stripe SDK with automatic Connect destination charges.
6
+ * Wraps Stripe SDK with automatic Connect direct charges via stripeAccount header.
7
7
  * Auto-reads STRIPE_SECRET_KEY, STRIPE_CONNECTED_ACCOUNT_ID, STRIPE_WEBHOOK_SECRET from env.
8
8
  */
9
9
 
@@ -21,6 +21,8 @@ interface CreateCheckoutOptions {
21
21
  cancelUrl: string;
22
22
  customerEmail?: string;
23
23
  metadata?: Record<string, string>;
24
+ /** Application fee in cents charged on top of the payment (Connect only) */
25
+ applicationFeeAmount?: number;
24
26
  }
25
27
  interface CreateSubscriptionOptions {
26
28
  priceId: string;
@@ -29,6 +31,8 @@ interface CreateSubscriptionOptions {
29
31
  customerEmail?: string;
30
32
  trialDays?: number;
31
33
  metadata?: Record<string, string>;
34
+ /** Application fee percentage for recurring charges (0-100, Connect only) */
35
+ applicationFeePercent?: number;
32
36
  }
33
37
  interface CheckoutResult {
34
38
  url: string;
@@ -46,12 +50,12 @@ interface WebhookHandlers {
46
50
  declare const payments: {
47
51
  /**
48
52
  * Create a Stripe Checkout Session for one-time payment.
49
- * Automatically includes destination charges when STRIPE_CONNECTED_ACCOUNT_ID is set.
53
+ * Automatically uses direct charges (stripeAccount) when STRIPE_CONNECTED_ACCOUNT_ID is set.
50
54
  */
51
55
  createCheckout(options: CreateCheckoutOptions): Promise<CheckoutResult>;
52
56
  /**
53
57
  * Create a Stripe Checkout Session for subscriptions.
54
- * Automatically includes destination charges when STRIPE_CONNECTED_ACCOUNT_ID is set.
58
+ * Automatically uses direct charges (stripeAccount) when STRIPE_CONNECTED_ACCOUNT_ID is set.
55
59
  */
56
60
  createSubscription(options: CreateSubscriptionOptions): Promise<CheckoutResult>;
57
61
  /**
@@ -3,7 +3,7 @@ import Stripe from 'stripe';
3
3
  /**
4
4
  * @coduckai/sdk/payments — Stripe Payments (server-only)
5
5
  *
6
- * Wraps Stripe SDK with automatic Connect destination charges.
6
+ * Wraps Stripe SDK with automatic Connect direct charges via stripeAccount header.
7
7
  * Auto-reads STRIPE_SECRET_KEY, STRIPE_CONNECTED_ACCOUNT_ID, STRIPE_WEBHOOK_SECRET from env.
8
8
  */
9
9
 
@@ -21,6 +21,8 @@ interface CreateCheckoutOptions {
21
21
  cancelUrl: string;
22
22
  customerEmail?: string;
23
23
  metadata?: Record<string, string>;
24
+ /** Application fee in cents charged on top of the payment (Connect only) */
25
+ applicationFeeAmount?: number;
24
26
  }
25
27
  interface CreateSubscriptionOptions {
26
28
  priceId: string;
@@ -29,6 +31,8 @@ interface CreateSubscriptionOptions {
29
31
  customerEmail?: string;
30
32
  trialDays?: number;
31
33
  metadata?: Record<string, string>;
34
+ /** Application fee percentage for recurring charges (0-100, Connect only) */
35
+ applicationFeePercent?: number;
32
36
  }
33
37
  interface CheckoutResult {
34
38
  url: string;
@@ -46,12 +50,12 @@ interface WebhookHandlers {
46
50
  declare const payments: {
47
51
  /**
48
52
  * Create a Stripe Checkout Session for one-time payment.
49
- * Automatically includes destination charges when STRIPE_CONNECTED_ACCOUNT_ID is set.
53
+ * Automatically uses direct charges (stripeAccount) when STRIPE_CONNECTED_ACCOUNT_ID is set.
50
54
  */
51
55
  createCheckout(options: CreateCheckoutOptions): Promise<CheckoutResult>;
52
56
  /**
53
57
  * Create a Stripe Checkout Session for subscriptions.
54
- * Automatically includes destination charges when STRIPE_CONNECTED_ACCOUNT_ID is set.
58
+ * Automatically uses direct charges (stripeAccount) when STRIPE_CONNECTED_ACCOUNT_ID is set.
55
59
  */
56
60
  createSubscription(options: CreateSubscriptionOptions): Promise<CheckoutResult>;
57
61
  /**
@@ -55,7 +55,7 @@ function getWebhookSecret() {
55
55
  var payments = {
56
56
  /**
57
57
  * Create a Stripe Checkout Session for one-time payment.
58
- * Automatically includes destination charges when STRIPE_CONNECTED_ACCOUNT_ID is set.
58
+ * Automatically uses direct charges (stripeAccount) when STRIPE_CONNECTED_ACCOUNT_ID is set.
59
59
  */
60
60
  async createCheckout(options) {
61
61
  const stripe = getStripeInstance();
@@ -79,17 +79,18 @@ var payments = {
79
79
  ...options.customerEmail && { customer_email: options.customerEmail },
80
80
  ...options.metadata && { metadata: options.metadata }
81
81
  };
82
- if (connectedAccountId) {
82
+ if (connectedAccountId && options.applicationFeeAmount) {
83
83
  params.payment_intent_data = {
84
- transfer_data: { destination: connectedAccountId }
84
+ application_fee_amount: options.applicationFeeAmount
85
85
  };
86
86
  }
87
- const session = await stripe.checkout.sessions.create(params);
87
+ const requestOptions = connectedAccountId ? { stripeAccount: connectedAccountId } : void 0;
88
+ const session = await stripe.checkout.sessions.create(params, requestOptions);
88
89
  return { url: session.url, sessionId: session.id };
89
90
  },
90
91
  /**
91
92
  * Create a Stripe Checkout Session for subscriptions.
92
- * Automatically includes destination charges when STRIPE_CONNECTED_ACCOUNT_ID is set.
93
+ * Automatically uses direct charges (stripeAccount) when STRIPE_CONNECTED_ACCOUNT_ID is set.
93
94
  */
94
95
  async createSubscription(options) {
95
96
  const stripe = getStripeInstance();
@@ -102,16 +103,17 @@ var payments = {
102
103
  ...options.customerEmail && { customer_email: options.customerEmail },
103
104
  ...options.metadata && { metadata: options.metadata }
104
105
  };
105
- if (options.trialDays || connectedAccountId) {
106
+ if (options.trialDays || connectedAccountId && options.applicationFeePercent) {
106
107
  params.subscription_data = {};
107
108
  if (options.trialDays) {
108
109
  params.subscription_data.trial_period_days = options.trialDays;
109
110
  }
110
- if (connectedAccountId) {
111
- params.subscription_data.transfer_data = { destination: connectedAccountId };
111
+ if (connectedAccountId && options.applicationFeePercent) {
112
+ params.subscription_data.application_fee_percent = options.applicationFeePercent;
112
113
  }
113
114
  }
114
- const session = await stripe.checkout.sessions.create(params);
115
+ const requestOptions = connectedAccountId ? { stripeAccount: connectedAccountId } : void 0;
116
+ const session = await stripe.checkout.sessions.create(params, requestOptions);
115
117
  return { url: session.url, sessionId: session.id };
116
118
  },
117
119
  /**
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/internal/env.ts","../../src/internal/config.ts","../../src/payments/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Environment variable helpers\n */\n\nexport function getEnv(key: string, fallback?: string): string | undefined {\n return process.env[key] || fallback;\n}\n\nexport function requireEnv(key: string): string {\n const val = process.env[key];\n if (!val) {\n throw new Error(`@coduckai/sdk: Missing required environment variable: ${key}`);\n }\n return val;\n}\n\nexport function isServer(): boolean {\n return typeof window === 'undefined';\n}\n\nexport function assertServer(moduleName: string): void {\n if (!isServer()) {\n throw new Error(\n `@coduckai/sdk/${moduleName} is server-only. Use @coduckai/sdk/client for browser code.`\n );\n }\n}\n","/**\n * Global SDK configuration\n *\n * CoDuck apps use env vars automatically — no configure() needed.\n * Standalone usage can call configure() to override.\n */\n\nexport interface SDKConfig {\n auth?: {\n jwtSecret?: string;\n jwtExpiresIn?: string;\n bcryptRounds?: number;\n };\n db?: {\n connectionString?: string;\n };\n client?: {\n baseUrl?: string;\n tokenKey?: string;\n };\n payments?: {\n stripeSecretKey?: string;\n connectedAccountId?: string;\n webhookSecret?: string;\n };\n ai?: {\n provider?: 'openai' | 'anthropic';\n apiKey?: string;\n };\n}\n\nlet globalConfig: SDKConfig = {};\n\nexport function setConfig(config: SDKConfig): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\nexport function getConfig(): SDKConfig {\n return globalConfig;\n}\n","/**\n * @coduckai/sdk/payments — Stripe Payments (server-only)\n *\n * Wraps Stripe SDK with automatic Connect destination charges.\n * Auto-reads STRIPE_SECRET_KEY, STRIPE_CONNECTED_ACCOUNT_ID, STRIPE_WEBHOOK_SECRET from env.\n */\n\nimport { assertServer } from '../internal/env';\nimport { getConfig } from '../internal/config';\nimport Stripe from 'stripe';\n\nassertServer('payments');\n\n// ---------------------------------------------------------------------------\n// Lazy singleton Stripe instance\n// ---------------------------------------------------------------------------\n\nlet stripeInstance: Stripe | null = null;\n\nfunction getStripeInstance(): Stripe {\n if (stripeInstance) return stripeInstance;\n\n const config = getConfig();\n const secretKey = config.payments?.stripeSecretKey || process.env.STRIPE_SECRET_KEY;\n\n if (!secretKey) {\n throw new Error(\n '@coduckai/sdk/payments: STRIPE_SECRET_KEY is required. Set the env var or call configure().'\n );\n }\n\n stripeInstance = new Stripe(secretKey);\n return stripeInstance;\n}\n\nfunction getConnectedAccountId(): string | undefined {\n const config = getConfig();\n return config.payments?.connectedAccountId || process.env.STRIPE_CONNECTED_ACCOUNT_ID || undefined;\n}\n\nfunction getWebhookSecret(): string {\n const config = getConfig();\n const secret = config.payments?.webhookSecret || process.env.STRIPE_WEBHOOK_SECRET;\n if (!secret) {\n throw new Error(\n '@coduckai/sdk/payments: STRIPE_WEBHOOK_SECRET is required for webhook handling.'\n );\n }\n return secret;\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface CheckoutItem {\n name: string;\n /** Price in dollars (e.g. 29.99) */\n price: number;\n quantity?: number;\n description?: string;\n image?: string;\n}\n\nexport interface CreateCheckoutOptions {\n items: CheckoutItem[];\n successUrl: string;\n cancelUrl: string;\n customerEmail?: string;\n metadata?: Record<string, string>;\n}\n\nexport interface CreateSubscriptionOptions {\n priceId: string;\n successUrl: string;\n cancelUrl: string;\n customerEmail?: string;\n trialDays?: number;\n metadata?: Record<string, string>;\n}\n\nexport interface CheckoutResult {\n url: string;\n sessionId: string;\n}\n\nexport interface WebhookHandlers {\n onCheckoutComplete?: (session: Stripe.Checkout.Session) => Promise<void> | void;\n onSubscriptionCreated?: (subscription: Stripe.Subscription) => Promise<void> | void;\n onSubscriptionUpdated?: (subscription: Stripe.Subscription) => Promise<void> | void;\n onSubscriptionDeleted?: (subscription: Stripe.Subscription) => Promise<void> | void;\n onPaymentSucceeded?: (invoice: Stripe.Invoice) => Promise<void> | void;\n onPaymentFailed?: (invoice: Stripe.Invoice) => Promise<void> | void;\n onChargeRefunded?: (charge: Stripe.Charge) => Promise<void> | void;\n}\n\n// ---------------------------------------------------------------------------\n// Payments API\n// ---------------------------------------------------------------------------\n\nexport const payments = {\n /**\n * Create a Stripe Checkout Session for one-time payment.\n * Automatically includes destination charges when STRIPE_CONNECTED_ACCOUNT_ID is set.\n */\n async createCheckout(options: CreateCheckoutOptions): Promise<CheckoutResult> {\n const stripe = getStripeInstance();\n const connectedAccountId = getConnectedAccountId();\n\n const params: Stripe.Checkout.SessionCreateParams = {\n mode: 'payment',\n line_items: options.items.map(item => ({\n price_data: {\n currency: 'usd',\n product_data: {\n name: item.name,\n ...(item.description && { description: item.description }),\n ...(item.image && { images: [item.image] }),\n },\n unit_amount: Math.round(item.price * 100),\n },\n quantity: item.quantity || 1,\n })),\n success_url: options.successUrl,\n cancel_url: options.cancelUrl,\n ...(options.customerEmail && { customer_email: options.customerEmail }),\n ...(options.metadata && { metadata: options.metadata }),\n };\n\n if (connectedAccountId) {\n params.payment_intent_data = {\n transfer_data: { destination: connectedAccountId },\n };\n }\n\n const session = await stripe.checkout.sessions.create(params);\n\n return { url: session.url!, sessionId: session.id };\n },\n\n /**\n * Create a Stripe Checkout Session for subscriptions.\n * Automatically includes destination charges when STRIPE_CONNECTED_ACCOUNT_ID is set.\n */\n async createSubscription(options: CreateSubscriptionOptions): Promise<CheckoutResult> {\n const stripe = getStripeInstance();\n const connectedAccountId = getConnectedAccountId();\n\n const params: Stripe.Checkout.SessionCreateParams = {\n mode: 'subscription',\n line_items: [{ price: options.priceId, quantity: 1 }],\n success_url: options.successUrl,\n cancel_url: options.cancelUrl,\n ...(options.customerEmail && { customer_email: options.customerEmail }),\n ...(options.metadata && { metadata: options.metadata }),\n };\n\n if (options.trialDays || connectedAccountId) {\n params.subscription_data = {};\n if (options.trialDays) {\n params.subscription_data.trial_period_days = options.trialDays;\n }\n if (connectedAccountId) {\n params.subscription_data.transfer_data = { destination: connectedAccountId };\n }\n }\n\n const session = await stripe.checkout.sessions.create(params);\n\n return { url: session.url!, sessionId: session.id };\n },\n\n /**\n * Handle Stripe webhook events with typed handlers.\n * Verifies the webhook signature and dispatches to your handler callbacks.\n */\n async handleWebhook(req: any, handlers: WebhookHandlers): Promise<void> {\n const stripe = getStripeInstance();\n const webhookSecret = getWebhookSecret();\n\n const sig = req.headers['stripe-signature'];\n const event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);\n\n switch (event.type) {\n case 'checkout.session.completed':\n await handlers.onCheckoutComplete?.(event.data.object as Stripe.Checkout.Session);\n break;\n case 'customer.subscription.created':\n await handlers.onSubscriptionCreated?.(event.data.object as Stripe.Subscription);\n break;\n case 'customer.subscription.updated':\n await handlers.onSubscriptionUpdated?.(event.data.object as Stripe.Subscription);\n break;\n case 'customer.subscription.deleted':\n await handlers.onSubscriptionDeleted?.(event.data.object as Stripe.Subscription);\n break;\n case 'invoice.payment_succeeded':\n await handlers.onPaymentSucceeded?.(event.data.object as Stripe.Invoice);\n break;\n case 'invoice.payment_failed':\n await handlers.onPaymentFailed?.(event.data.object as Stripe.Invoice);\n break;\n case 'charge.refunded':\n await handlers.onChargeRefunded?.(event.data.object as Stripe.Charge);\n break;\n }\n },\n\n /**\n * Express middleware for webhook routes.\n * Applies express.raw() body parser — mount BEFORE any json() middleware.\n */\n webhookMiddleware(): any {\n try {\n const express = require('express');\n return express.raw({ type: 'application/json' });\n } catch {\n return (_req: any, _res: any, next: any) => next();\n }\n },\n\n /**\n * Get the underlying Stripe instance for advanced usage.\n */\n getStripe(): Stripe {\n return getStripeInstance();\n },\n};\n"]}
1
+ {"version":3,"sources":["../../src/internal/env.ts","../../src/internal/config.ts","../../src/payments/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Environment variable helpers\n */\n\nexport function getEnv(key: string, fallback?: string): string | undefined {\n return process.env[key] || fallback;\n}\n\nexport function requireEnv(key: string): string {\n const val = process.env[key];\n if (!val) {\n throw new Error(`@coduckai/sdk: Missing required environment variable: ${key}`);\n }\n return val;\n}\n\nexport function isServer(): boolean {\n return typeof window === 'undefined';\n}\n\nexport function assertServer(moduleName: string): void {\n if (!isServer()) {\n throw new Error(\n `@coduckai/sdk/${moduleName} is server-only. Use @coduckai/sdk/client for browser code.`\n );\n }\n}\n","/**\n * Global SDK configuration\n *\n * CoDuck apps use env vars automatically — no configure() needed.\n * Standalone usage can call configure() to override.\n */\n\nexport interface SDKConfig {\n auth?: {\n jwtSecret?: string;\n jwtExpiresIn?: string;\n bcryptRounds?: number;\n };\n db?: {\n connectionString?: string;\n };\n client?: {\n baseUrl?: string;\n tokenKey?: string;\n };\n payments?: {\n stripeSecretKey?: string;\n connectedAccountId?: string;\n webhookSecret?: string;\n };\n ai?: {\n provider?: 'openai' | 'anthropic';\n apiKey?: string;\n };\n}\n\nlet globalConfig: SDKConfig = {};\n\nexport function setConfig(config: SDKConfig): void {\n globalConfig = { ...globalConfig, ...config };\n}\n\nexport function getConfig(): SDKConfig {\n return globalConfig;\n}\n","/**\n * @coduckai/sdk/payments — Stripe Payments (server-only)\n *\n * Wraps Stripe SDK with automatic Connect direct charges via stripeAccount header.\n * Auto-reads STRIPE_SECRET_KEY, STRIPE_CONNECTED_ACCOUNT_ID, STRIPE_WEBHOOK_SECRET from env.\n */\n\nimport { assertServer } from '../internal/env';\nimport { getConfig } from '../internal/config';\nimport Stripe from 'stripe';\n\nassertServer('payments');\n\n// ---------------------------------------------------------------------------\n// Lazy singleton Stripe instance\n// ---------------------------------------------------------------------------\n\nlet stripeInstance: Stripe | null = null;\n\nfunction getStripeInstance(): Stripe {\n if (stripeInstance) return stripeInstance;\n\n const config = getConfig();\n const secretKey = config.payments?.stripeSecretKey || process.env.STRIPE_SECRET_KEY;\n\n if (!secretKey) {\n throw new Error(\n '@coduckai/sdk/payments: STRIPE_SECRET_KEY is required. Set the env var or call configure().'\n );\n }\n\n stripeInstance = new Stripe(secretKey);\n return stripeInstance;\n}\n\nfunction getConnectedAccountId(): string | undefined {\n const config = getConfig();\n return config.payments?.connectedAccountId || process.env.STRIPE_CONNECTED_ACCOUNT_ID || undefined;\n}\n\nfunction getWebhookSecret(): string {\n const config = getConfig();\n const secret = config.payments?.webhookSecret || process.env.STRIPE_WEBHOOK_SECRET;\n if (!secret) {\n throw new Error(\n '@coduckai/sdk/payments: STRIPE_WEBHOOK_SECRET is required for webhook handling.'\n );\n }\n return secret;\n}\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface CheckoutItem {\n name: string;\n /** Price in dollars (e.g. 29.99) */\n price: number;\n quantity?: number;\n description?: string;\n image?: string;\n}\n\nexport interface CreateCheckoutOptions {\n items: CheckoutItem[];\n successUrl: string;\n cancelUrl: string;\n customerEmail?: string;\n metadata?: Record<string, string>;\n /** Application fee in cents charged on top of the payment (Connect only) */\n applicationFeeAmount?: number;\n}\n\nexport interface CreateSubscriptionOptions {\n priceId: string;\n successUrl: string;\n cancelUrl: string;\n customerEmail?: string;\n trialDays?: number;\n metadata?: Record<string, string>;\n /** Application fee percentage for recurring charges (0-100, Connect only) */\n applicationFeePercent?: number;\n}\n\nexport interface CheckoutResult {\n url: string;\n sessionId: string;\n}\n\nexport interface WebhookHandlers {\n onCheckoutComplete?: (session: Stripe.Checkout.Session) => Promise<void> | void;\n onSubscriptionCreated?: (subscription: Stripe.Subscription) => Promise<void> | void;\n onSubscriptionUpdated?: (subscription: Stripe.Subscription) => Promise<void> | void;\n onSubscriptionDeleted?: (subscription: Stripe.Subscription) => Promise<void> | void;\n onPaymentSucceeded?: (invoice: Stripe.Invoice) => Promise<void> | void;\n onPaymentFailed?: (invoice: Stripe.Invoice) => Promise<void> | void;\n onChargeRefunded?: (charge: Stripe.Charge) => Promise<void> | void;\n}\n\n// ---------------------------------------------------------------------------\n// Payments API\n// ---------------------------------------------------------------------------\n\nexport const payments = {\n /**\n * Create a Stripe Checkout Session for one-time payment.\n * Automatically uses direct charges (stripeAccount) when STRIPE_CONNECTED_ACCOUNT_ID is set.\n */\n async createCheckout(options: CreateCheckoutOptions): Promise<CheckoutResult> {\n const stripe = getStripeInstance();\n const connectedAccountId = getConnectedAccountId();\n\n const params: Stripe.Checkout.SessionCreateParams = {\n mode: 'payment',\n line_items: options.items.map(item => ({\n price_data: {\n currency: 'usd',\n product_data: {\n name: item.name,\n ...(item.description && { description: item.description }),\n ...(item.image && { images: [item.image] }),\n },\n unit_amount: Math.round(item.price * 100),\n },\n quantity: item.quantity || 1,\n })),\n success_url: options.successUrl,\n cancel_url: options.cancelUrl,\n ...(options.customerEmail && { customer_email: options.customerEmail }),\n ...(options.metadata && { metadata: options.metadata }),\n };\n\n if (connectedAccountId && options.applicationFeeAmount) {\n params.payment_intent_data = {\n application_fee_amount: options.applicationFeeAmount,\n };\n }\n\n const requestOptions: Stripe.RequestOptions | undefined = connectedAccountId\n ? { stripeAccount: connectedAccountId }\n : undefined;\n\n const session = await stripe.checkout.sessions.create(params, requestOptions);\n\n return { url: session.url!, sessionId: session.id };\n },\n\n /**\n * Create a Stripe Checkout Session for subscriptions.\n * Automatically uses direct charges (stripeAccount) when STRIPE_CONNECTED_ACCOUNT_ID is set.\n */\n async createSubscription(options: CreateSubscriptionOptions): Promise<CheckoutResult> {\n const stripe = getStripeInstance();\n const connectedAccountId = getConnectedAccountId();\n\n const params: Stripe.Checkout.SessionCreateParams = {\n mode: 'subscription',\n line_items: [{ price: options.priceId, quantity: 1 }],\n success_url: options.successUrl,\n cancel_url: options.cancelUrl,\n ...(options.customerEmail && { customer_email: options.customerEmail }),\n ...(options.metadata && { metadata: options.metadata }),\n };\n\n if (options.trialDays || (connectedAccountId && options.applicationFeePercent)) {\n params.subscription_data = {};\n if (options.trialDays) {\n params.subscription_data.trial_period_days = options.trialDays;\n }\n if (connectedAccountId && options.applicationFeePercent) {\n params.subscription_data.application_fee_percent = options.applicationFeePercent;\n }\n }\n\n const requestOptions: Stripe.RequestOptions | undefined = connectedAccountId\n ? { stripeAccount: connectedAccountId }\n : undefined;\n\n const session = await stripe.checkout.sessions.create(params, requestOptions);\n\n return { url: session.url!, sessionId: session.id };\n },\n\n /**\n * Handle Stripe webhook events with typed handlers.\n * Verifies the webhook signature and dispatches to your handler callbacks.\n */\n async handleWebhook(req: any, handlers: WebhookHandlers): Promise<void> {\n const stripe = getStripeInstance();\n const webhookSecret = getWebhookSecret();\n\n const sig = req.headers['stripe-signature'];\n const event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret);\n\n switch (event.type) {\n case 'checkout.session.completed':\n await handlers.onCheckoutComplete?.(event.data.object as Stripe.Checkout.Session);\n break;\n case 'customer.subscription.created':\n await handlers.onSubscriptionCreated?.(event.data.object as Stripe.Subscription);\n break;\n case 'customer.subscription.updated':\n await handlers.onSubscriptionUpdated?.(event.data.object as Stripe.Subscription);\n break;\n case 'customer.subscription.deleted':\n await handlers.onSubscriptionDeleted?.(event.data.object as Stripe.Subscription);\n break;\n case 'invoice.payment_succeeded':\n await handlers.onPaymentSucceeded?.(event.data.object as Stripe.Invoice);\n break;\n case 'invoice.payment_failed':\n await handlers.onPaymentFailed?.(event.data.object as Stripe.Invoice);\n break;\n case 'charge.refunded':\n await handlers.onChargeRefunded?.(event.data.object as Stripe.Charge);\n break;\n }\n },\n\n /**\n * Express middleware for webhook routes.\n * Applies express.raw() body parser — mount BEFORE any json() middleware.\n */\n webhookMiddleware(): any {\n try {\n const express = require('express');\n return express.raw({ type: 'application/json' });\n } catch {\n return (_req: any, _res: any, next: any) => next();\n }\n },\n\n /**\n * Get the underlying Stripe instance for advanced usage.\n */\n getStripe(): Stripe {\n return getStripeInstance();\n },\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coduckai/sdk",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "CoDuck platform SDK — auth, database, payments, AI, and more for generated apps",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",