Skip to content

@lastshotlabs/slingshot-core

npm install @lastshotlabs/slingshot-core

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

function applyPublicEntityExposure<TValue>(adapter: TValue, exposure: PublicEntityExposureMetadata | undefined, context: { readonly entity: string; readonly contract?: string; readonly source?: string },): TValue

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

Build the standard disableRoutes Zod field for a plugin config schema.

Produces a z.array(z.enum([...values])) schema that validates the disableRoutes array in a plugin config. Pass Object.values(MY_ROUTES) as the allowed values.

function assertMountPath(pluginName: string, mountPath: string): void

Source: packages/slingshot-core/src/configValidation.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 attachContext(app: object, ctx: SlingshotContext): void

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

Whether the Postgres runtime applies migrations on startup or assumes the schema is already migrated.

function attachPostgresPoolRuntime(pool: object, runtime: PostgresPoolRuntime): void

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

Stable plugin-state key published by slingshot-auth.

Packages that need only a narrow auth-facing peer contract should depend on this key and the accessors below instead of spelunking for raw string keys.

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

High-level event API exposed on the Slingshot context.

Wraps an EventDefinitionRegistry and a SlingshotEventBus to provide validated, envelope-wrapped event publishing with scope projection.

function authorizeEventSubscriber<K extends EventKey>(definition: EventDefinition<K>, principal: EventSubscriptionPrincipal, envelope: EventEnvelope<K>,): boolean

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

Swallow errors from a promise that must never block the main flow.

Use for fire-and-forget side effects (metadata updates, notifications, cleanup) where failure is acceptable but should be visible in logs.

function bestEffort(promise: Promise<unknown>, label?: string, logger?: Logger): void

Source: packages/slingshot-core/src/bestEffort.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.

function buildHookServices(args: { /** * The Hono application instance. Typed as `object` to avoid pinning a Hono * version in `slingshot-core`. Used to resolve `getContext(app).capabilityProviders` * for capability lookups and `requireEntityAdapter(app, ...)` for entity lookups. */ app: object; /** Live plugin-state map for the app instance. */ pluginState: PluginStateMap; /** Instance-scoped event bus to expose on `services.bus`. */ bus: SlingshotEventBus; /** Plugin-scoped logger to expose on `services.logger`. */ logger: Logger; /** * Default plugin name used as the `plugin:` qualifier when a hook author calls * `services.entities.get(entityModule)` without specifying a plugin. Should be * the firing plugin's own name (e.g. `'slingshot-auth'`) so hooks can read their * own entities by module reference without redundant `{ plugin: '...' }` qualifiers. */ pluginName: string; }): HookServices

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

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

function capabilityProviderKey(handle: PackageCapabilityHandle<unknown>): string

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

Structured logger contract used by prod-track packages.

The interface is deliberately small: four severity methods plus child() for fields that should appear on every log line in a sub-component (request id, plugin name, queue id, etc.). Implementations must never throw — log sinks that crash the caller are worse than silent loss.

createConsoleLogger is the default — one JSON line per call written to the underlying console method matching the level. noopLogger is the test default for code paths that need a logger handle but no output.

function createConsoleLogger(opts?: { level?: LogLevel; base?: LogFields }): Logger

Source: packages/slingshot-core/src/observability/logger.ts

Mutable bootstrap registrar that collects auth-boundary dependencies (route auth, cache adapters, email templates, …) from plugins, then drains them into a frozen CoreRegistrarSnapshot.

function createCoreRegistrar(): void

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

Creates a default FingerprintBuilder that produces a 6-byte SHA-256 hash of stable browser headers (User-Agent, Accept-Language, Accept-Encoding).

Used as a lightweight bot/request fingerprint when no authenticated user is present. The hash is a 12-character hex string derived from the first 6 bytes of the digest.

Remarks: Headers can be spoofed — this fingerprint is a soft signal, not a security guarantee. The auth plugin may replace this with a richer implementation (e.g., one that incorporates IP, TLS fingerprint, or cookie entropy).

function createDefaultFingerprintBuilder(): FingerprintBuilder

Source: packages/slingshot-core/src/defaults/defaultFingerprint.ts

Actor-based identity abstraction.

Decouples all framework consumers (guards, permissions, data scoping, audit, entity routes) from specific auth-provider field names by mapping them into the canonical Actor shape.

Custom auth integrations implement IdentityResolver to map their own identity representation into the canonical Actor shape.

function createDefaultIdentityResolver(): IdentityResolver

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

Validate an event definition, throwing if ownerPlugin is empty, no exposure is declared, exposures are duplicated, or internal is mixed with external exposures.

function createDefaultSubscriberAuthorizer<K extends EventKey>(definition: Pick<EventDefinition<K>, 'exposure'>,): EventSubscriberAuthorizer<K>

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

General-purpose entity registry.

Plugins that need to discover entities at runtime (search indexing, schema generation, admin UIs, migration tooling, documentation) use this registry.

Created by the framework during bootstrap, exposed to plugin lifecycle hooks on SlingshotFrameworkConfig, and consumed by framework-owned services such as search indexing.

function createEntityRegistry(): EntityRegistry

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

Registry of EventDefinitions keyed by event name, freezable once all plugins have registered their events.

function createEventDefinitionRegistry(options: EventDefinitionRegistryOptions = {},): EventDefinitionRegistry

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

Create a deep-frozen EventEnvelope from the given parameters.

Generates a unique eventId (UUID v4) and an ISO-8601 occurredAt timestamp. The returned envelope and all nested objects are recursively frozen to prevent downstream mutation.

function createEventEnvelope<K extends EventKey>(params: CreateEventEnvelopeParams<K>,): EventEnvelope<K>

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

High-level event API exposed on the Slingshot context.

Wraps an EventDefinitionRegistry and a SlingshotEventBus to provide validated, envelope-wrapped event publishing with scope projection.

function createEventPublisher(options: CreateEventPublisherOptions): SlingshotEvents

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

Result of validating an event payload against a registered schema.

function createEventSchemaRegistry(): EventSchemaRegistry

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

Evict the oldest entries from a Map when it exceeds maxEntries.

JavaScript Map iterates in insertion order, so the entries deleted from the front are always the oldest. Useful for capping memory store size in development and single-process deployments.

function createEvictExpired(intervalMs = EVICTION_INTERVAL_MS,): <K, V extends

Source: packages/slingshot-core/src/memoryEviction.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

Unified metrics emitter contract.

Thin, dependency-free interface that prod-track packages call to record counters, gauges, and timings without coupling to a specific backend (Prometheus, OpenTelemetry, etc). Defaults to a no-op so packages can emit unconditionally and let the host application decide whether and where to collect.

This is a separate seam from the framework-owned MetricsState (Prometheus-style registry used by the built-in /metrics HTTP endpoint). Plugins should prefer this MetricsEmitter for ad-hoc operational signals; the framework metrics plugin remains the home for the request-level counters/histograms exposed at the scrape endpoint.

function createInProcessMetricsEmitter(logger?: Logger): InProcessMetricsEmitter

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

Creates an in-memory CacheAdapter backed by a Map with TTL support.

Supports get, set (with optional TTL in seconds), del, and glob delPattern. Entries expire on get (point-in-time check) and are periodically swept by evictExpired. Store size is capped at DEFAULT_MAX_ENTRIES.

Remarks: This adapter is not distributed. For production multi-instance deployments, use a Redis-backed cache adapter registered via the auth or cache plugin.

function createMemoryCacheAdapter(): CacheAdapter

Source: packages/slingshot-core/src/defaults/memoryCacheAdapter.ts

Operation-level idempotency contract used by delivery and retry-aware packages (mail, push, notifications, orchestration) to dedup retried operations.

Remarks: This is distinct from the HTTP IdempotencyAdapter contract in ../idempotencyAdapter.ts, which is dedicated to the request-replay middleware (caching the full HTTP response for a given Idempotency-Key header).

Remarks: The contract here is keyed on a structured IdempotencyKey and stores an arbitrary payload (typed at the call site). Callers wrap their operation in withIdempotency so that retries skip work and replay the prior result.

function createMemoryOperationIdempotencyAdapter(opts?: { defaultTtlMs?: number; maxEntries?: number; }): IdempotencyAdapter

Source: packages/slingshot-core/src/idempotency/index.ts

Creates an in-memory rate limit adapter backed by a Map.

Tracks request counts per key in a rolling time window. The window resets when the current time exceeds resetAt — there is no sliding window; each key gets a fixed-duration bucket that resets on the first request after expiry.

Remarks: Not suitable for multi-instance or distributed deployments. Rate limit counts are held entirely in the process heap — no synchronisation with other server instances occurs. In a horizontally-scaled deployment each instance enforces its own independent limit, so the effective per-user limit becomes max × instanceCount. For production deployments with more than one server process, replace this adapter with a Redis-backed implementation via ctx.registrar.setRateLimitAdapter(...) in the auth plugin.

Remarks: Map eviction strategy: On every trackAttempt, expired entries (those whose resetAt has passed) are swept first so they do not consume capacity. If the store is still over DEFAULT_MAX_ENTRIES after sweeping, the oldest entries by insertion order are evicted. This bounds memory use while protecting valid entries from being evicted by stale or attacker-generated keys.

Remarks: Production warning: this adapter is registered as the framework default so the server starts without requiring an auth plugin. Replace it in any deployment that expects non-trivial traffic or has security requirements around rate limiting.

function createMemoryRateLimitAdapter(): RateLimitAdapter

Source: packages/slingshot-core/src/defaults/memoryRateLimit.ts

Unified metrics emitter contract.

Thin, dependency-free interface that prod-track packages call to record counters, gauges, and timings without coupling to a specific backend (Prometheus, OpenTelemetry, etc). Defaults to a no-op so packages can emit unconditionally and let the host application decide whether and where to collect.

This is a separate seam from the framework-owned MetricsState (Prometheus-style registry used by the built-in /metrics HTTP endpoint). Plugins should prefer this MetricsEmitter for ad-hoc operational signals; the framework metrics plugin remains the home for the request-level counters/histograms exposed at the scrape endpoint.

function createNoopMetricsEmitter(): MetricsEmitter

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

Writable plugin state used internally by the framework bootstrap lifecycle.

function createPluginStateMap(entries?: Iterable<readonly [string, unknown]>,): PluginStateMap

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

Whether the Postgres runtime applies migrations on startup or assumes the schema is already migrated.

function createPostgresPoolRuntime(opts?: { migrationMode?: PostgresMigrationMode; healthcheckTimeoutMs?: number; }): PostgresPoolRuntime

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

Create a deep-frozen EventEnvelope from the given parameters.

Generates a unique eventId (UUID v4) and an ISO-8601 occurredAt timestamp. The returned envelope and all nested objects are recursively frozen to prevent downstream mutation.

function createRawEventEnvelope<K extends EventKey>(key: K, payload: SlingshotEventMap[K],): EventEnvelope<K>

Source: packages/slingshot-core/src/eventEnvelope.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

Configuration for createRouterAdapter.

A default bus handles all events not matched by a namespace prefix. Namespace prefixes allow routing specific event families to dedicated buses (e.g., community events to a Redis-backed adapter while security events stay in-process).

function createRouterAdapter(opts: RouterAdapterOptions): SlingshotEventBus

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

Minimal local type for the subset of the undici Dispatcher we use. We keep this local so callers do not need to import undici types directly.

function createSafeFetch(opts: SafeFetchOptions = {}): typeof fetch

Source: packages/slingshot-core/src/http/safeFetch.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 cursorPaginatedResponse<T extends ZodType>(itemSchema: T, name: string): void

Source: packages/slingshot-core/src/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 cursorParams(defaults?: CursorParamDefaults): void

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

Shared cursor encoding/decoding for adapter pagination.

All adapters encode cursors as base64-encoded JSON. The payload shape varies by domain (timestamps vs sequence numbers, id vs _id), so the encode/decode functions are generic over the payload type.

function decodeCursor<T extends object>(cursor: string, validate?: (parsed: unknown) => parsed is T,): T | null

Source: packages/slingshot-core/src/cursor.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 decryptField(ciphertext: string, keyConfig: DataEncryptionKey[]): string

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

Recursively freezes an object and all of its nested plain-object values.

Remarks: Deep vs shallow: Object.freeze is inherently shallow — it only prevents top-level property mutations. deepFreeze recurses into all enumerable own values that are non-null objects and not already frozen. This means nested config objects (e.g., config.sessionPolicy, config.mfa) are also immutable after the call.

Remarks: Frozen objects throw TypeError on mutation attempts in strict mode (all TypeScript modules) and silently ignore mutations in sloppy mode.

Remarks: Arrays and class instances embedded in the value tree are also frozen if encountered during the traversal. Primitive values (string, number, boolean) are skipped.

Remarks: Caution: Do not deep-freeze config objects that hold mutable runtime references (e.g., database adapters, permission adapters). Use Object.freeze() (shallow) for those instead.

function deepFreeze<T>(value: T): T

Source: packages/slingshot-core/src/deepFreeze.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

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, env-validated app config — the canonical answer to “where does this setting come from and what shape must it have?”

Slingshot already has a secret-resolution layer (SecretRepository) that answers “where does this VALUE come from” (env var, AWS SSM, Vault, etc.). defineConfig answers the orthogonal question “what SHAPE does this app require, and where do those typed fields live in env.” Most apps need both; they’re complementary.

