@census-ai/census-sdk 0.5.0 → 0.5.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.
@@ -1,3 +1,3 @@
1
- 'use strict';var y="https://api.census.ai",u=class{constructor(t){if(!t.apiKey)throw new Error("Census: apiKey is required");["cs_live_","cs_test_","op_live_","op_test_"].some(r=>t.apiKey.startsWith(r))||console.warn('Census: API key should start with "cs_live_" or "cs_test_"'),this.apiKey=t.apiKey,this.baseUrl=t.baseUrl||y,this.debug=t.debug||false,this.log("Server client initialized with base URL:",this.baseUrl);}async syncUser(t){if(!t.userId)throw new Error("Census: userId is required for syncUser()");let e=await this.request("/api/sdk/sync","POST",this.prepareUserData(t));return this.log("User synced:",t.userId),e}async syncUsers(t,e){if(!t||t.length===0)return {success:true,created:0,updated:0,skipped:0,failed:0};if(t.length>1e3)throw new Error("Census: Maximum 1000 users per syncUsers() call. Split into multiple calls.");let r=t.filter(n=>!n.userId);if(r.length>0)throw new Error(`Census: ${r.length} users missing userId`);let i=await this.request("/api/sdk/sync/bulk","POST",{users:t.map(n=>this.prepareUserData(n)),options:{onConflict:e?.onConflict||"update",batchSize:e?.batchSize}});return this.log("Bulk sync complete:",`${i.created} created,`,`${i.updated} updated,`,`${i.failed} failed`),i}prepareUserData(t){let e={userId:t.userId,email:t.email,name:t.name,avatarUrl:t.avatarUrl,metadata:t.metadata,organizationId:t.organizationId,organizationName:t.organizationName,organizationDomain:t.organizationDomain,organizationPlan:t.organizationPlan,planName:t.planName,subscriptionStatus:t.subscriptionStatus,billingCycle:t.billingCycle,mrr:t.mrr,loginCount:t.loginCount};return t.subscriptionStartedAt&&(e.subscriptionStartedAt=this.toISOString(t.subscriptionStartedAt)),t.subscriptionEndsAt&&(e.subscriptionEndsAt=this.toISOString(t.subscriptionEndsAt)),t.trialEndsAt&&(e.trialEndsAt=this.toISOString(t.trialEndsAt)),t.signupAt&&(e.signupAt=this.toISOString(t.signupAt)),t.lastLoginAt&&(e.lastLoginAt=this.toISOString(t.lastLoginAt)),Object.fromEntries(Object.entries(e).filter(([,r])=>r!==void 0))}toISOString(t){return t instanceof Date?t.toISOString():t}async request(t,e,r){let i=`${this.baseUrl}${t}`,n=1;for(let a=0;a<=n;a++){let l=new AbortController,d=setTimeout(()=>l.abort(),3e4);try{let o={"X-Census-Key":this.apiKey,"Content-Type":"application/json"};this.log(`${e} ${t}`,r);let s=await fetch(i,{method:e,headers:o,body:r?JSON.stringify(r):void 0,signal:l.signal});if(!s.ok){if(s.status>=500&&a<n){this.log(`Retrying ${e} ${t} after ${s.status}`),await new Promise(g=>setTimeout(g,1e3*(a+1)));continue}let c=`Request failed with status ${s.status}`;try{c=(await s.json()).error||c;}catch{}throw {error:c,status:s.status}}return s.json()}catch(o){if(o&&typeof o=="object"&&"error"in o)throw o;if(a<n){this.log(`Retrying ${e} ${t} after network error`),await new Promise(c=>setTimeout(c,1e3*(a+1)));continue}throw {error:l.signal.aborted?`Request timed out after 30s: ${e} ${t}`:`Network error: ${e} ${t}`}}finally{clearTimeout(d);}}throw {error:"Unexpected error",status:500}}log(...t){this.debug&&console.log("[Census Server]",...t);}};function f(p){return new u(p)}
1
+ 'use strict';var y="https://census-api-production-97c0.up.railway.app",u=class{constructor(t){if(!t.apiKey)throw new Error("Census: apiKey is required");["cs_live_","cs_test_","op_live_","op_test_"].some(r=>t.apiKey.startsWith(r))||console.warn('Census: API key should start with "cs_live_" or "cs_test_"'),this.apiKey=t.apiKey,this.baseUrl=t.baseUrl||y,this.debug=t.debug||false,this.log("Server client initialized with base URL:",this.baseUrl);}async syncUser(t){if(!t.userId)throw new Error("Census: userId is required for syncUser()");let e=await this.request("/api/sdk/sync","POST",this.prepareUserData(t));return this.log("User synced:",t.userId),e}async syncUsers(t,e){if(!t||t.length===0)return {success:true,created:0,updated:0,skipped:0,failed:0};if(t.length>1e3)throw new Error("Census: Maximum 1000 users per syncUsers() call. Split into multiple calls.");let r=t.filter(n=>!n.userId);if(r.length>0)throw new Error(`Census: ${r.length} users missing userId`);let i=await this.request("/api/sdk/sync/bulk","POST",{users:t.map(n=>this.prepareUserData(n)),options:{onConflict:e?.onConflict||"update",batchSize:e?.batchSize}});return this.log("Bulk sync complete:",`${i.created} created,`,`${i.updated} updated,`,`${i.failed} failed`),i}prepareUserData(t){let e={userId:t.userId,email:t.email,name:t.name,avatarUrl:t.avatarUrl,metadata:t.metadata,organizationId:t.organizationId,organizationName:t.organizationName,organizationDomain:t.organizationDomain,organizationPlan:t.organizationPlan,planName:t.planName,subscriptionStatus:t.subscriptionStatus,billingCycle:t.billingCycle,mrr:t.mrr,loginCount:t.loginCount};return t.subscriptionStartedAt&&(e.subscriptionStartedAt=this.toISOString(t.subscriptionStartedAt)),t.subscriptionEndsAt&&(e.subscriptionEndsAt=this.toISOString(t.subscriptionEndsAt)),t.trialEndsAt&&(e.trialEndsAt=this.toISOString(t.trialEndsAt)),t.signupAt&&(e.signupAt=this.toISOString(t.signupAt)),t.lastLoginAt&&(e.lastLoginAt=this.toISOString(t.lastLoginAt)),Object.fromEntries(Object.entries(e).filter(([,r])=>r!==void 0))}toISOString(t){return t instanceof Date?t.toISOString():t}async request(t,e,r){let i=`${this.baseUrl}${t}`,n=1;for(let a=0;a<=n;a++){let l=new AbortController,d=setTimeout(()=>l.abort(),3e4);try{let o={"X-Census-Key":this.apiKey,"Content-Type":"application/json"};this.log(`${e} ${t}`,r);let s=await fetch(i,{method:e,headers:o,body:r?JSON.stringify(r):void 0,signal:l.signal});if(!s.ok){if(s.status>=500&&a<n){this.log(`Retrying ${e} ${t} after ${s.status}`),await new Promise(g=>setTimeout(g,1e3*(a+1)));continue}let c=`Request failed with status ${s.status}`;try{c=(await s.json()).error||c;}catch{}throw {error:c,status:s.status}}return s.json()}catch(o){if(o&&typeof o=="object"&&"error"in o)throw o;if(a<n){this.log(`Retrying ${e} ${t} after network error`),await new Promise(c=>setTimeout(c,1e3*(a+1)));continue}throw {error:l.signal.aborted?`Request timed out after 30s: ${e} ${t}`:`Network error: ${e} ${t}`}}finally{clearTimeout(d);}}throw {error:"Unexpected error",status:500}}log(...t){this.debug&&console.log("[Census Server]",...t);}};function f(p){return new u(p)}
2
2
  exports.CensusServerClient=u;exports.createCensusServer=f;//# sourceMappingURL=server.cjs.map
