chaokunyang opened a new issue, #3007:
URL: https://github.com/apache/fory/issues/3007

   ## Feature Request
   
   Create a `ForyField` decorator and configuration option for 
JavaScript/TypeScript to provide field-level metadata for performance and space 
optimization during xlang serialization.
   
   ## Is your feature request related to a problem? Please describe
   
   Currently, Fory's JavaScript xlang serialization treats all object fields 
uniformly:
   1. **Null checks are always performed** - Even for fields that are never 
null/undefined, Fory writes a null/ref flag (1 byte per field)
   2. **Reference tracking is always applied** (when enabled globally) - Even 
for fields that won't be shared/cyclic, objects are tracked with hash lookup 
cost
   3. **Field names use meta string encoding** - In schema evolution mode, 
field names are encoded using meta string compression, but for fields with long 
names, this still takes space
   
   These defaults ensure correctness but introduce unnecessary overhead when 
the developer has more specific knowledge about their data model.
   
   ## Describe the solution you'd like
   
   ### Option 1: TypeScript Decorators (Stage 3)
   
   ```typescript
   import { ForyClass, ForyField } from '@anthropic/fory';
   
   @ForyClass()
   class Foo {
     // Field f1: non-nullable (default), no ref tracking (default)
     // Tag ID 0 provides compact encoding in schema evolution mode
     @ForyField({ id: 0 })
     f1: string;
     
     // Field f2: non-nullable (default), no ref tracking (default)
     @ForyField({ id: 1 })
     f2: Bar;
     
     // Field f3: nullable field that may contain null values
     @ForyField({ id: 2, nullable: true })
     f3: string | null;
     
     // Field parent: shared reference that needs tracking (e.g., for circular 
refs)
     @ForyField({ id: 3, ref: true, nullable: true })
     parent: Node | null;
     
     // Field with long name: tag ID provides significant space savings
     @ForyField({ id: 4 })
     veryLongFieldNameThatWouldTakeManyBytes: string;
     
     // Explicit opt-out: use field name encoding but get nullable optimization
     @ForyField({ id: -1, nullable: true })
     optionalField: string | null;
   }
   ```
   
   ### Option 2: Schema Configuration (No Decorators)
   
   For environments without decorator support:
   
   ```typescript
   import { Fory, Type } from '@anthropic/fory';
   
   const fory = new Fory();
   
   // Define type with field metadata
   fory.registerType({
     name: 'Foo',
     fields: {
       f1: { type: Type.String, id: 0 },
       f2: { type: Type.Object, id: 1, typeName: 'Bar' },
       f3: { type: Type.String, id: 2, nullable: true },
       parent: { type: Type.Object, id: 3, typeName: 'Node', ref: true, 
nullable: true },
       veryLongFieldNameThatWouldTakeManyBytes: { type: Type.String, id: 4 },
       optionalField: { type: Type.String, id: -1, nullable: true },
     },
   });
   
   // Usage
   const foo = { f1: 'hello', f2: bar, f3: null, ... };
   const bytes = fory.serialize(foo, 'Foo');
   ```
   
   ### Option 3: Class with Static Schema
   
   ```typescript
   import { ForySerializable } from '@anthropic/fory';
   
   class Foo implements ForySerializable {
     f1: string;
     f2: Bar;
     f3: string | null;
     parent: Node | null;
     
     static forySchema = {
       f1: { id: 0 },
       f2: { id: 1 },
       f3: { id: 2, nullable: true },
       parent: { id: 3, ref: true, nullable: true },
       veryLongFieldNameThatWouldTakeManyBytes: { id: 4 },
       optionalField: { id: -1, nullable: true },
     };
   }
   ```
   
   ### ForyField Configuration
   
   ```typescript
   interface ForyFieldConfig {
     /**
      * Field tag ID for schema evolution mode (REQUIRED).
      * - When >= 0: Uses numeric ID instead of field name for compact encoding
      * - When -1: Explicitly opt-out, use field name with meta string encoding
      * Must be unique within the class (except -1) and stable across versions.
      */
     id: number;
     
     /**
      * Whether this field can be null/undefined.
      * When false (default), Fory skips writing the null flag (saves 1 byte).
      * When true, Fory writes null flag for nullable fields.
      * Default: false (aligned with xlang protocol defaults)
      */
     nullable?: boolean;
     
     /**
      * Whether to track references for this field.
      * When false (default):
      * - Avoids adding the object to reference tracking (saves hash overhead)
      * - Skips writing ref tracking flag
      * When true, enables reference tracking for shared/circular references.
      * Default: false (aligned with xlang protocol defaults)
      */
     ref?: boolean;
   }
   ```
   
   ### Design Decision: Required `id`
   
   The `id` property is **required**:
   - `id: 0` to `id: N`: Use tag ID encoding (compact)
   - `id: -1`: Explicit opt-out, use field name encoding
   
   Rationale:
   1. **Explicit control**: Using `ForyField` means opting into explicit control
   2. **Runtime validation**: Can check for duplicate IDs during registration
   3. **Proven pattern**: Similar to protobuf field numbers
   
   ### Optimization Details
   
   #### 1. `nullable: false` (Default) Optimization
   
   When `nullable: false` (default):
   - Skip writing the null flag entirely (1 byte saved per field)
   - Directly serialize the field value
   - **Throw error** if field value is `null` or `undefined` at runtime
   
   #### 2. `ref: false` (Default) Optimization
   
   When `ref: false` (default):
   - Skip reference tracking Map operations
   - Skip ref flag when combined with `nullable: false`
   
   #### 3. Tag ID Optimization
   
   When `id >= 0`:
   - Field name encoded as varint instead of meta string
   - Significant space savings for long field names
   
   **Space savings:**
   
   | Field Name | Meta String (approx) | Tag ID |
   |------------|---------------------|--------|
   | `f1` | ~2 bytes | 1 byte |
   | `userName` | ~6 bytes | 1 byte |
   | `transactionId` | ~10 bytes | 1 byte |
   
   ### Implementation Notes
   
   1. **Decorator Implementation**:
      ```typescript
      // fory/decorators.ts
      export function ForyField(config: ForyFieldConfig): PropertyDecorator {
        return (target, propertyKey) => {
          const metadata = Reflect.getMetadata('fory:fields', target) || {};
          metadata[propertyKey] = config;
          Reflect.defineMetadata('fory:fields', metadata, target);
        };
      }
      ```
   
   2. **Schema-based Registration**:
      ```typescript
      // fory/schema.ts
      class Fory {
        registerType(schema: TypeSchema): void {
          // Validate field IDs are unique
          // Store schema for serialization
        }
        
        serialize(obj: any, typeName: string): Uint8Array {
          const schema = this.schemas.get(typeName);
          // Use schema.fields for optimized serialization
        }
      }
      ```
   
   3. **Validation**:
      - **Throw error** if duplicate tag IDs (>= 0) in same type
      - **Throw error** if `id < -1`
      - **Throw error** if `nullable: false` but field is `null`/`undefined`
   
   4. **TypeScript Type Safety**:
      ```typescript
      // Type helper for nullable fields
      type NullableField<T> = T | null | undefined;
      
      // Enforce nullable: true for nullable types (via ESLint plugin or 
similar)
      ```
   
   ### Performance Impact
   
   For an object with 10 fields using default settings (`nullable: false`, 
`ref: false`):
   - **Space savings**: ~20 bytes per object (null + ref flags)
   - **CPU savings**: 10 fewer Map operations per serialization
   
   ## Additional context
   
   This is the JavaScript/TypeScript equivalent of Java's `@ForyField` 
annotation. See [Java issue #3000](https://github.com/apache/fory/issues/3000) 
for the original design discussion.
   
   Protocol spec: 
https://fory.apache.org/docs/specification/fory_xlang_serialization_spec


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to