or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

analysis-metrics.mdapi-definition-reduction.mdextensions-customization.mdindex.mdopenapi-definition-management.mdoperation-discovery-analysis.mdparameter-handling-json-schema.mdrequest-response-management.mdschema-dereferencing-references.mdsecurity-authentication.mdserver-url-management.mdutils.md

extensions-customization.mddocs/

0

# Extensions and Customization

1

2

ReadMe-specific OpenAPI extensions with validation and configuration management for enhanced API documentation.

3

4

## Capabilities

5

6

### Check Extension Existence

7

8

Determine if a custom specification extension exists within the API definition.

9

10

```typescript { .api }

11

/**

12

* Check if extension exists at API definition root level

13

* @param extension - Extension name to check for

14

* @returns True if extension exists in the API definition

15

*/

16

hasExtension(extension: string): boolean;

17

```

18

19

**Usage Examples:**

20

21

```typescript

22

const oas = new Oas(definition);

23

24

// Check for ReadMe extensions

25

if (oas.hasExtension('x-readme')) {

26

console.log("ReadMe extensions configured");

27

}

28

29

// Check for specific extensions

30

if (oas.hasExtension('x-readme.code-samples')) {

31

console.log("Custom code samples defined");

32

}

33

34

// Check for legacy extensions

35

if (oas.hasExtension('x-samples-languages')) {

36

console.log("Legacy samples language configuration found");

37

}

38

```

39

40

### Get Extension Values

41

42

Retrieve custom specification extension values with fallback to defaults.

43

44

```typescript { .api }

45

/**

46

* Get extension value from API definition or operation

47

* @param extension - Extension name or key

48

* @param operation - Optional operation to check for extension (takes precedence)

49

* @returns Extension value or default value

50

*/

51

getExtension(extension: string | keyof Extensions, operation?: Operation): any;

52

```

53

54

**Usage Examples:**

55

56

```typescript

57

// Get API-level extension

58

const codesamples = oas.getExtension('code-samples');

59

console.log("Default code samples:", codesamples);

60

61

// Get operation-level extension (takes precedence)

62

const operation = oas.operation('/users', 'post');

63

const operationSamples = oas.getExtension('code-samples', operation);

64

65

// Get with type safety

66

const languages = oas.getExtension('samples-languages') as string[];

67

const headers = oas.getExtension('headers') as Array<{key: string, value: string}>;

68

69

// Get nested ReadMe extensions

70

const proxyEnabled = oas.getExtension('proxy-enabled'); // From x-readme.proxy-enabled

71

const explorerEnabled = oas.getExtension('explorer-enabled'); // From x-readme.explorer-enabled

72

```

73

74

### Validate Extensions

75

76

Validate extension values against expected schemas and constraints.

77

78

```typescript { .api }

79

/**

80

* Validate a specific extension

81

* @param extension - Extension key to validate

82

* @throws TypeError if extension value is invalid

83

*/

84

validateExtension(extension: keyof Extensions): void;

85

86

/**

87

* Validate all known extensions

88

* @throws TypeError if any extension value is invalid

89

*/

90

validateExtensions(): void;

91

```

92

93

**Usage Examples:**

94

95

```typescript

96

try {

97

// Validate specific extension

98

oas.validateExtension('parameter-ordering');

99

console.log("Parameter ordering is valid");

100

101

// Validate all extensions

102

oas.validateExtensions();

103

console.log("All extensions are valid");

104

105

} catch (error) {

106

console.error("Extension validation failed:", error.message);

107

// Example: "x-readme.parameter-ordering" must contain 6 items comprised of: path, query, body, cookie, form, and header

108

}

109

```

110

111

## Extension Constants and Types

112

113

### Extension Key Constants

114

115

