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

@duffcloudservices/site-forms

v0.4.2

Published

Shared <DcsForm/> runtime for DCS customer sites — renders managed form definitions from .dcs/forms/<formId>.yaml

Readme

@duffcloudservices/site-forms

Shared <DcsForm/> runtime for DCS customer sites. Renders a managed form definition (created in the portal Form Manager) from a build-time .dcs/forms/<formId>.yaml snapshot, validates user input, and posts submissions to the public site-forms API.

This is the single import surface for managed-form runtime code on customer sites — do not redefine field components per site.

Inside the dcs-again workspace the package is also reachable as @duffcloudservices/site-forms via workspace:* (the workspace name and the published name are the same). In sibling customer-site repos install the published package from public npm — see PUBLISHING.md for the consumption story and registry setup.

Install

In a workspace app (inside dcs-again)

// portal/package.json
{
  "dependencies": {
    "@duffcloudservices/site-forms": "workspace:*"
  }
}

Then pnpm install from the repo root.

In a sibling customer-site repo (e.g. ktbraunlaw, kept)

pnpm add @duffcloudservices/site-forms

See PUBLISHING.md for the registry, version, and release-workflow details.

Vite setup

Three pieces are required in the consuming site:

  1. vite-plugin-yaml so YAML modules return parsed objects:

    // vite.config.ts
    import yaml from '@modyfi/vite-plugin-yaml'
    
    export default defineConfig({
      plugins: [vue(), yaml()],
    })

    Without it, the loader falls back to parsing raw strings via js-yaml, which works but pays the parse cost at boot.

  2. A formsModules loader in your site that does the import.meta.glob from a path Vite can resolve (see below).

  3. Env vars the runtime reads:

    | Variable | Purpose | | ----------------------- | ---------------------------------------------------- | | VITE_DCS_PUBLIC_API | Base URL of the DCS public API (no trailing slash). | | VITE_DCS_SITE_SLUG | Default site slug used when the prop is omitted. |

Why the formsModules prop is required in real sites

<DcsForm/> ships with an internal import.meta.glob('/.dcs/forms/*.yaml') fallback, but Vite resolves the leading / against the consumer's Vite project root (the directory containing vite.config.ts). On every customer-site repo today, the .dcs/forms/ directory lives at the repo root, one or more levels above the Vite root (typically site/ or docs/). The internal glob therefore matches nothing and you get:

[@duffcloudservices/site-forms] No form definition found for "contact".
Expected a YAML at /.dcs/forms/contact.yaml.

The fix is a one-file loader the rest of your site imports from.

Vue SPA (vite.config.ts in site/)

// site/src/dcs-forms.ts
const modules = import.meta.glob('../../.dcs/forms/*.yaml', {
  eager: true,
  import: 'default',
})
export const dcsFormsModules: Record<string, unknown> = modules

VitePress (vite block in docs/.vitepress/config.ts)

// docs/.vitepress/dcs-forms-loader.ts
const modules = import.meta.glob('../../.dcs/forms/*.yaml', {
  eager: true,
  import: 'default',
})
export const dcsFormsModules: Record<string, unknown> = modules

The relative depth (../../) depends on where the loader file lives relative to the repo root. Adjust as needed.

Usage

Place a YAML file at <site>/.dcs/forms/contact.yaml:

formId: contact
submission:
  kind: lead
fields:
  - id: name
    type: text
    label: Name
    required: true
  - id: email
    type: email
    label: Email
    required: true
  - id: message
    type: textarea
    label: Message
    required: true

Then in any page component:

<script setup lang="ts">
import { DcsForm } from '@duffcloudservices/site-forms'
import { dcsFormsModules } from '@/dcs-forms'
</script>

<template>
  <DcsForm
    form-id="contact"
    :forms-modules="dcsFormsModules"
    @submit-success="onSuccess"
    @submit-error="onError"
  />
</template>

Props

| Prop | Type | Default | Notes | | -------------------- | --------------------------------- | -------------------------------------- | ----------------------------------------------------------- | | formId | string (required) | — | Matches .dcs/forms/<formId>.yaml. | | siteSlug | string | import.meta.env.VITE_DCS_SITE_SLUG | Path segment in the submission URL. | | definitionOverride | PortalFormDefinition | — | Used by the portal preview iframe to show in-flight edits. | | apiBase | string | import.meta.env.VITE_DCS_PUBLIC_API | Override for tests / non-prod environments. | | captchaToken | string | — | Attached to the submission payload when set. | | formsModules | Record<string, unknown> | internal fallback glob (rarely matches) | Required in real sites. Pass a Record<string, unknown> from your own import.meta.glob('../../.dcs/forms/*.yaml', { eager: true, import: 'default' }) — see Vite setup section. |

Emits

| Event | Payload | When | | ------------------ | ---------------------- | ------------------------------------------- | | submit-success | DcsFormSubmitSuccess | API responded 2xx. | | submit-error | DcsFormSubmitError | Network or non-2xx response after retries. | | validation-error | FormErrors | Submit attempted with invalid required/regex/etc. fields. |

Slots

Every slot exposes scoped data so consumers (KT Braun, Kept) can swap shadcn primitives in without forking field components.

