Skip to content

Define fields and types

Add standard and user-defined TypeHandle fields to Product resources and read the validation refusals.

Use this guide when you add fields and types to resources in Workspace. Each field takes a TypeHandle, whether it is a standard type, a configured standard type or a user-defined type. Use the exact compatible CDK package pinned by your repository.

Before you start

You need a resource declared with a factory, as in Build Product resources. Each type you use must be a standard type or a value type defined in source the Product reaches. A type from another Product names its exact release.

Add fields

withField adds one field to an Entity and returns a new snapshot. Its type is a handle such as Types.text, with options such as label, required, and searchable. A reference uses Types.reference({ target: otherEntity.handle }). For two Entities that point at each other, use entityRef for the forward reference.

import { entity, Types } from '@merulan/cdk/product';

export const account = entity('example_account', { label: 'Account', pluralLabel: 'Accounts', displayField: 'name' })
  .withField('name', { type: Types.text, label: 'Name', required: true, searchable: true })
  .withField('website', { type: Types.url, label: 'Website' });

The display field must be one of the Entity's own fields. When omitted, it defaults to name, or the first displayable field if there is no name. Select the Entity and its owned fields through account.handles(); this also collects field-owned configured standard types.

Define value types

defineValueType creates a user type from a base type or named components. Standard types without required configuration are handles, such as Types.text. Configured types such as Types.orderedEnum({ options }), Types.reference({ target }) and Types.fileList({ maxCount: 20 }) return that same handle contract. Their configuration belongs to the type, not extra properties beside the field's type.

import { defineValueType, Types } from '@merulan/cdk/product';

export const contactsType = defineValueType('Contacts', {
  base: Types.json,
  constraints: {
    version: 1,
    root: { kind: 'objectArray', properties: { name: 'text', email: 'email' }, requireOneOf: ['name', 'email'] },
  },
});

A field uses this handle as .withField('contacts', { type: contactsType }). List a named reusable type once in its owning Block, alongside the Entity's handles. Importing a type module does not install it. configureValueType configures a standard type; use defineValueType to refine a user type. Portable constraints are validated by the existing native machinery, not arbitrary callbacks embedded in the declaration.

Checkpoints

The factories check their own options before compiling. A wrong option fails as soon as the module runs. merulan workspace lint then compiles the Product, and native validators decide each field type and configuration. Run merulan product build to read the lowered fields in the draft.

Success

The lint returns no diagnostics. The draft lists each Entity once, with its fields in declaration order. Each reference field points at the key of the Entity its handle names.

When it fails

A factory refusal names its code and the declaration path inside the call. A compile refusal names the path in the Product source.

retired_property

The declaration uses a retired property, such as fieldType, isRequired, or kindKey. Use the factory option, such as type or required, and drop the repeated key.

workspace.structure.obsolete_property

The lint found a retired property inside a factory call. Remove it at the reported line.

unknown_property

The factory does not accept the option. Check the spelling against the option list for that factory.

duplicate_definition

The same field name appears twice on one Entity. Keep one declaration.

Recovery

A refused declaration changes nothing on disk or in a Workspace. Correct the source at the reported path and run the lint again. A draft built before the change keeps its own digest. Build again to get a draft with the corrected fields.

Export and delegation

Use Merulan to check field types when every Workspace must read the same typed fields. Merulan retains each field's type, configuration, and display order in the release. Types may be designed in another tool. A type counts only after native validators admit it in a compiled draft. An exported release keeps the type definitions it selected, not unselected drafts.

Next steps