Runtimes

TypeScript runtime

The TypeScript runtime lives under ts/ and is distributed as @theory-cloud/tabletheory-ts. It targets Node.js 20+ (Node 20 LTS and Node 24 are exercised in CI) and the AWS SDK for JavaScript v3.

The TypeScript runtime is a peer, not a port: it implements the same P0 contract scenarios as Go and Python, and a behavior that passes the Go contract test but fails in TypeScript is a parity regression — never a “TypeScript-specific quirk.”

Install

This repo does not publish to npm. GitHub Releases are the source of truth. Install the release tarball directly:

# Stable release (replace X.Y.Z)
npm install --save-exact \
  https://github.com/theory-cloud/tabletheory/releases/download/vX.Y.Z/theory-cloud-tabletheory-ts-X.Y.Z.tgz

# Prerelease (replace X.Y.Z-rc.N)
npm install --save-exact \
  https://github.com/theory-cloud/tabletheory/releases/download/vX.Y.Z-rc.N/theory-cloud-tabletheory-ts-X.Y.Z-rc.N.tgz

@aws-sdk/client-dynamodb, @aws-sdk/client-kms, and @aws-sdk/client-sts are peer dependencies. npm 7+ installs peers automatically for ordinary installs; package managers configured with --legacy-peer-deps, some pnpm/yarn modes, or offline mirrors should install those peers explicitly with the tested ^3.1053.0 range.

The single distribution path is deliberate — it makes version drift between language registries impossible.

Find and automate release updates

GitHub Releases are the version source of truth:

gh release view --repo theory-cloud/TableTheory --json tagName,publishedAt,url
gh release list --repo theory-cloud/TableTheory --exclude-drafts --limit 10

Copy this Renovate regex manager into the consuming repository’s renovate.json to update stable TypeScript tarball URLs:

{
  "customManagers": [
    {
      "customType": "regex",
      "description": "Update TableTheory TypeScript GitHub Release asset URLs",
      "managerFilePatterns": ["/(^|/)package\\.json$/", "/(^|/)package-lock\\.json$/", "/(^|/)docs/.+\\.md$/"],
      "matchStrings": [
        "https://github\\.com/theory-cloud/[Tt]able[Tt]heory/releases/download/v(?<currentValue>\\d+\\.\\d+\\.\\d+)/theory-cloud-tabletheory-ts-(?<assetVersion>\\d+\\.\\d+\\.\\d+)\\.tgz"
      ],
      "datasourceTemplate": "github-releases",
      "depNameTemplate": "theory-cloud/TableTheory",
      "versioningTemplate": "semver",
      "extractVersionTemplate": "^v(?<version>\\d+\\.\\d+\\.\\d+)$",
      "autoReplaceStringTemplate": "https://github.com/theory-cloud/TableTheory/releases/download/v}/theory-cloud-tabletheory-ts-}.tgz"
    }
  ]
}

For the combined TypeScript + Python config, see Consumer update automation.

defineModel + TheorydbClient

The TypeScript public surface is explicit: models are declared via defineModel({ … }) (no decorators), and operations run through a TheorydbClient instance.

import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { TheorydbClient, defineModel } from "@theory-cloud/tabletheory-ts";

const Note = defineModel({
  name: "Note",
  table: { name: "notes_contract" },
  keys: {
    partition: { attribute: "PK", type: "S" },
    sort: { attribute: "SK", type: "S" },
  },
  attributes: [
    { attribute: "PK", type: "S", roles: ["pk"] },
    { attribute: "SK", type: "S", roles: ["sk"] },
    { attribute: "body", type: "S", optional: true, omit_empty: true },
    { attribute: "createdAt", type: "S", roles: ["created_at"] },
    { attribute: "updatedAt", type: "S", roles: ["updated_at"] },
    { attribute: "version", type: "N", roles: ["version"] },
  ],
});

const ddb = new DynamoDBClient({ region: "us-east-1" });
const db = new TheorydbClient(ddb).register(Note);

await db.create("Note", {
  PK: "USER#42",
  SK: "NOTE#welcome",
  body: "Hello, Theory Cloud.",
});

const item = await db.get("Note", { PK: "USER#42", SK: "NOTE#welcome" });

For a complete working program (model + GSI + create + query + update + delete + cursor pagination), see ts/examples/local.ts.

Role vocabulary

Every role accepted by defineModel attributes maps one-to-one onto the canonical TableTheory contract:

Go tag TypeScript role / option
theorydb:"pk" roles: ['pk']
theorydb:"sk" roles: ['sk']
theorydb:"gsi1pk" roles: ['gsi1pk'] + indexes:
theorydb:"encrypted" encryption: { v: 1 }
theorydb:"version" roles: ['version']
theorydb:"created_at" roles: ['created_at']
theorydb:"updated_at" roles: ['updated_at']
theorydb:"ttl" roles: ['ttl']
theorydb:"omitempty" omit_empty: true

Naming strategy is implied by how you declare each attribute’s name — the explicit shape of defineModel means no separate “naming strategy” decoration is needed.

CRUD methods

TheorydbClient exposes the canonical CRUD surface:

await db.create('Note', {  });
const item = await db.get('Note', key);
await db.update('Note', {  }, ['body']);
await db.delete('Note', key);

const page = await db.query('Note').partitionKey('USER#42').limit(20).page();

Aggregation helper warning

TypeScript aggregation helpers (sum, average, min, max, aggregate, countDistinct, and groupBy) are client-side conveniences over all(). They follow every page and materialize every matching item in memory before computing the result. Use them only for bounded result sets. When the planned native count() API lands, use it instead for count-only access patterns.

Workflows

  • cd ts && npm run format:check — Prettier verification
  • cd ts && npm run lint && npm run typecheck && npm run build — CI static checks
  • cd ts && npm run test:unit — unit tests
  • cd ts && npm run test:integration — integration tests with DynamoDB Local
  • Exercised against shared contract scenarios via contract-tests/runners/ on every commit

Where to go next

Stability and support

The TypeScript runtime is GA. Versions are aligned with Go and Python per the multi-language version-sync invariant. Breaking changes ship coordinated with the other two runtimes — never in isolation.