3
3
  //# sourceMappingURL=server.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server.ts"],"names":["DEFAULT_BASE_URL","CensusServerClient","config","prefix","user","response","users","options","invalidUsers","u","data","v","value","path","method","body","url","maxRetries","attempt","controller","timeoutId","headers","r","errorMessage","err","args","createCensusServer"],"mappings":"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","file":"server.cjs","sourcesContent":["/**\n * @census-ai/census-sdk/server - Server-side Census SDK\n *\n * For backend use cases: sync users from your server, track subscription changes,\n * and bulk import users.\n *\n * @example\n * ```typescript\n * import { createCensusServer } from '@census-ai/census-sdk/server';\n *\n * const census = createCensusServer({ apiKey: 'cs_live_xxx' });\n *\n * // Sync a single user (e.g., on signup or subscription change)\n * await census.syncUser({\n * userId: 'user_123',\n * email: 'john@example.com',\n * planName: 'Pro',\n * subscriptionStatus: 'active',\n * mrr: 9900, // $99.00 in cents\n * signupAt: new Date(),\n * });\n *\n * // Bulk sync users (e.g., initial import)\n * const result = await census.syncUsers(allUsers, { batchSize: 100 });\n * console.log(`Created: ${result.created}, Updated: ${result.updated}`);\n * ```\n */\n\nimport type {\n CensusConfig,\n SyncUserData,\n SyncUsersOptions,\n SyncResult,\n SyncUsersResult,\n CensusError,\n} from './types';\n\n/**\n * Default API base URL\n */\nconst DEFAULT_BASE_URL = 'https://api.census.ai';\n\n/**\n * Census Server SDK Client\n *\n * A server-side client for syncing user data from your backend.\n * Use `createCensusServer()` to create an instance.\n *\n * This client is designed for:\n * - Backend services (Node.js, Bun, Deno, Edge runtimes)\n * - Syncing subscription/billing data from your payment processor\n * - Bulk importing users from your database\n * - Tracking server-side events\n *\n * For client-side usage, use `createCensus()` from '@census-ai/census-sdk'.\n */\nexport class CensusServerClient {\n private apiKey: string;\n private baseUrl: string;\n private debug: boolean;\n\n constructor(config: CensusConfig) {\n if (!config.apiKey) {\n throw new Error('Census: apiKey is required');\n }\n\n // Support both new (cs_) and legacy (op_) key prefixes\n const validPrefixes = ['cs_live_', 'cs_test_', 'op_live_', 'op_test_'];\n if (!validPrefixes.some(prefix => config.apiKey.startsWith(prefix))) {\n console.warn('Census: API key should start with \"cs_live_\" or \"cs_test_\"');\n }\n\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;\n this.debug = config.debug || false;\n\n this.log('Server client initialized with base URL:', this.baseUrl);\n }\n\n /**\n * Sync a single user's data to Census.\n *\n * Call this when:\n * - A user signs up\n * - Subscription status changes (upgrade, downgrade, cancel)\n * - User profile is updated\n * - Login activity should be recorded\n *\n * @param user - User data to sync\n * @returns Sync result with user ID\n *\n * @example\n * ```typescript\n * // On user signup\n * await census.syncUser({\n * userId: 'user_123',\n * email: 'john@example.com',\n * name: 'John Doe',\n * signupAt: new Date(),\n * subscriptionStatus: 'trial',\n * trialEndsAt: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000),\n * });\n *\n * // On subscription purchase\n * await census.syncUser({\n * userId: 'user_123',\n * planName: 'Pro',\n * subscriptionStatus: 'active',\n * billingCycle: 'monthly',\n * mrr: 9900, // $99.00 in cents\n * subscriptionStartedAt: new Date(),\n * });\n *\n * // On cancellation\n * await census.syncUser({\n * userId: 'user_123',\n * subscriptionStatus: 'canceled',\n * subscriptionEndsAt: new Date('2024-02-01'),\n * });\n * ```\n */\n async syncUser(user: SyncUserData): Promise<SyncResult> {\n if (!user.userId) {\n throw new Error('Census: userId is required for syncUser()');\n }\n\n const response = await this.request<SyncResult>(\n '/api/sdk/sync',\n 'POST',\n this.prepareUserData(user)\n );\n\n this.log('User synced:', user.userId);\n return response;\n }\n\n /**\n * Bulk sync multiple users to Census.\n *\n * Use this for:\n * - Initial import of existing users\n * - Periodic full sync from your database\n * - Batch updates after bulk operations\n *\n * @param users - Array of users to sync (max 1000 per request)\n * @param options - Sync options\n * @returns Bulk sync result with counts\n *\n * @example\n * ```typescript\n * // Initial import\n * const allUsers = await db.users.findAll();\n *\n * const result = await census.syncUsers(\n * allUsers.map(u => ({\n * userId: u.id,\n * email: u.email,\n * name: u.name,\n * planName: u.subscription?.plan,\n * subscriptionStatus: u.subscription?.status,\n * mrr: u.subscription?.mrr,\n * signupAt: u.createdAt,\n * lastLoginAt: u.lastLogin,\n * loginCount: u.loginCount,\n * })),\n * { batchSize: 100 }\n * );\n *\n * console.log(`Synced: ${result.created} created, ${result.updated} updated`);\n * if (result.errors?.length) {\n * console.error('Errors:', result.errors);\n * }\n * ```\n */\n async syncUsers(\n users: SyncUserData[],\n options?: SyncUsersOptions\n ): Promise<SyncUsersResult> {\n if (!users || users.length === 0) {\n return {\n success: true,\n created: 0,\n updated: 0,\n skipped: 0,\n failed: 0,\n };\n }\n\n if (users.length > 1000) {\n throw new Error('Census: Maximum 1000 users per syncUsers() call. Split into multiple calls.');\n }\n\n // Validate all users have userId\n const invalidUsers = users.filter(u => !u.userId);\n if (invalidUsers.length > 0) {\n throw new Error(`Census: ${invalidUsers.length} users missing userId`);\n }\n\n const response = await this.request<SyncUsersResult>(\n '/api/sdk/sync/bulk',\n 'POST',\n {\n users: users.map(u => this.prepareUserData(u)),\n options: {\n onConflict: options?.onConflict || 'update',\n batchSize: options?.batchSize,\n },\n }\n );\n\n this.log(\n 'Bulk sync complete:',\n `${response.created} created,`,\n `${response.updated} updated,`,\n `${response.failed} failed`\n );\n\n return response;\n }\n\n /**\n * Prepare user data for API request, converting dates to ISO strings\n */\n private prepareUserData(user: SyncUserData): Record<string, unknown> {\n const data: Record<string, unknown> = {\n userId: user.userId,\n email: user.email,\n name: user.name,\n avatarUrl: user.avatarUrl,\n metadata: user.metadata,\n organizationId: user.organizationId,\n organizationName: user.organizationName,\n organizationDomain: user.organizationDomain,\n organizationPlan: user.organizationPlan,\n planName: user.planName,\n subscriptionStatus: user.subscriptionStatus,\n billingCycle: user.billingCycle,\n mrr: user.mrr,\n loginCount: user.loginCount,\n };\n\n // Convert dates to ISO strings\n if (user.subscriptionStartedAt) {\n data.subscriptionStartedAt = this.toISOString(user.subscriptionStartedAt);\n }\n if (user.subscriptionEndsAt) {\n data.subscriptionEndsAt = this.toISOString(user.subscriptionEndsAt);\n }\n if (user.trialEndsAt) {\n data.trialEndsAt = this.toISOString(user.trialEndsAt);\n }\n if (user.signupAt) {\n data.signupAt = this.toISOString(user.signupAt);\n }\n if (user.lastLoginAt) {\n data.lastLoginAt = this.toISOString(user.lastLoginAt);\n }\n\n // Remove undefined values\n return Object.fromEntries(\n Object.entries(data).filter(([, v]) => v !== undefined)\n );\n }\n\n /**\n * Convert Date or string to ISO string\n */\n private toISOString(value: string | Date): string {\n if (value instanceof Date) {\n return value.toISOString();\n }\n return value;\n }\n\n /**\n * Make an API request\n */\n private async request<T>(\n path: string,\n method: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const maxRetries = 1;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 30_000);\n\n try {\n const headers: Record<string, string> = {\n 'X-Census-Key': this.apiKey,\n 'Content-Type': 'application/json',\n };\n\n this.log(`${method} ${path}`, body);\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n // Retry on 5xx errors\n if (response.status >= 500 && attempt < maxRetries) {\n this.log(`Retrying ${method} ${path} after ${response.status}`);\n await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));\n continue;\n }\n\n let errorMessage = `Request failed with status ${response.status}`;\n try {\n const errorData = await response.json();\n errorMessage = errorData.error || errorMessage;\n } catch {\n // Use default error message\n }\n\n const error: CensusError = {\n error: errorMessage,\n status: response.status,\n };\n throw error;\n }\n\n return response.json();\n } catch (err) {\n if (err && typeof err === 'object' && 'error' in err) throw err;\n\n // Retry on network errors\n if (attempt < maxRetries) {\n this.log(`Retrying ${method} ${path} after network error`);\n await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));\n continue;\n }\n\n const error: CensusError = {\n error: controller.signal.aborted\n ? `Request timed out after 30s: ${method} ${path}`\n : `Network error: ${method} ${path}`,\n };\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n // TypeScript exhaustiveness — unreachable\n throw { error: 'Unexpected error', status: 500 } as CensusError;\n }\n\n /**\n * Log debug messages\n */\n private log(...args: unknown[]): void {\n if (this.debug) {\n console.log('[Census Server]', ...args);\n }\n }\n}\n\n/**\n * Create a new Census Server SDK client.\n *\n * @param config - Configuration options\n * @returns Census server client instance\n *\n * @example\n * ```typescript\n * import { createCensusServer } from '@census-ai/census-sdk/server';\n *\n * const census = createCensusServer({\n * apiKey: 'cs_live_your_key_here',\n * debug: true, // Enable debug logging\n * });\n *\n * // Sync user on subscription change\n * await census.syncUser({\n * userId: 'user_123',\n * planName: 'Pro',\n * subscriptionStatus: 'active',\n * mrr: 9900,\n * });\n * ```\n */\nexport function createCensusServer(config: CensusConfig): CensusServerClient {\n return new CensusServerClient(config);\n}\n\n// Re-export types needed for server usage\nexport type {\n CensusConfig,\n SyncUserData,\n SyncUsersOptions,\n SyncResult,\n SyncUsersResult,\n CensusError,\n} from './types';\n"]}
