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

weegloo-codegen

v0.1.2

Published

Build-time TypeScript codegen for the Weegloo delivery APIs (CDA / ACDA). Turns ContentType definitions into typed response shapes + locale unwrap helpers.

Downloads

845

Readme

weegloo-codegen

Build-time TypeScript codegen for the Weegloo delivery APIs — Content Delivery API (CDA) and App Content Delivery API (ACDA). Turns ContentType definitions into typed response shapes.

CDA and ACDA return the same published response shape (same ?locale modes, same include map). ACDA only narrows which items a ServiceUser sees — never the shape of an item — so every generated type applies verbatim to both.

Status: experimental (0.x). API may shift between minor versions. This package emits types only — per ContentType it generates the delivery response shapes (single-locale ?locale={code} and all-locales ?locale=*) and the management read shape (CMA / ACMA, see Managed shape). Locale unwrap and include-map resolution are left to the consumer (or an SDK layer).

Install

npm install --save-dev weegloo-codegen
# or: pnpm add -D weegloo-codegen
# or: yarn add -D weegloo-codegen

Optional peer for nicer generated source formatting:

npm install --save-dev prettier   # optional — auto-detected at runtime

Two surfaces are exposed:

  • weegloo-codegen (CLI bin) — the generator.
  • weegloo-codegen/runtime — runtime primitives (Refer, Media, PublishedContentSys, SingleResponseContent, ListResponseContent, SingleResponseContentAllLocales, ListResponseContentAllLocales, ManagedContentSys, SingleManagedResponse, ListManagedResponse, ContentMetadata, IncludeEntity, FieldTypes, LocaleWrap). Generated files import type from this subpath.

CLI

npx weegloo-codegen <input.json> [options]

Arguments

| Argument | Required | Description | | --------- | -------- | ----------------------------------------------------------- | | <input> | yes | Path to ContentType JSON dump (CMA list, array, or single). |

Options

| Flag | Default | Description | | ----------------- | -------------- | ------------------------------------------------------------------------------------- | | -o, --out <dir> | ./generated | Output directory (created if absent). | | --no-barrel | barrel emitted | Skip writing index.ts that re-exports every CT module. | | --no-prettier | prettier on | Skip prettier post-processing. Required if prettier is not installed (auto-detected). | | --header <text> | — | Header comment line(s) injected at the top of every emitted file. | | --quiet | verbose | Suppress per-file wrote … lines and the trailing summary. | | -h, --help | — | Print help. |

Exit codes

  • 0 — success.
  • 1 — input read/parse failure, schema-shape error (e.g. unsupported field type), symbol-table error (name collision or invalid identifier), or --no-prettier not set while prettier is missing.

Input shapes accepted

normalizeInput accepts three shapes so a CMA dump can be piped directly without reshaping:

  1. CMA list response: { items: ContentType[] }
  2. Bare array: ContentType[]
  3. Single ContentType: ContentType

In every case the CLI normalizes to an internal ContentType[] before rendering.

Sample input → output

Input (content-types.json):

{
  "items": [
    {
      "sys": {
        "id": "ct-post",
        "type": "ContentType",
        "version": 1,
        "status": "Published"
      },
      "name": "Post",
      "publishWithAuthor": false,
      "fields": [
        {
          "id": "f1",
          "name": "Title",
          "apiName": "title",
          "type": "ShortText",
          "localized": false,
          "required": true,
          "validations": [],
          "disabled": false
        },
        {
          "id": "f2",
          "name": "Author",
          "apiName": "author",
          "type": "Refer",
          "targetType": "Content",
          "localized": false,
          "required": false,
          "validations": [
            {
              "referContentType": [
                {
                  "sys": {
                    "id": "ct-author",
                    "type": "Refer",
                    "targetType": "ContentType"
                  }
                }
              ]
            }
          ],
          "disabled": false
        }
      ]
    }
  ]
}

Emit (generated/Post.ts):

// AUTO-GENERATED by weegloo-codegen
// Source ContentType: sys.id = "ct-post", name = "Post"
// Do not edit — regenerate via `weegloo-codegen`.
//
// Response shape map:
//   X / SingleXResponse / ListXResponse           — CDA / ACDA `?locale={code}` (or omitted)
//   XAllLocales / SingleXAllLocalesResponse / …   — CDA / ACDA `?locale=*`
//   XManaged / SingleXManagedResponse / …         — CMA / ACMA content

