Libertas TypeScript Developer's Manual
Development status
TypeScript support is under development and is not release ready. This manual and its examples describe the development contract; they do not certify a released TypeScript SDK or a production-ready App path. A matching SDK is still required for compilation and the target Hub's runtime ABI.
This manual provides a quick reference for writing TypeScript code for the Libertas Platform. Libertas is fundamentally about self-describing software. By writing standard TypeScript, the Libertas CLI and IDE automatically extract your code's documentation, structure, and decorators to understand its capabilities. A robust user interface is just one of the many great features automatically generated from this self-describing architecture, alongside API layers and Agent integrations.
Source code is the ground truth
Libertas relies exclusively on information extracted directly from your TypeScript definitions. You do not need to configure any external JSON schemas or metadata files. The parser follows TypeScript compiler symbols from the entry module. In an ordinary package, exported non-ignored function declarations in that entry module are the roots and only the minimum transitive type closure from their arguments and reachable schema-reference decorators is published. In a TypesOnly package, every public package-owned named type exported by the entry module is automatically a root unless ignored.
The TypeScript parser is authoritative for TypeScript annotation syntax, compiler-symbol resolution, source traversal, and extraction. Libertas Studio's shared schema validator is authoritative for semantic rules decidable from the emitted parser result and its materialized dependencies; another language parser must not duplicate those rules.
The parser records external package-qualified references and emits an empty
intermediate AppDependencies list. Studio assigns required exact Version
pins during the dependency-first deployment workflow.
Application source must never predict or manually maintain these pins.
For TypeScript SDK behavior, the public comments in
libertas-ts/index.d.ts are the sole ground
truth. This manual is a non-normative usage summary. If it, an example, a test,
or another document disagrees with those SDK comments, follow the SDK comments.
The 64-bit SDK (0.2.0) represents device, endpoint, LAN-device, user, and peer
identities as bigint aliases in the signed 64-bit range. Keep those values and
collection keys as bigint; converting through number loses precision above
2^53. Preserve negative runtime identities, including opaque peer handles;
their lifetime follows the system identity rules.
Use 0n only where an API defines a system context. Interaction transaction
IDs remain numbers. Rebuild Apps against this SDK for the matching Hub runtime.
The older 0.1.x SDK uses the earlier identity ABI and is not a substitute for the matching 64-bit SDK on a modern Hub. Confirm that the required SDK is available and installed before implementing runtime calls. If its public distribution is unavailable, report that prerequisite; do not use a private platform checkout, invent declarations, or silently downgrade the SDK.
Project configuration baseline
Preserve an existing project's SDK and compiler pins. For a new ESM App, this
is the supported strict baseline; the types entry makes the SDK's ambient
Libertas declarations available without invented imports:
{
"compilerOptions": {
"module": "nodenext",
"target": "esnext",
"lib": ["esnext"],
"types": ["@libertas-ai/types"],
"strict": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"skipLibCheck": true
},
"include": ["src/**/*.ts"]
}
Run the project's build/typecheck or at least tsc --noEmit before schema
generation.
Documents and comments
Libertas uses specific comment styles to extract localized documentation for your components, ensuring regular code comments don't accidentally leak into the user interface.
Every authored schema-visible package, function, named type, parameter, field, Enumeration member, Union variant, and both halves of a List must follow the schema documentation invariants: provide a non-empty natural label and description, explain meaning in ordinary domain language, and keep implementation vocabulary out of localized text. For every public function, additionally follow the developer function-comment invariants: state the purpose first, cover every user-relevant capability, use active voice, and name who performs each action.
-
Package Documentation (
///):- The triple-slash `///` comments define the **Package-level** documentation.
- They should only be placed at the very top of your entry file (e.g., `index.ts`).
-
Component Documentation (
/** ... */):- Standard JSDoc block comments (`/** ... */` or `/* ... */`) are used for documenting types, interfaces, classes, enums, and properties.
- **Short Document (First Line)**: The very first line of the comment is extracted as the **Short Document**, which is used as the Short Name or Display Name in the UI.
- **Long Document (Remaining Lines)**: Any remaining lines after the first line are extracted as the **Long Document**, which serves as the detailed Description field in the UI or LLM tool call description. Long descriptions use HTML-style flow: one logical CR/LF break becomes exactly one space, while two or more logical breaks become one LF paragraph break. CRLF counts as one break, so leave a blank JSDoc line only when a paragraph break is intended.
- **Enums & Union Variants**: Documenting an underlying type or enum member will correctly attach that documentation to the variant within the discriminated union.
-
Custom Document Properties (
[PropertyName]):- After the short and long documents, place a consumer-defined marker on its own line. Text beginning on the next line is the property value and continues until the next marker or the end of that comment section. Unknown properties are preserved but have no platform behavior; use one only when its consumer contract defines it.
- Multiple properties are supported. They are emitted as string keys directly beside
DisplayNameandDescriptionin document JSON. DisplayName,Description,List,StringResources,Strings,Enum, andDocumentVersionare reserved.Stringsis prohibited and is not a document field. A structural key at that location, such asName,Type,Locale, orTypes, is also unavailable.- With an array
----divider, properties above the divider belong to the array and properties below it belong to each element. - Display names and descriptions are always present in generated documents. The JSON structure permits an empty value only so generated, legacy, or incomplete nodes can be represented; a codebot-authored schema node must always provide both a non-empty natural label and description. Every document uses format version
0.0.2and one top-level packageStringResourcesmap. Per-function/per-typeStringsfields do not exist. [DefaultText]supplies localized initial text for an effectiveStringor Endpoint server field, or the initial task name on a function. Omit it when it exactly matches the ordinary localized fallback.[NullText]and[SetText]label the null and value branches of a nullable field's Nullable choice; an editable Nullable must declare[NullText], and[SetText]where a value answer is drawn (a Number, String, Bytes, date or time, Table or List; None and Specify are only a client fallback). Standard values are non-empty lines.-
Put every ordinary label/description line and extension block before the
first
@Libertas...tag. Document extraction stops at that tag. - The complete user-language, standard-extension, reserved-name, List-side, and translation contract is in the schema documentation invariants.
-
Code Comments (
//):- Standard double-slash `//` comments are **completely ignored** by the schema generator. Use them freely for internal code documentation.
/// Garden irrigation
/// Helps people schedule and manage watering for their planted areas.
/**
* Configure watering
* The watering controller follows the user's schedule and lets the user adjust
* each area's watering. Developed by Example Developer (example_developer).
* [DefaultText]
* Back garden watering
*/
export function configure(
/**
* Watering choices
* The user chooses the schedule and other settings for this watering plan.
*/
configuration: Configuration
): void {}
/**
* Watering settings
* Describes the choices used to schedule watering for planted areas.
*/
export interface Configuration {
/**
* Schedule name
* The name people use to recognize this watering schedule.
* [DefaultText]
* Weekday schedule
*/
scheduleName: string;
}
/**
* Controller settings
* Describes how the watering controller is identified for this watering plan.
* @LibertasExport
*/
export interface MyDeviceConfig {
// This is an internal code comment and will NOT appear in the UI
/**
* Controller code
* The user enters the code assigned to the watering controller.
*/
id: string;
}
Replace the example Plaza identity before publication. TypeScript parameter
documentation may come from @param text or the parameter declaration; keep
them semantically identical when both exist. Union variant text can come from a
discriminator member, branch, or payload type and must never conflict across
those sources.
The exhaustive current source-tag catalog is the schema annotation inventory. Do not infer an authoring tag from a generated schema attribute or a parser-internal alias.
Unless a block begins with package-level /// text, its declarations are an
excerpt to place inside an entry module that already has the required package
label and description. Such declaration excerpts remain copyable when their
referenced types are declared and documented elsewhere. A deliberately
incomplete grammar or precedence illustration is explicitly labeled
non-copyable.
Core Development Concepts
- **Global Types**: All TypeScript projects must reference `@libertas-ai/types` as global types.
- **Standard TypeScript**: Write standard TypeScript. Your code must compile successfully (`tsc --noEmit` should pass) before the schema can be generated.
- **Entry Point**: The schema generator always starts scanning at `index.ts`.
- Exported Types: The schema includes all exported functions and any types their arguments reference (directly or indirectly).
The Libertas Type System
Your TypeScript coding style directly defines the schema of your software. Because Libertas software is self-describing, the types you choose dictate how the system understands your data, which in turn determines the resulting UI representation and API boundaries. You can use standard TypeScript features combined with Libertas native types to organize your data intuitively.
Native Global Types
When defining properties and function parameters, use the native Libertas types
provided by the @libertas-ai/types package to ensure your schemas map correctly
to the platform's features:
- **`LibertasEndpoint`**: Represents a protocol endpoint for interactions with humans, AI, and other software.
- **`LibertasDateTime`**: A timestamp (Unix epoch/seconds). In the UI, this automatically displays in the user's local timezone and parsed back to UTC on save.
- **`LibertasTimeOnly`**: Provides a 24-hour time selector in the UI.
- **`Uint8Array`**: Represents binary data/bytes, mapping directly to the platform built-in type `"Bytes"`.
- **`LibertasUser`**: Presents a system user or user group picker. Use explicit [User and group defaults](../schema.md#user-and-group-defaults) for initial suggestions; the picker does not infer the current user.
- **`LibertasDevice`**: Presents a physical or logical device picker.
- **`LibertasVirtualDevice`**: Presents a software-emulated virtual device picker.
- **`LibertasLanDevice`**: Presents a WiFi or Ethernet network device picker.
For a MIME-aware Uint8Array field, write
/** @LibertasMimeTypes("image/png,image/jpeg") */ above the declaration.
The parser emits MimeTypes; Studio validates its filter and effective Bytes
host. The MIME-aware Bytes contract owns LMB1
encoding, empty values, and client presentation. A TypeScript App emits the
LMB1 byte sequence as its ordinary Uint8Array value; an empty Uint8Array
is the valid media-undefined value. Keep unannotated Uint8Array fields for
raw binary protocols.
Matter Status Values
The TypeScript SDK represents a Libertas Matter client status as an unsigned 32-bit number:
The following expression is ordinary runtime code, not a schema declaration, so it creates no localized document node.
const packedStatus = ((clusterStatus ?? 0) << 8) | interactionModelStatus;
Bits 0-7 contain the Matter interaction-model status, bits 8-15 contain the
optional cluster-specific status, and bits 16-31 contain a Libertas Hub status.
A zero cluster byte means that no cluster-specific status is present.
LibertasMatterHubStatus.Superseded is 0x00010000. It rejects a queued
command replaced by a later command, even when their payloads are identical;
the retained caller is notified only after the surviving interaction completes.
Only the surviving command receives its own success or error result.
For writes, it reports a requested attribute value differing from the value
finally written by the pooled Matter interaction. See the
Matter status contract.
This encoding is used by LibertasIdStatus and numeric LibertasCommandRsp
and LibertasDeviceRsp values received by a Matter client.
A Matter virtual device may return only the standard low 16 bits. Therefore a
plain Matter.Status value can be passed directly when no cluster status is
needed; a virtual device must never return LibertasMatterHubStatus values.
The TypeScript runtime rejects a virtual-device status with nonzero upper bits.
Decode a returned status with packedStatus & 0xff for the interaction-model
status and (packedStatus >>> 8) & 0xff for the cluster status.
Decode the Hub field with packedStatus >>> 16. See the
Libertas Matter client status contract for supersession,
completion timing, and error-override details.
Arrays & Interfaces
Interface & Class Composition
You can build reusable models using standard TypeScript extends clauses for classes and interfaces. The platform will deeply explore all inheritance chains and automatically resolve all properties from the parent base classes into your component's definition. Properties, attributes, and comments from base interfaces are automatically inherited and displayed as part of the child object in the UI. The child can selectively override any inherited property definition or comment by re-declaring it.
Array Documentation Dividers
When documenting a T[] or Array<T>, you often need to provide instructions for the list as a whole, as well as instructions for a single item. Libertas supports Array Divider Handling using four hyphens (----):
/// @LibertasStringResources(ValidationMessages)
const ValidationMessages = {
NONNEGATIVE: "Enter zero or more.",
PERCENTAGE_MAXIMUM: "Enter a percentage no greater than 100.",
INVALID_RANGE: "The lower limit cannot exceed the upper limit."
} as const;
/**
* Watering settings
* Describes the planted areas included in one watering plan.
*/
export interface Config {
/**
* Watering zones
* Lists the planted areas included in this watering plan, in watering order.
* ----
* Watering zone
* Describes one planted area and the valve that supplies its water.
* @LibertasSize(min=1)
*/
zones: SprinklerZone[];
}
- UI Presentation: The UI uses the text above the divider to label the array container, and uses the text below the divider to label each individual item within the list.
Enumerations
To represent a closed set of numeric constant choices, you have three options. All of them resolve identically to a simple dropdown or selection list in the user interface. Note: All enumeration variant values must be numeric.
- Standard TypeScript Enum: Use a standard TypeScript
enum. - **Const Object Enum**: Use a read-only `as const` object. Because TypeScript separates its value and type spaces, you must also export a corresponding type alias to use the object's values as a type annotation.
- **Const Enum File**: Place a `@LibertasConstEnum` JSDoc tag at the very top of a file. Every exported numeric `const` variable declaration within that file will automatically be parsed as an independent choice for that enumeration. This is particularly useful for aggregating sets of disparate constants into a single logical Enum type.
An enum member can carry /** @LibertasProtocolVariant("EditWages") */ to declare an enum/protocol association. Put the comment on the standard enum member, const-object property, or exported constant statement, respectively.
Code examples
The first two excerpts are alternative declarations in an already documented
package. The third starts a separate error_codes.ts file; it appears beside
the alternatives only for comparison.
// 1. Standard TypeScript Enum
/**
* Power state
* Describes whether equipment is supplying power.
*/
export enum PowerState {
/**
* Powered on
* The equipment is supplying power and can operate.
*/
On = 1,
/**
* Powered off
* The equipment is not supplying power.
*/
Off = 0,
}
// 2. Const Object Enum
/**
* Fan speed
* Describes the airflow level selected for a fan.
*/
export const FanSpeed = {
/**
* Low airflow
* The fan runs at its lowest available airflow level.
*/
Low: 1,
/**
* Medium airflow
* The fan runs at its middle available airflow level.
*/
Medium: 2,
/**
* High airflow
* The fan runs at its highest available airflow level.
*/
High: 3,
} as const;
/**
* Fan speed
* Describes the airflow level selected for a fan.
*/
export type FanSpeed = typeof FanSpeed[keyof typeof FanSpeed];
// 3. Const Enum File (e.g., error_codes.ts)
/**
* Connection problems
* Describes why an attempted account connection could not be completed.
* @LibertasConstEnum
*/
/**
* Network unavailable
* The application could not reach the remote service.
*/
export const ErrorNetwork = 100;
/**
* Connection timed out
* The remote service did not answer within the allowed time.
*/
export const ErrorTimeout = 101;
/**
* Sign-in rejected
* The remote service did not accept the supplied account authorization.
*/
export const ErrorAuth = 102;
Keep error_codes.ts dedicated to this Enumeration and declare exactly one
exported numeric member per statement so every member has its own JSDoc. The
App entry module imports that file as a namespace and exposes the schema type
through its own documented alias:
import * as ConnectionProblemValues from "./error_codes";
/**
* Connection problems
* Describes why an attempted account connection could not be completed.
*/
export type ConnectionProblem = typeof ConnectionProblemValues;
Discriminated Unions (Union Types)
Unlike Rust where union types are declared as enums with payloads, in TypeScript you use Discriminated Unions. Note: The discriminator field used to distinguish between variants must be the variant's first field and must have a numeric type: a member of a numeric TS enum or const object enum, or a numeric literal. A string discriminator is rejected.
When modeling discriminated unions for protocol or data schemas, you can define variants as either separate named interfaces or anonymous inline type literals. The UI handles anonymous inline literals { type, payload } seamlessly, providing the exact same input form as if you had used a named interface, recursively flattening fields directly under the variant.
The parser intentionally suppresses document text for the TypeScript discriminator field itself. This compiler-oriented field is the sole ordinary exception to the all-node documentation invariant. Document the observable variant through its enum member, Union branch, or payload type instead.
Optional Types
Optional modifiers (?) and nullable unions (| null or | undefined) declare meaningful
absence. An editor presents an editable one as a two-answer choice labelled by
[NullText] and [SetText]; give it no Default when the person must answer
(see Nullable choices).
For ordinary optional user text, prefer a string with an empty
Schema Default and size bounds permitting zero, under the shared
type-selection guidance:
/**
* Student details
* Describes the personal details recorded for one student.
*/
export interface Student {
/**
* Middle name
* Enter the student's middle name. Leave it empty if absent.
* @LibertasDefault("")
* @LibertasSize(min=0, max=120)
*/
middleName: string;
}
This middle name starts empty and may stay empty. When absence must remain
distinct from a present value, use amount?: number or
amount: number | null or amount: number | undefined, for example to distinguish an unknown amount from
known zero. Both forms emit the platform schema's Nullable attribute; a
Nullable value starts as valid absence. A size minimum constrains a supplied
String, not null. A non-Nullable String without a Fixed or applicable default
starts unanswered, even when its size minimum is zero.
- **Special Case**: If you define a property referencing a Union Type Alias with `| undefined`, the parser correctly resolves it as a reference to the type alias with the `Nullable` attribute, rather than flattening the union inline.
Supported JSDoc Decorators
Libertas utilizes custom JSDoc decorators (starting with @Libertas) to control how the UI and interactions behave.
Codebots must generate the exact positional or named-property spelling shown in
the exhaustive schema annotation inventory.
Some older named-argument variations remain parser compatibility syntax, but
they are not additional public authoring forms. @LibertasFormattedText and
@LibertasSingleton are no-argument markers.
General Decorators
- **`@LibertasExport`**: Parser/compiler helper that does not emit a schema attribute. It does not create a standalone root in an ordinary package and is not needed to root public types in a `TypesOnly` package.
- **`@LibertasIgnored`**: No-argument parser directive valid on an exported function or exported named type. It omits that declaration as a root. If an included root reaches an ignored type, parsing fails.
-
@LibertasSingleton: No-argument function marker. It emitsSingleton=trueand allows at most one task for that App function on a Hub. - **`@LibertasDefaultLocale(locale_name)`**: Package-level decorator (must be in `///` comments) setting the default locale for your package. Defaults to `"en"`. The [locale](../annotations.md#package-discovery-and-parser-helpers) is 1–128 ASCII alphanumeric characters in segments separated by one `-` or `_`; leading, trailing, and repeated separators are invalid.
- **`@LibertasStringResources(CONST_NAME)`**: Package-level decorator in the leading `///` comments. It resolves exactly one non-empty `const` object-literal catalog, emits its sorted keys on the schema package, and writes its LMF1 templates to the document's top-level `StringResources` object. It is [invalid on functions, types, and fields](../annotations.md#package-discovery-and-parser-helpers).
- **`@LibertasTypesOnly`**: No-argument package-level marker in the leading `///` comments. It automatically publishes every public package-owned named type exported by the entry module unless ignored, without compiling or deploying executable code. Exported functions must carry `@LibertasIgnored`; other exported functions are [rejected](../schema.md#8-types-only-and-interoperable-packages).
- **`@LibertasDefault(value)`**: Sets a default value for an input field. Enumeration member names or numeric defaults follow the [shared enumeration contract](../schema.md#53-enumerations). User and User-array defaults use the [identity-selector contract](../schema.md#user-and-group-defaults), for example `@LibertasDefault("current-user")`.
-
**`@LibertasFixed(value)`**: Emits one fixed encoded value. On an Endpoint
client the value instead identifies the peer task process as
developer-package::function; the Endpoint selector remains editable, the compatible server set is constrained to that process, and its package is a mandatory dependency. Enum member Fixed values and Union member Discriminator values come from their native declarations; these attributes have independent meanings. OnLibertasUser, Fixed is a Hub-enforced security constraint; onlycurrent-useris valid, at a request root or within nested request values outside Lists, optionally Hidden. - **`@LibertasSuppressLabel`** suppresses a scalar, struct, or Union row’s visual label while retaining its value and controls. On an array of structs or its type alias it suppresses only List-contributed member names and positions, retaining the List heading. Studio compiles it into the [Display Template](../presentation.md#display-template) and enforces [placement and page-title rules](../presentation.md#suppresslabel-label-free-value-rows). The former `@LibertasSuppressTitle` spelling is rejected. Promotion, flattening, row labels, and row titles have no decorator: write them as [`promote`](../presentation.md#promotion-flattening-and-union-selectors) (only on a field of a struct), [`flatten`](../presentation.md#flatten) (on a struct, array, or union), [`label`, and `title`](../presentation.md#labels-and-titles) rules in the project's `display.json`, beside `package.json` ([data-titled rows](../presentation.md#data-titled-rows)). See [removed annotations](../annotations.md#removed-annotations) when migrating older source.
- **`@LibertasUIIncludeDescription`**: No-argument client presentation marker for a schema data field or type. The UI node displays its localized long description inline together with its short display name. It does not change the typed value or wire encoding and is compiled into the [Display Template](../presentation.md#display-template) as its `include_description` hint.
-
@LibertasPermissions(CONST_NAME): Resolves a const string array into the comma-delimitedPermissionsschema attribute. Valid on functions, where every permission needs a matching non-empty string-resource reason, and on built-in Endpoint servers, where no reason resource is required. -
@LibertasAccessHost("host1,host2"): Function-only network declaration. Concrete DNS names and reserved{network_access_local}/{network_access_external}grants each need a non-empty same-key reason in packageStringResources. See App network access. - **`@LibertasReadOnly`**: Marks the field as read-only in the UI.
- **`@LibertasOutputOnly`**: No-argument marker emitting `OutputOnly=true` on a statically ReadOnly Request node the App never reads; Submit sends its empty value. The [output-only contract](../protocols.md#output-only-request-fields) owns hosts and client behavior.
- **`@LibertasHidden`**: Hides the field from the UI entirely.
- **`@LibertasLLMToolCall(false)` / `@LibertasLLMToolCall(true)`**: On a protocol Union or Request variant, controls whether the Hub generates an LLM tool definition. A Union's value applies to its variants unless a variant overrides it; omission defaults to true. See [the shared attribute contract](../annotations.md#endpoint-and-protocol-annotations).
- **`@LibertasLLMDocTrans(instruction="...", validation="@.facts", doc_type="image/*", accept_multiple_inputs=true)`**: On a named Table interface, declares one AI Import action per editable Request occurrence. Only `instruction` is required; the other named arguments are optional. Its direct result fields use `@LibertasLLMDocField` or `@LibertasLLMDocField(allowEmpty=false)`. A non-Nullable Number defaults to `allowEmpty=true` and needs a meaningful Schema `Default`; a clearly blank box maps to that typed Default. `@LibertasLLMDocField(blank=0)`, `blank=$null` on a Nullable field, or `blank=ShortTerm` naming a Union variant instead declares the [value a blank box applies](../llm-document-transformation.md#declared-blank-value). See [document transformation](../llm-document-transformation.md#app-schema-authoring) for Schema Path facts, numeric conversion, source files, and application.
-
**`@LibertasExcludeUI`**: Makes an Endpoint server carrying a resolved
protocol schema, or a reachable
Request/SubscriptionRequestvariant, nonexistent to Control UI. The Hub Control service omits the marked server Endpoint from its UI-device list and LLM tool collection; generated clients and Hub LLM tool-definition generation omit the marked operation. Admin creation/editing still uses the complete schema. It is a no-argument marker and does not change protocol roles, wire representation, routing, subscriptions, or authorization. Every server Endpoint sharing one resolvedProtocolSchemaroot must use the same value; see the Endpoint declaration contract. -
**`@LibertasPhysicalUnit("millimeter")`**: Declares a canonical CLDR core
unit identifier on a numeric field. It takes exactly one quoted positional identifier and emits
PhysicalUnit=millimeter; clients preserve that unit and localize its short visual and long accessibility names when supported by the platform ICU implementation. It cannot be combined with@LibertasDateOnly,@LibertasTimeInterval, or@LibertasEnumSource. Generated CLDR compound units such asmillimeter-per-dayare valid regardless of whether a particular client can currently display them; an unsupported client may omit unit decoration while preserving the value. The normative syntax and chart behavior are defined in Client-side charting §5.5.1 and Unicode UTS #35 unit identifiers. -
`@LibertasMoney("USD", 2)`: Declares `@LibertasNumber(type=i64)` Number money in exact integer minor units and
emits
Money=USD/2. Currency and scale are fixed schema metadata; clients display currency and edit major units in place. See the Money contract for supported scales, patterns, validation, and chart behavior. -
**`@LibertasFormat("pattern")`**: Display-only `Format`, compiled into the [Display Template](../presentation.md#display-template), on a
number,LibertasDateTime,LibertasTimeOnly, or@LibertasDateOnlyinteger. Ordinary numbers use Unicode UTS #35 number patterns; temporal hosts use Unicode UTS #35 date/time patterns. The viewer's locale supplies symbols and names. The pattern never changes the raw value, wire encoding, validation, sorting, or chart geometry. - **`@LibertasForeignType("developer-package::Type")`**: Declares the direct official type for this published compatible interface, class, type alias, or enum. It is [invalid on fields and references](../schema.md#32-foreign-wire-types). The value uses the exact `developer-package::Type` FQN syntax.
Use the normal imported or qualified TypeScript type name so the parser can
resolve its native declaration. Every named type published by an App, including a
compatible type, must be exported or re-exported from the App's root index.ts
under its exact declaration name. Renamed exports and
duplicate flattened names are rejected.
A resolved type outside the current package is automatically foreign and uses its
native owning App FQN. The owning package must be a Libertas App that exports the
exact type from its root. A compatible type remains published under its own App
FQN; @LibertasForeignType links it to the official type without changing its
identity or propagating to children. ExternalNativeType has been removed.
Ordinary reference FQNs are always inferred from TypeScript-native references. A
qualified reference may be quoted in a protocol/data-schema annotation, but it
must resolve and is converted to the owning App FQN. @LibertasForeignType is
the only FQN taken literally as schema identity. Every unqualified type name,
whether used in a type slot, heritage clause, or annotation, resolves only in
its declaration's actual TypeScript scope. A valid imported external name
becomes developer-package::TypeName; an unbound name is a parser error even if
another program file happens to declare the same terminal name.
/// Shared definitions
/// Provides reusable equipment status details for compatible products.
/// @LibertasTypesOnly
/**
* Equipment status
* Describes an equipment condition and its attached measurement file.
*/
export interface SharedStatus {
/**
* Operator status note
* Gives the free-form condition note entered by an equipment operator.
*/
message: string;
/**
* Measurement attachment
* Carries the measurement file supplied with this equipment condition.
*/
payload: Uint8Array;
}
Cross-language interoperable packages
Build every package under your own developer name; never use another developer's name as your package namespace. As a best practice, publish public data schemas in a TypesOnly package and make its source available.
If consumers need an equivalent schema in another language, create a compatible
package under a new package name. Each compatible public type declares exactly
one @LibertasForeignType containing its direct official type FQN. Other public
types may remain ordinary, and mappings may refer to different official Apps.
At runtime Libertas verifies wire structure, merges official attributes into a transient graph, and applies explicitly declared compatible attributes as overrides. The published compatible schema is unchanged. Localized type documents are shared automatically while TypeScript-only discriminator nodes remain local.
/// Portable shared definitions
/// Provides equivalent equipment status details for compatible products.
/// @LibertasTypesOnly
/**
* Equipment status
* Describes an equipment condition consistently across compatible products.
* @LibertasForeignType("origin-shared_types::SharedStatus")
*/
export interface PortableStatus {
/**
* Operator status note
* Gives the free-form condition note entered by an equipment operator.
*/
message: string;
}
Numeric Modifiers
- **`@LibertasNumber(type=u8|i8|u16|i16|u32|i32|u64|i64|u128|i128|f32|f64, min=..., max=..., step=...)`**: One numeric annotation for integer and floating fields. `type=` is mandatory whenever this annotation is used. DateTime requires `u64`; unannotated native `number` retains its `f64` mapping. Unquoted numeric bounds emit `NumberMin`/`NumberMax`; quoted bound expressions such as `max="[^/maximum]"` emit `RangeMin`/`RangeMax` under the [dynamic range contract](../validation-rules.md#31-dynamic-numeric-ranges). `step` remains fixed. Studio validates the result against the selected semantic type. Integer step defaults to 1. The former Integer/Float source spellings are removed.
- **`@LibertasRangeMin("expression")` / `@LibertasRangeMax("expression")`**: Each carries one Data Validation bound expression compatible with the host type on a Number or `LibertasDateTime` field/type. See [dynamic numeric ranges](../validation-rules.md#31-dynamic-numeric-ranges) for result typing, compatible dates, and GUI/final enforcement. These expressions have no message key, semicolon pair, or `FinalOnly` directive.
- **`@LibertasTimeInterval`**: In the Task Editor, this triggers a specialized `HH:MM` time interval input for users.
- **`@LibertasDateOnly`**: No-argument marker for an integer `number` whose value is a Gregorian calendar date in `yyyyMMdd` form. It emits `DateOnly=true`, keeps the integer wire representation, and generates a native date chooser in task configuration and runtime interaction. It cannot be combined with `@LibertasTimeInterval` or `@LibertasEnumSource`; defaults and fixed values must be valid eight-digit dates.
- **`@LibertasBitflags` / `@LibertasBitflags(TypeName)`**: The no-argument form projects a TypeScript enum into a localized Enumeration catalog. The one-type form projects that catalog onto an explicitly sized integer field without changing its integer wire value.
@LibertasFormat("pattern"): Uses a portable Unicode LDML display pattern. Examples are#,##0.00for an ordinary number,yyyy-MM-ddfor DateOnly,HH:mm:ssforLibertasTimeOnly, andd MMMM yyyy, HH:mm zforLibertasDateTime. It takes exactly one double-quoted positional string. DateOnly accepts date fields only, TimeOnly accepts time fields only, and DateTime uses the viewer's local timezone.
/**
* Service schedule
* Describes the calendar day chosen for one scheduled service visit.
*/
export interface Schedule {
/**
* Service day
* The user chooses the calendar day when the service should occur.
* @LibertasNumber(type=u32)
* @LibertasDateOnly
* @LibertasFormat("yyyy-MM-dd")
* @LibertasDefault(20260815)
*/
serviceDay: number;
}
Bitflag catalogs and integer fields
Use a TypeScript enum as the language-equivalent bitflag declaration. Put the
no-argument marker on the enum and name it from the integer field marker. The
enum may remain private because the parser fabricates schema metadata from it; the
enum itself is not the runtime or wire value.
/**
* Access choices
* Describes the independent actions a person may be allowed to perform.
* @LibertasBitflags
*/
enum Access {
/**
* No access
* The person cannot view, change, or run the protected content.
*/
None = 0,
/**
* View content
* The person may view the protected content without changing it.
*/
Read = 1 << 0,
/**
* Change content
* The person may change the protected content.
*/
Write = 1 << 1,
/**
* Run actions
* The person may run the protected actions.
*/
Execute = 1 << 2,
}
/**
* Access settings
* Describes the actions allowed for a person or group.
* @LibertasExport
*/
export interface Settings {
/**
* Allowed actions
* The user chooses each action that the person or group may perform.
* @LibertasNumber(type=u8)
* @LibertasBitflags(Access)
*/
access: number;
}
The field form may instead use a qualified TypeScript-native path to a catalog owned by another Libertas App:
import * as sharedFlags from "shared-flags";
/**
* Access settings
* Describes the actions allowed for a person or group.
* @LibertasExport
*/
export interface Settings {
/**
* Allowed actions
* The user chooses from the action catalog published by the shared package.
* @LibertasNumber(type=u8)
* @LibertasBitflags(sharedFlags.Access)
*/
access: number;
}
The parser resolves the declaration and emits
Bitflags=shared-flags::Access; it never copies the import alias into schema
identity. Because the enum is fabricated metadata, it may remain private, but
it must be declared under the exact name Access at the owning App's root
index.ts. During deployment planning Studio fetches/explores that package
from the selected Hub if it is not local, and its authoritative Hub version is
included in finalized AppDependencies.
The enum becomes an Enumeration with Bitflags=true; its members become
numeric children with compiler-evaluated Fixed values. Each nonzero value
must be a unique single bit. One zero value is allowed for a localized empty
label such as “None” or “N/A”. The field must be an integer wide enough for all
bits. Defaults, fixed values, task values, and runtime values may combine
declared bits but may not contain unknown bits.
Studio displays localized selected names in comma-delimited form and edits the
value with a multiple-choice chooser. The Hub is deliberately unaffected: it
sees, stores, and transports only the ordinary integer. Bitflags cannot share
a field with DateOnly, TimeInterval, EnumSource, Format, or PhysicalUnit.
Strings and Arrays
- **`@LibertasEnum(A, B, C)`**: Parser-supported compatibility syntax that fabricates positional string choices. Those fabricated members have no source location where a codebot can provide each member's required natural label and description. Codebots must therefore prefer a documented native numeric enum and must not generate this positional form until Libertas defines a member-document syntax for it. Existing hand-authored uses remain parseable.
- **`@LibertasSize(min?, max?)`**: Restricts [size for lists, strings, and bytes](../annotations.md#defaults-constraints-and-ordinary-presentation).
- **`@LibertasPattern("[0-9]{9}")` or `@LibertasPattern($CONST)`**: Emits `Pattern` on effective String fields, list elements, or named String types, including nullable String. Use one JSON double-quoted literal, decoded once without trim, or a native String constant. See [full-string patterns and complete escape rules](../validation-rules.md#132-full-string-patterns).
- **`@LibertasUnordered`**: Marks an array as an unordered set, affecting how arrays are merged or displayed.
- **`@LibertasUnique(level?: number)`**: Enforces [uniqueness](../annotations.md#defaults-constraints-and-ordinary-presentation) on a field, preventing users from entering duplicate values in a list. The optional `level` defines how many ancestor `List` arrays up the tree to check to define the uniqueness boundary.
- **`@LibertasKey`**: No-argument marker emitting `Key=true` on the one field of a list member interface that identifies the member to an EnumSource host; see [data-backed enum indexes](#data-backed-enum-indexes).
Union Types & Discriminators
The parser emits numeric Discriminator on each Union member, sorted by that
value. The values must be consecutive starting at zero; Studio rejects gaps
and other invalid mappings. It also emits
UnionTagField=true on its native tag field. UnionTagField is exclusively
ScriptLanguage metadata (such as TypeScript/Python), never Rust metadata. @LibertasDiscriminator("kind")
continues to select the native property and emits UnionTagName; it does not
set a numeric payload constraint. Fixed retains its ordinary value meaning.
See the Union schema contract.
@LibertasDiscriminator("tag_name"): Explicitly marks the field used as the union discriminator. If omitted, the platform will attempt to automatically detect the discriminator field.
Union Variant Documentation
When you represent complex states, commands, or data variants using TypeScript discriminated unions, Libertas aggregates your documentation to provide a clean user experience using a strict 3-tier precedence hierarchy.
1. Document Precedence
| Priority | Source Layer | Description |
|---|---|---|
| Priority 1 (Highest) | Corresponding Discriminator Value | JSDoc comments preceding the resolved enum member or const property declaration corresponding to the variant's discriminator value. |
| Priority 2 (Middle) | Union Variant Node | JSDoc comments preceding the variant type reference directly within the union type definition. |
| Priority 3 (Lowest) | Variant Class/Interface | JSDoc comments preceding the base interface, class, or type declaration of the variant itself. |
Non-copyable precedence schematic
The following fragment deliberately references omitted declarations and uses precedence-layer labels as placeholder document text. It exists only to show which of three comments wins. Do not copy it into an App; real declarations must use one consistent, natural label and description for every shown node.
export enum MessageType {
/**
* Priority 1: Discriminator Zone Info (Highest)
* This JSDoc takes highest precedence and will be the final document.
*/
ZoneInfoVal = 2,
}
/**
* Priority 3: Default Zone Info (Lowest)
* This is the fallback if no other comments exist.
*/
export interface ZoneInfoMessage {
type: MessageType.ZoneInfoVal;
}
export type ZoneDataProtocol =
| GetZoneInfoMessage
| /**
* Priority 2: Custom Zone Info (Middle)
* Takes precedence over the class docs but is overridden by the discriminator docs.
* @LibertasNextRequest("UpdateHoldOffMessage")
*/
ZoneInfoMessage
| UpdateHoldOffMessage;
In this example, the UI will display "Priority 1: Discriminator Zone Info" to the user.
2. Attribute and Decorator Merging
Unlike the Short Name and Description (which follow strict overriding precedence), custom JSDoc decorators and attributes are merged cumulatively across all three layers. All resolved attributes are combined, eliminating duplicates, to form the final variant metadata schema.
3. Discriminator & Matching Heuristics
The schema generator automatically locates union discriminators and matches union members:
- Discriminator Types: Supports
enumvalues as well as property lookups in objects defined asconst(usingas const). - Discriminator Validation Rules:
- Numeric Discriminators Only: Union variant discriminator fields must resolve to a numeric type. Using string literals as union variant discriminators is strictly prohibited.
- First Property Requirement: The discriminator field/property must be the very first property declared within each variant's body/definition.
Data-Backed Enum Indexes
Use @LibertasEnumSource("path") when an unsigned integer stores the zero-based index of an item in another list in the same schema-data tree. The attribute emits EnumSource; its one positional string must be a Libertas Schema Path in the EnumSource context. Studio rejects unresolved paths and paths whose target is not a list; for a list without a key, it also rejects signed integers, floats, and non-number hosts.
/**
* Choice settings
* Describes the available choices and the choice currently selected.
*/
export interface Settings {
/**
* Available choices
* Lists the literal choices in the order people see them.
* ----
* Choice
* Gives the literal name of one available choice.
*/
choices: string[];
/**
* Selected choice
* Identifies the choice currently selected from the available list.
* @LibertasNumber(type=u16)
* @LibertasEnumSource("$.choices")
*/
selectedChoice: number;
}
Here, selectedChoice = 0 refers to the first item in choices.
An index changes meaning when the list is reordered or an item is removed. To
store a stable identity instead, make the list's member an interface and mark
one non-nullable string or integer field of it with @LibertasKey and
@LibertasUnique. The @LibertasEnumSource host then stores that member's
key and must have the key's type. The EnumSource keys contract
owns the complete rules.
/**
* Report
* Describes one report a person can choose.
*/
export interface Report {
/**
* Report code
* Identifies the report independently of its position in the list.
* @LibertasKey
* @LibertasUnique
* @LibertasHidden
*/
code: string;
/**
* Report name
* Gives the name people see when choosing the report.
*/
name: string;
}
/**
* Report choices
* Describes the available reports and the report currently selected.
*/
export interface ReportSettings {
/**
* Available reports
* Lists the reports a person can choose in their displayed order.
* ----
* Available report
* Describes one report the person can choose.
*/
reports: Report[];
/**
* Selected report
* Identifies the chosen report by its code.
* @LibertasEnumSource("$.reports")
*/
selectedReport: string;
}
Formatted Runtime Text
Use the no-argument @LibertasFormattedText marker on an effective Uint8Array
(Bytes in the schema), together with static ReadOnly under the
placement and request-value contract.
The marker emits
"FormattedText": "true" without changing serialization.
The byte blob is the canonical resource key plus ordered, typed
LibertasMessageArgument values, including an empty argument list. Localized
templates use zero-based LMF1, and runtime payloads never contain resource
templates.
Do not use a string host containing Base64. Rendering would only decode that
String back to the same byte tuple, adding a pointless encode/decode cycle and
providing no benefit. Keeping formatted values as Bytes creates a sharp type
distinction from literal text, making developer mistakes less likely. App code
keeps the value as Uint8Array; if a generic JSON transport represents Bytes
as Base64, that is only the transport codec and never the field's semantic type.
The public SDK function libertasFormattedText(resourceName, arguments)
returns the canonical Uint8Array. App code ordinarily does not decode it;
clients resolve and render the LMF1 template at the presentation boundary.
/// Equipment status
/// Lets connected products request and present localized equipment conditions.
/// @LibertasStringResources(StatusStrings)
const StatusStrings = {
READY: "The equipment is ready.",
DEVICE_STATUS: "{0,LibertasDevice} reports a temperature of {1,number,0.0}."
} as const;
/**
* Check current status
* Asks the equipment controller to report its current condition.
* @LibertasRequest
*/
export interface ReadStatus { kind: 0; }
/**
* Equipment condition
* Describes the localized summary and detail reported for equipment.
*/
export interface StatusBody {
/**
* Status summary
* Gives people a concise localized description of the current condition.
* @LibertasReadOnly
* @LibertasFormattedText
*/
summary: Uint8Array; // canonical READY tuple with no arguments
/**
* Status detail
* Identifies the equipment and describes its current condition.
* @LibertasReadOnly
* @LibertasFormattedText
*/
detail: Uint8Array; // canonical DEVICE_STATUS tuple
}
/**
* Current equipment status
* Reports the equipment's current condition in localized text.
* @LibertasResponse
*/
export interface Status {
kind: 1;
/**
* Reported condition
* Gives the localized summary and detail for the equipment.
*/
body: StatusBody;
}
/**
* Equipment status check
* Describes how people check equipment and receive its current condition.
*/
export type StatusProtocol = ReadStatus | Status;
const summary = libertasFormattedText("READY", []);
function equipmentTemperatureDetail(
equipment: LibertasDevice,
temperatureCelsius: number,
): Uint8Array {
return libertasFormattedText("DEVICE_STATUS", [
{ type: 1, value: equipment },
{ type: 10, unit_type: "temperature-celsius", value: temperatureCelsius },
]);
}
The App package owns the catalog. StatusBody carries a reusable, statically
read-only Bytes shape; a TypesOnly package may publish it without choosing a
direction. A concrete App that later emits it uses that App's package catalog.
Studio checks each use against the linked placement contract.
Locally, Studio validates the exact marker and effective Bytes host plus the syntax and default-document membership of the optional package catalog. It does not require every formatted field or output path to prove runtime key membership. See Libertas Message Format for the LMF1 grammar, Libertas Message Argument contract, encoded-type priority, and chart behavior.
Protocol chart presentation
Chart metadata is part of the ordinary endpoint protocol schema. It adds no
payload bytes or chart-specific message. Apply @LibertasChart(mark) to an
effective Response or SubscriptionData payload with shape
List<Table Row>. Direct scalar row fields use
@LibertasChartChannel(...) and may use @LibertasChartScale(...) when they
own one scale-bearing channel. @LibertasChartGuide(...) configures the generic
guide subsystem independently of scale mapping. A scale's kind and a guide's
position are chart appearance, compiled into the Display Template.
/**
* Soil-moisture reading
* Records the soil moisture observed at one time.
*/
export interface Sample {
/**
* Observation time
* Identifies when the soil-moisture reading was observed.
* @LibertasChartChannel(x, tooltip)
* @LibertasChartScale(id=time, kind=utc)
*/
at: LibertasDateTime;
/**
* Observed soil moisture
* Reports the soil-moisture percentage measured at the observation time.
* @LibertasChartChannel(y, tooltip)
* @LibertasChartScale(id=value, kind=linear, zero=false)
* @LibertasPhysicalUnit("percent")
*/
value: number;
}
/**
* Soil-moisture history
* Shows how the observed soil moisture changes over time.
* ----
* Soil-moisture reading
* Records the soil moisture observed at one time.
* @LibertasSubscriptionData
* @LibertasChart(line)
*/
export type History = Sample[];
Chart accepts primitive point, line, area, bar, rect, rule,
arc, text, and polygon, plus composition layer, hconcat, and
vconcat. Point, line, rule, and text have Cartesian and polar coordinate
forms; polygon accepts Cartesian or polar vertices and uses implicitly closed
evenOdd compound fills.
Theta and radius are quantitative numeric positions; their explicit scale kind
may be linear, log, symlog, sqrt, or identity with already normalized
0...1 values, not a discrete or temporal kind.
For a primitive mark, the annotated value may be either List<Table Row> or a
carrier table with exactly one non-hidden direct List<Table Row> field. Every
other direct carrier field must use @LibertasHidden; those values remain in
the decoded response for typed NextRequest/CopyFrom transactions but never
become chart rows, channels, tooltips, accessibility content, or resource
usage. Compositions are tables whose non-hidden direct children independently
carry Chart; direct non-chart children are allowed only with
@LibertasHidden. A response/subscription Union variant may similarly wrap
exactly one chart-bearing field plus hidden transaction fields. The client
retains the complete variant and projects only the chart-bearing field.
A layer accepts unit children or nested layers, which flatten in schema order;
it cannot directly contain hconcat or vconcat. Concat grids may nest.
Channels are deduplicated and emitted in canonical order. Scale source key
kind is emitted as language-neutral key type; accepted scale kinds are
linear, log, symlog, sqrt, time, utc, ordinal, band, point,
and identity.
@LibertasChartScale accepts id,kind,min,max,mid,zero,reverse; it has no guide
property. Canonical generated DateOnly min and max must be exact valid
Gregorian yyyyMMdd values; an equivalent source numeric spelling is
normalized before that check, and clients map the result by calendar-day
ordinal. DateTime bounds must be integral epoch seconds in
0..253402214399, and TimeOnly bounds must lie in
0..86400 inclusive. Numeric scale properties use one shortest
round-tripping binary64 spelling. The effective target/scalar/scale-kind matrix
is closed; offsets and facets are band-only, and a numeric ordinal category
requires an integral, non-duration schema number. @LibertasChartGuide
requires target and source, followed by
optional position,title,domain,ticks,labels,grid. Sources are scale,
value, span, or none; positions are auto/top/right/bottom/left;
component values are auto or none. A scale/none guide is placed on the
field owning its
matching primary target. A value/span guide is placed on a guide-label field
with no scale-bearing channel. The following non-copyable field fragment shows
only the guide syntax; place an equivalent field inside a fully documented
named type:
/**
* Observation day
* Identifies the calendar day represented by this group of observations.
* @LibertasDateOnly
* @LibertasChartChannel(tooltip)
* @LibertasChartGuide(target=x, source=span, position=bottom,
* title=none, grid=none)
*/
bucketDate: number;
value targets x, y, theta, or radius; span requires continuous
Cartesian x,x2 or y,y2. Non-identity primary scales infer a scale-sourced
guide, while identity and offset scales infer none. One explicit declaration
for a target replaces the implicit guide. The exact target/side/component
matrix, layer and facet deduplication, localized titles and typed labels, tick
algorithm, span grouping, and limits are defined by
Client-side charting.
Guide overlay is deterministic runtime policy rather than author metadata:
measure outside first, then overlay every eligible left/right guide together
when the post-guide plot would be narrower than 200 logical points. The exact
tap, accessibility, and chart failure presentation is defined by that
contract; clients do not choose their own breakpoints.
Preview sizes its charting region from the pane (polar square; Cartesian 4:3 on
compact widths, otherwise 16:9), capped near half the pane height with a
180-point floor, in a viewport four thirds that tall; see
preview height.
Fullscreen uses 75% of current usable height, capped in portrait by current
usable width. Complete vertical legend overflow remains in the one outer chart
scroll, whose one-shot initial position exposes the first charting region.
These are runtime policies and add no ChartGuide property.
Discrete positional band and point domains remain complete. A client uses
the contract's 20-point minimum leaf-category pitch and bounded X/Y category
viewports instead of growing the chart or dropping categories. Heatmaps can pan
both overflowing dimensions; a one-dimensional category chart consumes only
that dimension. This is runtime presentation and adds no decorator.
Dense monotone Cartesian lines and vertical areas are automatically resampled
per logical plot point and disclose that state with the noninteractive ≈
indicator. Source rows remain exact for domains, tooltips, requests, and the
ordinary data view. Libertas imposes no chart-specific hard limit on row,
primitive, or source-path counts; structural/metadata limits and bounded
visible work remain enforced.
App task code may perform any data transformation, including time projection,
before returning the snapshot. Use native DateTime/DateOnly positions when the
automatic temporal scale is wanted, or emit transformed numeric coordinates
and attach typed source=value/source=span guide labels. The client localizes
those labels and never reverse-engineers the task's transform.
After parser extraction and dependency materialization, Studio's shared chart
validator rejects duplicate/misplaced metadata, invalid chart shapes,
incompatible scalar/channel and scale combinations, missing mark geometry,
secondary endpoints without primaries, and conflicting shared scale IDs.
Nearest same-name attribute lookup is a complete replacement, not a merge.
Enumeration, LibertasDevice, LibertasVirtualDevice, LibertasUser,
LibertasEndpoint, and LibertasLanDevice are categorical values whose raw
member/reference remains chart identity while the client resolves a localized,
permission-aware display name. Action is reserved and unsupported.
Formatted Uint8Array output may serve categorical or textual roles, but never
a quantitative role, continuous domain, or identity scale.
A visible plain string asserts genuine literal domain/user data such as a
proper name or free-form annotation. Never use one for a fixed App vocabulary,
dynamic App-authored wording, or a copied Libertas object name: use an enum,
formatted output, or the corresponding typed Libertas object reference. This
applies to every label, legend, facet, tooltip, value/span guide, and
accessibility path; key-only and guide-less identity appearance strings are
non-textual exceptions.
See Client-side charting for the complete channel,
scale, geometry, composition, and fallback contract.
3D chart source attributes
Three-dimensional presentation uses five separate decorators:
@LibertasChart3D, @LibertasChart3DChannel, @LibertasChart3DScale,
@LibertasChart3DGuide, and @LibertasChart3DView. They add only schema
metadata and do not alter the typed response payload or the existing 2D
decorators.
/**
* Surface observation
* Describes one measured height at a two-dimensional position.
*/
export interface SurfaceSample {
/**
* East-west position
* Gives the observation's position along the east-west direction.
* @LibertasChart3DChannel(x)
*/
x: number;
/**
* North-south position
* Gives the observation's position along the north-south direction.
* @LibertasChart3DChannel(y)
*/
y: number;
/**
* Measured height
* Gives the height recorded at this position.
* @LibertasChart3DChannel(z, tooltip)
* @LibertasChart3DScale(id=height, kind=linear)
* @LibertasChart3DGuide(target=z, source=scale)
*/
height: number;
}
/**
* Measured surface
* Shows how measured height varies across the observed area.
* ----
* Surface observation
* Represents one measured height at one position in the observed area.
* @LibertasResponse
* @LibertasChart3D(surface)
* @LibertasChart3DView(projection=perspective, azimuth=45,
* elevation=30, zoom=1, fov=45, orbit=true)
*/
export type Surface = SurfaceSample[];
Primitive tokens are point, line, bar, surface, wireframe, and
stem; compositions are layer, hconcat, and vconcat. Channels are x,
x2, y, y2, z, z2, u, v, color, fill, stroke, opacity,
fillOpacity, strokeOpacity, size, shape, strokeWidth, strokeDash,
tooltip, detail, order, key, row, and column. Scale syntax mirrors
@LibertasChartScale, including source kind becoming schema type that is
compiled into the Display Template, and adds
logBase > 1 for a logarithmic scale, non-negative growMin/growMax fractions
for automatic continuous x/y/z domain growth, and inclusive [0,1] padding
for a bar base axis. Guides add tickCount and
tickStep, and may target detail.
Chart-family names are not extra tokens: scatter and point clouds use point,
bubbles add size, traces and waterfalls use line, columns use bar, height
maps and sampled mathematical grids use surface, contours layer line over
surface, and impulses use stem. Use detail for homogeneous multiple
series and layer for heterogeneous series.
bar accepts discrete or sparse continuous x/y bases with a continuous z
value. x2, y2, and z2 may appear independently; omitted x/y extents and
the z baseline are inferred, while all six coordinates form an explicit
cuboid. The legacy any-orientation form remains valid when exactly two primary
axes are band scales and the sole continuous primary is the value axis. Surface
and wireframe u,v topology values may be arbitrary finite values and may
contain holes.
@LibertasChart3DView accepts, in canonical order, projection, azimuth,
elevation, zoom, fov, orbit, aspectX, aspectY, aspectZ, targetX,
targetY, targetZ, pan, and zoomEnabled. Projection is perspective or
orthographic; orbit, pan, and zoomEnabled are booleans; azimuth,
elevation, and target coordinates are finite; zoom and each aspect are greater
than zero; and 0 < fov < 180.
Orthographic projection accepts fov but ignores it. No fixed Chart3D schema
cap limits rows, vertices, composition units, guides, or tooltips; normal
transport and runtime resource admission still applies. The complete schema
and rendering contract is
Client-side 3D charting.
zoomEnabled=false disables user zoom independently of orbit and pan without
changing the authored initial zoom.
These booleans independently gate the client's fixed camera actions; they do
not select or remap touch gestures. A preview is gesture-neutral, and its
Explore 3D chart action opens full-screen Chart Explorer, where one-finger
drag rotates, two-finger translation moves the view, and pinch/spread zooms.
There is no mode, gesture mapping, sensitivity, or control-label decorator or
runtime field. Tap-only controls and accessibility actions name Rotate, Move,
Zoom, and Reset explicitly. See the
universal 3D interaction contract.
Point and stem-tip shapes use
circle,square,triangle,diamond,sphere,cube,pyramid,cylinder in that automatic
order; bars use box,cylinder,pyramid,sphere. Size zero suppresses or
degenerates the corresponding geometry without deleting its source row.
Protocol and persistent-data schema evolution
Published protocol and persistent-data Unions are append-only contracts. Keep each container's native name versionless and invariant for the App's lifetime, and append new payload variants to that same Union. Do not replace it with a version-named Union. See the App data-type design guidelines.
Endpoint Protocols
When defining endpoints for a Libertas AI Agent, you use decorators to define conversational roles and context:
- **`@LibertasEndpointSchema(MySchemaUnion)`**: Applied to a `LibertasEndpoint` field to define the payload structure for the agent. The target schema **must** be a [union type](../protocols.md#declaring-an-endpoint).
- **`@LibertasEndpointServer`**: Marks the endpoint as a server.
-
**`@LibertasDefault("developer-package::function")`** on an Endpoint client:
when no schema-compatible server Endpoint exists, recommends provisioning the named task process. It does not filter existing compatible choices. If this Endpoint is absolutely required by its ancestor path, the client blocks task creation and requires that process to be installed when no compatible server exists.
-
**`@LibertasFixed("developer-package::function")`** on an Endpoint client:
after schema compatibility is evaluated, restricts choices to server Endpoints owned by that task process and forces its package into finalized
AppDependencies. If an absolutely required occurrence has no matching server, the client requires that process to be installed before task creation.DefaultandFixedare mutually exclusive here. Both Endpoint-ID selectors remain editable and may offer multiple server instances. The established separator is::; the value is task-process metadata, not the encoded Endpoint ID and not a schema-type FQN. -
@LibertasExcludeUI: On an Endpoint server carrying a resolved protocol schema, makes the Hub Control service omit that Endpoint from its UI-device list and LLM tool collection. On a reachableRequestorSubscriptionRequestvariant, removes that operation from generated Control UI and Hub LLM tool-definition generation. The admin plane still presents and persists the complete schema; the endpoint remains registered and protocol, routing, subscription, authorization, and wire behavior are unchanged. - **`@LibertasProtocolConformance("VariantA & (VariantB | !VariantC)")`**: Declares variants required from the opposite endpoint when this Union is used as a protocol. It is valid on a Union declaration because the declaration alone does not identify protocol usage or a client/server role. Use direct Union variant native names with AND (`&`), OR (`|`), NOT (`!`), and XOR (`^`). During configuration, the client and server expressions are evaluated against each other's variants and both must pass. A scalar operand evaluates name presence; a Union-typed variant received from a Rust schema evaluates its nested conformance instead. Without the decorator, every direct variant declared by that Union context is mandatory. Shared variants must also have pass-through-compatible payloads. The client task host rewrites only the [capability prefix](../protocols.md#capability-prefixes), so a capability may sit at a different Union depth or position in each peer; positional fields and overlapping Union slots inside the payload must remain aligned, and the receiving App decoder validates the untouched payload. See [endpoint protocol validation](../schema.md#61-endpoint-protocol-validation).
- **`@LibertasEndpointBaseObjects("$.device,$.room")`**: Binds the Endpoint instance to the configuration objects named by one positional quoted comma-delimited path string; the Hub derives the Endpoint's displayed name and icon from them. Every path must resolve to set values, and task-value validation rejects two Endpoint instances in one task that resolve to exactly the same base-object set; see [path-based object binding](../schema.md#7-path-based-object-and-choice-binding).
An occurrence is absolutely required when neither the Endpoint nor an ancestor
is Nullable and no ancestor array permits zero items. Clients traverse the
complete effective function-input tree, queue the original creation while
installing every missing required task, and rerun the check after installation.
Cancelling any required dependency installation cancels the originating task
creation. See the normative
task pre-creation dependency check.
Protocol Roles & Recursive Variant Validation
Within your protocol union type, every variant must satisfy one of the following requirements:
@LibertasRequest/@LibertasResponse@LibertasSubscriptionRequest/@LibertasSubscriptionData(Note: A variant can hold multiple roles).
Add the no-argument @LibertasError marker beside @LibertasResponse when
the complete response represents an error result rather than a normal result.
It emits Error=true; it does not imply the Response role and does not alter
the payload, discriminator, wire encoding, routing, or transition graph. The
payload remains an ordinary typed response. A consuming client may use the
marker to select its standard error procedure. The error is out-of-band of the
current protocol state transition: Android and iOS consume the correlated
response without installing or visualizing it, preserve the current
interaction and current response data, and leave retry available. They toast
the first renderable FormattedText found in deterministic depth-first
response order (including hidden fields), or a localized generalized error
when none can be rendered. Error classification takes precedence over normal
default-response, chart, and data presentation.
Optionally mark a Nullable String field (string | null)
with the no-argument @LibertasErrorFieldPath to emit
ErrorFieldPath=true. The error field path contract
owns placement, rejected-Request square paths and client marking after the
error dialog. Null names no field.
Outside workflows and automatic WorkflowRequest entry, a normal correlated
@LibertasResponse may omit its effective
@LibertasNextRequest. Omission is a non-transition acknowledgement, commonly
used by shared Success or Failure variants; it never enables every Request
in the Union. Clients complete the in-flight attempt without displaying the
acknowledgement as protocol data, changing the current choices, or displacing
a live DataReport. Its reference context follows the
interaction reference-context rule.
For a variant that is both Response and SubscriptionData, the explicit runtime operation controls:
response delivery follows this rule, while SubscriptionData/DataReport delivery
still refreshes subscribed state. @LibertasError remains the higher-priority
out-of-band procedure above.
The protocol contract owns automatic versus explicit subscription selection, callback prerequisites, and per-user access. Argument-free does not mean automatic.
Use @LibertasAccessPrivilege("Read"),
@LibertasAccessPrivilege("Write"), or
@LibertasAccessPrivilege("Read,Write") on a @LibertasRequest or
@LibertasSubscriptionRequest to restrict
which end users may invoke that runtime operation. The parser emits the
language-neutral integer bitmap AccessPrivilege=1, 2, or 3. The listed
levels are alternatives: any matching effective access to the owning endpoint
authorizes the operation. If omitted, the default is Read,Write (3), so a
read-only user can invoke the operation. Libertas cannot infer whether an
application-specific operation mutates state; developers must explicitly select
Write for operations that require write access. Studio filters disallowed
operations and the Hub independently decodes and enforces the selected protocol input
before routing it.
AccessPrivilege is not a task Permission. Permissions grant a task process
access to data and resources and are checked during configuration.
AccessPrivilege constrains an end user's access to one protocol operation at
runtime. Currently only Read=1 and Write=2 are defined; additional bitmap
values are reserved.
/**
* Check equipment status
* Lets an authorized person view the equipment's current condition.
* @LibertasRequest
* @LibertasAccessPrivilege("Read")
*/
export interface GetStatus { kind: 0; }
/**
* Change equipment availability
* Lets an authorized person make the equipment available or unavailable.
* @LibertasRequest
* @LibertasAccessPrivilege("Write")
*/
export interface SetEnabled {
kind: 1;
/**
* Equipment available
* When selected, the equipment may operate; when cleared, the equipment
* remains unavailable for operation.
*/
enabled: boolean;
}
If the variant is directly resolved as a union type itself or a single-field union wrapper, role validation is performed recursively on the members of that union type.
@LibertasWorkflowRequest("EditProfile") declares the
WorkflowRequest semi-role.
@LibertasPrevRequest("ReopenProfile,ReopenAddress") belongs on a Request and
declares ordinary Back actions constructed from the current editor. Their
controls are suppressed in embedded master-detail trailing panes under the
pane contract.
NextResponse also permits Request variants returned as
correlated EditRequest messages; they need no Response role. See the
workflow contract for completion,
initialization, explicit submission, and operation ownership.
@LibertasLink("OpenTopic") on any field, or on a struct, enum, or union
declaration, names the one Request variant that tapping that node opens;
see links and link rendering.
The optional Boolean stacked defaults to false: omission or false preserves
the ordinary Link value. @LibertasLink("OpenTopic", stacked=true) opens the completing
Response on a stacked page; the Links contract owns
its input-free LinkOnly target and subscription requirements.
@LibertasLinkOnly, with no argument, on a Request variant that a link
targets (or a SubscriptionRequest targeted by a stacked link) offers it only
through links, never in Actions; see
link-only requests.
@LibertasRequestConfirmation, with no argument, on a Request variant
makes a client ask the person to confirm before it sends the request; see
critical requests.
Advanced Chaining & Caching
- **`@LibertasNextRequest("...")`** / **`@LibertasNextResponse("...")`**: Connects multi-step conversations by specifying the next valid step. The value **must be non-empty and match the native name of variants** (which is the resolved discriminator value name). You can provide a comma-delimited list for multiple valid next steps. `NextRequest` is optional on a Response; omission is the non-transition acknowledgement above, not an implicit list of every Request.
State Management
- **`@LibertasCalculationRequest("CalculatePrice,CalculateShipping")`**: Declares the calculation Request variants a field participates in. See the [field calculation contract](../protocols.md#field-calculation-requests) for placement, participation and its checks, root resolution, and client implementation scope.
- **`@LibertasCalculationRoot("CalculatePrice")`**: Marks the node a calculation is for and names the calculations it roots. Placement, the required root and root-relative paths follow the [calculation root](../protocols.md#calculation-root) contract.
-
**`@LibertasCopyTo("@.orderTotal")`**: Assigns a successful calculation response field into the originating editor, inside the root's subtree. On a reduced SubscriptionData variant, use a strict variant-rooted destination list such as `^
,^ ` under the [reduced subscription pattern](../protocols.md#reduced-subscriptiondata-design-pattern). See [calculation results](../protocols.md#applying-calculation-results) for types, null, atomicity, indexed paths, ownership, resets, and deferred execution. -
**`@LibertasCopyFrom("$.first,$.fallback")`**: Valid only on protocol Request/SubscriptionRequest data. It uses one positional quoted comma-delimited path string. The [interaction reference-context rule](../protocols.md#interaction-reference-context) supplies the ordinary construction source; ordered selection, missing values, and initialization follow the [shared copy rules](../schema.md#shared-copyfrom-and-copyto-rules).
- Path syntax and context follow Libertas Schema Path.
Ordinary CopyFrom supports strict variant-based paths such as
^<FullState.value>,^<OtherState.value>, using the same resolution as directed subscription CopyTo. CopyFrom takes the first match; CopyTo rejects multiple matches as an App design flaw.$root-relative shorthand remains supported. Calculation mappings are instead relative to their calculation root and read reference objects only through&paths.
- Path syntax and context follow Libertas Schema Path.
Ordinary CopyFrom supports strict variant-based paths such as
Semantic validation rules
Use @LibertasValidationRules("...") on a function, any data field, or any
schema data-type declaration. Function rules see all arguments as one Table;
field and named-type rules are cumulative and are rooted at the annotated value.
The empty path [] denotes that value. A square path reads current editor data,
while {$.path,$.fallback} searches the current interaction reference context
in source-then-path order under the curly-reference Libertas Schema Path form. Missing
and null paths are skipped; the first non-null value wins, and no match
evaluates to null.
Single | is a typed first-non-null fallback. It evaluates operands from left
to right, returns the first non-null value unchanged, and stops before later
operands. false, zero, empty text/collections, and objects are non-null and
win. It binds after additive operators and before is and comparisons:
/**
* @LibertasValidationRules('([preferred] | [legacy] | 100) >= 0 : "VALID_LIMIT";')
*/
The selected value may have any supported intermediate type, but the complete rule must be Boolean. Use an explicit comparison for non-Boolean values; there is no general truthiness conversion.
/**
* Nonnegative amount
* Describes an amount that may be zero or greater when supplied.
* @LibertasValidationRules('[] == null || [] >= 0 : "NONNEGATIVE";')
*/
export type NonNegative = number;
/**
* Percentage range
* Describes a percentage that may be selected between zero and one hundred.
*/
export interface Range {
/**
* Percentage
* The user enters a percentage from zero through one hundred.
* @LibertasValidationRules('[] == null || [] <= 100 : "PERCENTAGE_MAXIMUM";')
*/
percentage: NonNegative;
}
/**
* Choose an operating range
* The user chooses inclusive lower and upper limits for an operating range.
* The application prevents the lower limit from exceeding the upper limit.
* Developed by Example Developer (example_developer).
* @LibertasValidationRules('[minimum] == null || [maximum] == null || [minimum] <= [maximum] : "INVALID_RANGE";')
*/
export declare function configure(
/**
* Lower limit
* The user enters the smallest amount included in the operating range.
*/
minimum: number,
/**
* Upper limit
* The user enters the largest amount included in the operating range.
*/
maximum: number,
): void;
Null tests are valid for non-nullable fields because an editor may not have user
input yet. Final submission still requires ordinary structural validity and all
rules to evaluate to true. A rule set without a directive (or with FinalOnly=false;) is also
evaluated incrementally, so it must tolerate incomplete editing; prefix
FinalOnly=true; to keep that complete rule set final-only. Every rule remains in final validation. The directive stays inside the
ValidationRules value and is not a separate schema attribute. Every
expression must be followed by :, a quoted package StringResources key,
and ;. Studio rejects undeclared keys and empty default-locale messages.
Clients stop at the first failing applicable rule in declaration order and
display its localized message. A missing or blank installed message produces a
generic validation failure. See the normative
rule language.
For a response-dependent numeric or DateTime control, including an editable request, use @LibertasRangeMin and
@LibertasRangeMax with one value expression each:
/**
* @LibertasRangeMin('{$.limits.minimum}')
* @LibertasRangeMax('{$.limits.maximum}')
*/
percentage: number;
The expression must produce a bound compatible with the control’s semantic type. A Boolean predicate
such as [] <= 100 is invalid; use 100 to set that maximum on an ordinary
Number. Date controls require their respective semantic date types. The
dynamic range contract
defines static/runtime type rejection, compatible date values, first-response
absence, and enforcement in both the GUI and final validation.
Testing validation rules
Keep TypeScript compilation and application tests alongside schema validation.
For client-rule fixtures in CI, use Studio's installed schema compiler launcher
with test-rules, the same command used by the Rust SDK's
examples/validation-rules sample. Fixtures use the schema's JSON value form
regardless of source language. See
Debugging rules for the fixture format,
exact expectations, report, exit status and launcher command. It shares the
compile path and evaluator with evaluate_validation_rules.
Endpoint Signaling and Liveness (non-normative summary)
Use the endpoint signaling orientation
and the installed @libertas-ai/types endpoint declarations before implementing
callbacks. The public SDK owns the complete contract. Its TypeScript liveness
entry point is libertasEndpointPeerAlive(endpoint, peer?).
Standard Data Schemas
libertasData<Keys, Values>() supplies the synchronous schema-bound context;
data.async() supplies the Promise API. Both accept exact sorted-key targets
and prefixes. The transactional API
owns the primitive inventory, ownership rules, scan continuations, and limits.
Ordinary Data API failures stop the task in the Hub runtime without permanently
disabling it or automatically restarting it. A function may explicitly opt into
checked status-returning calls with DataErrorStatus. See the
Data API failure contract
for the flag, return values, consistency responsibility, and third-party Enable action.
Place @LibertasDataErrorStatus in the public task function's JSDoc to permit
try… methods on the Data or transaction handle. They return DataResult<T> with a numeric DataStatus;
only Success carries value. Ordinary Data methods retain default
failure behavior, even on opted-in tasks.
Every exported task function declares both storage unions:
@LibertasDataSchema(key=DataKey,value=StoredData). Both named arguments are
required exactly once. Each native TypeScript reference resolves to its local
schema name or external App FQN. Studio marks the value union LibertasData=true.
Use normal native union declarations; no standalone persistence marker is needed.
Key String and Uint8Array fields reuse @LibertasSize(min=...,max=...) with byte
bounds. A maximum alone selects a bounded payload; explicit equal bounds select
an exact-length payload. Numeric fields reuse @LibertasNumber(type=...).
Optional fields use the existing Nullable mapping. See the
shared storage schema rules.
Singleton tasks
Use @LibertasSingleton on an exported function when a Hub may contain at most
one task that instantiates that function. The marker is invalid on the package,
types, fields, and function parameters, and it cannot take arguments or appear
twice.
/**
* Coordinate irrigation
* The irrigation system uses one coordination service so all watering
* activities share a single coordinator. Developed by Example Developer
* (example_developer).
* @LibertasExport
* @LibertasSingleton
*/
export function coordinateIrrigation(): void { }
The singleton identity is the App plus the function's native name; it is not a package-wide restriction and it does not reset between App versions. Studio disables a singleton function when another matching task is already present, and the Hub authoritatively checks every create, retarget, and task-version change. A task may update itself. If two selected versions disagree about the marker, the function remains singleton when either version declares it.
Permissions
Use @LibertasPermissions(CONST_NAME) on a function to declare the permissions it requires. The referenced declaration must be a const initialized with a string array literal; the parser reports an error if the constant is missing, mutable, not an array, or contains a non-string literal. Every permission must also be a key in the package StringResources, and the default-locale value must be a non-empty explanation of why the permission is needed. Because the constants are schema-only metadata, the compiler may eliminate them when the application does not otherwise use them.
/// Local weather service
/// Uses the installation's location to provide weather for nearby activities.
/// @LibertasStringResources(WeatherStrings)
const WeatherPermissions = [
"libertas.permission.ACCESS_FINE_LOCATION"
] as const;
const WeatherStrings = {
"libertas.permission.ACCESS_FINE_LOCATION":
"Use the installation's location to request the correct local weather forecast."
} as const;
/**
* Start local weather service
* The service uses the installation's location to retrieve the correct local
* forecast for weather-aware activities. Developed by Example Developer
* (example_developer).
* @LibertasExport
* @LibertasPermissions(WeatherPermissions)
*/
export function startWeatherService(): void { }
This produces "Permissions": "libertas.permission.ACCESS_FINE_LOCATION" on the function and stores the explanation under the same key in the package document's StringResources. Studio's shared validator rejects missing keys and blank explanations.
The same decorator may be placed on a built-in Endpoint property marked @LibertasEndpointServer. Its permissions are downstream access requirements and do not need same-key reason resources. The Hub compares them with the selected client App task's function permissions once during task approval/configuration; it does not repeat the check for each endpoint message.
/**
* Weather service connection
* Describes the connection through which weather activities are available.
* @LibertasExport
*/
export interface WeatherServer {
/**
* Weather service
* The user chooses the weather service used by this weather-aware activity.
* @LibertasEndpointSchema(WeatherProtocol)
* @LibertasEndpointServer
* @LibertasPermissions(WeatherPermissions)
*/
endpoint: LibertasEndpoint;
}
Network host access
Use @LibertasAccessHost("host1,host2") on a function to declare the DNS
hostnames its task process needs to contact. Every exact hostname must be a key
in the package StringResources, and its non-empty default-locale value must
explain why the access is required.
/// Weather-aware irrigation
/// Adjusts watering with observations supplied by an online weather service.
/// @LibertasStringResources(NetworkStrings)
const NetworkStrings = {
"api.weather.example":
"Download weather observations used to adjust the watering schedule."
} as const;
/**
* Weather-aware irrigation
* The irrigation controller retrieves recent weather observations and uses
* them to adjust watering schedules. Developed by Example Developer
* (example_developer).
* @LibertasAccessHost("api.weather.example")
*/
export function irrigationController(): void { }
The value is a comma-delimited hostname list with no whitespace. Hostnames are unique without regard to DNS letter case. Do not include a URL scheme, port, path, wildcard, or IP address; those are not hostname identities in this contract. DNS may resolve the declared name to different addresses over time, while the resource key and localized access explanation remain stable.
Without this attribute the task has neither local nor external TCP/UDP access.
A concrete host grants both TCP and UDP to all of its resolved IPv4/IPv6
addresses. Use {network_access_local} or {network_access_external} only for
the corresponding broad class; both are reserved resource keys and still need
localized reasons. The Hub refreshes DNS at runtime and adds new answers
without removing older answers until the task process restarts. Changing the
declaration forces that restart. See App network access.
String Resources
Declare one localized catalog for the whole package. Notifications, permission
and host-access reasons, data names, FormattedText, and formatted chart labels from every
function use the same stable package-local keys. The task already identifies
the App package, so encoded tuples do not carry a function/type owner or FQN.
/// Garden sprinkler controls
/// Lets people start watering for configured garden areas.
/// @LibertasStringResources(AppStrings)
const AppStrings = {
"MSG_SUCCESS": "The sprinkler started watering the selected zone.",
"MSG_ERROR": "The sprinkler could not start watering: {0}"
} as const;
/**
* Start zone watering
* The user can start the sprinkler for one configured watering zone.
* Developed by Example Developer (example_developer).
* @LibertasExport
*/
export function triggerSprinkler(): void { }
These strings provide the default translations for the UI. Users and developers can then translate these strings into other languages within the platform.
For a TypesOnly package, the same package-level declaration is valid. The
referenced declaration must be one non-empty const object literal with unique,
non-empty keys and literal string values:
/// Shared status messages
/// Provides reusable localized status messages for compatible products.
/// @LibertasTypesOnly
/// @LibertasStringResources(ProtocolStrings)
const ProtocolStrings = { READY: "The equipment is ready." } as const;
/**
* Status message
* Describes localized wording shown for a reported condition.
*/
export interface OutputText {
/**
* Current status
* Gives people the localized status reported by the application.
* @LibertasReadOnly
* @LibertasFormattedText
*/
status: Uint8Array;
}
The keys are emitted in QWHAScriptPackageJson.Attributes; localized values are
stored once in PackageDocumentJson.StringResources. OutputText has no
resource field or document table.
Localized Documentation
Libertas supports full localization of your package documentation. If your primary language is English, you can create translated documentation files for your package.
No manual alignment is required: The IDE automatically updates the structure of all localized documentation files when you save changes to your TypeScript source code, guaranteeing your translations stay in sync.
@LibertasFoldable permits manual folding on a struct, union, or list type or field; @LibertasFoldable(initially_open=false) starts it collapsed. Both are compiled into the Display Template. See folding structs and unions.
@LibertasInlineChoices on an enum or union declaration, or on a field of either type, shows its choices inline as radio controls and is compiled into the Display Template; see inline choices.
Backend-constrained choices
@LibertasConstrainedBy("$.allowed_choices") constrains an enum or union request field using a hidden,
backend-provided integer list in the current request. See the
complete contract for numeric identity,
initialization, calculated allowed sets, validation, and payload editing.
Default endpoint entry uses @LibertasDefaultRequest on a Request variant whose payload needs no user input (Nil, or only hidden/read-only Fixed or Default fields). Response-level ConstrainedBy filters NextRequest through a hidden integer array. See default entry and dynamic actions.
For additional response controls, put @LibertasActionControl("sections")
and @LibertasHidden on a direct integer-list field of a Response or
SubscriptionData payload with NextRequest. Give the list member its normal
integer NumberType annotation. The values are original protocol Union Request
discriminants; "Actions" names the built-in button. See
named action controls.
For large workflow request payloads, @LibertasCacheIdentity marks direct root-record
identity fields for opt-in draft recovery. Small requests omit it. See the
CacheIdentity contract for immutable multi-field
identity, optional application protocol revision, and restoration rules.
Subscription array append
On a report's root array field:
/** @LibertasAppendTo("Entries.entries") @LibertasAppendIndex("$.start_index") */
added_entries: Entry[];
AppendTo names one working variant and root array. Optional AppendIndex
references the report's non-null integer field holding the expected current
length. Elements append in order, retaining duplicates and assignable nulls;
invalid indices or resulting values reject the entire report. The App supplies
complete initial snapshots and owns ordering and retention. See the
array append contract for
placement, combining CopyTo with append, exclusion of index controls from copied
data, writer conflicts, independent recipients, and live charts.
AI Search guidance
Use @LibertasAINavigationPrompt("NAVIGATION_PROMPT") on a protocol Union or page variant to reference
a package StringResource. Both Union and page guidance contribute, in that
order. Document destination vocabulary in each offered topic or Enumeration
member’s extracted description. See AI Search
for candidate construction, selection, confirmation, and submission rules.