Skip to content

@lastshotlabs/slingshot-entity

npm install @lastshotlabs/slingshot-entity

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:

  1. Built-in auto-default ('uuid', 'cuid', 'now'): delegated to resolveAutoDefault (with customAutoDefault forwarded).
  2. Custom string default: if customAutoDefault is provided and the default value is a string that is not a built-in sentinel, the resolver is called. If it returns a non-undefined value, that value is used; otherwise the literal string is used as-is.
  3. 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

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:

  1. Built-in sentinel ('now'): sets the field to new Date().
  2. Custom sentinel (any other string): if customOnUpdate is provided, it is invoked with the sentinel string. When the resolver returns a non-undefined value, that value is written to the field. If the resolver returns undefined, 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

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>>,): void

Source: packages/slingshot-entity/src/configDriven/backendProfiles.ts

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>,): EntityAuditResult

Source: packages/slingshot-entity/src/audits/index.ts

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:

  1. Validates payload.room is a non-empty string with a valid room name pattern.
  2. Parses room → {storageName}:{entityId}:{channelName}.
  3. Confirms storageName matches the entity and channelName has a receive config.
  4. Confirms the event type is in receive.events whitelist (defense in depth).
  5. Confirms the sender is subscribed (via ws.data.rooms) — prevents relay to rooms the sender has not joined.
  6. If toRoom (default true), calls publishFn to broadcast to the room, optionally excluding the sender (excludeSender, default true).

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

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): PolicyAction

Source: packages/slingshot-entity/src/policy/resolvePolicy.ts

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:

  1. Parse room name → { storageName, entityId, channelName } — deny if malformed.
  2. Look up channelConfigs.get(storageName) — deny if not registered.
  3. Look up channels[channelName] — deny if not declared.
  4. If auth === 'userAuth' or 'bearer': call deps.getActor(ws) — deny if null or actor.kind === 'anonymous'.
  5. If permission present: call deps.checkPermission(actor, ...) — deny if false. If permission.ownerField is set: load entity via deps.getEntity() and compare entity[ownerField] to actor.id — deny if mismatch.
  6. For each name in declaration.middleware: call deps.middleware[name] — deny if false.
  7. Return true.
function buildSubscribeGuard(channelConfigs: Map<string, EntityChannelConfig>, deps: ChannelConfigDeps,): (ws: unknown, room: string) => Promise<boolean>

Source: packages/slingshot-entity/src/channels/applyChannelConfig.ts

Create an EntityPlugin from a declarative config.

The plugin wires entities into the Slingshot plugin lifecycle:

  • setupRoutes — for each entity, calls buildAdapter(), 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 optional setupPost hook from the config.
  • teardown — unsubscribes all cascade and channel event handlers registered during setupRoutes and setupPost.
function createEntityPlugin(pluginConfig: EntityPluginConfig): EntityPlugin

Source: packages/slingshot-entity/src/createEntityPlugin.ts

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(): LazyMiddlewareRef

Source: packages/slingshot-entity/src/lazyMiddleware.ts

Create an in-memory EntityAdapter for the given entity config.

  • Stores records in a Map keyed 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

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

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[]): void

Source: packages/slingshot-entity/src/validation.ts

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 by getById, create, update, list.
  • CreateInput — the input accepted by create.
  • UpdateInput — the partial input accepted by update.
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

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

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

Decode an opaque cursor string back to pagination state.

function decodeCursor(cursor: string): Record<string, unknown>

Source: packages/slingshot-entity/src/configDriven/fieldUtils.ts

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 | EntityRouteExecutorDefinition

Source: packages/slingshot-entity/src/routing/entityRoutePlanning.ts

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

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

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,): MigrationPlan

Source: packages/slingshot-entity/src/migrations/diff.ts

Encode cursor pagination state to an opaque base64url string.

function encodeCursor(values: Record<string, unknown>): string

Source: packages/slingshot-entity/src/configDriven/fieldUtils.ts

Encode one entity identity/version tuple as the canonical strong ETag.

function encodeEntityEtag(storageName: string, id: string | number, version: number,): string

Source: packages/slingshot-entity/src/concurrency/etag.ts

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

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

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

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

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>): void

Source: packages/slingshot-entity/src/policy/registerEntityPolicy.ts

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 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>.ts per entry in options.backends (default: all five).

When options.operations is non-empty:

  • routes.ts is added (Hono route handlers for every operation).
  • events.ts is added when any route declares an event config.
function generate(config: ResolvedEntityConfig, options?: GenerateOptions,): Record<string, string>

Source: packages/slingshot-entity/src/generate.ts

Generates the initial MongoDB migration script (index and unique-constraint creation) for an entity.

function generateInitialMigrationMongo(config: ResolvedEntityConfig): string

