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

oc-azure-table-metadata-adapter

v0.1.1

Published

Azure Table Storage metadata adapter for OpenComponents

Readme

oc-azure-table-metadata-adapter

Azure Table Storage metadata adapter for OpenComponents registries.

This adapter stores only the registry metadata index: component name, version, publish date, and template size. Component static files and package.json files remain in the configured OC storage adapter (e.g. Azure Blob Storage, S3).

Azure Table Storage is a natural fit for this workload: the PartitionKey + RowKey uniqueness guarantee is the exact concurrency model the metadata store needs (same component+version → one insert wins; different components never contend), it is schemaless (no migrations framework needed), and if you already use Azure Blob Storage for statics you can use the same storage account for the metadata table — no second database to provision.

Installation

npm install oc-azure-table-metadata-adapter

Registry configuration

Metadata mode is enabled by adding a metadata block to the registry configuration. Storage is still required because the table stores only the metadata index.

const azureTableMetadataAdapter = require('oc-azure-table-metadata-adapter').default;

registry.configure({
  storage: {
    adapter: require('oc-s3-storage-adapter'),
    options: {
      // existing storage adapter options for component statics
    }
  },
  metadata: {
    adapter: azureTableMetadataAdapter,
    options: {
      connectionString: process.env.OC_METADATA_TABLE_CONNECTION_STRING
    }
  }
});

You can also pass an explicit endpoint with credentials instead of a connection string:

metadata: {
  adapter: azureTableMetadataAdapter,
  options: {
    endpoint: 'https://myaccount.table.core.windows.net',
    accountName: 'myaccount',
    accountKey: process.env.AZURE_STORAGE_KEY
  }
}

Managed identity (no secret)

When you pass only an endpoint (no connectionString, accountKey or sasToken), the adapter authenticates with DefaultAzureCredential — managed identity, workload identity, az login, environment credentials, etc. This lets the registry run without any secret in config:

metadata: {
  adapter: azureTableMetadataAdapter,
  options: {
    endpoint: 'https://myaccount.table.core.windows.net'
    // optionally: credential: new DefaultAzureCredential({ managedIdentityClientId })
  }
}

For local development with Azurite:

metadata: {
  adapter: azureTableMetadataAdapter,
  options: {
    connectionString: 'UseDevelopmentStorage=true',
    allowInsecureConnection: true
  }
}

Adapter options