1
+ {"version":3,"sources":["../../src/server.ts"],"names":["DEFAULT_BASE_URL","CensusServerClient","config","prefix","user","response","users","options","invalidUsers","u","data","v","value","path","method","body","url","maxRetries","attempt","controller","timeoutId","headers","r","errorMessage","err","args","createCensusServer"],"mappings":"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","file":"server.cjs","sourcesContent":["/**\n * @census-ai/census-sdk/server - Server-side Census SDK\n *\n * For backend use cases: sync users from your server, track subscription changes,\n * and bulk import users.\n *\n * @example\n * ```typescript\n * import { createCensusServer } from '@census-ai/census-sdk/server';\n *\n * const census = createCensusServer({ apiKey: 'cs_live_xxx' });\n *\n * // Sync a single user (e.g., on signup or subscription change)\n * await census.syncUser({\n * userId: 'user_123',\n * email: 'john@example.com',\n * planName: 'Pro',\n * subscriptionStatus: 'active',\n * mrr: 9900, // $99.00 in cents\n * signupAt: new Date(),\n * });\n *\n * // Bulk sync users (e.g., initial import)\n * const result = await census.syncUsers(allUsers, { batchSize: 100 });\n * console.log(`Created: ${result.created}, Updated: ${result.updated}`);\n * ```\n */\n\nimport type {\n CensusConfig,\n SyncUserData,\n SyncUsersOptions,\n SyncResult,\n SyncUsersResult,\n CensusError,\n} from './types';\n\n/**\n * Default API base URL\n */\nconst DEFAULT_BASE_URL = 'https://census-api-production-97c0.up.railway.app';\n\n/**\n * Census Server SDK Client\n *\n * A server-side client for syncing user data from your backend.\n * Use `createCensusServer()` to create an instance.\n *\n * This client is designed for:\n * - Backend services (Node.js, Bun, Deno, Edge runtimes)\n * - Syncing subscription/billing data from your payment processor\n * - Bulk importing users from your database\n * - Tracking server-side events\n *\n * For client-side usage, use `createCensus()` from '@census-ai/census-sdk'.\n */\nexport class CensusServerClient {\n private apiKey: string;\n private baseUrl: string;\n private debug: boolean;\n\n constructor(config: CensusConfig) {\n if (!config.apiKey) {\n throw new Error('Census: apiKey is required');\n }\n\n // Support both new (cs_) and legacy (op_) key prefixes\n const validPrefixes = ['cs_live_', 'cs_test_', 'op_live_', 'op_test_'];\n if (!validPrefixes.some(prefix => config.apiKey.startsWith(prefix))) {\n console.warn('Census: API key should start with \"cs_live_\" or \"cs_test_\"');\n }\n\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;\n this.debug = config.debug || false;\n\n this.log('Server client initialized with base URL:', this.baseUrl);\n }\n\n /**\n * Sync a single user's data to Census.\n *\n * Call this when:\n * - A user signs up\n * - Subscription status changes (upgrade, downgrade, cancel)\n * - User profile is updated\n * - Login activity should be recorded\n *\n * @param user - User data to sync\n * @returns Sync result with user ID\n *\n * @example\n * ```typescript\n * // On user signup\n * await census.syncUser({\n * userId: 'user_123',\n * email: 'john@example.com',\n * name: 'John Doe',\n * signupAt: new Date(),\n * subscriptionStatus: 'trial',\n * trialEndsAt: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000),\n * });\n *\n * // On subscription purchase\n * await census.syncUser({\n * userId: 'user_123',\n * planName: 'Pro',\n * subscriptionStatus: 'active',\n * billingCycle: 'monthly',\n * mrr: 9900, // $99.00 in cents\n * subscriptionStartedAt: new Date(),\n * });\n *\n * // On cancellation\n * await census.syncUser({\n * userId: 'user_123',\n * subscriptionStatus: 'canceled',\n * subscriptionEndsAt: new Date('2024-02-01'),\n * });\n * ```\n */\n async syncUser(user: SyncUserData): Promise<SyncResult> {\n if (!user.userId) {\n throw new Error('Census: userId is required for syncUser()');\n }\n\n const response = await this.request<SyncResult>(\n '/api/sdk/sync',\n 'POST',\n this.prepareUserData(user)\n );\n\n this.log('User synced:', user.userId);\n return response;\n }\n\n /**\n * Bulk sync multiple users to Census.\n *\n * Use this for:\n * - Initial import of existing users\n * - Periodic full sync from your database\n * - Batch updates after bulk operations\n *\n * @param users - Array of users to sync (max 1000 per request)\n * @param options - Sync options\n * @returns Bulk sync result with counts\n *\n * @example\n * ```typescript\n * // Initial import\n * const allUsers = await db.users.findAll();\n *\n * const result = await census.syncUsers(\n * allUsers.map(u => ({\n * userId: u.id,\n * email: u.email,\n * name: u.name,\n * planName: u.subscription?.plan,\n * subscriptionStatus: u.subscription?.status,\n * mrr: u.subscription?.mrr,\n * signupAt: u.createdAt,\n * lastLoginAt: u.lastLogin,\n * loginCount: u.loginCount,\n * })),\n * { batchSize: 100 }\n * );\n *\n * console.log(`Synced: ${result.created} created, ${result.updated} updated`);\n * if (result.errors?.length) {\n * console.error('Errors:', result.errors);\n * }\n * ```\n */\n async syncUsers(\n users: SyncUserData[],\n options?: SyncUsersOptions\n ): Promise<SyncUsersResult> {\n if (!users || users.length === 0) {\n return {\n success: true,\n created: 0,\n updated: 0,\n skipped: 0,\n failed: 0,\n };\n }\n\n if (users.length > 1000) {\n throw new Error('Census: Maximum 1000 users per syncUsers() call. Split into multiple calls.');\n }\n\n // Validate all users have userId\n const invalidUsers = users.filter(u => !u.userId);\n if (invalidUsers.length > 0) {\n throw new Error(`Census: ${invalidUsers.length} users missing userId`);\n }\n\n const response = await this.request<SyncUsersResult>(\n '/api/sdk/sync/bulk',\n 'POST',\n {\n users: users.map(u => this.prepareUserData(u)),\n options: {\n onConflict: options?.onConflict || 'update',\n batchSize: options?.batchSize,\n },\n }\n );\n\n this.log(\n 'Bulk sync complete:',\n `${response.created} created,`,\n `${response.updated} updated,`,\n `${response.failed} failed`\n );\n\n return response;\n }\n\n /**\n * Prepare user data for API request, converting dates to ISO strings\n */\n private prepareUserData(user: SyncUserData): Record<string, unknown> {\n const data: Record<string, unknown> = {\n userId: user.userId,\n email: user.email,\n name: user.name,\n avatarUrl: user.avatarUrl,\n metadata: user.metadata,\n organizationId: user.organizationId,\n organizationName: user.organizationName,\n organizationDomain: user.organizationDomain,\n organizationPlan: user.organizationPlan,\n planName: user.planName,\n subscriptionStatus: user.subscriptionStatus,\n billingCycle: user.billingCycle,\n mrr: user.mrr,\n loginCount: user.loginCount,\n };\n\n // Convert dates to ISO strings\n if (user.subscriptionStartedAt) {\n data.subscriptionStartedAt = this.toISOString(user.subscriptionStartedAt);\n }\n if (user.subscriptionEndsAt) {\n data.subscriptionEndsAt = this.toISOString(user.subscriptionEndsAt);\n }\n if (user.trialEndsAt) {\n data.trialEndsAt = this.toISOString(user.trialEndsAt);\n }\n if (user.signupAt) {\n data.signupAt = this.toISOString(user.signupAt);\n }\n if (user.lastLoginAt) {\n data.lastLoginAt = this.toISOString(user.lastLoginAt);\n }\n\n // Remove undefined values\n return Object.fromEntries(\n Object.entries(data).filter(([, v]) => v !== undefined)\n );\n }\n\n /**\n * Convert Date or string to ISO string\n */\n private toISOString(value: string | Date): string {\n if (value instanceof Date) {\n return value.toISOString();\n }\n return value;\n }\n\n /**\n * Make an API request\n */\n private async request<T>(\n path: string,\n method: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const maxRetries = 1;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 30_000);\n\n try {\n const headers: Record<string, string> = {\n 'X-Census-Key': this.apiKey,\n 'Content-Type': 'application/json',\n };\n\n this.log(`${method} ${path}`, body);\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n // Retry on 5xx errors\n if (response.status >= 500 && attempt < maxRetries) {\n this.log(`Retrying ${method} ${path} after ${response.status}`);\n await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));\n continue;\n }\n\n let errorMessage = `Request failed with status ${response.status}`;\n try {\n const errorData = await response.json();\n errorMessage = errorData.error || errorMessage;\n } catch {\n // Use default error message\n }\n\n const error: CensusError = {\n error: errorMessage,\n status: response.status,\n };\n throw error;\n }\n\n return response.json();\n } catch (err) {\n if (err && typeof err === 'object' && 'error' in err) throw err;\n\n // Retry on network errors\n if (attempt < maxRetries) {\n this.log(`Retrying ${method} ${path} after network error`);\n await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));\n continue;\n }\n\n const error: CensusError = {\n error: controller.signal.aborted\n ? `Request timed out after 30s: ${method} ${path}`\n : `Network error: ${method} ${path}`,\n };\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n // TypeScript exhaustiveness — unreachable\n throw { error: 'Unexpected error', status: 500 } as CensusError;\n }\n\n /**\n * Log debug messages\n */\n private log(...args: unknown[]): void {\n if (this.debug) {\n console.log('[Census Server]', ...args);\n }\n }\n}\n\n/**\n * Create a new Census Server SDK client.\n *\n * @param config - Configuration options\n * @returns Census server client instance\n *\n * @example\n * ```typescript\n * import { createCensusServer } from '@census-ai/census-sdk/server';\n *\n * const census = createCensusServer({\n * apiKey: 'cs_live_your_key_here',\n * debug: true, // Enable debug logging\n * });\n *\n * // Sync user on subscription change\n * await census.syncUser({\n * userId: 'user_123',\n * planName: 'Pro',\n * subscriptionStatus: 'active',\n * mrr: 9900,\n * });\n * ```\n */\nexport function createCensusServer(config: CensusConfig): CensusServerClient {\n return new CensusServerClient(config);\n}\n\n// Re-export types needed for server usage\nexport type {\n CensusConfig,\n SyncUserData,\n SyncUsersOptions,\n SyncResult,\n SyncUsersResult,\n CensusError,\n} from './types';\n"]}
@@ -1,3 +1,3 @@
1
- var y="https://api.census.ai",u=class{constructor(t){if(!t.apiKey)throw new Error("Census: apiKey is required");["cs_live_","cs_test_","op_live_","op_test_"].some(r=>t.apiKey.startsWith(r))||console.warn('Census: API key should start with "cs_live_" or "cs_test_"'),this.apiKey=t.apiKey,this.baseUrl=t.baseUrl||y,this.debug=t.debug||false,this.log("Server client initialized with base URL:",this.baseUrl);}async syncUser(t){if(!t.userId)throw new Error("Census: userId is required for syncUser()");let e=await this.request("/api/sdk/sync","POST",this.prepareUserData(t));return this.log("User synced:",t.userId),e}async syncUsers(t,e){if(!t||t.length===0)return {success:true,created:0,updated:0,skipped:0,failed:0};if(t.length>1e3)throw new Error("Census: Maximum 1000 users per syncUsers() call. Split into multiple calls.");let r=t.filter(n=>!n.userId);if(r.length>0)throw new Error(`Census: ${r.length} users missing userId`);let i=await this.request("/api/sdk/sync/bulk","POST",{users:t.map(n=>this.prepareUserData(n)),options:{onConflict:e?.onConflict||"update",batchSize:e?.batchSize}});return this.log("Bulk sync complete:",`${i.created} created,`,`${i.updated} updated,`,`${i.failed} failed`),i}prepareUserData(t){let e={userId:t.userId,email:t.email,name:t.name,avatarUrl:t.avatarUrl,metadata:t.metadata,organizationId:t.organizationId,organizationName:t.organizationName,organizationDomain:t.organizationDomain,organizationPlan:t.organizationPlan,planName:t.planName,subscriptionStatus:t.subscriptionStatus,billingCycle:t.billingCycle,mrr:t.mrr,loginCount:t.loginCount};return t.subscriptionStartedAt&&(e.subscriptionStartedAt=this.toISOString(t.subscriptionStartedAt)),t.subscriptionEndsAt&&(e.subscriptionEndsAt=this.toISOString(t.subscriptionEndsAt)),t.trialEndsAt&&(e.trialEndsAt=this.toISOString(t.trialEndsAt)),t.signupAt&&(e.signupAt=this.toISOString(t.signupAt)),t.lastLoginAt&&(e.lastLoginAt=this.toISOString(t.lastLoginAt)),Object.fromEntries(Object.entries(e).filter(([,r])=>r!==void 0))}toISOString(t){return t instanceof Date?t.toISOString():t}async request(t,e,r){let i=`${this.baseUrl}${t}`,n=1;for(let a=0;a<=n;a++){let l=new AbortController,d=setTimeout(()=>l.abort(),3e4);try{let o={"X-Census-Key":this.apiKey,"Content-Type":"application/json"};this.log(`${e} ${t}`,r);let s=await fetch(i,{method:e,headers:o,body:r?JSON.stringify(r):void 0,signal:l.signal});if(!s.ok){if(s.status>=500&&a<n){this.log(`Retrying ${e} ${t} after ${s.status}`),await new Promise(g=>setTimeout(g,1e3*(a+1)));continue}let c=`Request failed with status ${s.status}`;try{c=(await s.json()).error||c;}catch{}throw {error:c,status:s.status}}return s.json()}catch(o){if(o&&typeof o=="object"&&"error"in o)throw o;if(a<n){this.log(`Retrying ${e} ${t} after network error`),await new Promise(c=>setTimeout(c,1e3*(a+1)));continue}throw {error:l.signal.aborted?`Request timed out after 30s: ${e} ${t}`:`Network error: ${e} ${t}`}}finally{clearTimeout(d);}}throw {error:"Unexpected error",status:500}}log(...t){this.debug&&console.log("[Census Server]",...t);}};function f(p){return new u(p)}
1
+ var y="https://census-api-production-97c0.up.railway.app",u=class{constructor(t){if(!t.apiKey)throw new Error("Census: apiKey is required");["cs_live_","cs_test_","op_live_","op_test_"].some(r=>t.apiKey.startsWith(r))||console.warn('Census: API key should start with "cs_live_" or "cs_test_"'),this.apiKey=t.apiKey,this.baseUrl=t.baseUrl||y,this.debug=t.debug||false,this.log("Server client initialized with base URL:",this.baseUrl);}async syncUser(t){if(!t.userId)throw new Error("Census: userId is required for syncUser()");let e=await this.request("/api/sdk/sync","POST",this.prepareUserData(t));return this.log("User synced:",t.userId),e}async syncUsers(t,e){if(!t||t.length===0)return {success:true,created:0,updated:0,skipped:0,failed:0};if(t.length>1e3)throw new Error("Census: Maximum 1000 users per syncUsers() call. Split into multiple calls.");let r=t.filter(n=>!n.userId);if(r.length>0)throw new Error(`Census: ${r.length} users missing userId`);let i=await this.request("/api/sdk/sync/bulk","POST",{users:t.map(n=>this.prepareUserData(n)),options:{onConflict:e?.onConflict||"update",batchSize:e?.batchSize}});return this.log("Bulk sync complete:",`${i.created} created,`,`${i.updated} updated,`,`${i.failed} failed`),i}prepareUserData(t){let e={userId:t.userId,email:t.email,name:t.name,avatarUrl:t.avatarUrl,metadata:t.metadata,organizationId:t.organizationId,organizationName:t.organizationName,organizationDomain:t.organizationDomain,organizationPlan:t.organizationPlan,planName:t.planName,subscriptionStatus:t.subscriptionStatus,billingCycle:t.billingCycle,mrr:t.mrr,loginCount:t.loginCount};return t.subscriptionStartedAt&&(e.subscriptionStartedAt=this.toISOString(t.subscriptionStartedAt)),t.subscriptionEndsAt&&(e.subscriptionEndsAt=this.toISOString(t.subscriptionEndsAt)),t.trialEndsAt&&(e.trialEndsAt=this.toISOString(t.trialEndsAt)),t.signupAt&&(e.signupAt=this.toISOString(t.signupAt)),t.lastLoginAt&&(e.lastLoginAt=this.toISOString(t.lastLoginAt)),Object.fromEntries(Object.entries(e).filter(([,r])=>r!==void 0))}toISOString(t){return t instanceof Date?t.toISOString():t}async request(t,e,r){let i=`${this.baseUrl}${t}`,n=1;for(let a=0;a<=n;a++){let l=new AbortController,d=setTimeout(()=>l.abort(),3e4);try{let o={"X-Census-Key":this.apiKey,"Content-Type":"application/json"};this.log(`${e} ${t}`,r);let s=await fetch(i,{method:e,headers:o,body:r?JSON.stringify(r):void 0,signal:l.signal});if(!s.ok){if(s.status>=500&&a<n){this.log(`Retrying ${e} ${t} after ${s.status}`),await new Promise(g=>setTimeout(g,1e3*(a+1)));continue}let c=`Request failed with status ${s.status}`;try{c=(await s.json()).error||c;}catch{}throw {error:c,status:s.status}}return s.json()}catch(o){if(o&&typeof o=="object"&&"error"in o)throw o;if(a<n){this.log(`Retrying ${e} ${t} after network error`),await new Promise(c=>setTimeout(c,1e3*(a+1)));continue}throw {error:l.signal.aborted?`Request timed out after 30s: ${e} ${t}`:`Network error: ${e} ${t}`}}finally{clearTimeout(d);}}throw {error:"Unexpected error",status:500}}log(...t){this.debug&&console.log("[Census Server]",...t);}};function f(p){return new u(p)}
2
2
  export{u as CensusServerClient,f as createCensusServer};//# sourceMappingURL=server.js.map
