# Component definitions

`defineX` helpers create reusable metadata definitions. Objects, functions, and ACP agents also expose runtime operations that accept an authenticated client per call.

## Usage

```ts
import {
  RipplingSDK,
  defineCustomObject,
  defineListView,
  defineManifest,
  fields,
  eq,
} from '@rippling/rippling-sdk';

export const Ticket = defineCustomObject({
  apiName: 'ticket__c',
  name: 'Ticket',
  description: 'Support tickets',
  fields: { title__c: fields.text({ label: 'Title' }) },
});

export const Tickets = defineListView(Ticket, {
  apiName: 'all_tickets__c',
  name: 'All tickets',
  description: 'Support tickets',
  fields: [Ticket.fields.title__c],
  isPublic: true,
});

export default defineManifest({ components: [Ticket, Tickets] });

Ticket.apiName;
Ticket.fields.title__c;

const client = new RipplingSDK({ bearerToken: 'YOUR_TOKEN' });
await Ticket.records.create(client, { name: 'Bug', title__c: 'Bug' });
// Equivalent client-helper form:
await client.objects(Ticket).create({ name: 'Bug', title__c: 'Bug' });
await Ticket.records.list(client, { where: eq(Ticket.fields.title__c, 'Bug') });
await Ticket.records.query(client, { query: "(title__c == 'Bug')" });
```

Definitions retain no credentials and remain reusable manifest inputs with either call style. Constructor-based authoring remains available through `ManifestBuilder`.

- Objects expose typed `records` operations and filters such as `eq(Ticket.fields.title__c, 'Bug')`. `Ticket.fields` contains local field tokens; deployed metadata is available through `Ticket.schema.retrieve(client)` and `Ticket.schema.fields.list(client)`.
- Functions expose `fn.execute(client, input)` and `fn.executions` for listing, retrieval, and logs. `fn.handler(...)` shares the input/output contract. The client-helper form is `client.function(fn)`.
- ACP agents expose `agent.retrieve(client)` and `agent.sessions.create/list(client, ...)`. The client-helper form is `client.agent(agent)`.

Operations reuse generated request options and pagination helpers. TypeScript checks local field values and references; live use also requires backend support for API-name creation and runtime routing.

Definition-based record writes accept `null` for optional writable custom fields, including fields declared with `nullable: true`. Required and computed fields retain their write restrictions. Numeric fields and numeric formulas decode API decimal strings to JavaScript numbers, with JavaScript's usual floating-point precision.

## Composition and source files

`defineManifest` collects dependencies declared by the component's `references` hook, such as an app's pages and their rendering functions. Include other desired children as roots: listing an object does not include every view or action created for it. Raw strings and opaque JSON do not create dependencies.

`defineCanvasInstance` accepts Canvas components, actions, and variables in the backend wire format. Grid positions, event handlers, property metadata, media references, and versions are typed; property values and extra grid fields remain JSON because they vary by widget. Each component requires a nested `gridProperties.position`. Media entries must include the source `s3_bucket` and `s3_key` required for installation. For a `ripplingFunction` action, use `properties.values.functionApiName` rather than `functionId`, and pass the matching `defineFunction` definitions in `functions` (or include them as manifest roots). Use `sourceId` only to preserve the source ID of an extracted config; Canvas identity is its API name. Custom App Canvas page references are not supported by `defineApp`.

New custom objects require at least one list view, including objects included through lookup dependencies. Add their views as explicit roots.

Composition snapshots the existing `{ metadata, components }` wire format. Identical serialized keys are deduplicated; different configs with the same key are rejected. Fields and layouts use object-scoped names; native fields and sections use model-scoped names. Custom-object sections, actions, views, validations, and record-triggered flows require names unique across objects because their backend lookups or installation maps are company-scoped. Authored metadata replaces generated defaults, and base/extended objects share generated layouts.

Supply complete API names: objects, categories, nested field and section keys, actions, validations, record-triggered flows, workflows, pages, Canvas instances, custom skills, and ACP agents require `__c`. The helpers validate names rather than adding suffixes. Category string references also use the complete API name. Native sections require `__cfs`; native fields require `__cfv2`. Employee custom fields use `applicableGroupId` for an existing applicability group. Owner permissions are created by the backend when it installs the object.

Definitions with file sources require `compileManifest(manifest, readSource)` or `rippling project export` before JSON serialization. Source files are read only at compilation, unchanged; export does not bundle local imports. `fn.setCode(...)` and `agent.setSource(...)` replace file sources for subsequent manifests. Previously composed manifests keep their source snapshots.

`defineCustomSkill` emits its API name as both `api_name` and `source_skill_id`, with `identity_format_version: 2`. Skill references use that API name; legacy constructors keep their source-ID format.

## Adding a component

Keep each component’s types, construction, serialization, reference handling, and runtime operations in one module using `defineComponent`. Related components reuse the owning module’s helpers:

1. Put public input/result types first, followed by a named exported `defineX` function. Keep runtime methods explicit in public interfaces. The factory constructs its result and calls the component adapter’s `register(result, ...arguments)`; a metadata class is optional.
2. Add `references` when there are local dependencies; it defaults to none. For replaceable references, return a callback reading the current state used by serialization; see [action.ts](./action.ts).
3. Implement `serialize(definition)` to return its wire config. Existing `toDict()` methods are used automatically when no serializer is supplied. Use `compileBatch(definitions, context)` for schema expansion or merging, as in [custom-object.ts](./custom-object.ts). Return `{ config, fallback: true }` only for generated defaults that should yield to authored metadata.
4. Export the named factory and public types from [index.ts](./index.ts). Place the `defineComponent({ type, define: defineX, ... })` adapter and private serialization/reference helpers below the public API.

Use these existing implementations as references:

- [app.ts](./app.ts): minimal registration reusing a metadata class and serializer. `detachedManifestScope` keeps constructor effects separate from legacy builders.
- [object-settings.ts](./object-settings.ts): a definition without a metadata class, with its own fields and serialization.
- [function.ts](./function.ts) and [custom-object.ts](./custom-object.ts): runtime methods taking a structural authenticated context. Add any client accessor in [client.ts](../../../client.ts) by forwarding to the same operations.
- [component.ts](./component.ts): wire config types, serialized key extraction, and `sourceEntry` for deferred file loading. Add a new kind to `ComponentType` here or `ManifestComponentType` in [manifest-builder.ts](../manifest-builder.ts), and define its identifying wire fields in `MetadataConfig`. Scoped or singleton kinds also need an `entryKey` case.

Component implementations own serialization; there is no separate identity declaration. Runtime methods and client accessors are explicit implementations, not generated by `defineComponent`. New metadata kinds and operations require corresponding backend contracts.

Verify wire output, dependencies, scoped conflicts, and public types. For mutable definitions, check that subsequent manifests use current references while prior snapshots remain unchanged. Runtime checks should cover the endpoint contract and pagination where applicable.

Run `yarn test --runInBand` and `yarn lint`.
