npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lastshotlabs/slingshot-entity

v0.0.2

Published

Config-driven entity CRUD, code generation, and search for Slingshot

Downloads

294

Readme


title: Human Guide description: Human-maintained guidance for @lastshotlabs/slingshot-entity

Human-owned documentation. This page explains what this package is for and which constraints should stay true as it evolves.

Purpose

@lastshotlabs/slingshot-entity is Slingshot's authoring and orchestration layer for config-driven data models. It turns the shared types from slingshot-core into real tools for declaring entities, generating artifacts, validating manifests, planning migrations, and assembling runtime plugins.

Package Boundaries

This package should stay strongly aligned to three ideas:

  • authoring DSLs
  • pure generation
  • runtime orchestration that is still driven by config

It should not become a dumping ground for framework internals or feature-specific business logic.

Important Invariants

  • generate() should remain pure: config in, source strings out.
  • Entity authoring APIs should stay package-author friendly and should not require callers to understand framework-private implementation details.
  • Runtime orchestration should compose core contracts instead of inventing competing abstractions.
  • Manifest and migration support should stay aligned with the same entity model rather than drifting into a second configuration universe.

The Two Big Responsibilities

DSL and generation

This package owns the ergonomic side of the platform:

  • defineEntity
  • defineOperations
  • entity(...) for package-first runtime authoring
  • builders such as field, index, relation, and op
  • code generation from those definitions

The goal is to make the declarative path easier than the hand-wired path.

Runtime orchestration

createEntityPlugin() and the routing helpers are the runtime proof that the same definitions can drive live packages, not just generated code. This is what lets packages like community express behavior through entity config, middleware references, registry-backed route events, composed extra routes, and generated executor overrides instead of bespoke route files.

The default composition path is now package-first: definePackage(...) owns composition and entity(...) is the canonical home for entity-owned runtime behavior. createEntityPlugin() stays available as the lower-level compatibility and escape-hatch surface.

The stock CRUD list route is part of that contract. For entities mounted through createEntityPlugin(), GET /{entity} accepts the same allowlisted list query params that the generated route path exposes: indexed fields, enum fields, boolean fields, the tenant field, and limit / cursor / sortDir. Runtime row scoping still wins over caller-supplied filters.

Entity runtime assembly now has a few explicit invariants:

  • entity adapters are published during setupRoutes, not delayed until setupPost
  • dependent route builders should use the published-adapter lookup helpers rather than closing over sibling adapters manually
  • generated CRUD and named-operation routes keep framework-owned shells; overrides replace executors, not route shapes
  • extra routes and generated routes share one planner, one collision check, and one specificity ordering model
  • extra routes and generated overrides can declare typed request schemas and OpenAPI response metadata
  • the manual router escape hatch still exists in plugin setupRoutes, but it should be for routes that do not fit the entity shell rather than a default workaround

Consumer Shape Hardening

Entity definitions support configurable system fields, storage field mapping, and storage conventions so consumers are not locked into first-party naming assumptions.

System Fields

systemFields on EntityConfig lets consumers rename audit and ownership fields:

defineEntity('Task', {
  fields: { ... },
  systemFields: {
    createdBy: 'author',
    updatedBy: 'lastEditor',
    ownerField: 'assignee',
    tenantField: 'workspace',
    version: 'rev',
  },
});

Storage Field Mapping

storageFields on EntityConfig lets consumers rename backend-specific fields:

defineEntity('Task', {
  fields: { ... },
  storageFields: {
    mongoPkField: 'pk',       // default: '_id'
    ttlField: 'expiresAt',    // default: '_expires_at'
    mongoTtlField: 'expiry',  // default: '_expiresAt'
  },
});

Storage Conventions

conventions on EntityConfig opens ID generation, on-update strategies, and Redis key format beyond the built-in defaults:

defineEntity('Task', {
  fields: { ... },
  conventions: {
    redisKey: ({ appName, storageName, pk }) => `${appName}/${storageName}/${pk}`,
    autoDefault: (kind) => kind === 'ulid' ? generateUlid() : undefined,
    onUpdate: (kind) => kind === 'increment' ? computeNextVersion() : undefined,
  },
});

Built-in defaults: Redis key is ${storageName}:${appName}:${pk}, auto-default handles 'uuid'/'cuid'/'now', on-update handles 'now'. Custom resolvers return undefined to fall through to the built-in handler.

Manifest Helper Field Mapping

autoGrant and activityLog manifest helpers resolve payload fields from entity metadata (_pkField, _systemFields.tenantField) instead of hardcoding id and orgId. Explicit config (resourceIdField, tenantIdField, actorIdFields) overrides resolved defaults.

Operation Registry

Policy and data-scope logic resolves operation semantics through a centralized registry instead of scattered CRUD_OPS sets and switch statements. Built-in CRUD operations are registered by default; named operations resolve through the same pipeline.

Relationship To Other Packages

  • slingshot-core owns the canonical contracts and shared type families.
  • slingshot-entity turns those contracts into authoring and orchestration tools.
  • Feature packages such as community consume the tools and provide domain-specific adapters, middleware, and side effects.

Review Heuristics

  • If a change introduces framework-private assumptions into the authoring DSL, pause and recheck the boundary.
  • If a new feature duplicates what manifests, migrations, or generation already know how to express, prefer extending the shared model instead.
  • If runtime helpers start growing domain rules, those rules probably belong in the consuming package instead.

Related Reading

  • Config-Driven Domain example - runnable entity -> operations -> plugin -> app composition in examples/config-driven-domain/
  • Config-Driven Workflow - recommended implementation order from entity through docs
  • docs/specs/completed/config-driven-packages.md
  • docs/specs/completed/community-config-rewrite.md
  • packages/slingshot-core/docs/human/index.md
  • packages/slingshot-community/docs/human/index.md