or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

bundles-queries.mdcore-payload-types.mdindex.mdstorage-metadata.mdtype-guards.mdutility-types.mdvalidation-errors.md

utility-types.mddocs/

0

# Utility Types

1

2

Additional utility types for payload manipulation, filtering, value expressions, and type transformations to enhance developer experience and type safety within the XYO Protocol 2.0 ecosystem.

3

4

## Capabilities

5

6

### Payload Value Expressions

7

8

Type-safe property extraction and manipulation utilities for working with payload data properties.

9

10

```typescript { .api }

11

/**

12

* Type representing all property keys of a payload

13

*/

14

type PayloadProperty<T extends Payload = Payload> = keyof T;

15

16

/**

17

* Type representing the value of a specific payload property

18

*/

19

type PayloadValue<

20

T extends Payload = Payload,

21

Key extends PayloadProperty<T> = PayloadProperty<T>

22

> = T[Key];

23

24

/**

25

* Function type for extracting property values from payloads

26

*/

27

type PayloadValueExpression<

28

T extends Payload = Payload,

29

Key extends PayloadProperty<T> = PayloadProperty<T>,

30

TValue = PayloadValue<T, Key>

31

> = (payload: T) => TValue;

32

```

33

34

**Usage Examples:**

35

36

```typescript

37

import {

38

PayloadProperty,

39

PayloadValue,

40

PayloadValueExpression,

41

Payload

42

} from "@xyo-network/payload-model";

43

44

// Define custom payload type

45

interface UserPayload extends Payload {

46

schema: "network.example.user";

47

name: string;

48

email: string;

49

age: number;

50

preferences: {

51

theme: "light" | "dark";

52

notifications: boolean;

53

};

54

}

55

56

// Extract property types

57

type UserProperties = PayloadProperty<UserPayload>;

58

// "schema" | "name" | "email" | "age" | "preferences"

59

60

type NameValue = PayloadValue<UserPayload, "name">; // string

61

type PreferencesValue = PayloadValue<UserPayload, "preferences">;

62

// { theme: "light" | "dark"; notifications: boolean; }

63

64

// Create value expression functions

65

const getName: PayloadValueExpression<UserPayload, "name"> = (payload) => payload.name;

66

const getAge: PayloadValueExpression<UserPayload, "age"> = (payload) => payload.age;

67

68

// Generic value extractor

69

function createValueExtractor<T extends Payload, K extends PayloadProperty<T>>(

70

key: K

71

): PayloadValueExpression<T, K> {

72

return (payload) => payload[key];

73

}

74

75

// Use value extractors

76

const userPayload: UserPayload = {

77

schema: "network.example.user",

78

name: "Alice",

79

email: "alice@example.com",

80

age: 25,

81

preferences: {

82

theme: "dark",

83

notifications: true

84

}

85

};

86

87

const nameExtractor = createValueExtractor<UserPayload, "name">("name");

88

const emailExtractor = createValueExtractor<UserPayload, "email">("email");

89

90

console.log("Name:", nameExtractor(userPayload)); // "Alice"

91

console.log("Email:", emailExtractor(userPayload)); // "alice@example.com"

92

93

// Complex value expressions

94

const getDisplayName: PayloadValueExpression<UserPayload, "name", string> = (payload) => {

95

return `${payload.name} (${payload.age})`;

96

};

97

98

const getTheme: PayloadValueExpression<UserPayload, "preferences", string> = (payload) => {

99

return payload.preferences.theme;

100

};

101

102

console.log("Display:", getDisplayName(userPayload)); // "Alice (25)"

103

console.log("Theme:", getTheme(userPayload)); // "dark"

104

105

// Array processing with value expressions

106

const users: UserPayload[] = [

107

{ schema: "network.example.user", name: "Alice", email: "alice@example.com", age: 25, preferences: { theme: "dark", notifications: true } },

108

{ schema: "network.example.user", name: "Bob", email: "bob@example.com", age: 30, preferences: { theme: "light", notifications: false } }

109

];

110

111

const names = users.map(getName);

112

const ages = users.map(getAge);

113

const themes = users.map(getTheme);

114

115

console.log("Names:", names); // ["Alice", "Bob"]

116

console.log("Ages:", ages); // [25, 30]

117

console.log("Themes:", themes); // ["dark", "light"]

118

```

