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

@atmyapp/structure

v0.1.1

Published

[![npm version](https://badge.fury.io/js/%40atmyapp%2Fstructure.svg)](https://badge.fury.io/js/%40atmyapp%2Fstructure) [![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC) [![TypeScript](https://img.shi

Readme

AtMyApp Structure

npm version License: ISC TypeScript

Canonical schema toolkit for AtMyApp. @atmyapp/structure is the single runtime package that owns schema parsing, normalization, validation, introspection, migration planning, and legacy .structure.json compatibility.

What it solves

  • One canonical schema model for collections, documents, files, and images
  • Runtime-agnostic authoring with both TypeScript DSL and neutral JSON input
  • Shared validation across CLI, server, dashboard, and assistant flows
  • First-class system fields such as id, createdAt, updatedAt, and slug
  • First-class asset fields for associated files and images
  • First-class analytics event definitions with ordered columns
  • First-class submission definitions with typed fields and captcha config
  • Shared migration planning with actionable change prompts
  • Legacy .structure.json generation and parsing during rollout

Installation

# npm
npm install @atmyapp/structure

# yarn
yarn add @atmyapp/structure

# pnpm
pnpm add @atmyapp/structure

Rollout status

@atmyapp/structure is the canonical runtime schema package today.

  • authoring should target canonical schema inputs such as ama.schema.ts or ama.schema.json
  • runtime consumers should use compiled schema APIs from this package
  • .structure.json is still supported as the persisted/generated compatibility artifact during rollout
  • legacy labels such as jsonx and legacy markers such as __amatype are accepted only for compatibility with older projects and older CLI outputs

Core concepts

  • collection: multi-entry structured data with indexes, query semantics, and system fields
  • document: single path-backed structured file, replacing the old jsonx concept
  • asset fields: first-class associated files such as image, file, and gallery
  • systemFields: reserved fields that can be enabled and configured but not declared as normal fields
  • fields are required by default; use optional: true to make a field optional
  • description: supported at schema, definition, field, MDX config, and MDX component levels for assistant-facing context
  • events: top-level analytics/event definitions with assistant-facing descriptions and ordered columns
  • submissions: top-level form submission definitions with typed fields and optional captcha config

TypeScript DSL

import {
  compileSchema,
  defineCollection,
  defineEvent,
  defineDocument,
  defineSubmission,
  defineSchema,
  s,
} from "@atmyapp/structure";

const schema = defineSchema({
  description: "Marketing site schema",
  mdx: {
    blog: {
      description: "Blog content components",
      components: {
        Callout: {
          description: "Highlighted message block",
          props: {
            title: "string",
          },
        },
      },
    },
  },
  events: {
    page_view: defineEvent(["page", "referrer", "timestamp"], {
      description: "Tracked page view analytics event",
    }),
  },
  submissions: {
    contact: defineSubmission({
      description: "Main contact form",
      fields: {
        name: s.string(),
        email: s.email(),
        message: s.longText({
          optional: true,
        }),
        resume: s.file({
          optional: true,
        }),
      },
      captcha: {
        required: true,
        provider: "hcaptcha",
      },
    }),
  },
  definitions: {
    posts: defineCollection({
      description: "Blog posts",
      fields: {
        title: s.string({
          description: "Public title shown on the page",
        }),
        cover: s.image({
          description: "Associated hero image for the post",
          config: {
            optimizeFormat: "webp",
            maxSize: {
              width: 1920,
              height: 1080,
            },
          },
        }),
        body: s.mdx({
          config: "blog",
          description: "Long-form article body",
        }),
        author: s.reference({
          target: "authors",
          description: "Linked author profile",
          by: "slug",
        }),
        seo: s.object({
          description: "SEO metadata",
          fields: {
            title: s.string(),
            socialImages: s.array({
              optional: true,
              description: "Nested list of social preview images",
              items: s.image({
                description: "Image used for social previews",
              }),
            }),
          },
        }),
      },
      systemFields: {
        slug: {
          enabled: true,
          source: "title",
        },
      },
      indexes: ["title"],
    }),
    settings: defineDocument({
      description: "Single site settings file",
      fields: {
        theme: s.string({
          description: "Theme identifier used by the UI",
        }),
        accent: s.string({
          description: "Optional accent color",
          optional: true,
        }),
      },
    }),
  },
});

const compiled = compileSchema(schema);

defineDocument() does not need a path by default. If the key is settings, both settings and settings.json resolve to the same document automatically.

Neutral JSON input

The same canonical model can be authored without TypeScript:

{
  "version": 1,
  "description": "Marketing site schema",
  "definitions": {
    "settings": {
      "kind": "document",
      "description": "Single site settings file",
      "path": "settings.json",
      "fields": {
        "theme": {
          "kind": "scalar",
          "scalar": "string",
          "description": "Theme identifier used by the UI"
        }
      }
    }
  }
}

Main APIs

Compiler

import { compileSchema, parseSchema, normalizeSchema } from "@atmyapp/structure";
  • parseSchema(input): parses canonical JSON or legacy .structure.json
  • normalizeSchema(schema): normalizes definitions, paths, and defaults
  • compileSchema(input): builds the compiled schema with precomputed indexes

Introspection

import {
  getDefinition,
  getCollection,
  getDocument,
  getField,
  listAssetFields,
  listConfigs,
  listReferences,
  listSystemFields,
  resolveDefinitionForPath,
} from "@atmyapp/structure";

Use these helpers instead of manually traversing raw .structure objects.

Events

Analytics events are modeled as a top-level schema concern and preserved through canonical and legacy compilation:

import { defineBasicEvent, defineEvent, defineSchema } from "@atmyapp/structure";

const schema = defineSchema({
  events: {
    page_view: defineEvent(["page", "referrer", "timestamp"], {
      description: "Ordered columns for page view analytics",
    }),
    session_start: defineBasicEvent({
      description: "Marker event with dynamic payload",
    }),
  },
  definitions: {},
});

The compiled schema exposes these under compiled.events, and legacy .structure.json compatibility output preserves them under top-level events.

Submissions

Submissions are modeled as a first-class top-level schema concern:

import {
  defineSchema,
  defineSubmission,
  s,
  type InferSubmission,
} from "@atmyapp/structure";

const schema = defineSchema({
  definitions: {},
  submissions: {
    contact: defineSubmission({
      description: "Marketing contact form",
      fields: {
        name: s.string(),
        email: s.email(),
        message: s.longText({
          optional: true,
        }),
        resume: s.file({
          optional: true,
        }),
      },
      captcha: {
        required: true,
        provider: "hcaptcha",
      },
    }),
  },
});

type ContactSubmission = InferSubmission<"contact", typeof schema>;

Notes:

  • acceptingResponses is intentionally not part of the schema; that stays dashboard-managed
  • submission fields use the same field DSL as collections and documents
  • InferSubmission is input-oriented, so asset fields infer upload values rather than stored CDN URLs
  • legacy compatibility output keeps captcha settings under requiresCaptcha, captchaProvider, and hcaptchaSecret

References

References store string values by default, but the schema can describe how those strings should resolve:

s.reference({
  target: "authors",
  by: "id",
});

s.reference({
  target: "posts",
  by: "slug",
});

s.reference({
  target: "settings",
  by: "path",
});
  • target: the referenced definition name
  • by: "id": default for collection-style identity
  • by: "slug": requires the target to expose a slug
  • by: "path": useful for document-style references
  • multiple: true: stores an array of references
  • onDelete: optional lifecycle hint for downstream consumers

The current runtime value shape is a string or string array. That keeps references easy to store, easy to index, and easy to migrate while still carrying explicit semantics in the schema.

Nested types and lists

Nested objects and arrays are first-class in the canonical schema model:

const article = defineDocument({
  fields: {
    title: s.string(),
    seo: s.object({
      description: "SEO metadata",
      fields: {
        title: s.string(),
        summary: s.string({ optional: true }),
      },
    }),
    gallery: s.array({
      optional: true,
      items: s.image({
        config: {
          optimizeFormat: "webp",
        },
      }),
    }),
  },
});

Notes:

  • all nested fields are required unless marked with optional: true
  • composite fields use the object-input style: s.object({ fields, optional, description }), s.array({ items, optional, description }), s.reference({ target, ... }), and s.mdx({ config, ... })
  • s.array({ items: s.image() }) works for image lists
  • s.gallery() is still available when you want a semantic “multiple images” asset field
  • nested field descriptions are preserved in the compiled schema and legacy output

Images and asset config

Image fields support config aligned with existing @atmyapp/core and @atmyapp/cli image definitions:

s.image({
  config: {
    optimizeFormat: "webp",
    optimizeLoad: "progressive",
    ratioHint: { x: 16, y: 9 },
    maxSize: { width: 1920, height: 1080 },
  },
});

That config is preserved through canonical compilation and legacy .structure.json emission.

Validation

import {
  validateSchemaDocument,
  validateContent,
  validateContentAtPath,
} from "@atmyapp/structure";
  • validateSchemaDocument(input): validates authored schema documents
  • validateContent(compiled, definitionName, data): validates parsed content objects
  • validateContentAtPath(compiled, path, content, mimeType?): validates raw file content against the resolved definition

Migrations

import {
  diffSchemas,
  planMigration,
  renderMigrationPrompts,
} from "@atmyapp/structure";

Migration planning classifies changes as compatible, safe auto-convert, confirmable convert, or incompatible. The planner produces executable actions such as:

  • confirm_convert
  • require_union
  • backfill_generated_field
  • create_unique_index
  • drop_field
  • drop_definition

Legacy .structure.json compatibility

The package can compile existing .structure.json files and also emit legacy-compatible output:

import { compileSchema, toLegacyStructure } from "@atmyapp/structure";

const compiled = compileSchema(existingStructureJson);
const legacy = toLegacyStructure(compiled.document);

This keeps old projects working while runtime consumers migrate to the canonical schema model.

Development

npm ci
npm test
npm run build
npm pack --dry-run

Publishing

The repository includes manual release workflows similar to the @atmyapp/core package:

  • run tests before release
  • verify the version was bumped
  • build the package
  • create a GitHub release
  • publish to npm with provenance

Before publishing:

  1. Update the version in package.json.
  2. Run npm test.
  3. Run npm run build.
  4. Run npm pack --dry-run.
  5. Trigger the GitHub release workflow.

License

ISC