Source: packages/slingshot-entity/src/migrations/generators/initialMongo.ts

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): string

Source: packages/slingshot-entity/src/migrations/generators/initial.ts

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): string

Source: packages/slingshot-entity/src/migrations/generators/initial.ts

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): string

Source: packages/slingshot-entity/src/migrations/generators/mongo.ts

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): string

Source: packages/slingshot-entity/src/migrations/generators/postgres.ts

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

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): string

Source: packages/slingshot-entity/src/migrations/generators/sqlite.ts

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,): GeneratedSchemas

Source: packages/slingshot-entity/src/configDriven/schemaGen.ts

Return the immutable semantic profile for a standard entity store.

function getEntityBackendProfile(store: StoreType): EntityBackendProfile

Source: packages/slingshot-entity/src/configDriven/backendProfiles.ts

Reads the EntityPluginToolingMetadata attached to an entity plugin, or null if absent.

function getEntityPluginToolingMetadata(plugin: unknown,): EntityPluginToolingMetadata | null

Source: packages/slingshot-entity/src/createEntityPlugin.ts

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 | undefined

Source: 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 },): IndexDef

Source: packages/slingshot-entity/src/builders/entityHelpers.ts

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 | null

Source: packages/slingshot-entity/src/migrations/snapshotStore.ts

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 | null

Source: packages/slingshot-core/src/pluginState.ts

Normalize dynamic path params to : for collision detection (e.g. /notes/:idnotes/:).

function normalizeEntityRouteShape(path: string): string

Source: 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

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): ParsedEntityEtag

Source: packages/slingshot-entity/src/concurrency/etag.ts

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

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): boolean

Source: packages/slingshot-entity/src/policy/resolvePolicy.ts

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

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 string
  • registerEntityPolicy(app, token) — typed token from definePolicy(...)

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>, ): void

Source: packages/slingshot-entity/src/policy/registerEntityPolicy.ts

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

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>,): TAdapter

Source: packages/slingshot-core/src/pluginState.ts

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

Invoke a policy resolver and throw an HTTPException on deny.

Normalizes:

  • true / false{ allow: boolean, status: 403 }
  • leakSafe: true on the config OR an explicit status: 404 on 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

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 null input; 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

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): void

Source: packages/slingshot-entity/src/migrations/snapshotStore.ts

Score a route path by specificity: static segments add 1000, dynamic segments subtract 10, plus segment count.

function scoreEntityRouteSpecificity(path: string): number

Source: packages/slingshot-entity/src/routing/entityRoutePlanning.ts

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

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',): string

Source: packages/slingshot-entity/src/lib/naming.ts

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): string

Source: packages/slingshot-entity/src/configDriven/fieldUtils.ts

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

Convert a camelCase string to snake_case.

Used by SQL adapters to map domain field names to database column names.

function toSnakeCase(str: string): string

Source: packages/slingshot-entity/src/configDriven/fieldUtils.ts

Deterministically upgrade the legacy full-config snapshot to v2 schema facts.

function upgradeSnapshotV1(snapshot: EntitySnapshotV1): EntitySnapshotV2

Source: packages/slingshot-entity/src/migrations/snapshotStore.ts

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): ValidationResult

Source: packages/slingshot-entity/src/validation.ts

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[],): ValidationResult

Source: packages/slingshot-entity/src/validation.ts

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>,): () => void

Source: packages/slingshot-entity/src/channels/applyChannelConfig.ts

Generate source files for an entity and write them to disk.

Wraps generate() with file-system I/O:

  1. Calls generate(config, options) to produce the file map.
  2. If options.snapshotDir and options.migration are both set, loads the previous snapshot, diffs it against config, and adds migration scripts to the file map before writing.
  3. Writes each file only when its content has changed (avoids unnecessary git diffs on unchanged files).
  4. 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

Startup error thrown when a standard entity backend cannot honor its resolved configuration.

Source: packages/slingshot-entity/src/configDriven/backendProfiles.ts

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

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

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

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

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

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

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

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

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

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

Coordinates for locating an entity adapter within plugin state.

Source: packages/slingshot-core/src/pluginStateTypes.ts

A single finding produced by one audit rule.

Findings are aggregated into an EntityAuditResult by auditEntity().

Source: packages/slingshot-entity/src/audits/types.ts

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

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

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

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

Configuration for the createEntityPlugin() factory.

Wire entities as TypeScript EntityPluginEntry[] defined via defineEntity() + defineOperations().

Source: packages/slingshot-entity/src/createEntityPlugin.ts

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

Tooling-facing metadata attached to a compiled entity plugin, exposing its registered entity entries.

Source: packages/slingshot-entity/src/createEntityPlugin.ts

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

Context passed to an EntityRouteExecutorBuilder when it constructs a route executor at plan time.

