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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@jarrywc/aprimo-dam-connector

v0.17.0

Published

Cross-runtime TypeScript client for the Aprimo DAM API

Downloads

1,238

Readme

aprimo-dam-connector

Cross-runtime TypeScript client for the Aprimo DAM API. Zero dependencies, works everywhere: Node.js, Bun, Cloudflare Workers, and Adobe plugins (CEP/UXP).

Install

This package is hosted on GitHub Packages. You'll need to configure npm to use the GitHub registry for the @jarrywc scope.

1. Add a .npmrc file to your project root (or home directory):

@jarrywc:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=YOUR_GITHUB_TOKEN

Replace YOUR_GITHUB_TOKEN with a personal access token that has read:packages scope.

2. Install the package:

npm install @jarrywc/aprimo-dam-connector

Quick Start

Client Credentials (server-side)

import { AprimoClient } from '@jarrywc/aprimo-dam-connector';

const client = new AprimoClient({
  tenant: 'your-tenant',
  auth: {
    type: 'client_credentials',
    clientId: 'your-client-id',
    clientSecret: 'your-client-secret',
  },
});

// List records
const result = await client.records.list({ pageSize: 10 });
if (result.ok) {
  console.log(result.data.items);
}

// Get a specific record
const record = await client.records.get('record-id', {
  fields: ['title', 'status'],
});

// Upload a file
const file = new Blob(['hello'], { type: 'text/plain' });
const upload = await client.uploads.upload(file, 'hello.txt');

OAuth PKCE (browser / Adobe plugin)

The PKCE flow is browser-interactive. The SDK provides helper functions — your app handles the redirect.

import {
  generateCodeVerifier,
  generateCodeChallenge,
  buildAuthorizeUrl,
  exchangeCodeForToken,
  AprimoClient,
} from '@jarrywc/aprimo-dam-connector';

// Step 1: Generate PKCE values and redirect user
const verifier = await generateCodeVerifier();
const challenge = await generateCodeChallenge(verifier);

const authorizeUrl = buildAuthorizeUrl({
  tenant: 'your-tenant',
  clientId: 'your-client-id',
  redirectUri: 'https://your-app/callback',
  codeChallenge: challenge,
});
// Redirect user to authorizeUrl...

// Step 2: After redirect, exchange code for token
const tokenResponse = await exchangeCodeForToken({
  tenant: 'your-tenant',
  clientId: 'your-client-id',
  code: 'code-from-callback',
  codeVerifier: verifier,
  redirectUri: 'https://your-app/callback',
});

// Step 3: Create client with token
const client = new AprimoClient({
  tenant: 'your-tenant',
  auth: {
    type: 'pkce',
    tokenProvider: async () => tokenResponse.access_token,
  },
});

Custom Token Provider

For full control over token management:

const client = new AprimoClient({
  tenant: 'your-tenant',
  auth: {
    type: 'custom',
    tokenProvider: async () => {
      // Fetch token from your own auth service, KV store, etc.
      return 'your-access-token';
    },
  },
});

Records

// List with pagination
const page = await client.records.list({ page: 1, pageSize: 50 });

// List with metadata fields included
const withMetadata = await client.records.list({
  pageSize: 50,
  fields: ['fields'],
  languages: ['en-US'],
});

// Search
const results = await client.records.search('landscape photo');

// Search with metadata fields included
const richResults = await client.records.search('landscape photo', {
  fields: ['fields'],
});

// Auto-paginate with async generator
for await (const batch of client.records.listPaged({ pageSize: 100 })) {
  for (const record of batch) {
    console.log(record.id, record.title);
  }
}

// Auto-paginate with metadata
for await (const batch of client.records.listPaged({ pageSize: 100, fields: ['fields'] })) {
  for (const record of batch) {
    console.log(record.id, record.fields);
  }
}

// Auto-paginate search results
for await (const batch of client.records.searchPaged('landscape', { fields: ['fields'] })) {
  for (const record of batch) {
    console.log(record.id, record.fields);
  }
}

// Create
const created = await client.records.create({
  fields: {
    title: { value: 'My Asset' },
  },
});

// Update
await client.records.update('record-id', {
  fields: {
    title: { value: 'Updated Title' },
  },
});

// Delete
await client.records.delete('record-id');

Collections

// List collections
const collections = await client.collections.list({ pageSize: 50 });

// Get a specific collection
const collection = await client.collections.get('collection-id');

// Auto-paginate collections
for await (const batch of client.collections.listPaged({ pageSize: 100 })) {
  for (const collection of batch) {
    console.log(collection.id, collection.name);
  }
}

Metadata

Load field definitions, content types, and classifications to understand your DAM's record structure.

// List all field definitions
const fields = await client.metadata.getFieldDefinitions();

// Get a specific field definition
const field = await client.metadata.getFieldDefinition('field-id');

// List content types
const types = await client.metadata.getContentTypes();

// Browse classifications
const classifications = await client.metadata.getClassifications();
const classification = await client.metadata.getClassification('class-id');

// Auto-paginate field definitions
for await (const batch of client.metadata.getFieldDefinitionsPaged()) {
  for (const field of batch) {
    console.log(field.name, field.dataType);
  }
}

File Uploads

Files under 20 MB upload directly. Larger files are automatically split into segments.

// Auto-routes based on file size
const upload = await client.uploads.upload(fileBlob, 'photo.jpg');

// Force segmented upload with progress tracking
const upload = await client.uploads.upload(largeFile, 'video.mp4', {
  forceSegmented: true,
  parallelLimit: 3,
  onProgress: (uploaded, total) => {
    console.log(`${Math.round((uploaded / total) * 100)}%`);
  },
});

// Use the upload token when creating a record
if (upload.ok) {
  await client.records.create({
    fileToken: upload.data.token,
    fields: { title: { value: 'My Video' } },
  });
}

Token Persistence

Plug in your own storage for tokens:

const client = new AprimoClient({
  tenant: 'your-tenant',
  auth: {
    type: 'client_credentials',
    clientId: '...',
    clientSecret: '...',
  },
  onTokenChange: async (token) => {
    // Persist to KV, localStorage, database, etc.
    await myStore.set('aprimo-token', JSON.stringify(token));
  },
  getStoredToken: async () => {
    const stored = await myStore.get('aprimo-token');
    return stored ? JSON.parse(stored) : null;
  },
});

Custom Fetch

Pass your own fetch implementation for environments that need it:

const client = new AprimoClient({
  tenant: 'your-tenant',
  auth: { type: 'custom', tokenProvider: async () => token },
  fetchImpl: myCustomFetch,
});

Runtime Compatibility

| Runtime | Status | |---------|--------| | Node.js 18+ | Supported | | Bun | Supported | | Cloudflare Workers | Supported | | Adobe CEP | Supported | | Adobe UXP | Supported |

The package uses only Web APIs (fetch, crypto.subtle, URL, FormData, Blob) — no Node.js built-ins required.

Error Handling

All methods return ApiResult<T> — no exceptions thrown for API errors:

const result = await client.records.get('id');

if (result.ok) {
  console.log(result.data);
} else {
  console.error(result.status, result.error.message);
}

License

MIT