119

120

### Payload Collection Utilities

121

122

Interfaces and types for filtering, organizing, and managing collections of payloads.

123

124

```typescript { .api }

125

/**

126

* Filter interface for payload discovery and search operations

127

*/

128

interface PayloadFindFilter {

129

/** Maximum number of results to return */

130

limit?: number;

131

132

/** Sort order for results */

133

order?: 'desc' | 'asc';

134

135

/** Schema filter - single schema or array of schemas to match */

136

schema?: string | string[];

137

}

138

139

/**

140

* Hash map interface for efficient payload storage and lookup

141

*/

142

interface PayloadHashMap<

143

TPayload extends Payload = Payload,

144

TId extends string | number | symbol = Hash

145

> {

146

/**

147

* Data hash mappings - maps root hashes to data hashes

148

* Multiple root hashes may reference the same data hash

149

*/

150

dataHash: Record<TId, TId>;

151

152

/**

153

* Complete payload mappings - maps hashes to full payload objects

154

*/

155

hash: Record<TId, TPayload>;

156

}

157

```

158

159

**Usage Examples:**

160

161

```typescript

162

import {

163

PayloadFindFilter,

164

PayloadHashMap,

165

Payload,

166

Hash

167

} from "@xyo-network/payload-model";

168

169

// Payload filtering utilities

170

class PayloadFilter {

171

static applyFilter<T extends Payload>(

172

payloads: T[],

173

filter: PayloadFindFilter

174

): T[] {

175

let result = [...payloads];

176

177

// Schema filtering

178

if (filter.schema) {

179

const schemas = Array.isArray(filter.schema) ? filter.schema : [filter.schema];

180

result = result.filter(payload => schemas.includes(payload.schema));

181

}

182

183

// Sorting (simplified example)

184

if (filter.order) {

185

result.sort((a, b) => {

186

const aKey = a.schema;

187

const bKey = b.schema;

188

const comparison = aKey.localeCompare(bKey);

189

return filter.order === 'desc' ? -comparison : comparison;

190

});

191

}

192

193

// Limit results

194

if (filter.limit && filter.limit > 0) {

195

result = result.slice(0, filter.limit);

196

}

197

198

return result;

199

}

200

201

// Create common filters

202

static createSchemaFilter(schema: string | string[]): PayloadFindFilter {

203

return { schema };

204

}

205

206

static createLimitFilter(limit: number, order?: 'desc' | 'asc'): PayloadFindFilter {

207

return { limit, order };

208

}

209

210

static createCompleteFilter(

211

schema?: string | string[],

212

limit?: number,

213

order?: 'desc' | 'asc'

214

): PayloadFindFilter {

215

return { schema, limit, order };

216

}

217

}

218

219

// Example payloads

220

const payloads: Payload[] = [

221

{ schema: "network.example.user", name: "Alice", email: "alice@example.com" },

222

{ schema: "network.example.product", name: "Laptop", price: 999 },

223

{ schema: "network.example.user", name: "Bob", email: "bob@example.com" },

224

{ schema: "network.example.order", userId: "alice", productId: "laptop" },

225

{ schema: "network.example.user", name: "Charlie", email: "charlie@example.com" }

226

];

227

228

// Apply various filters

229

const userFilter = PayloadFilter.createSchemaFilter("network.example.user");

230

const limitFilter = PayloadFilter.createLimitFilter(3, "asc");

231

const complexFilter = PayloadFilter.createCompleteFilter(

232

["network.example.user", "network.example.product"],

233

2,

234

"desc"

235

);

236

237

const userPayloads = PayloadFilter.applyFilter(payloads, userFilter);

238

const limitedPayloads = PayloadFilter.applyFilter(payloads, limitFilter);

239

const complexResults = PayloadFilter.applyFilter(payloads, complexFilter);

240

241

console.log("Users:", userPayloads.length); // 3

242

console.log("Limited:", limitedPayloads.length); // 3

243

console.log("Complex:", complexResults.length); // 2

244

245

// Hash map utilities

246

class PayloadHashMapManager<T extends Payload> {

247

private hashMap: PayloadHashMap<T> = {

248

dataHash: {},

249

hash: {}

250

};

251

252

// Add payload to hash map

253

addPayload(payload: T, hash: string, dataHash?: string): void {

254

this.hashMap.hash[hash] = payload;

255

this.hashMap.dataHash[hash] = dataHash || hash;

256

}

257

258

// Get payload by hash

259

getPayload(hash: string): T | undefined {

260

return this.hashMap.hash[hash];

261

}

262

263

// Get all payloads with same data hash

264

getPayloadsByDataHash(dataHash: string): T[] {

265

const hashes = Object.entries(this.hashMap.dataHash)

266

.filter(([_, dHash]) => dHash === dataHash)

267

.map(([hash, _]) => hash);

268

269

return hashes

270

.map(hash => this.hashMap.hash[hash])

271

.filter((payload): payload is T => payload !== undefined);

272

}

273

274

// Get statistics

275

getStats(): {

276

totalPayloads: number;

277

uniqueDataHashes: number;

278

duplicateDataHashes: number;

279

} {

280

const totalPayloads = Object.keys(this.hashMap.hash).length;

281

const dataHashes = Object.values(this.hashMap.dataHash);

282

const uniqueDataHashes = new Set(dataHashes).size;

283

const duplicateDataHashes = dataHashes.length - uniqueDataHashes;

284

285

return {

286

totalPayloads,

287

uniqueDataHashes,

288

duplicateDataHashes

289

};

290

}

291

292

// Export hash map

293

export(): PayloadHashMap<T> {

294

return { ...this.hashMap };

295

}

296

}

297

298

// Use hash map manager

299

interface UserPayload extends Payload {

300

schema: "network.example.user";

301

name: string;

302

email: string;

303

}

304

305

const hashMapManager = new PayloadHashMapManager<UserPayload>();

306

307

const user1: UserPayload = {

308

schema: "network.example.user",

309

name: "Alice",

310

email: "alice@example.com"

311

};

312

313

const user2: UserPayload = {

314

schema: "network.example.user",

315

name: "Alice",

316

email: "alice@example.com"

317

}; // Same data, different hash

318

319

hashMapManager.addPayload(user1, "0x123...", "0xabc...");

320

hashMapManager.addPayload(user2, "0x456...", "0xabc..."); // Same data hash

321

322

const stats = hashMapManager.getStats();

323

console.log("Stats:", stats); // { totalPayloads: 2, uniqueDataHashes: 1, duplicateDataHashes: 1 }

324

325

const duplicates = hashMapManager.getPayloadsByDataHash("0xabc...");

326

console.log("Duplicates:", duplicates.length); // 2

327

```

