@closeloop/sdk 0.1.2 → 0.1.4

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/README.md CHANGED
@@ -257,55 +257,108 @@ export async function POST(request: Request) {
257
257
 
258
258
  ## Next.js Integration
259
259
 
260
- ### Middleware
260
+ ### Proxy (Next.js 16+)
261
261
 
262
- Protect routes by requiring credits:
262
+ Protect routes by verifying credits:
263
263
 
264
264
  ```typescript
265
- // middleware.ts
266
- import { CloseLoop, creditGate } from "@closeloop/sdk/nextjs"
265
+ // proxy.ts (Next.js 16+)
266
+ import { NextRequest, NextResponse } from "next/server"
267
+ import { CloseLoop } from "@closeloop/sdk"
267
268
 
268
269
  const client = new CloseLoop({ apiKey: process.env.CLOSELOOP_API_KEY! })
269
270
 
270
- export default creditGate({
271
- client,
272
- planId: "plan_abc123",
273
- creditsPerRequest: 1,
274
- getWalletAddress: (req) => req.headers.get("x-wallet-address")
275
- })
271
+ // Define routes that require credit verification
272
+ const creditProtectedRoutes = ["/api/ai", "/api/generate"]
273
+
274
+ export default async function proxy(req: NextRequest) {
275
+ const path = req.nextUrl.pathname
276
+ const isCreditProtectedRoute = creditProtectedRoutes.some(route =>
277
+ path.startsWith(route)
278
+ )
279
+
280
+ if (!isCreditProtectedRoute) {
281
+ return NextResponse.next()
282
+ }
283
+
284
+ // Get wallet address from header or session
285
+ const walletAddress = req.headers.get("x-wallet-address")
286
+
287
+ if (!walletAddress) {
288
+ return NextResponse.json(
289
+ { error: "Wallet address required" },
290
+ { status: 401 }
291
+ )
292
+ }
293
+
294
+ // Verify credits before allowing the request
295
+ const verification = await client.credits.verify({
296
+ walletAddress,
297
+ planId: "plan_abc123",
298
+ amount: 1
299
+ })
300
+
301
+ if (!verification.hasEnoughCredits) {
302
+ return NextResponse.json(
303
+ { error: "Insufficient credits" },
304
+ { status: 402 }
305
+ )
306
+ }
307
+
308
+ return NextResponse.next()
309
+ }
276
310
 
277
311
  export const config = {
278
- matcher: ["/api/protected/:path*"]
312
+ matcher: ["/((?!_next/static|_next/image|.*\\.png$).*)"]
279
313
  }
280
314
  ```
281
315
 
282
- ### API Route Helper
316
+ ### Route Handler
283
317
 
284
- Consume credits after processing:
318
+ Verify and consume credits atomically in API routes:
285
319
 
286
320
  ```typescript
287
- // app/api/ai/route.ts
321
+ // app/api/ai/route.ts (Next.js 16+)
288
322
  import { NextRequest, NextResponse } from "next/server"
289
- import { CloseLoop, consumeCreditsAfterRequest } from "@closeloop/sdk/nextjs"
323
+ import { CloseLoop, InsufficientCreditsError } from "@closeloop/sdk"
290
324
 
291
325
  const client = new CloseLoop({ apiKey: process.env.CLOSELOOP_API_KEY! })
292
326
 
293
327
  export async function POST(request: NextRequest) {
294
- const wallet = request.headers.get("x-wallet-address")!
295
-
296
- // Process request...
297
- const result = await processAIRequest()
328
+ const wallet = request.headers.get("x-wallet-address")
298
329
 
299
- // Consume credit after successful processing
300
- await consumeCreditsAfterRequest(client, {
301
- walletAddress: wallet,
302
- planId: "plan_abc123",
303
- amount: 1,
304
- consumedBy: "ai-text-generation",
305
- metadata: { requestId: result.id }
306
- })
330
+ if (!wallet) {
331
+ return NextResponse.json(
332
+ { error: "Wallet address required" },
333
+ { status: 401 }
334
+ )
335
+ }
307
336
 
308
- return NextResponse.json(result)
337
+ // Verify and consume credits atomically
338
+ try {
339
+ const result = await client.credits.verifyAndConsume({
340
+ walletAddress: wallet,
341
+ planId: "plan_abc123",
342
+ amount: 1,
343
+ consumedBy: "ai-text-generation"
344
+ })
345
+
346
+ // Process request after credit verification
347
+ const aiResult = await processAIRequest()
348
+
349
+ return NextResponse.json({
350
+ ...aiResult,
351
+ remainingCredits: result.remainingCredits
352
+ })
353
+ } catch (error) {
354
+ if (error instanceof InsufficientCreditsError) {
355
+ return NextResponse.json(
356
+ { error: "Insufficient credits", remaining: error.remainingCredits },
357
+ { status: 402 }
358
+ )
359
+ }
360
+ throw error
361
+ }
309
362
  }
310
363
  ```
311
364
 
@@ -369,11 +422,12 @@ All inputs are validated using Zod schemas before being sent to the API:
369
422
 
370
423
  ### Rate Limiting
371
424
 
372
- The `creditGate` middleware does **not** include rate limiting. We strongly recommend combining it with a rate limiter:
425
+ The proxy does **not** include rate limiting. We strongly recommend combining it with a rate limiter:
373
426
 
374
427
  ```typescript
375
- // middleware.ts - With rate limiting (RECOMMENDED)
376
- import { CloseLoop, creditGate } from "@closeloop/sdk/nextjs"
428
+ // proxy.ts - With rate limiting (RECOMMENDED)
429
+ import { NextRequest, NextResponse } from "next/server"
430
+ import { CloseLoop } from "@closeloop/sdk"
377
431
  import { Ratelimit } from "@upstash/ratelimit"
378
432
  import { Redis } from "@upstash/redis"
379
433
 
@@ -381,31 +435,59 @@ const client = new CloseLoop({ apiKey: process.env.CLOSELOOP_API_KEY! })
381
435
 
382
436
  const ratelimit = new Ratelimit({
383
437
  redis: Redis.fromEnv(),
384
- limiter: Ratelimit.slidingWindow(10, "10 s"), // 10 requests per 10 seconds
438
+ limiter: Ratelimit.slidingWindow(10, "10 s"),
385
439
  })
386
440
 
387
- const creditGateMiddleware = creditGate({
388
- client,
389
- planId: "plan_abc123",
390
- creditsPerRequest: 1,
391
- getWalletAddress: (req) => req.headers.get("x-wallet-address")
392
- })
441
+ const creditProtectedRoutes = ["/api/ai", "/api/generate"]
442
+
443
+ export default async function proxy(request: NextRequest) {
444
+ const path = request.nextUrl.pathname
445
+ const isCreditProtectedRoute = creditProtectedRoutes.some(route =>
446
+ path.startsWith(route)
447
+ )
448
+
449
+ if (!isCreditProtectedRoute) {
450
+ return NextResponse.next()
451
+ }
393
452
 
394
- export default async function middleware(request: NextRequest) {
395
453
  // Rate limit by IP first
396
- const ip = request.ip ?? "127.0.0.1"
454
+ const ip = request.headers.get("x-forwarded-for") ?? "127.0.0.1"
397
455
  const { success } = await ratelimit.limit(ip)
398
456
 
399
457
  if (!success) {
400
- return NextResponse.json({ error: "Rate limit exceeded" }, { status: 429 })
458
+ return NextResponse.json(
459
+ { error: "Rate limit exceeded" },
460
+ { status: 429 }
461
+ )
462
+ }
463
+
464
+ // Then verify credits
465
+ const walletAddress = request.headers.get("x-wallet-address")
466
+ if (!walletAddress) {
467
+ return NextResponse.json(
468
+ { error: "Wallet address required" },
469
+ { status: 401 }
470
+ )
471
+ }
472
+
473
+ const verification = await client.credits.verify({
474
+ walletAddress,
475
+ planId: "plan_abc123",
476
+ amount: 1
477
+ })
478
+
479
+ if (!verification.hasEnoughCredits) {
480
+ return NextResponse.json(
481
+ { error: "Insufficient credits" },
482
+ { status: 402 }
483
+ )
401
484
  }
402
485
 
403
- // Then check credits
404
- return creditGateMiddleware(request)
486
+ return NextResponse.next()
405
487
  }
406
488
 
407
489
  export const config = {
408
- matcher: ["/api/protected/:path*"]
490
+ matcher: ["/((?!_next/static|_next/image|.*\\.png$).*)"]
409
491
  }
410
492
  ```
411
493
 
package/dist/index.js CHANGED
@@ -249,7 +249,7 @@ var Balances = class {
249
249
  async get(params) {
250
250
  const validated = validateInput(getBalanceSchema, params);
251
251
  const query = this.buildQueryString({
252
- wallet: validated.walletAddress,
252
+ walletAddress: validated.walletAddress,
253
253
  plan: validated.planId
254
254
  });
255
255
  try {
@@ -293,7 +293,7 @@ var Balances = class {
293
293
  async list(params) {
294
294
  const validated = validateInput(listBalancesSchema, params);
295
295
  const query = this.buildQueryString({
296
- wallet: validated.walletAddress,
296
+ walletAddress: validated.walletAddress,
297
297
  activeOnly: validated.activeOnly,
298
298
  limit: validated.limit,
299
299
  cursor: validated.cursor
@@ -348,7 +348,7 @@ var Balances = class {
348
348
  */
349
349
  async stats(walletAddress) {
350
350
  validateInput(walletAddressSchema, walletAddress);
351
- const query = this.buildQueryString({ wallet: walletAddress });
351
+ const query = this.buildQueryString({ walletAddress });
352
352
  return this.http.request({
353
353
  method: "GET",
354
354
  path: `${ENDPOINTS.STATS}?${query}`
@@ -773,7 +773,7 @@ var HttpClient = class {
773
773
  headers: {
774
774
  "Content-Type": "application/json",
775
775
  "User-Agent": USER_AGENT,
776
- "X-API-Key": this.apiKey,
776
+ Authorization: `Bearer ${this.apiKey}`,
777
777
  ...options.headers
778
778
  },
779
779
  body: options.body ? JSON.stringify(options.body) : void 0,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/schemas/validation.ts","../src/utils/errors.ts","../src/utils/validation.ts","../src/resources/balances.ts","../src/resources/credits.ts","../src/utils/crypto.ts","../src/resources/webhooks.ts","../src/utils/http.ts","../src/client.ts"],"sourcesContent":["// Main client\nexport { CloseLoop } from \"./client\"\nexport type { CloseLoopOptions } from \"./client\"\n\n// Resources\nexport { Credits } from \"./resources/credits\"\nexport { Balances } from \"./resources/balances\"\nexport { Webhooks } from \"./resources/webhooks\"\nexport type { VerifyWebhookParams } from \"./resources/webhooks\"\n\n// Errors\nexport {\n CloseLoopError,\n InsufficientCreditsError,\n CreditsExpiredError,\n AuthenticationError,\n RateLimitError,\n NetworkError,\n NotFoundError,\n ValidationError\n} from \"./utils/errors\"\n\n// Utilities\nexport { validateInput } from \"./utils/validation\"\n\n// Credit types\nexport type {\n VerifyCreditsParams,\n VerifyCreditsResponse,\n ConsumeCreditsParams,\n ConsumeCreditsResponse,\n BatchConsumeParams,\n BatchConsumeResponse\n} from \"./types/credits\"\n\n// Balance types\nexport type {\n CreditBalance,\n GetBalanceParams,\n ListBalancesParams,\n ListBalancesResponse,\n CreditTransaction,\n ListTransactionsParams,\n ListTransactionsResponse,\n CreditStats\n} from \"./types/balances\"\n\n// Webhook types\nexport type {\n WebhookEventType,\n WebhookEvent,\n PaymentSuccessPayload,\n CreditsLowPayload,\n CreditsExpiredPayload,\n WebhookPayload\n} from \"./types/webhooks\"\n","export const DEFAULT_BASE_URL = \"https://closeloop.app\"\nexport const DEFAULT_TIMEOUT = 30000 // 30 seconds\nexport const SDK_VERSION = \"0.1.0\"\nexport const USER_AGENT = `closeloop-node/${SDK_VERSION}`\n","import { z } from \"zod\"\n\n/**\n * Ethereum wallet address validation (0x + 40 hex chars)\n */\nexport const walletAddressSchema = z\n .string()\n .regex(/^0x[a-fA-F0-9]{40}$/, \"Invalid Ethereum wallet address\")\n\n/**\n * Plan ID validation (non-empty string with reasonable length)\n */\nexport const planIdSchema = z\n .string()\n .min(1, \"Plan ID is required\")\n .max(100, \"Plan ID too long\")\n\n/**\n * Balance ID validation\n */\nexport const balanceIdSchema = z\n .string()\n .min(1, \"Balance ID is required\")\n .max(100, \"Balance ID too long\")\n\n/**\n * Credit amount validation (positive integer with reasonable max)\n */\nexport const creditAmountSchema = z\n .number()\n .int(\"Amount must be an integer\")\n .positive(\"Amount must be positive\")\n .max(1_000_000_000, \"Amount exceeds maximum\")\n\n/**\n * Pagination limit validation\n */\nexport const paginationLimitSchema = z\n .number()\n .int()\n .positive()\n .max(100, \"Limit cannot exceed 100\")\n\n/**\n * Transaction type validation\n */\nexport const transactionTypeSchema = z.enum([\n \"PURCHASE\",\n \"CONSUMPTION\",\n \"REFUND\",\n \"EXPIRATION\"\n])\n\n/**\n * Dangerous prototype pollution keys to skip\n */\nconst DANGEROUS_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"])\n\n/**\n * Sanitize metadata object to prevent prototype pollution\n * Creates a clean object with no prototype chain\n */\nexport function sanitizeMetadata(\n metadata: Record<string, unknown> | undefined\n): Record<string, unknown> | undefined {\n if (!metadata) return undefined\n\n // Create a clean object with null prototype to prevent prototype pollution\n const clean = Object.create(null) as Record<string, unknown>\n\n for (const [key, value] of Object.entries(metadata)) {\n // Skip dangerous prototype pollution keys\n if (DANGEROUS_KEYS.has(key)) continue\n\n // Copy all JSON-serializable values\n clean[key] = value\n }\n\n return Object.freeze(clean)\n}\n\n// ============================================================================\n// Credit Schemas\n// ============================================================================\n\nexport const verifyCreditsSchema = z.object({\n walletAddress: walletAddressSchema,\n planId: planIdSchema,\n amount: creditAmountSchema\n})\n\nexport const consumeCreditsSchema = z.object({\n walletAddress: walletAddressSchema,\n planId: planIdSchema,\n amount: creditAmountSchema,\n consumedBy: z.string().max(100).optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n idempotencyKey: z.string().max(100).optional()\n})\n\nexport const batchConsumeSchema = z.object({\n operations: z\n .array(\n z.object({\n walletAddress: walletAddressSchema,\n planId: planIdSchema,\n amount: creditAmountSchema,\n consumedBy: z.string().max(100).optional(),\n metadata: z.record(z.string(), z.unknown()).optional()\n })\n )\n .min(1, \"At least one operation required\")\n .max(100, \"Maximum 100 operations per batch\"),\n atomic: z.boolean().optional()\n})\n\n// ============================================================================\n// Balance Schemas\n// ============================================================================\n\nexport const getBalanceSchema = z.object({\n walletAddress: walletAddressSchema,\n planId: planIdSchema\n})\n\nexport const listBalancesSchema = z.object({\n walletAddress: walletAddressSchema,\n activeOnly: z.boolean().optional(),\n limit: paginationLimitSchema.optional(),\n cursor: z.string().max(200).optional()\n})\n\nexport const listTransactionsSchema = z.object({\n balanceId: balanceIdSchema,\n type: transactionTypeSchema.optional(),\n limit: paginationLimitSchema.optional(),\n cursor: z.string().max(200).optional()\n})\n\n// ============================================================================\n// Webhook Schemas\n// ============================================================================\n\nexport const webhookEventTypeSchema = z.enum([\n \"payment.success\",\n \"credits.low\",\n \"credits.expired\"\n])\n\nexport const webhookEventSchema = z.object({\n type: webhookEventTypeSchema,\n id: z.string().min(1),\n createdAt: z.string(),\n data: z.record(z.string(), z.unknown())\n})\n\nexport const paymentSuccessPayloadSchema = z\n .object({\n type: z.string(),\n planId: z.string(),\n planName: z.string(),\n amount: z.number(),\n walletAddress: z.string(),\n transactionId: z.string()\n })\n .loose()\n\nexport const creditsLowPayloadSchema = z.object({\n walletAddress: z.string(),\n planId: z.string(),\n remainingCredits: z.number(),\n threshold: z.number()\n})\n\nexport const creditsExpiredPayloadSchema = z.object({\n walletAddress: z.string(),\n planId: z.string(),\n expiredCredits: z.number(),\n expiresAt: z.string()\n})\n","/**\n * Base error class for all CloseLoop SDK errors\n */\nexport class CloseLoopError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number,\n public details?: Record<string, unknown>\n ) {\n super(message)\n this.name = \"CloseLoopError\"\n }\n}\n\n/**\n * Thrown when a user doesn't have enough credits for an operation\n */\nexport class InsufficientCreditsError extends CloseLoopError {\n constructor(\n public remainingCredits: number,\n public requiredCredits: number\n ) {\n super(\n `Insufficient credits: ${remainingCredits} available, ${requiredCredits} required`,\n \"INSUFFICIENT_CREDITS\",\n 402\n )\n this.name = \"InsufficientCreditsError\"\n }\n}\n\n/**\n * Thrown when credits have expired\n */\nexport class CreditsExpiredError extends CloseLoopError {\n constructor(public expiresAt: Date) {\n super(\n `Credits expired at ${expiresAt.toISOString()}`,\n \"CREDITS_EXPIRED\",\n 410\n )\n this.name = \"CreditsExpiredError\"\n }\n}\n\n/**\n * Thrown when authentication fails (invalid API key)\n */\nexport class AuthenticationError extends CloseLoopError {\n constructor(message = \"Invalid API key\") {\n super(message, \"AUTHENTICATION_ERROR\", 401)\n this.name = \"AuthenticationError\"\n }\n}\n\n/**\n * Thrown when rate limit is exceeded\n */\nexport class RateLimitError extends CloseLoopError {\n constructor(public retryAfter?: number) {\n super(\"Rate limit exceeded\", \"RATE_LIMIT\", 429)\n this.name = \"RateLimitError\"\n }\n}\n\n/**\n * Thrown when a network error occurs\n */\nexport class NetworkError extends CloseLoopError {\n constructor(\n message: string,\n public cause?: Error\n ) {\n super(message, \"NETWORK_ERROR\")\n this.name = \"NetworkError\"\n }\n}\n\n/**\n * Thrown when a resource is not found\n */\nexport class NotFoundError extends CloseLoopError {\n constructor(resource: string) {\n super(`${resource} not found`, \"NOT_FOUND\", 404)\n this.name = \"NotFoundError\"\n }\n}\n\n/**\n * Thrown when input validation fails\n */\nexport class ValidationError extends CloseLoopError {\n constructor(\n message: string,\n public field?: string\n ) {\n super(message, \"VALIDATION_ERROR\", 400)\n this.name = \"ValidationError\"\n }\n}\n","import { CloseLoopError } from \"./errors\"\n\n/**\n * Generic schema interface for validation\n */\nexport interface ValidationSchema<T> {\n safeParse: (data: unknown) => {\n success: boolean\n data?: T\n error?: { message: string }\n }\n}\n\n/**\n * Validate input against a Zod schema\n * Throws CloseLoopError if validation fails\n *\n * @param schema - Zod schema to validate against\n * @param data - Data to validate\n * @returns Validated and typed data\n * @throws {CloseLoopError} When validation fails\n */\nexport function validateInput<T>(\n schema: ValidationSchema<T>,\n data: unknown\n): T {\n const result = schema.safeParse(data)\n\n if (!result.success) {\n throw new CloseLoopError(\n `Validation error: ${result.error?.message || \"Invalid input\"}`,\n \"VALIDATION_ERROR\",\n 400\n )\n }\n\n return result.data as T\n}\n\n/**\n * Validate a single value against a Zod schema\n * Returns a boolean without throwing\n */\nexport function isValid<T>(\n schema: ValidationSchema<T>,\n data: unknown\n): data is T {\n return schema.safeParse(data).success\n}\n","import {\n getBalanceSchema,\n listBalancesSchema,\n listTransactionsSchema,\n walletAddressSchema\n} from \"../schemas/validation\"\nimport type {\n CreditBalance,\n CreditStats,\n GetBalanceParams,\n ListBalancesParams,\n ListBalancesResponse,\n ListTransactionsParams,\n ListTransactionsResponse\n} from \"../types/balances\"\nimport { NotFoundError } from \"../utils/errors\"\nimport { HttpClient } from \"../utils/http\"\nimport { validateInput } from \"../utils/validation\"\n\n// API Endpoints\nconst ENDPOINTS = {\n BALANCE: \"/api/credit/balance\",\n BALANCES: \"/api/credit/balances\",\n STATS: \"/api/credit/stats\"\n} as const\n\n/**\n * Resource for querying credit balances and transaction history\n */\nexport class Balances {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Get a specific credit balance for a wallet and plan.\n *\n * @example\n * ```typescript\n * const balance = await client.balances.get({\n * walletAddress: \"0x1234...\",\n * planId: \"plan_abc123\"\n * })\n *\n * if (balance) {\n * console.log(`Credits: ${balance.remainingCredits}/${balance.totalCredits}`)\n * }\n * ```\n *\n * @returns The credit balance, or null if not found\n * @throws {CloseLoopError} When input validation fails\n */\n async get(params: GetBalanceParams): Promise<CreditBalance | null> {\n const validated = validateInput(getBalanceSchema, params)\n\n const query = this.buildQueryString({\n wallet: validated.walletAddress,\n plan: validated.planId\n })\n\n try {\n return await this.http.request<CreditBalance>({\n method: \"GET\",\n path: `${ENDPOINTS.BALANCE}?${query}`\n })\n } catch (error) {\n if (error instanceof NotFoundError) {\n return null\n }\n throw error\n }\n }\n\n /**\n * List all credit balances for a wallet.\n *\n * @example\n * ```typescript\n * const { balances, nextCursor } = await client.balances.list({\n * walletAddress: \"0x1234...\",\n * activeOnly: true,\n * limit: 10\n * })\n *\n * for (const balance of balances) {\n * console.log(`${balance.planName}: ${balance.remainingCredits} credits`)\n * }\n *\n * // Paginate if needed\n * if (nextCursor) {\n * const nextPage = await client.balances.list({\n * walletAddress: \"0x1234...\",\n * cursor: nextCursor\n * })\n * }\n * ```\n *\n * @throws {CloseLoopError} When input validation fails\n */\n async list(params: ListBalancesParams): Promise<ListBalancesResponse> {\n const validated = validateInput(listBalancesSchema, params)\n\n const query = this.buildQueryString({\n wallet: validated.walletAddress,\n activeOnly: validated.activeOnly,\n limit: validated.limit,\n cursor: validated.cursor\n })\n\n return this.http.request<ListBalancesResponse>({\n method: \"GET\",\n path: `${ENDPOINTS.BALANCES}?${query}`\n })\n }\n\n /**\n * Get transaction history for a balance.\n *\n * @example\n * ```typescript\n * const { transactions } = await client.balances.transactions({\n * balanceId: \"bal_xyz\",\n * type: \"CONSUMPTION\",\n * limit: 50\n * })\n *\n * for (const tx of transactions) {\n * console.log(`${tx.type}: ${tx.amount} - ${tx.description}`)\n * }\n * ```\n *\n * @throws {CloseLoopError} When input validation fails\n */\n async transactions(\n params: ListTransactionsParams\n ): Promise<ListTransactionsResponse> {\n const validated = validateInput(listTransactionsSchema, params)\n\n const query = this.buildQueryString({\n type: validated.type,\n limit: validated.limit,\n cursor: validated.cursor\n })\n\n const basePath = `${ENDPOINTS.BALANCE}/${encodeURIComponent(validated.balanceId)}/transactions`\n const path = query ? `${basePath}?${query}` : basePath\n\n return this.http.request<ListTransactionsResponse>({\n method: \"GET\",\n path\n })\n }\n\n /**\n * Get aggregated stats for a wallet's credits.\n *\n * @example\n * ```typescript\n * const stats = await client.balances.stats(\"0x1234...\")\n * console.log(`Total: ${stats.totalCredits}, Used: ${stats.totalUsed}`)\n * ```\n *\n * @throws {CloseLoopError} When input validation fails\n */\n async stats(walletAddress: string): Promise<CreditStats> {\n // Validate using the shared utility for consistency\n validateInput(walletAddressSchema, walletAddress)\n\n const query = this.buildQueryString({ wallet: walletAddress })\n\n return this.http.request<CreditStats>({\n method: \"GET\",\n path: `${ENDPOINTS.STATS}?${query}`\n })\n }\n\n // ==========================================================================\n // Private Helpers\n // ==========================================================================\n\n /**\n * Build URL query string from params, filtering out undefined values\n */\n private buildQueryString(\n params: Record<string, string | number | boolean | undefined>\n ): string {\n const query = new URLSearchParams()\n\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n query.set(key, String(value))\n }\n }\n\n return query.toString()\n }\n}\n","import {\n batchConsumeSchema,\n consumeCreditsSchema,\n sanitizeMetadata,\n verifyCreditsSchema\n} from \"../schemas/validation\"\nimport type {\n BatchConsumeParams,\n BatchConsumeResponse,\n ConsumeCreditsParams,\n ConsumeCreditsResponse,\n VerifyCreditsParams,\n VerifyCreditsResponse\n} from \"../types/credits\"\nimport { CreditsExpiredError, InsufficientCreditsError } from \"../utils/errors\"\nimport { HttpClient } from \"../utils/http\"\nimport { validateInput } from \"../utils/validation\"\n\n// API Endpoints\nconst ENDPOINTS = {\n VERIFY: \"/api/credit/verify\",\n CONSUME: \"/api/credit/consume\",\n BATCH_CONSUME: \"/api/credit/consume/batch\"\n} as const\n\n/**\n * Resource for credit verification and consumption operations\n */\nexport class Credits {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Verify if a user has enough credits without consuming them.\n *\n * @example\n * ```typescript\n * const result = await client.credits.verify({\n * walletAddress: \"0x1234...\",\n * planId: \"plan_abc123\",\n * amount: 10\n * })\n *\n * if (result.hasEnoughCredits) {\n * // Proceed with service\n * }\n * ```\n *\n * @throws {CloseLoopError} When input validation fails\n */\n async verify(params: VerifyCreditsParams): Promise<VerifyCreditsResponse> {\n const validated = validateInput(verifyCreditsSchema, params)\n\n return this.http.request<VerifyCreditsResponse>({\n method: \"POST\",\n path: ENDPOINTS.VERIFY,\n body: validated\n })\n }\n\n /**\n * Consume credits from a user's balance.\n *\n * @example\n * ```typescript\n * const result = await client.credits.consume({\n * walletAddress: \"0x1234...\",\n * planId: \"plan_abc123\",\n * amount: 1,\n * consumedBy: \"ai-generation\",\n * metadata: { requestId: \"req_xyz\" }\n * })\n *\n * console.log(`Remaining: ${result.remainingCredits}`)\n * ```\n *\n * @throws {InsufficientCreditsError} When user doesn't have enough credits\n * @throws {CreditsExpiredError} When credits have expired\n * @throws {CloseLoopError} When input validation fails\n */\n async consume(params: ConsumeCreditsParams): Promise<ConsumeCreditsResponse> {\n const validated = validateInput(consumeCreditsSchema, params)\n\n return this.http.request<ConsumeCreditsResponse>({\n method: \"POST\",\n path: ENDPOINTS.CONSUME,\n body: this.buildConsumeBody(validated),\n headers: this.buildIdempotencyHeaders(validated.idempotencyKey)\n })\n }\n\n /**\n * Verify and consume credits in a single atomic operation.\n * This is useful when you want to ensure credits are available\n * before consuming them, without race conditions.\n *\n * @example\n * ```typescript\n * try {\n * const result = await client.credits.verifyAndConsume({\n * walletAddress: \"0x1234...\",\n * planId: \"plan_abc123\",\n * amount: 5,\n * consumedBy: \"batch-processing\"\n * })\n * // Credits were verified and consumed\n * } catch (error) {\n * if (error instanceof InsufficientCreditsError) {\n * // Handle insufficient credits\n * }\n * }\n * ```\n *\n * @throws {InsufficientCreditsError} When user doesn't have enough credits\n * @throws {CreditsExpiredError} When credits have expired\n * @throws {CloseLoopError} When input validation fails\n */\n async verifyAndConsume(\n params: ConsumeCreditsParams\n ): Promise<ConsumeCreditsResponse> {\n const validated = validateInput(consumeCreditsSchema, params)\n\n // First verify\n const verification = await this.http.request<VerifyCreditsResponse>({\n method: \"POST\",\n path: ENDPOINTS.VERIFY,\n body: {\n walletAddress: validated.walletAddress,\n planId: validated.planId,\n amount: validated.amount\n }\n })\n\n this.assertCreditsAvailable(verification, validated.amount)\n\n // Then consume\n return this.http.request<ConsumeCreditsResponse>({\n method: \"POST\",\n path: ENDPOINTS.CONSUME,\n body: this.buildConsumeBody(validated),\n headers: this.buildIdempotencyHeaders(validated.idempotencyKey)\n })\n }\n\n /**\n * Consume credits in batch.\n *\n * @example\n * ```typescript\n * const result = await client.credits.batchConsume({\n * operations: [\n * { walletAddress: \"0x1234...\", planId: \"plan_a\", amount: 1 },\n * { walletAddress: \"0x5678...\", planId: \"plan_b\", amount: 2 }\n * ],\n * atomic: false // Allow partial success\n * })\n *\n * console.log(`Success: ${result.successCount}, Failed: ${result.failedCount}`)\n * ```\n *\n * @throws {CloseLoopError} When input validation fails\n */\n async batchConsume(\n params: BatchConsumeParams\n ): Promise<BatchConsumeResponse> {\n const validated = validateInput(batchConsumeSchema, params)\n\n // Sanitize metadata in each operation\n const sanitizedOperations = validated.operations.map(op => ({\n ...op,\n metadata: sanitizeMetadata(op.metadata)\n }))\n\n return this.http.request<BatchConsumeResponse>({\n method: \"POST\",\n path: ENDPOINTS.BATCH_CONSUME,\n body: {\n operations: sanitizedOperations,\n atomic: validated.atomic\n }\n })\n }\n\n // ==========================================================================\n // Private Helpers\n // ==========================================================================\n\n /**\n * Build consumption request body with sanitized metadata\n */\n private buildConsumeBody(validated: {\n walletAddress: string\n planId: string\n amount: number\n consumedBy?: string\n metadata?: Record<string, unknown>\n }) {\n return {\n walletAddress: validated.walletAddress,\n planId: validated.planId,\n amount: validated.amount,\n consumedBy: validated.consumedBy,\n metadata: sanitizeMetadata(validated.metadata)\n }\n }\n\n /**\n * Build idempotency headers if key is provided\n */\n private buildIdempotencyHeaders(\n idempotencyKey?: string\n ): Record<string, string> {\n if (!idempotencyKey) return {}\n return { \"Idempotency-Key\": idempotencyKey }\n }\n\n /**\n * Assert credits are available, throw typed errors if not\n */\n private assertCreditsAvailable(\n verification: VerifyCreditsResponse,\n requiredAmount: number\n ): void {\n if (!verification.hasEnoughCredits) {\n throw new InsufficientCreditsError(\n verification.remainingCredits,\n requiredAmount\n )\n }\n\n if (verification.expiresAt) {\n const expirationDate = new Date(verification.expiresAt)\n if (expirationDate < new Date()) {\n throw new CreditsExpiredError(expirationDate)\n }\n }\n }\n}\n","import { createHmac, timingSafeEqual } from \"crypto\"\n\n/**\n * Generates an HMAC signature for webhook verification\n */\nexport function generateSignature(payload: string, secret: string): string {\n return createHmac(\"sha256\", secret).update(payload).digest(\"hex\")\n}\n\n/**\n * Verifies that a webhook signature is valid using timing-safe comparison\n */\nexport function verifySignature(\n payload: string,\n signature: string,\n secret: string\n): boolean {\n const expectedSignature = generateSignature(payload, secret)\n\n // Use timing-safe comparison to prevent timing attacks\n if (signature.length !== expectedSignature.length) {\n return false\n }\n\n return timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))\n}\n","import {\n creditsExpiredPayloadSchema,\n creditsLowPayloadSchema,\n paymentSuccessPayloadSchema,\n webhookEventSchema\n} from \"../schemas/validation\"\nimport type {\n CreditsExpiredPayload,\n CreditsLowPayload,\n PaymentSuccessPayload,\n WebhookEvent,\n WebhookPayload\n} from \"../types/webhooks\"\nimport { verifySignature } from \"../utils/crypto\"\nimport { CloseLoopError } from \"../utils/errors\"\n\n/**\n * Parameters for verifying a webhook\n */\nexport interface VerifyWebhookParams {\n /**\n * Raw request body as string or Buffer\n */\n payload: string | Buffer\n\n /**\n * X-CloseLoop-Signature header value\n */\n signature: string\n\n /**\n * Your webhook secret\n */\n secret: string\n}\n\n// Validation error codes\nconst ERROR_CODES = {\n VALIDATION: \"VALIDATION_ERROR\",\n INVALID_SIGNATURE: \"INVALID_SIGNATURE\",\n INVALID_PAYLOAD: \"INVALID_PAYLOAD\"\n} as const\n\n/**\n * Resource for webhook signature verification and event handling\n */\nexport class Webhooks {\n /**\n * Verify a webhook signature and parse the event.\n *\n * @example\n * ```typescript\n * // Express handler\n * app.post(\"/webhook\", (req, res) => {\n * try {\n * const event = client.webhooks.verify({\n * payload: req.body,\n * signature: req.headers[\"x-closeloop-signature\"],\n * secret: process.env.WEBHOOK_SECRET!\n * })\n *\n * if (client.webhooks.isPaymentSuccess(event)) {\n * const { type, walletAddress, planId, amount } = event.data\n * // Handle payment success\n * }\n *\n * res.json({ received: true })\n * } catch (error) {\n * res.status(400).json({ error: \"Invalid signature\" })\n * }\n * })\n * ```\n *\n * @example\n * ```typescript\n * // Next.js App Router\n * export async function POST(request: Request) {\n * const payload = await request.text()\n * const signature = request.headers.get(\"x-closeloop-signature\")!\n *\n * const event = client.webhooks.verify({\n * payload,\n * signature,\n * secret: process.env.WEBHOOK_SECRET!\n * })\n *\n * // Handle event...\n * return Response.json({ received: true })\n * }\n * ```\n *\n * @throws {CloseLoopError} When signature is invalid or payload is malformed\n */\n verify(params: VerifyWebhookParams): WebhookEvent {\n this.validateRequiredParams(params)\n\n const payloadString = this.normalizePayload(params.payload)\n\n this.verifySignatureOrThrow(payloadString, params.signature, params.secret)\n\n const parsed = this.parseJsonOrThrow(payloadString)\n\n return this.validateEventStructure(parsed)\n }\n\n /**\n * Type guard for payment.success events\n * Also validates the payload structure\n *\n * @example\n * ```typescript\n * if (client.webhooks.isPaymentSuccess(event)) {\n * // TypeScript knows event.data is PaymentSuccessPayload\n * console.log(event.data.planId, event.data.walletAddress)\n * }\n * ```\n */\n isPaymentSuccess(\n event: WebhookEvent<WebhookPayload>\n ): event is WebhookEvent<PaymentSuccessPayload> {\n return (\n event.type === \"payment.success\" &&\n paymentSuccessPayloadSchema.safeParse(event.data).success\n )\n }\n\n /**\n * Type guard for credits.low events\n * Also validates the payload structure\n *\n * @example\n * ```typescript\n * if (client.webhooks.isCreditsLow(event)) {\n * // TypeScript knows event.data is CreditsLowPayload\n * console.log(`Low balance: ${event.data.remainingCredits}`)\n * }\n * ```\n */\n isCreditsLow(\n event: WebhookEvent<WebhookPayload>\n ): event is WebhookEvent<CreditsLowPayload> {\n return (\n event.type === \"credits.low\" &&\n creditsLowPayloadSchema.safeParse(event.data).success\n )\n }\n\n /**\n * Type guard for credits.expired events\n * Also validates the payload structure\n *\n * @example\n * ```typescript\n * if (client.webhooks.isCreditsExpired(event)) {\n * // TypeScript knows event.data is CreditsExpiredPayload\n * console.log(`Expired: ${event.data.expiredCredits} credits`)\n * }\n * ```\n */\n isCreditsExpired(\n event: WebhookEvent<WebhookPayload>\n ): event is WebhookEvent<CreditsExpiredPayload> {\n return (\n event.type === \"credits.expired\" &&\n creditsExpiredPayloadSchema.safeParse(event.data).success\n )\n }\n\n // ==========================================================================\n // Private Helpers\n // ==========================================================================\n\n /**\n * Validate that all required parameters are present\n */\n private validateRequiredParams(params: VerifyWebhookParams): void {\n if (!params.payload) {\n throw new CloseLoopError(\n \"Webhook payload is required\",\n ERROR_CODES.VALIDATION,\n 400\n )\n }\n\n if (!params.signature) {\n throw new CloseLoopError(\n \"Webhook signature is required\",\n ERROR_CODES.VALIDATION,\n 400\n )\n }\n\n if (!params.secret) {\n throw new CloseLoopError(\n \"Webhook secret is required\",\n ERROR_CODES.VALIDATION,\n 400\n )\n }\n }\n\n /**\n * Convert payload to string regardless of input type\n */\n private normalizePayload(payload: string | Buffer): string {\n return typeof payload === \"string\" ? payload : payload.toString(\"utf8\")\n }\n\n /**\n * Verify HMAC signature or throw error\n */\n private verifySignatureOrThrow(\n payload: string,\n signature: string,\n secret: string\n ): void {\n if (!verifySignature(payload, signature, secret)) {\n throw new CloseLoopError(\n \"Invalid webhook signature\",\n ERROR_CODES.INVALID_SIGNATURE,\n 401\n )\n }\n }\n\n /**\n * Parse JSON payload or throw error\n */\n private parseJsonOrThrow(payload: string): unknown {\n try {\n return JSON.parse(payload)\n } catch {\n throw new CloseLoopError(\n \"Invalid webhook payload: could not parse JSON\",\n ERROR_CODES.INVALID_PAYLOAD,\n 400\n )\n }\n }\n\n /**\n * Validate event structure using Zod schema\n */\n private validateEventStructure(parsed: unknown): WebhookEvent {\n const result = webhookEventSchema.safeParse(parsed)\n\n if (!result.success) {\n throw new CloseLoopError(\n \"Invalid webhook payload structure\",\n ERROR_CODES.INVALID_PAYLOAD,\n 400\n )\n }\n\n // Safe to cast: we've validated the base structure, and the type guards\n // will validate specific payload types when used\n return result.data as unknown as WebhookEvent\n }\n}\n","import { DEFAULT_TIMEOUT, USER_AGENT } from \"../constants\"\nimport {\n AuthenticationError,\n CloseLoopError,\n NetworkError,\n NotFoundError,\n RateLimitError\n} from \"./errors\"\n\nexport interface HttpClientOptions {\n baseUrl: string\n apiKey: string\n timeout?: number\n}\n\nexport interface RequestOptions {\n method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\"\n path: string\n body?: unknown\n headers?: Record<string, string>\n}\n\ninterface ErrorResponse {\n message?: string\n code?: string\n details?: Record<string, unknown>\n}\n\n/**\n * HTTP client for making requests to the CloseLoop API\n */\nexport class HttpClient {\n private baseUrl: string\n private apiKey: string\n private timeout: number\n\n constructor(options: HttpClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\") // Remove trailing slash\n this.apiKey = options.apiKey\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT\n }\n\n /**\n * Make an HTTP request to the CloseLoop API\n */\n async request<T>(options: RequestOptions): Promise<T> {\n const url = `${this.baseUrl}${options.path}`\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), this.timeout)\n\n try {\n const response = await fetch(url, {\n method: options.method,\n headers: {\n \"Content-Type\": \"application/json\",\n \"User-Agent\": USER_AGENT,\n \"X-API-Key\": this.apiKey,\n ...options.headers\n },\n body: options.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal\n })\n\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n await this.handleErrorResponse(response)\n }\n\n // Handle empty responses (204 No Content)\n if (response.status === 204) {\n return {} as T\n }\n\n return (await response.json()) as T\n } catch (error) {\n clearTimeout(timeoutId)\n\n if (error instanceof CloseLoopError) {\n throw error\n }\n\n if (error instanceof Error && error.name === \"AbortError\") {\n throw new NetworkError(\"Request timeout\")\n }\n\n throw new NetworkError(\n error instanceof Error ? error.message : \"Network request failed\",\n error instanceof Error ? error : undefined\n )\n }\n }\n\n private async handleErrorResponse(response: Response): Promise<never> {\n let errorData: ErrorResponse = {}\n\n try {\n const json = (await response.json()) as ErrorResponse\n errorData = json\n } catch {\n // Response body is not JSON\n }\n\n const message =\n errorData.message || `Request failed with status ${response.status}`\n\n switch (response.status) {\n case 401:\n throw new AuthenticationError(message)\n case 404:\n throw new NotFoundError(message)\n case 429: {\n const retryAfter = response.headers.get(\"Retry-After\")\n throw new RateLimitError(\n retryAfter ? parseInt(retryAfter, 10) : undefined\n )\n }\n default:\n throw new CloseLoopError(\n message,\n errorData.code || \"API_ERROR\",\n response.status,\n errorData.details\n )\n }\n }\n}\n","import { DEFAULT_BASE_URL } from \"./constants\"\nimport { Balances } from \"./resources/balances\"\nimport { Credits } from \"./resources/credits\"\nimport { Webhooks } from \"./resources/webhooks\"\nimport { HttpClient } from \"./utils/http\"\n\n/**\n * Configuration options for the CloseLoop client\n */\nexport interface CloseLoopOptions {\n /**\n * Your CloseLoop API key.\n * Get this from your CloseLoop dashboard.\n */\n apiKey: string\n\n /**\n * Base URL for the CloseLoop API.\n * Defaults to https://closeloop.app\n */\n baseUrl?: string\n\n /**\n * Request timeout in milliseconds.\n * Defaults to 30000 (30 seconds)\n */\n timeout?: number\n}\n\n/**\n * CloseLoop SDK client for credit billing integration.\n *\n * @example\n * ```typescript\n * import { CloseLoop } from \"@closeloop/sdk\"\n *\n * const client = new CloseLoop({\n * apiKey: process.env.CLOSELOOP_API_KEY!\n * })\n *\n * // Verify credits\n * const verification = await client.credits.verify({\n * walletAddress: \"0x1234...\",\n * planId: \"plan_abc123\",\n * amount: 1\n * })\n *\n * if (verification.hasEnoughCredits) {\n * // Process request...\n *\n * // Consume credit\n * await client.credits.consume({\n * walletAddress: \"0x1234...\",\n * planId: \"plan_abc123\",\n * amount: 1,\n * consumedBy: \"my-service\"\n * })\n * }\n * ```\n */\nexport class CloseLoop {\n private httpClient: HttpClient\n\n /**\n * Credit consumption and verification operations\n */\n readonly credits: Credits\n\n /**\n * Credit balance queries\n */\n readonly balances: Balances\n\n /**\n * Webhook signature verification utilities\n */\n readonly webhooks: Webhooks\n\n constructor(options: CloseLoopOptions) {\n if (!options.apiKey) {\n throw new Error(\"CloseLoop API key is required\")\n }\n\n const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL\n\n this.httpClient = new HttpClient({\n baseUrl,\n apiKey: options.apiKey,\n timeout: options.timeout\n })\n\n this.credits = new Credits(this.httpClient)\n this.balances = new Balances(this.httpClient)\n this.webhooks = new Webhooks()\n }\n}\n"],"mappings":"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","names":["ENDPOINTS"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/constants.ts","../src/schemas/validation.ts","../src/utils/errors.ts","../src/utils/validation.ts","../src/resources/balances.ts","../src/resources/credits.ts","../src/utils/crypto.ts","../src/resources/webhooks.ts","../src/utils/http.ts","../src/client.ts"],"sourcesContent":["// Main client\nexport { CloseLoop } from \"./client\"\nexport type { CloseLoopOptions } from \"./client\"\n\n// Resources\nexport { Credits } from \"./resources/credits\"\nexport { Balances } from \"./resources/balances\"\nexport { Webhooks } from \"./resources/webhooks\"\nexport type { VerifyWebhookParams } from \"./resources/webhooks\"\n\n// Errors\nexport {\n CloseLoopError,\n InsufficientCreditsError,\n CreditsExpiredError,\n AuthenticationError,\n RateLimitError,\n NetworkError,\n NotFoundError,\n ValidationError\n} from \"./utils/errors\"\n\n// Utilities\nexport { validateInput } from \"./utils/validation\"\n\n// Credit types\nexport type {\n VerifyCreditsParams,\n VerifyCreditsResponse,\n ConsumeCreditsParams,\n ConsumeCreditsResponse,\n BatchConsumeParams,\n BatchConsumeResponse\n} from \"./types/credits\"\n\n// Balance types\nexport type {\n CreditBalance,\n GetBalanceParams,\n ListBalancesParams,\n ListBalancesResponse,\n CreditTransaction,\n ListTransactionsParams,\n ListTransactionsResponse,\n CreditStats\n} from \"./types/balances\"\n\n// Webhook types\nexport type {\n WebhookEventType,\n WebhookEvent,\n PaymentSuccessPayload,\n CreditsLowPayload,\n CreditsExpiredPayload,\n WebhookPayload\n} from \"./types/webhooks\"\n","export const DEFAULT_BASE_URL = \"https://closeloop.app\"\nexport const DEFAULT_TIMEOUT = 30000 // 30 seconds\nexport const SDK_VERSION = \"0.1.0\"\nexport const USER_AGENT = `closeloop-node/${SDK_VERSION}`\n","import { z } from \"zod\"\n\n/**\n * Ethereum wallet address validation (0x + 40 hex chars)\n */\nexport const walletAddressSchema = z\n .string()\n .regex(/^0x[a-fA-F0-9]{40}$/, \"Invalid Ethereum wallet address\")\n\n/**\n * Plan ID validation (non-empty string with reasonable length)\n */\nexport const planIdSchema = z\n .string()\n .min(1, \"Plan ID is required\")\n .max(100, \"Plan ID too long\")\n\n/**\n * Balance ID validation\n */\nexport const balanceIdSchema = z\n .string()\n .min(1, \"Balance ID is required\")\n .max(100, \"Balance ID too long\")\n\n/**\n * Credit amount validation (positive integer with reasonable max)\n */\nexport const creditAmountSchema = z\n .number()\n .int(\"Amount must be an integer\")\n .positive(\"Amount must be positive\")\n .max(1_000_000_000, \"Amount exceeds maximum\")\n\n/**\n * Pagination limit validation\n */\nexport const paginationLimitSchema = z\n .number()\n .int()\n .positive()\n .max(100, \"Limit cannot exceed 100\")\n\n/**\n * Transaction type validation\n */\nexport const transactionTypeSchema = z.enum([\n \"PURCHASE\",\n \"CONSUMPTION\",\n \"REFUND\",\n \"EXPIRATION\"\n])\n\n/**\n * Dangerous prototype pollution keys to skip\n */\nconst DANGEROUS_KEYS = new Set([\"__proto__\", \"constructor\", \"prototype\"])\n\n/**\n * Sanitize metadata object to prevent prototype pollution\n * Creates a clean object with no prototype chain\n */\nexport function sanitizeMetadata(\n metadata: Record<string, unknown> | undefined\n): Record<string, unknown> | undefined {\n if (!metadata) return undefined\n\n // Create a clean object with null prototype to prevent prototype pollution\n const clean = Object.create(null) as Record<string, unknown>\n\n for (const [key, value] of Object.entries(metadata)) {\n // Skip dangerous prototype pollution keys\n if (DANGEROUS_KEYS.has(key)) continue\n\n // Copy all JSON-serializable values\n clean[key] = value\n }\n\n return Object.freeze(clean)\n}\n\n// ============================================================================\n// Credit Schemas\n// ============================================================================\n\nexport const verifyCreditsSchema = z.object({\n walletAddress: walletAddressSchema,\n planId: planIdSchema,\n amount: creditAmountSchema\n})\n\nexport const consumeCreditsSchema = z.object({\n walletAddress: walletAddressSchema,\n planId: planIdSchema,\n amount: creditAmountSchema,\n consumedBy: z.string().max(100).optional(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n idempotencyKey: z.string().max(100).optional()\n})\n\nexport const batchConsumeSchema = z.object({\n operations: z\n .array(\n z.object({\n walletAddress: walletAddressSchema,\n planId: planIdSchema,\n amount: creditAmountSchema,\n consumedBy: z.string().max(100).optional(),\n metadata: z.record(z.string(), z.unknown()).optional()\n })\n )\n .min(1, \"At least one operation required\")\n .max(100, \"Maximum 100 operations per batch\"),\n atomic: z.boolean().optional()\n})\n\n// ============================================================================\n// Balance Schemas\n// ============================================================================\n\nexport const getBalanceSchema = z.object({\n walletAddress: walletAddressSchema,\n planId: planIdSchema\n})\n\nexport const listBalancesSchema = z.object({\n walletAddress: walletAddressSchema,\n activeOnly: z.boolean().optional(),\n limit: paginationLimitSchema.optional(),\n cursor: z.string().max(200).optional()\n})\n\nexport const listTransactionsSchema = z.object({\n balanceId: balanceIdSchema,\n type: transactionTypeSchema.optional(),\n limit: paginationLimitSchema.optional(),\n cursor: z.string().max(200).optional()\n})\n\n// ============================================================================\n// Webhook Schemas\n// ============================================================================\n\nexport const webhookEventTypeSchema = z.enum([\n \"payment.success\",\n \"credits.low\",\n \"credits.expired\"\n])\n\nexport const webhookEventSchema = z.object({\n type: webhookEventTypeSchema,\n id: z.string().min(1),\n createdAt: z.string(),\n data: z.record(z.string(), z.unknown())\n})\n\nexport const paymentSuccessPayloadSchema = z\n .object({\n type: z.string(),\n planId: z.string(),\n planName: z.string(),\n amount: z.number(),\n walletAddress: z.string(),\n transactionId: z.string()\n })\n .loose()\n\nexport const creditsLowPayloadSchema = z.object({\n walletAddress: z.string(),\n planId: z.string(),\n remainingCredits: z.number(),\n threshold: z.number()\n})\n\nexport const creditsExpiredPayloadSchema = z.object({\n walletAddress: z.string(),\n planId: z.string(),\n expiredCredits: z.number(),\n expiresAt: z.string()\n})\n","/**\n * Base error class for all CloseLoop SDK errors\n */\nexport class CloseLoopError extends Error {\n constructor(\n message: string,\n public code: string,\n public statusCode?: number,\n public details?: Record<string, unknown>\n ) {\n super(message)\n this.name = \"CloseLoopError\"\n }\n}\n\n/**\n * Thrown when a user doesn't have enough credits for an operation\n */\nexport class InsufficientCreditsError extends CloseLoopError {\n constructor(\n public remainingCredits: number,\n public requiredCredits: number\n ) {\n super(\n `Insufficient credits: ${remainingCredits} available, ${requiredCredits} required`,\n \"INSUFFICIENT_CREDITS\",\n 402\n )\n this.name = \"InsufficientCreditsError\"\n }\n}\n\n/**\n * Thrown when credits have expired\n */\nexport class CreditsExpiredError extends CloseLoopError {\n constructor(public expiresAt: Date) {\n super(\n `Credits expired at ${expiresAt.toISOString()}`,\n \"CREDITS_EXPIRED\",\n 410\n )\n this.name = \"CreditsExpiredError\"\n }\n}\n\n/**\n * Thrown when authentication fails (invalid API key)\n */\nexport class AuthenticationError extends CloseLoopError {\n constructor(message = \"Invalid API key\") {\n super(message, \"AUTHENTICATION_ERROR\", 401)\n this.name = \"AuthenticationError\"\n }\n}\n\n/**\n * Thrown when rate limit is exceeded\n */\nexport class RateLimitError extends CloseLoopError {\n constructor(public retryAfter?: number) {\n super(\"Rate limit exceeded\", \"RATE_LIMIT\", 429)\n this.name = \"RateLimitError\"\n }\n}\n\n/**\n * Thrown when a network error occurs\n */\nexport class NetworkError extends CloseLoopError {\n constructor(\n message: string,\n public cause?: Error\n ) {\n super(message, \"NETWORK_ERROR\")\n this.name = \"NetworkError\"\n }\n}\n\n/**\n * Thrown when a resource is not found\n */\nexport class NotFoundError extends CloseLoopError {\n constructor(resource: string) {\n super(`${resource} not found`, \"NOT_FOUND\", 404)\n this.name = \"NotFoundError\"\n }\n}\n\n/**\n * Thrown when input validation fails\n */\nexport class ValidationError extends CloseLoopError {\n constructor(\n message: string,\n public field?: string\n ) {\n super(message, \"VALIDATION_ERROR\", 400)\n this.name = \"ValidationError\"\n }\n}\n","import { CloseLoopError } from \"./errors\"\n\n/**\n * Generic schema interface for validation\n */\nexport interface ValidationSchema<T> {\n safeParse: (data: unknown) => {\n success: boolean\n data?: T\n error?: { message: string }\n }\n}\n\n/**\n * Validate input against a Zod schema\n * Throws CloseLoopError if validation fails\n *\n * @param schema - Zod schema to validate against\n * @param data - Data to validate\n * @returns Validated and typed data\n * @throws {CloseLoopError} When validation fails\n */\nexport function validateInput<T>(\n schema: ValidationSchema<T>,\n data: unknown\n): T {\n const result = schema.safeParse(data)\n\n if (!result.success) {\n throw new CloseLoopError(\n `Validation error: ${result.error?.message || \"Invalid input\"}`,\n \"VALIDATION_ERROR\",\n 400\n )\n }\n\n return result.data as T\n}\n\n/**\n * Validate a single value against a Zod schema\n * Returns a boolean without throwing\n */\nexport function isValid<T>(\n schema: ValidationSchema<T>,\n data: unknown\n): data is T {\n return schema.safeParse(data).success\n}\n","import {\n getBalanceSchema,\n listBalancesSchema,\n listTransactionsSchema,\n walletAddressSchema\n} from \"../schemas/validation\"\nimport type {\n CreditBalance,\n CreditStats,\n GetBalanceParams,\n ListBalancesParams,\n ListBalancesResponse,\n ListTransactionsParams,\n ListTransactionsResponse\n} from \"../types/balances\"\nimport { NotFoundError } from \"../utils/errors\"\nimport { HttpClient } from \"../utils/http\"\nimport { validateInput } from \"../utils/validation\"\n\n// API Endpoints\nconst ENDPOINTS = {\n BALANCE: \"/api/credit/balance\",\n BALANCES: \"/api/credit/balances\",\n STATS: \"/api/credit/stats\"\n} as const\n\n/**\n * Resource for querying credit balances and transaction history\n */\nexport class Balances {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Get a specific credit balance for a wallet and plan.\n *\n * @example\n * ```typescript\n * const balance = await client.balances.get({\n * walletAddress: \"0x1234...\",\n * planId: \"plan_abc123\"\n * })\n *\n * if (balance) {\n * console.log(`Credits: ${balance.remainingCredits}/${balance.totalCredits}`)\n * }\n * ```\n *\n * @returns The credit balance, or null if not found\n * @throws {CloseLoopError} When input validation fails\n */\n async get(params: GetBalanceParams): Promise<CreditBalance | null> {\n const validated = validateInput(getBalanceSchema, params)\n\n const query = this.buildQueryString({\n walletAddress: validated.walletAddress,\n plan: validated.planId\n })\n\n try {\n return await this.http.request<CreditBalance>({\n method: \"GET\",\n path: `${ENDPOINTS.BALANCE}?${query}`\n })\n } catch (error) {\n if (error instanceof NotFoundError) {\n return null\n }\n throw error\n }\n }\n\n /**\n * List all credit balances for a wallet.\n *\n * @example\n * ```typescript\n * const { balances, nextCursor } = await client.balances.list({\n * walletAddress: \"0x1234...\",\n * activeOnly: true,\n * limit: 10\n * })\n *\n * for (const balance of balances) {\n * console.log(`${balance.planName}: ${balance.remainingCredits} credits`)\n * }\n *\n * // Paginate if needed\n * if (nextCursor) {\n * const nextPage = await client.balances.list({\n * walletAddress: \"0x1234...\",\n * cursor: nextCursor\n * })\n * }\n * ```\n *\n * @throws {CloseLoopError} When input validation fails\n */\n async list(params: ListBalancesParams): Promise<ListBalancesResponse> {\n const validated = validateInput(listBalancesSchema, params)\n\n const query = this.buildQueryString({\n walletAddress: validated.walletAddress,\n activeOnly: validated.activeOnly,\n limit: validated.limit,\n cursor: validated.cursor\n })\n\n return this.http.request<ListBalancesResponse>({\n method: \"GET\",\n path: `${ENDPOINTS.BALANCES}?${query}`\n })\n }\n\n /**\n * Get transaction history for a balance.\n *\n * @example\n * ```typescript\n * const { transactions } = await client.balances.transactions({\n * balanceId: \"bal_xyz\",\n * type: \"CONSUMPTION\",\n * limit: 50\n * })\n *\n * for (const tx of transactions) {\n * console.log(`${tx.type}: ${tx.amount} - ${tx.description}`)\n * }\n * ```\n *\n * @throws {CloseLoopError} When input validation fails\n */\n async transactions(\n params: ListTransactionsParams\n ): Promise<ListTransactionsResponse> {\n const validated = validateInput(listTransactionsSchema, params)\n\n const query = this.buildQueryString({\n type: validated.type,\n limit: validated.limit,\n cursor: validated.cursor\n })\n\n const basePath = `${ENDPOINTS.BALANCE}/${encodeURIComponent(validated.balanceId)}/transactions`\n const path = query ? `${basePath}?${query}` : basePath\n\n return this.http.request<ListTransactionsResponse>({\n method: \"GET\",\n path\n })\n }\n\n /**\n * Get aggregated stats for a wallet's credits.\n *\n * @example\n * ```typescript\n * const stats = await client.balances.stats(\"0x1234...\")\n * console.log(`Total: ${stats.totalCredits}, Used: ${stats.totalUsed}`)\n * ```\n *\n * @throws {CloseLoopError} When input validation fails\n */\n async stats(walletAddress: string): Promise<CreditStats> {\n // Validate using the shared utility for consistency\n validateInput(walletAddressSchema, walletAddress)\n\n const query = this.buildQueryString({ walletAddress })\n\n return this.http.request<CreditStats>({\n method: \"GET\",\n path: `${ENDPOINTS.STATS}?${query}`\n })\n }\n\n // ==========================================================================\n // Private Helpers\n // ==========================================================================\n\n /**\n * Build URL query string from params, filtering out undefined values\n */\n private buildQueryString(\n params: Record<string, string | number | boolean | undefined>\n ): string {\n const query = new URLSearchParams()\n\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined) {\n query.set(key, String(value))\n }\n }\n\n return query.toString()\n }\n}\n","import {\n batchConsumeSchema,\n consumeCreditsSchema,\n sanitizeMetadata,\n verifyCreditsSchema\n} from \"../schemas/validation\"\nimport type {\n BatchConsumeParams,\n BatchConsumeResponse,\n ConsumeCreditsParams,\n ConsumeCreditsResponse,\n VerifyCreditsParams,\n VerifyCreditsResponse\n} from \"../types/credits\"\nimport { CreditsExpiredError, InsufficientCreditsError } from \"../utils/errors\"\nimport { HttpClient } from \"../utils/http\"\nimport { validateInput } from \"../utils/validation\"\n\n// API Endpoints\nconst ENDPOINTS = {\n VERIFY: \"/api/credit/verify\",\n CONSUME: \"/api/credit/consume\",\n BATCH_CONSUME: \"/api/credit/consume/batch\"\n} as const\n\n/**\n * Resource for credit verification and consumption operations\n */\nexport class Credits {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Verify if a user has enough credits without consuming them.\n *\n * @example\n * ```typescript\n * const result = await client.credits.verify({\n * walletAddress: \"0x1234...\",\n * planId: \"plan_abc123\",\n * amount: 10\n * })\n *\n * if (result.hasEnoughCredits) {\n * // Proceed with service\n * }\n * ```\n *\n * @throws {CloseLoopError} When input validation fails\n */\n async verify(params: VerifyCreditsParams): Promise<VerifyCreditsResponse> {\n const validated = validateInput(verifyCreditsSchema, params)\n\n return this.http.request<VerifyCreditsResponse>({\n method: \"POST\",\n path: ENDPOINTS.VERIFY,\n body: validated\n })\n }\n\n /**\n * Consume credits from a user's balance.\n *\n * @example\n * ```typescript\n * const result = await client.credits.consume({\n * walletAddress: \"0x1234...\",\n * planId: \"plan_abc123\",\n * amount: 1,\n * consumedBy: \"ai-generation\",\n * metadata: { requestId: \"req_xyz\" }\n * })\n *\n * console.log(`Remaining: ${result.remainingCredits}`)\n * ```\n *\n * @throws {InsufficientCreditsError} When user doesn't have enough credits\n * @throws {CreditsExpiredError} When credits have expired\n * @throws {CloseLoopError} When input validation fails\n */\n async consume(params: ConsumeCreditsParams): Promise<ConsumeCreditsResponse> {\n const validated = validateInput(consumeCreditsSchema, params)\n\n return this.http.request<ConsumeCreditsResponse>({\n method: \"POST\",\n path: ENDPOINTS.CONSUME,\n body: this.buildConsumeBody(validated),\n headers: this.buildIdempotencyHeaders(validated.idempotencyKey)\n })\n }\n\n /**\n * Verify and consume credits in a single atomic operation.\n * This is useful when you want to ensure credits are available\n * before consuming them, without race conditions.\n *\n * @example\n * ```typescript\n * try {\n * const result = await client.credits.verifyAndConsume({\n * walletAddress: \"0x1234...\",\n * planId: \"plan_abc123\",\n * amount: 5,\n * consumedBy: \"batch-processing\"\n * })\n * // Credits were verified and consumed\n * } catch (error) {\n * if (error instanceof InsufficientCreditsError) {\n * // Handle insufficient credits\n * }\n * }\n * ```\n *\n * @throws {InsufficientCreditsError} When user doesn't have enough credits\n * @throws {CreditsExpiredError} When credits have expired\n * @throws {CloseLoopError} When input validation fails\n */\n async verifyAndConsume(\n params: ConsumeCreditsParams\n ): Promise<ConsumeCreditsResponse> {\n const validated = validateInput(consumeCreditsSchema, params)\n\n // First verify\n const verification = await this.http.request<VerifyCreditsResponse>({\n method: \"POST\",\n path: ENDPOINTS.VERIFY,\n body: {\n walletAddress: validated.walletAddress,\n planId: validated.planId,\n amount: validated.amount\n }\n })\n\n this.assertCreditsAvailable(verification, validated.amount)\n\n // Then consume\n return this.http.request<ConsumeCreditsResponse>({\n method: \"POST\",\n path: ENDPOINTS.CONSUME,\n body: this.buildConsumeBody(validated),\n headers: this.buildIdempotencyHeaders(validated.idempotencyKey)\n })\n }\n\n /**\n * Consume credits in batch.\n *\n * @example\n * ```typescript\n * const result = await client.credits.batchConsume({\n * operations: [\n * { walletAddress: \"0x1234...\", planId: \"plan_a\", amount: 1 },\n * { walletAddress: \"0x5678...\", planId: \"plan_b\", amount: 2 }\n * ],\n * atomic: false // Allow partial success\n * })\n *\n * console.log(`Success: ${result.successCount}, Failed: ${result.failedCount}`)\n * ```\n *\n * @throws {CloseLoopError} When input validation fails\n */\n async batchConsume(\n params: BatchConsumeParams\n ): Promise<BatchConsumeResponse> {\n const validated = validateInput(batchConsumeSchema, params)\n\n // Sanitize metadata in each operation\n const sanitizedOperations = validated.operations.map(op => ({\n ...op,\n metadata: sanitizeMetadata(op.metadata)\n }))\n\n return this.http.request<BatchConsumeResponse>({\n method: \"POST\",\n path: ENDPOINTS.BATCH_CONSUME,\n body: {\n operations: sanitizedOperations,\n atomic: validated.atomic\n }\n })\n }\n\n // ==========================================================================\n // Private Helpers\n // ==========================================================================\n\n /**\n * Build consumption request body with sanitized metadata\n */\n private buildConsumeBody(validated: {\n walletAddress: string\n planId: string\n amount: number\n consumedBy?: string\n metadata?: Record<string, unknown>\n }) {\n return {\n walletAddress: validated.walletAddress,\n planId: validated.planId,\n amount: validated.amount,\n consumedBy: validated.consumedBy,\n metadata: sanitizeMetadata(validated.metadata)\n }\n }\n\n /**\n * Build idempotency headers if key is provided\n */\n private buildIdempotencyHeaders(\n idempotencyKey?: string\n ): Record<string, string> {\n if (!idempotencyKey) return {}\n return { \"Idempotency-Key\": idempotencyKey }\n }\n\n /**\n * Assert credits are available, throw typed errors if not\n */\n private assertCreditsAvailable(\n verification: VerifyCreditsResponse,\n requiredAmount: number\n ): void {\n if (!verification.hasEnoughCredits) {\n throw new InsufficientCreditsError(\n verification.remainingCredits,\n requiredAmount\n )\n }\n\n if (verification.expiresAt) {\n const expirationDate = new Date(verification.expiresAt)\n if (expirationDate < new Date()) {\n throw new CreditsExpiredError(expirationDate)\n }\n }\n }\n}\n","import { createHmac, timingSafeEqual } from \"crypto\"\n\n/**\n * Generates an HMAC signature for webhook verification\n */\nexport function generateSignature(payload: string, secret: string): string {\n return createHmac(\"sha256\", secret).update(payload).digest(\"hex\")\n}\n\n/**\n * Verifies that a webhook signature is valid using timing-safe comparison\n */\nexport function verifySignature(\n payload: string,\n signature: string,\n secret: string\n): boolean {\n const expectedSignature = generateSignature(payload, secret)\n\n // Use timing-safe comparison to prevent timing attacks\n if (signature.length !== expectedSignature.length) {\n return false\n }\n\n return timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))\n}\n","import {\n creditsExpiredPayloadSchema,\n creditsLowPayloadSchema,\n paymentSuccessPayloadSchema,\n webhookEventSchema\n} from \"../schemas/validation\"\nimport type {\n CreditsExpiredPayload,\n CreditsLowPayload,\n PaymentSuccessPayload,\n WebhookEvent,\n WebhookPayload\n} from \"../types/webhooks\"\nimport { verifySignature } from \"../utils/crypto\"\nimport { CloseLoopError } from \"../utils/errors\"\n\n/**\n * Parameters for verifying a webhook\n */\nexport interface VerifyWebhookParams {\n /**\n * Raw request body as string or Buffer\n */\n payload: string | Buffer\n\n /**\n * X-CloseLoop-Signature header value\n */\n signature: string\n\n /**\n * Your webhook secret\n */\n secret: string\n}\n\n// Validation error codes\nconst ERROR_CODES = {\n VALIDATION: \"VALIDATION_ERROR\",\n INVALID_SIGNATURE: \"INVALID_SIGNATURE\",\n INVALID_PAYLOAD: \"INVALID_PAYLOAD\"\n} as const\n\n/**\n * Resource for webhook signature verification and event handling\n */\nexport class Webhooks {\n /**\n * Verify a webhook signature and parse the event.\n *\n * @example\n * ```typescript\n * // Express handler\n * app.post(\"/webhook\", (req, res) => {\n * try {\n * const event = client.webhooks.verify({\n * payload: req.body,\n * signature: req.headers[\"x-closeloop-signature\"],\n * secret: process.env.WEBHOOK_SECRET!\n * })\n *\n * if (client.webhooks.isPaymentSuccess(event)) {\n * const { type, walletAddress, planId, amount } = event.data\n * // Handle payment success\n * }\n *\n * res.json({ received: true })\n * } catch (error) {\n * res.status(400).json({ error: \"Invalid signature\" })\n * }\n * })\n * ```\n *\n * @example\n * ```typescript\n * // Next.js App Router\n * export async function POST(request: Request) {\n * const payload = await request.text()\n * const signature = request.headers.get(\"x-closeloop-signature\")!\n *\n * const event = client.webhooks.verify({\n * payload,\n * signature,\n * secret: process.env.WEBHOOK_SECRET!\n * })\n *\n * // Handle event...\n * return Response.json({ received: true })\n * }\n * ```\n *\n * @throws {CloseLoopError} When signature is invalid or payload is malformed\n */\n verify(params: VerifyWebhookParams): WebhookEvent {\n this.validateRequiredParams(params)\n\n const payloadString = this.normalizePayload(params.payload)\n\n this.verifySignatureOrThrow(payloadString, params.signature, params.secret)\n\n const parsed = this.parseJsonOrThrow(payloadString)\n\n return this.validateEventStructure(parsed)\n }\n\n /**\n * Type guard for payment.success events\n * Also validates the payload structure\n *\n * @example\n * ```typescript\n * if (client.webhooks.isPaymentSuccess(event)) {\n * // TypeScript knows event.data is PaymentSuccessPayload\n * console.log(event.data.planId, event.data.walletAddress)\n * }\n * ```\n */\n isPaymentSuccess(\n event: WebhookEvent<WebhookPayload>\n ): event is WebhookEvent<PaymentSuccessPayload> {\n return (\n event.type === \"payment.success\" &&\n paymentSuccessPayloadSchema.safeParse(event.data).success\n )\n }\n\n /**\n * Type guard for credits.low events\n * Also validates the payload structure\n *\n * @example\n * ```typescript\n * if (client.webhooks.isCreditsLow(event)) {\n * // TypeScript knows event.data is CreditsLowPayload\n * console.log(`Low balance: ${event.data.remainingCredits}`)\n * }\n * ```\n */\n isCreditsLow(\n event: WebhookEvent<WebhookPayload>\n ): event is WebhookEvent<CreditsLowPayload> {\n return (\n event.type === \"credits.low\" &&\n creditsLowPayloadSchema.safeParse(event.data).success\n )\n }\n\n /**\n * Type guard for credits.expired events\n * Also validates the payload structure\n *\n * @example\n * ```typescript\n * if (client.webhooks.isCreditsExpired(event)) {\n * // TypeScript knows event.data is CreditsExpiredPayload\n * console.log(`Expired: ${event.data.expiredCredits} credits`)\n * }\n * ```\n */\n isCreditsExpired(\n event: WebhookEvent<WebhookPayload>\n ): event is WebhookEvent<CreditsExpiredPayload> {\n return (\n event.type === \"credits.expired\" &&\n creditsExpiredPayloadSchema.safeParse(event.data).success\n )\n }\n\n // ==========================================================================\n // Private Helpers\n // ==========================================================================\n\n /**\n * Validate that all required parameters are present\n */\n private validateRequiredParams(params: VerifyWebhookParams): void {\n if (!params.payload) {\n throw new CloseLoopError(\n \"Webhook payload is required\",\n ERROR_CODES.VALIDATION,\n 400\n )\n }\n\n if (!params.signature) {\n throw new CloseLoopError(\n \"Webhook signature is required\",\n ERROR_CODES.VALIDATION,\n 400\n )\n }\n\n if (!params.secret) {\n throw new CloseLoopError(\n \"Webhook secret is required\",\n ERROR_CODES.VALIDATION,\n 400\n )\n }\n }\n\n /**\n * Convert payload to string regardless of input type\n */\n private normalizePayload(payload: string | Buffer): string {\n return typeof payload === \"string\" ? payload : payload.toString(\"utf8\")\n }\n\n /**\n * Verify HMAC signature or throw error\n */\n private verifySignatureOrThrow(\n payload: string,\n signature: string,\n secret: string\n ): void {\n if (!verifySignature(payload, signature, secret)) {\n throw new CloseLoopError(\n \"Invalid webhook signature\",\n ERROR_CODES.INVALID_SIGNATURE,\n 401\n )\n }\n }\n\n /**\n * Parse JSON payload or throw error\n */\n private parseJsonOrThrow(payload: string): unknown {\n try {\n return JSON.parse(payload)\n } catch {\n throw new CloseLoopError(\n \"Invalid webhook payload: could not parse JSON\",\n ERROR_CODES.INVALID_PAYLOAD,\n 400\n )\n }\n }\n\n /**\n * Validate event structure using Zod schema\n */\n private validateEventStructure(parsed: unknown): WebhookEvent {\n const result = webhookEventSchema.safeParse(parsed)\n\n if (!result.success) {\n throw new CloseLoopError(\n \"Invalid webhook payload structure\",\n ERROR_CODES.INVALID_PAYLOAD,\n 400\n )\n }\n\n // Safe to cast: we've validated the base structure, and the type guards\n // will validate specific payload types when used\n return result.data as unknown as WebhookEvent\n }\n}\n","import { DEFAULT_TIMEOUT, USER_AGENT } from \"../constants\"\nimport {\n AuthenticationError,\n CloseLoopError,\n NetworkError,\n NotFoundError,\n RateLimitError\n} from \"./errors\"\n\nexport interface HttpClientOptions {\n baseUrl: string\n apiKey: string\n timeout?: number\n}\n\nexport interface RequestOptions {\n method: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\"\n path: string\n body?: unknown\n headers?: Record<string, string>\n}\n\ninterface ErrorResponse {\n message?: string\n code?: string\n details?: Record<string, unknown>\n}\n\n/**\n * HTTP client for making requests to the CloseLoop API\n */\nexport class HttpClient {\n private baseUrl: string\n private apiKey: string\n private timeout: number\n\n constructor(options: HttpClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/$/, \"\") // Remove trailing slash\n this.apiKey = options.apiKey\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT\n }\n\n /**\n * Make an HTTP request to the CloseLoop API\n */\n async request<T>(options: RequestOptions): Promise<T> {\n const url = `${this.baseUrl}${options.path}`\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), this.timeout)\n\n try {\n const response = await fetch(url, {\n method: options.method,\n headers: {\n \"Content-Type\": \"application/json\",\n \"User-Agent\": USER_AGENT,\n Authorization: `Bearer ${this.apiKey}`,\n ...options.headers\n },\n body: options.body ? JSON.stringify(options.body) : undefined,\n signal: controller.signal\n })\n\n clearTimeout(timeoutId)\n\n if (!response.ok) {\n await this.handleErrorResponse(response)\n }\n\n // Handle empty responses (204 No Content)\n if (response.status === 204) {\n return {} as T\n }\n\n return (await response.json()) as T\n } catch (error) {\n clearTimeout(timeoutId)\n\n if (error instanceof CloseLoopError) {\n throw error\n }\n\n if (error instanceof Error && error.name === \"AbortError\") {\n throw new NetworkError(\"Request timeout\")\n }\n\n throw new NetworkError(\n error instanceof Error ? error.message : \"Network request failed\",\n error instanceof Error ? error : undefined\n )\n }\n }\n\n private async handleErrorResponse(response: Response): Promise<never> {\n let errorData: ErrorResponse = {}\n\n try {\n const json = (await response.json()) as ErrorResponse\n errorData = json\n } catch {\n // Response body is not JSON\n }\n\n const message =\n errorData.message || `Request failed with status ${response.status}`\n\n switch (response.status) {\n case 401:\n throw new AuthenticationError(message)\n case 404:\n throw new NotFoundError(message)\n case 429: {\n const retryAfter = response.headers.get(\"Retry-After\")\n throw new RateLimitError(\n retryAfter ? parseInt(retryAfter, 10) : undefined\n )\n }\n default:\n throw new CloseLoopError(\n message,\n errorData.code || \"API_ERROR\",\n response.status,\n errorData.details\n )\n }\n }\n}\n","import { DEFAULT_BASE_URL } from \"./constants\"\nimport { Balances } from \"./resources/balances\"\nimport { Credits } from \"./resources/credits\"\nimport { Webhooks } from \"./resources/webhooks\"\nimport { HttpClient } from \"./utils/http\"\n\n/**\n * Configuration options for the CloseLoop client\n */\nexport interface CloseLoopOptions {\n /**\n * Your CloseLoop API key.\n * Get this from your CloseLoop dashboard.\n */\n apiKey: string\n\n /**\n * Base URL for the CloseLoop API.\n * Defaults to https://closeloop.app\n */\n baseUrl?: string\n\n /**\n * Request timeout in milliseconds.\n * Defaults to 30000 (30 seconds)\n */\n timeout?: number\n}\n\n/**\n * CloseLoop SDK client for credit billing integration.\n *\n * @example\n * ```typescript\n * import { CloseLoop } from \"@closeloop/sdk\"\n *\n * const client = new CloseLoop({\n * apiKey: process.env.CLOSELOOP_API_KEY!\n * })\n *\n * // Verify credits\n * const verification = await client.credits.verify({\n * walletAddress: \"0x1234...\",\n * planId: \"plan_abc123\",\n * amount: 1\n * })\n *\n * if (verification.hasEnoughCredits) {\n * // Process request...\n *\n * // Consume credit\n * await client.credits.consume({\n * walletAddress: \"0x1234...\",\n * planId: \"plan_abc123\",\n * amount: 1,\n * consumedBy: \"my-service\"\n * })\n * }\n * ```\n */\nexport class CloseLoop {\n private httpClient: HttpClient\n\n /**\n * Credit consumption and verification operations\n */\n readonly credits: Credits\n\n /**\n * Credit balance queries\n */\n readonly balances: Balances\n\n /**\n * Webhook signature verification utilities\n */\n readonly webhooks: Webhooks\n\n constructor(options: CloseLoopOptions) {\n if (!options.apiKey) {\n throw new Error(\"CloseLoop API key is required\")\n }\n\n const baseUrl = options.baseUrl ?? DEFAULT_BASE_URL\n\n this.httpClient = new HttpClient({\n baseUrl,\n apiKey: options.apiKey,\n timeout: options.timeout\n })\n\n this.credits = new Credits(this.httpClient)\n this.balances = new Balances(this.httpClient)\n this.webhooks = new Webhooks()\n }\n}\n"],"mappings":"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","names":["ENDPOINTS"]}
package/dist/index.mjs CHANGED
@@ -211,7 +211,7 @@ var Balances = class {
211
211
  async get(params) {
212
212
  const validated = validateInput(getBalanceSchema, params);
213
213
  const query = this.buildQueryString({
214
- wallet: validated.walletAddress,
214
+ walletAddress: validated.walletAddress,
215
215
  plan: validated.planId
216
216
  });
217
217
  try {
@@ -255,7 +255,7 @@ var Balances = class {
255
255
  async list(params) {
256
256
  const validated = validateInput(listBalancesSchema, params);
257
257
  const query = this.buildQueryString({
258
- wallet: validated.walletAddress,
258
+ walletAddress: validated.walletAddress,
259
259
  activeOnly: validated.activeOnly,
260
260
  limit: validated.limit,
261
261
  cursor: validated.cursor
@@ -310,7 +310,7 @@ var Balances = class {
310
310
  */
311
311
  async stats(walletAddress) {
312
312
  validateInput(walletAddressSchema, walletAddress);
313
- const query = this.buildQueryString({ wallet: walletAddress });
313
+ const query = this.buildQueryString({ walletAddress });
314
314
  return this.http.request({
315
315
  method: "GET",
316
316
  path: `${ENDPOINTS.STATS}?${query}`
@@ -735,7 +735,7 @@ var HttpClient = class {
735
735
  headers: {
736
736
  "Content-Type": "application/json",
737
737
  "User-Agent": USER_AGENT,
738
- "X-API-Key": this.apiKey,
738
+ Authorization: `Bearer ${this.apiKey}`,
739
739
  ...options.headers
740
740
  },
741
741
  body: options.body ? JSON.stringify(options.body) : void 0,