A config definition declares a namespace and a Zod schema. At app boot, the framework reads process.env (or a custom source), maps each schema field to a NAMESPACE_FIELD env var, validates the whole thing through Zod, and caches the result. After boot, anywhere in your app, cfg.get() returns the typed validated values — fail-fast at startup, not at the first request.

function defineConfig<S extends z.ZodObject>(spec: { readonly namespace: string; readonly schema: S; readonly source?: ConfigSource; }): ConfigDefinition<z.infer<S>>

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

Validate an event definition, throwing if ownerPlugin is empty, no exposure is declared, exposures are duplicated, or internal is mixed with external exposures.

function defineEvent<K extends EventKey>(key: K, definition: Omit<EventDefinition<K>, 'key'>,): Readonly<EventDefinition<K>>

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

Resolve the canonical Actor from a HandlerMeta object.

function defineHandler<TInput extends ZodTypeAny, TOutput extends ZodTypeAny>(config: HandlerConfig<TInput, TOutput>,): SlingshotHandler<TInput, TOutput>

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

Component health contract for prod-track packages.

Components (event bus adapters, queues, search clients, mailers, etc.) implement HealthCheck so the framework can aggregate per-component state into a single readiness/liveness response without each package inventing its own shape.

getHealth() is synchronous and returns the last cached state — cheap enough to call from a /health request handler. checkHealth() is the optional active probe that may perform I/O against the underlying system.

function defineHealthIndicator(indicator: HealthIndicator): HealthIndicator

Source: packages/slingshot-core/src/observability/health.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

Writable plugin state used internally by the framework bootstrap lifecycle.

function definePluginStateKey<T>(name: string): PluginStateKey<T>

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

function definePolicy<TRecord = unknown, TInput = unknown>(key: string, resolver: PolicyResolver<TRecord, TInput>,): PolicyToken<TRecord, TInput>

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

Request-scoped state — the canonical way to wire per-request resources.

A request scope is a typed handle for “something that exists for the duration of a single HTTP request and gets cleaned up at the end.” Common examples: a database transaction, a per-request HTTP client with the actor’s token, an idempotency session, a request-bound tracing helper.

The framework runs factory lazily on first getRequestScoped(c, scope) inside a request and caches the result for the rest of that request. After the handler returns (success or error), the framework calls cleanup for every scope that was actually initialized — in LIFO order, so a transaction can roll back before its underlying connection is released.

function defineRequestScope<T>(spec: { readonly name: string; readonly factory: (context: RequestScopeContext) => T | Promise<T>; readonly cleanup?: (value: T, context: RequestScopeContext) => void | Promise<void>; }): RequestScope<T>

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

Build the standard disableRoutes Zod field for a plugin config schema.

Produces a z.array(z.enum([...values])) schema that validates the disableRoutes array in a plugin config. Pass Object.values(MY_ROUTES) as the allowed values.

function disableRoutesSchema<T extends string>(values: readonly T[]): void

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

Maturity label for a Slingshot package, driving the runtime warning emitted for non-stable packages.

function emitPackageStabilityWarning(packageName: string, stability: Exclude<PackageStability, 'stable'>, detail?: string,): void

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

Shared cursor encoding/decoding for adapter pagination.

All adapters encode cursors as base64-encoded JSON. The payload shape varies by domain (timestamps vs sequence numbers, id vs _id), so the encode/decode functions are generic over the payload type.

function encodeCursor(payload: object): string

Source: packages/slingshot-core/src/cursor.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 encryptField(plaintext: string, keyConfig: DataEncryptionKey[]): string

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

Zod schema for validating an EntityChannelConfig input at runtime.

Used by plugin bootstrap and validateEntityChannelConfig to catch misconfigured WebSocket channel declarations before server startup. Enforces that channels is a non-empty record and that all sub-schemas conform to their expected shapes.

Source: packages/slingshot-core/src/entityChannelConfigSchema.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

Zod schema for validating an EntityRouteConfig input at runtime.

Used by plugin bootstrap and the validateEntityRouteConfig helper to catch misconfigured entity route declarations early, before server startup. Pass any raw config object (from JSON, YAML, or untyped module exports) to get structured Zod errors rather than opaque runtime failures.

Remarks: Forbidden event key namespaces (security., auth:, community:delivery., push:, app:) are enforced by the inline eventKeySchema applied to every event key field across create, get, list, update, delete, and operations entries. Any event key using a forbidden prefix causes validation to fail with a descriptive Zod issue. Rate limit fields (windowMs, max) must be positive integers — zero and negative values are rejected. The retention.hardDelete.after duration string must match {positive integer}{s|m|h|d|w|y} (e.g. '90d', '1y').

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

Build a consistent JSON error response that always includes requestId.

Replaces the common c.json({ error: '…' }, status) pattern so every error the client sees carries the request-id for support/debugging.

function errorResponse<E extends AppEnv, S extends ErrorStatus>(c: Context<E>, message: string, status: S,): void

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

Stable plugin-state key published by slingshot-auth.

Packages that need only a narrow auth-facing peer contract should depend on this key and the accessors below instead of spelunking for raw string keys.

async function evaluateAuthUserAccess(runtime: AuthRuntimePeer, input: AuthUserAccessInput,): Promise<AuthUserAccessDecision>

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

Runtime filter expression evaluator.

Pure function: takes a record, a filter expression, and resolved params, returns whether the record matches.

This is the runtime counterpart of the codegen filter compiler in slingshot-data/generators/filter.ts. Same semantics, different output: the compiler generates code strings, this evaluates live.

function evaluateFilter(record: Record<string, unknown>, filter: FilterExpression, params: Record<string, unknown> = {},): boolean

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

Validate an event definition, throwing if ownerPlugin is empty, no exposure is declared, exposures are duplicated, or internal is mixed with external exposures.

function eventHasExternalExposure<K extends EventKey>(definition: Pick<EventDefinition<K>, 'exposure'>,): boolean

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

Evict the oldest entries from a Map when it exceeds maxEntries.

JavaScript Map iterates in insertion order, so the entries deleted from the front are always the oldest. Useful for capping memory store size in development and single-process deployments.

function evictOldest<K, V>(map: Map<K, V>, maxEntries: number): void

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

Evict the oldest entries from a Map when it exceeds maxEntries.

JavaScript Map iterates in insertion order, so the entries deleted from the front are always the oldest. Useful for capping memory store size in development and single-process deployments.

function evictOldestArray(arr: unknown[], maxEntries: number): void

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

Runtime filter expression evaluator.

Pure function: takes a record, a filter expression, and resolved params, returns whether the record matches.

This is the runtime counterpart of the codegen filter compiler in slingshot-data/generators/filter.ts. Same semantics, different output: the compiler generates code strings, this evaluates live.

function extractFilterParams(filter: FilterExpression): string[]

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

Runtime filter expression evaluator.

Pure function: takes a record, a filter expression, and resolved params, returns whether the record matches.

This is the runtime counterpart of the codegen filter compiler in slingshot-data/generators/filter.ts. Same semantics, different output: the compiler generates code strings, this evaluates live.

function extractMatchParams(match: Record<string, string | number | boolean>): string[]

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

Broadcast keywords that take precedence over user-ID mentions.

function extractMentionsFromBody(body: string): readonly string[]

Source: packages/slingshot-core/src/contentParser.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

Public API for generating fake data from any Zod schema.

function generateExample<T>(schema: { _zod: { def: { type: string } } }, overrides?: Record<string, unknown>,): T

Source: packages/slingshot-core/src/faker/generateFromSchema.ts

Public API for generating fake data from any Zod schema.

function generateFromSchema<T>(schema: { _zod: { def: { type: string } } }, opts: GenerateOptions = {},): T

Source: packages/slingshot-core/src/faker/generateFromSchema.ts

Public API for generating fake data from any Zod schema.

function generateMany<T>(schema: { _zod: { def: { type: string } } }, count: number, opts: GenerateOptions = {},): T[]

Source: packages/slingshot-core/src/faker/generateFromSchema.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 generateSecureToken(): string

Source: packages/slingshot-core/src/crypto.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

Stable plugin-state key published by slingshot-auth.

Packages that need only a narrow auth-facing peer contract should depend on this key and the accessors below instead of spelunking for raw string keys.

function getAuthRuntimePeer(input: PluginStateMap | PluginStateCarrier | object | null | undefined,): AuthRuntimePeer

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

Stable plugin-state key published by slingshot-auth.

Packages that need only a narrow auth-facing peer contract should depend on this key and the accessors below instead of spelunking for raw string keys.

function getAuthRuntimePeerOrNull(input: PluginStateMap | PluginStateCarrier | object | null | undefined,): AuthRuntimePeer | null

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

Unified cache interface: implementations wrap a backing store (Redis, memory, SQLite, …) behind a consistent get/set/del API used by response caching and session storage.

function getCacheAdapter(input: ContextCarrier, store: CacheStoreName): CacheAdapter

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

Unified cache interface: implementations wrap a backing store (Redis, memory, SQLite, …) behind a consistent get/set/del API used by response caching and session storage.

function getCacheAdapterOrNull(input: ContextCarrier, store: CacheStoreName,): CacheAdapter | null

Source: packages/slingshot-core/src/cache.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 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 getClientIpFromRequest(req: Request, trustProxy: false | number): 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

A static email template (subject/html/text) registered by a plugin and consumed by the mail plugin.

function getEmailTemplate(input: ContextCarrier, key: string): EmailTemplate | null

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

A static email template (subject/html/text) registered by a plugin and consumed by the mail plugin.

function getEmailTemplates(input: ContextCarrier): Record<string, EmailTemplate>

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

Cross-package handle to the embeds plugin’s URL unfurling capability.

function getEmbedsPeer(input: PluginStateMap | PluginStateCarrier | object | null | undefined,): EmbedsPeer

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

Cross-package handle to the embeds plugin’s URL unfurling capability.

function getEmbedsPeerOrNull(input: PluginStateMap | PluginStateCarrier | object | null | undefined,): EmbedsPeer | null

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

Builds a short fingerprint hash from stable HTTP request headers, used for unauthenticated bot detection and request fingerprinting.

function getFingerprintBuilder(input: ContextCarrier): FingerprintBuilder

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

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions
function getPermissionsState(input: PluginStateMap | PluginStateCarrier | object | null | undefined,): PermissionsState

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

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions
function getPermissionsStateOrNull(input: PluginStateMap | PluginStateCarrier | object | null | undefined,): PermissionsState | null

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

Writable plugin state used internally by the framework bootstrap lifecycle.

function getPluginState(input: PluginStateMap | PluginStateCarrier | object,): PluginStateMap

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

Writable plugin state used internally by the framework bootstrap lifecycle.

function getPluginStateFromRequest(c: { get(key: string): unknown }): PluginStateMap

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

Writable plugin state used internally by the framework bootstrap lifecycle.

function getPluginStateFromRequestOrNull(c: { get(key: string): unknown; }): PluginStateMap | null

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

Writable plugin state used internally by the framework bootstrap lifecycle.

function getPluginStateOrNull(input: PluginStateMap | PluginStateCarrier | object | null | undefined,): PluginStateMap | null

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

function getPolicyResolverKey(resolver: string | PolicyTokenRef): string

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

Whether the Postgres runtime applies migrations on startup or assumes the schema is already migrated.

function getPostgresPoolRuntime(pool: object): PostgresPoolRuntime | null

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

Cross-package handle for resolving and updating interactive message components published by a peer package.

function getPublishedInteractionsPeerOrNull<TPeer extends PublishedInteractionsPeer>(input: PluginStateMap | PluginStateCarrier | object | null | undefined, pluginKey: string,): TPeer | null

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

Shape of a formatted push notification: title plus optional body, data, icon, badge, and URL.

function getPushFormatterPeer(input: PluginStateMap | PluginStateCarrier | object | null | undefined,): PushFormatterPeer

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

Shape of a formatted push notification: title plus optional body, data, icon, badge, and URL.

function getPushFormatterPeerOrNull(input: PluginStateMap | PluginStateCarrier | object | null | undefined,): PushFormatterPeer | null

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

Builds a short fingerprint hash from stable HTTP request headers, used for unauthenticated bot detection and request fingerprinting.

function getRateLimitAdapter(input: ContextCarrier): RateLimitAdapter

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

Resolves the connecting actor for framework WebSocket/SSE upgrades without depending on the auth plugin; registered by the auth plugin during setup.

function getRequestActorResolver(input: ContextCarrier): RequestActorResolver

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

Resolves the connecting actor for framework WebSocket/SSE upgrades without depending on the auth plugin; registered by the auth plugin during setup.

function getRequestActorResolverOrNull(input: ContextCarrier): RequestActorResolver | null

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

Request-scoped state — the canonical way to wire per-request resources.

A request scope is a typed handle for “something that exists for the duration of a single HTTP request and gets cleaned up at the end.” Common examples: a database transaction, a per-request HTTP client with the actor’s token, an idempotency session, a request-bound tracing helper.

The framework runs factory lazily on first getRequestScoped(c, scope) inside a request and caches the result for the rest of that request. After the handler returns (success or error), the framework calls cleanup for every scope that was actually initialized — in LIFO order, so a transaction can roll back before its underlying connection is released.

