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

@tandem-language-exchange/content-store

v1.1.2

Published

SDK for fetching CMS content bundles from S3 and querying them locally from the filesystem. Supports content synced from **Contentful** and **Sanity**.

Readme

Content Store

SDK for fetching CMS content bundles from S3 and querying them locally from the filesystem. Supports content synced from Contentful and Sanity.

For the server, CLI, and deployment documentation, see the Server & CLI README.

Package entry points

  • @tandem-language-exchange/content-store (default) — types only at runtime. Safe to import from shared code that Next.js, Vite, or Turbopack may bundle for the browser.

  • @tandem-language-exchange/content-store/nodeContentStoreSDK, fetchBundles, queryBundle, ContentStore, and trimDepth. Real implementations use the filesystem and S3 and run only under the Node (node) export condition (Route Handlers, getServerSideProps, CLI, etc.).

    For browser bundles (including anything imported from _app.tsx, client components, or shared modules that reach the client graph), bundlers should resolve the browser / edge-light conditions to a stub that does not import fs. That stub throws if you call server-only APIs; trimDepth is fully implemented and safe on the client.

    Prefer not importing /node from files that _app or layouts load: keep SDK usage in server-only modules and pass data in as props. If the stub throws at runtime, move the import to server-only code.

Installation

npm install content-store

Initialisation

import { ContentStoreSDK } from '@tandem-language-exchange/content-store/node';

const sdk = new ContentStoreSDK({
  s3: {
    bucket: 'beta-content-store',
    region: 'eu-central-1',
    accessKeyId: process.env.AWS_ACCESS_KEY!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
  },
  outputDir: './content-cache',
});

| Option | Description | | --- | --- | | s3.bucket | S3 bucket where content bundles are stored | | s3.region | AWS region of the bucket | | s3.accessKeyId | AWS IAM access key | | s3.secretAccessKey | AWS IAM secret key | | outputDir | Local directory where bundle JSON files are written |

S3 config via environment variables

The CLI commands (fetch-content-bundles, query) read S3 credentials automatically from the following environment variables — no code needed:

| Variable | Description | | --- | --- | | CONTENT_STORE_S3_BUCKET | S3 bucket name | | CONTENT_STORE_S3_REGION | AWS region (default: eu-central-1) | | AWS_ACCESS_KEY | AWS IAM access key | | AWS_SECRET_ACCESS_KEY | AWS IAM secret key |

When using the SDK class directly, pass the values explicitly as shown above. You can load them from env vars yourself:

const sdk = new ContentStoreSDK({
  s3: {
    bucket: process.env.CONTENT_STORE_S3_BUCKET!,
    region: process.env.CONTENT_STORE_S3_REGION!,
    accessKeyId: process.env.AWS_ACCESS_KEY!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
  },
  outputDir: './content-cache',
});

fetchBundles(options)

Downloads the latest content bundles from S3 and saves them as JSON files to outputDir.

const files = await sdk.fetchBundles({
  cms: 'contentful',
  contentTypes: ['gridLayout', 'iconWithText', 'page'],
});

Parameters:

| Field | Type | Description | | --- | --- | --- | | cms | 'contentful' \| 'sanity' | Which CMS the bundles were synced from | | contentTypes | string[] | Content types to download |

Returns: Record<string, string> — a map of content type to absolute file path.

{
  gridLayout: '/abs/path/content-contentful-gridLayout.json',
  iconWithText: '/abs/path/content-contentful-iconWithText.json',
  page: '/abs/path/content-contentful-page.json'
}

Files are written to outputDir with the naming pattern {cms}-{contentType}.json.

queryBundle(cms, contentType, options?)

Reads a previously fetched bundle from the local filesystem and returns a filtered, shaped result set.

const results = await sdk.queryBundle('contentful', 'gridLayout', {
  fields: { columns: '2' },
  select: ['title', 'bodyBefore'],
  limit: 10,
  include: 2,
});

Parameters:

| Field | Type | Description | | --- | --- | --- | | cms | 'contentful' \| 'sanity' | CMS provider | | contentType | string | Content type to query | | options | QueryOptions | Optional filtering/shaping (see below) |

QueryOptions

All fields are optional.

| Field | Type | Description | | --- | --- | --- | | fields | Record<string, unknown> | Filter by top-level properties (see Filtering) | | select | string[] | Properties to include in each result object | | limit | number | Maximum number of items to return | | include | number | Depth of nested references to include (see Include depth) |

Filtering

The fields option matches items by their top-level properties.

Exact match — value must be strictly equal:

