Skip to content

@lastshotlabs/slingshot

npm install @lastshotlabs/slingshot

Shared field-mapping utilities for config-driven adapters.

  • camelCase ↔ snake_case conversion
  • SQL / Mongo type mapping
  • Record transformation (domain ↔ storage)
  • Auto-default resolution
  • Naming conventions per backend
function applyDefaults(input: Record<string, unknown>, fields: Record<string, FieldDef>, customAutoDefault?: CustomAutoDefaultResolver,): Record<string, unknown>

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

Shared field-mapping utilities for config-driven adapters.

  • camelCase ↔ snake_case conversion
  • SQL / Mongo type mapping
  • Record transformation (domain ↔ storage)
  • Auto-default resolution
  • Naming conventions per backend
function applyOnUpdate(input: Record<string, unknown>, fields: Record<string, FieldDef>, customOnUpdate?: CustomOnUpdateResolver,): Record<string, unknown>

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

function assertProductionReadiness(config: AuditConfig, options?: ProductionReadinessAuditOptions,): ProductionReadinessReport

Source: src/prodReadiness.ts

Skip logging for requests with these HTTP methods (e.g. ["GET", "HEAD"]).

function auditLog(options: AuditLogMiddlewareOptions): MiddlewareHandler<AppEnv>

Source: src/framework/middleware/auditLog.ts

function auditProductionReadiness(config: AuditConfig, options: ProductionReadinessAuditOptions = {},): ProductionReadinessReport

Source: src/prodReadiness.ts

Convert a dotted-decimal IPv4 string to an unsigned 32-bit integer.

function botProtection({ blockList = [] }: BotProtectionOptions): MiddlewareHandler

Source: src/framework/middleware/botProtection.ts

Get or create the Mongoose CacheEntry model on the given connection. Accepts connection and mongoose module as parameters — no module-level state.

async function bustCache(key: string, app: object): void

Source: src/framework/middleware/cacheResponse.ts

Get or create the Mongoose CacheEntry model on the given connection. Accepts connection and mongoose module as parameters — no module-level state.

async function bustCachePattern(pattern: string, app: object): void

Source: src/framework/middleware/cacheResponse.ts

Get or create the Mongoose CacheEntry model on the given connection. Accepts connection and mongoose module as parameters — no module-level state.

function cacheResponse({ ttl, key, store: storeOverride, }: CacheOptions): MiddlewareHandler<AppEnv>

Source: src/framework/middleware/cacheResponse.ts

Create a fresh, instance-scoped metrics state container.

async function closeMetricsQueues(state: MetricsState): Promise<void>

Source: src/framework/metrics/registry.ts

Absolute path to the service’s routes directory (use import.meta.dir + “/routes”). Optional — omit when all routes are registered via plugins.

async function createApp<T extends object = object>(config: CreateAppConfig<T>,): Promise<CreateAppResult>

Source: src/app.ts

Configuration for creating an audit log provider.

function createAuditLogProvider(options: AuditLogOptions): AuditLogProvider

Source: src/framework/auditLog/index.ts

Creates the slingshot-auth plugin instance for use with createApp(), createServer(), or as a standalone Hono plugin via plugin.setup().

The plugin bootstraps all auth subsystems (session store, adapters, rate limiting, credential stuffing detection, OAuth providers, MFA, SAML, SCIM, etc.) and mounts the corresponding route handlers on the Hono app.

Remarks: - In standalone mode (no SlingshotFrameworkConfig), config.runtime.password is required. - Production boot requires an explicit security.signing.sessionBinding choice. Without it a stolen JWT+session pair is usable from any IP or browser. Set security.signing.sessionBinding to either a real binding policy or false to acknowledge the risk explicitly. - OAuth routes are provided by @lastshotlabs/slingshot-oauth and mounted by that plugin.

function createAuthPlugin(rawConfig: AuthPluginConfig): StandalonePlugin

Source: packages/slingshot-auth/src/plugin.ts

Narrow, untyped view of the event bus for string-keyed subscriptions.

Plugins that subscribe to dynamically named events (e.g. entity:${storageName}.created) cast the typed SlingshotEventBus to this interface rather than widening the global SlingshotEventMap. This keeps type widening local per rule 12.

Defined here once so every consumer imports the same shape (rule 6). Use Pick to narrow further when a module only needs emit or only needs on/off.

function createInProcessAdapter(serializationOpts?: EventBusSerializationOptions,): SlingshotEventBus

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

Config-driven memory adapter generator.

Produces an EntityAdapter backed by an in-memory Map with LRU eviction, optional TTL, soft-delete, cursor pagination, and tenant scoping.

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

Create a fresh, instance-scoped metrics state container.

function createMetricsState(): MetricsState

Source: src/framework/metrics/registry.ts

Config-driven MongoDB adapter generator.

Lazily creates a Mongoose model from the entity config, including compound indices, TTL expiration, soft-delete, cursor pagination, and tenant scoping.

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

Config-driven PostgreSQL adapter generator.

Produces a full EntityAdapter implementation backed by a pg connection pool, driven entirely by ResolvedEntityConfig — no hand-written SQL schema required.

Features:

  • Auto-creates the table on first use (CREATE TABLE IF NOT EXISTS) with correct column types, NOT NULL constraints, and a PRIMARY KEY.
  • Creates compound indexes (CREATE INDEX IF NOT EXISTS) and unique constraints (CREATE UNIQUE INDEX IF NOT EXISTS) from config.indexes and config.uniques.
  • Optional TTL column (configurable, default _expires_at) when config.ttl.defaultSeconds is set.
  • Soft-delete: delete() writes the soft-delete field value instead of DELETE.
  • Cursor pagination: multi-field lexicographic cursors via buildCursorForRecord/decodeCursor.
  • create() uses plain INSERT; primary-key and unique conflicts reject consistently with the memory and SQLite adapters. Replacement requires an explicit update/upsert operation.
  • Spreads buildPostgresOperations() result to attach custom operation methods.
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

Sign data with the active key (first element of secret). Normalizes string | string[] so that an array is never passed directly to createHmac() — which would silently call .toString() and produce “[object Array]” as the key.

function createPresignedUrl(base: string, key: string, opts: { method: string; expiry: number; extra?: Record<string, string> }, secret: string | string[],): string

Source: src/lib/signing.ts

Config-driven Redis adapter generator.

Records are stored as JSON strings under prefixed keys. Supports TTL, soft-delete, cursor pagination, and tenant scoping.

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

ioredis connection options or a Redis URL string

function createRedisTransport(opts: RedisTransportOptions): WsTransportAdapter

Source: src/framework/ws/redisTransport.ts

The schema parameter type expected by zodOpenAPIRegistry.add().

function createRoute<T extends RouteConfig>(config: T): T

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

A single field-level validation error detail produced by the default formatter.

function createRouter(): void

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

Shutdown registry shape. Lives on process via a well-known Symbol so there is zero module-level mutable state. The registry is truly process-scoped (where POSIX signals live) and survives module re-evaluation.

async function createServer<T extends object = object>(config: CreateServerConfig<T>,): Promise<Server<SocketData<T>>>

Source: src/server.ts

Configuration for the Slingshot-integrated admin plugin.

