Atomic Payload
Plugins

@pro-laico/zap

Zod with Atomic Payload extensions: the shared schema layer that lets every @pro-laico package describe its data once and have those shapes flow into your generated Payload types.

This was created to more easily type custom JSON fields that stored values from the Atomic hook. Which culminated in a zod wrapper with utilities you can utilize across a Payload application. The main feature though is a global register that you can easily add to and pull from with type safety.

You rarely reach for @pro-laico/zap directly, since the other @pro-laico/* packages depend on it and bring it along. It's the shared schema layer underneath them: Zod, re-exported with a small registry attached as z.ap. Each package describes its data shapes once and registers them under a string ID, and those shapes flow through Payload's type generation into your project's types. You'll usually only touch it when you're building your own package on top of Atomic Payload, or registering an extra schema of your own.

@pro-laico/zap is a Tool package, a building block of the Atomic Payload stack, meant to be used alongside @pro-laico/core and the other @pro-laico/* packages (most easily via the atomic-payload template), not on its own.

Installation

pnpm add @pro-laico/zap
npm install @pro-laico/zap
yarn add @pro-laico/zap

zap bundles its own copy of Zod (pinned to zod@4.1.11) and pulls in @pro-laico/core, so there are no peer dependencies for you to install separately. If you already have another @pro-laico package, it's already in your tree.

Usage

zap is a helper, not a Payload plugin, so you don't add it to buildConfig. Instead, you import z from it (Zod, with the z.ap registry attached) and register a schema under a string ID. Other parts of your project can then look that schema, or its type, back up by ID.

import { z } from '@pro-laico/zap'

// Register a schema under a string ID
export const MySlug = z.ap.add(z.enum(['pages', 'posts']), { id: 'MySlug' })

// Look it back up elsewhere (returns the registered Zod schema)
const slug = z.ap.get('MySlug')

// Use the matching TypeScript type in an annotation
const value: z.ap.Type<'MySlug'> = 'pages'

This is exactly how the packages use it. For example, @pro-laico/styles registers the colors used by a design set:

import { z } from '@pro-laico/zap'

export const designSetColors = z.ap.add(z.array(z.object({ name: z.string(), light: z.string(), dark: z.string() })), { id: 'DesignSetColors' })

Once a schema is registered, two more helpers feed those shapes into Payload's TypeScript generation. The @pro-laico/core JSON-schema plugin spreads toJSONSchemaExtensions into Payload's typescript.schema so every registered schema becomes a generated type, and generateBlocksType describes a union of block references. The starter template wires both in:

import { jsonSchemaPlugin } from '@pro-laico/core'
import { generateBlocksType, toJSONSchemaExtensions } from '@pro-laico/zap'

export const jsonSchemaPluginConfig = jsonSchemaPlugin({
  enabled: true,
  toJSONSchemaExtensions,
  generateBlocksType,
  blocks: {
    // name → the block slugs that belong to each generated union
  },
})

After that runs, the IDs you registered with z.ap.add show up as members of the generated AtomicRegistry type, so z.ap.get(id) and z.ap.Type<id> resolve to your project's real shapes.

The registry imports server-only and is populated while your Payload config resolves on the server. Register schemas in server-side modules, and don't import z.ap into a client component.

Exports

Export

Type

zobject
Zod, re-exported with the z.ap registry attached. z.ap.add(schema, { id }) registers a schema under a string ID (and returns the same schema, so you can export it); z.ap.get(id) looks the registered schema back up; and the z.ap.Type<id> type helper resolves the ID to its generated TypeScript type. Both the default export and a named export.

Example

import { z } from '@pro-laico/zap'// Register a schema once, under a string ID, and export the schema as usual.export const DesignSetColors = z.ap.add(z.array(z.object({ name: z.string(), light: z.string(), dark: z.string() })),{ id: 'DesignSetColors' },)// Anywhere else, look it back up by ID (returns the registered Zod schema)...const colors = z.ap.get('DesignSetColors')// ...or use its generated type in an annotation:const value: z.ap.Type<'DesignSetColors'> = []

Location

@pro-laico/zap
toJSONSchemaExtensionsfunction
Turns every schema registered with z.ap.add into a generated type. It merges all registered schemas into the definitions of the JSON schema Payload builds during payload generate:types. Pass it to the core JSON-schema plugin, or spread its result into your own Payload typescript.schema function.

Parameters

args:{ jsonSchema: JSONSchema4 }The JSON schema Payload passes to each typescript.schema function.

Returns

JSONSchema4The same JSON schema with every registered schema added under definitions.

Example

import { jsonSchemaPlugin } from '@pro-laico/core'import { generateBlocksType, toJSONSchemaExtensions } from '@pro-laico/zap'// src/plugins/jsonSchema.ts: hand it to the core plugin, which wires it// into Payload's typescript.schema for you.export const jsonSchemaPluginConfig = jsonSchemaPlugin({enabled: true,toJSONSchemaExtensions,generateBlocksType,blocks: {  // name -> the block slugs that belong to each generated union},})

Location

@pro-laico/zap
generateBlocksTypefunction
Builds a single JSON-schema entry describing a union of block references. The core JSON-schema plugin calls it once per entry in its blocks map to generate a named block-union type, so you usually just hand it to that plugin alongside toJSONSchemaExtensions.

Parameters

args:{ name: string; refs: (string | undefined)[] }name is the generated type name; refs is the list of block slugs in the union (falsy entries are dropped).

Returns

Record<string, JSONSchema4>A one-key object ({ [name]: ... }) describing the union, ready to merge into a JSON schema's definitions.

Example

import { generateBlocksType } from '@pro-laico/zap'// Describe an "ActionBlocks" type as a union of these block slugs.const definitions = {...generateBlocksType({ name: 'ActionBlocks', refs: ['redirect', 'sendEmail', 'toast'] }),}// -> { ActionBlocks: { oneOf: [{ items: { oneOf: [...] } }] } }

Location

@pro-laico/zap
AtomicRegistrytype
The type that maps each registered ID to its generated shape. Resolves through the @pro-laico/core kernel to your project's generated types, falling back to Record<string, any> before they are generated.

Location

@pro-laico/zap/schema

On this page