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

@seifer-webapp-factory/organizations

v0.1.0

Published

organizations — zevende capability-module: verticale, pluggable full-stack multi-tenancy (organisaties/teams: lidmaatschap, rollen, uitnodigingen, groepen, org-context) die de kits samenbindt via één contract. Eigendom is host-gedeclareerd per resource-ty

Readme

organizations — capability module

The organizations capability module — a vertical, full-stack multi-tenancy feature: organizations / teams with membership, roles, email invitations, optional groups, and an active-org context. It pairs a frontend slice with a backend slice through one shared contract, composed from the foundation kits. It is the first module to introduce a tenant boundary as a cross-cutting concern, and the first where data ownership is host-declared per resource type rather than assumed by the module.

Design doc: ../organizations.md · Tier model + conventions: ../README.md.

What it is

| Part | Contents | |---|---| | contract/ | the seam — endpoint catalogue, DTOs (zod), error taxonomy, events, config, and the ownership/teardown catalogue (ResourceOwnershipDescriptor + the owner→legal-strategy invariant). Both halves derive types from it. | | backend/src | mechanism (pinned dep): framework-free flow services (create/invite/accept/role-change/leave/delete), a ScopeResolver over access-control ReBAC, and the teardown registry (isomorphic to the privacy provider registry). | | backend/templates | surface (materialized): NestJS controller/module, organizations/memberships/org_invitations/org_groups (+ org_relations fallback) migrations, pg stores, privacy DataProvider, config fragment, security helpers. | | frontend/src | mechanism: the typed client + Vue composables (useOrganizations / useMembers / useInvitations) + an SSR-safe active-org context. | | frontend/templates | surface: organizations.vue, organization-members.vue, organization-invitations.vue, accept-invitation.vue, nav/switcher, i18n, runtime. | | manifest.ts | facades @seifer-webapp-factory/capability-spec; backendRoutes derived from the contract. | | scaffolder/ | version-aware materializer + the requires.modules presence-check (refuses without authentication). |

The core idea — ownership is host-declared, per resource type

Ownership is not a module assumption and not a single per-integration default — it varies per resource type within one app. In zangles a delivered lesson is subject-owned (it survives the school being deleted, because the student bought it) while the roster is org-owned. The host therefore declares a ResourceOwnershipDescriptor[] (one per resource type, like user-settings' SettingDescriptor[]) and the module runs an isomorphic teardown registry over it (like the privacy DataProvider registry). The owner root constrains the legal teardown strategies (subject ⇒ never cascaded), enforced by the registry — so a subject-owned record can never be silently deleted because an org left. The same module serves zangles (mixed ownership) and a corporate LMS (everything → org → cascaded) with no fork.

Composition

  • Backend kits: access-control (ReBAC scope primitive), persistence, mailer, privacy, audit-log, http-kernel, config, i18n.
  • Frontend kits: auth, forms, http-client, data, notifications, i18n, app-kernel.
  • Requires module: authentication (subject + email via a SubjectProvider port; the single-use token for invitations). Recommended companion: authorization (its PolicyStore for member/owns relation scoping — without it the module ships an org_relations fallback + flat roles).
  • Requires-ports (host provides): SubjectProvider, SingleUseTokenService, Mailer, a pg Pool; optional relationStore (authorization's), ownershipDescriptors (app resources), auditLog, events.

Install & wire (backend)

import { OrganizationsModule } from '@seifer-webapp-factory/organizations/backend'; // materialized surface
import { ORGANIZATIONS_SCOPE_RESOLVER } from '@seifer-webapp-factory/organizations/backend';

OrganizationsModule.forRoot({
  pool,
  // from the authentication module:
  subjectProvider: { verify: (token) => authTokenService.verify(token) }, // → { subject, email }
  singleUseTokenService,                                                    // auth-kit single-use TTL token
  mailer,                                                                   // mailer-kit adapter (invitations)
  config: { roles: ['owner', 'admin', 'member'], grouping: 'none', membership: 'multi' },
  // host declares ownership per app resource type (drives teardown):
  ownershipDescriptors: [lessonDescriptor /* subject/severed */, rosterDescriptor /* org/cascaded */],
});

Run the four migrations via the persistence-kit migration runner. Register the module's DataProvider (ORGANIZATIONS_DATA_PROVIDER) in your privacy registry so subject-erasure removes the subject's memberships. Consume ORGANIZATIONS_SCOPE_RESOLVER in any tenant-aware module to scope reads to the active org.

Required host env / secrets

Invitations send email, so the module needs a working Mailer. When deploying, the target's deployer must forward the mailer's env/secrets (e.g. SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, MAIL_FROM) — otherwise a missing var arrives empty, the config-kit fails fast, and the container crash-loops before the migration gate. The module itself owns no other secrets (Postgres comes from the host pool).

Lifecycle (the proven happy path)

POST /organizations                → create (creator = first owner; writes member+owner relations)
POST /organizations/:id/invitations→ invite by email (single-use TTL token, mailed)
POST /organizations/invitations/accept → accept (verifies token + email match) → member added
PATCH /organizations/:id/members/:subjectId → change role (last-owner invariant enforced)
POST /organizations/:id/members/leave       → leave (last owner cannot)
DELETE /organizations/:id          → delete → runs the teardown registry (per-type ownership) → audited

Divergence ladder

  1. Configure — role set, grouping (none/flat/nested), membership multiplicity, implicit-org, invite TTL.
  2. Extend — register a ResourceOwnershipDescriptor for an app resource; consume ORGANIZATIONS_SCOPE_RESOLVER in another module.
  3. Materialize & edit — eject any */templates file (e.g. organization-members.vue); the mechanism keeps upgrading via semver, template upgrades become a three-way merge.
  4. Fork — replace the ReBAC scope resolver or the teardown registry (last resort).

Verification (all green)

| Gate | Result | |---|---| | Contract + manifest + backend services + frontend client/composables/pages + scaffolder + divergence proof | 74 unit/component tests | | Backend e2e (Postgres testcontainer + Nest surface) | 6 tests | | Vertical Playwright e2e (browser → Vite → proxy → Nest → Postgres, assembled with real authentication) | 1 test | | build · typecheck · typecheck:backend · openapi drift | pass |

Run: npm test (unit) · npm run test:e2e (backend, needs Docker) · npx playwright test (vertical, needs Docker + browser).

Known debt

The scaffolder core (materialize/presence/ports/errors) is thin re-exports of the shared @seifer-webapp-factory/scaffolder-core with module-named aliases (materializeOrganizationsModule …), as flagged in ../README.md. The org_relations fallback exists so the module runs without authorization; a host with authorization should inject its PolicyStore as relationStore instead.

Production defaults & scaling

OrganizationsModule.forRoot defaults the audit store to inMemoryAuditStore() — in-memory / per-process. Fine for a single node, but in a multi-node deployment the org audit trail fragments across processes and is lost on restart. Inject a durable, shared audit-log adapter via the auditLog option before running more than one node. (Org/membership rows live in Postgres, so tenant state itself is unaffected.)

Accessibility

The frontend/templates surface ships unstyled (styling delegated to the host DesignSystem). Destructive actions (remove member, delete org, leave) confirm via aria-live. The host must meet the WCAG 2.2 AA obligations in ../ACCESSIBILITY.md (contrast, visible focus, reduced motion, <main> landmark).