Extends AdminPluginConfig with optional overrides for accessProvider, managedUserProvider, and permissions. When these are omitted, sensible framework defaults are resolved automatically:

  • accessProvider defaults to createSlingshotAuthAccessProvider, which reads permissions from the auth runtime context.
  • managedUserProvider defaults to createSlingshotManagedUserProvider, constructed from the auth runtime adapter, config, and session repository.
  • permissions defaults to the value stored under PERMISSIONS_STATE_KEY in ctx.pluginState — set by the community plugin or another permissions source during its setupRoutes phase.

All three are required at route-registration time. If permissions is not provided and no plugin has populated PERMISSIONS_STATE_KEY, setupPost throws.

function createSlingshotAdminPlugin(config: SlingshotAdminPluginConfig): SlingshotPlugin

Source: src/framework/admin/index.ts

Config-driven SQLite adapter generator.

Auto-creates the table on first use, generates indices for indexed fields, and handles domain ↔ storage mapping including dates, JSON, booleans, etc. Supports soft-delete, cursor pagination, TTL, and tenant scoping.

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

Per-app SSE connection registry.

Manages the full lifecycle of Server-Sent Event connections: opening streams, broadcasting events to connected clients, optional per-client filtering, and graceful shutdown. One registry instance is created per app via createSseRegistry and stored in the app context.

All methods are safe to call from any async context — the internal map is updated synchronously and eviction is performed inline on enqueue failure.

function createSseUpgradeHandler<T extends object = object>(endpoint: string, actorResolver?: RequestActorResolver | null,): (req: Request) => Promise<SseClientData<T>>

Source: src/framework/sse/index.ts

Read-only representation of a tenant record.

function createTenantService(conn: Connection, getTenantCache?: () => { delete(tenantId: string): void } | null, ): TenantService

Source: src/framework/tenancy/service.ts

Per-socket data attached to every WebSocket connection by the Bun server.

This type is the data object passed to all ws.* handler callbacks (open, message, close, drain). The generic T parameter lets plugins attach custom fields (e.g., roomId) at upgrade time. The base fields are populated by createWsUpgradeHandler.

function createWsUpgradeHandler(server: Server<BaseSocketData>, endpoint: string, actorResolver?: RequestActorResolver | null): void

Source: src/framework/ws/index.ts

Default values for cursor-based pagination query parameters.

function cursorParams(defaults?: CursorParamDefaults): void

Source: src/framework/lib/pagination.ts

Default values for cursor-based pagination query parameters.

function cursorResponse<T extends ZodType>(itemSchema: T, name: string): void

Source: src/framework/lib/pagination.ts

Shared field-mapping utilities for config-driven adapters.

  • camelCase ↔ snake_case conversion
  • SQL / Mongo type mapping
  • Record transformation (domain ↔ storage)
  • Auto-default resolution
  • Naming conventions per backend
function decodeCursor(cursor: string): Record<string, unknown>

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

A single field-level validation error detail produced by the default formatter.

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

Canonical declarative app config shape.

Alias of CreateServerConfig surfaced under a friendlier name. Users author this in app.config.ts and the framework boots from the default export.

function defineApp<T extends object = object>(config: AppConfig<T>): AppConfig<T>

Source: src/defineApp.ts

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

function defineCapability<TValue>(name: string): PackageCapabilityHandle<TValue>

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

function definePackage(input: DefinePackageInput): SlingshotPackageDefinition

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

function definePackageContract<const TName extends string>(contractName: TName,): PackageContract<TName>

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

Store a new upload record. Keyed by the storage key.

async function deleteUploadRecord(key: string, app: object): Promise<boolean>

Source: src/framework/upload/registry.ts

Shared field-mapping utilities for config-driven adapters.

  • camelCase ↔ snake_case conversion
  • SQL / Mongo type mapping
  • Record transformation (domain ↔ storage)
  • Auto-default resolution
  • Naming conventions per backend
function encodeCursor(values: Record<string, unknown>): string

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

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

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