328

329

### PayloadSet Types

330

331

Types and interfaces for working with payload sets and collections of schema requirements.

332

333

```typescript { .api }

334

/**

335

* PayloadSet schema constant

336

*/

337

const PayloadSetSchema = "network.xyo.payload.set" as const;

338

type PayloadSetSchema = typeof PayloadSetSchema;

339

340

/**

341

* Interface defining required and optional schema counts

342

*/

343

interface PayloadSet {

344

/** Optional schemas with their minimum required counts */

345

optional?: Record<string, number>;

346

/** Required schemas with their minimum required counts */

347

required?: Record<string, number>;

348

}

349

350

/**

351

* PayloadSet payload type combining schema requirements with payload structure

352

*/

353

type PayloadSetPayload = Payload<PayloadSet, PayloadSetSchema>;

354

```

355

356

**Usage Examples:**

357

358

```typescript

359

import {

360

PayloadSet,

361

PayloadSetPayload,

362

PayloadSetSchema,

363

Payload

364

} from "@xyo-network/payload-model";

365

366

// Define a payload set requirement

367

const apiPayloadSet: PayloadSetPayload = {

368

schema: PayloadSetSchema,

369

required: {

370

"network.example.user": 1, // At least 1 user payload required

371

"network.example.session": 1 // At least 1 session payload required

372

},

373

optional: {

374

"network.example.metadata": 2, // Up to 2 metadata payloads optional

375

"network.example.config": 1 // Up to 1 config payload optional

376

}

377

};

378

379

// Utility class for working with payload sets

380

class PayloadSetValidator {

381

// Validate if a collection of payloads meets the requirements

382

static validate(payloads: Payload[], requirements: PayloadSet): {

383

valid: boolean;

384

missing: string[];

385

satisfied: string[];

386

counts: Record<string, number>;

387

} {

388

const counts: Record<string, number> = {};

389

const missing: string[] = [];

390

const satisfied: string[] = [];

391

392

// Count payloads by schema

393

payloads.forEach(payload => {

394

counts[payload.schema] = (counts[payload.schema] || 0) + 1;

395

});

396

397

// Check required schemas

398

if (requirements.required) {

399

Object.entries(requirements.required).forEach(([schema, required]) => {

400

const actual = counts[schema] || 0;

401

if (actual >= required) {

402

satisfied.push(schema);

403

} else {

404

missing.push(`${schema} (need ${required}, have ${actual})`);

405

}

406

});

407

}

408

409

// Check optional schemas (they're always satisfied if present)

410

if (requirements.optional) {

411

Object.entries(requirements.optional).forEach(([schema, _]) => {

412

if (counts[schema] > 0) {

413

satisfied.push(schema);

414

}

415

});

416

}

417

418

return {

419

valid: missing.length === 0,

420

missing,

421

satisfied,

422

counts

423

};

424

}

425

426

// Generate a report for payload set validation

427

static generateReport(payloads: Payload[], requirements: PayloadSet): string {

428

const result = this.validate(payloads, requirements);

429

430

let report = `Payload Set Validation Report\n`;

431

report += `================================\n`;

432

report += `Status: ${result.valid ? 'VALID' : 'INVALID'}\n\n`;

433

434

report += `Schema Counts:\n`;

435

Object.entries(result.counts).forEach(([schema, count]) => {

436

report += ` ${schema}: ${count}\n`;

437

});

438

439

if (result.satisfied.length > 0) {

440

report += `\nSatisfied Requirements:\n`;

441

result.satisfied.forEach(schema => {

442

report += ` ✓ ${schema}\n`;

443

});

444

}

445

446

if (result.missing.length > 0) {

447

report += `\nMissing Requirements:\n`;

448

result.missing.forEach(requirement => {

449

report += ` ✗ ${requirement}\n`;

450

});

451

}

452

453

return report;

454

}

455

456

// Filter payloads that match the set requirements

457

static filterMatchingPayloads(

458

allPayloads: Payload[],

459

requirements: PayloadSet

460

): Payload[] {

461

const requiredSchemas = Object.keys(requirements.required || {});

462

const optionalSchemas = Object.keys(requirements.optional || {});

463

const allowedSchemas = new Set([...requiredSchemas, ...optionalSchemas]);

464

465

return allPayloads.filter(payload =>

466

allowedSchemas.has(payload.schema)

467

);

468

}

469

}

470

471

// Usage examples

472

const testPayloads: Payload[] = [

473

{ schema: "network.example.user", name: "Alice", email: "alice@example.com" },

474

{ schema: "network.example.user", name: "Bob", email: "bob@example.com" },

475

{ schema: "network.example.session", sessionId: "sess123", userId: "alice" },

476

{ schema: "network.example.metadata", type: "analytics", data: {} },

477

{ schema: "network.example.config", theme: "dark", lang: "en" },

478

{ schema: "network.example.unknown", data: "should be filtered" }

479

];

480

481

// Validate against payload set requirements

482

const validation = PayloadSetValidator.validate(testPayloads, apiPayloadSet);

483

console.log("Validation result:", validation.valid);

484

console.log("Missing:", validation.missing);

485

486

// Generate detailed report

487

const report = PayloadSetValidator.generateReport(testPayloads, apiPayloadSet);

488

console.log(report);

489

490

// Filter matching payloads

491

const matchingPayloads = PayloadSetValidator.filterMatchingPayloads(

492

testPayloads,

493

apiPayloadSet

494

);

495

console.log("Matching payloads:", matchingPayloads.length); // Excludes unknown schema

496

```