async function getRequestScoped<T>(c: Context, scope: RequestScope<T>): Promise<T>

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

Request-scoped state — the canonical way to wire per-request resources.

A request scope is a typed handle for “something that exists for the duration of a single HTTP request and gets cleaned up at the end.” Common examples: a database transaction, a per-request HTTP client with the actor’s token, an idempotency session, a request-bound tracing helper.

The framework runs factory lazily on first getRequestScoped(c, scope) inside a request and caches the result for the rest of that request. After the handler returns (success or error), the framework calls cleanup for every scope that was actually initialized — in LIFO order, so a transaction can roll back before its underlying connection is released.

function getRequestScopeStore(c: Context): RequestScopeStore | undefined

Source: packages/slingshot-core/src/requestScope.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

A guard that runs after authentication, in registration order; the first to return a PostAuthGuardFailure short-circuits the request.

function getRouteAuth(input: ContextCarrier): RouteAuthRegistry

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

A guard that runs after authentication, in registration order; the first to return a PostAuthGuardFailure short-circuits the request.

function getRouteAuthOrNull(input: ContextCarrier): RouteAuthRegistry | null

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

Search plugin runtime contract.

Defines the shape stored in pluginState by slingshot-search and consumed by framework-internal code (createContextStoreInfra) for write-through sync and late entity initialization.

Lives in slingshot-core because both sides (framework and search plugin) depend on core — same rationale as SearchProviderContract.

function getSearchPluginRuntime(input: PluginStateMap | PluginStateCarrier | object | null | undefined,): SearchPluginRuntime

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

Search plugin runtime contract.

Defines the shape stored in pluginState by slingshot-search and consumed by framework-internal code (createContextStoreInfra) for write-through sync and late entity initialization.

Lives in slingshot-core because both sides (framework and search plugin) depend on core — same rationale as SearchProviderContract.

function getSearchPluginRuntimeOrNull(input: PluginStateMap | PluginStateCarrier | object | null | undefined,): SearchPluginRuntime | null

Source: packages/slingshot-core/src/searchPluginRuntime.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

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 hashToken(token: string): string

Source: packages/slingshot-core/src/crypto.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 hmacSign(input: string, secret: string | string[]): string

Source: packages/slingshot-core/src/crypto.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

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 isEncryptedField(value: string): boolean

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

Create a deep-frozen EventEnvelope from the given parameters.

Generates a unique eventId (UUID v4) and an ISO-8601 occurredAt timestamp. The returned envelope and all nested objects are recursively frozen to prevent downstream mutation.

function isEventEnvelope<K extends EventKey = EventKey>(value: unknown, key?: K,): value is EventEnvelope<K>

Source: packages/slingshot-core/src/eventEnvelope.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.

function isHttpError(err: unknown): err is HttpError

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

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

function isPackageRegistered(app: object, packageName: string): boolean

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

Writable plugin state used internally by the framework bootstrap lifecycle.

function isPluginStateSealed(pluginState: PluginStateMap): boolean

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

function isPolicyToken(value: unknown): value is PolicyTokenRef

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

Minimal local type for the subset of the undici Dispatcher we use. We keep this local so callers do not need to import undici types directly.

function isPrivateOrLoopbackIp(ip: string, family: 4 | 6): boolean

Source: packages/slingshot-core/src/http/safeFetch.ts

Returns whether a request path matches any declared public-path pattern.