import type {
  ContentMetadata,
  FieldTypes,
  ListManagedResponse,
  ListResponseContent,
  ListResponseContentAllLocales,
  LocaleWrap,
  ManagedContentSys,
  PublishedContentSys,
  Refer,
  SingleManagedResponse,
  SingleResponseContent,
  SingleResponseContentAllLocales,
} from "weegloo-codegen/runtime";
import type { Author } from "./Author";

// Single-locale shape (`?locale={code}` or omitted)
export interface PostFields {
  title: FieldTypes.ShortText;
  author?: Refer<"Content", Author> | undefined;
}

export interface Post {
  sys: PublishedContentSys;
  fields: PostFields;
}

export type SinglePostResponse = SingleResponseContent<Post>;
export type ListPostResponse = ListResponseContent<Post>;

// `?locale=*` shape — every field wrapped as LocaleWrap<T>
export interface PostFieldsAllLocales {
  title: LocaleWrap<FieldTypes.ShortText>;
  author?: LocaleWrap<Refer<"Content", Author>> | undefined;
}

export interface PostAllLocales {
  sys: PublishedContentSys;
  fields: PostFieldsAllLocales;
}

export type SinglePostAllLocalesResponse =
  SingleResponseContentAllLocales<PostAllLocales>;
export type ListPostAllLocalesResponse =
  ListResponseContentAllLocales<PostAllLocales>;

// Managed shape (CMA / ACMA) — sys/metadata envelope; fields reuse AllLocales
export interface PostManaged {
  sys: ManagedContentSys;
  fields: PostFieldsAllLocales;
  metadata: ContentMetadata;
}

export type SinglePostManagedResponse = SingleManagedResponse<PostManaged>;
export type ListPostManagedResponse = ListManagedResponse<PostManaged>;

Each CT file emits 11 type exports (no runtime helpers). A barrel generated/index.ts re-exports every emitted module alphabetically by TS name.

Field type mapping

| CMA field type | Emitted TS | | ------------------------------------------- | --------------------------------------- | | ShortText / LongText / RichText | FieldTypes.ShortText etc. (string) | | Long / Number | FieldTypes.Long / Number (number) | | Boolean | FieldTypes.Boolean | | Date | FieldTypes.Date (string) | | Location | FieldTypes.Location | | Json | FieldTypes.Json | | ReferMedia | Refer<"Media", Media> | | ReferContent (single-CT narrowed) | Refer<"Content", X> | | ReferContent (multi-CT narrowed) | Refer<"Content", X \| Y> | | ReferContent (no referContentType) | Refer<"Content"> (broad) | | Array of ShortText | FieldTypes.ShortTextList (string[]) | | Array of Refer → Media | Refer<"Media", Media>[] | | Array of Refer → Content (narrowed) | Refer<"Content", X \| Y>[] | | disabled: true | field skipped from Fields interface |

Required fields are emitted bare; non-required fields get ?: T | undefined (compatible with exactOptionalPropertyTypes).

apiName is used verbatim as the TS property key when it is a valid JS identifier and not a TS reserved word; otherwise it is quoted ("foo-bar", "class").

PublishedContentSys is emitted without a literal CT id generic argument — the generic defaults to string. This keeps a single generated artifact reusable across environments (dev/prod CT ids differ).

Two delivery call modes (CDA / ACDA)

Both modes apply identically to CDA (cda.weegloo.com) and ACDA (acda.weegloo.com) — the path examples below use a CDA URL, but the response shape (and therefore the generated type) is the same on ACDA.

?locale={code} (single locale)

Default mode. Main body fields and include map entities arrive as flat scalars.

import type { SinglePostResponse } from "./generated/Post";

const res = (await fetch(
  "/v1/spaces/X/content-types/Y/contents/Z?locale=en-US&include=1",
).then((r) => r.json())) as SinglePostResponse;

const title: string = res.fields.title;

?locale=* (all locales)

Main body and include entities arrive as LocaleWrap<T> per field.