497

498

### Timestamp Utilities

499

500

Utility types and interfaces for working with temporal data in payloads.

501

502

```typescript { .api }

503

/**

504

* Interface for objects with timestamp information

505

*/

506

interface Timestamp {

507

timestamp: number;

508

}

509

510

/**

511

* Utility type to add timestamp field to any object

512

*/

513

type WithTimestamp<T extends EmptyObject = EmptyObject> = T & Timestamp;

514

```

515

516

**Usage Examples:**

517

518

```typescript

519

import {

520

Timestamp,

521

WithTimestamp,

522

Payload

523

} from "@xyo-network/payload-model";

524

525

// Add timestamps to payload types

526

interface TimestampedUserPayload extends WithTimestamp<Payload> {

527

schema: "network.example.user";

528

name: string;

529

email: string;

530

}

531

532

// Create timestamped payload

533

const timestampedUser: TimestampedUserPayload = {

534

schema: "network.example.user",

535

name: "Alice",

536

email: "alice@example.com",

537

timestamp: Date.now()

538

};

539

540

// Utility functions for timestamps

541

class TimestampUtils {

542

// Add timestamp to existing payload

543

static addTimestamp<T extends Payload>(payload: T): WithTimestamp<T> {

544

return {

545

...payload,

546

timestamp: Date.now()

547

};

548

}

549

550

// Check if payload has timestamp

551

static hasTimestamp(payload: unknown): payload is Timestamp {

552

return typeof payload === 'object' &&

553

payload !== null &&

554

'timestamp' in payload &&

555

typeof (payload as any).timestamp === 'number';

556

}

557

558

// Sort payloads by timestamp

559

static sortByTimestamp<T extends Timestamp>(

560

payloads: T[],

561

order: 'asc' | 'desc' = 'desc'

562

): T[] {

563

return [...payloads].sort((a, b) => {

564

return order === 'desc'

565

? b.timestamp - a.timestamp

566

: a.timestamp - b.timestamp;

567

});

568

}

569

570

// Filter payloads by time range

571

static filterByTimeRange<T extends Timestamp>(

572

payloads: T[],

573

startTime: number,

574

endTime: number

575

): T[] {

576

return payloads.filter(payload =>

577

payload.timestamp >= startTime && payload.timestamp <= endTime

578

);

579

}

580

581

// Group payloads by time period

582

static groupByTimePeriod<T extends Timestamp>(

583

payloads: T[],

584

periodMs: number

585

): Record<string, T[]> {

586

const groups: Record<string, T[]> = {};

587

588

payloads.forEach(payload => {

589

const periodStart = Math.floor(payload.timestamp / periodMs) * periodMs;

590

const key = periodStart.toString();

591

592

if (!groups[key]) {

593

groups[key] = [];

594

}

595

groups[key].push(payload);

596

});

597

598

return groups;

599

}

600

}

601

602

// Usage examples

603

const users: WithTimestamp<Payload>[] = [

604

{ schema: "network.example.user", name: "Alice", timestamp: Date.now() - 3600000 }, // 1 hour ago

605

{ schema: "network.example.user", name: "Bob", timestamp: Date.now() - 1800000 }, // 30 min ago

606

{ schema: "network.example.user", name: "Charlie", timestamp: Date.now() - 900000 } // 15 min ago

607

];

608

609

// Sort by timestamp

610

const sortedUsers = TimestampUtils.sortByTimestamp(users, 'asc');

611

console.log("Sorted users:", sortedUsers.map(u => u.name)); // ["Alice", "Bob", "Charlie"]

612

613

// Filter recent users (last 45 minutes)

614

const recentUsers = TimestampUtils.filterByTimeRange(

615

users,

616

Date.now() - 45 * 60 * 1000,

617

Date.now()

618

);

619

console.log("Recent users:", recentUsers.length); // 2

620

621

// Group by hour

622

const hourlyGroups = TimestampUtils.groupByTimePeriod(users, 60 * 60 * 1000);

623

console.log("Hourly groups:", Object.keys(hourlyGroups));

624

625

// Add timestamp to existing payload

626

const existingPayload: Payload = {

627

schema: "network.example.data",

628

data: "some data"

629

};

630

631

const timestampedPayload = TimestampUtils.addTimestamp(existingPayload);

632

console.log("Timestamped:", timestampedPayload.timestamp);

633

```