Supported pattern forms:

  • exact path: /.well-known/assetlinks.json
  • prefix wildcard: /.well-known/*
function isPublicPath(path: string, publicPaths?: Iterable<string> | null): boolean

Source: packages/slingshot-core/src/publicPath.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.

function isValidationError(err: unknown): err is ValidationError

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

Shared WebSocket utility helpers.

Extracted to slingshot-core so both the framework (src/framework/lib/ws.ts) and entity packages (slingshot-entity) can import without creating a cross-layer dependency.

function isValidRoomName(room: string): boolean

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

Typed, env-validated app config — the canonical answer to “where does this setting come from and what shape must it have?”

Slingshot already has a secret-resolution layer (SecretRepository) that answers “where does this VALUE come from” (env var, AWS SSM, Vault, etc.). defineConfig answers the orthogonal question “what SHAPE does this app require, and where do those typed fields live in env.” Most apps need both; they’re complementary.

A config definition declares a namespace and a Zod schema. At app boot, the framework reads process.env (or a custom source), maps each schema field to a NAMESPACE_FIELD env var, validates the whole thing through Zod, and caches the result. After boot, anywhere in your app, cfg.get() returns the typed validated values — fail-fast at startup, not at the first request.

function loadConfigs(configs: readonly ConfigDefinition<unknown>[], env: Readonly<Record<string, string | undefined>> = process.env as Readonly< Record<string, string | undefined> >,): void

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

Operation-level idempotency contract used by delivery and retry-aware packages (mail, push, notifications, orchestration) to dedup retried operations.

Remarks: This is distinct from the HTTP IdempotencyAdapter contract in ../idempotencyAdapter.ts, which is dedicated to the request-replay middleware (caching the full HTTP response for a given Idempotency-Key header).

Remarks: The contract here is keyed on a structured IdempotencyKey and stores an arbitrary payload (typed at the call site). Callers wrap their operation in withIdempotency so that retries skip work and replay the prior result.

function makeIdempotencyKey(parts: ReadonlyArray<string | number>): IdempotencyKey

Source: packages/slingshot-core/src/idempotency/index.ts

Validate an event definition, throwing if ownerPlugin is empty, no exposure is declared, exposures are duplicated, or internal is mixed with external exposures.

function matchSubscriberToScope(principal: EventSubscriptionPrincipal, scope: EventScope | null, exposure: readonly EventExposure[],): boolean

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

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

function maybeAutoRegister(exportName: string, value: unknown): void

Source: packages/slingshot-core/src/createRoute.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 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

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

Source: packages/slingshot-core/src/packageAuthoring.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

Broadcast keywords that take precedence over user-ID mentions.

function parseBody(body: string | undefined | null, format: ContentFormat = 'markdown',): ParsedBody

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

Broadcast keywords that take precedence over user-ID mentions.

function parseContentTokens(body: string): ParsedContent

Source: packages/slingshot-core/src/contentParser.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 parseCursorParams(raw: { limit?: string; cursor?: string }, defaults?: CursorParamDefaults,): ParsedCursorParams

Source: packages/slingshot-core/src/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

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions

Source: packages/slingshot-core/src/permissions.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

Writable plugin state used internally by the framework bootstrap lifecycle.

function publishEntityAdaptersState<TAdapter extends object>(pluginState: PluginStateMap, pluginName: string, entityAdapters: Record<string, TAdapter>,): Readonly<EntityAdaptersPluginState<TAdapter> & Record<string, unknown>>

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

Writable plugin state used internally by the framework bootstrap lifecycle.

function publishPluginState(pluginState: PluginStateMap, key: string, value: unknown): void; export function publishPluginState<T>( pluginState: PluginStateMap, key: PluginStateKey<T>, value: T, ): void; export function publishPluginState( pluginState: PluginStateMap, key: string | PluginStateKey<unknown>, value: unknown, ): void

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

Writable plugin state used internally by the framework bootstrap lifecycle.

function readPluginState<T>(input: PluginStateMap | PluginStateCarrier | object | null | undefined, key: PluginStateKey<T>,): T | undefined

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

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

async function registerPluginCapabilities(ctx: { readonly capabilityProviders?: ReadonlyMap<string, string>; readonly pluginState: PluginStateMap; }, pluginName: string, provided: ReadonlyArray<PublishedPackageCapability<unknown>>,): Promise<void>

Source: packages/slingshot-core/src/packageAuthoring.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

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

Writable plugin state used internally by the framework bootstrap lifecycle.

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

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

Writable plugin state used internally by the framework bootstrap lifecycle.

function requirePluginState<T>(input: PluginStateMap | PluginStateCarrier | object | null | undefined, key: PluginStateKey<T>,): T

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

Canonical store infrastructure contract shared across all slingshot packages.

Plugin authors import these types from @lastshotlabs/slingshot-core to declare repository factories without depending on framework or auth internals.

The pattern:

  1. Declare RepoFactories<YourRepo> with one factory per StoreType
  2. Call resolveRepo(factories, storeType, infra) at startup
  3. The framework provides StoreInfra — your factory receives it

The framework bootstrap (createContextStoreInfra) injects additional capabilities onto StoreInfra via well-known Reflect symbols. Packages that need these capabilities use Reflect.get(infra, SYMBOL) — they never receive these as function arguments (CLAUDE.md Rule 16).

RESOLVE_ENTITY_FACTORIES — injected by the bootstrap with the createEntityFactories function. Allows packages/slingshot-entity to create RepoFactories<T> from entity configs without importing from the root app.

RESOLVE_COMPOSITE_FACTORIES — injected by the bootstrap with the createCompositeFactories function. Used by the composite entity path in packages/slingshot-entity to build multi-entity adapter sets.

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

Canonical store infrastructure contract shared across all slingshot packages.

Plugin authors import these types from @lastshotlabs/slingshot-core to declare repository factories without depending on framework or auth internals.

The pattern:

  1. Declare RepoFactories<YourRepo> with one factory per StoreType
  2. Call resolveRepo(factories, storeType, infra) at startup
  3. The framework provides StoreInfra — your factory receives it

The framework bootstrap (createContextStoreInfra) injects additional capabilities onto StoreInfra via well-known Reflect symbols. Packages that need these capabilities use Reflect.get(infra, SYMBOL) — they never receive these as function arguments (CLAUDE.md Rule 16).

RESOLVE_ENTITY_FACTORIES — injected by the bootstrap with the createEntityFactories function. Allows packages/slingshot-entity to create RepoFactories<T> from entity configs without importing from the root app.

RESOLVE_COMPOSITE_FACTORIES — injected by the bootstrap with the createCompositeFactories function. Used by the composite entity path in packages/slingshot-entity to build multi-entity adapter sets.

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

Canonical store infrastructure contract shared across all slingshot packages.

Plugin authors import these types from @lastshotlabs/slingshot-core to declare repository factories without depending on framework or auth internals.

The pattern:

  1. Declare RepoFactories<YourRepo> with one factory per StoreType
  2. Call resolveRepo(factories, storeType, infra) at startup
  3. The framework provides StoreInfra — your factory receives it

The framework bootstrap (createContextStoreInfra) injects additional capabilities onto StoreInfra via well-known Reflect symbols. Packages that need these capabilities use Reflect.get(infra, SYMBOL) — they never receive these as function arguments (CLAUDE.md Rule 16).

RESOLVE_ENTITY_FACTORIES — injected by the bootstrap with the createEntityFactories function. Allows packages/slingshot-entity to create RepoFactories<T> from entity configs without importing from the root app.

RESOLVE_COMPOSITE_FACTORIES — injected by the bootstrap with the createCompositeFactories function. Used by the composite entity path in packages/slingshot-entity to build multi-entity adapter sets.

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

Resolve the canonical Actor from a HandlerMeta object.

function resolveActor(meta: HandlerMeta): Actor

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

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

function resolveCapabilityValue<TValue>(ctx: { readonly capabilityProviders?: ReadonlyMap<string, string>; readonly pluginState: PluginStateMap; }, capability: PackageCapabilityHandle<TValue>,): TValue | undefined

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

A value that either IS a SlingshotContext or is an app instance that HAS one attached.

Used as the parameter type for all context-accessor helpers (getRequestActorResolver, getRateLimitAdapter, getCacheAdapter, etc.) so callers can pass either the raw context or the app without ceremony.

Remarks: Branded-context pattern: ContextCarrier is intentionally typed as SlingshotContext | object rather than SlingshotContext | Hono to avoid importing the Hono type into contextAccess.ts and to support any future app container that happens to carry a context via getContext. The actual discrimination between “is a context” and “has a context” is performed at runtime by checking for the internal context brand installed by attachContext(). This avoids property-shape guessing, which can false-positive on unrelated objects that happen to expose fields like config or cacheAdapters.

function resolveContext(input: ContextCarrier): SlingshotContext

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

Runtime filter expression evaluator.

Pure function: takes a record, a filter expression, and resolved params, returns whether the record matches.

This is the runtime counterpart of the codegen filter compiler in slingshot-data/generators/filter.ts. Same semantics, different output: the compiler generates code strings, this evaluates live.

function resolveMatch(match: Record<string, string | number | boolean>, params: Record<string, unknown>,): Record<string, unknown>

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

function resolveOpConfig(routeConfig: EntityRouteConfig, opName: string,): RouteOperationConfig | undefined

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

Writable plugin state used internally by the framework bootstrap lifecycle.

function resolvePluginState(input: PluginStateMap | PluginStateCarrier | null | undefined,): PluginStateMap | null

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

Canonical store infrastructure contract shared across all slingshot packages.

Plugin authors import these types from @lastshotlabs/slingshot-core to declare repository factories without depending on framework or auth internals.

The pattern:

  1. Declare RepoFactories<YourRepo> with one factory per StoreType
  2. Call resolveRepo(factories, storeType, infra) at startup
  3. The framework provides StoreInfra — your factory receives it

The framework bootstrap (createContextStoreInfra) injects additional capabilities onto StoreInfra via well-known Reflect symbols. Packages that need these capabilities use Reflect.get(infra, SYMBOL) — they never receive these as function arguments (CLAUDE.md Rule 16).

RESOLVE_ENTITY_FACTORIES — injected by the bootstrap with the createEntityFactories function. Allows packages/slingshot-entity to create RepoFactories<T> from entity configs without importing from the root app.

RESOLVE_COMPOSITE_FACTORIES — injected by the bootstrap with the createCompositeFactories function. Used by the composite entity path in packages/slingshot-entity to build multi-entity adapter sets.

function resolveRepo<T>(factories: RepoFactories<T>, storeType: StoreType, infra: StoreInfra,): T

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

Canonical store infrastructure contract shared across all slingshot packages.

Plugin authors import these types from @lastshotlabs/slingshot-core to declare repository factories without depending on framework or auth internals.

The pattern:

  1. Declare RepoFactories<YourRepo> with one factory per StoreType
  2. Call resolveRepo(factories, storeType, infra) at startup
  3. The framework provides StoreInfra — your factory receives it

The framework bootstrap (createContextStoreInfra) injects additional capabilities onto StoreInfra via well-known Reflect symbols. Packages that need these capabilities use Reflect.get(infra, SYMBOL) — they never receive these as function arguments (CLAUDE.md Rule 16).

RESOLVE_ENTITY_FACTORIES — injected by the bootstrap with the createEntityFactories function. Allows packages/slingshot-entity to create RepoFactories<T> from entity configs without importing from the root app.

RESOLVE_COMPOSITE_FACTORIES — injected by the bootstrap with the createCompositeFactories function. Used by the composite entity path in packages/slingshot-entity to build multi-entity adapter sets.

async function resolveRepoAsync<T>(factories: Record<StoreType, (infra: StoreInfra) => T | Promise<T>>, storeType: StoreType, infra: StoreInfra,): Promise<T>

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

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

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

A branded route key string in the format "METHOD /path" (method always uppercased).

Constructed exclusively via routeKey() — never hand-typed — to prevent drift between the route constant definition and the shouldMountRoute runtime check.

function routeKey<M extends string, P extends string>(method: M, path: P): RouteKey<M, P>

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

Per-subsystem SQLite migration runner.

Tracks applied migrations in a shared _slingshot_migrations table keyed by subsystem name, rather than the global PRAGMA user_version. This allows multiple subsystems (auth, permissions, webhooks, push) to share the same SQLite file without colliding on the single global version integer.

The _slingshot_migrations table is created automatically if it doesn’t exist. Each row records the highest migration index applied for a given subsystem. Migration execution is serialized with BEGIN IMMEDIATE so concurrent processes cannot both read the same stale version and race each other through the same schema change.

function runSubsystemMigrations(db: RuntimeSqliteDatabase, subsystem: string, migrations: ReadonlyArray<(db: RuntimeSqliteDatabase) => void>,): void

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

Thrown when a path operation would escape its configured base directory.

Used by safeJoin to surface path-traversal attempts (.. segments, absolute paths, null bytes, or any input that resolves outside the allowed root). Callers should treat this as a 4xx-class input error rather than an internal failure: the input was untrusted and rejected.

function safeJoin(baseDir: string, userPath: string): string

Source: packages/slingshot-core/src/lib/safePath.ts

CRLF / NUL injection guards for header-like and log-like sinks.

When user-controlled values flow into HTTP headers, email headers, queue message headers, or log lines without sanitization, an attacker can inject carriage-return / line-feed sequences (\r\n, \n) and forge additional headers, split log records, or hide malicious payloads in logs. This module provides the two helpers prod-track packages should use at every boundary that emits a header value or log fragment derived from external input.

Two complementary functions are exposed:

  • sanitizeHeaderValue — strict, throws HeaderInjectionError when the input contains \r, \n, or NUL. Use at HTTP / email / queue header sinks where silent stripping would mask other bugs.
  • sanitizeLogValue — lenient, escapes the same control characters so they appear as literal \\r / \\n / \\0 in the log output. Logging must never throw, so this function never throws.
function sanitizeHeaderValue(value: string, header?: string): string

Source: packages/slingshot-core/src/lib/sanitize.ts

CRLF / NUL injection guards for header-like and log-like sinks.

When user-controlled values flow into HTTP headers, email headers, queue message headers, or log lines without sanitization, an attacker can inject carriage-return / line-feed sequences (\r\n, \n) and forge additional headers, split log records, or hide malicious payloads in logs. This module provides the two helpers prod-track packages should use at every boundary that emits a header value or log fragment derived from external input.

Two complementary functions are exposed:

  • sanitizeHeaderValue — strict, throws HeaderInjectionError when the input contains \r, \n, or NUL. Use at HTTP / email / queue header sinks where silent stripping would mask other bugs.
  • sanitizeLogValue — lenient, escapes the same control characters so they appear as literal \\r / \\n / \\0 in the log output. Logging must never throw, so this function never throws.
function sanitizeLogValue(value: unknown): string

Source: packages/slingshot-core/src/lib/sanitize.ts

Writable plugin state used internally by the framework bootstrap lifecycle.

function sealPluginState(pluginState: PluginStateMap): void

Source: packages/slingshot-core/src/pluginState.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

Request-scoped state — the canonical way to wire per-request resources.

A request scope is a typed handle for “something that exists for the duration of a single HTTP request and gets cleaned up at the end.” Common examples: a database transaction, a per-request HTTP client with the actor’s token, an idempotency session, a request-bound tracing helper.

The framework runs factory lazily on first getRequestScoped(c, scope) inside a request and caches the result for the rest of that request. After the handler returns (success or error), the framework calls cleanup for every scope that was actually initialized — in LIFO order, so a transaction can roll back before its underlying connection is released.

function setRequestScopeStore(c: Context, store: RequestScopeStore): void

Source: packages/slingshot-core/src/requestScope.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 setStandaloneClientIp(req: Request, value: string): void

Source: packages/slingshot-core/src/clientIp.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 setStandaloneTrustProxy(req: Request, value: false | number): void

Source: packages/slingshot-core/src/clientIp.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

A branded route key string in the format "METHOD /path" (method always uppercased).

Constructed exclusively via routeKey() — never hand-typed — to prevent drift between the route constant definition and the shouldMountRoute runtime check.

function shouldMountRoute(method: string, path: string, disabledRoutes?: readonly string[],): boolean

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

Broadcast keywords that take precedence over user-ID mentions.

function stripContentTokens(body: string): string

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

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions

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

Promise and AbortSignal timeout helpers.

withTimeout wraps an arbitrary promise and rejects with TimeoutError if the configured deadline elapses before the promise settles. The internal timer is always cleared when the wrapped promise resolves or rejects, so a fast-settling promise will not produce a delayed rejection from a stale timer.

timeoutSignal returns an AbortSignal that is aborted after timeoutMs. Useful for libraries (HTTP clients, streams) that accept an AbortSignal directly and do not need a wrapping promise.

function timeoutSignal(timeoutMs: number): AbortSignal

Source: packages/slingshot-core/src/concurrency/withTimeout.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

Convert a hono-style path (/posts/:id, /users/:id?, /posts/:slug{.+}) into the OpenAPI form (/posts/{id}, /users/{id}, /posts/{slug}).

Hono uses colon-prefixed parameters; OpenAPI emits and codegen tools (including the Slingshot snapshot client) expect {name} braces. The framework’s runtime router still needs the colon form, so this conversion only applies at the boundary where a path is handed to createRoute(...) / the OpenAPI registry — never to the live router.

Idempotent on already-converted {name} segments. Strips hono’s optional marker (:id?) and regex constraint (:slug{.+}) since OpenAPI represents them as plain {slug} without those modifiers.

function toHonoPath(path: string): string

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

Convert a hono-style path (/posts/:id, /users/:id?, /posts/:slug{.+}) into the OpenAPI form (/posts/{id}, /users/{id}, /posts/{slug}).

Hono uses colon-prefixed parameters; OpenAPI emits and codegen tools (including the Slingshot snapshot client) expect {name} braces. The framework’s runtime router still needs the colon form, so this conversion only applies at the boundary where a path is handed to createRoute(...) / the OpenAPI registry — never to the live router.

Idempotent on already-converted {name} segments. Strips hono’s optional marker (:id?) and regex constraint (:slug{.+}) since OpenAPI represents them as plain {slug} without those modifiers.

function toOpenApiPath(path: string): string

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

Build the standard disableRoutes Zod field for a plugin config schema.

Produces a z.array(z.enum([...values])) schema that validates the disableRoutes array in a plugin config. Pass Object.values(MY_ROUTES) as the allowed values.

function validateAdapterShape(pluginName: string, adapterLabel: string, adapter: unknown, requiredMethods: string[],): void

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

Zod schema for validating an EntityChannelConfig input at runtime.

Used by plugin bootstrap and validateEntityChannelConfig to catch misconfigured WebSocket channel declarations before server startup. Enforces that channels is a non-empty record and that all sub-schemas conform to their expected shapes.

function validateEntityChannelConfig(config: unknown): void

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

Zod schema for validating an EntityRouteConfig input at runtime.

Used by plugin bootstrap and the validateEntityRouteConfig helper to catch misconfigured entity route declarations early, before server startup. Pass any raw config object (from JSON, YAML, or untyped module exports) to get structured Zod errors rather than opaque runtime failures.

Remarks: Forbidden event key namespaces (security., auth:, community:delivery., push:, app:) are enforced by the inline eventKeySchema applied to every event key field across create, get, list, update, delete, and operations entries. Any event key using a forbidden prefix causes validation to fail with a descriptive Zod issue. Rate limit fields (windowMs, max) must be positive integers — zero and negative values are rejected. The retention.hardDelete.after duration string must match {positive integer}{s|m|h|d|w|y} (e.g. '90d', '1y').

function validateEntityRouteConfig(config: unknown): void

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

Validate an event definition, throwing if ownerPlugin is empty, no exposure is declared, exposures are duplicated, or internal is mixed with external exposures.

function validateEventDefinition<K extends EventKey>(definition: EventDefinition<K>): void

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

Result of validating an event payload against a registered schema.

function validateEventPayload(event: string, payload: unknown, registry: EventSchemaRegistry | undefined, mode: ValidationMode, logger?: Logger,): unknown

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

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions
function validateGrant(grant: Omit<PermissionGrant, 'id' | 'grantedAt'>): void

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

Build the standard disableRoutes Zod field for a plugin config schema.

Produces a z.array(z.enum([...values])) schema that validates the disableRoutes array in a plugin config. Pass Object.values(MY_ROUTES) as the allowed values.

function validatePluginConfig<S extends z.ZodType>(pluginName: string, rawConfig: unknown, schema: S, logger?: Logger,): z.infer<S>

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

Operation-level idempotency contract used by delivery and retry-aware packages (mail, push, notifications, orchestration) to dedup retried operations.

Remarks: This is distinct from the HTTP IdempotencyAdapter contract in ../idempotencyAdapter.ts, which is dedicated to the request-replay middleware (caching the full HTTP response for a given Idempotency-Key header).

Remarks: The contract here is keyed on a structured IdempotencyKey and stores an arbitrary payload (typed at the call site). Callers wrap their operation in withIdempotency so that retries skip work and replay the prior result.

async function withIdempotency<T>(adapter: IdempotencyAdapter, key: IdempotencyKey, fn: () => Promise<T>, opts?: WithIdempotencyOptions,): Promise<

Source: packages/slingshot-core/src/idempotency/index.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

Promise and AbortSignal timeout helpers.

withTimeout wraps an arbitrary promise and rejects with TimeoutError if the configured deadline elapses before the promise settles. The internal timer is always cleared when the wrapped promise resolves or rejects, so a fast-settling promise will not produce a delayed rejection from a stale timer.

timeoutSignal returns an AbortSignal that is aborted after timeoutMs. Useful for libraries (HTTP clients, streams) that accept an AbortSignal directly and do not need a wrapping promise.

function withTimeout<T>(promise: Promise<T>, timeoutMs: number, label?: string): Promise<T>

Source: packages/slingshot-core/src/concurrency/withTimeout.ts

Actor-based identity abstraction.

Decouples all framework consumers (guards, permissions, data scoping, audit, entity routes) from specific auth-provider field names by mapping them into the canonical Actor shape.

Custom auth integrations implement IdentityResolver to map their own identity representation into the canonical Actor shape.

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

Zod schema for voice message metadata.

Source: packages/slingshot-core/src/content.schemas.ts

Stable plugin-state key published by slingshot-chat. Used by the interactions peer bridge (probeChatPeer) to discover chat’s published interactionsPeer.

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

Stable plugin-state key published by slingshot-chat. Used by the interactions peer bridge (probeChatPeer) to discover chat’s published interactionsPeer.

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

Zod schema for voice message metadata.

Source: packages/slingshot-core/src/content.schemas.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

Evict the oldest entries from a Map when it exceeds maxEntries.

JavaScript Map iterates in insertion order, so the entries deleted from the front are always the oldest. Useful for capping memory store size in development and single-process deployments.

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

Stable plugin-state key published by slingshot-chat. Used by the interactions peer bridge (probeChatPeer) to discover chat’s published interactionsPeer.

Source: packages/slingshot-core/src/pluginKeys.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

Controls how event payloads are encoded for durable transport (Kafka topics, BullMQ queues) and decoded on the consumer side.

Non-durable listeners always receive the original in-process object — the serializer is only invoked on durable produce and consume paths.

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

Zod schema for voice message metadata.

Source: packages/slingshot-core/src/content.schemas.ts

Content format indicator. Tells the client how to parse the body field.

  • 'plain' — render as literal text, no markdown parsing, tokens still parsed
  • 'markdown' — parse as GitHub-flavored markdown, then parse tokens in text runs

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

Content format indicator. Tells the client how to parse the body field.

  • 'plain' — render as literal text, no markdown parsing, tokens still parsed
  • 'markdown' — parse as GitHub-flavored markdown, then parse tokens in text runs

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

Content format indicator. Tells the client how to parse the body field.

  • 'plain' — render as literal text, no markdown parsing, tokens still parsed
  • 'markdown' — parse as GitHub-flavored markdown, then parse tokens in text runs

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

Structured logger contract used by prod-track packages.

The interface is deliberately small: four severity methods plus child() for fields that should appear on every log line in a sub-component (request id, plugin name, queue id, etc.). Implementations must never throw — log sinks that crash the caller are worse than silent loss.

createConsoleLogger is the default — one JSON line per call written to the underlying console method matching the level. noopLogger is the test default for code paths that need a logger handle but no output.

Source: packages/slingshot-core/src/observability/logger.ts

Zod schema for voice message metadata.

Source: packages/slingshot-core/src/content.schemas.ts

Search plugin runtime contract.

Defines the shape stored in pluginState by slingshot-search and consumed by framework-internal code (createContextStoreInfra) for write-through sync and late entity initialization.

Lives in slingshot-core because both sides (framework and search plugin) depend on core — same rationale as SearchProviderContract.

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

Zod schema for voice message metadata.

Source: packages/slingshot-core/src/content.schemas.ts

Resolve the canonical Actor from a HandlerMeta object.

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

CRLF / NUL injection guards for header-like and log-like sinks.

When user-controlled values flow into HTTP headers, email headers, queue message headers, or log lines without sanitization, an attacker can inject carriage-return / line-feed sequences (\r\n, \n) and forge additional headers, split log records, or hide malicious payloads in logs. This module provides the two helpers prod-track packages should use at every boundary that emits a header value or log fragment derived from external input.

Two complementary functions are exposed:

  • sanitizeHeaderValue — strict, throws HeaderInjectionError when the input contains \r, \n, or NUL. Use at HTTP / email / queue header sinks where silent stripping would mask other bugs.
  • sanitizeLogValue — lenient, escapes the same control characters so they appear as literal \\r / \\n / \\0 in the log output. Logging must never throw, so this function never throws.

Source: packages/slingshot-core/src/lib/sanitize.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

Resolve the canonical Actor from a HandlerMeta object.

Source: packages/slingshot-core/src/handler.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

Controls how event payloads are encoded for durable transport (Kafka topics, BullMQ queues) and decoded on the consumer side.

Non-durable listeners always receive the original in-process object — the serializer is only invoked on durable produce and consume paths.

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

Thrown when a path operation would escape its configured base directory.

Used by safeJoin to surface path-traversal attempts (.. segments, absolute paths, null bytes, or any input that resolves outside the allowed root). Callers should treat this as a 4xx-class input error rather than an internal failure: the input was untrusted and rejected.

Source: packages/slingshot-core/src/lib/safePath.ts

Minimal local type for the subset of the undici Dispatcher we use. We keep this local so callers do not need to import undici types directly.

Source: packages/slingshot-core/src/http/safeFetch.ts

Minimal local type for the subset of the undici Dispatcher we use. We keep this local so callers do not need to import undici types directly.

Source: packages/slingshot-core/src/http/safeFetch.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

The rendered output of an email template. Contains the final HTML body, an optional plain-text alternative, and an optional subject line.

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

Promise and AbortSignal timeout helpers.

withTimeout wraps an arbitrary promise and rejects with TimeoutError if the configured deadline elapses before the promise settles. The internal timer is always cleared when the wrapped promise resolves or rejects, so a fast-settling promise will not produce a delayed rejection from a stale timer.

timeoutSignal returns an AbortSignal that is aborted after timeoutMs. Useful for libraries (HTTP clients, streams) that accept an AbortSignal directly and do not need a wrapping promise.

Source: packages/slingshot-core/src/concurrency/withTimeout.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

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

Actor-based identity abstraction.

Decouples all framework consumers (guards, permissions, data scoping, audit, entity routes) from specific auth-provider field names by mapping them into the canonical Actor shape.

Custom auth integrations implement IdentityResolver to map their own identity representation into the canonical Actor shape.

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

The authenticated admin identity extracted from a verified admin request. Produced by AdminAccessProvider.verifyRequest() and carried through admin routes.

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

The authenticated admin identity extracted from a verified admin request. Produced by AdminAccessProvider.verifyRequest() and carried through admin routes.

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

One normalized record extracted from a trigger event.

Source: packages/slingshot-core/src/functions.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

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

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

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

Content format indicator. Tells the client how to parse the body field.

  • 'plain' — render as literal text, no markdown parsing, tokens still parsed
  • 'markdown' — parse as GitHub-flavored markdown, then parse tokens in text runs

Source: packages/slingshot-core/src/content.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

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

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

Stable plugin-state key published by slingshot-auth.

Packages that need only a narrow auth-facing peer contract should depend on this key and the accessors below instead of spelunking for raw string keys.

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

Stable plugin-state key published by slingshot-auth.

Packages that need only a narrow auth-facing peer contract should depend on this key and the accessors below instead of spelunking for raw string keys.

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

Stable plugin-state key published by slingshot-auth.

Packages that need only a narrow auth-facing peer contract should depend on this key and the accessors below instead of spelunking for raw string keys.

Source: packages/slingshot-core/src/authPeer.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

One normalized record extracted from a trigger event.

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

Supported CAPTCHA verification providers.

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

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

Authentication strategy enforced at WebSocket channel subscribe time.

  • 'userAuth' — requires a valid session (resolved by RequestActorResolver)
  • 'bearer' — requires a valid bearer token
  • 'none' — no auth check; any client may subscribe

Remarks: This mirrors RouteAuthConfig from entityRouteConfig.ts but applies to the WebSocket subscribe handshake rather than HTTP route handlers. The auth check runs once when the client sends the initial subscribe message; it is not re-evaluated on every incoming WebSocket frame.

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

Authentication strategy enforced at WebSocket channel subscribe time.

  • 'userAuth' — requires a valid session (resolved by RequestActorResolver)
  • 'bearer' — requires a valid bearer token
  • 'none' — no auth check; any client may subscribe

Remarks: This mirrors RouteAuthConfig from entityRouteConfig.ts but applies to the WebSocket subscribe handshake rather than HTTP route handlers. The auth check runs once when the client sends the initial subscribe message; it is not re-evaluated on every incoming WebSocket frame.

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

Authentication strategy enforced at WebSocket channel subscribe time.

  • 'userAuth' — requires a valid session (resolved by RequestActorResolver)
  • 'bearer' — requires a valid bearer token
  • 'none' — no auth check; any client may subscribe

Remarks: This mirrors RouteAuthConfig from entityRouteConfig.ts but applies to the WebSocket subscribe handshake rather than HTTP route handlers. The auth check runs once when the client sends the initial subscribe message; it is not re-evaluated on every incoming WebSocket frame.

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

Authentication strategy enforced at WebSocket channel subscribe time.

  • 'userAuth' — requires a valid session (resolved by RequestActorResolver)
  • 'bearer' — requires a valid bearer token
  • 'none' — no auth check; any client may subscribe

Remarks: This mirrors RouteAuthConfig from entityRouteConfig.ts but applies to the WebSocket subscribe handshake rather than HTTP route handlers. The auth check runs once when the client sends the initial subscribe message; it is not re-evaluated on every incoming WebSocket frame.

Source: packages/slingshot-core/src/entityChannelConfig.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

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

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

Typed, env-validated app config — the canonical answer to “where does this setting come from and what shape must it have?”

Slingshot already has a secret-resolution layer (SecretRepository) that answers “where does this VALUE come from” (env var, AWS SSM, Vault, etc.). defineConfig answers the orthogonal question “what SHAPE does this app require, and where do those typed fields live in env.” Most apps need both; they’re complementary.

A config definition declares a namespace and a Zod schema. At app boot, the framework reads process.env (or a custom source), maps each schema field to a NAMESPACE_FIELD env var, validates the whole thing through Zod, and caches the result. After boot, anywhere in your app, cfg.get() returns the typed validated values — fail-fast at startup, not at the first request.

Source: packages/slingshot-core/src/config.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

Content format indicator. Tells the client how to parse the body field.

  • 'plain' — render as literal text, no markdown parsing, tokens still parsed
  • 'markdown' — parse as GitHub-flavored markdown, then parse tokens in text runs

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

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.ts

Unified metrics emitter contract.

Thin, dependency-free interface that prod-track packages call to record counters, gauges, and timings without coupling to a specific backend (Prometheus, OpenTelemetry, etc). Defaults to a no-op so packages can emit unconditionally and let the host application decide whether and where to collect.

This is a separate seam from the framework-owned MetricsState (Prometheus-style registry used by the built-in /metrics HTTP endpoint). Plugins should prefer this MetricsEmitter for ad-hoc operational signals; the framework metrics plugin remains the home for the request-level counters/histograms exposed at the scrape endpoint.

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

High-level event API exposed on the Slingshot context.

Wraps an EventDefinitionRegistry and a SlingshotEventBus to provide validated, envelope-wrapped event publishing with scope projection.

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

Cron scheduler registry — persists the set of BullMQ scheduler names registered by the current deployment so the next deployment can identify and remove stale schedulers.

Remarks: When a scheduled job is renamed or removed between deployments, the old BullMQ RepeatableJob must be explicitly deleted or it will keep running forever. The cron registry solves this by saving the current deployment’s scheduler names at startup, then comparing against the previous deployment’s names to find stale ones.

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

CSRF protection configuration for the auth plugin.

When enabled, the CSRF middleware verifies that the x-csrf-token header value matches the csrf_token cookie on state-changing requests (POST, PUT, PATCH, DELETE).

Remarks: CSRF protection is only meaningful for cookie-authenticated requests. Bearer token and M2M requests are inherently CSRF-safe and bypass the check. OAuth callback paths are always exempt to prevent breaking the redirect flow.

Source: packages/slingshot-core/src/csrf.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 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

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

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.

Source: packages/slingshot-core/src/crypto.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

Shared notification record / adapter / builder types used across packages.

These types form the typed surface that slingshot-notifications publishes through its Notifications package contract (@lastshotlabs/slingshot-notifications/public.ts). Cross-package consumers resolve the contract handles via ctx.capabilities.require(...) — the legacy NOTIFICATIONS_PLUGIN_STATE_KEY peer-state publish has been removed.

Source: packages/slingshot-core/src/notificationsPeer.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

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

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

Source: packages/slingshot-core/src/packageAuthoring.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

Content format indicator. Tells the client how to parse the body field.

  • 'plain' — render as literal text, no markdown parsing, tokens still parsed
  • 'markdown' — parse as GitHub-flavored markdown, then parse tokens in text runs

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

Cross-package handle to the embeds plugin’s URL unfurling capability.

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

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.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

Instance-scoped map of plugin name -> plugin-owned state.

Each plugin stores its runtime state under its own plugin name key. Values are opaque to the framework; plugins own their state shape and expose typed accessors for dependent plugins.

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

Authentication strategy enforced at WebSocket channel subscribe time.

  • 'userAuth' — requires a valid session (resolved by RequestActorResolver)
  • 'bearer' — requires a valid bearer token
  • 'none' — no auth check; any client may subscribe

Remarks: This mirrors RouteAuthConfig from entityRouteConfig.ts but applies to the WebSocket subscribe handshake rather than HTTP route handlers. The auth check runs once when the client sends the initial subscribe message; it is not re-evaluated on every incoming WebSocket frame.

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

Authentication strategy enforced at WebSocket channel subscribe time.

  • 'userAuth' — requires a valid session (resolved by RequestActorResolver)
  • 'bearer' — requires a valid bearer token
  • 'none' — no auth check; any client may subscribe

Remarks: This mirrors RouteAuthConfig from entityRouteConfig.ts but applies to the WebSocket subscribe handshake rather than HTTP route handlers. The auth check runs once when the client sends the initial subscribe message; it is not re-evaluated on every incoming WebSocket frame.

Source: packages/slingshot-core/src/entityChannelConfig.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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

General-purpose entity registry.

Plugins that need to discover entities at runtime (search indexing, schema generation, admin UIs, migration tooling, documentation) use this registry.

Created by the framework during bootstrap, exposed to plugin lifecycle hooks on SlingshotFrameworkConfig, and consumed by framework-owned services such as search indexing.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

Source: packages/slingshot-core/src/entityRouteConfig.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

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

One normalized record extracted from a trigger event.

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

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions

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

Controls how event payloads are encoded for durable transport (Kafka topics, BullMQ queues) and decoded on the consumer side.

Non-durable listeners always receive the original in-process object — the serializer is only invoked on durable produce and consume paths.

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

Union of all registered event names — the string keys of the SlingshotEventMap.

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

Registry of EventDefinitions keyed by event name, freezable once all plugins have registered their events.

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

Registry of EventDefinitions keyed by event name, freezable once all plugins have registered their events.

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

Union of all registered event names — the string keys of the SlingshotEventMap.

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

Union of all registered event names — the string keys of the SlingshotEventMap.

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

Union of all registered event names — the string keys of the SlingshotEventMap.

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

Result of validating an event payload against a registered schema.

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

Union of all registered event names — the string keys of the SlingshotEventMap.

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

Controls how event payloads are encoded for durable transport (Kafka topics, BullMQ queues) and decoded on the consumer side.

Non-durable listeners always receive the original in-process object — the serializer is only invoked on durable produce and consume paths.

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

Union of all registered event names — the string keys of the SlingshotEventMap.

Source: packages/slingshot-core/src/eventTypes.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

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

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

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

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

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

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

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

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

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

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

One normalized record extracted from a trigger event.

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

One normalized record extracted from a trigger event.

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

One normalized record extracted from a trigger event.

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

Unified metrics emitter contract.

Thin, dependency-free interface that prod-track packages call to record counters, gauges, and timings without coupling to a specific backend (Prometheus, OpenTelemetry, etc). Defaults to a no-op so packages can emit unconditionally and let the host application decide whether and where to collect.

This is a separate seam from the framework-owned MetricsState (Prometheus-style registry used by the built-in /metrics HTTP endpoint). Plugins should prefer this MetricsEmitter for ad-hoc operational signals; the framework metrics plugin remains the home for the request-level counters/histograms exposed at the scrape endpoint.

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

Public API for generating fake data from any Zod schema.

Source: packages/slingshot-core/src/faker/generateFromSchema.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

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.ts

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.ts

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions

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

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.ts

Resolve the canonical Actor from a HandlerMeta object.

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

Resolve the canonical Actor from a HandlerMeta object.

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

Resolve the canonical Actor from a HandlerMeta object.

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

Component health contract for prod-track packages.

Components (event bus adapters, queues, search clients, mailers, etc.) implement HealthCheck so the framework can aggregate per-component state into a single readiness/liveness response without each package inventing its own shape.

getHealth() is synchronous and returns the last cached state — cheap enough to call from a /health request handler. checkHealth() is the optional active probe that may perform I/O against the underlying system.

Source: packages/slingshot-core/src/observability/health.ts

Component health contract for prod-track packages.

Components (event bus adapters, queues, search clients, mailers, etc.) implement HealthCheck so the framework can aggregate per-component state into a single readiness/liveness response without each package inventing its own shape.

getHealth() is synchronous and returns the last cached state — cheap enough to call from a /health request handler. checkHealth() is the optional active probe that may perform I/O against the underlying system.

Source: packages/slingshot-core/src/observability/health.ts

Component health contract for prod-track packages.

Components (event bus adapters, queues, search clients, mailers, etc.) implement HealthCheck so the framework can aggregate per-component state into a single readiness/liveness response without each package inventing its own shape.

getHealth() is synchronous and returns the last cached state — cheap enough to call from a /health request handler. checkHealth() is the optional active probe that may perform I/O against the underlying system.

Source: packages/slingshot-core/src/observability/health.ts

Component health contract for prod-track packages.

Components (event bus adapters, queues, search clients, mailers, etc.) implement HealthCheck so the framework can aggregate per-component state into a single readiness/liveness response without each package inventing its own shape.

getHealth() is synchronous and returns the last cached state — cheap enough to call from a /health request handler. checkHealth() is the optional active probe that may perform I/O against the underlying system.

Source: packages/slingshot-core/src/observability/health.ts

Component health contract for prod-track packages.

Components (event bus adapters, queues, search clients, mailers, etc.) implement HealthCheck so the framework can aggregate per-component state into a single readiness/liveness response without each package inventing its own shape.

getHealth() is synchronous and returns the last cached state — cheap enough to call from a /health request handler. checkHealth() is the optional active probe that may perform I/O against the underlying system.

Source: packages/slingshot-core/src/observability/health.ts

Component health contract for prod-track packages.

Components (event bus adapters, queues, search clients, mailers, etc.) implement HealthCheck so the framework can aggregate per-component state into a single readiness/liveness response without each package inventing its own shape.

getHealth() is synchronous and returns the last cached state — cheap enough to call from a /health request handler. checkHealth() is the optional active probe that may perform I/O against the underlying system.

Source: packages/slingshot-core/src/observability/health.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

One normalized record extracted from a trigger event.

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

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.ts

Actor-based identity abstraction.

Decouples all framework consumers (guards, permissions, data scoping, audit, entity routes) from specific auth-provider field names by mapping them into the canonical Actor shape.

Custom auth integrations implement IdentityResolver to map their own identity representation into the canonical Actor shape.

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

Actor-based identity abstraction.

Decouples all framework consumers (guards, permissions, data scoping, audit, entity routes) from specific auth-provider field names by mapping them into the canonical Actor shape.

Custom auth integrations implement IdentityResolver to map their own identity representation into the canonical Actor shape.

Source: packages/slingshot-core/src/identity.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

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

Unified metrics emitter contract.

Thin, dependency-free interface that prod-track packages call to record counters, gauges, and timings without coupling to a specific backend (Prometheus, OpenTelemetry, etc). Defaults to a no-op so packages can emit unconditionally and let the host application decide whether and where to collect.

This is a separate seam from the framework-owned MetricsState (Prometheus-style registry used by the built-in /metrics HTTP endpoint). Plugins should prefer this MetricsEmitter for ad-hoc operational signals; the framework metrics plugin remains the home for the request-level counters/histograms exposed at the scrape endpoint.

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

One normalized record extracted from a trigger event.

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

Resolve the canonical Actor from a HandlerMeta object.

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

Health snapshot for one inbound Kafka connector.

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

Health snapshot for one inbound Kafka connector.

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

Health snapshot for one inbound Kafka connector.

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

Health snapshot for one inbound Kafka connector.

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

Health snapshot for one inbound Kafka connector.

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

The authenticated admin identity extracted from a verified admin request. Produced by AdminAccessProvider.verifyRequest() and carried through admin routes.

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

The authenticated admin identity extracted from a verified admin request. Produced by AdminAccessProvider.verifyRequest() and carried through admin routes.

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

Content format indicator. Tells the client how to parse the body field.

  • 'plain' — render as literal text, no markdown parsing, tokens still parsed
  • 'markdown' — parse as GitHub-flavored markdown, then parse tokens in text runs

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

Structured logger contract used by prod-track packages.

The interface is deliberately small: four severity methods plus child() for fields that should appear on every log line in a sub-component (request id, plugin name, queue id, etc.). Implementations must never throw — log sinks that crash the caller are worse than silent loss.

createConsoleLogger is the default — one JSON line per call written to the underlying console method matching the level. noopLogger is the test default for code paths that need a logger handle but no output.

Source: packages/slingshot-core/src/observability/logger.ts

Structured logger contract used by prod-track packages.

The interface is deliberately small: four severity methods plus child() for fields that should appear on every log line in a sub-component (request id, plugin name, queue id, etc.). Implementations must never throw — log sinks that crash the caller are worse than silent loss.

createConsoleLogger is the default — one JSON line per call written to the underlying console method matching the level. noopLogger is the test default for code paths that need a logger handle but no output.

Source: packages/slingshot-core/src/observability/logger.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

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.ts

The rendered output of an email template. Contains the final HTML body, an optional plain-text alternative, and an optional subject line.

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

The authenticated admin identity extracted from a verified admin request. Produced by AdminAccessProvider.verifyRequest() and carried through admin routes.

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

The authenticated admin identity extracted from a verified admin request. Produced by AdminAccessProvider.verifyRequest() and carried through admin routes.

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

The authenticated admin identity extracted from a verified admin request. Produced by AdminAccessProvider.verifyRequest() and carried through admin routes.

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

The authenticated admin identity extracted from a verified admin request. Produced by AdminAccessProvider.verifyRequest() and carried through admin routes.

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

Unified metrics emitter contract.

Thin, dependency-free interface that prod-track packages call to record counters, gauges, and timings without coupling to a specific backend (Prometheus, OpenTelemetry, etc). Defaults to a no-op so packages can emit unconditionally and let the host application decide whether and where to collect.

This is a separate seam from the framework-owned MetricsState (Prometheus-style registry used by the built-in /metrics HTTP endpoint). Plugins should prefer this MetricsEmitter for ad-hoc operational signals; the framework metrics plugin remains the home for the request-level counters/histograms exposed at the scrape endpoint.

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

Unified metrics emitter contract.

Thin, dependency-free interface that prod-track packages call to record counters, gauges, and timings without coupling to a specific backend (Prometheus, OpenTelemetry, etc). Defaults to a no-op so packages can emit unconditionally and let the host application decide whether and where to collect.

This is a separate seam from the framework-owned MetricsState (Prometheus-style registry used by the built-in /metrics HTTP endpoint). Plugins should prefer this MetricsEmitter for ad-hoc operational signals; the framework metrics plugin remains the home for the request-level counters/histograms exposed at the scrape endpoint.

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

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.ts

Shared notification record / adapter / builder types used across packages.

These types form the typed surface that slingshot-notifications publishes through its Notifications package contract (@lastshotlabs/slingshot-notifications/public.ts). Cross-package consumers resolve the contract handles via ctx.capabilities.require(...) — the legacy NOTIFICATIONS_PLUGIN_STATE_KEY peer-state publish has been removed.

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

Shared notification record / adapter / builder types used across packages.

These types form the typed surface that slingshot-notifications publishes through its Notifications package contract (@lastshotlabs/slingshot-notifications/public.ts). Cross-package consumers resolve the contract handles via ctx.capabilities.require(...) — the legacy NOTIFICATIONS_PLUGIN_STATE_KEY peer-state publish has been removed.

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

Shared notification record / adapter / builder types used across packages.

These types form the typed surface that slingshot-notifications publishes through its Notifications package contract (@lastshotlabs/slingshot-notifications/public.ts). Cross-package consumers resolve the contract handles via ctx.capabilities.require(...) — the legacy NOTIFICATIONS_PLUGIN_STATE_KEY peer-state publish has been removed.

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

Shared notification record / adapter / builder types used across packages.

These types form the typed surface that slingshot-notifications publishes through its Notifications package contract (@lastshotlabs/slingshot-notifications/public.ts). Cross-package consumers resolve the contract handles via ctx.capabilities.require(...) — the legacy NOTIFICATIONS_PLUGIN_STATE_KEY peer-state publish has been removed.

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

Shared notification record / adapter / builder types used across packages.

These types form the typed surface that slingshot-notifications publishes through its Notifications package contract (@lastshotlabs/slingshot-notifications/public.ts). Cross-package consumers resolve the contract handles via ctx.capabilities.require(...) — the legacy NOTIFICATIONS_PLUGIN_STATE_KEY peer-state publish has been removed.

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

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.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

One normalized record extracted from a trigger event.

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

Operation-level idempotency contract used by delivery and retry-aware packages (mail, push, notifications, orchestration) to dedup retried operations.

Remarks: This is distinct from the HTTP IdempotencyAdapter contract in ../idempotencyAdapter.ts, which is dedicated to the request-replay middleware (caching the full HTTP response for a given Idempotency-Key header).

Remarks: The contract here is keyed on a structured IdempotencyKey and stores an arbitrary payload (typed at the call site). Callers wrap their operation in withIdempotency so that retries skip work and replay the prior result.

Source: packages/slingshot-core/src/idempotency/index.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

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

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

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.ts

Content format indicator. Tells the client how to parse the body field.

  • 'plain' — render as literal text, no markdown parsing, tokens still parsed
  • 'markdown' — parse as GitHub-flavored markdown, then parse tokens in text runs

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

Content format indicator. Tells the client how to parse the body field.

  • 'plain' — render as literal text, no markdown parsing, tokens still parsed
  • 'markdown' — parse as GitHub-flavored markdown, then parse tokens in text runs

Source: packages/slingshot-core/src/content.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

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

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions

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

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions

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

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions

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

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions

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

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions

Source: packages/slingshot-core/src/permissions.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

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

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

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

Instance-scoped map of plugin name -> plugin-owned state.

Each plugin stores its runtime state under its own plugin name key. Values are opaque to the framework; plugins own their state shape and expose typed accessors for dependent plugins.

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

Writable plugin state used internally by the framework bootstrap lifecycle.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Canonical store infrastructure contract shared across all slingshot packages.

Plugin authors import these types from @lastshotlabs/slingshot-core to declare repository factories without depending on framework or auth internals.

The pattern:

  1. Declare RepoFactories<YourRepo> with one factory per StoreType
  2. Call resolveRepo(factories, storeType, infra) at startup
  3. The framework provides StoreInfra — your factory receives it

The framework bootstrap (createContextStoreInfra) injects additional capabilities onto StoreInfra via well-known Reflect symbols. Packages that need these capabilities use Reflect.get(infra, SYMBOL) — they never receive these as function arguments (CLAUDE.md Rule 16).

RESOLVE_ENTITY_FACTORIES — injected by the bootstrap with the createEntityFactories function. Allows packages/slingshot-entity to create RepoFactories<T> from entity configs without importing from the root app.

RESOLVE_COMPOSITE_FACTORIES — injected by the bootstrap with the createCompositeFactories function. Used by the composite entity path in packages/slingshot-entity to build multi-entity adapter sets.

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

Whether the Postgres runtime applies migrations on startup or assumes the schema is already migrated.

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

Whether the Postgres runtime applies migrations on startup or assumes the schema is already migrated.

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

Whether the Postgres runtime applies migrations on startup or assumes the schema is already migrated.

Source: packages/slingshot-core/src/postgresRuntime.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

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

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

Cross-package handle for resolving and updating interactive message components published by a peer package.

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

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

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

Shape of a formatted push notification: title plus optional body, data, icon, badge, and URL.

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

Shape of a formatted push notification: title plus optional body, data, icon, badge, and URL.

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

Common lifecycle contract shared by domain-specific queue implementations (mail, webhooks, etc.). Plugins reference this type in teardown and health-check code that does not need to know the domain-specific start() signature.

Each domain queue extends this interface and adds its own start(arg) overload.

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

Content format indicator. Tells the client how to parse the body field.

  • 'plain' — render as literal text, no markdown parsing, tokens still parsed
  • 'markdown' — parse as GitHub-flavored markdown, then parse tokens in text runs

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

One normalized record extracted from a trigger event.

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

One normalized record extracted from a trigger event.

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

Canonical Redis client interface used across all slingshot packages.

Concrete implementations (ioredis, Upstash Redis, etc.) satisfy this contract. Typed as an interface rather than a class so that any Redis-compatible client can be used without an adapter layer.

Remarks: getdel is optional because it is not supported by all Redis versions (requires Redis 6.2+). Framework code that calls getdel should fall back to GET+DEL when it is absent.

Source: packages/slingshot-core/src/redis.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

The rendered output of an email template. Contains the final HTML body, an optional plain-text alternative, and an optional subject line.

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

Request-scoped state — the canonical way to wire per-request resources.

A request scope is a typed handle for “something that exists for the duration of a single HTTP request and gets cleaned up at the end.” Common examples: a database transaction, a per-request HTTP client with the actor’s token, an idempotency session, a request-bound tracing helper.

The framework runs factory lazily on first getRequestScoped(c, scope) inside a request and caches the result for the rest of that request. After the handler returns (success or error), the framework calls cleanup for every scope that was actually initialized — in LIFO order, so a transaction can roll back before its underlying connection is released.

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

Request-scoped state — the canonical way to wire per-request resources.

A request scope is a typed handle for “something that exists for the duration of a single HTTP request and gets cleaned up at the end.” Common examples: a database transaction, a per-request HTTP client with the actor’s token, an idempotency session, a request-bound tracing helper.

The framework runs factory lazily on first getRequestScoped(c, scope) inside a request and caches the result for the rest of that request. After the handler returns (success or error), the framework calls cleanup for every scope that was actually initialized — in LIFO order, so a transaction can roll back before its underlying connection is released.

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

Request-scoped state — the canonical way to wire per-request resources.

A request scope is a typed handle for “something that exists for the duration of a single HTTP request and gets cleaned up at the end.” Common examples: a database transaction, a per-request HTTP client with the actor’s token, an idempotency session, a request-bound tracing helper.

The framework runs factory lazily on first getRequestScoped(c, scope) inside a request and caches the result for the rest of that request. After the handler returns (success or error), the framework calls cleanup for every scope that was actually initialized — in LIFO order, so a transaction can roll back before its underlying connection is released.

Source: packages/slingshot-core/src/requestScope.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

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

Shared notification record / adapter / builder types used across packages.

These types form the typed surface that slingshot-notifications publishes through its Notifications package contract (@lastshotlabs/slingshot-notifications/public.ts). Cross-package consumers resolve the contract handles via ctx.capabilities.require(...) — the legacy NOTIFICATIONS_PLUGIN_STATE_KEY peer-state publish has been removed.

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

Resolved store selections — which backing store each framework subsystem uses.

Defined here (not in framework internals) so plugins can reference the type without importing from the framework’s private modules.

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

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions

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

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Configuration for createRouterAdapter.

A default bus handles all events not matched by a namespace prefix. Namespace prefixes allow routing specific event families to dedicated buses (e.g., community events to a Redis-backed adapter while security events stay in-process).

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Runtime-agnostic password hashing contract.

Abstracts over Bun’s Bun.password API so that tests and alternative runtimes can substitute a faster or mock implementation without touching calling code.

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

Runtime-agnostic password hashing contract.

Abstracts over Bun’s Bun.password API so that tests and alternative runtimes can substitute a faster or mock implementation without touching calling code.

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

Runtime-agnostic password hashing contract.

Abstracts over Bun’s Bun.password API so that tests and alternative runtimes can substitute a faster or mock implementation without touching calling code.

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

Runtime-agnostic password hashing contract.

Abstracts over Bun’s Bun.password API so that tests and alternative runtimes can substitute a faster or mock implementation without touching calling code.

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

Runtime-agnostic password hashing contract.

Abstracts over Bun’s Bun.password API so that tests and alternative runtimes can substitute a faster or mock implementation without touching calling code.

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

Runtime-agnostic password hashing contract.

Abstracts over Bun’s Bun.password API so that tests and alternative runtimes can substitute a faster or mock implementation without touching calling code.

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

Runtime-agnostic password hashing contract.

Abstracts over Bun’s Bun.password API so that tests and alternative runtimes can substitute a faster or mock implementation without touching calling code.

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

Runtime-agnostic password hashing contract.

Abstracts over Bun’s Bun.password API so that tests and alternative runtimes can substitute a faster or mock implementation without touching calling code.

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

Runtime-agnostic password hashing contract.

Abstracts over Bun’s Bun.password API so that tests and alternative runtimes can substitute a faster or mock implementation without touching calling code.

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

Runtime-agnostic password hashing contract.

Abstracts over Bun’s Bun.password API so that tests and alternative runtimes can substitute a faster or mock implementation without touching calling code.

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

Runtime-agnostic password hashing contract.

Abstracts over Bun’s Bun.password API so that tests and alternative runtimes can substitute a faster or mock implementation without touching calling code.

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

Runtime-agnostic password hashing contract.

Abstracts over Bun’s Bun.password API so that tests and alternative runtimes can substitute a faster or mock implementation without touching calling code.

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

Minimal local type for the subset of the undici Dispatcher we use. We keep this local so callers do not need to import undici types directly.

Source: packages/slingshot-core/src/http/safeFetch.ts

Search plugin runtime contract.

Defines the shape stored in pluginState by slingshot-search and consumed by framework-internal code (createContextStoreInfra) for write-through sync and late entity initialization.

Lives in slingshot-core because both sides (framework and search plugin) depend on core — same rationale as SearchProviderContract.

Source: packages/slingshot-core/src/searchPluginRuntime.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

Search plugin runtime contract.

Defines the shape stored in pluginState by slingshot-search and consumed by framework-internal code (createContextStoreInfra) for write-through sync and late entity initialization.

Lives in slingshot-core because both sides (framework and search plugin) depend on core — same rationale as SearchProviderContract.

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

Minimal search provider contract for write-through document sync.

This is the minimal contract shared with slingshot-search providers. The full-featured interface (search, suggest, index management, reindex) lives in slingshot-search. Core only declares the write-side contract so the framework can sync documents without depending on the full search package.

Remarks: Kept intentionally minimal — slingshot-core stays lean for apps that don’t use search. Search plugin providers implement both this interface and the extended interface in slingshot-search.

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

Search plugin runtime contract.

Defines the shape stored in pluginState by slingshot-search and consumed by framework-internal code (createContextStoreInfra) for write-through sync and late entity initialization.

Lives in slingshot-core because both sides (framework and search plugin) depend on core — same rationale as SearchProviderContract.

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

Search plugin runtime contract.

Defines the shape stored in pluginState by slingshot-search and consumed by framework-internal code (createContextStoreInfra) for write-through sync and late entity initialization.

Lives in slingshot-core because both sides (framework and search plugin) depend on core — same rationale as SearchProviderContract.

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

Secret repository contracts — read-only abstraction for resolving credentials, API keys, and signing secrets from any backing store.

Resolved at startup BEFORE database connections are established. Implementations must be self-contained (no DB dependencies).

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

Secret repository contracts — read-only abstraction for resolving credentials, API keys, and signing secrets from any backing store.

Resolved at startup BEFORE database connections are established. Implementations must be self-contained (no DB dependencies).

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

The authenticated admin identity extracted from a verified admin request. Produced by AdminAccessProvider.verifyRequest() and carried through admin routes.

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

Signing and crypto configuration for the Slingshot framework.

Controls HMAC signing for cookies, cursors, presigned URLs, request signing, idempotency keys, and session-binding security features.

Remarks: All features are opt-in and default to false. Enable each feature when your threat model calls for it. requestSigning and sessionBinding are strongly recommended for production APIs that accept third-party callers.

Source: packages/slingshot-core/src/signing.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

High-level event API exposed on the Slingshot context.

Wraps an EventDefinitionRegistry and a SlingshotEventBus to provide validated, envelope-wrapped event publishing with scope projection.

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

Resolved store selections — which backing store each framework subsystem uses.

Defined here (not in framework internals) so plugins can reference the type without importing from the framework’s private modules.

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

Resolve the canonical Actor from a HandlerMeta object.

Source: packages/slingshot-core/src/handler.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

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

Runtime-agnostic password hashing contract.

Abstracts over Bun’s Bun.password API so that tests and alternative runtimes can substitute a faster or mock implementation without touching calling code.

Source: packages/slingshot-core/src/runtime.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

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

Canonical store infrastructure contract shared across all slingshot packages.

Plugin authors import these types from @lastshotlabs/slingshot-core to declare repository factories without depending on framework or auth internals.

The pattern:

  1. Declare RepoFactories<YourRepo> with one factory per StoreType
  2. Call resolveRepo(factories, storeType, infra) at startup
  3. The framework provides StoreInfra — your factory receives it

The framework bootstrap (createContextStoreInfra) injects additional capabilities onto StoreInfra via well-known Reflect symbols. Packages that need these capabilities use Reflect.get(infra, SYMBOL) — they never receive these as function arguments (CLAUDE.md Rule 16).

RESOLVE_ENTITY_FACTORIES — injected by the bootstrap with the createEntityFactories function. Allows packages/slingshot-entity to create RepoFactories<T> from entity configs without importing from the root app.

RESOLVE_COMPOSITE_FACTORIES — injected by the bootstrap with the createCompositeFactories function. Used by the composite entity path in packages/slingshot-entity to build multi-entity adapter sets.

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

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions

Source: packages/slingshot-core/src/permissions.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

The authenticated admin identity extracted from a verified admin request. Produced by AdminAccessProvider.verifyRequest() and carried through admin routes.

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

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.ts

Content format indicator. Tells the client how to parse the body field.

  • 'plain' — render as literal text, no markdown parsing, tokens still parsed
  • 'markdown' — parse as GitHub-flavored markdown, then parse tokens in text runs

Source: packages/slingshot-core/src/content.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

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.ts

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions

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

Unified metrics emitter contract.

Thin, dependency-free interface that prod-track packages call to record counters, gauges, and timings without coupling to a specific backend (Prometheus, OpenTelemetry, etc). Defaults to a no-op so packages can emit unconditionally and let the host application decide whether and where to collect.

This is a separate seam from the framework-owned MetricsState (Prometheus-style registry used by the built-in /metrics HTTP endpoint). Plugins should prefer this MetricsEmitter for ad-hoc operational signals; the framework metrics plugin remains the home for the request-level counters/histograms exposed at the scrape endpoint.

Source: packages/slingshot-core/src/metrics.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

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

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

One normalized record extracted from a trigger event.

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

One normalized record extracted from a trigger event.

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

One normalized record extracted from a trigger event.

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

One normalized record extracted from a trigger event.

Source: packages/slingshot-core/src/functions.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

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

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

The authenticated admin identity extracted from a verified admin request. Produced by AdminAccessProvider.verifyRequest() and carried through admin routes.

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

The authenticated admin identity extracted from a verified admin request. Produced by AdminAccessProvider.verifyRequest() and carried through admin routes.

Source: packages/slingshot-core/src/adminProvider.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

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

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

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.ts

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.ts

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

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

Content format indicator. Tells the client how to parse the body field.

  • 'plain' — render as literal text, no markdown parsing, tokens still parsed
  • 'markdown' — parse as GitHub-flavored markdown, then parse tokens in text runs

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

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.ts

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.ts

Operation-level idempotency contract used by delivery and retry-aware packages (mail, push, notifications, orchestration) to dedup retried operations.

Remarks: This is distinct from the HTTP IdempotencyAdapter contract in ../idempotencyAdapter.ts, which is dedicated to the request-replay middleware (caching the full HTTP response for a given Idempotency-Key header).

Remarks: The contract here is keyed on a structured IdempotencyKey and stores an arbitrary payload (typed at the call site). Callers wrap their operation in withIdempotency so that retries skip work and replay the prior result.

Source: packages/slingshot-core/src/idempotency/index.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

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

Shared WebSocket utility helpers.

Extracted to slingshot-core so both the framework (src/framework/lib/ws.ts) and entity packages (slingshot-entity) can import without creating a cross-layer dependency.

Source: packages/slingshot-core/src/wsHelpers.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

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

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

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

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

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

Actor-based identity abstraction.

Decouples all framework consumers (guards, permissions, data scoping, audit, entity routes) from specific auth-provider field names by mapping them into the canonical Actor shape.

Custom auth integrations implement IdentityResolver to map their own identity representation into the canonical Actor shape.

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

Resolve the canonical Actor from a HandlerMeta object.

Source: packages/slingshot-core/src/handler.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

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

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

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

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

Normalised identity profile sourced from an OAuth provider.

Populated by OAuthAdapter.findOrCreateByProvider() when a user authenticates via an external provider. All fields are optional — each provider exposes different claim sets.

Source: packages/slingshot-core/src/auth-adapter.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

Unified cache interface: implementations wrap a backing store (Redis, memory, SQLite, …) behind a consistent get/set/del API used by response caching and session storage.

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

Unified cache interface: implementations wrap a backing store (Redis, memory, SQLite, …) behind a consistent get/set/del API used by response caching and session storage.

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

Supported CAPTCHA verification providers.

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

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

Authentication strategy enforced at WebSocket channel subscribe time.

  • 'userAuth' — requires a valid session (resolved by RequestActorResolver)
  • 'bearer' — requires a valid bearer token
  • 'none' — no auth check; any client may subscribe

Remarks: This mirrors RouteAuthConfig from entityRouteConfig.ts but applies to the WebSocket subscribe handshake rather than HTTP route handlers. The auth check runs once when the client sends the initial subscribe message; it is not re-evaluated on every incoming WebSocket frame.

Source: packages/slingshot-core/src/entityChannelConfig.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

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

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

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

Typed, env-validated app config — the canonical answer to “where does this setting come from and what shape must it have?”

Slingshot already has a secret-resolution layer (SecretRepository) that answers “where does this VALUE come from” (env var, AWS SSM, Vault, etc.). defineConfig answers the orthogonal question “what SHAPE does this app require, and where do those typed fields live in env.” Most apps need both; they’re complementary.

A config definition declares a namespace and a Zod schema. At app boot, the framework reads process.env (or a custom source), maps each schema field to a NAMESPACE_FIELD env var, validates the whole thing through Zod, and caches the result. After boot, anywhere in your app, cfg.get() returns the typed validated values — fail-fast at startup, not at the first request.

Source: packages/slingshot-core/src/config.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

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

Content format indicator. Tells the client how to parse the body field.

  • 'plain' — render as literal text, no markdown parsing, tokens still parsed
  • 'markdown' — parse as GitHub-flavored markdown, then parse tokens in text runs

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

Content format indicator. Tells the client how to parse the body field.

  • 'plain' — render as literal text, no markdown parsing, tokens still parsed
  • 'markdown' — parse as GitHub-flavored markdown, then parse tokens in text runs

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

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

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

Mutable bootstrap registrar that collects auth-boundary dependencies (route auth, cache adapters, email templates, …) from plugins, then drains them into a frozen CoreRegistrarSnapshot.

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

Mutable bootstrap registrar that collects auth-boundary dependencies (route auth, cache adapters, email templates, …) from plugins, then drains them into a frozen CoreRegistrarSnapshot.

Source: packages/slingshot-core/src/coreRegistrar.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

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

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

A static email template (subject/html/text) registered by a plugin and consumed by the mail plugin.

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

Zod schema for validating an EntityChannelConfig input at runtime.

Used by plugin bootstrap and validateEntityChannelConfig to catch misconfigured WebSocket channel declarations before server startup. Enforces that channels is a non-empty record and that all sub-schemas conform to their expected shapes.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

Source: packages/slingshot-core/src/entityRouteConfig.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 for validating an EntityRouteConfig input at runtime.

Used by plugin bootstrap and the validateEntityRouteConfig helper to catch misconfigured entity route declarations early, before server startup. Pass any raw config object (from JSON, YAML, or untyped module exports) to get structured Zod errors rather than opaque runtime failures.

Remarks: Forbidden event key namespaces (security., auth:, community:delivery., push:, app:) are enforced by the inline eventKeySchema applied to every event key field across create, get, list, update, delete, and operations entries. Any event key using a forbidden prefix causes validation to fail with a descriptive Zod issue. Rate limit fields (windowMs, max) must be positive integers — zero and negative values are rejected. The retention.hardDelete.after duration string must match {positive integer}{s|m|h|d|w|y} (e.g. '90d', '1y').

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

One normalized record extracted from a trigger event.

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

Union of all registered event names — the string keys of the SlingshotEventMap.

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

Union of all registered event names — the string keys of the SlingshotEventMap.

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

Result of validating an event payload against a registered schema.

Source: packages/slingshot-core/src/eventSchemaRegistry.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

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

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

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

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

Builds a short fingerprint hash from stable HTTP request headers, used for unauthenticated bot detection and request fingerprinting.

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

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions

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

Resolve the canonical Actor from a HandlerMeta object.

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

Component health contract for prod-track packages.

Components (event bus adapters, queues, search clients, mailers, etc.) implement HealthCheck so the framework can aggregate per-component state into a single readiness/liveness response without each package inventing its own shape.

getHealth() is synchronous and returns the last cached state — cheap enough to call from a /health request handler. checkHealth() is the optional active probe that may perform I/O against the underlying system.

Source: packages/slingshot-core/src/observability/health.ts

Component health contract for prod-track packages.

Components (event bus adapters, queues, search clients, mailers, etc.) implement HealthCheck so the framework can aggregate per-component state into a single readiness/liveness response without each package inventing its own shape.

getHealth() is synchronous and returns the last cached state — cheap enough to call from a /health request handler. checkHealth() is the optional active probe that may perform I/O against the underlying system.

Source: packages/slingshot-core/src/observability/health.ts

Operation-level idempotency contract used by delivery and retry-aware packages (mail, push, notifications, orchestration) to dedup retried operations.

Remarks: This is distinct from the HTTP IdempotencyAdapter contract in ../idempotencyAdapter.ts, which is dedicated to the request-replay middleware (caching the full HTTP response for a given Idempotency-Key header).

Remarks: The contract here is keyed on a structured IdempotencyKey and stores an arbitrary payload (typed at the call site). Callers wrap their operation in withIdempotency so that retries skip work and replay the prior result.

Source: packages/slingshot-core/src/idempotency/index.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

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

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

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

Structured logger contract used by prod-track packages.

The interface is deliberately small: four severity methods plus child() for fields that should appear on every log line in a sub-component (request id, plugin name, queue id, etc.). Implementations must never throw — log sinks that crash the caller are worse than silent loss.

createConsoleLogger is the default — one JSON line per call written to the underlying console method matching the level. noopLogger is the test default for code paths that need a logger handle but no output.

Source: packages/slingshot-core/src/observability/logger.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

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Shared notification record / adapter / builder types used across packages.

These types form the typed surface that slingshot-notifications publishes through its Notifications package contract (@lastshotlabs/slingshot-notifications/public.ts). Cross-package consumers resolve the contract handles via ctx.capabilities.require(...) — the legacy NOTIFICATIONS_PLUGIN_STATE_KEY peer-state publish has been removed.

Source: packages/slingshot-core/src/notificationsPeer.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

Maturity label for a Slingshot package, driving the runtime warning emitted for non-stable packages.

Source: packages/slingshot-core/src/stability.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

Instance-scoped map of plugin name -> plugin-owned state.

Each plugin stores its runtime state under its own plugin name key. Values are opaque to the framework; plugins own their state shape and expose typed accessors for dependent plugins.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

A guard that runs after authentication, in registration order; the first to return a PostAuthGuardFailure short-circuits the request.

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

A guard that runs after authentication, in registration order; the first to return a PostAuthGuardFailure short-circuits the request.

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

Whether the Postgres runtime applies migrations on startup or assumes the schema is already migrated.

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

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

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

Shape of a formatted push notification: title plus optional body, data, icon, badge, and URL.

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

Builds a short fingerprint hash from stable HTTP request headers, used for unauthenticated bot detection and request fingerprinting.

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

Canonical store infrastructure contract shared across all slingshot packages.

Plugin authors import these types from @lastshotlabs/slingshot-core to declare repository factories without depending on framework or auth internals.

The pattern:

  1. Declare RepoFactories<YourRepo> with one factory per StoreType
  2. Call resolveRepo(factories, storeType, infra) at startup
  3. The framework provides StoreInfra — your factory receives it

The framework bootstrap (createContextStoreInfra) injects additional capabilities onto StoreInfra via well-known Reflect symbols. Packages that need these capabilities use Reflect.get(infra, SYMBOL) — they never receive these as function arguments (CLAUDE.md Rule 16).

RESOLVE_ENTITY_FACTORIES — injected by the bootstrap with the createEntityFactories function. Allows packages/slingshot-entity to create RepoFactories<T> from entity configs without importing from the root app.

RESOLVE_COMPOSITE_FACTORIES — injected by the bootstrap with the createCompositeFactories function. Used by the composite entity path in packages/slingshot-entity to build multi-entity adapter sets.

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

Resolves the connecting actor for framework WebSocket/SSE upgrades without depending on the auth plugin; registered by the auth plugin during setup.

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

Secret repository contracts — read-only abstraction for resolving credentials, API keys, and signing secrets from any backing store.

Resolved at startup BEFORE database connections are established. Implementations must be self-contained (no DB dependencies).

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

A guard that runs after authentication, in registration order; the first to return a PostAuthGuardFailure short-circuits the request.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

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

A branded route key string in the format "METHOD /path" (method always uppercased).

Constructed exclusively via routeKey() — never hand-typed — to prevent drift between the route constant definition and the shouldMountRoute runtime check.

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

Authentication strategy for a route or operation.

  • 'userAuth' — requires a valid session cookie or user token (via RouteAuthRegistry.userAuth)
  • 'bearer' — requires a bearer token (via RouteAuthRegistry.bearerAuth)
  • 'none' — publicly accessible; no auth middleware applied

Remarks: Set on EntityRouteConfig.defaults to apply the same auth strategy to all generated CRUD routes, then override individual operations (e.g. list: { auth: 'none' }) as needed. The framework wires the corresponding middleware from RouteAuthRegistry automatically — you never call the middleware factory directly.

Source: packages/slingshot-core/src/entityRouteConfig.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

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

Secret repository contracts — read-only abstraction for resolving credentials, API keys, and signing secrets from any backing store.

Resolved at startup BEFORE database connections are established. Implementations must be self-contained (no DB dependencies).

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

Secret repository contracts — read-only abstraction for resolving credentials, API keys, and signing secrets from any backing store.

Resolved at startup BEFORE database connections are established. Implementations must be self-contained (no DB dependencies).

Source: packages/slingshot-core/src/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

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

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

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

Canonical backing store type union shared across all slingshot packages.

Used as the key type in RepoFactories<T> and CacheStoreName so that a single source-of-truth controls which stores are supported. Adding a new store here propagates to auth adapters, framework persistence, and cache adapters automatically.

Remarks: 'memory' is always available without external dependencies, making it the default for development, testing, and single-process deployments.

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

The type of entity a permission grant applies to.

  • 'user' — a concrete end-user identity; subjectId is the user’s primary key as stored in the auth adapter (e.g. a UUID or nanoid)
  • 'group' — a named collection of users resolved at evaluation time via GroupResolver; subjectId is the group’s ID; grants to a group apply to all current members
  • 'service-account' — a non-human M2M client or API service identity; subjectId is the service account’s client ID or name; used for backend-to-backend trust grants that should not be confused with end-user permissions

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

Canonical store infrastructure contract shared across all slingshot packages.

Plugin authors import these types from @lastshotlabs/slingshot-core to declare repository factories without depending on framework or auth internals.

The pattern:

  1. Declare RepoFactories<YourRepo> with one factory per StoreType
  2. Call resolveRepo(factories, storeType, infra) at startup
  3. The framework provides StoreInfra — your factory receives it

The framework bootstrap (createContextStoreInfra) injects additional capabilities onto StoreInfra via well-known Reflect symbols. Packages that need these capabilities use Reflect.get(infra, SYMBOL) — they never receive these as function arguments (CLAUDE.md Rule 16).

RESOLVE_ENTITY_FACTORIES — injected by the bootstrap with the createEntityFactories function. Allows packages/slingshot-entity to create RepoFactories<T> from entity configs without importing from the root app.

RESOLVE_COMPOSITE_FACTORIES — injected by the bootstrap with the createCompositeFactories function. Used by the composite entity path in packages/slingshot-entity to build multi-entity adapter sets.

Source: packages/slingshot-core/src/storeInfra.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

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

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

// a return type that points at slingshot-core/dist/src/operations and // raises TS2742. LookupOneMethod`

Source: packages/slingshot-core/src/operations.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

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

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

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

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

Union of all registered event names — the string keys of the SlingshotEventMap.

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

Shared WebSocket utility helpers.

Extracted to slingshot-core so both the framework (src/framework/lib/ws.ts) and entity packages (slingshot-entity) can import without creating a cross-layer dependency.

Source: packages/slingshot-core/src/wsHelpers.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

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

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