Skip to content

Typed Document

Liu.Yandong.Hanks edited this page Aug 21, 2026 · 4 revisions

Typed Document (TDoc)

Use TDoc to serialize and restore AuroraScript values while preserving supported runtime types.

Applies to 4.0.0.

Home · Script TDoc API · .NET TDoc API

On this page

Introduction

TDoc is AuroraScript's typed text-data format. It writes one AuroraScript value as a standalone document and restores ordinary objects, arrays, packed arrays, Date, Path, Regex, HashMap, StringBuffer, and host-registered CLR/CIL objects when read.

A standalone TDoc document is not a second scripting syntax: its content does not execute code and does not support variables, calls, the script tdoc marker, or inline expressions; it starts directly with its root value.

Use Cases

  • Persist configuration, snapshots, or state that must retain AuroraScript type identity.
  • Exchange data between AuroraScript hosts when types such as packed arrays or Date must be preserved.
  • Write CLR/CIL objects explicitly registered through AuroraEngine.RegisterType into a controlled data contract.

TDoc does not treat runtime behavior as persistent data. When writing, functions, proxies, accessor properties, unregistered CLR/CIL objects, non-finite Numbers, and circular/shared references already seen in the current document are treated as skippable values rather than failing the entire stringify.

Quick Example

Object {
    readonly String id "u-001",
    String name "Aurora",
    Number version 4,
    Int8Array payload [-128, 0, 127],
    Date createdAt "2026-08-20 10:00:00",
}

The root value above is a ScriptObject; id is a shallow read-only property and payload remains a ScriptInt8Array, rather than becoming a general Array.

Use TDoc in scripts:

var profile = TDoc.parse('Object { String name "Aurora", Int8Array levels [1, 2] }');
var text = TDoc.stringify(profile, false);
return text;

Use AuroraTypedDocument in a host:

var tdoc = new AuroraTypedDocument(engine);
var text = tdoc.Serialize(ScriptDatum.FromString("Aurora"));
var value = tdoc.Deserialize(text);

Native tdoc Literals in Scripts

In an .as script, lowercase tdoc constructs a TDoc value directly. It is a compiler-recognized expression boundary, not a method call on the TDoc global; the result is a normal AuroraScript runtime value that can be assigned, returned, or created in a module initializer.

Basic Syntax

func createProfile(user, baseAge) {
    return tdoc Object {
        readonly String id $(user.id),
        name "Aurora",
        age $(baseAge + 1),
        tags [String "system", Number 4],
    };
}

The syntax shape is:

tdoc-literal = "tdoc" typed-value ;
typed-value  = [ type-name ] raw-value ;
raw-value    = "null" | boolean | number | string | array | object | interpolation ;
array        = "[" [ typed-value { "," typed-value } [ "," ] ] "]" ;
object       = "{" [ member { "," member } [ "," ] ] "}" ;
member       = [ "readonly" ] [ type-name ] property-name raw-value ;
property-name = identifier | string ;
interpolation = "$(" aurora-expression ")" ;
  • tdoc is lowercase and must be followed by one TDoc value; standalone .tdoc files do not write this prefix.
  • The type name is optional. Types uniquely inferred from their literals, such as Object, Array, String, Number, and Boolean, may be omitted; built-in types such as Int8Array, Date, Path, Regex, HashMap, and StringBuffer may be written explicitly to preserve identity.
  • Native script literals do not construct registered CLR/CIL objects from a type name; an existing instance may be supplied through $(). Text round-tripping of registered aliases is provided by TDoc.parse, TDoc.stringify, and the host AuroraTypedDocument API.
  • Object members use space-separated type-name property-name value syntax, not :. With one identifier it is a property name, such as name "Aurora"; two adjacent identifiers mean an explicit type and property name, such as String name "Aurora".
  • readonly applies only to object members and creates a shallow read-only property; it does not freeze an object stored in that property.

Dynamic Values

Only value positions accept $(). The contents are ordinary AuroraScript expressions evaluated at script runtime; it can be used for array elements, object member values, and the root value.

func createProfile(user, baseAge) {
    return tdoc Object {
        readonly String id $(user.id),
        role $(user.role),
        age $(baseAge + 1),
        tags [$(user.role), "system"],
    };
}

Property names and type names must be static text; dynamic injection is not supported:

tdoc Object { $(key) "value" }      // invalid: dynamic property name
tdoc $(typeName) { enabled true }    // invalid: dynamic type name

The pure TDoc data portion does not execute calls or other script expressions; dynamic computation must be explicit inside $(). Template strings, arbitrary unary expressions, and unwrapped calls cannot be used directly as TDoc values.

Boundary with Standalone .tdoc Documents

Native script literals and standalone documents share objects, arrays, type names, trailing commas, and readonly, but their entry points differ:

Entry Root syntax $() Typical use
.as script tdoc Object { ... } Allowed for values only Construct configuration or requests in code
.tdoc file / TDoc.parse Object { ... } Forbidden Configuration, persistence, and host exchange