634

635

### Advanced Type Manipulation

636

637

Advanced utility types for complex payload type transformations and manipulations.

638

639

```typescript { .api }

640

/**

641

* Utility types for deep property manipulation (from @xylabs/object)

642

*/

643

type DeepOmitStartsWith<T, Prefix extends string> = T; // Simplified representation

644

type DeepPickStartsWith<T, Prefix extends string> = T; // Simplified representation

645

646

/**

647

* Empty object type for type safety

648

*/

649

type EmptyObject = Record<string, never>;

650

651

/**

652

* JSON value type for flexible data storage

653

*/

654

type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };

655

```

656

657

**Usage Examples:**

658

659

```typescript

660

import {

661

Payload,

662

WithoutMeta,

663

WithoutClientMeta,

664

WithoutStorageMeta,

665

JsonValue

666

} from "@xyo-network/payload-model";

667

668

// Complex payload with various metadata

669

interface ComplexPayload extends Payload {

670

schema: "network.example.complex";

671

data: JsonValue;

672

673

// Client metadata (starts with $)

674

$userId?: string;

675

$sessionId?: string;

676

$clientVersion?: string;

677

678

// Storage metadata (starts with _)

679

_hash?: string;

680

_dataHash?: string;

681

_sequence?: string;

682

683

// Private storage metadata (starts with __)

684

__internalId?: string;

685

__debugInfo?: JsonValue;

686

}

687

688

// Create utility class for payload transformation

689

class PayloadTransformer {

690

// Remove all metadata for external API

691

static toExternalFormat<T extends Payload>(payload: T): WithoutMeta<T> {

692

const cleaned = { ...payload };

693

694

// Remove client metadata

695

Object.keys(cleaned).forEach(key => {

696

if (key.startsWith('$') || key.startsWith('_')) {

697

delete (cleaned as any)[key];

698

}

699

});

700

701

return cleaned as WithoutMeta<T>;

702

}

703

704

// Remove only client metadata

705

static removeClientMeta<T extends Payload>(payload: T): WithoutClientMeta<T> {

706

const cleaned = { ...payload };

707

708

Object.keys(cleaned).forEach(key => {

709

if (key.startsWith('$')) {

710

delete (cleaned as any)[key];

711

}

712

});

713

714

return cleaned as WithoutClientMeta<T>;

715

}

716

717

// Extract only metadata

718

static extractMetadata<T extends Payload>(payload: T): {

719

client: Record<string, any>;

720

storage: Record<string, any>;

721

private: Record<string, any>;

722

} {

723

const client: Record<string, any> = {};

724

const storage: Record<string, any> = {};

725

const private: Record<string, any> = {};

726

727

Object.entries(payload).forEach(([key, value]) => {

728

if (key.startsWith('__')) {

729

private[key] = value;

730

} else if (key.startsWith('_')) {

731

storage[key] = value;

732

} else if (key.startsWith('$')) {

733

client[key] = value;

734

}

735

});

736

737

return { client, storage, private };

738

}

739

740

// Merge metadata back into payload

741

static mergeMetadata<T extends Payload>(

742

payload: T,

743

metadata: {

744

client?: Record<string, any>;

745

storage?: Record<string, any>;

746

private?: Record<string, any>;

747

}

748

): T {

749

return {

750

...payload,

751

...metadata.client,

752

...metadata.storage,

753

...metadata.private

754

};

755

}

756

}

757

758

// Usage examples

759

const complexPayload: ComplexPayload = {

760

schema: "network.example.complex",

761

data: { message: "Hello World", count: 42 },

762

$userId: "user123",

763

$sessionId: "session456",

764

$clientVersion: "1.2.3",

765

_hash: "0x123...",

766

_dataHash: "0x456...",

767

_sequence: "seq789",

768

__internalId: "internal123",

769

__debugInfo: { trace: true, level: "debug" }

770

};

771

772

// Transform for external API

773

const externalPayload = PayloadTransformer.toExternalFormat(complexPayload);

774

console.log("External payload keys:", Object.keys(externalPayload));

775

// Only: ["schema", "data"]

776

777

// Remove client metadata only

778

const withoutClient = PayloadTransformer.removeClientMeta(complexPayload);

779

console.log("Without client metadata:", Object.keys(withoutClient));

780

// Includes storage metadata but not client metadata

781

782

// Extract metadata

783

const metadata = PayloadTransformer.extractMetadata(complexPayload);

784

console.log("Client metadata:", metadata.client);

785

console.log("Storage metadata:", metadata.storage);

786

console.log("Private metadata:", metadata.private);

787

788

// Work with JsonValue data

789

function processJsonData(data: JsonValue): string {

790

if (typeof data === 'string') return data;

791

if (typeof data === 'number') return data.toString();

792

if (typeof data === 'boolean') return data ? 'true' : 'false';

793

if (data === null) return 'null';

794

if (Array.isArray(data)) return `[${data.length} items]`;

795

if (typeof data === 'object') return `{${Object.keys(data).length} props}`;

796

return 'unknown';

797

}

798

799

const processed = processJsonData(complexPayload.data);

800

console.log("Processed data:", processed); // "{2 props}"

801

```

