npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@deliverart/sdk-js-core

v2.6.9

Published

Core SDK for DeliverArt, providing essential functionalities and utilities.

Downloads

12,726

Readme

@deliverart/sdk-js-core

Core package for the DeliverArt JavaScript SDK. Provides the API client, plugin system, and base request/response handling.

Installation

npm install @deliverart/sdk-js-core
# or
pnpm add @deliverart/sdk-js-core
# or
yarn add @deliverart/sdk-js-core

Exported Types

ApiClient

type ApiClient<Extensions extends ApiExtension = NonNullable<unknown>>

The main API client interface that provides methods to make API calls and manage plugins.

Methods:

  • call<TInputSchema, TOutputSchema, Q, H>(request: AbstractApiRequest): Promise<output<TOutputSchema>> - Execute an API request
  • addPlugin<T extends ApiExtension>(plugin: ApiClientPlugin<T>): ApiClient<T> - Add a plugin to extend client functionality
  • addRequestMiddleware(middleware: RequestMiddleware): void - Add middleware to intercept requests
  • addResponseMiddleware(middleware: ResponseMiddleware): void - Add middleware to intercept responses

AbstractApiRequest

abstract class AbstractApiRequest<
  TInputSchema extends ZodType<any, any, any>,
  TOutputSchema extends ZodType<any, any, any>,
  Query = unknown,
  Headers = unknown
>

Base class for all API requests. Extend this class to create custom API requests.

Abstract Properties:

  • method: 'GET' | 'POST' | 'PATCH' | 'DELETE' - HTTP method (required)
  • contentType: 'application/json' | 'multipart/form-data' | 'application/merge-patch+json' - Content-Type header (required)
  • accept: 'application/json' - Accept header (required)
  • inputSchema: TInputSchema - Zod schema for request validation (required)
  • outputSchema: TOutputSchema - Zod schema for response validation (required)
  • querySchema?: ZodType<Query> - Zod schema for query parameters (optional)
  • headersSchema?: ZodType<Headers> - Zod schema for headers (optional)

Abstract Methods:

  • getPath(): string - Returns the API endpoint path (required)

Built-in Methods:

  • validateInput(): output<TInputSchema> - Validates the request input
  • validateQuery(): Query | undefined - Validates query parameters
  • validateHeaders(): Headers | undefined - Validates headers
  • validateOutput(data: unknown): output<TOutputSchema> - Validates the response
  • parseResponse(data: unknown, rawResponse?: Response): output<TOutputSchema> - Parses and validates the response (can be overridden)

ApiExtension

type ApiExtension = Record<string, unknown>

Base type for plugin extensions that add new methods to the API client.

ApiClientPlugin

interface ApiClientPlugin<T extends ApiExtension = Record<string, unknown>> {
  setup: (client: ApiClient<Record<string, unknown>>) => T
}

Interface for creating plugins that extend the API client functionality.

Errors

InputValidationError

class InputValidationError extends Error {
  constructor(public readonly issues: ZodIssue[])
}

Thrown when request input validation fails.

Properties:

  • issues: ZodIssue[] - Array of validation errors from Zod

OutputValidationError

class OutputValidationError extends Error {
  constructor(public readonly issues: ZodIssue[])
}

Thrown when response validation fails.

Properties:

  • issues: ZodIssue[] - Array of validation errors from Zod

Usage

Creating the API Client

import { createApiClient } from '@deliverart/sdk-js-core';

const client = createApiClient({
  baseUrl: 'https://api.deliverart.com'
});

Parameters:

  • baseUrl: string (required) - The base URL for all API requests

Creating a Custom Request

import { AbstractApiRequest } from '@deliverart/sdk-js-core';
import { z } from 'zod';

// Define input schema
const createUserInputSchema = z.object({
  name: z.string().min(1),
  email: z.string().email(),
  age: z.coerce.number().min(18).optional()
});

// Define output schema
const userResponseSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string(),
  age: z.coerce.number().nullable(),
  createdAt: z.string()
});

// Define query parameters schema (optional)
const getUsersQuerySchema = z.object({
  page: z.coerce.number().optional(),
  limit: z.coerce.number().optional(),
  search: z.string().optional()
});

// Create the request class
class CreateUser extends AbstractApiRequest<
  typeof createUserInputSchema,
  typeof userResponseSchema
> {
  readonly method = 'POST';
  readonly contentType = 'application/json';
  readonly accept = 'application/json';
  
  readonly inputSchema = createUserInputSchema;
  readonly outputSchema = userResponseSchema;
  readonly querySchema = undefined;
  readonly headersSchema = undefined;

  constructor(input: z.input<typeof createUserInputSchema>) {
    super(input);
  }

  getPath(): string {
    return '/users';
  }
}

// Request with path parameters
class GetUser extends AbstractApiRequest<
  typeof z.undefined,
  typeof userResponseSchema
> {
  readonly method = 'GET';
  readonly contentType = 'application/json';
  readonly accept = 'application/json';
  
  readonly inputSchema = z.undefined();
  readonly outputSchema = userResponseSchema;
  readonly querySchema = undefined;
  readonly headersSchema = undefined;

  private readonly userId: string;

  constructor(userId: string) {
    super(undefined);
    this.userId = userId;
  }

  getPath(): string {
    return `/users/${this.userId}`;
  }
}