{ fields: { columns: '2' } }

IN match — value must be one of the provided options:

{ fields: { variant: ['A', 'B', 'E'] } }

Multiple fields are combined with AND logic:

{ fields: { columns: '2', refsType: 'Icon with Text' } }

Include depth

The include option controls how many levels of nested referenced objects are returned. Omit it to get the full depth.

Given this bundle item:

{
  "title": "Page Title",
  "columns": "2",
  "refs": [
    {
      "heading": "Child heading",
      "icon": {
        "title": "Icon title",
        "file": { "url": "//images.ctfassets.net/..." }
      }
    }
  ]
}

include: 1 — scalar properties only, all nested objects/refs are null:

{
  "title": "Page Title",
  "columns": "2",
  "refs": null
}

include: 2 — the item including its direct refs, but refs' own nested objects are null:

{
  "title": "Page Title",
  "columns": "2",
  "refs": [
    {
      "heading": "Child heading",
      "icon": null
    }
  ]
}

include: 3 — three levels deep; icon is included but icon.file is null:

{
  "title": "Page Title",
  "columns": "2",
  "refs": [
    {
      "heading": "Child heading",
      "icon": {
        "title": "Icon title",
        "file": null
      }
    }
  ]
}

Processing order

Query options are applied in this order:

  1. fields — filter the full item set
  2. limit — cap the result count
  3. include — trim nested depth
  4. select — pick output properties

Standalone functions

The core fetchBundles and queryBundle functions are also available as standalone imports for use outside the SDK class:

import { fetchBundles, queryBundle, ContentStore } from '@tandem-language-exchange/content-store/node';

const store = new ContentStore({
  bucket: 'beta-content-store',
  region: 'eu-central-1',
  accessKeyId: process.env.AWS_ACCESS_KEY!,
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
});

await fetchBundles(store, './content-cache', {
  cms: 'contentful',
  contentTypes: ['gridLayout'],
});

const results = await queryBundle('./content-cache', 'contentful', 'gridLayout', {
  fields: { columns: '2' },
  limit: 5,
});

Full example

import { ContentStoreSDK } from '@tandem-language-exchange/content-store/node';

const sdk = new ContentStoreSDK({
  s3: {
    bucket: process.env.CONTENT_STORE_S3_BUCKET!,
    region: process.env.CONTENT_STORE_S3_REGION!,
    accessKeyId: process.env.AWS_ACCESS_KEY!,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
  },
  outputDir: './.content-cache',
});

// 1. Pull latest bundles from S3 to disk
await sdk.fetchBundles({
  cms: 'contentful',
  contentTypes: ['page', 'gridLayout'],
});

// 2. Query locally — no further network calls
const grids = await sdk.queryBundle('contentful', 'gridLayout', {
  fields: { columns: '2' },
  select: ['title', 'refs'],
  include: 2,
  limit: 5,
});

console.log(grids);

CLI

The package ships two CLI entry points that can be called from npm scripts in a consuming application.

fetch-content-bundles — download bundles from S3

Downloads the latest version of each requested content type bundle from S3 and writes them as JSON files to a local directory. Reads S3 credentials from environment variables (see S3 config via environment variables).

npx fetch-content-bundles --cms contentful --types gridLayout,page

| Flag | Required | Default | Description | | --- | --- | --- | --- | | --cms <provider> | Yes | | contentful or sanity | | --types <types> | Yes | | Comma-separated content types | | --output <dir> | No | ./content-cache | Local directory to write bundle files to |

Files are written as {cms}-{contentType}.json inside the output directory.

Typical use in a host app's package.json:

"scripts": {
  "fetch-content": "fetch-content-bundles --cms contentful --types gridLayout,iconWithText,page --output ./content-cache"
}

content-store query — query a local bundle

Reads a previously fetched bundle from disk and prints filtered results as JSON. Invoke via npx or as a local script using node:

npx @tandem-language-exchange/content-store query \
  --cms contentful --type gridLayout \
  --fields '{"columns":"2"}' \
  --select title,bodyBefore \
  --limit 5 \
  --include 2

| Flag | Required | Default | Description | | --- | --- | --- | --- | | --cms <provider> | Yes | | contentful or sanity | | --type <type> | Yes | | Content type to query | | --output <dir> | No | ./content-cache | Directory where bundles are stored | | --fields <json> | No | | JSON filter object (e.g. '{"columns":"2"}') | | --select <props> | No | | Comma-separated properties to include in results | | --limit <n> | No | | Maximum number of results | | --include <n> | No | | Depth of nested references to include |