802

803

## Advanced Usage Patterns

804

805

### Payload Processing Pipeline

806

807

```typescript

808

import {

809

Payload,

810

PayloadFindFilter,

811

PayloadValueExpression,

812

WithTimestamp

813

} from "@xyo-network/payload-model";

814

815

// Create a comprehensive payload processing pipeline

816

class PayloadPipeline<T extends Payload> {

817

private processors: Array<(payload: T) => T> = [];

818

private validators: Array<(payload: T) => boolean> = [];

819

private extractors: Map<string, PayloadValueExpression<T, any, any>> = new Map();

820

821

// Add processing step

822

addProcessor(processor: (payload: T) => T): this {

823

this.processors.push(processor);

824

return this;

825

}

826

827

// Add validation step

828

addValidator(validator: (payload: T) => boolean): this {

829

this.validators.push(validator);

830

return this;

831

}

832

833

// Add value extractor

834

addExtractor<K extends keyof T>(

835

name: string,

836

extractor: PayloadValueExpression<T, K, any>

837

): this {

838

this.extractors.set(name, extractor);

839

return this;

840

}

841

842

// Process single payload

843

process(payload: T): {

844

success: boolean;

845

payload?: T;

846

extracted: Record<string, any>;

847

errors: string[];

848

} {

849

const errors: string[] = [];

850

const extracted: Record<string, any> = {};

851

let currentPayload = payload;

852

853

// Apply processors

854

try {

855

for (const processor of this.processors) {

856

currentPayload = processor(currentPayload);

857

}

858

} catch (error) {

859

errors.push(`Processing error: ${error}`);

860

return { success: false, extracted, errors };

861

}

862

863

// Apply validators

864

for (const validator of this.validators) {

865

if (!validator(currentPayload)) {

866

errors.push("Validation failed");

867

}

868

}

869

870

// Extract values

871

for (const [name, extractor] of this.extractors) {

872

try {

873

extracted[name] = extractor(currentPayload);

874

} catch (error) {

875

errors.push(`Extraction error for ${name}: ${error}`);

876

}

877

}

878

879

return {

880

success: errors.length === 0,

881

payload: errors.length === 0 ? currentPayload : undefined,

882

extracted,

883

errors

884

};

885

}

886

887

// Process batch of payloads

888

processBatch(payloads: T[]): {

889

successful: Array<{ payload: T; extracted: Record<string, any> }>;

890

failed: Array<{ original: T; errors: string[] }>;

891

} {

892

const successful: Array<{ payload: T; extracted: Record<string, any> }> = [];

893

const failed: Array<{ original: T; errors: string[] }> = [];

894

895

for (const payload of payloads) {

896

const result = this.process(payload);

897

if (result.success && result.payload) {

898

successful.push({ payload: result.payload, extracted: result.extracted });

899

} else {

900

failed.push({ original: payload, errors: result.errors });

901

}

902

}

903

904

return { successful, failed };

905

}

906

}

907

908

// Usage example

909

interface UserPayload extends WithTimestamp<Payload> {

910

schema: "network.example.user";

911

name: string;

912

email: string;

913

age: number;

914

}

915

916

const userPipeline = new PayloadPipeline<UserPayload>()

917

.addProcessor((payload) => ({

918

...payload,

919

name: payload.name.trim(),

920

email: payload.email.toLowerCase()

921

}))

922

.addValidator((payload) => payload.age >= 18)

923

.addValidator((payload) => payload.email.includes('@'))

924

.addExtractor('displayName', (payload) => `${payload.name} (${payload.age})`)

925

.addExtractor('domain', (payload) => payload.email.split('@')[1]);

926

927

const testUsers: UserPayload[] = [

928

{

929

schema: "network.example.user",

930

name: " Alice ",

931

email: "ALICE@EXAMPLE.COM",

932

age: 25,

933

timestamp: Date.now()

934

},

935

{

936

schema: "network.example.user",

937

name: "Bob",

938

email: "bob@test",

939

age: 16,

940

timestamp: Date.now()

941

}

942

];

943

944

const results = userPipeline.processBatch(testUsers);

945

console.log("Successful:", results.successful.length);

946

console.log("Failed:", results.failed.length);

947

948

results.successful.forEach(({ payload, extracted }) => {

949

console.log(`Processed: ${extracted.displayName} from ${extracted.domain}`);

950

});

951

```