import type { SinglePostAllLocalesResponse } from "./generated/Post";

const res = (await fetch(
  "/v1/spaces/X/content-types/Y/contents/Z?locale=*",
).then((r) => r.json())) as SinglePostAllLocalesResponse;

const titleByLocale = res.fields.title; // LocaleWrap<string>
const enTitle: string | undefined = titleByLocale["en-US"];

Locale unwrap (LocaleWrap<T> → T) and include-map resolution are intentionally out of codegen scope. Use an SDK layer or hand-roll them as below.

Managed shape (CMA / ACMA)

The same ContentType also has a management read shape on CMA (cma.weegloo.com) and ACMA (acma.weegloo.com). Two things make it differ from delivery:

  • Fields are always locale-wrapped. CMA/ACMA return every field as { [locale]: T } regardless of the field's localized flag — identical to delivery ?locale=*. So XManaged reuses XFieldsAllLocales for fields; there is no separate managed fields type.
  • The envelope is the authoring/draft view. sys carries the resource version (for the x-weegloo-version header), the lifecycle status (Draft / Changed / Published / Archived), and a publish block (absent until first publish). Items also carry top-level metadata.tags (Refer<"Tag">[]). The list wrapper's sys is { type: "TotalPageResponse" } (no id, unlike delivery).
import type { ListPostManagedResponse } from "./generated/Post";

const res = (await fetch("/v1/spaces/X/contents?include=1").then((r) =>
  r.json(),
)) as ListPostManagedResponse;

for (const item of res.items) {
  const status = item.sys.status; // "Draft" | "Changed" | "Published" | "Archived"
  const enTitle = item.fields.title["en-US"]; // LocaleWrap<string>
  const tagIds = item.metadata.tags.map((t) => t.sys.id);
}

CMA and ACMA content responses share this shape — XManaged applies verbatim to both. (ACMA's author targetType may be ServiceUser rather than User; the runtime type covers both.)

Resolving includes by hand

response.include is typed as Record<string, IncludeEntity[]>IncludeEntity is raw { sys: { id; type; contentType? } }. To resolve a link to its full entity, look up by sys.id and verify by sys.contentType.sys.id, then cast to the sibling CT type:

import type { IncludeEntity, Refer } from "weegloo-codegen/runtime";
import type { Author, SinglePostResponse } from "./generated";

function lookupAuthor(
  ref: Refer<"Content", Author> | undefined,
  res: SinglePostResponse,
): Author | undefined {
  if (!ref || !res.include) return undefined;
  for (const bucket of Object.values(res.include)) {
    for (const entity of bucket) {
      const e: IncludeEntity = entity;
      if (
        e.sys.id === ref.sys.id &&
        e.sys.contentType?.sys.id === "ct-author"
      ) {
        return e as unknown as Author;
      }
    }
  }
  return undefined;
}

?select= query is not represented at the type level — generated types still claim every field exists. Treat the response as Partial<...> and use optional chaining when you select a subset.

Export matrix (per CT)

Each emitted .ts file produces 11 type exports and 0 runtime helpers:

| Group | Count | Names | | -------------------- | ----- | ------------------------------------------------------------------------------------------ | | Single-locale | 4 | XFields, X, SingleXResponse, ListXResponse | | ?locale=* | 4 | XFieldsAllLocales, XAllLocales, SingleXAllLocalesResponse, ListXAllLocalesResponse | | Managed (CMA / ACMA) | 3 | XManaged, SingleXManagedResponse, ListXManagedResponse |

Strict tsconfig compatibility

Generated code is compatible with the strictest TypeScript settings:

  • strict: true
  • noUnusedLocals: true
  • noUnusedParameters: true
  • exactOptionalPropertyTypes: true

Verified against the fixture-11 sanitized real-space dump under an isolated tsconfig.

Local development

pnpm --filter weegloo-codegen build
pnpm --filter weegloo-codegen test:run
pnpm --filter weegloo-codegen type-check
pnpm --filter weegloo-codegen lint

E2E tests (src/cli.test.ts, src/consumer.type-check.test.ts) invoke pnpm build in their beforeAll so they always pick up source changes — slow but unavoidable for a binary.

License

MIT