// Request with query parameters
class GetUsers extends AbstractApiRequest<
  typeof z.undefined,
  typeof z.array(userResponseSchema),
  z.infer<typeof getUsersQuerySchema>
> {
  readonly method = 'GET';
  readonly contentType = 'application/json';
  readonly accept = 'application/json';
  
  readonly inputSchema = z.undefined();
  readonly outputSchema = z.array(userResponseSchema);
  readonly querySchema = getUsersQuerySchema;
  readonly headersSchema = undefined;

  constructor(options?: { query?: z.infer<typeof getUsersQuerySchema> }) {
    super(undefined, options);
  }

  getPath(): string {
    return '/users';
  }
}

Making API Calls

import { client } from './client';

// Create a user
const newUser = await client.call(new CreateUser({
  name: 'John Doe',
  email: '[email protected]',
  age: 25
}));

// Get a user
const user = await client.call(new GetUser('user-123'));

// Get users with filters
const users = await client.call(new GetUsers({
  query: {
    page: 1,
    limit: 10,
    search: 'john'
  }
}));

Adding Middleware

Request Middleware

client.addRequestMiddleware(async (ctx) => {
  console.log('Making request to:', ctx.url.toString());
  
  // Modify headers
  return {
    ...ctx,
    init: {
      ...ctx.init,
      headers: {
        ...ctx.init.headers,
        'X-Custom-Header': 'value'
      }
    }
  };
});

Parameters:

  • ctx.url: URL - The request URL
  • ctx.init: RequestInit - The fetch init options

Return: Modified RequestContext or the original context

Response Middleware

client.addResponseMiddleware(async (parsed, requestCtx) => {
  console.log('Response status:', parsed.response.status);
  console.log('Response body:', parsed.body);
  
  return parsed;
});

Parameters:

  • parsed.response: Response - The fetch Response object
  • parsed.body: unknown - The parsed response body
  • requestCtx: RequestContext - The original request context

Return: Modified ParsedResponse or the original parsed response

Creating a Plugin

import { ApiClient, ApiClientPlugin, ApiExtension } from '@deliverart/sdk-js-core';

// Define the extension interface
interface LoggerExtension extends ApiExtension {
  logger: {
    enable: () => void;
    disable: () => void;
  };
}

// Create the plugin
class LoggerPlugin implements ApiClientPlugin<LoggerExtension> {
  private enabled = true;

  setup(client: ApiClient): LoggerExtension {
    // Add request middleware
    client.addRequestMiddleware(async (ctx) => {
      if (this.enabled) {
        console.log('[Request]', ctx.init.method, ctx.url.toString());
      }
      return ctx;
    });

    // Add response middleware
    client.addResponseMiddleware(async (parsed, ctx) => {
      if (this.enabled) {
        console.log('[Response]', parsed.response.status);
      }
      return parsed;
    });

    // Return extension methods
    return {
      logger: {
        enable: () => {
          this.enabled = true;
        },
        disable: () => {
          this.enabled = false;
        }
      }
    };
  }
}

// Use the plugin
const client = createApiClient({ baseUrl: 'https://api.example.com' })
  .addPlugin(new LoggerPlugin());

// Now you can use the logger methods
client.logger.disable();
client.logger.enable();

Error Handling

import { InputValidationError, OutputValidationError } from '@deliverart/sdk-js-core';

try {
  await client.call(new CreateUser({
    name: '', // Invalid: min length is 1
    email: 'invalid-email', // Invalid email format
  }));
} catch (error) {
  if (error instanceof InputValidationError) {
    console.error('Input validation failed:');
    error.issues.forEach(issue => {
      console.error(`- ${issue.path.join('.')}: ${issue.message}`);
    });
  } else if (error instanceof OutputValidationError) {
    console.error('Response validation failed:');
    error.issues.forEach(issue => {
      console.error(`- ${issue.path.join('.')}: ${issue.message}`);
    });
  } else {
    console.error('Other error:', error);
  }
}

Advanced Features

Custom Response Parsing

Override parseResponse to customize how responses are parsed:

import { Paginated, responseToPagination } from '@deliverart/sdk-js-global-types';

class GetUsers extends AbstractApiRequest</*...*/> {
  // ... other properties ...

  parseResponse(data: unknown, rawResponse: Response): Paginated<User> {
    const users = z.array(userResponseSchema).parse(data);
    
    return this.validateOutput({
      data: users,
      pagination: responseToPagination(rawResponse)
    });
  }
}

FormData Requests

const uploadImageInputSchema = z.instanceof(FormData);

class UploadImage extends AbstractApiRequest<
  typeof uploadImageInputSchema,
  typeof imageResponseSchema
> {
  readonly method = 'POST';
  readonly contentType = 'multipart/form-data';
  readonly accept = 'application/json';
  
  readonly inputSchema = uploadImageInputSchema;
  readonly outputSchema = imageResponseSchema;

  constructor(formData: FormData) {
    super(formData);
  }

  getPath(): string {
    return '/images';
  }
}

// Usage
const formData = new FormData();
formData.append('file', fileBlob);
formData.append('name', 'My Image');

const image = await client.call(new UploadImage(formData));

License

MIT