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 verificationcd ts && npm run lint && npm run typecheck && npm run build— CI static checkscd ts && npm run test:unit— unit testscd 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
- Getting Started — full walkthrough
ts/docs/getting-started.md— TypeScript-specific runtime documentationts/docs/api-reference.md— full TypeScript API reference- Features → CRUD & Marshaling, Optimistic Locking, Encryption
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.