3
3
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server.ts"],"names":["DEFAULT_BASE_URL","CensusServerClient","config","prefix","user","response","users","options","invalidUsers","u","data","v","value","path","method","body","url","maxRetries","attempt","controller","timeoutId","headers","r","errorMessage","err","args","createCensusServer"],"mappings":"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","file":"server.js","sourcesContent":["/**\n * @census-ai/census-sdk/server - Server-side Census SDK\n *\n * For backend use cases: sync users from your server, track subscription changes,\n * and bulk import users.\n *\n * @example\n * ```typescript\n * import { createCensusServer } from '@census-ai/census-sdk/server';\n *\n * const census = createCensusServer({ apiKey: 'cs_live_xxx' });\n *\n * // Sync a single user (e.g., on signup or subscription change)\n * await census.syncUser({\n * userId: 'user_123',\n * email: 'john@example.com',\n * planName: 'Pro',\n * subscriptionStatus: 'active',\n * mrr: 9900, // $99.00 in cents\n * signupAt: new Date(),\n * });\n *\n * // Bulk sync users (e.g., initial import)\n * const result = await census.syncUsers(allUsers, { batchSize: 100 });\n * console.log(`Created: ${result.created}, Updated: ${result.updated}`);\n * ```\n */\n\nimport type {\n CensusConfig,\n SyncUserData,\n SyncUsersOptions,\n SyncResult,\n SyncUsersResult,\n CensusError,\n} from './types';\n\n/**\n * Default API base URL\n */\nconst DEFAULT_BASE_URL = 'https://api.census.ai';\n\n/**\n * Census Server SDK Client\n *\n * A server-side client for syncing user data from your backend.\n * Use `createCensusServer()` to create an instance.\n *\n * This client is designed for:\n * - Backend services (Node.js, Bun, Deno, Edge runtimes)\n * - Syncing subscription/billing data from your payment processor\n * - Bulk importing users from your database\n * - Tracking server-side events\n *\n * For client-side usage, use `createCensus()` from '@census-ai/census-sdk'.\n */\nexport class CensusServerClient {\n private apiKey: string;\n private baseUrl: string;\n private debug: boolean;\n\n constructor(config: CensusConfig) {\n if (!config.apiKey) {\n throw new Error('Census: apiKey is required');\n }\n\n // Support both new (cs_) and legacy (op_) key prefixes\n const validPrefixes = ['cs_live_', 'cs_test_', 'op_live_', 'op_test_'];\n if (!validPrefixes.some(prefix => config.apiKey.startsWith(prefix))) {\n console.warn('Census: API key should start with \"cs_live_\" or \"cs_test_\"');\n }\n\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;\n this.debug = config.debug || false;\n\n this.log('Server client initialized with base URL:', this.baseUrl);\n }\n\n /**\n * Sync a single user's data to Census.\n *\n * Call this when:\n * - A user signs up\n * - Subscription status changes (upgrade, downgrade, cancel)\n * - User profile is updated\n * - Login activity should be recorded\n *\n * @param user - User data to sync\n * @returns Sync result with user ID\n *\n * @example\n * ```typescript\n * // On user signup\n * await census.syncUser({\n * userId: 'user_123',\n * email: 'john@example.com',\n * name: 'John Doe',\n * signupAt: new Date(),\n * subscriptionStatus: 'trial',\n * trialEndsAt: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000),\n * });\n *\n * // On subscription purchase\n * await census.syncUser({\n * userId: 'user_123',\n * planName: 'Pro',\n * subscriptionStatus: 'active',\n * billingCycle: 'monthly',\n * mrr: 9900, // $99.00 in cents\n * subscriptionStartedAt: new Date(),\n * });\n *\n * // On cancellation\n * await census.syncUser({\n * userId: 'user_123',\n * subscriptionStatus: 'canceled',\n * subscriptionEndsAt: new Date('2024-02-01'),\n * });\n * ```\n */\n async syncUser(user: SyncUserData): Promise<SyncResult> {\n if (!user.userId) {\n throw new Error('Census: userId is required for syncUser()');\n }\n\n const response = await this.request<SyncResult>(\n '/api/sdk/sync',\n 'POST',\n this.prepareUserData(user)\n );\n\n this.log('User synced:', user.userId);\n return response;\n }\n\n /**\n * Bulk sync multiple users to Census.\n *\n * Use this for:\n * - Initial import of existing users\n * - Periodic full sync from your database\n * - Batch updates after bulk operations\n *\n * @param users - Array of users to sync (max 1000 per request)\n * @param options - Sync options\n * @returns Bulk sync result with counts\n *\n * @example\n * ```typescript\n * // Initial import\n * const allUsers = await db.users.findAll();\n *\n * const result = await census.syncUsers(\n * allUsers.map(u => ({\n * userId: u.id,\n * email: u.email,\n * name: u.name,\n * planName: u.subscription?.plan,\n * subscriptionStatus: u.subscription?.status,\n * mrr: u.subscription?.mrr,\n * signupAt: u.createdAt,\n * lastLoginAt: u.lastLogin,\n * loginCount: u.loginCount,\n * })),\n * { batchSize: 100 }\n * );\n *\n * console.log(`Synced: ${result.created} created, ${result.updated} updated`);\n * if (result.errors?.length) {\n * console.error('Errors:', result.errors);\n * }\n * ```\n */\n async syncUsers(\n users: SyncUserData[],\n options?: SyncUsersOptions\n ): Promise<SyncUsersResult> {\n if (!users || users.length === 0) {\n return {\n success: true,\n created: 0,\n updated: 0,\n skipped: 0,\n failed: 0,\n };\n }\n\n if (users.length > 1000) {\n throw new Error('Census: Maximum 1000 users per syncUsers() call. Split into multiple calls.');\n }\n\n // Validate all users have userId\n const invalidUsers = users.filter(u => !u.userId);\n if (invalidUsers.length > 0) {\n throw new Error(`Census: ${invalidUsers.length} users missing userId`);\n }\n\n const response = await this.request<SyncUsersResult>(\n '/api/sdk/sync/bulk',\n 'POST',\n {\n users: users.map(u => this.prepareUserData(u)),\n options: {\n onConflict: options?.onConflict || 'update',\n batchSize: options?.batchSize,\n },\n }\n );\n\n this.log(\n 'Bulk sync complete:',\n `${response.created} created,`,\n `${response.updated} updated,`,\n `${response.failed} failed`\n );\n\n return response;\n }\n\n /**\n * Prepare user data for API request, converting dates to ISO strings\n */\n private prepareUserData(user: SyncUserData): Record<string, unknown> {\n const data: Record<string, unknown> = {\n userId: user.userId,\n email: user.email,\n name: user.name,\n avatarUrl: user.avatarUrl,\n metadata: user.metadata,\n organizationId: user.organizationId,\n organizationName: user.organizationName,\n organizationDomain: user.organizationDomain,\n organizationPlan: user.organizationPlan,\n planName: user.planName,\n subscriptionStatus: user.subscriptionStatus,\n billingCycle: user.billingCycle,\n mrr: user.mrr,\n loginCount: user.loginCount,\n };\n\n // Convert dates to ISO strings\n if (user.subscriptionStartedAt) {\n data.subscriptionStartedAt = this.toISOString(user.subscriptionStartedAt);\n }\n if (user.subscriptionEndsAt) {\n data.subscriptionEndsAt = this.toISOString(user.subscriptionEndsAt);\n }\n if (user.trialEndsAt) {\n data.trialEndsAt = this.toISOString(user.trialEndsAt);\n }\n if (user.signupAt) {\n data.signupAt = this.toISOString(user.signupAt);\n }\n if (user.lastLoginAt) {\n data.lastLoginAt = this.toISOString(user.lastLoginAt);\n }\n\n // Remove undefined values\n return Object.fromEntries(\n Object.entries(data).filter(([, v]) => v !== undefined)\n );\n }\n\n /**\n * Convert Date or string to ISO string\n */\n private toISOString(value: string | Date): string {\n if (value instanceof Date) {\n return value.toISOString();\n }\n return value;\n }\n\n /**\n * Make an API request\n */\n private async request<T>(\n path: string,\n method: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const maxRetries = 1;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 30_000);\n\n try {\n const headers: Record<string, string> = {\n 'X-Census-Key': this.apiKey,\n 'Content-Type': 'application/json',\n };\n\n this.log(`${method} ${path}`, body);\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n // Retry on 5xx errors\n if (response.status >= 500 && attempt < maxRetries) {\n this.log(`Retrying ${method} ${path} after ${response.status}`);\n await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));\n continue;\n }\n\n let errorMessage = `Request failed with status ${response.status}`;\n try {\n const errorData = await response.json();\n errorMessage = errorData.error || errorMessage;\n } catch {\n // Use default error message\n }\n\n const error: CensusError = {\n error: errorMessage,\n status: response.status,\n };\n throw error;\n }\n\n return response.json();\n } catch (err) {\n if (err && typeof err === 'object' && 'error' in err) throw err;\n\n // Retry on network errors\n if (attempt < maxRetries) {\n this.log(`Retrying ${method} ${path} after network error`);\n await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));\n continue;\n }\n\n const error: CensusError = {\n error: controller.signal.aborted\n ? `Request timed out after 30s: ${method} ${path}`\n : `Network error: ${method} ${path}`,\n };\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n // TypeScript exhaustiveness — unreachable\n throw { error: 'Unexpected error', status: 500 } as CensusError;\n }\n\n /**\n * Log debug messages\n */\n private log(...args: unknown[]): void {\n if (this.debug) {\n console.log('[Census Server]', ...args);\n }\n }\n}\n\n/**\n * Create a new Census Server SDK client.\n *\n * @param config - Configuration options\n * @returns Census server client instance\n *\n * @example\n * ```typescript\n * import { createCensusServer } from '@census-ai/census-sdk/server';\n *\n * const census = createCensusServer({\n * apiKey: 'cs_live_your_key_here',\n * debug: true, // Enable debug logging\n * });\n *\n * // Sync user on subscription change\n * await census.syncUser({\n * userId: 'user_123',\n * planName: 'Pro',\n * subscriptionStatus: 'active',\n * mrr: 9900,\n * });\n * ```\n */\nexport function createCensusServer(config: CensusConfig): CensusServerClient {\n return new CensusServerClient(config);\n}\n\n// Re-export types needed for server usage\nexport type {\n CensusConfig,\n SyncUserData,\n SyncUsersOptions,\n SyncResult,\n SyncUsersResult,\n CensusError,\n} from './types';\n"]}
1
+ {"version":3,"sources":["../../src/server.ts"],"names":["DEFAULT_BASE_URL","CensusServerClient","config","prefix","user","response","users","options","invalidUsers","u","data","v","value","path","method","body","url","maxRetries","attempt","controller","timeoutId","headers","r","errorMessage","err","args","createCensusServer"],"mappings":"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","file":"server.js","sourcesContent":["/**\n * @census-ai/census-sdk/server - Server-side Census SDK\n *\n * For backend use cases: sync users from your server, track subscription changes,\n * and bulk import users.\n *\n * @example\n * ```typescript\n * import { createCensusServer } from '@census-ai/census-sdk/server';\n *\n * const census = createCensusServer({ apiKey: 'cs_live_xxx' });\n *\n * // Sync a single user (e.g., on signup or subscription change)\n * await census.syncUser({\n * userId: 'user_123',\n * email: 'john@example.com',\n * planName: 'Pro',\n * subscriptionStatus: 'active',\n * mrr: 9900, // $99.00 in cents\n * signupAt: new Date(),\n * });\n *\n * // Bulk sync users (e.g., initial import)\n * const result = await census.syncUsers(allUsers, { batchSize: 100 });\n * console.log(`Created: ${result.created}, Updated: ${result.updated}`);\n * ```\n */\n\nimport type {\n CensusConfig,\n SyncUserData,\n SyncUsersOptions,\n SyncResult,\n SyncUsersResult,\n CensusError,\n} from './types';\n\n/**\n * Default API base URL\n */\nconst DEFAULT_BASE_URL = 'https://census-api-production-97c0.up.railway.app';\n\n/**\n * Census Server SDK Client\n *\n * A server-side client for syncing user data from your backend.\n * Use `createCensusServer()` to create an instance.\n *\n * This client is designed for:\n * - Backend services (Node.js, Bun, Deno, Edge runtimes)\n * - Syncing subscription/billing data from your payment processor\n * - Bulk importing users from your database\n * - Tracking server-side events\n *\n * For client-side usage, use `createCensus()` from '@census-ai/census-sdk'.\n */\nexport class CensusServerClient {\n private apiKey: string;\n private baseUrl: string;\n private debug: boolean;\n\n constructor(config: CensusConfig) {\n if (!config.apiKey) {\n throw new Error('Census: apiKey is required');\n }\n\n // Support both new (cs_) and legacy (op_) key prefixes\n const validPrefixes = ['cs_live_', 'cs_test_', 'op_live_', 'op_test_'];\n if (!validPrefixes.some(prefix => config.apiKey.startsWith(prefix))) {\n console.warn('Census: API key should start with \"cs_live_\" or \"cs_test_\"');\n }\n\n this.apiKey = config.apiKey;\n this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;\n this.debug = config.debug || false;\n\n this.log('Server client initialized with base URL:', this.baseUrl);\n }\n\n /**\n * Sync a single user's data to Census.\n *\n * Call this when:\n * - A user signs up\n * - Subscription status changes (upgrade, downgrade, cancel)\n * - User profile is updated\n * - Login activity should be recorded\n *\n * @param user - User data to sync\n * @returns Sync result with user ID\n *\n * @example\n * ```typescript\n * // On user signup\n * await census.syncUser({\n * userId: 'user_123',\n * email: 'john@example.com',\n * name: 'John Doe',\n * signupAt: new Date(),\n * subscriptionStatus: 'trial',\n * trialEndsAt: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000),\n * });\n *\n * // On subscription purchase\n * await census.syncUser({\n * userId: 'user_123',\n * planName: 'Pro',\n * subscriptionStatus: 'active',\n * billingCycle: 'monthly',\n * mrr: 9900, // $99.00 in cents\n * subscriptionStartedAt: new Date(),\n * });\n *\n * // On cancellation\n * await census.syncUser({\n * userId: 'user_123',\n * subscriptionStatus: 'canceled',\n * subscriptionEndsAt: new Date('2024-02-01'),\n * });\n * ```\n */\n async syncUser(user: SyncUserData): Promise<SyncResult> {\n if (!user.userId) {\n throw new Error('Census: userId is required for syncUser()');\n }\n\n const response = await this.request<SyncResult>(\n '/api/sdk/sync',\n 'POST',\n this.prepareUserData(user)\n );\n\n this.log('User synced:', user.userId);\n return response;\n }\n\n /**\n * Bulk sync multiple users to Census.\n *\n * Use this for:\n * - Initial import of existing users\n * - Periodic full sync from your database\n * - Batch updates after bulk operations\n *\n * @param users - Array of users to sync (max 1000 per request)\n * @param options - Sync options\n * @returns Bulk sync result with counts\n *\n * @example\n * ```typescript\n * // Initial import\n * const allUsers = await db.users.findAll();\n *\n * const result = await census.syncUsers(\n * allUsers.map(u => ({\n * userId: u.id,\n * email: u.email,\n * name: u.name,\n * planName: u.subscription?.plan,\n * subscriptionStatus: u.subscription?.status,\n * mrr: u.subscription?.mrr,\n * signupAt: u.createdAt,\n * lastLoginAt: u.lastLogin,\n * loginCount: u.loginCount,\n * })),\n * { batchSize: 100 }\n * );\n *\n * console.log(`Synced: ${result.created} created, ${result.updated} updated`);\n * if (result.errors?.length) {\n * console.error('Errors:', result.errors);\n * }\n * ```\n */\n async syncUsers(\n users: SyncUserData[],\n options?: SyncUsersOptions\n ): Promise<SyncUsersResult> {\n if (!users || users.length === 0) {\n return {\n success: true,\n created: 0,\n updated: 0,\n skipped: 0,\n failed: 0,\n };\n }\n\n if (users.length > 1000) {\n throw new Error('Census: Maximum 1000 users per syncUsers() call. Split into multiple calls.');\n }\n\n // Validate all users have userId\n const invalidUsers = users.filter(u => !u.userId);\n if (invalidUsers.length > 0) {\n throw new Error(`Census: ${invalidUsers.length} users missing userId`);\n }\n\n const response = await this.request<SyncUsersResult>(\n '/api/sdk/sync/bulk',\n 'POST',\n {\n users: users.map(u => this.prepareUserData(u)),\n options: {\n onConflict: options?.onConflict || 'update',\n batchSize: options?.batchSize,\n },\n }\n );\n\n this.log(\n 'Bulk sync complete:',\n `${response.created} created,`,\n `${response.updated} updated,`,\n `${response.failed} failed`\n );\n\n return response;\n }\n\n /**\n * Prepare user data for API request, converting dates to ISO strings\n */\n private prepareUserData(user: SyncUserData): Record<string, unknown> {\n const data: Record<string, unknown> = {\n userId: user.userId,\n email: user.email,\n name: user.name,\n avatarUrl: user.avatarUrl,\n metadata: user.metadata,\n organizationId: user.organizationId,\n organizationName: user.organizationName,\n organizationDomain: user.organizationDomain,\n organizationPlan: user.organizationPlan,\n planName: user.planName,\n subscriptionStatus: user.subscriptionStatus,\n billingCycle: user.billingCycle,\n mrr: user.mrr,\n loginCount: user.loginCount,\n };\n\n // Convert dates to ISO strings\n if (user.subscriptionStartedAt) {\n data.subscriptionStartedAt = this.toISOString(user.subscriptionStartedAt);\n }\n if (user.subscriptionEndsAt) {\n data.subscriptionEndsAt = this.toISOString(user.subscriptionEndsAt);\n }\n if (user.trialEndsAt) {\n data.trialEndsAt = this.toISOString(user.trialEndsAt);\n }\n if (user.signupAt) {\n data.signupAt = this.toISOString(user.signupAt);\n }\n if (user.lastLoginAt) {\n data.lastLoginAt = this.toISOString(user.lastLoginAt);\n }\n\n // Remove undefined values\n return Object.fromEntries(\n Object.entries(data).filter(([, v]) => v !== undefined)\n );\n }\n\n /**\n * Convert Date or string to ISO string\n */\n private toISOString(value: string | Date): string {\n if (value instanceof Date) {\n return value.toISOString();\n }\n return value;\n }\n\n /**\n * Make an API request\n */\n private async request<T>(\n path: string,\n method: string,\n body?: unknown\n ): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const maxRetries = 1;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), 30_000);\n\n try {\n const headers: Record<string, string> = {\n 'X-Census-Key': this.apiKey,\n 'Content-Type': 'application/json',\n };\n\n this.log(`${method} ${path}`, body);\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n // Retry on 5xx errors\n if (response.status >= 500 && attempt < maxRetries) {\n this.log(`Retrying ${method} ${path} after ${response.status}`);\n await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));\n continue;\n }\n\n let errorMessage = `Request failed with status ${response.status}`;\n try {\n const errorData = await response.json();\n errorMessage = errorData.error || errorMessage;\n } catch {\n // Use default error message\n }\n\n const error: CensusError = {\n error: errorMessage,\n status: response.status,\n };\n throw error;\n }\n\n return response.json();\n } catch (err) {\n if (err && typeof err === 'object' && 'error' in err) throw err;\n\n // Retry on network errors\n if (attempt < maxRetries) {\n this.log(`Retrying ${method} ${path} after network error`);\n await new Promise((r) => setTimeout(r, 1000 * (attempt + 1)));\n continue;\n }\n\n const error: CensusError = {\n error: controller.signal.aborted\n ? `Request timed out after 30s: ${method} ${path}`\n : `Network error: ${method} ${path}`,\n };\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n // TypeScript exhaustiveness — unreachable\n throw { error: 'Unexpected error', status: 500 } as CensusError;\n }\n\n /**\n * Log debug messages\n */\n private log(...args: unknown[]): void {\n if (this.debug) {\n console.log('[Census Server]', ...args);\n }\n }\n}\n\n/**\n * Create a new Census Server SDK client.\n *\n * @param config - Configuration options\n * @returns Census server client instance\n *\n * @example\n * ```typescript\n * import { createCensusServer } from '@census-ai/census-sdk/server';\n *\n * const census = createCensusServer({\n * apiKey: 'cs_live_your_key_here',\n * debug: true, // Enable debug logging\n * });\n *\n * // Sync user on subscription change\n * await census.syncUser({\n * userId: 'user_123',\n * planName: 'Pro',\n * subscriptionStatus: 'active',\n * mrr: 9900,\n * });\n * ```\n */\nexport function createCensusServer(config: CensusConfig): CensusServerClient {\n return new CensusServerClient(config);\n}\n\n// Re-export types needed for server usage\nexport type {\n CensusConfig,\n SyncUserData,\n SyncUsersOptions,\n SyncResult,\n SyncUsersResult,\n CensusError,\n} from './types';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@census-ai/census-sdk",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Official Census SDK for integrating feedback, knowledge base, and analytics into your application",
5
5
  "author": "Census AI",
6
6
  "license": "MIT",