@lastshotlabs/slingshot-entity
npm install @lastshotlabs/slingshot-entity
Functions
Section titled “Functions”applyDefaults
Section titled “applyDefaults”Apply auto-defaults and literal defaults to a create input, producing a full entity record ready for persistence.
For each field that is absent from input but has a default defined in
its FieldDef, the default is resolved as follows:
- Built-in auto-default (
'uuid','cuid','now'): delegated toresolveAutoDefault(withcustomAutoDefaultforwarded). - Custom string default: if
customAutoDefaultis provided and the default value is a string that is not a built-in sentinel, the resolver is called. If it returns a non-undefinedvalue, that value is used; otherwise the literal string is used as-is. - Literal default (number, boolean, or unresolved string): applied directly as the field value.
Fields already present in input are never overwritten.
function applyDefaults(input: Record<string, unknown>, fields: Record<string, FieldDef>, customAutoDefault?: CustomAutoDefaultResolver,): Record<string, unknown>Source: packages/slingshot-entity/src/configDriven/fieldUtils.ts
applyOnUpdate
Section titled “applyOnUpdate”Apply onUpdate fields to an update payload, injecting computed values
for fields that declare an onUpdate sentinel.
Resolution order for each field with a non-undefined onUpdate:
- Built-in sentinel (
'now'): sets the field tonew Date(). - Custom sentinel (any other string): if
customOnUpdateis provided, it is invoked with the sentinel string. When the resolver returns a non-undefinedvalue, that value is written to the field. If the resolver returnsundefined, the field is left unchanged (the sentinel is silently ignored).
Values already present in input for non-onUpdate fields are preserved.
onUpdate fields are always overwritten regardless of whether the caller
included them in input.
function applyOnUpdate(input: Record<string, unknown>, fields: Record<string, FieldDef>, customOnUpdate?: CustomOnUpdateResolver,): Record<string, unknown>Source: packages/slingshot-entity/src/configDriven/fieldUtils.ts
assertEntityBackendRequirements
Section titled “assertEntityBackendRequirements”Assert that a standard store can satisfy one resolved entity configuration.
Validation runs before adapter construction and throws a typed, deterministic error containing every missing capability.
function assertEntityBackendRequirements(store: StoreType, config: ResolvedEntityConfig, operations?: Readonly<Record<string, OperationConfig>>,): voidSource: packages/slingshot-entity/src/configDriven/backendProfiles.ts
auditEntity
Section titled “auditEntity”Run all built-in audit rules against an entity definition and its operations.
Combines four rule sets:
- Structural — field type/default compatibility, soft-delete field type, missing indexes on large entities.
- Index coverage — unindexed lookup fields, pagination cursor fields,
soft-delete fields, aggregate
groupBy, upsert match fields. - Operation consistency — field existence and enum-value validity for transitions, immutability violations in fieldUpdates, collection config completeness, consume expiry field type.
- Search config — search field references, geo field types, filterable/facetable/sortable coverage.
function auditEntity(config: ResolvedEntityConfig, operations?: Record<string, OperationConfig>,): EntityAuditResultSource: packages/slingshot-entity/src/audits/index.ts
buildEntityReceiveHandlers
Section titled “buildEntityReceiveHandlers”Build the receive handler map for a single entity’s channel config.
For each channel declaration with receive.events, adds one entry per event type to
the returned map. Each handler:
- Validates
payload.roomis a non-empty string with a valid room name pattern. - Parses room →
{storageName}:{entityId}:{channelName}. - Confirms
storageNamematches the entity andchannelNamehas areceiveconfig. - Confirms the event type is in
receive.eventswhitelist (defense in depth). - Confirms the sender is subscribed (via
ws.data.rooms) — prevents relay to rooms the sender has not joined. - If
toRoom(defaulttrue), callspublishFnto broadcast to the room, optionally excluding the sender (excludeSender, defaulttrue).
Returns a Record<string, ChannelIncomingEventDeclaration> for merging into the WS
endpoint’s incoming config via buildReceiveIncoming().
When the same eventType appears in multiple channel declarations the last one wins
(last-wins merge semantics, consistent with buildReceiveIncoming).
function buildEntityReceiveHandlers(channelConfig: EntityChannelConfig, entity: ResolvedEntityConfig, getWsState: () => WsState | null, publishFn: WsPublishFn<WsState>, endpoint: string,): Record<string, ChannelIncomingEventDeclaration>Source: packages/slingshot-entity/src/channels/applyChannelConfig.ts
buildPolicyAction
Section titled “buildPolicyAction”Map an operation name string to a structured PolicyAction.
Uses the operation kind registry to determine the action kind:
- CRUD operations return
{ kind: '<crud>' }(e.g.{ kind: 'create' }) - Named operations return
{ kind: 'operation', name: '<opName>' }
function buildPolicyAction(opName: string): PolicyActionSource: packages/slingshot-entity/src/policy/resolvePolicy.ts
buildSubscribeGuard
Section titled “buildSubscribeGuard”Build a subscribe guard from a combined map of storageName → EntityChannelConfig.
The returned guard function is passed to WsConfig.endpoints[name].onRoomSubscribe.
When called, it parses the room name into {storageName, entityId, channelName},
locates the matching channel declaration, and runs auth → permission →
middleware checks in order, returning true only when every gate passes.
Guard execution order per subscribe:
- Parse room name →
{ storageName, entityId, channelName }— deny if malformed. - Look up
channelConfigs.get(storageName)— deny if not registered. - Look up
channels[channelName]— deny if not declared. - If
auth === 'userAuth'or'bearer': calldeps.getActor(ws)— deny ifnulloractor.kind === 'anonymous'. - If
permissionpresent: calldeps.checkPermission(actor, ...)— deny iffalse. Ifpermission.ownerFieldis set: load entity viadeps.getEntity()and compareentity[ownerField]toactor.id— deny if mismatch. - For each name in
declaration.middleware: calldeps.middleware[name]— deny iffalse. - Return
true.
function buildSubscribeGuard(channelConfigs: Map<string, EntityChannelConfig>, deps: ChannelConfigDeps,): (ws: unknown, room: string) => Promise<boolean>Source: packages/slingshot-entity/src/channels/applyChannelConfig.ts
createEntityPlugin
Section titled “createEntityPlugin”Create an EntityPlugin from a declarative config.
The plugin wires entities into the Slingshot plugin lifecycle:
setupRoutes— for each entity, callsbuildAdapter(), registers the entity in the framework registry, creates a Hono router, applies route config (auth, permissions, rate limits, middleware, events), mounts bare CRUD + named operation routes, and wires cascade event handlers.setupPost— registers permission resource types, wires WebSocket channel event forwarding, and calls the optionalsetupPosthook from the config.teardown— unsubscribes all cascade and channel event handlers registered duringsetupRoutesandsetupPost.
function createEntityPlugin(pluginConfig: EntityPluginConfig): EntityPluginSource: packages/slingshot-entity/src/createEntityPlugin.ts
createLazyMiddleware
Section titled “createLazyMiddleware”Create a LazyMiddlewareRef initialised with a pass-through next() no-op.
Packages should mount middleware that forwards through ref.handler so the
actual implementation can be swapped in later. The initial value is
intentionally a no-op rather than a thrower: routes that read it before the
package’s setupPost runs (e.g. during route definition) still resolve.
function createLazyMiddleware(): LazyMiddlewareRefSource: packages/slingshot-entity/src/lazyMiddleware.ts
createMemoryEntityAdapter
Section titled “createMemoryEntityAdapter”Create an in-memory EntityAdapter for the given entity config.
- Stores records in a
Mapkeyed by primary key - Supports TTL via per-entry
expiresAt - Soft-delete: sets the configured field instead of removing
- Cursor pagination using cursor field values
- Tenant scoping in list operations
function createMemoryEntityAdapter<Entity, CreateInput, UpdateInput>(config: ResolvedEntityConfig, operations?: Record<string, OperationConfig>,): EntityAdapter<Entity, CreateInput, UpdateInput> & Record<string, unknown>Source: packages/slingshot-entity/src/configDriven/memoryAdapter.ts
createMongoEntityAdapter
Section titled “createMongoEntityAdapter”Creates a MongoDB-backed EntityAdapter for the given entity config.
function createMongoEntityAdapter<Entity, CreateInput, UpdateInput>(conn: Connection, mongoosePkg: MongooseModule, config: ResolvedEntityConfig, operations?: Record<string, OperationConfig>,): EntityAdapter<Entity, CreateInput, UpdateInput> & Record<string, unknown>Source: packages/slingshot-entity/src/configDriven/mongoAdapter.ts
createOperationValidator
Section titled “createOperationValidator”Build a Zod validator for an operation record against a specific set of entity field names.
The returned schema validates that field references inside operation configs
(e.g. transition.field, fieldUpdate.set, search.fields) all point to
real fields on the entity.
Remarks: This factory is called once per validateOperations() invocation. If you need to validate multiple operation records against the same entity, cache the result of one call to avoid rebuilding the schema.
function createOperationValidator(fieldNames: readonly string[]): voidSource: packages/slingshot-entity/src/validation.ts
createPostgresEntityAdapter
Section titled “createPostgresEntityAdapter”Create a config-driven Postgres entity adapter.
The adapter is fully lazy — table creation is deferred to the first query via
ensureTable(), which is idempotent and runs at most once per adapter instance
(guarded by the initialized flag in closure).
Generic type parameters let callers get back properly-typed records and inputs:
Entity— the full entity type returned bygetById,create,update,list.CreateInput— the input accepted bycreate.UpdateInput— the partial input accepted byupdate.
function createPostgresEntityAdapter<Entity, CreateInput, UpdateInput>(pool: PgPool, config: ResolvedEntityConfig, operations?: Record<string, OperationConfig>,): EntityAdapter<Entity, CreateInput, UpdateInput> & Record<string, unknown>Source: packages/slingshot-entity/src/configDriven/postgresAdapter.ts
createRedisEntityAdapter
Section titled “createRedisEntityAdapter”Create a Redis-backed EntityAdapter for the given entity config.
Records are stored as JSON strings under prefixed keys (default format:
${storageName}:${appName}:${pk}). Supports TTL expiration, soft-delete,
cursor pagination, and tenant-scoped list operations.
The key format can be customised via config._conventions.redisKey.
function createRedisEntityAdapter<Entity, CreateInput, UpdateInput>(redis: RedisLike, appName: string, config: ResolvedEntityConfig, operations?: Record<string, OperationConfig>,): EntityAdapter<Entity, CreateInput, UpdateInput> & Record<string, unknown>Source: packages/slingshot-entity/src/configDriven/redisAdapter.ts
createSqliteEntityAdapter
Section titled “createSqliteEntityAdapter”Create a SQLite-backed EntityAdapter for the given entity config.
Auto-creates the table and indexes on first use. Handles domain ↔ storage mapping including dates (epoch ms), JSON (serialised text), booleans (0/1), and snake_case column names. Supports soft-delete, cursor pagination, TTL (via a configurable expiry column), and tenant-scoped list operations.
function createSqliteEntityAdapter<Entity, CreateInput, UpdateInput>(db: SqliteDb, config: ResolvedEntityConfig, operations?: Record<string, OperationConfig>,): EntityAdapter<Entity, CreateInput, UpdateInput> & Record<string, unknown>Source: packages/slingshot-entity/src/configDriven/sqliteAdapter.ts
decodeCursor
Section titled “decodeCursor”Decode an opaque cursor string back to pagination state.
function decodeCursor(cursor: string): Record<string, unknown>Source: packages/slingshot-entity/src/configDriven/fieldUtils.ts
defineEntityExecutor
Section titled “defineEntityExecutor”Freeze an executor definition or builder for safe registration during plugin setup.
function defineEntityExecutor(builder: EntityRouteExecutorBuilder,): EntityRouteExecutorBuilder; export function defineEntityExecutor< const TRequest extends TypedRouteRequestSpec = TypedRouteRequestSpec, >(definition: EntityRouteExecutorDefinition<TRequest>): EntityRouteExecutorDefinition<TRequest>; export function defineEntityExecutor( builderOrDefinition: EntityRouteExecutorBuilder | EntityRouteExecutorDefinition, ): EntityRouteExecutorBuilder | EntityRouteExecutorDefinitionSource: packages/slingshot-entity/src/routing/entityRoutePlanning.ts
defineEntityRoute
Section titled “defineEntityRoute”Freeze an extra route definition for safe registration during plugin setup.
function defineEntityRoute< const TRequest extends TypedRouteRequestSpec = TypedRouteRequestSpec,>(route: EntityExtraRoute<TRequest>): EntityExtraRoute<TRequest>Source: packages/slingshot-entity/src/routing/entityRoutePlanning.ts
definePolicyDispatch
Section titled “definePolicyDispatch”Compose a dispatched policy resolver from per-discriminator handlers.
The returned function is a normal PolicyResolver and can be passed
directly to registerEntityPolicy.
function definePolicyDispatch<TRecord, TInput, TKey extends string = string>(config: PolicyDispatchConfig<TRecord, TInput, TKey>,): PolicyResolver<TRecord, TInput>Source: packages/slingshot-entity/src/policy/definePolicyDispatch.ts
diffEntityConfig
Section titled “diffEntityConfig”Diff two ResolvedEntityConfig snapshots and produce a MigrationPlan.
Compares fields, indexes, unique constraints, soft-delete config, and
pagination config between the previous and current entity definition. The
result is consumed by generateMigrationSqlite(), generateMigrationPostgres(),
and generateMigrationMongo() to produce DDL migration scripts.
Remarks: Change ordering guarantee: within a single diff, removals are emitted before additions for indexes and unique constraints. This matters when the same physical index changes shape (e.g. a non-unique index becomes unique): the old index must be dropped first, otherwise CREATE INDEX IF NOT EXISTS would silently no-op on the existing name.
function diffEntityConfig(previous: ResolvedEntityConfig, current: ResolvedEntityConfig,): MigrationPlanSource: packages/slingshot-entity/src/migrations/diff.ts
encodeCursor
Section titled “encodeCursor”Encode cursor pagination state to an opaque base64url string.
function encodeCursor(values: Record<string, unknown>): stringSource: packages/slingshot-entity/src/configDriven/fieldUtils.ts
encodeEntityEtag
Section titled “encodeEntityEtag”Encode one entity identity/version tuple as the canonical strong ETag.
function encodeEntityEtag(storageName: string, id: string | number, version: number,): stringSource: packages/slingshot-entity/src/concurrency/etag.ts
entity
Section titled “entity”Declare a package-owned entity module.
Standard wiring is the default and should cover the normal case where the framework can resolve the adapter from the entity config and active persistence backend.
function entity< const TConfig extends ResolvedEntityConfig, const TOperations extends EntityOperationsInput = undefined,>(config: { /** Resolved entity config to mount. */ readonly config: TConfig; /** Operation map or `defineOperations(...)` result used for generated routes. */ readonly operations?: TOperations; /** Additional custom routes mounted inside the entity route shell. */ readonly extraRoutes?: readonly EntityExtraRoute[]; /** Generated-route executor overrides. */ readonly overrides?: EntityRouteExecutorOverrides; /** Optional realtime channel declarations. */ readonly channels?: EntityChannelConfig; /** Optional route path override relative to the package mount path. */ readonly path?: string; /** Optional parent path prefix for nested entity routes. */ readonly parentPath?: string; /** Adapter wiring override. Defaults to `{ mode: 'standard' }`. */ readonly wiring?: EntityModuleWiring; }): PackageEntityModule< PackageEntityAdapterFor<TConfig, NormalizeOperationsInput<TOperations>>, EntityMiddlewareNamesOf<TConfig> >; export function entity(config:Source: packages/slingshot-entity/src/packageAuthoring.ts
ENTITY_BACKEND_PROFILES
Section titled “ENTITY_BACKEND_PROFILES”Exhaustive semantic profiles for Slingshot’s five standard entity adapters.
These objects are the sole support source for startup validation, conformance selection, generated reports, and backend documentation.
Source: packages/slingshot-entity/src/configDriven/backendProfiles.ts
entityConfigSchema
Section titled “entityConfigSchema”Zod schema that validates a full EntityConfig object.
Used internally by defineEntity() and validateEntityConfig(). Contains
cross-field refinements (e.g. exactly one primary key, referenced fields
must exist, auto-default type compatibility, route config consistency).
Remarks: Consumers typically do not use this schema directly — prefer validateEntityConfig() for programmatic validation and defineEntity() for build-time validation. This export exists for tools that need the raw Zod schema (e.g. JSON Schema generation, OpenAPI derivation).
Source: packages/slingshot-entity/src/validation.ts
evaluateRouteAuth
Section titled “evaluateRouteAuth”Evaluate auth and permission requirements for a route operation.
Shared between generated entity API routes and entity-driven SSR pages so both surfaces enforce the same auth, parent-auth, and permission behavior.
async function evaluateRouteAuth(c: Context<AppEnv, string>, operationConfig: RouteOperationConfig | undefined, deps: EvaluateRouteAuthDeps,): Promise<RouteAuthResult>Source: packages/slingshot-entity/src/routing/evaluateRouteAuth.ts
Fluent builder namespace for entity field definitions.
Each method returns a frozen FieldDef object describing the field’s type
and constraints. Pass the result directly into the fields record of
defineEntity().
Remarks: - field.string({ primary: true }) implicitly sets immutable: true. - field.enum() requires a values array as its first argument. The allowed values are embedded in the generated TypeScript union type. - field.date({ onUpdate: 'now' }) automatically updates the field to the current timestamp on every write (equivalent to updated_at columns).
Source: packages/slingshot-entity/src/builders/field.ts
freezeEntityPolicyRegistry
Section titled “freezeEntityPolicyRegistry”Freeze the registry. Called once by slingshot-entity.setupRoutes after
it has resolved every policy key used by every entity. Subsequent
registerEntityPolicy calls throw.
function freezeEntityPolicyRegistry(app: Hono<AppEnv>): voidSource: packages/slingshot-entity/src/policy/registerEntityPolicy.ts
fromPgRow
Section titled “fromPgRow”Convert a Postgres row (snake_case keys) back to a domain record (camelCase keys, native JS types).
function fromPgRow(row: Record<string, unknown>, fields: Record<string, FieldDef>,): Record<string, unknown>Source: packages/slingshot-entity/src/configDriven/fieldUtils.ts
generate
Section titled “generate”Generate all source files for an entity definition.
This is a pure function — it performs no disk I/O and has no side
effects. Pass the result to writeGenerated() to write the files to disk,
or process the map directly in tests.
The returned map always includes:
types.ts— TypeScript interfaces for the entity and its operations.schemas.ts— Zod validation schemas.adapter.ts— Adapter interface (CRUD + named operation method signatures).index.ts— Barrel re-exporting all of the above.- One
<backend>.tsper entry inoptions.backends(default: all five).
When options.operations is non-empty:
routes.tsis added (Hono route handlers for every operation).events.tsis added when any route declares aneventconfig.
function generate(config: ResolvedEntityConfig, options?: GenerateOptions,): Record<string, string>Source: packages/slingshot-entity/src/generate.ts
generateInitialMigrationMongo
Section titled “generateInitialMigrationMongo”Generates the initial MongoDB migration script (index and unique-constraint creation) for an entity.
function generateInitialMigrationMongo(config: ResolvedEntityConfig): stringSource: packages/slingshot-entity/src/migrations/generators/initialMongo.ts
generateInitialMigrationPostgres
Section titled “generateInitialMigrationPostgres”Generate the initial PostgreSQL CREATE TABLE migration for an entity.
Emits a single transactional script with CREATE TABLE IF NOT EXISTS, all
column definitions (types, NOT NULL, defaults, primary key), a TTL column
when configured, and CREATE INDEX / CREATE UNIQUE INDEX statements for
declared indexes and unique constraints.
Use this when no prior snapshot exists for the entity (first-ever migration).
Subsequent changes should use generateMigrationPostgres() against a diff.
function generateInitialMigrationPostgres(config: ResolvedEntityConfig): stringSource: packages/slingshot-entity/src/migrations/generators/initial.ts
generateInitialMigrationSqlite
Section titled “generateInitialMigrationSqlite”Generate the initial SQLite CREATE TABLE migration for an entity.
Mirrors generateInitialMigrationPostgres() but uses SQLite column types
(TEXT/INTEGER/REAL) and SQLite-specific default expressions.
function generateInitialMigrationSqlite(config: ResolvedEntityConfig): stringSource: packages/slingshot-entity/src/migrations/generators/initial.ts
generateMigrationMongo
Section titled “generateMigrationMongo”Generate a MongoDB migration script from a MigrationPlan.
Produces a mongosh-runnable JavaScript file. Because MongoDB is
schemaless, field additions are no-ops at the schema level; field removals
use $unset updateMany. Index changes use createIndex / dropIndex.
Collection and field names are never inlined as raw identifiers —
db.getCollection("...") and bracket notation are used so hyphens, dots,
and reserved characters don’t break the generated JavaScript.
Output is deterministic and split into named sections (CLAUDE.md rule 13).
function generateMigrationMongo(plan: MigrationPlan): stringSource: packages/slingshot-entity/src/migrations/generators/mongo.ts
generateMigrationPostgres
Section titled “generateMigrationPostgres”Generate a PostgreSQL migration script from a MigrationPlan.
Emits ALTER TABLE statements for field additions and removals (wrapped in
BEGIN / COMMIT), and CREATE / DROP INDEX statements for index and unique
constraint changes. The output is split into named sections so users can
override individual sections without regenerating the whole file
(CLAUDE.md rule 13).
Output is deterministic — running with the same inputs always produces byte-identical output.
Remarks: - Adding a non-optional column generates an ADD COLUMN without NOT NULL, plus a commented-out SET NOT NULL that the developer should run after backfilling existing rows. - Column type changes are emitted as commented-out ALTER COLUMN … TYPE statements that require manual verification before execution. - UUID defaults use gen_random_uuid() (available in Postgres ≥ 13 without extensions, or via pgcrypto).
function generateMigrationPostgres(plan: MigrationPlan): stringSource: packages/slingshot-entity/src/migrations/generators/postgres.ts
generateMigrations
Section titled “generateMigrations”Generate migration scripts for all specified backends by diffing two entity configs.
Calls diffEntityConfig() internally and routes the MigrationPlan to
each backend-specific generator. Returns an empty object when no changes are
detected. The returned map keys use the pattern migration.<backend>.<ext>
(e.g. migration.sqlite.sql, migration.postgres.sql,
migration.mongo.js).
function generateMigrations(previous: ResolvedEntityConfig, current: ResolvedEntityConfig, backends?: Backend[],): Record<string, string>Source: packages/slingshot-entity/src/migrations/index.ts
generateMigrationSqlite
Section titled “generateMigrationSqlite”Generate a SQLite migration script from a MigrationPlan.
Emits ALTER TABLE statements for field additions and removals, and
CREATE / DROP INDEX statements for index and unique constraint changes.
The output is split into named sections (# --- section:schema ---,
# --- section:indexes ---) so users can override individual sections
without regenerating the whole file (CLAUDE.md rule 13).
Output is deterministic — running with the same inputs always produces byte-identical output, so git diffs only show real changes.
Remarks: - SQLite DROP COLUMN requires version ≥ 3.35.0. The generator emits the statement as a comment for safety, requiring the developer to uncomment it. - Column type changes are emitted as warning comments only — SQLite does not support ALTER COLUMN TYPE.
function generateMigrationSqlite(plan: MigrationPlan): stringSource: packages/slingshot-entity/src/migrations/generators/sqlite.ts
generateSchemas
Section titled “generateSchemas”Generate Zod validation schemas from a resolved entity config.
All four schemas are derived in a single pass over config.fields, with additional
passes for config.indexes, config.tenant, and pagination defaults.
The function is pure — it has no side effects and can be called at any time.
function generateSchemas(config: ResolvedEntityConfig, inputVariant?: string,): GeneratedSchemasSource: packages/slingshot-entity/src/configDriven/schemaGen.ts
getEntityBackendProfile
Section titled “getEntityBackendProfile”Return the immutable semantic profile for a standard entity store.
function getEntityBackendProfile(store: StoreType): EntityBackendProfileSource: packages/slingshot-entity/src/configDriven/backendProfiles.ts
getEntityPluginToolingMetadata
Section titled “getEntityPluginToolingMetadata”Reads the EntityPluginToolingMetadata attached to an entity plugin, or null if absent.
function getEntityPluginToolingMetadata(plugin: unknown,): EntityPluginToolingMetadata | nullSource: packages/slingshot-entity/src/createEntityPlugin.ts
getEntityPolicyResolver
Section titled “getEntityPolicyResolver”Look up a resolver by key. Used internally by slingshot-entity’s
setupRoutes to thread resolvers into the runtime middleware.
Returns undefined if no resolver is registered; callers are
responsible for treating that as a startup error.
function getEntityPolicyResolver(app: Hono<AppEnv>, key: string,): PolicyResolver | undefinedSource: packages/slingshot-entity/src/policy/registerEntityPolicy.ts
Declare a database index on one or more entity fields.
The returned IndexDef is passed into EntityConfig.indexes. Each backend
adapter generates the appropriate DDL or index creation statement during
schema initialisation.
function index(fields: string[], opts?: { direction?: 'asc' | 'desc'; unique?: boolean },): IndexDefSource: packages/slingshot-entity/src/builders/entityHelpers.ts
loadSnapshot
Section titled “loadSnapshot”Load the most recent entity snapshot from snapshotDir.
The snapshot file is located by the entity’s _storageName (e.g.
.slingshot/snapshots/chat_messages.json). Returns null when no snapshot
exists yet (first run).
function loadSnapshot(snapshotDir: string, config: ResolvedEntityConfig,): EntitySnapshot | nullSource: packages/slingshot-entity/src/migrations/snapshotStore.ts
maybeEntityAdapter
Section titled “maybeEntityAdapter”Read an entity adapter from plugin-owned state when available.
Returns null when the plugin has not published that entity adapter. Throws
when the owning plugin’s state shape is malformed.
function maybeEntityAdapter<TAdapter extends object = object>(input: PluginStateMap | PluginStateCarrier | object | null | undefined, lookup: EntityAdapterLookupInput<TAdapter>,): TAdapter | nullSource: packages/slingshot-core/src/pluginState.ts
normalizeEntityRouteShape
Section titled “normalizeEntityRouteShape”Normalize dynamic path params to : for collision detection (e.g. /notes/:id → notes/:).
function normalizeEntityRouteShape(path: string): stringSource: packages/slingshot-entity/src/routing/entityRoutePlanning.ts
Fluent builder namespace for entity operation definitions.
Each method accepts the operation-specific config (without the kind
discriminant) and injects the correct kind string. Pass the results into
defineOperations() to validate field references and freeze the config.
Remarks: op.*() builders are pure — they perform no validation. Validation (field existence, constraint checks) happens when the returned config is passed to defineOperations().
Source: packages/slingshot-entity/src/builders/op.ts
parseEntityEtag
Section titled “parseEntityEtag”Parse one canonical strong Slingshot entity ETag.
Weak tags, wildcards, comma-separated alternatives, non-canonical base64url, malformed JSON, and invalid tuple values are rejected.
function parseEntityEtag(value: string): ParsedEntityEtagSource: packages/slingshot-entity/src/concurrency/etag.ts
planEntityRoutes
Section titled “planEntityRoutes”Plan all entity routes (generated CRUD + named operations + extras), detect collisions, and return them sorted by specificity (most-specific first within each HTTP method).
function planEntityRoutes(entity: ResolvedEntityConfig, operations: Record<string, OperationConfig> | undefined, options?: { routePath?: string; parentPath?: string; extraRoutes?: readonly EntityExtraRoute[]; overrides?: EntityRouteExecutorOverrides; },): PlannedEntityRoute[]Source: packages/slingshot-entity/src/routing/entityRoutePlanning.ts
policyAppliesToOp
Section titled “policyAppliesToOp”Check whether a policy config applies to a given operation.
Resolution uses the operation kind registry to normalize the operation name:
- CRUD operations match their kind directly (e.g.
'create') - Named operations match with the
operation:prefix (e.g.'operation:publish')
When config.applyTo is not set, the policy applies to all operations.
function policyAppliesToOp(config: EntityRoutePolicyConfig, opName: string): booleanSource: packages/slingshot-entity/src/policy/resolvePolicy.ts
publishEntityAdaptersState
Section titled “publishEntityAdaptersState”Publish canonical entity adapters into the owning plugin’s state.
The state entry is always a new frozen plain object. Existing top-level keys
are preserved, and entityAdapters is replaced with a new frozen merged map.
Re-publishing the same entity name with a different adapter instance is a
startup error so dependent plugins never observe ambiguous adapter identity.
function publishEntityAdaptersState<TAdapter extends object>(pluginState: PluginStateMap, pluginName: string, entityAdapters: Record<string, TAdapter>,): Readonly<EntityAdaptersPluginState<TAdapter> & Record<string, unknown>>Source: packages/slingshot-core/src/pluginState.ts
registerEntityPolicy
Section titled “registerEntityPolicy”Register a policy resolver under a named key. Consumers call this from
their plugin’s setupMiddleware phase, before any slingshot-entity
setupRoutes runs for entities that reference the key.
Two call shapes are supported:
registerEntityPolicy(app, key, resolver)— legacy, key passed as a stringregisterEntityPolicy(app, token)— typed token fromdefinePolicy(...)
Registration after the registry has been frozen (which happens at the
end of slingshot-entity.setupRoutes) throws. This prevents late
registration from silently affecting requests in flight.
function registerEntityPolicy<TRecord = unknown, TInput = unknown>(app: Hono<AppEnv>, token: PolicyToken<TRecord, TInput>,): void; export function registerEntityPolicy<TRecord = unknown, TInput = unknown>( app: Hono<AppEnv>, key: string, resolver: PolicyResolver<TRecord, TInput>, ): void; export function registerEntityPolicy( app: Hono<AppEnv>, keyOrToken: string | PolicyToken<unknown, unknown>, maybeResolver?: PolicyResolver<unknown, unknown>, ): voidSource: packages/slingshot-entity/src/policy/registerEntityPolicy.ts
relation
Section titled “relation”Fluent builder namespace for entity relation definitions.
Relations are informational: they drive TypeScript type generation but do not create foreign-key constraints in the database. Use them together with indexes for query performance.
Source: packages/slingshot-entity/src/builders/entityHelpers.ts
requireEntityAdapter
Section titled “requireEntityAdapter”Read an entity adapter from plugin-owned state.
Throws with a startup-focused error when the provider plugin has not published the requested adapter yet.
function requireEntityAdapter<TAdapter extends object = object>(input: PluginStateMap | PluginStateCarrier | object | null | undefined, lookup: EntityAdapterLookupInput<TAdapter>,): TAdapterSource: packages/slingshot-core/src/pluginState.ts
resolveEntityBackendRequirements
Section titled “resolveEntityBackendRequirements”Resolve every backend capability required by a standard entity definition.
The returned array is deterministic and may contain the same capability more than once when several config surfaces require it. Startup errors group those sources while preserving all of them.
function resolveEntityBackendRequirements(config: ResolvedEntityConfig, operations?: Readonly<Record<string, OperationConfig>>,): readonly EntityBackendRequirement[]Source: packages/slingshot-entity/src/configDriven/backendProfiles.ts
resolvePolicy
Section titled “resolvePolicy”Invoke a policy resolver and throw an HTTPException on deny.
Normalizes:
true/false→{ allow: boolean, status: 403 }leakSafe: trueon the config OR an explicitstatus: 404on the decision → 404 instead of 403- A thrown error from the resolver → 500 (resolvers must not throw for policy decisions; throwing is reserved for programmer errors)
async function resolvePolicy<TRecord, TInput>(args: ResolvePolicyArgs<TRecord, TInput>,): Promise<void>Source: packages/slingshot-entity/src/policy/resolvePolicy.ts
safeReadJsonBody
Section titled “safeReadJsonBody”Read the parsed JSON body from a Hono context without consuming the
body stream. Relies on Hono’s built-in body caching — c.req.json()
parses on first call and stores the result; subsequent calls return
the cached value.
Returns null on:
- non-JSON content type
- empty body (GET/DELETE)
- malformed JSON (resolver receives
nullinput; it may deny)
Never throws — policy evaluation must never 500 on a client body issue.
async function safeReadJsonBody(c: Context): Promise<Record<string, unknown> | null>Source: packages/slingshot-entity/src/policy/safeReadJsonBody.ts
saveSnapshot
Section titled “saveSnapshot”Persist the current entity definition as a snapshot file.
The snapshot is written atomically: content is serialized to a randomly
named .tmp file in the same directory, then renamed into place. On POSIX
systems rename(2) is atomic, so concurrent CLI invocations will always
see a complete snapshot — never a partially written file.
The snapshot directory is created recursively if it does not exist.
function saveSnapshot(snapshotDir: string, config: ResolvedEntityConfig): voidSource: packages/slingshot-entity/src/migrations/snapshotStore.ts
scoreEntityRouteSpecificity
Section titled “scoreEntityRouteSpecificity”Score a route path by specificity: static segments add 1000, dynamic segments subtract 10, plus segment count.
function scoreEntityRouteSpecificity(path: string): numberSource: packages/slingshot-entity/src/routing/entityRoutePlanning.ts
setPostgresTenantContext
Section titled “setPostgresTenantContext”Bind PostgreSQL row-level-security policies to a tenant for the current
transaction. The third set_config argument is deliberately true, so a
pooled connection cannot retain tenant identity after COMMIT or ROLLBACK.
async function setPostgresTenantContext(client: PostgresTenantContextClient, tenantId: string,): Promise<void>Source: packages/slingshot-entity/src/migrations/postgresTenantContext.ts
storageName
Section titled “storageName”Derive the backend-specific storage name for an entity.
Returns the name that should be used when addressing this entity’s backing
store — the table name for relational backends, the collection name for
Mongo, or the key prefix for Redis. Falls back to the entity’s canonical
_storageName when no backend-specific override is configured, except for
PostgreSQL which prefixes with slingshot_ by default to avoid collisions with
system tables.
function storageName(config: ResolvedEntityConfig, backend: 'sqlite' | 'postgres' | 'mongo' | 'redis',): stringSource: packages/slingshot-entity/src/lib/naming.ts
toCamelCase
Section titled “toCamelCase”Convert a snake_case string to camelCase.
Used by SQL adapters to map database column names back to domain field names.
function toCamelCase(str: string): stringSource: packages/slingshot-entity/src/configDriven/fieldUtils.ts
toPgRow
Section titled “toPgRow”Convert a domain record to a Postgres row (snake_case keys, native PG types).
function toPgRow(record: Record<string, unknown>, fields: Record<string, FieldDef>,): Record<string, unknown>Source: packages/slingshot-entity/src/configDriven/fieldUtils.ts
toSnakeCase
Section titled “toSnakeCase”Convert a camelCase string to snake_case.
Used by SQL adapters to map domain field names to database column names.
function toSnakeCase(str: string): stringSource: packages/slingshot-entity/src/configDriven/fieldUtils.ts
upgradeSnapshotV1
Section titled “upgradeSnapshotV1”Deterministically upgrade the legacy full-config snapshot to v2 schema facts.
function upgradeSnapshotV1(snapshot: EntitySnapshotV1): EntitySnapshotV2Source: packages/slingshot-entity/src/migrations/snapshotStore.ts
validateEntityConfig
Section titled “validateEntityConfig”Validate an arbitrary value as an entity config.
Runs the same Zod schema used by defineEntity() without throwing on
failure. Useful for validating JSON blobs from external sources (CLI input,
config files, etc.) before passing them to defineEntity().
function validateEntityConfig(config: unknown): ValidationResultSource: packages/slingshot-entity/src/validation.ts
validateOperations
Section titled “validateOperations”Validate an operations record against an entity’s field names.
Checks that field references inside operation configs (transition, fieldUpdate, search, upsert, aggregate, collection, consume) all point to known entity fields. Returns structured errors rather than throwing.
function validateOperations(operations: unknown, fieldNames: readonly string[],): ValidationResultSource: packages/slingshot-entity/src/validation.ts
wireChannelForwarding
Section titled “wireChannelForwarding”Wire event forwarding for a single entity’s channel config.
For each channel declaration that has forward.events, subscribes to every
listed bus event and, on receipt, constructs the room name
{storageName}:{entityId}:{channelName} and calls publishFn to broadcast
the payload to all connected room subscribers.
The entityId is extracted from the event payload using
declaration.forward.idField ?? entity._pkField.
publishFn is injected rather than imported directly because the framework’s
publish() helper lives in src/framework/lib/ws.ts, which is not accessible
to packages. The caller (createEntityPlugin setupPost) provides it.
function wireChannelForwarding(channelConfig: EntityChannelConfig, entity: ResolvedEntityConfig, getWsState: () => WsState | null, bus: SlingshotEventBus, endpoint: string, publishFn: WsPublishFn<WsState>,): () => voidSource: packages/slingshot-entity/src/channels/applyChannelConfig.ts
writeGenerated
Section titled “writeGenerated”Generate source files for an entity and write them to disk.
Wraps generate() with file-system I/O:
- Calls
generate(config, options)to produce the file map. - If
options.snapshotDirandoptions.migrationare both set, loads the previous snapshot, diffs it againstconfig, and adds migration scripts to the file map before writing. - Writes each file only when its content has changed (avoids unnecessary git diffs on unchanged files).
- After all files are written successfully, saves the current config as the new snapshot (so the next run can diff against it).
Remarks: The snapshot is saved after all writes succeed. If a write throws, the snapshot is not updated — the next run will re-generate from the last successful state and produce accurate migration scripts.
function writeGenerated(config: ResolvedEntityConfig, options: WriteOptions,): Record<string, string>Source: packages/slingshot-entity/src/cli.ts
Classes
Section titled “Classes”UnsupportedEntityBackendError
Section titled “UnsupportedEntityBackendError”Startup error thrown when a standard entity backend cannot honor its resolved configuration.
Source: packages/slingshot-entity/src/configDriven/backendProfiles.ts
Interfaces
Section titled “Interfaces”AggregateOpConfig
Section titled “AggregateOpConfig”Aggregate operation — compute summary statistics over a set of records.
Optionally groups results by a field value. Each key in compute produces a
calculated column (count, sum, avg, min, max).
Source: packages/slingshot-core/src/operations.ts
BatchOpConfig
Section titled “BatchOpConfig”Batch operation — update or delete multiple records matching a filter in one call.
Returns the number of affected rows. Optionally atomic (transaction-wrapped) when the backend supports a real transaction and the caller wants the stricter boundary.
Source: packages/slingshot-core/src/operations.ts
ChannelConfigDeps
Section titled “ChannelConfigDeps”Runtime dependencies for buildSubscribeGuard().
Provides identity resolution, permission checking, named middleware handlers, and an optional entity loader for ownership checks.
Source: packages/slingshot-entity/src/channels/applyChannelConfig.ts
CollectionOpConfig
Section titled “CollectionOpConfig”Collection operation — manage an embedded ordered list of sub-documents within a parent entity.
Provides typed list/add/remove/update/set methods for arrays stored as a JSON field (Postgres/SQLite) or embedded array (Mongo).
Source: packages/slingshot-core/src/operations.ts
ComputedAggregateOpConfig
Section titled “ComputedAggregateOpConfig”Computed aggregate operation — query one entity, aggregate it, and materialise the result back into another entity (the “target”).
Used for denormalised summary fields (e.g., commentCount on a Post).
Source: packages/slingshot-core/src/operations.ts
ComputedField
Section titled “ComputedField”A field-level computed aggregate specification.
Used in op.aggregate to declare what to compute across grouped records.
Source: packages/slingshot-core/src/operations.ts
ConsumeOpConfig
Section titled “ConsumeOpConfig”Consume operation — atomically read and delete a record in a single call.
Used for one-time-use tokens, OTPs, magic links, and other single-claim resources.
Optional expiry field check rejects records that have passed their TTL.
Source: packages/slingshot-core/src/operations.ts
CustomOpConfig
Section titled “CustomOpConfig”Custom operation — escape hatch for operations that cannot be expressed declaratively.
Each backend key is an optional factory that receives the raw store handle and returns
a typed callable. Only the factory for the active StoreType is called at runtime.
Standard vs. manual wiring:
Standard config-driven factories require a callable factory for the active backend.
Startup fails with UnsupportedEntityBackendError when it is absent. Applications
that supply operation methods externally must use manual adapter wiring instead.
Route auto-mounting:
Set http to have the entity plugin auto-mount an HTTP route for this operation.
The method in http.method controls the HTTP verb; http.path overrides the URL
segment (defaults to /{opName} in kebab-case). The route handler calls
adapter[opName](body) — standard wiring verifies that the method can be built before
the adapter or route is constructed.
Source: packages/slingshot-core/src/operations.ts
DeriveOpConfig
Section titled “DeriveOpConfig”Derive operation — compose results from multiple entity sources.
Queries each source in sources and merges the results according to merge.
Useful for “feed” or “inbox” queries that pull from multiple entity types.
Source: packages/slingshot-core/src/operations.ts
DeriveSource
Section titled “DeriveSource”A single data source for a DeriveOpConfig.
Specifies an entity to query (from) with match conditions (where).
Optional traverse resolves a relation to a different entity.
Source: packages/slingshot-core/src/operations.ts
EntityAdapterLookup
Section titled “EntityAdapterLookup”Coordinates for locating an entity adapter within plugin state.
Source: packages/slingshot-core/src/pluginStateTypes.ts
EntityAuditFinding
Section titled “EntityAuditFinding”A single finding produced by one audit rule.
Findings are aggregated into an EntityAuditResult by auditEntity().
Source: packages/slingshot-entity/src/audits/types.ts
EntityAuditResult
Section titled “EntityAuditResult”The aggregated result of running all audit rules against one entity.
Returned by auditEntity(). Check errors > 0 to gate CI pipelines.
Source: packages/slingshot-entity/src/audits/types.ts
EntityConfig
Section titled “EntityConfig”Full declarative configuration for a single entity: its fields, indexes, relations, storage conventions, soft-delete, TTL, search, and route settings.
Source: packages/slingshot-entity/src/types/entity.ts
EntityExtraRoute
Section titled “EntityExtraRoute”A user-defined extra route mounted alongside the generated CRUD routes for an entity.
Extra routes participate in collision detection and specificity sorting with
generated routes, but their executors are always user-provided via buildExecutor.
Source: packages/slingshot-entity/src/routing/entityRoutePlanning.ts
EntityPlugin
Section titled “EntityPlugin”A SlingshotPlugin extended with WebSocket channel helpers.
Returned by createEntityPlugin(). Implements the full plugin lifecycle
(setupRoutes, setupPost, teardown) and adds buildSubscribeGuard
for declarative WebSocket subscribe authorization.
Remarks: Wire the returned guard into WsConfig.endpoints[wsEndpoint].onRoomSubscribe in your app config. The guard is built lazily — call plugin.buildSubscribeGuard(deps) after the plugin has been created, passing runtime identity/permission resolvers.
Source: packages/slingshot-entity/src/createEntityPlugin.ts
EntityPluginConfig
Section titled “EntityPluginConfig”Configuration for the createEntityPlugin() factory.
Wire entities as TypeScript EntityPluginEntry[] defined via
defineEntity() + defineOperations().
Source: packages/slingshot-entity/src/createEntityPlugin.ts
EntityPluginContext
Section titled “EntityPluginContext”Context object passed to the EntityPluginConfig.setupPost hook.
Provides access to the event bus, entity entries, and permissions wiring.
Source: packages/slingshot-entity/src/createEntityPlugin.ts
EntityPluginToolingMetadata
Section titled “EntityPluginToolingMetadata”Tooling-facing metadata attached to a compiled entity plugin, exposing its registered entity entries.
Source: packages/slingshot-entity/src/createEntityPlugin.ts
EntityRouteExecutionContext
Section titled “EntityRouteExecutionContext”Execution context passed to entity route executors.
Extends the typed route context with entity-specific fields: the resolved entity config, the backing adapter, data scope bindings, and cross-entity adapter lookup.
Source: packages/slingshot-entity/src/routing/entityRoutePlanning.ts
EntityRouteExecutorBuilderContext
Section titled “EntityRouteExecutorBuilderContext”Context passed to an EntityRouteExecutorBuilder when it constructs a route executor at plan time.
Source: packages/slingshot-entity/src/routing/entityRoutePlanning.ts
EntityRouteExecutorDefinition
Section titled “EntityRouteExecutorDefinition”Full executor override for a generated route, pairing a builder with optional request/response schemas and OpenAPI metadata.
Source: packages/slingshot-entity/src/routing/entityRoutePlanning.ts
EntityRouteExecutorOverrides
Section titled “EntityRouteExecutorOverrides”Override map for replacing generated CRUD and named operation route executors.
Each key corresponds to a generated route key. Values can be a bare executor builder
function or a full EntityRouteExecutorDefinition with request/response schemas.
Source: packages/slingshot-entity/src/routing/entityRoutePlanning.ts
EntitySnapshotV1
Section titled “EntitySnapshotV1”A persisted snapshot of an entity config at a point in time.
Written to disk by saveSnapshot() and read back by loadSnapshot().
The entity field is the full ResolvedEntityConfig serialized as JSON.
Source: packages/slingshot-entity/src/migrations/types.ts
EntitySnapshotV2
Section titled “EntitySnapshotV2”Normalized migration snapshot. Runtime callbacks are removed by JSON serialization before persistence so planning depends only on schema facts.
Source: packages/slingshot-entity/src/migrations/types.ts
EntityStorageConventions
Section titled “EntityStorageConventions”Consumer-configurable storage convention overrides for an entity.
Passed via conventions on EntityConfig. Allows consumers to customize
how records are keyed in Redis, how IDs are generated, and how fields are
updated without forking adapter code.
All properties are optional. When omitted, the built-in defaults apply:
- Redis key format:
${storageName}:${appName}:${pk} - ID generation:
'uuid','cuid','now'(built-in sentinels) - On-update:
'now'(built-in sentinel)
Source: packages/slingshot-core/src/entityConfig.ts
EntityStorageFieldMap
Section titled “EntityStorageFieldMap”Storage-level field name overrides for backend adapters.
These control how domain fields are mapped to physical storage fields. All fields have sensible defaults when omitted.
Source: packages/slingshot-core/src/entityConfig.ts
EntityStorageHints
Section titled “EntityStorageHints”Per-backend storage customisation hints.
All properties are optional. When omitted, each backend derives its table /
collection name from the entity’s _storageName.
Remarks: These are hints, not guarantees — backends that don’t support a given hint silently ignore it.
Source: packages/slingshot-entity/src/types/entity.ts
EntitySystemFields
Section titled “EntitySystemFields”Consumer-configurable system field names for an entity.
Allows consumers to rename framework-assumed field names to match their domain model. All fields have sensible defaults when omitted.
Source: packages/slingshot-core/src/entityConfig.ts
EntityTtlConfig
Section titled “EntityTtlConfig”Time-to-live configuration for entities stored in TTL-capable backends (e.g. Redis).
Records are automatically expired after defaultSeconds unless the adapter
or caller overrides the TTL at write time.
Source: packages/slingshot-entity/src/types/entity.ts
EntityVersionConcurrencyConfig
Section titled “EntityVersionConcurrencyConfig”Opt-in optimistic-concurrency configuration for an entity.
Source: packages/slingshot-core/src/entityConfig.ts
EntityWriteOptions
Section titled “EntityWriteOptions”Per-write optimistic-concurrency options accepted by entity adapters.
Source: packages/slingshot-core/src/entityConfig.ts
EvaluateRouteAuthDeps
Section titled “EvaluateRouteAuthDeps”Runtime dependencies required to evaluate route auth.
Source: packages/slingshot-entity/src/routing/evaluateRouteAuth.ts
ExistsOpConfig
Section titled “ExistsOpConfig”Exists operation — check whether at least one record satisfies a field match.
More efficient than lookup when you only need a boolean result.
Optional check fields narrow the test beyond the primary match.
Source: packages/slingshot-core/src/operations.ts
FactoriesEntityModuleWiring
Section titled “FactoriesEntityModuleWiring”Resolve the entity adapter from repo factories instead of the default registry path.
Source: packages/slingshot-entity/src/packageAuthoring.ts
FieldDef
Section titled “FieldDef”The resolved, frozen description of a single entity field.
Produced by the field.*() builders and stored inside EntityConfig.fields.
All properties are readonly — mutating a field definition after
defineEntity() is called is not supported.
Source: packages/slingshot-entity/src/types/fields.ts
FieldOptions
Section titled “FieldOptions”Options accepted by the field.*() builder functions.
All options are optional; the builder applies sensible defaults.
Remarks: Setting primary: true implicitly sets immutable: true unless immutable is explicitly provided as false.
Source: packages/slingshot-entity/src/types/fields.ts
FieldUpdateOpConfig
Section titled “FieldUpdateOpConfig”Field update operation — selectively update a subset of fields on a matched record.
More targeted than a full update — only the fields listed in set can be mutated.
Useful for operations that update one attribute without overwriting others (e.g., mark as read).
Source: packages/slingshot-core/src/operations.ts
GeneratedSchemas
Section titled “GeneratedSchemas”The four Zod schemas generated from a ResolvedEntityConfig.
Each schema is a z.ZodObject so callers can merge, extend, or .parse() it.
Source: packages/slingshot-entity/src/configDriven/schemaGen.ts
GenerateOptions
Section titled “GenerateOptions”Options controlling what generate() produces.
All options are optional. Omitting backends generates all five adapters.
Omitting operations generates CRUD-only adapters (no named operation methods).
Source: packages/slingshot-entity/src/generate.ts
IncrementOpConfig
Section titled “IncrementOpConfig”Increment (or decrement) a numeric field on a specific record.
The record is looked up by primary key. The named field is increased by by
(default 1). Pass a negative value for by to decrement. All backends
perform the increment atomically where the store supports it (Postgres uses
SET field = field + $n, Mongo uses $inc, memory/Redis use read-modify-write).
Source: packages/slingshot-core/src/operations.ts
LazyMiddlewareRef
Section titled “LazyMiddlewareRef”Mutable container holding the active middleware handler.
Source: packages/slingshot-entity/src/lazyMiddleware.ts
LookupOpConfig
Section titled “LookupOpConfig”Lookup operation — find one or many records by matching field values.
fields maps entity field names to 'param:x' references or literals.
returns: 'one' produces Entity | null; returns: 'many' produces a paginated list.
Source: packages/slingshot-core/src/operations.ts
ManualEntityModuleWiring
Section titled “ManualEntityModuleWiring”Fully manual adapter construction for advanced authoring cases.
Source: packages/slingshot-entity/src/packageAuthoring.ts
MigrationPlan
Section titled “MigrationPlan”The complete migration plan produced by diffEntityConfig().
Passed to the backend-specific generators to emit DDL/script files.
hasBreakingChanges is true when any changeFieldType change was detected.
warnings are human-readable notes included as comments in generated files.
Source: packages/slingshot-entity/src/migrations/types.ts
MissingEntityBackendCapability
Section titled “MissingEntityBackendCapability”One unsupported requirement reported by UnsupportedEntityBackendError.
Source: packages/slingshot-entity/src/configDriven/backendProfiles.ts
PackageEntityModule
Section titled “PackageEntityModule”Package-owned entity module returned by entity(...).
Source: packages/slingshot-entity/src/packageAuthoring.ts
PackageEntityModuleImplementation
Section titled “PackageEntityModuleImplementation”Normalized entity module implementation compiled into the framework plugin lifecycle.
Source: packages/slingshot-entity/src/packageAuthoring.ts
PaginationConfig
Section titled “PaginationConfig”Cursor-based pagination configuration for list operations.
When set, the generated list() method accepts a cursor parameter and
returns nextCursor alongside the result items.
Source: packages/slingshot-entity/src/types/entity.ts
ParsedEntityEtag
Section titled “ParsedEntityEtag”The decoded identity and version carried by a Slingshot entity ETag.
Source: packages/slingshot-entity/src/concurrency/etag.ts
PlannedEntityRoute
Section titled “PlannedEntityRoute”A fully resolved entity route with method, path, specificity score, and optional
operation config. Produced by planEntityRoutes after collision detection
and sorted by specificity (static segments first, then dynamic).
Source: packages/slingshot-entity/src/routing/entityRoutePlanning.ts
PolicyDispatchConfig
Section titled “PolicyDispatchConfig”Configuration for definePolicyDispatch.
Source: packages/slingshot-entity/src/policy/definePolicyDispatch.ts
RelationDef
Section titled “RelationDef”Describes a foreign-key relationship from this entity to another.
Used by the code generator to emit typed relation accessors. Relations are informational at the database level — no FK constraints are created unless the consumer’s migration scripts add them manually.
Source: packages/slingshot-entity/src/types/entity.ts
ResolvedEntityConfig
Section titled “ResolvedEntityConfig”A fully resolved, immutable entity configuration.
Produced by defineEntity() and used by all downstream APIs — code
generation, the audit runner, the migration differ, and the plugin factory.
The config is deep-frozen at creation time (CLAUDE.md rule 12) and must not
be mutated.
Source: packages/slingshot-entity/src/types/entity.ts
ResolvedEntityStorageConventions
Section titled “ResolvedEntityStorageConventions”Resolved storage conventions attached to ResolvedEntityConfig._conventions.
Mirrors EntityStorageConventions with the same optional shape.
undefined fields mean “use built-in behavior”. The resolved object is
frozen at definition time and consumed by all backend adapters.
Source: packages/slingshot-core/src/entityConfig.ts
ResolvedEntityStorageFieldMap
Section titled “ResolvedEntityStorageFieldMap”Resolved (defaulted) storage field mapping attached to ResolvedEntityConfig.
All fields are guaranteed non-null — defaults are applied at definition time
by defineEntity(). Backend adapters read these resolved names instead of
hardcoding storage-level field conventions.
Source: packages/slingshot-core/src/entityConfig.ts
ResolvedEntitySystemFields
Section titled “ResolvedEntitySystemFields”Resolved (defaulted) system fields attached to ResolvedEntityConfig.
All fields are guaranteed non-null — defaults are applied at definition time
by defineEntity(). Adapters and route builders read these resolved names
instead of hardcoding first-party conventions.
Source: packages/slingshot-core/src/entityConfig.ts
ResolvedEntityVersionConcurrencyConfig
Section titled “ResolvedEntityVersionConcurrencyConfig”Normalized optimistic-concurrency metadata attached by defineEntity.
Source: packages/slingshot-core/src/entityConfig.ts
ResolvedOperations
Section titled “ResolvedOperations”The resolved result of pairing an entity config with its named operation configs.
Produced by op.define() (in slingshot-data) and consumed by the executor and
codegen layers. The operations map is keyed by operation name (e.g. 'getByRoom').
Source: packages/slingshot-core/src/operations.ts
ResolvePolicyArgs
Section titled “ResolvePolicyArgs”Arguments for resolvePolicy.
Source: packages/slingshot-entity/src/policy/resolvePolicy.ts
RouteAuthResult
Section titled “RouteAuthResult”Result of evaluating auth and permission requirements for an entity route.
Source: packages/slingshot-entity/src/routing/evaluateRouteAuth.ts
SearchOpConfig
Section titled “SearchOpConfig”Search operation — full-text or filtered search across entity records.
When useSearchProvider is true (default when the entity has a search config),
the search is delegated to the configured search provider (e.g., Meilisearch).
Otherwise, a DB-native LIKE/text search is used.
Source: packages/slingshot-core/src/operations.ts
StandardEntityModuleWiring
Section titled “StandardEntityModuleWiring”Use the framework’s default adapter resolution for the entity.
Source: packages/slingshot-entity/src/packageAuthoring.ts
TenantConfig
Section titled “TenantConfig”Multi-tenant isolation configuration.
When set, every database query automatically filters by the value stored in
field — preventing cross-tenant data access. The field must exist in
EntityConfig.fields.
Source: packages/slingshot-entity/src/types/entity.ts
TransitionOpConfig
Section titled “TransitionOpConfig”Transition operation — atomically move a record from one state to another.
The operation matches a record by match fields, verifies the current value of
field equals from, then updates it to to. Optional set fields are updated
at the same time. Useful for state machine transitions (e.g., pending → active).
Source: packages/slingshot-core/src/operations.ts
UpsertOpConfig
Section titled “UpsertOpConfig”Upsert operation — create or update a record based on uniqueness fields.
Matches on the fields listed in match. If a record exists, updates the set fields.
If not, creates a new record applying onCreate defaults.
Source: packages/slingshot-core/src/operations.ts
ValidationResult
Section titled “ValidationResult”The result of a validation call.
When success is false, errors contains a ZodError with structured
issue paths that can be mapped to user-friendly messages.
Source: packages/slingshot-entity/src/validation.ts
WriteOptions
Section titled “WriteOptions”Options for writeGenerated() — extends GenerateOptions with disk I/O
controls.
Source: packages/slingshot-entity/src/cli.ts
AuditSeverity
Section titled “AuditSeverity”The severity of an audit finding.
'error'— the entity definition is incorrect and will likely cause runtime failures (e.g. atransition.fieldthat doesn’t exist).'warning'— the definition is valid but has a quality issue that will likely cause performance problems or subtle bugs (e.g. missing index on a lookup field).'info'— an advisory note with no urgency (e.g. no facetable search fields configured).
Source: packages/slingshot-entity/src/audits/types.ts
AutoDefault
Section titled “AutoDefault”Sentinel values that instruct slingshot to generate a default at write time rather than storing a literal.
'uuid'— generate a random UUID v4 (string fields only).'cuid'— generate a CUID (string fields only).'now'— use the current timestamp (date fields only).
Source: packages/slingshot-entity/src/types/fields.ts
BareEntityAdapter
Section titled “BareEntityAdapter”A full entity adapter: typed CRUD methods plus a dynamic index for named
operation methods (for example adapter.byRoom(input)).
Source: packages/slingshot-entity/src/routing/adapterTypes.ts
ChannelMiddlewareHandler
Section titled “ChannelMiddlewareHandler”A named WebSocket channel middleware handler.
Called by buildSubscribeGuard() for each middleware name listed in a
channel declaration. Return false (or throw) to deny the subscription.
Source: packages/slingshot-entity/src/channels/applyChannelConfig.ts
CollectionOperation
Section titled “CollectionOperation”Valid operations on a collection (embedded array of sub-documents).
Source: packages/slingshot-core/src/operations.ts
CustomAutoDefaultResolver
Section titled “CustomAutoDefaultResolver”Custom auto-default resolver function for entity field defaults.
Extends the built-in 'uuid' | 'cuid' | 'now' auto-default sentinels with
consumer-defined strategies. Called during record creation when a field’s
default value is a string that does not match a built-in sentinel.
Return the generated value to use it, or undefined to signal that the
sentinel is not recognized (which will throw an error).
Source: packages/slingshot-core/src/entityConfig.ts
CustomOnUpdateResolver
Section titled “CustomOnUpdateResolver”Custom on-update resolver function for entity field update-time values.
Extends the built-in 'now' on-update sentinel with consumer-defined
strategies. Called during record updates when a field’s onUpdate value
is a string that does not match 'now'.
Return the computed value to apply it, or undefined to skip the field.
Source: packages/slingshot-core/src/entityConfig.ts
DefineEntityConfig
Section titled “DefineEntityConfig”Authoring-time entity config accepted by defineEntity.
Identical to EntityConfig except the routes slot is narrowed against
the entity’s own fields and DTO variants: the DTO variant slot is restricted to
the keys of dto, and the input variant slot to the union of declared
field.inputVariants strings — so referencing a variant that no field declares
is a compile error.
This narrowing lives here, on the input type, rather than on the structural
EntityConfig so that the resolved config stays covariant in F (see the
routes note on EntityConfig).
Source: packages/slingshot-entity/src/types/entity.ts
EntityGeneratedRouteKey
Section titled “EntityGeneratedRouteKey”Identifies a generated CRUD route (create/list/get/update/delete) or a named operation route (operations.${name}).
Source: packages/slingshot-entity/src/routing/entityRoutePlanning.ts
EntityModuleWiring
Section titled “EntityModuleWiring”Supported adapter-wiring strategies for a package-owned entity module.
Source: packages/slingshot-entity/src/packageAuthoring.ts
EntityPluginEntry
Section titled “EntityPluginEntry”Describes a single entity wired into an EntityPlugin.
Two forms are supported:
EntityPluginEntryFactories(factories±entityKey) — zero-code wiring. Pass single-entity or composite factories; the plugin resolves the adapter and mixes composite-level ops automatically. UseonAdapterto capture a ref.EntityPluginEntryManual(buildAdapter) — escape hatch for adapters that cannot be expressed as factories (custom infra, wrapping logic, etc.).
Source: packages/slingshot-entity/src/createEntityPlugin.ts
EntityRouteExecutor
Section titled “EntityRouteExecutor”Handles a single entity route request, returning a Response from the given execution context.
Source: packages/slingshot-entity/src/routing/entityRoutePlanning.ts
EntityRouteExecutorBuilder
Section titled “EntityRouteExecutorBuilder”Builds an EntityRouteExecutor from builder context at plan time, binding the entity, adapter, and route key.
Source: packages/slingshot-entity/src/routing/entityRoutePlanning.ts
EntitySnapshot
Section titled “EntitySnapshot”Persisted migration snapshot accepted by the deterministic v1-to-v2 loader.
Source: packages/slingshot-entity/src/migrations/types.ts
ExtractInputVariants
Section titled “ExtractInputVariants”Extract the union of all named input-variant strings declared on the
fields of an entity. Used to narrow routes.<op>.input so picking a
variant name not declared on any field is a compile error.
Source: packages/slingshot-entity/src/types/entity.ts
FilterOperator
Section titled “FilterOperator”Union of all supported comparison operator objects for a single field filter.
Source: packages/slingshot-core/src/operations.ts
FilterValue
Section titled “FilterValue”A single field’s filter value — a literal, null, or a comparison operator.
String values starting with 'param:' are treated as parameter references
resolved at runtime (e.g. 'param:userId' resolves to the userId param).
The sentinel 'now' in comparison operators resolves to new Date().
Remarks: The 'param:x' prefix is a runtime injection mechanism: the executor reads the call-time params map and substitutes the value of key x before the filter reaches the database adapter. This means filters can be defined statically in the operation config while still accepting dynamic values per call. Literal strings that do not start with 'param:' are passed through unchanged as constant equality checks.
Source: packages/slingshot-core/src/operations.ts
MergeStrategy
Section titled “MergeStrategy”Strategy for merging results from multiple sources in op.derive.
'union'— deduplicate by ID across all sources'concat'— concatenate all results in source order'intersect'— return only IDs present in all sources'first'— return results from the first non-empty source only'priority'— like first, but sources are weighted by configuration
Source: packages/slingshot-core/src/operations.ts
MigrationChange
Section titled “MigrationChange”A single detected change between two entity config snapshots.
Produced by diffEntityConfig() and consumed by the per-backend migration
generators (generateMigrationSqlite, generateMigrationPostgres,
generateMigrationMongo).
The discriminated union covers all change types that affect the physical schema:
addField/removeField— column additions/removals.changeFieldType— column type change (breaking — emitted as a warning comment, not a live ALTER statement, for safety).addIndex/removeIndex— index creation/deletion.addUnique/removeUnique— unique constraint creation/deletion.changeSoftDelete— soft-delete config added, removed, or changed.changePagination— pagination config changed (informational only, no DDL emitted).
Source: packages/slingshot-entity/src/migrations/types.ts
OperationConfig
Section titled “OperationConfig”Union of all supported declarative operation configuration types.
Used as the value type in ResolvedOperations.operations and PipeStep.config.
Source: packages/slingshot-core/src/operations.ts
PackageEntityAdapterFor
Section titled “PackageEntityAdapterFor”Strongly typed adapter surface inferred from an entity config and optional operation map.
Source: packages/slingshot-entity/src/packageAuthoring.ts
SoftDeleteConfig
Section titled “SoftDeleteConfig”Soft-delete configuration for an entity; when set, deletes update a field instead of removing the record.
Source: packages/slingshot-entity/src/types/entity.ts
WsPublishFn
Section titled “WsPublishFn”Publishes a payload to a WebSocket room on a given endpoint; re-exported from slingshot-core.
Source: packages/slingshot-entity/src/channels/applyChannelConfig.ts
Exports
Section titled “Exports”createCompositeFactories
Section titled “createCompositeFactories”Create a RepoFactories object that produces a composite adapter covering
all the supplied entities, plus any cross-entity transaction or pipe operations.
Each entry in entities maps a key name to an { config, operations? } pair.
The returned factories object has a factory for every StoreType (memory, redis,
sqlite, mongo, postgres). When a factory is called by resolveRepo(), it
instantiates an individual adapter for each entity, wires any composite operations
against the full adapters map, and combines everything into a single composite object
that also exposes a clear() method.
TypeScript infers the full shape of the composite adapter from both the entity map and the composite operations map, including all operation method signatures.
Why composite operations?
op.transaction and op.pipe need access to multiple entity adapters simultaneously —
they cannot be wired inside a single entity’s adapter factory. Declare them here so they
receive the full adapters map at wiring time.
Atomicity:
memory— steps run sequentially but earlier writes are not rolled back after failure.sqlite— steps are wrapped in an explicitBEGIN/COMMIT/ROLLBACKblock.postgres— transaction ops run on a singlepgclient insideBEGIN/COMMIT/ROLLBACK, so all steps share one real database transaction.mongo,redis— no transaction wrapper is applied yet. These backends require backend-specific session or multi-command transaction support and are left for future work.
Source: packages/slingshot-entity/src/configDriven/composition.ts
createEntityFactories
Section titled “createEntityFactories”Create TestableRepoFactories for a resolved entity config without
operations.
Source: packages/slingshot-entity/src/configDriven/createEntityFactories.ts
defineEntity
Section titled “defineEntity”Declare an entity and validate its configuration.
This is the primary entry point for defining an entity at dev time. It:
- Validates the config with Zod (field types, primary key constraints, cross-field references for indexes, softDelete, tenant, and routes).
- Derives
_pkField(the field withprimary: true). - Derives
_storageNamefrom the entity name and optional namespace. - Deep-freezes the result so consumers always receive immutable data (CLAUDE.md rule 12).
Source: packages/slingshot-entity/src/defineEntity.ts
defineOperations
Section titled “defineOperations”Declare and validate operations for an entity.
Operations describe business-logic queries and mutations beyond basic CRUD. This function:
- Validates that all field references in operation configs point to real
fields on the entity (e.g.
transition.field,fieldUpdate.set,search.fields). - Deep-freezes the resulting operations object (CLAUDE.md rule 12).
Source: packages/slingshot-entity/src/defineOperations.ts