function entityRef<TAdapter>(entity: SlingshotPackageEntityModuleLike<TAdapter>, options?: { plugin?: string },): PackageEntityRef<TAdapter>; /** * Create a typed entity ref directly from a package/entity name pair. * * @deprecated For cross-package entity access, prefer publishing a typed ref through a * package contract: `Matches.publicEntities(

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

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Zod schema generation from entity config.

Derives four Zod schemas from a ResolvedEntityConfig at runtime, without any code generation step. Schemas are consumed by route handlers for request validation and by the REST API generator for OpenAPI type inference.

Generated schemas:

  1. entitySchema — Full record shape; optional fields become .optional().
  2. createSchema — Create input; excludes auto-default fields (uuid, now, cuid) and onUpdate fields. Fields with literal defaults or marked optional are optional in this schema.
  3. updateSchema — Partial update input; all included fields are .optional(). Excludes immutable fields and onUpdate fields.
  4. listOptionsSchema — Filter + pagination options for list endpoints. Includes enum/boolean fields, all indexed fields, the tenant field, and limit/cursor/sortDir.
function generateSchemas(config: ResolvedEntityConfig, inputVariant?: string,): GeneratedSchemas

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

Resolve the canonical actor for a Hono request context.

Reads the actor variable published by the auth middleware (identify, bearerAuth, or a custom identity middleware). Returns ANONYMOUS_ACTOR when no actor has been published.

function getActor(c: Context<AppEnv>): Actor

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

Resolve the canonical actor for a Hono request context.

Reads the actor variable published by the auth middleware (identify, bearerAuth, or a custom identity middleware). Returns ANONYMOUS_ACTOR when no actor has been published.

function getActorId(c: Context<AppEnv>): string | null

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

Resolve the canonical actor for a Hono request context.

Reads the actor variable published by the auth middleware (identify, bearerAuth, or a custom identity middleware). Returns ANONYMOUS_ACTOR when no actor has been published.

function getActorTenantId(c: Context<AppEnv>): string | null

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

Attach a trust-proxy depth to a raw Request object for standalone (non-framework) usage.

When using getClientIp outside of a full Slingshot app (e.g., in a plain Hono server), call this function before the request reaches the handler so getClientIp can read the correct proxy trust depth from the request.

Remarks: Side effects: this function mutates the Request object by defining a non-enumerable property keyed by an internal Symbol. The mutation is confined to the single Request instance — it does not affect other requests or global state. The property is configurable: true so it can be overwritten by a subsequent call with a different value on the same request object.

Remarks: In a full Slingshot app, do NOT call this — set trustProxy in the app config instead and the framework reads it from SlingshotContext. This function is only for plain Hono servers that call getClientIp() directly without a SlingshotContext in scope.

function getClientIp<E extends AppEnv>(c: Context<E>): string

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

Attach a SlingshotContext to a Hono app instance.

Called once by createApp() after the context is fully assembled. The context is stored as a non-enumerable property keyed by a well-known Symbol so that it does not appear in object spreads or JSON.stringify output. When the target object exposes app.use(...) (for example a Hono app in standalone tests), attachContext() also installs a lightweight request middleware that seeds c.set('slingshotCtx', ctx) so request-time helpers like getSlingshotCtx(c) work without the full createApp() bootstrap.

Remarks: Do not call from plugin or application code. This function is called exactly once per app instance by createApp() after the context is fully assembled.

Remarks: Calling attachContext more than once on the same app object with different context instances causes two categories of breakage, so this function now throws instead of allowing a second attachment:

Remarks: 1. Duplicate context per app — a second call would otherwise overwrite the first context. Any code that captured a reference to the first context via getContext(app) would then hold stale state while request middleware could still reference the old closure.

Remarks: 2. WeakMap collision — framework internals that key off the app object (e.g. the Reflect-symbol DI table) are keyed by app identity. If two contexts share the same app reference they collide on those lookups, producing hard-to-diagnose bugs where one plugin’s resolver or repo leaks into another app instance’s context.

Remarks: Plugin code should always call getContext(app) to read the context and must never attempt to create or attach one.

function getContext(app: object): SlingshotContext

Source: packages/slingshot-core/src/context/contextStore.ts

Attach a SlingshotContext to a Hono app instance.

Called once by createApp() after the context is fully assembled. The context is stored as a non-enumerable property keyed by a well-known Symbol so that it does not appear in object spreads or JSON.stringify output. When the target object exposes app.use(...) (for example a Hono app in standalone tests), attachContext() also installs a lightweight request middleware that seeds c.set('slingshotCtx', ctx) so request-time helpers like getSlingshotCtx(c) work without the full createApp() bootstrap.

Remarks: Do not call from plugin or application code. This function is called exactly once per app instance by createApp() after the context is fully assembled.

Remarks: Calling attachContext more than once on the same app object with different context instances causes two categories of breakage, so this function now throws instead of allowing a second attachment:

Remarks: 1. Duplicate context per app — a second call would otherwise overwrite the first context. Any code that captured a reference to the first context via getContext(app) would then hold stale state while request middleware could still reference the old closure.

Remarks: 2. WeakMap collision — framework internals that key off the app object (e.g. the Reflect-symbol DI table) are keyed by app identity. If two contexts share the same app reference they collide on those lookups, producing hard-to-diagnose bugs where one plugin’s resolver or repo leaks into another app instance’s context.

Remarks: Plugin code should always call getContext(app) to read the context and must never attempt to create or attach one.

function getContextOrNull(app: object): SlingshotContext | null

Source: packages/slingshot-core/src/context/contextStore.ts

Lazy mongoose module loader — caching a require() result, not runtime state.

function getMongoFromApp(app: object,): { auth: Connection | null; app: Connection | null } | null

Source: src/lib/mongo.ts

Lazy mongoose module loader — caching a require() result, not runtime state.

function getMongooseModule(): MongooseModule

Source: src/lib/mongo.ts

Redis host:port (e.g., “localhost:6379”)

function getRedisFromApp(app: object): RedisClass | null

Source: src/lib/redis.ts

Resolve the canonical actor for a Hono request context.

Reads the actor variable published by the auth middleware (identify, bearerAuth, or a custom identity middleware). Returns ANONYMOUS_ACTOR when no actor has been published.

function getRequestTenantId(c: Context<AppEnv>): string | null

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

True when the user holds at least one live socket in the room.

Presence is the only reliable liveness signal for a room member: the engine’s per-player connected flag exists only while a game runtime is active, so a lobby (no runtime yet) has no other way to tell whether the host is still there. Apps use this to detect an absent host and offer recovery.

function getRoomPresence(state: WsState, endpoint: string, room: string): string[]

Source: src/framework/ws/presence.ts

Safe default room-subscribe authorization, applied when an endpoint provides no explicit onRoomSubscribe guard.

Raw {action:'subscribe', room} messages are handled before per-event auth, so without a guard any socket — including an anonymous one — could subscribe to another user’s private room (e.g. sessions:<sid>:player:<victimUserId>) and receive their private state. This default denies subscription to any per-user room (a room containing a :player:<userId> segment) unless the socket’s authenticated actor IS that user. The target userId is embedded in the room name, so this self-authorization needs no external session lookup.

All other rooms remain allowed by default so existing consumers and legitimate session-room subscriptions keep working. Endpoints that need stricter or namespace-specific rules set their own onRoomSubscribe, which fully replaces this default.

function getRooms(state: WsState, endpoint: string): string[]

Source: src/framework/ws/rooms.ts

Safe default room-subscribe authorization, applied when an endpoint provides no explicit onRoomSubscribe guard.

Raw {action:'subscribe', room} messages are handled before per-event auth, so without a guard any socket — including an anonymous one — could subscribe to another user’s private room (e.g. sessions:<sid>:player:<victimUserId>) and receive their private state. This default denies subscription to any per-user room (a room containing a :player:<userId> segment) unless the socket’s authenticated actor IS that user. The target userId is embedded in the room name, so this self-authorization needs no external session lookup.

All other rooms remain allowed by default so existing consumers and legitimate session-room subscriptions keep working. Endpoints that need stricter or namespace-specific rules set their own onRoomSubscribe, which fully replaces this default.

function getRoomSubscribers(state: WsState, endpoint: string, room: string): string[]

Source: src/framework/ws/rooms.ts

Shutdown registry shape. Lives on process via a well-known Symbol so there is zero module-level mutable state. The registry is truly process-scoped (where POSIX signals live) and survives module re-evaluation.

function getServerContext(server: object): SlingshotContext | null

Source: src/server.ts

A single field-level validation error detail produced by the default formatter.

function getSlingshotCtx(c: Context<AppEnv>): SlingshotContext

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

Safe default room-subscribe authorization, applied when an endpoint provides no explicit onRoomSubscribe guard.

Raw {action:'subscribe', room} messages are handled before per-event auth, so without a guard any socket — including an anonymous one — could subscribe to another user’s private room (e.g. sessions:<sid>:player:<victimUserId>) and receive their private state. This default denies subscription to any per-user room (a room containing a :player:<userId> segment) unless the socket’s authenticated actor IS that user. The target userId is embedded in the room name, so this self-authorization needs no external session lookup.

All other rooms remain allowed by default so existing consumers and legitimate session-room subscriptions keep working. Endpoints that need stricter or namespace-specific rules set their own onRoomSubscribe, which fully replaces this default.

function getSubscriptions<T extends WithRooms>(ws: ServerWebSocket<T>): string[]

Source: src/framework/ws/rooms.ts

Store a new upload record. Keyed by the storage key.

async function getUploadRecord(key: string, app: object): Promise<UploadRecord | null>

Source: src/framework/upload/registry.ts

True when the user holds at least one live socket in the room.

Presence is the only reliable liveness signal for a room member: the engine’s per-player connected flag exists only while a game runtime is active, so a lobby (no runtime yet) has no other way to tell whether the host is still there. Apps use this to detect an absent host and offer recovery.

function getUserPresence(state: WsState, userId: string): string[]

Source: src/framework/ws/presence.ts

Options for the handleUpload middleware.

All fields are optional overrides of the app-level upload configuration stored in SlingshotContext. Values provided here take precedence over the app-level defaults for a specific route.

See UploadOpts (from @framework/upload/upload) for available fields: maxFileSize, maxFiles, allowedMimeTypes, keyPrefix, etc.

function handleUpload(opts?: UploadMiddlewareOptions): MiddlewareHandler<AppEnv>

Source: src/framework/middleware/upload.ts

Sign data with the active key (first element of secret). Normalizes string | string[] so that an array is never passed directly to createHmac() — which would silently call .toString() and produce “[object Array]” as the key.

function hmacSign(data: string, secret: string | string[]): string

Source: src/lib/signing.ts

Sign data with the active key (first element of secret). Normalizes string | string[] so that an array is never passed directly to createHmac() — which would silently call .toString() and produce “[object Array]” as the key.

function hmacVerify(data: string, sig: string, secret: string | string[]): boolean

Source: src/lib/signing.ts

TTL in seconds for cached responses. Default: 86400 (24 hours).

function idempotent(opts?: IdempotencyOptions): MiddlewareHandler<AppEnv>

Source: src/framework/lib/idempotency.ts

Create a fresh, instance-scoped metrics state container.

function incrementCounter(state: MetricsState, name: string, labels: Labels, amount = 1,): void

Source: src/framework/metrics/registry.ts

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});
function index(fields: string[], opts?: { direction?: 'asc' | 'desc'; unique?: boolean },): IndexDef

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

function inspectPackage(pkg: SlingshotPackageDefinition): PackageInspection

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

A fixed-capacity in-memory LRU cache with per-entry TTL.

Entries are evicted in least-recently-used order when the cache reaches maxSize. Expired entries are lazily removed on read (no background timer). Used internally by createTenantMiddleware to cache resolved TenantConfig objects and avoid redundant onResolve calls.

function invalidateTenantCache(cache: TenantResolutionCache | null | undefined, tenantId: string,): void

Source: src/framework/middleware/tenant.ts

True when the user holds at least one live socket in the room.

Presence is the only reliable liveness signal for a room member: the engine’s per-player connected flag exists only while a game runtime is active, so a lobby (no runtime yet) has no other way to tell whether the host is still there. Apps use this to detect an absent host and offer recovery.

function isUserPresent(state: WsState, endpoint: string, room: string, userId: string,): boolean

Source: src/framework/ws/presence.ts

Default RuntimeFs implementation using Bun’s native file APIs.

function localStorage(config: LocalStorageConfig): StorageAdapter

Source: src/framework/adapters/localStorage.ts

Verbose-mode console logger.

Writes to console.log when LOGGING_VERBOSE=true or when NODE_ENV is not 'production'. Silenced in production by default.

function log(...args: unknown[]): void

Source: src/framework/lib/logger.ts

Writable plugin state used internally by the framework bootstrap lifecycle.

function maybeEntityAdapter<TAdapter extends object = object>(input: PluginStateMap | PluginStateCarrier | object | null | undefined, lookup: EntityAdapterLookupInput<TAdapter>,): TAdapter | null

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

Default values for cursor-based pagination query parameters.

function maybeSignCursor(cursor: string | null, signing?: { config: SigningConfig | null; secret: string | string[] | null },): string | null

Source: src/framework/lib/pagination.ts

Create a StorageAdapter that stores files in-process in a Map.

Remarks: Ephemeral, in-process only — all stored data lives in heap memory and is discarded when the process exits or the adapter is garbage-collected. This adapter is suitable for development, testing, and single-process environments only. It must not be used in production multi-process deployments because files stored in one process are invisible to other processes.

Remarks: Entries are evicted via LRU when the store reaches DEFAULT_MAX_ENTRIES (imported from slingshot-core) to prevent unbounded memory growth.

function memoryStorage(): StorageAdapter

Source: src/framework/adapters/memoryStorage.ts

Instance-owned metrics registry.

function metricsCollector(options: MetricsMiddlewareOptions): MiddlewareHandler<AppEnv>

Source: src/framework/middleware/metrics.ts

Create a fresh, instance-scoped metrics state container.

function observeHistogram(state: MetricsState, name: string, labels: Labels, value: number, buckets: number[] = DEFAULT_BUCKETS,): void

Source: src/framework/metrics/registry.ts

Default overrides for offset-based pagination query parameters. All fields are optional — omitted fields fall back to framework defaults (limit=50, offset=0, maxLimit=200).

function offsetParams(defaults?: OffsetParamDefaults): void

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

op.*() builder API for operation definitions.

Each builder adds the kind discriminant and returns a typed config object. Pure functions — no side effects, no validation (that’s defineOperations’ job).

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

Default overrides for offset-based pagination query parameters. All fields are optional — omitted fields fall back to framework defaults (limit=50, offset=0, maxLimit=200).

function paginatedResponse<T extends ZodType>(itemSchema: T, name: string): void

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

Default values for cursor-based pagination query parameters.

function parseCursorParams(raw: { limit?: string; cursor?: string }, defaults?: CursorParamDefaults, signing?: { config: SigningConfig | null; secret: string | string[] | null },): ParsedCursorParams &

Source: src/framework/lib/pagination.ts

Default overrides for offset-based pagination query parameters. All fields are optional — omitted fields fall back to framework defaults (limit=50, offset=0, maxLimit=200).

function parseOffsetParams(raw: { limit?: string; offset?: string }, defaults?: OffsetParamDefaults,): ParsedOffsetParams

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

Options for configuring file upload parsing and storage.

async function parseUpload(c: Context<AppEnv>, opts?: UploadOpts,): Promise<UploadResult[]>

Source: src/framework/upload/upload.ts

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

function provideCapability<TValue>(capability: PackageCapabilityHandle<TValue>, resolve: PublishedPackageCapability<TValue>['resolve'],): PublishedPackageCapability<TValue>

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

Safe default room-subscribe authorization, applied when an endpoint provides no explicit onRoomSubscribe guard.

Raw {action:'subscribe', room} messages are handled before per-event auth, so without a guard any socket — including an anonymous one — could subscribe to another user’s private room (e.g. sessions:<sid>:player:<victimUserId>) and receive their private state. This default denies subscription to any per-user room (a room containing a :player:<userId> segment) unless the socket’s authenticated actor IS that user. The target userId is embedded in the room name, so this self-authorization needs no external session lookup.

All other rooms remain allowed by default so existing consumers and legitimate session-room subscriptions keep working. Endpoints that need stricter or namespace-specific rules set their own onRoomSubscribe, which fully replaces this default.

function publish(state: WsState, endpoint: string, room: string, data: unknown, options?: PublishOptions,): void

Source: src/framework/ws/rooms.ts

Also rate-limit by HTTP fingerprint in addition to IP. Default: false

function rateLimit({ windowMs, max, fingerprintLimit = false, }: RateLimitOptions): MiddlewareHandler<AppEnv>

Source: src/framework/middleware/rateLimit.ts

Create a fresh, instance-scoped metrics state container.

function registerGaugeCallback(state: MetricsState, name: string, cb: GaugeCallback): void

Source: src/framework/metrics/registry.ts

The schema parameter type expected by zodOpenAPIRegistry.add().

function registerSchema<T extends ZodType>(name: string, schema: T): T

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

The schema parameter type expected by zodOpenAPIRegistry.add().

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

Store a new upload record. Keyed by the storage key.

async function registerUpload(record: UploadRecord, app: object): Promise<void>

Source: src/framework/upload/registry.ts

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Hono middleware that generates and propagates a unique request identifier.

A fresh UUID v4 is generated server-side on every request. Client-supplied X-Request-Id headers are intentionally ignored — accepting client-provided values would allow audit log spoofing and idempotency key manipulation.

The ID is:

  • Set on the Hono context via c.set('requestId', id) so it is accessible in all subsequent middleware and route handlers.
  • Written to the X-Request-Id response header after the handler chain completes, allowing clients to correlate requests with server-side logs.

Source: src/framework/middleware/requestId.ts

Severity level for a structured request log entry.

  • "info" — successful responses (status < 400).
  • "warn" — client-error responses (status 400–499).
  • "error" — server-error responses (status >= 500) or unhandled exceptions.
function requestLogger(options: RequestLoggerOptions = {}): MiddlewareHandler<AppEnv>

Source: src/framework/middleware/requestLogger.ts

Middleware factory that verifies a CAPTCHA token from the request body.

When no config is provided, falls back to the captcha configuration from the app’s SlingshotContext. If neither is available, the middleware is a no-op (passes through to the next handler).

function requireCaptcha(config?: CaptchaConfig): MiddlewareHandler<AppEnv>

Source: src/framework/middleware/captcha.ts

Allowed age of the timestamp in milliseconds. Default: 300_000 (5 min).

function requireSignedRequest(opts?: RequestSigningOptions): MiddlewareHandler<AppEnv>

Source: src/framework/middleware/requestSigning.ts

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

function s3Storage(config: S3StorageConfig): StorageAdapter

Source: src/framework/adapters/s3Storage.ts

Narrow, untyped view of the event bus for string-keyed subscriptions.

Plugins that subscribe to dynamically named events (e.g. entity:${storageName}.created) cast the typed SlingshotEventBus to this interface rather than widening the global SlingshotEventMap. This keeps type widening local per rule 12.

Defined here once so every consumer imports the same shape (rule 6). Use Pick to narrow further when a module only needs emit or only needs on/off.

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

Create a fresh, instance-scoped metrics state container.

async function serializeMetrics(state: MetricsState): Promise<string>

Source: src/framework/metrics/registry.ts

Constant-time string comparison to prevent timing attacks on secret verification.

Uses Node.js’s native crypto.timingSafeEqual so that the comparison time is independent of how many characters match. When the strings differ in length, a same-buffer compare is performed to burn equivalent time before returning false.

function sha256(input: string): string

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

Sign data with the active key (first element of secret). Normalizes string | string[] so that an array is never passed directly to createHmac() — which would silently call .toString() and produce “[object Array]” as the key.

function signCookieValue(value: string, secret: string | string[]): string

Source: src/lib/signing.ts

Sign data with the active key (first element of secret). Normalizes string | string[] so that an array is never passed directly to createHmac() — which would silently call .toString() and produce “[object Array]” as the key.

function signCursor(payload: string, secret: string | string[]): string

Source: src/lib/signing.ts

Constant-time string comparison to prevent timing attacks on secret verification.

Uses Node.js’s native crypto.timingSafeEqual so that the comparison time is independent of how many characters match. When the strings differ in length, a same-buffer compare is performed to burn equivalent time before returning false.

function timingSafeEqual(a: string, b: string): boolean

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

Shared field-mapping utilities for config-driven adapters.

  • camelCase ↔ snake_case conversion
  • SQL / Mongo type mapping
  • Record transformation (domain ↔ storage)
  • Auto-default resolution
  • Naming conventions per backend
function toCamelCase(str: string): string

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

Shared field-mapping utilities for config-driven adapters.

  • camelCase ↔ snake_case conversion
  • SQL / Mongo type mapping
  • Record transformation (domain ↔ storage)
  • Auto-default resolution
  • Naming conventions per backend
function toSnakeCase(str: string): string

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

Parse and validate the JSON body of a Request against a Zod schema.

async function validate<T extends z.ZodType>(schema: T, req: Request,): Promise<z.output<T>>

Source: src/framework/lib/validate.ts

Sign data with the active key (first element of secret). Normalizes string | string[] so that an array is never passed directly to createHmac() — which would silently call .toString() and produce “[object Array]” as the key.

function verifyCookieValue(signed: string, secret: string | string[]): string | null

Source: src/lib/signing.ts

Sign data with the active key (first element of secret). Normalizes string | string[] so that an array is never passed directly to createHmac() — which would silently call .toString() and produce “[object Array]” as the key.

function verifyCursor(cursor: string, secret: string | string[]): string | null

Source: src/lib/signing.ts

Sign data with the active key (first element of secret). Normalizes string | string[] so that an array is never passed directly to createHmac() — which would silently call .toString() and produce “[object Array]” as the key.

function verifyPresignedUrl(url: string, method: string, secret: string | string[],): void

Source: src/lib/signing.ts

Header name containing the Unix timestamp (seconds or ms).

function webhookAuth(options: WebhookAuthOptions): MiddlewareHandler<AppEnv>

Source: src/framework/middleware/webhookAuth.ts

The schema parameter type expected by zodOpenAPIRegistry.add().

function withSecurity<T extends RouteConfig>(route: T, ...schemes: Array<Record<string, string[]>>): T

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

Produces a collision-safe composite key for scoping rooms to an endpoint.

Both endpoint and room are percent-encoded before joining with :. Since encodeURIComponent encodes :%3A, the literal : separator can only come from this function — not from endpoint or room values.

Examples: wsEndpointKey(“/chat”, “general”) → “%2Fchat:general” wsEndpointKey(“/chat”, “room:1”) → “%2Fchat:room%3A1” wsEndpointKey(“/a:b”, “c”) → “%2Fa%3Ab:c” wsEndpointKey(“/notifications”, “x”) → “%2Fnotifications:x”

Used for: in-memory room maps, Redis channel names, Redis message keys. NOT used for: SQLite or MongoDB schemas (those store endpoint + room separately).

function wsEndpointKey(endpoint: string, room: string): string

Source: src/framework/ws/namespace.ts

Lazily access the Mongoose Schema class (avoids top-level require of mongoose)

function zodToMongoose(zodSchema: ZodObjectLike, config: ZodToMongooseConfig = {},): Record<string, unknown>

Source: src/framework/lib/zodToMongoose.ts

Cookie name for the session access token (HttpOnly, short-lived). Used by the auth plugin to set and read the primary session credential.

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

Cookie name for the session access token (HttpOnly, short-lived). Used by the auth plugin to set and read the primary session credential.

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

Cookie name for the session access token (HttpOnly, short-lived). Used by the auth plugin to set and read the primary session credential.

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

Cookie name for the session access token (HttpOnly, short-lived). Used by the auth plugin to set and read the primary session credential.

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

Cookie name for the session access token (HttpOnly, short-lived). Used by the auth plugin to set and read the primary session credential.

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

Cookie name for the session access token (HttpOnly, short-lived). Used by the auth plugin to set and read the primary session credential.

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

Cookie name for the session access token (HttpOnly, short-lived). Used by the auth plugin to set and read the primary session credential.

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

Cookie name for the session access token (HttpOnly, short-lived). Used by the auth plugin to set and read the primary session credential.

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

Cookie name for the session access token (HttpOnly, short-lived). Used by the auth plugin to set and read the primary session credential.

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

Cookie name for the session access token (HttpOnly, short-lived). Used by the auth plugin to set and read the primary session credential.

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

Base error class for all Slingshot errors.

Carries a machine-readable code string for programmatic discrimination and an optional cause for error chaining. Feature packages should extend this class (or one of the HTTP-aware subclasses) rather than throwing generic Error instances.

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

Pluggable transport for cross-instance WebSocket message delivery.

publish() is called on every room broadcast — the transport fans out the message to other server instances (e.g. via Redis pub/sub).

connect() is called once at server startup. The onMessage callback should be invoked when a message arrives from another instance — it will be delivered to local sockets via Bun’s native server.publish().

disconnect() is called on graceful shutdown.

Source: src/framework/ws/transport.ts

Source: src/prodReadiness.ts

Base error class for all Slingshot errors.

Carries a machine-readable code string for programmatic discrimination and an optional cause for error chaining. Feature packages should extend this class (or one of the HTTP-aware subclasses) rather than throwing generic Error instances.

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

Source: src/config/types/meta.ts

A single audit log entry recording an HTTP request or admin action.

Stored by AuditLogProvider.logEntry() and queryable via AuditLogProvider.getLogs(). The audit middleware creates entries automatically for authenticated requests.

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

Source: src/framework/middleware/auditLog.ts

Configuration for creating an audit log provider.

Source: src/framework/auditLog/index.ts

Configuration for creating an audit log provider.

Source: src/framework/auditLog/index.ts

Database/store connection configuration for slingshot-auth.

Controls which persistence backends are used for sessions, OAuth state, and the user auth adapter. Each field is optional — defaults are chosen by the bootstrap layer based on what is available (Redis → SQLite → memory).

Remarks: When running under the full framework (createApp / createServer), connection objects are provided automatically via SlingshotFrameworkConfig. These fields are only relevant in standalone mode or when explicitly overriding framework-provided connections.

Source: packages/slingshot-auth/src/types/config.ts

Database/store connection configuration for slingshot-auth.

Controls which persistence backends are used for sessions, OAuth state, and the user auth adapter. Each field is optional — defaults are chosen by the bootstrap layer based on what is available (Redis → SQLite → memory).

Remarks: When running under the full framework (createApp / createServer), connection objects are provided automatically via SlingshotFrameworkConfig. These fields are only relevant in standalone mode or when explicitly overriding framework-provided connections.

Source: packages/slingshot-auth/src/types/config.ts

Source: src/config/types/security.ts

Source: src/framework/middleware/botProtection.ts

Supported CAPTCHA verification providers.

  • 'recaptcha' — Google reCAPTCHA v2 or v3
  • 'hcaptcha' — hCaptcha
  • 'turnstile' — Cloudflare Turnstile

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

Source: src/app.ts

Absolute path to the service’s routes directory (use import.meta.dir + “/routes”). Optional — omit when all routes are registered via plugins.

Source: src/app.ts

Source: src/server.ts

Read-only representation of a tenant record.

Source: src/framework/tenancy/service.ts

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Default values for cursor-based pagination query parameters.

Source: src/framework/lib/pagination.ts

Default values for cursor-based pagination query parameters.

Source: src/framework/lib/pagination.ts

Source: src/config/types/db.ts

A single field-level validation error detail produced by the default formatter.

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Zod schema generation from entity config.

Derives four Zod schemas from a ResolvedEntityConfig at runtime, without any code generation step. Schemas are consumed by route handlers for request validation and by the REST API generator for OpenAPI type inference.

Generated schemas:

  1. entitySchema — Full record shape; optional fields become .optional().
  2. createSchema — Create input; excludes auto-default fields (uuid, now, cuid) and onUpdate fields. Fields with literal defaults or marked optional are optional in this schema.
  3. updateSchema — Partial update input; all included fields are .optional(). Excludes immutable fields and onUpdate fields.
  4. listOptionsSchema — Filter + pagination options for list endpoints. Includes enum/boolean fields, all indexed fields, the tenant field, and limit/cursor/sortDir.

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

Per-endpoint WebSocket heartbeat configuration.

The server sends periodic pings; sockets that fail to respond with a pong within the timeout window are closed automatically.

Source: src/framework/ws/heartbeat.ts

HookServices — typed accessors for out-of-request callbacks.

Auth lifecycle hooks, orchestration workflow hooks, and queue dead-letter callbacks fire outside any Hono request context — there is no c to read state from. Without a canonical contract these callbacks ended up starved differently in each plugin (some got pluginState, others got only request metadata, others nothing at all).

HookServices is the shared shape every plugin’s out-of-request callbacks intersect into their payload. Hook authors get the same typed accessors that PackageDomainRouteContext exposes to package-authored route handlers, so the ergonomics line up across request and non-request callsites:

postLogin: async ({ userId, services }) => {
const adapter = services.entities.get(GuestIdentity); // typed via entity module
const mailer = services.capabilities.require(MailerCapability);
await services.bus.emit('user.signed-in', { userId });
}

Plugins build a HookServices instance by calling buildHookServices() at the call site of each hook, threading their own pluginState/bus/logger and the app’s Hono reference. The framework supplies the shared SlingshotContext.capabilityProviders map so capability lookups work identically to those inside request handlers.

Worker-process boundary. Some adapters (notably the Temporal orchestration worker) run hook code in a separate process where the framework’s app is not reachable. Those callsites pass services: undefined rather than fabricate broken accessors. Hook authors who need framework state from worker-mode code should restructure their work to run at the workflow level (in-process) and thread data into the task input.

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

Storage contract for idempotency key deduplication.

When a client retries a mutating request with the same Idempotency-Key header, the idempotency middleware looks up the cached response via this adapter and returns it instead of executing the handler again.

Remarks: Implementations must respect the ttlSeconds argument in set() — records should expire automatically after the configured TTL. The framework sets a default TTL of 24 hours for idempotency records.

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

Source: src/framework/lib/idempotency.ts

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Source: src/config/types/jobs.ts

Source: src/framework/adapters/localStorage.ts

Source: src/config/types/logging.ts

Source: src/config/types/metrics.ts

Source: src/framework/middleware/metrics.ts

Configuration for distributed tracing via OpenTelemetry.

Slingshot instruments bootstrap phases, request lifecycles, and plugin lifecycle calls with OTel spans. The framework depends on @opentelemetry/api only — install an OTel SDK and exporter in your app to collect spans.

When enabled is false or omitted, no tracer is created and no spans are recorded. The OTel API returns no-op implementations in that case, so there is zero runtime overhead.

Source: src/config/types/observability.ts

Default overrides for offset-based pagination query parameters. All fields are optional — omitted fields fall back to framework defaults (limit=50, offset=0, maxLimit=200).

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

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

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Default values for cursor-based pagination query parameters.

Source: src/framework/lib/pagination.ts

Default overrides for offset-based pagination query parameters. All fields are optional — omitted fields fall back to framework defaults (limit=50, offset=0, maxLimit=200).

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

Server-level permissions configuration.

When set, the framework bootstraps a shared PermissionsAdapter, PermissionRegistry, and PermissionEvaluator from the existing infra connection and writes them to ctx.pluginState at PERMISSIONS_STATE_KEY before any plugin setup phase runs.

Plugins that accept permissions in their own config remain backward compatible - an explicit plugin-level config takes precedence over the server-level bootstrap.

Requires @lastshotlabs/slingshot-permissions to be installed. A clear error is thrown at startup if the package is missing.

Source: src/config/types/permissions.ts

Source: src/config/types/upload.ts

Source: src/prodReadiness.ts

Source: src/prodReadiness.ts

Source: src/prodReadiness.ts

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

Source: src/framework/ws/rooms.ts

Source: src/framework/middleware/rateLimit.ts

Source: src/lib/redis.ts

Source: src/framework/ws/redisTransport.ts

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Severity level for a structured request log entry.

  • "info" — successful responses (status < 400).
  • "warn" — client-error responses (status 400–499).
  • "error" — server-error responses (status >= 500) or unhandled exceptions.

Source: src/framework/middleware/requestLogger.ts

Source: src/framework/middleware/requestLogger.ts

Source: src/framework/middleware/requestSigning.ts

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Operation configuration types — canonical definitions.

12 declarative operation patterns + 1 custom escape hatch. Both the codegen package (slingshot-data) and the runtime framework import from here. Single source of truth — never duplicate these types in consumer packages.

Remarks: Operation configs are consumed by: - slingshot-data code generators to produce typed adapter implementations - The runtime framework’s op.* executor registry for live evaluation - InferOperationMethods<Ops, Entity> for TypeScript adapter method typing

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

Resolved persistence repositories for the application instance.

Created by resolveFrameworkPersistence() during server bootstrap and wired into SlingshotContext by createApp(). All repositories are instance-scoped — no shared module-level state across app instances.

Remarks: Access these repositories via ctx.persistence.* in plugin setupPost hooks and in framework middleware. Never access them before createApp() completes.

Source: packages/slingshot-core/src/context/slingshotContext.ts

Configuration for the S3-compatible storage adapter.

Compatible with AWS S3, Cloudflare R2, MinIO, and any S3-compatible endpoint.

Source: src/framework/adapters/s3Storage.ts

Source: src/config/types/security.ts

Configuration for the Slingshot-integrated admin plugin.

Extends AdminPluginConfig with optional overrides for accessProvider, managedUserProvider, and permissions. When these are omitted, sensible framework defaults are resolved automatically:

  • accessProvider defaults to createSlingshotAuthAccessProvider, which reads permissions from the auth runtime context.
  • managedUserProvider defaults to createSlingshotManagedUserProvider, constructed from the auth runtime adapter, config, and session repository.
  • permissions defaults to the value stored under PERMISSIONS_STATE_KEY in ctx.pluginState — set by the community plugin or another permissions source during its setupRoutes phase.

All three are required at route-registration time. If permissions is not provided and no plugin has populated PERMISSIONS_STATE_KEY, setupPost throws.

Source: src/framework/admin/index.ts

Resolved persistence repositories for the application instance.

Created by resolveFrameworkPersistence() during server bootstrap and wired into SlingshotContext by createApp(). All repositories are instance-scoped — no shared module-level state across app instances.

Remarks: Access these repositories via ctx.persistence.* in plugin setupPost hooks and in framework middleware. Never access them before createApp() completes.

Source: packages/slingshot-core/src/context/slingshotContext.ts

Narrow, untyped view of the event bus for string-keyed subscriptions.

Plugins that subscribe to dynamically named events (e.g. entity:${storageName}.created) cast the typed SlingshotEventBus to this interface rather than widening the global SlingshotEventMap. This keeps type widening local per rule 12.

Defined here once so every consumer imports the same shape (rule 6). Use Pick to narrow further when a module only needs emit or only needs on/off.

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

Central event map for all built-in Slingshot events.

Typed key to payload pairs consumed by SlingshotEventBus. Plugin packages extend this map via TypeScript module augmentation in their own events.ts file, never by modifying this interface directly.

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

Context object passed to all plugin lifecycle methods.

Using an options object instead of positional parameters means adding a new field in the future is non-breaking — plugins that don’t need the new field simply don’t destructure it.

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

Resolved persistence repositories for the application instance.

Created by resolveFrameworkPersistence() during server bootstrap and wired into SlingshotContext by createApp(). All repositories are instance-scoped — no shared module-level state across app instances.

Remarks: Access these repositories via ctx.persistence.* in plugin setupPost hooks and in framework middleware. Never access them before createApp() completes.

Source: packages/slingshot-core/src/context/slingshotContext.ts

Source: src/config/types/sse.ts

Data attached to each active SSE connection.

The generic parameter T allows endpoint-specific metadata to be added at upgrade time (e.g., subscription filters). The base fields (id, actor, requestTenantId, endpoint) are always present.

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

Context object passed to all plugin lifecycle methods.

Using an options object instead of positional parameters means adding a new field in the future is non-breaking — plugins that don’t need the new field simply don’t destructure it.

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

Pluggable object storage adapter for the upload middleware.

Implement this interface to connect any storage backend (S3, R2, local disk, etc.) to the Slingshot upload infrastructure. Registered via the uploads plugin configuration.

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

Source: src/config/types/tenancy.ts

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Read-only representation of a tenant record.

Source: src/framework/tenancy/service.ts

Read-only representation of a tenant record.

Source: src/framework/tenancy/service.ts

Configuration for distributed tracing via OpenTelemetry.

Slingshot instruments bootstrap phases, request lifecycles, and plugin lifecycle calls with OTel spans. The framework depends on @opentelemetry/api only — install an OTel SDK and exporter in your app to collect spans.

When enabled is false or omitted, no tracer is created and no spans are recorded. The OTel API returns no-op implementations in that case, so there is zero runtime overhead.

Source: src/config/types/observability.ts

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

Source: src/config/types/upload.ts

Options for configuring file upload parsing and storage.

Source: src/framework/upload/upload.ts

Metadata record stored when a file is uploaded via the framework upload middleware.

Used to verify ownership and tenancy when users request presigned download URLs or initiate delete operations. Stored in the UploadRegistryRepository.

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

Metadata record stored when a file is uploaded via the framework upload middleware.

Used to verify ownership and tenancy when users request presigned download URLs or initiate delete operations. Stored in the UploadRegistryRepository.

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

Pluggable object storage adapter for the upload middleware.

Implement this interface to connect any storage backend (S3, R2, local disk, etc.) to the Slingshot upload infrastructure. Registered via the uploads plugin configuration.

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

Source: src/config/types/validation.ts

A single field-level validation error detail produced by the default formatter.

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

Source: src/config/types/versioning.ts

Source: src/framework/middleware/webhookAuth.ts

Source: src/framework/middleware/webhookAuth.ts

Source: src/config/types/ws.ts

Authentication level required for a WebSocket event or endpoint.

  • 'userAuth' — requires an authenticated user session.
  • 'bearer' — requires a valid bearer token.
  • 'none' — no authentication required.

Source: src/config/types/ws.ts

Source: src/config/types/ws.ts

A single WebSocket message that has been persisted to the backing store. Includes a unique ID and a creation timestamp for history and cursor pagination.

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

Resolved persistence repositories for the application instance.

Created by resolveFrameworkPersistence() during server bootstrap and wired into SlingshotContext by createApp(). All repositories are instance-scoped — no shared module-level state across app instances.

Remarks: Access these repositories via ctx.persistence.* in plugin setupPost hooks and in framework middleware. Never access them before createApp() completes.

Source: packages/slingshot-core/src/context/slingshotContext.ts

Pluggable transport for cross-instance WebSocket message delivery.

publish() is called on every room broadcast — the transport fans out the message to other server instances (e.g. via Redis pub/sub).

connect() is called once at server startup. The onMessage callback should be invoked when a message arrives from another instance — it will be delivered to local sockets via Bun’s native server.publish().

disconnect() is called on graceful shutdown.

Source: src/framework/ws/transport.ts

Resolved persistence repositories for the application instance.

Created by resolveFrameworkPersistence() during server bootstrap and wired into SlingshotContext by createApp(). All repositories are instance-scoped — no shared module-level state across app instances.

Remarks: Access these repositories via ctx.persistence.* in plugin setupPost hooks and in framework middleware. Never access them before createApp() completes.

Source: packages/slingshot-core/src/context/slingshotContext.ts

Source: src/app.ts

Canonical declarative app config shape.

Alias of CreateServerConfig surfaced under a friendlier name. Users author this in app.config.ts and the framework boots from the default export.

Source: src/defineApp.ts

A single field-level validation error detail produced by the default formatter.

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

A single field-level validation error detail produced by the default formatter.

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

Source: src/app.ts

Database/store connection configuration for slingshot-auth.

Controls which persistence backends are used for sessions, OAuth state, and the user auth adapter. Each field is optional — defaults are chosen by the bootstrap layer based on what is available (Redis → SQLite → memory).

Remarks: When running under the full framework (createApp / createServer), connection objects are provided automatically via SlingshotFrameworkConfig. These fields are only relevant in standalone mode or when explicitly overriding framework-provided connections.

Source: packages/slingshot-auth/src/types/config.ts

Source: src/app.ts

Source: src/app.ts

Supported CAPTCHA verification providers.

  • 'recaptcha' — Google reCAPTCHA v2 or v3
  • 'hcaptcha' — hCaptcha
  • 'turnstile' — Cloudflare Turnstile

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

Source: src/app.ts

Source: src/framework/lib/createDtoMapper.ts

Source: src/app.ts

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

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

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

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Source: ./app

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Source: src/app.ts

Severity level for a structured request log entry.

  • "info" — successful responses (status < 400).
  • "warn" — client-error responses (status 400–499).
  • "error" — server-error responses (status >= 500) or unhandled exceptions.

Source: src/framework/middleware/requestLogger.ts

Source: src/app.ts

Source: src/app.ts

Source: src/app.ts

Source: src/app.ts

Source: ./app

Source: src/lib/mongo.ts

Source: src/app.ts

Source: src/app.ts

Operation configuration types — canonical definitions.

12 declarative operation patterns + 1 custom escape hatch. Both the codegen package (slingshot-data) and the runtime framework import from here. Single source of truth — never duplicate these types in consumer packages.

Remarks: Operation configs are consumed by: - slingshot-data code generators to produce typed adapter implementations - The runtime framework’s op.* executor registry for live evaluation - InferOperationMethods<Ops, Entity> for TypeScript adapter method typing

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

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

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

Source: src/app.ts

Source: src/app.ts

Source: src/prodReadiness.ts

Source: src/prodReadiness.ts

Source: src/prodReadiness.ts

Source: src/app.ts

Source: src/app.ts

Source: src/app.ts

Source: src/config/types/secrets.ts

Central event map for all built-in Slingshot events.

Typed key to payload pairs consumed by SlingshotEventBus. Plugin packages extend this map via TypeScript module augmentation in their own events.ts file, never by modifying this interface directly.

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

Source: src/app.ts

Per-socket data attached to every WebSocket connection by the Bun server.

This type is the data object passed to all ws.* handler callbacks (open, message, close, drain). The generic T parameter lets plugins attach custom fields (e.g., roomId) at upgrade time. The base fields are populated by createWsUpgradeHandler.

Source: src/framework/ws/index.ts

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Source: src/framework/sse/index.ts

Source: src/framework/sse/index.ts

Source: src/app.ts

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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

Options for the handleUpload middleware.

All fields are optional overrides of the app-level upload configuration stored in SlingshotContext. Values provided here take precedence over the app-level defaults for a specific route.

See UploadOpts (from @framework/upload/upload) for available fields: maxFileSize, maxFiles, allowedMimeTypes, keyPrefix, etc.

Source: src/framework/middleware/upload.ts

A single field-level validation error detail produced by the default formatter.

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

Authentication level required for a WebSocket event or endpoint.

  • 'userAuth' — requires an authenticated user session.
  • 'bearer' — requires a valid bearer token.
  • 'none' — no authentication required.

Source: src/config/types/ws.ts

Authentication level required for a WebSocket event or endpoint.

  • 'userAuth' — requires an authenticated user session.
  • 'bearer' — requires a valid bearer token.
  • 'none' — no authentication required.

Source: src/config/types/ws.ts

Authentication level required for a WebSocket event or endpoint.

  • 'userAuth' — requires an authenticated user session.
  • 'bearer' — requires a valid bearer token.
  • 'none' — no authentication required.

Source: src/config/types/ws.ts

Source: src/framework/lib/zodToMongoose.ts

Source: src/framework/lib/zodToMongoose.ts

Source: ./app

Multi-entity composition — combine multiple entity adapters into a single plugin adapter, optionally with operations.

Plugins typically persist several related entities together (e.g. rooms + messages). Rather than returning multiple separate factory objects, createCompositeFactories() merges them behind a single RepoFactories<CompositeAdapter> object. The composite adapter exposes each entity’s adapter under its own key, plus a clear() method that resets all entities simultaneously (useful in tests).

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

DB field name -> API field name for ObjectId refs (e.g., { account: “accountId” })

Source: src/framework/lib/createDtoMapper.ts

Config-driven adapter factory generator.

Pure runtime builder for entity repo factories. This package-side version intentionally stays free of app/framework wiring so workspace packages can depend on it without pulling in src/framework.

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

Config-driven entity & repository generation.

Plugin authors describe an entity’s shape declaratively using the field.*() builder API, then get generated TypeScript types, Zod schemas, adapter interfaces, and backend-specific implementations from a single source of truth.

import { defineEntity, field, index } from '@lastshotlabs/slingshot-core';
export const Message = defineEntity('Message', {
namespace: 'chat',
fields: {
id: field.string({ primary: true, default: 'uuid' }),
roomId: field.string(),
authorId: field.string(),
content: field.string(),
type: field.enum(['text', 'image', 'system'], { default: 'text' }),
metadata: field.json({ optional: true }),
createdAt: field.date({ default: 'now' }),
updatedAt: field.date({ default: 'now', onUpdate: 'now' }),
},
softDelete: { field: 'status', value: 'deleted' },
defaultSort: { field: 'createdAt', direction: 'desc' },
pagination: { cursor: { fields: ['createdAt', 'id'] }, defaultLimit: 50, maxLimit: 200 },
indexes: [
index(['roomId', 'createdAt'], { direction: 'desc' }),
index(['authorId', 'createdAt'], { direction: 'desc' }),
],
});

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

Operations definition entry point — dev-time only.

Validates operation configs using Zod schema against entity field names.

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

Typed, named token a package publishes and consumers resolve to exchange a capability value across packages.

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