| Slot | Scope | Default | | ---------- | ------------------------------------------------------------------ | ---------------------------------------------------- | | header | { definition } | Empty; page/section headings live outside the managed form | | progress | { current, total, step } | Step N of M — Title (multi-step only) | | actions | { isFirstStep, isLastStep, submitting, prev, next } | Plain <button> elements | | success | { definition } | definition.successMessage | | missing | { formId } | Friendly fallback when the YAML can't be found |

Per-field components (DcsFormText etc.) expose #input slots so a shadcn site can replace the underlying primitive while keeping the wrapper, label, help, and error-message structure.

Composables

For sites that want a fully custom layout, drop <DcsForm/> and use the underlying composables directly:

import {
  useDcsForm,
  validateForm,
  submitFormValues,
  parseFormYaml,
} from '@duffcloudservices/site-forms'
  • useDcsForm({ definition }) — reactive values, errors, steps, next, prev, validateAll, collectSubmissionValues, etc.
  • validateForm(def, values, fieldIds?) — pure validator usable in any setting (server-side, tests, custom adapters).
  • submitFormValues({ apiBase, siteSlug, payload }) — one-shot POST with a single retry on 5xx and multipart/form-data when files are present.

File fields emit a single File by default. When attachmentPolicy.maxFiles is greater than 1, the file field emits File[], enables multiple selection, previews selected files, and allows removing files before submit.

Visual editor integration

The form root carries data-form-key="<formId>" and every field wrapper carries data-form-field-key="<fieldId>". The portal preview iframe bridge uses these to discover managed forms, show the preview affordance, and route preview click / context-menu actions into the same portal FormManagerSheet. Do not strip these attributes in custom layouts.

definitionOverride is a preview-only draft path for the iframe. The durable form truth still lives in the form definition saved by the portal and in the committed .dcs/forms/<formId>.yaml snapshot consumed by the site runtime.

For the cross-package first-party component contract (runtime markers, bridge discovery, portal entry points, rollout, validation), see ../FIRST-PARTY-COMPONENTS.md.

Schema validation

In dev (import.meta.env.DEV === true) the runtime validates each loaded definition against the JSON Schema bundled in src/schema/form-definition.schema.json (snapshot of contracts/dist/form-definition.schema.json) and logs failures via console.warn. Production builds skip the warning to avoid noisy end-user consoles.

When the contracts schema is regenerated (pnpm --filter @dcs/contracts generate), refresh the snapshot:

Copy-Item ../../contracts/dist/form-definition.schema.json ./src/schema/form-definition.schema.json -Force
pnpm --filter @duffcloudservices/site-forms test --run

Scripts

pnpm --filter @duffcloudservices/site-forms build       # vite library build (esm + dts)
pnpm --filter @duffcloudservices/site-forms test        # vitest --run
pnpm --filter @duffcloudservices/site-forms type-check  # vue-tsc --noEmit

Compliance requirements for form submissions

Sensitive forms (isSensitive: true)

Forms flagged as sensitive (law firm intake, HIPAA intake, any privilege-sensitive questionnaire) carry platform-enforced rules that form authors and site integrators must not override:

  1. Notification emails suppress submission content. When isSensitive: true, the portal notification email for a new submission contains only a portal link — never field values, names, or any submission body. This is required for attorney-client privilege (ABA Rule 1.6) and HIPAA confidentiality.

  2. Access audit logging. Every time a portal user opens a sensitive-form submission, the platform writes an audit log entry (PortalAuditLog, event: submission_viewed). This cannot be disabled.

  3. Set via portal, not YAML. The isSensitive flag lives in the PortalSiteForms table (managed via the portal Form Manager). It is not part of the .dcs/forms/*.yaml snapshot — the runtime itself has no concept of sensitivity.

Form version tracking

Every submission row stores formVersion (the schema version of the form at submission time). If a form's fields change after submissions are collected, old submissions remain interpretable: the portal can reconstruct what was shown by looking up the version-keyed schema. This is required for compliance audit trails.

General form requirements

All DCS-managed forms that collect personal data must:

  • Link a Privacy Policy URL in proximity to the submit action
  • Store formVersionId, submission timestamp, and submitter IP with every submission row (handled automatically by the platform)
  • Not collect unnecessary data fields (data minimization)

Intake questionnaires (attorneys, healthcare)

Use the Legal Intake — Standard form template (created via the portal Form Manager → Templates) when building intake forms for law firms. This template:

  • Pre-populates the attorney-client privilege disclaimer and consent checkbox (non-removable)
  • Automatically sets isSensitive: true
  • Includes matter-type and adverse-parties fields for conflict screening

For healthcare intake, mark the form as FormKind: "hipaa" so the submission handler applies PHI-aware redaction for notification emails.


Related docs

  • Authoring guide.docs/forms/AUTHORING.md covers the YAML schema, worked examples, validation flow, HIPAA guardrails, and the hand-coded → <DcsForm/> migration recipe.
  • PublishingPUBLISHING.md covers the registry, OIDC trusted publishing, version bump policy, and the exact dep line sibling customer-site repos should add.
  • First-party visual-editor contract../FIRST-PARTY-COMPONENTS.md captures the shared adaptation model used by forms and future component families.
  • Validation CLIcli/forms/README.md documents dcs forms validate and dcs forms doctor, which lint the .dcs/forms/*.yaml files in a customer-site repo.

Ownership

Per packages/README.md: external/consumer-facing docs live here, not in repo-root docs. Cross-cutting details (e.g. the public submissions API contract) belong in contracts/README.md.