A tdoc expression does not produce text by itself. Call TDoc.stringify(value, indented, emitTypes) when text is needed, and TDoc.parse(text) to restore a value from text. Standalone .tdoc files cannot contain variables, calls, or $().

Object View and Skippable Values

An ordinary ScriptObject is written from its script-visible enumerable properties: own properties first, then unshadowed properties along its prototype chain. The resulting TDoc is a flat object; prototype identity, the prototype chain, and accessors themselves are not preserved.

stringify uses fixed, non-throwing degradation for unrepresentable runtime values: object properties (including properties visible through a prototype) are omitted; array elements become null to preserve indices; an unrepresentable key or value in a HashMap entry becomes null; and an unrepresentable root becomes null. TDoc is therefore a data snapshot, not a lossless clone of a full object graph.

var action = () => true;
var value = { name: "Aurora", cancel: action, values: [1, action, 3] };
TDoc.stringify(value, false);
// {name "Aurora",values [1,null,3,],}

Text Structure

A document permits one root value. A type name may be explicit; when it is absent, only values uniquely determined by their raw literal are inferred automatically.

document      = typed-value EOF ;
typed-value   = [ type-name ] raw-value ;
raw-value     = "null" | boolean | number | string | array | object ;
array         = "[" [ typed-value { "," typed-value } [ "," ] ] "]" ;
object        = "{" [ member { "," member } [ "," ] ] "}" ;
member        = [ "readonly" ] [ type-name ] property-name raw-value ;
property-name = identifier | string ;
type-name     = identifier ;

In an object member, two consecutive identifiers mean “type name + property name”; one identifier is the property name. For example, String name "Aurora" explicitly declares String, while name "Aurora" lets the reader infer a string.

String values must use single or double quotes. Object { id UX01 } and Object { id UX01-03 } are both invalid: UX01 is in an identifier position in the former, and - is not part of an identifier in the latter. Write Object { id "UX01" } or Object { id "UX01-03" } instead.

Line comments (//), block comments (/* ... */), and trailing commas are supported. The script tdoc marker is invalid in standalone documents and produces a syntax error.

Types and Inference

Text shape Read result Type name emitted by default
null Null No
true / false Boolean No
42 / 1.5 / 0xFF Number (double) No
"text" / 'text' String No
[ ... ] ScriptArray No
{ ... } ScriptObject No
StringBuffer "text" StringBuffer Yes
Date "..." / Date 14425655658 ScriptDate Yes
Regex { ... } ScriptRegex Yes
Path "a/b.as" ScriptPathValue Yes
HashMap [ [key, value] ] ScriptHashMap Yes
Int32Array / Int8Array / Float64Array / BooleanArray Corresponding packed array Always
Registered type, for example User { ... } Registered CLR/CIL instance Always

The table lists read results and whether a type name is emitted by default.

EmitTypeNames defaults to false, so the writer emits only type names that cannot be uniquely inferred from raw literals. Object, Array, String, Number, and Boolean are omitted by default; Date, all object-like built-ins, all packed arrays, and registered CLR/CIL types still force a type name. Set EmitTypeNames to true to force every available type name.

// default: EmitTypeNames = false
{ name "Aurora", Int8Array bytes [1, 2] }

// EmitTypeNames = true
Object { String name "Aurora", Int8Array bytes [1, 2] }

readonly Properties

readonly is an object-property descriptor, not a type. It prevents the property from being written again, but does not freeze its object value.

Object {
    readonly Object settings { retries 3 },
}

After reading, settings = other fails, while settings.retries = 4 remains valid. The TDoc writer preserves the readonly marker.

Date, Regex, and HashMap

A Date string must match the current AuroraEngine EngineOptions.Runtime.DateTimeFormat; a numeric form is .NET ticks and must be an in-range integer. Writing always produces a string using the engine date format.

Date "2026-08-20 10:00:00"
Regex { pattern "ab+", flags "gi" }
HashMap [["name", "Aurora"], [1, true]]

HashMap uses two-element arrays for keys and values, so non-string keys do not degrade into object properties.

CLR/CIL Type Boundary

A CLR/CIL type name in a document must be an alias already registered on the current engine. TDoc never auto-loads a type from an assembly name, .NET type name, or reflection.

engine.RegisterType<User>("User");
var tdoc = new AuroraTypedDocument(engine);
var user = tdoc.Deserialize("User { String Name \"Hanks\", Number Age 18 }");

When reading, an explicit alias for an unregistered object, an unknown alias, a non-constructible type, or text that violates the registered member contract fails. When writing, an unregistered or non-writable CLR/CIL value follows the skippable-value rules. A registered type name such as User is never omitted, even when EmitTypeNames is false.

Errors and Diagnostics

Host APIs throw TypedDocumentException. Its SourceName, Line, Column, and DataPath locate the error; for example, $.meta.tags[2] identifies the third item in a nested array.

The script API converts this exception to AuroraRuntimeException, with a message starting with TDoc.parse error: or TDoc.stringify error:.

Next steps

Clone this wiki locally