```typescript { .api }

116

/** Extension key constants for type safety */

117

const CODE_SAMPLES = 'code-samples';

118

const EXPLORER_ENABLED = 'explorer-enabled';

119

const HEADERS = 'headers';

120

const METRICS_ENABLED = 'metrics-enabled';

121

const OAUTH_OPTIONS = 'oauth-options';

122

const PARAMETER_ORDERING = 'parameter-ordering';

123

const PROXY_ENABLED = 'proxy-enabled';

124

const SAMPLES_LANGUAGES = 'samples-languages';

125

const SIMPLE_MODE = 'simple-mode';

126

const DISABLE_TAG_SORTING = 'disable-tag-sorting';

127

```

128

129

### Extensions Interface

130

131

```typescript { .api }

132

interface Extensions {

133

/** Custom code samples for operations */

134

'code-samples': Array<{

135

code: string;

136

correspondingExample?: string;

137

install?: string;

138

language: string;

139

name?: string;

140

}>;

141

142

/** Disable automatic tag sorting */

143

'disable-tag-sorting': boolean;

144

145

/** Enable/disable API Explorer */

146

'explorer-enabled': boolean;

147

148

/** Static headers to add to requests */

149

'headers': Array<Record<string, number | string>>;

150

151

/** Enable/disable API metrics collection */

152

'metrics-enabled': boolean;

153

154

/** OAuth2 flow configuration options */

155

'oauth-options': {

156

scopeSeparator?: string;

157

useInsecureClientAuthentication?: boolean;

158

};

159

160

/** Parameter display order in documentation */

161

'parameter-ordering': ('body' | 'cookie' | 'form' | 'header' | 'path' | 'query')[];

162

163

/** Enable/disable CORS proxy */

164

'proxy-enabled': boolean;

165

166

/** Default code sample languages */

167

'samples-languages': string[];

168

169

/** Enable/disable simple mode */

170

'simple-mode': boolean;

171

}

172

```

173

174

### Extension Defaults

175

176

```typescript { .api }

177

/** Default values for all extensions */

178

const extensionDefaults: Extensions = {

179

'code-samples': undefined,

180

'disable-tag-sorting': false,

181

'explorer-enabled': true,

182

'headers': undefined,

183

'metrics-enabled': true,

184

'oauth-options': {},

185

'parameter-ordering': ['path', 'query', 'body', 'cookie', 'form', 'header'],

186

'proxy-enabled': true,

187

'samples-languages': ['shell', 'node', 'ruby', 'php', 'python', 'java', 'csharp'],

188

'simple-mode': true

189

};

190

```

191

192

## Extension Usage Patterns

193

194

### Code Samples Configuration

195

196

```typescript

197

// Check and configure custom code samples

198

const operation = oas.operation('/users', 'post');

199

const customSamples = oas.getExtension('code-samples', operation);

200

201

if (customSamples && Array.isArray(customSamples)) {

202

customSamples.forEach(sample => {

203

console.log(`${sample.language}${sample.name ? ` (${sample.name})` : ''}:`);

204

console.log(sample.code);

205

206

if (sample.install) {

207

console.log(`Installation: ${sample.install}`);

208

}

209

210

if (sample.correspondingExample) {

211

console.log(`Matches example: ${sample.correspondingExample}`);

212

}

213

});

214

}

215

216

// Add custom code samples

217

const definition = oas.getDefinition();

218

if (!definition.paths['/users'].post['x-readme']) {

219

definition.paths['/users'].post['x-readme'] = {};

220

}

221

222

definition.paths['/users'].post['x-readme']['code-samples'] = [

223

{

224

language: 'curl',

225

name: 'Create User with cURL',

226

code: 'curl -X POST https://api.example.com/users -H "Content-Type: application/json" -d \'{"name":"John","email":"john@example.com"}\'',

227

install: 'curl is usually pre-installed'

228

},

229

{

230

language: 'javascript',

231

name: 'Create User with Fetch',

232

code: 'fetch("https://api.example.com/users", {\n method: "POST",\n headers: { "Content-Type": "application/json" },\n body: JSON.stringify({ name: "John", email: "john@example.com" })\n});'

233

}

234

];

235

```