| Option | Default | Description | | --- | --- | --- | | connectionString | none | Azure Storage account connection string. If present, used instead of endpoint + credentials. | | endpoint | none | Table service endpoint URL (e.g. https://<account>.table.core.windows.net). Required when connectionString is not used. | | accountName | none | Storage account name. Required with accountKey when connectionString is not used. | | accountKey | none | Storage account key. Required with accountName when connectionString is not used. | | sasToken | none | SAS token — alternative to accountKey for authentication. | | credential | none | Explicit TokenCredential (Microsoft Entra ID). When omitted with no key/SAS/connection string, a DefaultAzureCredential (managed identity) is used. | | tableName | occomponents | Azure Table name. Must be 3–63 chars, start with a letter, and contain only alphanumeric characters. | | manageSchema | true | When true, the adapter creates the table if missing (idempotent — createTable does not throw if the table already exists). When false, it verifies the table exists and fails fast if it does not. | | allowInsecureConnection | false | Allow HTTP (insecure) connections — for Azurite or local development. | | reservationTtlSeconds | 3600 | Age after which a publishing reservation is considered abandoned and can be reclaimed by a new publish or healed by storage reconciliation. |

Authentication precedence when connectionString is absent: accountName + accountKeysasToken → explicit credentialDefaultAzureCredential. In OC registry config, manageSchema can be set either in metadata.options or as top-level metadata.manageSchema; OC forwards the top-level value to the adapter.

Entity model

Each component version is stored as a single table entity:

| Property | Source | Description | | --- | --- | --- | | PartitionKey | row.name | Component name — gives partition-level isolation between components. | | RowKey | row.version | Component version — combined with PartitionKey forms the unique primary key. | | publishDate | row.publishDate | Unix timestamp (seconds). | | templateSize | row.templateSize | Template file size in bytes. Omitted when not set. | | status | adapter | publishing while reserved, committed once visible to reads. | | publishToken | adapter | Reservation token used to commit or abort only the publisher that reserved the row. | | createdAt | Date.now() | Insertion timestamp (milliseconds) — reserved for future delta cursor / audit. | | updatedAt | Date.now() | Last reservation status update timestamp (milliseconds). |

The PartitionKey + RowKey uniqueness is the reservation guarantee: concurrent publishes of the same component version map the Azure Table Storage 409 Conflict to the shared duplicate/in-progress metadata error codes before any storage upload.

Managed schema

With the default manageSchema: true, the adapter calls createTable on startup. Azure Table Storage's createTable is idempotent — it does not throw if the table already exists.

Use manageSchema: false when operators manage the table separately. On startup the adapter reads the first page of entities to verify the table exists. Because Azure Table returns 404 both for a missing table and for a missing entity, the adapter uses a list (not a single-entity getEntity) so an existing-but-empty table verifies cleanly while a genuinely missing table fails fast with a clear error.

Runtime behavior

  • The registry initialises the metadata store before loading caches.
  • Startup fails if the table cannot be created or accessed.
  • Reads are served from OC's in-memory cache; hot component reads do not hit Table Storage.
  • Polling first checks a point-read cursor entity and only re-hydrates when that cursor changes, with a periodic full refresh safety net in OC core. Rehydration uses getAllComponents(); the @azure/data-tables SDK auto-paginates the entity query via its paged async iterator, so all rows are returned regardless of registry size.
  • If polling fails after startup, OC keeps serving the previous in-memory cache and retries on the next poll.
  • Publish reserves a publishing metadata entity first, uploads statics only after reservation succeeds, then commits the entity. If upload or commit fails, OC best-effort aborts the matching reservation.
  • Successful commits update a best-effort cursor entity at PartitionKey = 'oc.metadata', RowKey = 'cursor'. This key cannot collide with component rows because OC component names reject dots.
  • If a publisher dies, stale publishing entities older than reservationTtlSeconds are reclaimed on the next same-version publish; storage reconciliation can also commit a stale reservation when the component files already exist in storage.
  • When the registry is shut down via registry.close(callback), the adapter's close() is called. Since Table Storage is HTTP-based with no connection pool, close() simply clears the internal client reference and is safe to call even when no client was created.

Connection pool lifecycle

Unlike the SQL adapters, this adapter has no connection pool to manage. Table Storage is HTTP-based — each operation is a stateless REST call. The close() method clears the internal TableClient reference so a subsequent operation creates a fresh client. The registry wires close() into its existing registry.close(callback) shutdown hook; the oc registry migrate-metadata CLI command also calls it before exiting.

Current limitations

  • Integration tests against real Azure Table Storage / Azurite are gated on the OC_METADATA_TABLE_CONNECTION_STRING environment variable and are skipped otherwise. Run them locally or in CI against Azurite or a real storage account by setting that variable.
  • Metadata migration, backfill, storage reconciliation, and legacy file export are implemented in OC core, not in this adapter package.

Testing

# mocked unit tests (no Azure account required)
npm test

# integration tests against a real Azure Table Storage / Azurite instance
OC_METADATA_TABLE_CONNECTION_STRING="UseDevelopmentStorage=true" npm test

The mocked unit tests run by default. The integration tests verify managed table creation, addVersion() inserts, duplicate mapping to VERSION_ALREADY_EXISTS, getAllComponents() row mapping, custom table names, and close() client lifecycle against a real table. Each integration run uses uniquely-named tables and cleans them up afterwards.