Skip to content

Field Behavior

This package provides primitives for implementing AIP field behavior annotations as described by AIP-203.

Clear fields with specific field behaviors from a message. For instance, clear all OUTPUT_ONLY fields before processing a request:

import { clearFields } from "@protoutil/aip/fieldbehavior";
import { FieldBehavior } from "@buf/googleapis_googleapis.bufbuild_es/google/api/field_behavior_pb.js";
const message = create(MyMessageSchema, { ... });
const updated = clearFields(MyMessageSchema, message, [FieldBehavior.OUTPUT_ONLY]);

By default, this function returns a copy of the original message with the fields cleared. Pass { mutate: true } to mutate the original message instead.

import { getFieldBehavior, hasFieldBehavior, hasAnyFieldBehavior } from "@protoutil/aip/fieldbehavior";
// Get the field behaviors for a specific field
const behaviors = getFieldBehavior(fieldDescriptor);
// Check if a field has a specific behavior
hasFieldBehavior(fieldDescriptor, FieldBehavior.REQUIRED); // true/false
// Check if a field has any of the specified behaviors
hasAnyFieldBehavior(fieldDescriptor, [FieldBehavior.REQUIRED, FieldBehavior.IMMUTABLE]);

Validate that no immutable fields have been changed on a message:

import { validateImmutableFields } from "@protoutil/aip/fieldbehavior";
validateImmutableFields(MyMessageSchema, message); // throws if any immutable fields are set

With a field mask to only check specific fields:

import { fieldMask } from "@protoutil/core/wkt";
validateImmutableFields(MyMessageSchema, message, {
fieldMask: fieldMask(MyMessageSchema, ["immutable_field"]),
});

Similarly, validate that all required fields have been set:

import { validateRequiredFields } from "@protoutil/aip/fieldbehavior";
validateRequiredFields(MyMessageSchema, message); // throws if any required fields are missing
validateRequiredFields(MyMessageSchema, message, { fieldMask: mask }); // checks only masked fields

Build a FieldMask that excludes fields with specific behavior annotations. This is useful for constructing default read or update masks for a repository or service layer.

import { fieldMaskFromBehavior, outputOnlyMask, inputOnlyMask, immutableMask } from "@protoutil/aip/fieldbehavior";
import { FieldBehavior } from "@buf/googleapis_googleapis.bufbuild_es/google/api/field_behavior_pb.js";
// Exclude OUTPUT_ONLY fields (useful for write operations)
const writeMask = outputOnlyMask(MyMessageSchema);
// Exclude INPUT_ONLY fields (useful for read operations)
const readMask = inputOnlyMask(MyMessageSchema);
// Exclude IMMUTABLE fields (useful for update operations)
const updateMask = immutableMask(MyMessageSchema);
// Exclude multiple behaviors at once
const mask = fieldMaskFromBehavior(MyMessageSchema, [
FieldBehavior.OUTPUT_ONLY,
FieldBehavior.IMMUTABLE,
]);

The function recursively traverses nested message, repeated message, and map-of-message fields, producing dotted paths (e.g. parent.field) and wildcard paths for collections (e.g. children.*.field). A maxDepth option (default: 5) controls recursion depth and handles self-referential schemas. When the limit is reached, the parent field path is included as-is, preserving the entire subtree.

// Control recursion depth
const shallow = outputOnlyMask(MyMessageSchema, { maxDepth: 0 }); // top-level fields only
const deep = outputOnlyMask(MyMessageSchema, { maxDepth: 3 }); // recurse up to 3 levels
ExportDescription
clearFields(schema, message, behaviors, opts?)Clear fields matching the given behaviors. Options: { mutate? }. Returns a copy unless mutate is true.
fieldMaskFromBehavior(schema, exclude, opts?)Build a FieldMask excluding fields with any of the specified behaviors. Recurses into nested messages. Options: { maxDepth? }.
getFieldBehavior(field)Get the list of FieldBehavior annotations for a field descriptor.
hasFieldBehavior(field, behavior)Check if a field has a specific FieldBehavior.
hasAnyFieldBehavior(field, behaviors)Check if a field has any of the specified FieldBehavior values.
immutableMask(schema, opts?)Shorthand for fieldMaskFromBehavior(schema, [IMMUTABLE], opts).
inputOnlyMask(schema, opts?)Shorthand for fieldMaskFromBehavior(schema, [INPUT_ONLY], opts).
outputOnlyMask(schema, opts?)Shorthand for fieldMaskFromBehavior(schema, [OUTPUT_ONLY], opts).
validateImmutableFields(schema, message, opts?)Throws if any IMMUTABLE fields are set. Options: { fieldMask? }.
validateRequiredFields(schema, message, opts?)Throws if any REQUIRED fields are missing. Options: { fieldMask? }.