236

237

### API Explorer Configuration

238

239

```typescript

240

// Configure API Explorer behavior

241

const explorerEnabled = oas.getExtension('explorer-enabled');

242

const proxyEnabled = oas.getExtension('proxy-enabled');

243

const metricsEnabled = oas.getExtension('metrics-enabled');

244

245

console.log(`API Explorer: ${explorerEnabled ? 'enabled' : 'disabled'}`);

246

console.log(`CORS Proxy: ${proxyEnabled ? 'enabled' : 'disabled'}`);

247

console.log(`Metrics Collection: ${metricsEnabled ? 'enabled' : 'disabled'}`);

248

249

// Disable API Explorer for sensitive operations

250

const adminOperation = oas.operation('/admin/users', 'delete');

251

const adminExplorerSetting = oas.getExtension('explorer-enabled', adminOperation);

252

253

if (adminExplorerSetting) {

254

console.log("Admin operations allow API Explorer usage");

255

}

256

```

257

258

### Static Headers Configuration

259

260

```typescript

261

// Configure static headers

262

const staticHeaders = oas.getExtension('headers');

263

264

if (staticHeaders && Array.isArray(staticHeaders)) {

265

console.log("Static headers configured:");

266

staticHeaders.forEach(header => {

267

console.log(` ${header.key}: ${header.value}`);

268

});

269

}

270

271

// Add static headers to definition

272

const definition = oas.getDefinition();

273

definition['x-readme'] = {

274

...definition['x-readme'],

275

headers: [

276

{ key: 'X-API-Version', value: '2024-01' },

277

{ key: 'X-Client-ID', value: 'docs-client' },

278

{ key: 'X-Custom-Header', value: 'documentation' }

279

]

280

};

281

```

282

283

### Parameter Ordering Configuration

284

285

```typescript

286

// Get and validate parameter ordering

287

const parameterOrder = oas.getExtension('parameter-ordering');

288

console.log("Parameter display order:", parameterOrder);

289

290

// Validate parameter ordering

291

try {

292

oas.validateExtension('parameter-ordering');

293

} catch (error) {

294

console.error("Invalid parameter ordering:", error.message);

295

}

296

297

// Custom parameter ordering

298

const definition = oas.getDefinition();

299

definition['x-readme'] = {

300

...definition['x-readme'],

301

'parameter-ordering': ['header', 'path', 'query', 'body', 'form', 'cookie']

302

};

303

304

// Re-validate after changes

305

oas.validateExtension('parameter-ordering');

306

```

307

308

### OAuth Configuration

309

310

```typescript

311

// Configure OAuth2 options

312

const oauthOptions = oas.getExtension('oauth-options');

313

314

console.log("OAuth configuration:");

315

console.log(` Scope separator: "${oauthOptions.scopeSeparator || ' '}"`);

316

console.log(` Insecure client auth: ${oauthOptions.useInsecureClientAuthentication || false}`);

317

318

// Custom OAuth configuration

319

const definition = oas.getDefinition();

320

definition['x-readme'] = {

321

...definition['x-readme'],

322

'oauth-options': {

323

scopeSeparator: ',',

324

useInsecureClientAuthentication: false

325

}

326

};

327

```

328

329

## Advanced Extension Patterns

330

331

### Extension Migration

332

333

