This guide is for STAN assistants modifying or using this repository’s published library surfaces without importing additional type definitions or documentation from the repo into STAN imports. It is intentionally dense and aims to be complete.
This package has two public entrypoints:
@karmaniverous/entity-client-dynamodb@karmaniverous/entity-client-dynamodb/get-dotenvRule: do not import from src/** in consumer code; use only package exports entrypoints and their documented surfaces.
EntityClient) and a fluent query builder (QueryBuilder) on top of @karmaniverous/entity-manager and AWS SDK v3./get-dotenv subpath.typedoc.json includes both entrypoints in entryPoints and includes this guide in projectDocuments.These are “must stay consistent” constraints; many regressions come from violating them.
.) and subpath entrypoint (./get-dotenv) are distinct surfaces (Option B). They must not be aliases.package.json exports must point to files that actually exist after npm run build:
dist/mjs/index.js, dist/cjs/index.jsdist/index.d.tsdist/mjs/get-dotenv/index.js, dist/cjs/get-dotenv/index.jsdist/get-dotenv/index.d.ts@karmaniverous/get-dotenv/cliHost must not be bundled). Bundling transitive dependencies (notably execa → npm-run-path → unicorn-magic) can break builds.__type trees; prefer exporting a documented config type and keeping schema values @internal.From package.json scripts:
npm run lint must be clean (no errors; warnings should be treated as “fix” unless explicitly ignored by config).npm run test must pass (Docker-based tests may be skipped if Docker is unavailable).npm run typecheck must pass (tsc && tsd).npm run docs must pass with 0 warnings (typedoc --emit none), with typedoc.validation.notDocumented: true.This section is the “replacement” for importing type definitions from the repo: it lists the exported symbols that matter, grouped by entrypoint and intent.
@karmaniverous/entity-client-dynamodbSource entrypoint: src/index.ts
Exports:
EntityClientEntityClientOptionsBatchGetOptionsBatchWriteOptionsWaiterConfigGetItemOutput (exported helper type)GetItemsOutput (exported helper interface)getDocumentQueryArgs (for adapter/tests)QueryBuildercreateQueryBuilderaddFilterCondition, addRangeKeyCondition, attributeValueAliasFilterCondition, RangeKeyCondition, IndexParams, QueryCondition* union interfaces/typesgenerateTableDefinitionTranscodeAttributeTypeMapdefaultTranscodeAttributeTypeMap@karmaniverous/entity-manager for downstream DX:
EntityItem, EntityItemPartialEntityRecord, EntityRecordPartialEntityTokenProjectedCore “how to use it” principles:
entityManager.removeKeys(entityToken, recordOrRecords) when you need domain shapes.Minimal cookbook:
import {
EntityClient,
generateTableDefinition,
} from '@karmaniverous/entity-client-dynamodb';
const client = new EntityClient({
entityManager,
tableName: 'UserTable',
region: 'local',
});
await client.createTable({
BillingMode: 'PAY_PER_REQUEST',
...generateTableDefinition(entityManager),
});
await client.putItem({ hashKey2: 'h', rangeKey: 'r', a: 1 });
const out = await client.getItem('user', { hashKey2: 'h', rangeKey: 'r' });
const domain = out.Item && client.entityManager.removeKeys('user', out.Item);
QueryBuilder usage:
import { createQueryBuilder } from '@karmaniverous/entity-client-dynamodb';
const qb = createQueryBuilder({
entityClient: client,
entityToken: 'user' as const,
hashKeyToken: 'hashKey2' as const,
});
qb.addRangeKeyCondition('created', {
property: 'created',
operator: 'between',
value: { from: 1700000000000, to: 1900000000000 },
});
const withProjection = qb.setProjection('created', ['created'] as const);
const shardQueryMap = withProjection.build();
const { items, pageKeyMap } = await client.entityManager.query({
entityToken: 'user',
item: {},
shardQueryMap,
pageSize: 25,
});
Projection invariant (adapter behavior):
uniqueProperty and any explicit sort keys at runtime to preserve dedupe and sort invariants.@karmaniverous/entity-client-dynamodb/get-dotenvSource entrypoint: src/get-dotenv/index.ts
Purpose:
Exports (grouped by usage):
dynamodbPlugin (CLI plugin factory; registers commands)DynamodbPluginInstance (typed instance seam; used in command registration modules)parseVersionValue, formatVersionTokenlistVersionDirs, listVersionDirEntriesresolveVersionDirgetVersionedPathsForTokenresolveTableFile, resolveTransformFileresolveEntityManagerFileWithFallbackenumerateStepVersionsloadEntityManagerFromFileresolveAndLoadEntityManagercomputeGeneratedSectionscomposeNewTableYamlrefreshGeneratedSectionsInPlaceGeneratedSections, OverlayOptions (creation-time overlay)resolveManagedTablePropertiesassertManagedTablePropertiesInvariantspickManagedActualFromPropertiesTablePropertiesConfig, ManagedTablePropertiesInfovalidateGeneratedSectionsValidateResult, GeneratedDiffgenerateTableDefinitionAtVersionvalidateTableDefinitionAtVersioncreateTableAtVersiondeleteTablepurgeTablemigrateDatastartLocal, stopLocal, statusLocal, deriveEndpointdefineTransformMapTransformMap, TransformHandler, TransformContextresolveLayoutConfigresolveGenerateAtVersion, resolveValidateAtVersionresolveCreateAtVersion, resolveDelete, resolvePurge, resolveMigrateparseFiniteNumber, parsePositiveInt, parseNonNegativeIntExpected usage is as a child of the shipped get-dotenv aws plugin:
aws dynamodb ...plugins["aws/dynamodb"] (realized mount path)Minimal host composition example:
import { createCli } from '@karmaniverous/get-dotenv';
import { awsPlugin } from '@karmaniverous/get-dotenv/plugins';
import { dynamodbPlugin } from '@karmaniverous/entity-client-dynamodb/get-dotenv';
await createCli({
alias: 'mycli',
compose: (p) => p.use(awsPlugin().use(dynamodbPlugin())),
})(process.argv.slice(2));
Config interpolation boundary (critical):
Precedence (every resolver/service adapter must follow):
Config surface (documented as DynamodbPluginConfig):
tablesPath, tokens.{table,entityManager,transform}, minTableVersionWidthgenerate.{version,clean,tableProperties}validate.{version}create.{version,validate,refreshGenerated,force,waiter.maxSeconds,tableNameOverride}delete.{tableName,waiter.maxSeconds}purge.{tableName}migrate.{sourceTable,targetTable,fromVersion,toVersion,pageSize,limit,transformConcurrency,progressIntervalMs}local.{port,endpoint,start,stop,status}Default layout under tablesPath (default: tables):
tables/
table.template.yml (optional baseline)
001/
entityManager.ts|.js (fallback-aware)
table.yml|.yaml (full AWS::DynamoDB::Table resource)
transform.ts|.js (optional per-step transforms)
Numeric ordering rules:
^\d+$).1/ and 001/) are a hard error.minTableVersionWidth is cosmetic padding for formatting tokens when emitting a canonical token; it does not restrict existing dirs.Fallback EntityManager resolution (per requirements):
entityManager.(ts|js) by scanning backward across existing version directories whose numeric value is <= V until a file is found.Generated sections:
Properties.AttributeDefinitionsProperties.KeySchemaProperties.GlobalSecondaryIndexesRules:
Properties; all other properties and comments are preserved.tables/table.template.yml as a baseline if present; otherwise starts from a minimal doc template with a warning banner.Managed table properties (non-generated keys; optional):
Properties.BillingModeProperties.ProvisionedThroughputProperties.TableNameInvariants:
PROVISIONED, and both RCU/WCU must be present.PAY_PER_REQUEST, ProvisionedThroughput must not exist in YAML.Validation checks:
Create flow drift policy:
refreshGenerated updates YAML in place (generated sections + managed properties) before create.force only bypasses drift failure when validate is enabled; it is not a confirmation mechanism.Latest-only create guard (safety invariant):
allowNonLatest: true (--allow-non-latest flag).Boundary existence guard (hard):
fromVersion and toVersion must exist as version directories or migration errors (never silently no-op).Step discovery:
k where from < k <= to, ascending numeric order.Per-step context:
prev EM (fallback-aware) and next EM (fallback-aware).transform.(ts|js) for that step version; missing transform module means default chain for all entities.Default chain (per record):
entityToken by parsing the global hash key prefix using prev.config.hashKey and prev.config.shardKeyDelimiter (default delimiter: !).prev.removeKeys(entityToken, record)next.addKeys(entityToken, item)Transform semantics:
undefined → dropStreaming and batching:
EntityClient.putItems.Progress:
{ pages, items, outputs, ratePerSec } at intervals.Transform authoring helper (defineTransformMap):
import { defineTransformMap } from '@karmaniverous/entity-client-dynamodb/get-dotenv';
import type { ConfigMap as PrevCM } from '../001/entityManager';
import type { ConfigMap as NextCM } from './entityManager';
export default defineTransformMap<PrevCM, NextCM>({
user: async (record, { prev, next }) => {
const item = prev.removeKeys('user', record);
return next.addKeys('user', item);
},
});
Command subtree: dynamodb local start|status|stop (mounted as aws dynamodb local ... when nested).
Endpoint derivation precedence:
plugins["aws/dynamodb"].local.endpointplugins["aws/dynamodb"].local.port → http://localhost:{port}DYNAMODB_LOCAL_ENDPOINT (env)http://localhost:${DYNAMODB_LOCAL_PORT ?? '8000'}Execution behavior:
local.start|stop|status), run them under a shell in the composed env.@karmaniverous/dynamodb-local to be installed; otherwise error with guidance.This repo uses @commander-js/extra-typings and follows the shipped aws plugin patterns.
Action handler arity:
.argument(...) is declared: .action(async (opts, thisCommand) => {}).argument('[args...]') is declared: .action(async (args, opts, thisCommand) => {})Numeric option parsing (strict):
parsePositiveInt, parseNonNegativeInt, parseFiniteNumber) so action handlers receive number at runtime and invalid values fail during parsing.Root bag usage (shell/capture):
readMergedOptions(thisCommand) and compute capture via shouldCapture(bag.capture).Config access:
plugin.readConfig(cli) (instance helper) rather than indexing ctx.pluginConfigs[...] because config is keyed by realized mount path (aws/dynamodb).No optional chaining for ctx:
cli.getCtx() as non-optional at action time.Use this section when you need to find the actual implementation quickly.
src/index.tssrc/EntityClient/EntityClient.tssrc/EntityClient/methods/*.tssrc/QueryBuilder/**src/Tables/generateTableDefinition.tssrc/get-dotenv/index.tssrc/get-dotenv/layout.tssrc/get-dotenv/emLoader.tssrc/get-dotenv/tableDefinition.ts, src/get-dotenv/tableProperties.tssrc/get-dotenv/validate.tssrc/get-dotenv/services/**src/get-dotenv/cli/plugin/**src/get-dotenv/cli/options/**src/get-dotenv/cli/plugin/parsers.tsTests (co-located):
*.test.ts nearby; wiring tests under src/get-dotenv/cli/plugin/commands/*.wiring.test.ts verify command registration and option presence.notDocumented warnings for anonymous return object properties: prefer named return types (interfaces/types) with documented properties.rollup.config.ts externals; ensure dependency subpath imports are treated as external.src/get-dotenv/validate.ts and ensure YAML nodes are normalized to plain JS before comparison./get-dotenv) and avoid leaking exports across entrypoints.exports and build outputs consistent (JS + d.ts for both entrypoints).