FieldedText TypeScript Library
    Preparing search index...

    Sequences with Readback Example

    This example demonstrates sequence redirects, which allow different records to have different field structures based on field values.

    1. Deserializes metadata from an XML string with multiple sequences for different pet types (Cat, Dog, GoldFish)
    2. Uses sequence redirects to include different fields based on a Type field
    3. Demonstrates nested redirects (Dog training fields only appear if Training=true)
    4. Writes sample data for different pet types
    5. Reads the data back and displays appropriate fields for each type
    npx tsx examples/sequences/index.ts
    
    === Sequence Redirects Example ===
    
    Demonstrates conditional field structures using sequence redirects.
    Different pet types have different fields:
    
    Metadata structure:
      Root: Type, Name
      Cat (Type=1): RunningSpeed
      Dog (Type=2): WalkDistance, RunningSpeed, Training
        Training (Training=true): Trainer, SessionCost
      GoldFish (Type=3): Color, Classification
    
    === Generated CSV ===
    1,Misty,45
    1,Oscar,35
    2,Buddy,0.5,35,False
    2,Charlie,2,48,True,John,32
    2,Max,0.5,30,False
    3,Bubbles,Orange,Wen
    3,Flash,Yellow,Crucian
    
    Total redirects triggered: 8
    
    === Reading Back Records ===
    
    1. Misty (Type 1): Running speed 45
    2. Oscar (Type 1): Running speed 35
    3. Buddy (Type 2): Walk 0.5, Run 35, No training
    4. Charlie (Type 2): Walk 2, Run 48, Training with John ($32)
    5. Max (Type 2): Walk 0.5, Run 30, No training
    6. Bubbles (Type 3): Orange Wen
    7. Flash (Type 3): Yellow Crucian
    
    • Meta deserialization: Load metadata from XML text with FtXmlMetaSerialization.deserialize()
    • Sequence redirects: Change field structure based on field values
    • Root sequence: Always present at the start of each record
    • Redirect conditions: Match field values (ExactInteger, Boolean, ExactString, etc.)
    • Invokation delay: Fields added immediately (AfterField) or after sequence ends (AfterSequence)
    • Nested redirects: Redirects within redirected sequences
    • Variable record types: Different fields for different record types in one file
    • Optional field groups: Include fields only when needed
    • Type-based layouts: Client records vs Transaction records
    • Conditional data: Survey responses with follow-up questions
    • Polymorphic data: Different attributes based on entity type
    // Sequences Example
    // Demonstrates sequence redirects for conditional field structures
    // Models the C# BuildMetaWithSequences/WriteSequence examples

    import {
    FtReader,
    FtStringWriter,
    FtWriter,
    FtXmlMetaSerialization,
    } from "@pbkware/fielded-text-web";

    console.log("=== Sequence Redirects Example ===\n");
    console.log(
    "Demonstrates conditional field structures using sequence redirects.",
    );
    console.log("Different pet types have different fields:\n");

    // Load metadata from XML string
    const metaXml = `<?xml version="1.0" encoding="utf-8"?>
    <FieldedText>
    <Field DataType="Integer" Name="Type" />
    <Field Name="Name" />
    <Field DataType="Float" Name="RunningSpeed" />
    <Field DataType="Float" Name="WalkDistance" />
    <Field DataType="Boolean" Name="Training" />
    <Field Name="Trainer" />
    <Field DataType="Decimal" Name="SessionCost" />
    <Field Name="Color" />
    <Field Name="Classification" />
    <Sequence Name="Root" Root="True">
    <Item FieldIndex="0">
    <Redirect SequenceName="Cat" InvokationDelay="AfterSequence" Value="1" />
    <Redirect SequenceName="Dog" InvokationDelay="AfterSequence" Value="2" />
    <Redirect SequenceName="GoldFish" InvokationDelay="AfterSequence" Value="3" />
    </Item>
    <Item FieldIndex="1" />
    </Sequence>
    <Sequence Name="Cat">
    <Item FieldIndex="2" />
    </Sequence>
    <Sequence Name="Dog">
    <Item FieldIndex="3" />
    <Item FieldIndex="2" />
    <Item FieldIndex="4">
    <Redirect SequenceName="Training" Value="True" />
    </Item>
    </Sequence>
    <Sequence Name="Training">
    <Item FieldIndex="5" />
    <Item FieldIndex="6" />
    </Sequence>
    <Sequence Name="GoldFish">
    <Item FieldIndex="7" />
    <Item FieldIndex="8" />
    </Sequence>
    </FieldedText>`;

    const meta = FtXmlMetaSerialization.deserialize(metaXml);

    console.log("Metadata structure:");
    console.log(" Root: Type, Name");
    console.log(" Cat (Type=1): RunningSpeed");
    console.log(" Dog (Type=2): WalkDistance, RunningSpeed, Training");
    console.log(" Training (Training=true): Trainer, SessionCost");
    console.log(" GoldFish (Type=3): Color, Classification\n");

    // Write sample data
    const stringWriter = new FtStringWriter();
    const writer = new FtWriter(meta, stringWriter);

    // Track redirects
    let redirectCount = 0;
    writer.onSequenceRedirected = () => {
    redirectCount++;
    };

    // Cat records
    writer.setFieldValueByName("Type", BigInt(1));
    writer.setFieldValueByName("Name", "Misty");
    writer.setFieldValueByName("RunningSpeed", 45);
    writer.write();

    writer.setFieldValueByName("Type", BigInt(1));
    writer.setFieldValueByName("Name", "Oscar");
    writer.setFieldValueByName("RunningSpeed", 35);
    writer.write();

    // Dog records (without training)
    writer.setFieldValueByName("Type", BigInt(2));
    writer.setFieldValueByName("Name", "Buddy");
    writer.setFieldValueByName("WalkDistance", 0.5);
    writer.setFieldValueByName("RunningSpeed", 35);
    writer.setFieldValueByName("Training", false);
    writer.write();

    // Dog with training (nested redirect)
    writer.setFieldValueByName("Type", BigInt(2));
    writer.setFieldValueByName("Name", "Charlie");
    writer.setFieldValueByName("WalkDistance", 2);
    writer.setFieldValueByName("RunningSpeed", 48);
    writer.setFieldValueByName("Training", true);
    writer.setFieldValueByName("Trainer", "John");
    writer.setFieldValueByName("SessionCost", 32);
    writer.write();

    // Another dog without training
    writer.setFieldValueByName("Type", BigInt(2));
    writer.setFieldValueByName("Name", "Max");
    writer.setFieldValueByName("WalkDistance", 0.5);
    writer.setFieldValueByName("RunningSpeed", 30);
    writer.setFieldValueByName("Training", false);
    writer.write();

    // GoldFish records
    writer.setFieldValueByName("Type", BigInt(3));
    writer.setFieldValueByName("Name", "Bubbles");
    writer.setFieldValueByName("Color", "Orange");
    writer.setFieldValueByName("Classification", "Wen");
    writer.write();

    writer.setFieldValueByName("Type", BigInt(3));
    writer.setFieldValueByName("Name", "Flash");
    writer.setFieldValueByName("Color", "Yellow");
    writer.setFieldValueByName("Classification", "Crucian");
    writer.write();

    // writer.close(); // Not necessary due to 'using' declaration

    const csvText = stringWriter.toString();

    console.log("=== Generated CSV ===");
    console.log(csvText);
    console.log(`Total redirects triggered: ${redirectCount}\n`);

    // Read back and verify
    console.log("=== Reading Back Records ===\n");

    const reader = new FtReader(meta, csvText);
    reader.autoNextTable = true; // Continue across table boundaries

    let recordNum = 0;
    while (reader.read()) {
    recordNum++;
    const type = reader.getFieldByName("Type")?.asBigInt;
    const name = reader.getFieldByName("Name")?.asString;

    console.log(`${recordNum}. ${name} (Type ${type}): `);

    if (type === BigInt(1)) {
    // Cat
    const speed = reader.getFieldByName("RunningSpeed")?.asFloat;
    console.log(`Running speed ${speed}`);
    } else if (type === BigInt(2)) {
    // Dog
    const walk = reader.getFieldByName("WalkDistance")?.asFloat;
    const speed = reader.getFieldByName("RunningSpeed")?.asFloat;
    const training = reader.getFieldByName("Training")?.asBoolean;
    if (training) {
    const trainer = reader.getFieldByName("Trainer")?.asString;
    const cost = reader.getFieldByName("SessionCost")?.asDecimal;
    console.log(
    `Walk ${walk}, Run ${speed}, Training with ${trainer} ($${cost})`,
    );
    } else {
    console.log(`Walk ${walk}, Run ${speed}, No training`);
    }
    } else if (type === BigInt(3)) {
    // GoldFish
    const color = reader.getFieldByName("Color")?.asString;
    const classification = reader.getFieldByName("Classification")?.asString;
    console.log(`${color} ${classification}`);
    }
    }

    console.log("\n=== Summary ===");
    console.log("Sequence redirects allow different records to have different");
    console.log("field structures based on field values. This is more flexible");
    console.log("than having all records with the same fields.");