```typescript

334

// Migrate from legacy extension format to new format

335

function migrateLegacyExtensions(definition: OASDocument): OASDocument {

336

const migrated = JSON.parse(JSON.stringify(definition));

337

338

// Migrate x-samples-languages to x-readme.samples-languages

339

if (migrated['x-samples-languages']) {

340

migrated['x-readme'] = migrated['x-readme'] || {};

341

migrated['x-readme']['samples-languages'] = migrated['x-samples-languages'];

342

delete migrated['x-samples-languages'];

343

}

344

345

// Migrate x-explorer-enabled to x-readme.explorer-enabled

346

if (migrated['x-explorer-enabled'] !== undefined) {

347

migrated['x-readme'] = migrated['x-readme'] || {};

348

migrated['x-readme']['explorer-enabled'] = migrated['x-explorer-enabled'];

349

delete migrated['x-explorer-enabled'];

350

}

351

352

return migrated;

353

}

354

355

const migratedDef = migrateLegacyExtensions(oas.getDefinition());

356

const migratedOas = new Oas(migratedDef);

357

```

358

359

### Extension Inheritance

360

361

```typescript

362

// Check extension inheritance from API to operation level

363

function getEffectiveExtension(oas: Oas, operation: Operation, extension: keyof Extensions): any {

364

// Operation-level takes precedence

365

const operationValue = oas.getExtension(extension, operation);

366

if (operationValue !== undefined) {

367

return operationValue;

368

}

369

370

// Fall back to API-level

371

return oas.getExtension(extension);

372

}

373

374

const operation = oas.operation('/users', 'post');

375

const effectiveLanguages = getEffectiveExtension(oas, operation, 'samples-languages');

376

console.log("Effective sample languages:", effectiveLanguages);

377

```

378

379

### Extension Validation Pipeline

380

381

```typescript

382

// Comprehensive extension validation

383

function validateAllExtensions(oas: Oas): { valid: boolean; errors: string[] } {

384

const errors: string[] = [];

385

386

try {

387

oas.validateExtensions();

388

return { valid: true, errors: [] };

389

} catch (error) {

390

errors.push(error.message);

391

}

392

393

// Additional custom validations

394

const samplesLanguages = oas.getExtension('samples-languages');

395

if (samplesLanguages && !Array.isArray(samplesLanguages)) {

396

errors.push('samples-languages must be an array');

397

}

398

399

const headers = oas.getExtension('headers');

400

if (headers && Array.isArray(headers)) {

401

headers.forEach((header, index) => {

402

if (!header.key || !header.value) {

403

errors.push(`Header at index ${index} missing key or value`);

404

}

405

});

406

}

407

408

return { valid: errors.length === 0, errors };

409

}

410

411

const validation = validateAllExtensions(oas);

412

if (!validation.valid) {

413

console.error("Extension validation failed:");

414

validation.errors.forEach(error => console.error(` ${error}`));

415

}

416

```

417

418

## Integration Examples

419

420

### Documentation Generation

421

422

```typescript

423

// Generate extension-aware documentation

424

function generateDocumentationConfig(oas: Oas) {

425

return {

426

explorerEnabled: oas.getExtension('explorer-enabled'),

427

proxyEnabled: oas.getExtension('proxy-enabled'),

428

metricsEnabled: oas.getExtension('metrics-enabled'),

429

defaultLanguages: oas.getExtension('samples-languages'),

430

parameterOrder: oas.getExtension('parameter-ordering'),

431

staticHeaders: oas.getExtension('headers'),

432

tagSortingDisabled: oas.getExtension('disable-tag-sorting'),

433

simpleMode: oas.getExtension('simple-mode')

434

};

435

}

436

437

const docConfig = generateDocumentationConfig(oas);

438

console.log("Documentation configuration:", docConfig);

439

```

440

441

### Code Generation Integration

442

443

```typescript

444

// Use extensions for code generation customization

445

function generateSDKConfig(oas: Oas) {

446

const languages = oas.getExtension('samples-languages');

447

const headers = oas.getExtension('headers');

448

449

return {

450

targetLanguages: languages,

451

defaultHeaders: headers?.reduce((acc, header) => {

452

acc[header.key] = header.value;

453

return acc;

454

}, {} as Record<string, any>),

455

includeMetrics: oas.getExtension('metrics-enabled'),

456

proxyMode: oas.getExtension('proxy-enabled')

457

};

458

}

459

```