952

953

## Types Reference

954

955

### Value Expression Types

956

957

- **`PayloadProperty<T>`**: Type representing all property keys of a payload

958

- **`PayloadValue<T, Key>`**: Type representing the value of a specific payload property

959

- **`PayloadValueExpression<T, Key, TValue>`**: Function type for extracting property values

960

961

### Collection Types

962

963

- **`PayloadFindFilter`**: Interface for payload filtering operations

964

- **`PayloadHashMap<TPayload, TId>`**: Interface for efficient payload storage and lookup

965

966

### Timestamp Types

967

968

- **`Timestamp`**: Interface for objects with timestamp information

969

- **`WithTimestamp<T>`**: Utility type to add timestamp field to any object

970

971

### Utility Types

972

973

- **`EmptyObject`**: Empty object type for type safety

974

- **`JsonValue`**: JSON value type for flexible data storage

975

- **`DeepOmitStartsWith<T, Prefix>`**: Remove properties starting with prefix

976

- **`DeepPickStartsWith<T, Prefix>`**: Select properties starting with prefix

977

978

### Filter Options

979

980

- **`PayloadFindFilter.limit`**: Maximum number of results

981

- **`PayloadFindFilter.order`**: Sort order ('desc' | 'asc')

982

- **`PayloadFindFilter.schema`**: Schema filter (string or string array)

983

984

### Hash Map Structure

985

986

- **`PayloadHashMap.dataHash`**: Data hash mappings (Record<TId, TId>)

987

- **`PayloadHashMap.hash`**: Complete payload mappings (Record<TId, TPayload>)