Source: packages/slingshot-entity/src/routing/entityRoutePlanning.ts

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

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

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

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

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

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

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

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

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

Opt-in optimistic-concurrency configuration for an entity.

Source: packages/slingshot-core/src/entityConfig.ts

Per-write optimistic-concurrency options accepted by entity adapters.

Source: packages/slingshot-core/src/entityConfig.ts

Runtime dependencies required to evaluate route auth.

Source: packages/slingshot-entity/src/routing/evaluateRouteAuth.ts

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

Resolve the entity adapter from repo factories instead of the default registry path.

Source: packages/slingshot-entity/src/packageAuthoring.ts

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

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

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

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

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

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

Mutable container holding the active middleware handler.

Source: packages/slingshot-entity/src/lazyMiddleware.ts

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

Fully manual adapter construction for advanced authoring cases.

Source: packages/slingshot-entity/src/packageAuthoring.ts

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

One unsupported requirement reported by UnsupportedEntityBackendError.

Source: packages/slingshot-entity/src/configDriven/backendProfiles.ts

Package-owned entity module returned by entity(...).

Source: packages/slingshot-entity/src/packageAuthoring.ts

Normalized entity module implementation compiled into the framework plugin lifecycle.

Source: packages/slingshot-entity/src/packageAuthoring.ts

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

The decoded identity and version carried by a Slingshot entity ETag.

Source: packages/slingshot-entity/src/concurrency/etag.ts

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

Configuration for definePolicyDispatch.

Source: packages/slingshot-entity/src/policy/definePolicyDispatch.ts

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

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

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

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

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

Normalized optimistic-concurrency metadata attached by defineEntity.

Source: packages/slingshot-core/src/entityConfig.ts

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

Arguments for resolvePolicy.

Source: packages/slingshot-entity/src/policy/resolvePolicy.ts

Result of evaluating auth and permission requirements for an entity route.

Source: packages/slingshot-entity/src/routing/evaluateRouteAuth.ts

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

Use the framework’s default adapter resolution for the entity.

Source: packages/slingshot-entity/src/packageAuthoring.ts

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

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., pendingactive).

Source: packages/slingshot-core/src/operations.ts

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

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

Options for writeGenerated() — extends GenerateOptions with disk I/O controls.

Source: packages/slingshot-entity/src/cli.ts

The severity of an audit finding.

  • 'error' — the entity definition is incorrect and will likely cause runtime failures (e.g. a transition.field that 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

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

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

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

Valid operations on a collection (embedded array of sub-documents).

Source: packages/slingshot-core/src/operations.ts

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

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

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

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

Supported adapter-wiring strategies for a package-owned entity module.

Source: packages/slingshot-entity/src/packageAuthoring.ts

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. Use onAdapter to 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

Handles a single entity route request, returning a Response from the given execution context.

Source: packages/slingshot-entity/src/routing/entityRoutePlanning.ts

Builds an EntityRouteExecutor from builder context at plan time, binding the entity, adapter, and route key.

Source: packages/slingshot-entity/src/routing/entityRoutePlanning.ts

Persisted migration snapshot accepted by the deterministic v1-to-v2 loader.

Source: packages/slingshot-entity/src/migrations/types.ts

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

Union of all supported comparison operator objects for a single field filter.

Source: packages/slingshot-core/src/operations.ts

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

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

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

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

Strongly typed adapter surface inferred from an entity config and optional operation map.

Source: packages/slingshot-entity/src/packageAuthoring.ts

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

Publishes a payload to a WebSocket room on a given endpoint; re-exported from slingshot-core.

Source: packages/slingshot-entity/src/channels/applyChannelConfig.ts

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 explicit BEGIN / COMMIT / ROLLBACK block.
  • postgres — transaction ops run on a single pg client inside BEGIN / 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

Create TestableRepoFactories for a resolved entity config without operations.

Source: packages/slingshot-entity/src/configDriven/createEntityFactories.ts

Declare an entity and validate its configuration.

This is the primary entry point for defining an entity at dev time. It:

  1. Validates the config with Zod (field types, primary key constraints, cross-field references for indexes, softDelete, tenant, and routes).
  2. Derives _pkField (the field with primary: true).
  3. Derives _storageName from the entity name and optional namespace.
  4. Deep-freezes the result so consumers always receive immutable data (CLAUDE.md rule 12).

Source: packages/slingshot-entity/src/defineEntity.ts

Declare and validate operations for an entity.

Operations describe business-logic queries and mutations beyond basic CRUD. This function:

  1. Validates that all field references in operation configs point to real fields on the entity (e.g. transition.field, fieldUpdate.set, search.fields).
  2. Deep-freezes the resulting operations object (CLAUDE.md rule 12).

Source: packages/slingshot-entity/src/defineOperations.ts