soaring-symbols 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/README.md +6 -5
  2. package/dist/airlines.json +1 -1
  3. package/dist/assets/air-arabia/icon-mono.svg +1 -1
  4. package/dist/assets/air-arabia/icon.svg +1 -1
  5. package/dist/assets/air-berlin/icon-mono.svg +1 -1
  6. package/dist/assets/air-berlin/icon.svg +1 -1
  7. package/dist/assets/air-berlin/logo-mono.svg +1 -1
  8. package/dist/assets/air-berlin/logo.svg +1 -1
  9. package/dist/assets/american-airlines/icon.svg +1 -1
  10. package/dist/assets/american-airlines/logo.svg +1 -1
  11. package/dist/assets/austrian-airlines/logo.svg +1 -1
  12. package/dist/assets/azul/icon.svg +1 -1
  13. package/dist/assets/binter/logo-mono.svg +1 -0
  14. package/dist/assets/binter/logo.svg +1 -0
  15. package/dist/assets/cebu-pacific/icon.svg +1 -1
  16. package/dist/assets/cebu-pacific/logo.svg +1 -1
  17. package/dist/assets/china-airlines/logo.svg +1 -1
  18. package/dist/assets/china-eastern-airlines/icon.svg +1 -1
  19. package/dist/assets/china-southern-airlines/tail.svg +1 -1
  20. package/dist/assets/condor/icon-mono.svg +1 -1
  21. package/dist/assets/condor/icon.svg +1 -1
  22. package/dist/assets/condor/logo-mono.svg +1 -1
  23. package/dist/assets/condor/logo.svg +1 -1
  24. package/dist/assets/delta-air-lines/icon-mono.svg +1 -1
  25. package/dist/assets/easyjet/logo-mono.svg +1 -1
  26. package/dist/assets/easyjet/logo.svg +1 -1
  27. package/dist/assets/egyptair/icon-mono.svg +1 -1
  28. package/dist/assets/egyptair/icon.svg +1 -1
  29. package/dist/assets/flybe/logo-mono.svg +1 -1
  30. package/dist/assets/flybe/logo.svg +1 -1
  31. package/dist/assets/frontier-airlines/icon-mono.svg +1 -1
  32. package/dist/assets/frontier-airlines/icon.svg +1 -1
  33. package/dist/assets/gol/logo.svg +1 -1
  34. package/dist/assets/hawaiian-airlines/icon.svg +1 -1
  35. package/dist/assets/hawaiian-airlines/logo.svg +1 -1
  36. package/dist/assets/ita-airways/logo.svg +1 -1
  37. package/dist/assets/jet2com/icon-mono.svg +1 -1
  38. package/dist/assets/jet2com/icon.svg +1 -1
  39. package/dist/assets/juneyao-air/icon-mono.svg +1 -0
  40. package/dist/assets/juneyao-air/icon.svg +1 -0
  41. package/dist/assets/juneyao-air/logo-mono.svg +1 -0
  42. package/dist/assets/juneyao-air/logo.svg +1 -0
  43. package/dist/assets/level/icon.svg +1 -0
  44. package/dist/assets/level/logo.svg +1 -0
  45. package/dist/assets/lion-air/icon-mono.svg +1 -1
  46. package/dist/assets/lion-air/icon.svg +1 -1
  47. package/dist/assets/norwegian-air-shuttle/icon.svg +1 -1
  48. package/dist/assets/norwegian-air-shuttle/logo-mono.svg +1 -1
  49. package/dist/assets/norwegian-air-shuttle/logo.svg +1 -1
  50. package/dist/assets/pegasus-airlines/icon-mono.svg +1 -1
  51. package/dist/assets/pegasus-airlines/icon.svg +1 -1
  52. package/dist/assets/spirit-airlines/logo-mono.svg +1 -1
  53. package/dist/assets/spirit-airlines/logo.svg +1 -1
  54. package/dist/assets/viva/icon-mono.svg +1 -1
  55. package/dist/assets/viva/icon.svg +1 -1
  56. package/dist/assets/volaris/icon.svg +1 -1
  57. package/dist/assets/vueling/logo.svg +1 -1
  58. package/dist/assets/winair/logo-mono.svg +1 -0
  59. package/dist/assets/winair/logo.svg +1 -0
  60. package/dist/svg-validator.cjs +382 -5
  61. package/dist/svg-validator.d.cts +12 -0
  62. package/dist/svg-validator.d.ts +12 -0
  63. package/dist/svg-validator.js +382 -5
  64. package/package.json +4 -3
  65. package/AIRLINES.md +0 -144
@@ -4,6 +4,17 @@
4
4
 
5
5
  const r = (n) => Math.round(n * 1000) / 1000
6
6
 
7
+ // Top-level constants are inlined by the build like the functions; only
8
+ // functions may be exported (see scripts/build.js)
9
+ const HEX = /^#(?:[0-9A-F]{3}){1,2}$/i
10
+ // Paint keywords that are not colors to check or move
11
+ const PAINT_KEYWORDS = /^(none|currentColor|transparent|inherit)$/i
12
+
13
+ // Whether a file is a monochrome variant, drawn in currentColor
14
+ function isMonoFile(name) {
15
+ return name.toLowerCase().endsWith('-mono.svg')
16
+ }
17
+
7
18
  // Check: <title> exists and has non-empty text content
8
19
  function validateTitle(svg) {
9
20
  const title = svg.querySelector('title')
@@ -83,6 +94,17 @@ function validateNoTransforms(svg) {
83
94
  return { pass, detail }
84
95
  }
85
96
 
97
+ // Check: no <g> inside another <g> (a single level is fine for shared attributes)
98
+ function validateNoNestedGroups(svg) {
99
+ const nested = svg.querySelectorAll('g g').length
100
+ const pass = nested === 0
101
+
102
+ return {
103
+ pass,
104
+ detail: pass ? 'clean' : `${nested} nested <g> found`,
105
+ }
106
+ }
107
+
86
108
  // Check: geometry — fills viewBox, fits within it, and is centered.
87
109
  // Requires a pre-computed bbox (e.g. from getBBox() in the browser).
88
110
  // Returns an array of SvgValidation-shaped objects (may be 1–3 items).
@@ -151,12 +173,367 @@ function validateGeometry(type, bbox) {
151
173
  return results
152
174
  }
153
175
 
176
+ // Check: <title> is the first element inside <svg>, so screen readers and
177
+ // the catalog read it before anything else
178
+ function validateTitleFirst(svg) {
179
+ const title = svg.querySelector('title')
180
+ const first = svg.firstElementChild
181
+ const pass = !!title && first === title
182
+
183
+ return {
184
+ pass,
185
+ detail: pass
186
+ ? 'first child'
187
+ : title
188
+ ? `<${first.tagName.toLowerCase()}> comes before <title>`
189
+ : 'missing',
190
+ }
191
+ }
192
+
193
+ // Check: no width or height on the root <svg>; the viewBox sets the size, so
194
+ // the marks scale to whatever box they are placed in
195
+ function validateNoDimensions(svg) {
196
+ const attrs = ['width', 'height'].filter((a) => svg.hasAttribute(a))
197
+
198
+ return {
199
+ pass: attrs.length === 0,
200
+ detail: attrs.length
201
+ ? attrs.map((a) => `${a}="${svg.getAttribute(a)}"`).join(' ')
202
+ : 'clean',
203
+ attrs,
204
+ }
205
+ }
206
+
207
+ // Check: no comments, <metadata> or design-tool namespaces and attributes
208
+ // (Inkscape, Sketch, Illustrator…) inside <svg>
209
+ function validateNoEditorData(svg) {
210
+ const editorPrefix = /^(inkscape|sodipodi|sketch|serif|i|x|graph|a):/
211
+ const editorNs = /^xmlns:(?!xlink$)/
212
+ const walker = svg.ownerDocument.createTreeWalker(svg, 128) // comments
213
+ const comments = []
214
+ while (walker.nextNode()) comments.push(walker.currentNode)
215
+
216
+ const leftovers = new Set()
217
+ for (const el of [svg, ...svg.querySelectorAll('*')]) {
218
+ if (
219
+ el.tagName.toLowerCase() === 'metadata' ||
220
+ editorPrefix.test(el.tagName)
221
+ ) {
222
+ leftovers.add(`<${el.tagName}>`)
223
+ }
224
+ for (const { name } of el.attributes) {
225
+ if (editorPrefix.test(name) || editorNs.test(name)) {
226
+ leftovers.add(name)
227
+ }
228
+ }
229
+ }
230
+ const list = [...leftovers]
231
+ const found = comments.length ? ['comments', ...list] : list
232
+
233
+ return {
234
+ pass: found.length === 0,
235
+ detail: found.length ? found.join(', ') : 'clean',
236
+ comments,
237
+ leftovers: list,
238
+ }
239
+ }
240
+
241
+ // Check: path data is written the way SVGO writes it: at most five decimals,
242
+ // no negative zero and no leading zero (".5", not "0.5"). `paths` lists what
243
+ // each <path> breaks, with `trimmed` the d without leading zeros.
244
+ function validateNumberFormat(svg) {
245
+ const maxDecimals = 5
246
+ const paths = []
247
+ for (const el of svg.querySelectorAll('path[d]')) {
248
+ const d = el.getAttribute('d')
249
+ const long = d.match(new RegExp(`\\.\\d{${maxDecimals + 1},}`))
250
+ const negativeZero = /-0(?![\d.])/.test(d)
251
+ // "0.5" -> ".5" and "-0.5" -> "-.5": the same numbers, written shorter
252
+ const trimmed = d.replace(/(?<![\d.])0\.(?=\d)/g, '.')
253
+ if (long || negativeZero || trimmed !== d) {
254
+ paths.push({
255
+ el,
256
+ long: long?.[0] ?? null,
257
+ negativeZero,
258
+ trimmed: trimmed !== d ? trimmed : null,
259
+ })
260
+ }
261
+ }
262
+ const problems = [
263
+ paths.some((p) => p.long) &&
264
+ `more than ${maxDecimals} decimals ("${paths.find((p) => p.long).long}")`,
265
+ paths.some((p) => p.negativeZero) && 'negative zero',
266
+ paths.some((p) => p.trimmed) && 'leading zeros ("0.5" → ".5")',
267
+ ].filter(Boolean)
268
+
269
+ return {
270
+ pass: paths.length === 0,
271
+ detail: problems.length ? problems.join(' · ') : 'clean',
272
+ maxDecimals,
273
+ paths,
274
+ }
275
+ }
276
+
277
+ // The six-digit lowercase form of a hex color: #F60 -> #ff6600
278
+ function toFullHex(hex) {
279
+ return (
280
+ hex.length === 4
281
+ ? '#' + [...hex.slice(1)].map((c) => c + c).join('')
282
+ : hex
283
+ ).toLowerCase()
284
+ }
285
+
286
+ // Check: colors in fill, stroke and stop-color are six-digit lowercase hex.
287
+ // `invalid` holds values that aren't hex at all (color names, rgb()), `short`
288
+ // hex that only needs rewriting.
289
+ function validateHexColors(svg) {
290
+ const invalid = []
291
+ const short = []
292
+ for (const attr of ['fill', 'stroke', 'stop-color']) {
293
+ const els = [svg, ...svg.querySelectorAll(`[${attr}]`)]
294
+ for (const el of els.filter((e) => e.hasAttribute(attr))) {
295
+ const value = el.getAttribute(attr).trim()
296
+ if (PAINT_KEYWORDS.test(value) || /^url\(.*\)$/.test(value))
297
+ continue
298
+ if (!HEX.test(value)) {
299
+ invalid.push({ el, attr, value })
300
+ continue
301
+ }
302
+ const full = toFullHex(value)
303
+ if (value !== full) short.push({ el, attr, value, full })
304
+ }
305
+ }
306
+ const problems = [
307
+ ...invalid.map((c) => `${c.attr}="${c.value}" is not hex`),
308
+ ...short.map((c) => `"${c.value}" should be "${c.full}"`),
309
+ ]
310
+
311
+ return {
312
+ pass: problems.length === 0,
313
+ detail: problems.length ? problems.join(' · ') : 'clean',
314
+ invalid,
315
+ short,
316
+ }
317
+ }
318
+
319
+ // Check: a single-color SVG sets its fill once, on the root <svg>
320
+ // (currentColor for a -mono file), and none of its children has one; a
321
+ // multi-color SVG has no fill on the root. Gradients and non-hex fills are
322
+ // left alone (`skipped`): validateHexColors reports the latter.
323
+ // `rootFill` is the fill the root should have (null for none), `rootOk`
324
+ // whether it has it, and `redundant` the children whose fill should go.
325
+ function validateFillPlacement(svg, isMono = false) {
326
+ // The root counts: a single-color SVG keeps its fill there
327
+ const withFill = [svg, ...svg.querySelectorAll('[fill]')].filter((el) =>
328
+ el.hasAttribute('fill'),
329
+ )
330
+ const fills = withFill.map((el) => el.getAttribute('fill').trim())
331
+ const result = (pass, detail, rest) => ({
332
+ pass,
333
+ detail,
334
+ skipped: false,
335
+ rootFill: null,
336
+ rootOk: true,
337
+ redundant: [],
338
+ ...rest,
339
+ })
340
+
341
+ if (fills.some((f) => !HEX.test(f) && !PAINT_KEYWORDS.test(f))) {
342
+ return result(true, 'skipped: gradient or non-hex fill', {
343
+ skipped: true,
344
+ })
345
+ }
346
+
347
+ const colors = new Set(
348
+ fills.filter((f) => HEX.test(f)).map((f) => f.toLowerCase()),
349
+ )
350
+ const existing = svg.getAttribute('fill')
351
+
352
+ if (colors.size > 1) {
353
+ const rootOk = existing === null
354
+
355
+ return result(
356
+ rootOk,
357
+ rootOk
358
+ ? `${colors.size} colors, no root fill`
359
+ : 'root <svg> should have no fill in a multi-color SVG',
360
+ { rootOk },
361
+ )
362
+ }
363
+
364
+ const rootFill = isMono ? 'currentColor' : [...colors][0] || 'currentColor'
365
+ const rootOk = isMono
366
+ ? existing === rootFill
367
+ : existing?.toLowerCase() === rootFill
368
+ const redundant = withFill.filter((el) => el !== svg)
369
+ const problems = [
370
+ !rootOk && `root <svg> should have fill="${rootFill}"`,
371
+ redundant.length &&
372
+ `remove fill from ${redundant.length} child element${redundant.length > 1 ? 's' : ''}`,
373
+ ].filter(Boolean)
374
+
375
+ return result(
376
+ problems.length === 0,
377
+ problems.length ? problems.join(' · ') : `fill="${rootFill}"`,
378
+ { rootFill, rootOk, redundant },
379
+ )
380
+ }
381
+
382
+ // Check (warning): a single-color SVG draws everything in one <path>
383
+ function validateSinglePath(svg, isMono = false) {
384
+ const hexFill = /#[0-9a-f]{3}(?:[0-9a-f]{3})?\b/gi
385
+ const markup = svg.outerHTML
386
+ const usesPaint = /url\(#|stroke=|stroke:|<(clipPath|mask)\b/i.test(markup)
387
+ const fills = new Set(
388
+ [...svg.querySelectorAll('[fill], [style]')].flatMap((el) =>
389
+ (
390
+ `${el.getAttribute('fill') || ''} ${el.getAttribute('style') || ''}`.match(
391
+ hexFill,
392
+ ) || []
393
+ ).map((c) => c.toLowerCase()),
394
+ ),
395
+ )
396
+ if (!isMono && (usesPaint || fills.size > 1)) {
397
+ return { pass: true, detail: 'multi-color', count: 0 }
398
+ }
399
+
400
+ const count = [
401
+ ...svg.querySelectorAll(
402
+ 'path, circle, rect, ellipse, polygon, polyline, line',
403
+ ),
404
+ ].filter((el) => !el.closest('defs, clipPath, mask')).length
405
+
406
+ return {
407
+ pass: count <= 1,
408
+ detail: count <= 1 ? 'one shape' : `${count} shapes`,
409
+ count,
410
+ }
411
+ }
412
+
413
+ // The file with anything before <svg> (XML declaration, DOCTYPE, comment)
414
+ // removed
415
+ function stripProlog(source) {
416
+ const start = source.indexOf('<svg')
417
+
418
+ return start > 0 ? source.slice(start) : source
419
+ }
420
+
421
+ // The file with fill before d on every <path>
422
+ function orderPathAttrs(source) {
423
+ return source.replace(
424
+ /<path([^>]*?) (d="[^"]*?")([^>]*?) (fill="[^"]*?")/g,
425
+ (match, before, dAttr, after, fillAttr) =>
426
+ `<path${before} ${fillAttr} ${dAttr}${after}`,
427
+ )
428
+ }
429
+
430
+ // The file with the root <svg> attributes ordered role, viewBox, xmlns, fill,
431
+ // then the rest
432
+ function orderSvgAttrs(source) {
433
+ const first = ['role', 'viewBox', 'xmlns', 'fill']
434
+ const svgTag = source.match(/<svg([^>]*)>/)
435
+ if (!svgTag) return source
436
+
437
+ const attributes = {}
438
+ const attrRegex = /([a-zA-Z0-9:-]+)="([^"]*)"/g
439
+ let match
440
+ while ((match = attrRegex.exec(svgTag[1])) !== null) {
441
+ attributes[match[1]] = match[2]
442
+ }
443
+ const ordered = [
444
+ ...first.filter((k) => k in attributes),
445
+ ...Object.keys(attributes).filter((k) => !first.includes(k)),
446
+ ]
447
+ .map((k) => `${k}="${attributes[k]}"`)
448
+ .join(' ')
449
+ if (ordered.replace(/\s+/g, '') === svgTag[1].trim().replace(/\s+/g, '')) {
450
+ return source
451
+ }
452
+
453
+ return source.replace(svgTag[0], `<svg ${ordered}>`)
454
+ }
455
+
456
+ // Check: the file starts with <svg>, with nothing before it
457
+ function validateNoProlog(source) {
458
+ const pass = stripProlog(source) === source
459
+
460
+ return {
461
+ pass,
462
+ detail: pass ? 'clean' : 'content before <svg>',
463
+ }
464
+ }
465
+
466
+ // Check: root <svg> attributes come as role, viewBox, xmlns, fill, then the
467
+ // rest, and fill comes before d on <path>
468
+ function validateAttrOrder(source) {
469
+ const svgOk = orderSvgAttrs(source) === source
470
+ const pathOk = orderPathAttrs(source) === source
471
+ const problems = [
472
+ !svgOk && 'root <svg>: role, viewBox, xmlns, fill first',
473
+ !pathOk && '<path>: fill before d',
474
+ ].filter(Boolean)
475
+
476
+ return {
477
+ pass: problems.length === 0,
478
+ detail: problems.length ? problems.join(' · ') : 'clean',
479
+ }
480
+ }
481
+
482
+
483
+ // The same checks as `scripts/lint-svg.js`, in the browser. `fileName` tells
484
+ // a -mono.svg apart (its fill is currentColor); `source`, the file's text,
485
+ // adds the check that needs it (nothing before <svg>) and is what the
486
+ // attribute order is read from, falling back to the parsed element as the
487
+ // CLI does.
488
+ export function runSvgValidations(svg, type, bbox = null, options = {}) {
489
+ const { fileName = '', source = null } = options
490
+ const isMono = isMonoFile(fileName)
491
+ const row = (group, label, result, level = 'error') => ({
492
+ group,
493
+ label,
494
+ level,
495
+ pass: result.pass,
496
+ detail: result.detail,
497
+ })
154
498
 
155
- export function runSvgValidations(svg, type, bbox = null) {
156
499
  return [
157
- { label: 'Required attributes', ...validateRequiredAttrs(svg, type) },
158
- { label: 'No embedded scripts', ...validateNoScripts(svg) },
159
- { label: 'No transforms', ...validateNoTransforms(svg) },
160
- ...validateGeometry(type, bbox),
500
+ row(
501
+ 'Structure',
502
+ 'Required attributes',
503
+ validateRequiredAttrs(svg, type),
504
+ ),
505
+ row('Structure', 'Title first', validateTitleFirst(svg)),
506
+ row('Structure', 'No embedded scripts', validateNoScripts(svg)),
507
+ row('Structure', 'No transforms', validateNoTransforms(svg)),
508
+ row(
509
+ 'Structure',
510
+ 'No nested groups',
511
+ validateNoNestedGroups(svg),
512
+ 'warning',
513
+ ),
514
+ row(
515
+ 'Structure',
516
+ 'Single path',
517
+ validateSinglePath(svg, isMono),
518
+ 'warning',
519
+ ),
520
+ row('Format', 'No width or height', validateNoDimensions(svg)),
521
+ row('Format', 'No editor data', validateNoEditorData(svg)),
522
+ row('Format', 'Number format', validateNumberFormat(svg)),
523
+ ...(source === null
524
+ ? []
525
+ : [row('Format', 'Starts with <svg>', validateNoProlog(source))]),
526
+ row(
527
+ 'Format',
528
+ 'Attribute order',
529
+ validateAttrOrder(source ?? svg.outerHTML),
530
+ ),
531
+ row('Style', 'Hex colors', validateHexColors(svg)),
532
+ row('Style', 'Fill placement', validateFillPlacement(svg, isMono)),
533
+ ...validateGeometry(type, bbox).map((check) => ({
534
+ group: 'Geometry',
535
+ level: 'error',
536
+ ...check,
537
+ })),
161
538
  ]
162
539
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "soaring-symbols",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "A curated collection of airline branding assets — logos, icons, and tail designs in SVG format.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -59,7 +59,6 @@
59
59
  ],
60
60
  "files": [
61
61
  "dist/",
62
- "AIRLINES.md",
63
62
  "API.md",
64
63
  "LICENSE",
65
64
  "README.md"
@@ -76,9 +75,11 @@
76
75
  "lint": "eslint .",
77
76
  "lint:fix": "eslint . --fix",
78
77
  "format": "prettier --write \"**/*.{js,ts}\"",
78
+ "format:check": "prettier --check \"**/*.{js,ts}\"",
79
79
  "release": "node scripts/release.js",
80
80
  "sync:data": "node scripts/sync-data.js",
81
- "optimize": "svgo --config svgo.config.mjs"
81
+ "optimize": "svgo --config svgo.config.mjs",
82
+ "optimize:check": "node scripts/check-optimized.js"
82
83
  },
83
84
  "lint-staged": {
84
85
  "*.js": [
package/AIRLINES.md DELETED
@@ -1,144 +0,0 @@
1
- # Soaring Symbols: Airlines
2
-
3
- This file provides an overview of the airlines included in the Soaring Symbols project.
4
-
5
- | Airline | Country | IATA | ICAO | Alliance | Primary Color | Icon | Logo | Tail |
6
- |:---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
7
- | [Aegean Airlines](https://en.aegeanair.com) 🇬🇷 | GR | A3 | AEE | Star Alliance | ![#002677](https://place-hold.it/10x10/002677/002677.png) | ✅ | ✅ | |
8
- | [Aer Lingus](https://www.aerlingus.com) 🇮🇪 | IE | EI | EIN | | ![#006472](https://place-hold.it/10x10/006472/006472.png) | ☑️ | ☑️ | |
9
- | [Aerolíneas Argentinas](https://www.aerolineas.com.ar) 🇦🇷 | AR | AR | ARG | SkyTeam | ![#0080be](https://place-hold.it/10x10/0080be/0080be.png) | ✅ | ✅ | |
10
- | [Aeroméxico](https://www.aeromexico.com) 🇲🇽 | MX | AM | AMX | SkyTeam | ![#020c41](https://place-hold.it/10x10/020c41/020c41.png) | ✅ | ✅ | |
11
- | [Air Albania](https://www.airalbania.com.al) 🇦🇱 | AL | ZB | ABN | | ![#ce1f2e](https://place-hold.it/10x10/ce1f2e/ce1f2e.png) | ✅ | ✅ | |
12
- | [Air Algérie](https://airalgerie.dz) 🇩🇿 | DZ | AH | DAH | | ![#e2231a](https://place-hold.it/10x10/e2231a/e2231a.png) | ✅ | ✅ | |
13
- | [Air Arabia](https://www.airarabia.com) | AE | G9 | ABY | | ![#ed1b48](https://place-hold.it/10x10/ed1b48/ed1b48.png) | ✅ | ✅ | |
14
- | [Air Astana](https://airastana.com) 🇰🇿 | KZ | KC | KZR | | ![#a48c5d](https://place-hold.it/10x10/a48c5d/a48c5d.png) | ✅ | ✅ | |
15
- | [Air Berlin](https://www.airberlin.com) | DE | AB | BER | oneworld | ![#cc2234](https://place-hold.it/10x10/cc2234/cc2234.png) | ✅ | ✅ | |
16
- | [Air Canada](https://www.aircanada.com) 🇨🇦 | CA | AC | ACA | Star Alliance | ![#f01428](https://place-hold.it/10x10/f01428/f01428.png) | ✅ | ✅ | |
17
- | [Air China](https://www.airchina.com) 🇨🇳 | CN | CA | CCA | Star Alliance | ![#ed1c24](https://place-hold.it/10x10/ed1c24/ed1c24.png) | ✅ | ✅ | |
18
- | [Air Dolomiti](https://www.airdolomiti.eu) | IT | EN | DLA | | ![#009eb3](https://place-hold.it/10x10/009eb3/009eb3.png) | ✅ | ✅ | |
19
- | [Air Europa](https://www.aireuropa.com) | ES | UX | AEA | SkyTeam | ![#006fde](https://place-hold.it/10x10/006fde/006fde.png) | ✅ | ✅ | |
20
- | [Air France](https://airfrance.com) 🇫🇷 | FR | AF | AFR | SkyTeam | ![#051039](https://place-hold.it/10x10/051039/051039.png) | ✅ | ✅ | |
21
- | [Air India](https://www.airindia.com) 🇮🇳 | IN | AI | AIC | Star Alliance | ![#da0e29](https://place-hold.it/10x10/da0e29/da0e29.png) | ✅ | ✅ | |
22
- | [Air Mauritius](https://www.airmauritius.com) 🇲🇺 | MU | MK | MAU | Vanilla Alliance | ![#be2239](https://place-hold.it/10x10/be2239/be2239.png) | ✅ | ✅ | |
23
- | [Air New Zealand](https://www.airnewzealand.co.nz) 🇳🇿 | NZ | NZ | ANZ | Star Alliance | ![#000000](https://place-hold.it/10x10/000000/000000.png) | ✅ | ✅ | |
24
- | [Air Serbia](https://www.airserbia.com) 🇷🇸 | RS | JU | ASL | | ![#0e203f](https://place-hold.it/10x10/0e203f/0e203f.png) | ✅ | ✅ | |
25
- | [Air Transat](https://airtransat.com) | CA | TS | TSC | | ![#172b54](https://place-hold.it/10x10/172b54/172b54.png) | ✅ | ✅ | |
26
- | [AirAsia](https://www.airasia.com) | MY | AK | AXM | | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | | ✅ | |
27
- | [AirAsia Cambodia](https://www.airasia.com) | KH | KT | KTC | | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | | ✅ | |
28
- | [airBaltic](https://www.airbaltic.com) 🇱🇻 | LV | BT | BTI | | ![#152649](https://place-hold.it/10x10/152649/152649.png) | | ✅ | |
29
- | [Akasa Air](https://www.akasaair.com) | IN | QP | AKJ | | ![#ff6300](https://place-hold.it/10x10/ff6300/ff6300.png) | ✅ | ✅ | |
30
- | [Alaska Airlines](https://www.alaskaair.com) | US | AS | ASA | oneworld | ![#01426a](https://place-hold.it/10x10/01426a/01426a.png) | | ✅ | |
31
- | [All Nippon Airways](https://www.ana.co.jp) | JP | NH | ANA | Star Alliance | ![#00146e](https://place-hold.it/10x10/00146e/00146e.png) | | ☑️ | |
32
- | [American Airlines](https://www.aa.com) | US | AA | AAL | oneworld | ![#0078d2](https://place-hold.it/10x10/0078d2/0078d2.png) | ✅ | ✅ | |
33
- | [Asiana Airlines](https://flyasiana.com) | KR | OZ | AAR | Star Alliance | ![#4a4f54](https://place-hold.it/10x10/4a4f54/4a4f54.png) | ✅ | ✅ | |
34
- | [Atlantic Airways](https://www.atlanticairways.com) 🇫🇴 | FO | RC | FLI | | ![#004c93](https://place-hold.it/10x10/004c93/004c93.png) | ✅ | ✅ | |
35
- | [Austrian Airlines](https://www.austrian.com) 🇦🇹 | AT | OS | AUA | Star Alliance | ![#d81824](https://place-hold.it/10x10/d81824/d81824.png) | ✅ | ☑️ | |
36
- | [avianca](https://www.avianca.com) 🇨🇴 | CO | AV | AVA | Star Alliance | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | ✅ | ✅ | |
37
- | [Avianca Costa Rica](https://www.avianca.com) 🇨🇷 | CR | LR | LRC | | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | ✅ | ✅ | |
38
- | [Avianca Ecuador](https://www.avianca.com) | EC | 2K | GLG | | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | ✅ | ✅ | |
39
- | [Avianca El Salvador](https://www.avianca.com) 🇸🇻 | SV | TA | TAI | | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | ✅ | ✅ | |
40
- | [Azerbaijan Airlines](https://azal.az) 🇦🇿 | AZ | J2 | AHY | | ![#082e6a](https://place-hold.it/10x10/082e6a/082e6a.png) | ✅ | ✅ | |
41
- | [Azul](https://www.voeazul.com.br) | BR | AD | AZU | | ![#005baa](https://place-hold.it/10x10/005baa/005baa.png) | ☑️ | ☑️ | |
42
- | [Bamboo Airways](https://www.bambooairways.com) | VN | QH | BAV | | ![#64ae53](https://place-hold.it/10x10/64ae53/64ae53.png) | ☑️ | ☑️ | |
43
- | [Bangkok Airways](https://www.bangkokair.com) | TH | PG | BKP | | ![#000f8f](https://place-hold.it/10x10/000f8f/000f8f.png) | ✅ | ✅ | |
44
- | [British Airways](https://www.britishairways.com) 🇬🇧 | GB | BA | BAW | oneworld | ![#2e5c99](https://place-hold.it/10x10/2e5c99/2e5c99.png) | ✅ | ✅ | |
45
- | [Brussels Airlines](https://www.brusselsairlines.com) 🇧🇪 | BE | SN | BEL | Star Alliance | ![#051446](https://place-hold.it/10x10/051446/051446.png) | ✅ | ✅ | |
46
- | [Cathay Pacific](https://www.cathaypacific.com) 🇭🇰 | HK | CX | CPA | oneworld | ![#005d63](https://place-hold.it/10x10/005d63/005d63.png) | ✅ | ✅ | |
47
- | [Cebu Pacific](https://www.cebupacificair.com) | PH | 5J | CEB | | ![#0096d6](https://place-hold.it/10x10/0096d6/0096d6.png) | ✅ | ✅ | |
48
- | [China Airlines](https://www.china-airlines.com) 🇹🇼 | TW | CI | CAL | SkyTeam | ![#2b3088](https://place-hold.it/10x10/2b3088/2b3088.png) | ☑️ | ☑️ | |
49
- | [China Eastern Airlines](https://www.ceair.com) | CN | MU | CES | SkyTeam | ![#1a4fa3](https://place-hold.it/10x10/1a4fa3/1a4fa3.png) | ☑️ | ☑️ | |
50
- | [China Southern Airlines](https://www.csair.com) | CN | CZ | CSN | | ![#0093d0](https://place-hold.it/10x10/0093d0/0093d0.png) | ☑️ | ✅ | ☑️ |
51
- | [Condor](https://www.condor.com) | DE | DE | CFG | | ![#000000](https://place-hold.it/10x10/000000/000000.png) | ✅ | ✅ | |
52
- | [Copa Airlines](https://www.copaair.com) 🇵🇦 | PA | CM | CMP | Star Alliance | ![#0032a0](https://place-hold.it/10x10/0032a0/0032a0.png) | ✅ | ✅ | |
53
- | [Delta Air Lines](https://www.delta.com) | US | DL | DAL | SkyTeam | ![#e31837](https://place-hold.it/10x10/e31837/e31837.png) | ✅ | ✅ | |
54
- | [easyJet](https://www.easyjet.com) | GB | U2 | EZY | | ![#ff6600](https://place-hold.it/10x10/ff6600/ff6600.png) | ☑️ | ✅ | |
55
- | [EgyptAir](https://www.egyptair.com) 🇪🇬 | EG | MS | MSR | Star Alliance | ![#002855](https://place-hold.it/10x10/002855/002855.png) | ✅ | ✅ | |
56
- | [Emirates](https://www.emirates.com) 🇦🇪 | AE | EK | UAE | | ![#d71921](https://place-hold.it/10x10/d71921/d71921.png) | ✅ | ✅ | |
57
- | [Ethiopian Airlines](https://www.ethiopianairlines.com) 🇪🇹 | ET | ET | ETH | Star Alliance | ![#b71d20](https://place-hold.it/10x10/b71d20/b71d20.png) | ✅ | ✅ | |
58
- | [Etihad Airways](https://www.etihad.com) 🇦🇪 | AE | EY | ETD | | ![#bd8b13](https://place-hold.it/10x10/bd8b13/bd8b13.png) | | ✅ | |
59
- | [Eurowings](https://www.eurowings.com) | DE | EW | EWG | | ![#7c2045](https://place-hold.it/10x10/7c2045/7c2045.png) | ✅ | ✅ | |
60
- | [EVA Air](https://www.evaair.com) | TW | BR | EVA | Star Alliance | ![#00a64f](https://place-hold.it/10x10/00a64f/00a64f.png) | ☑️ | ✅ | ☑️ |
61
- | [Ewa Air](https://ewa-air.com) | FR | ZD | EWR | | ![#e1251b](https://place-hold.it/10x10/e1251b/e1251b.png) | | ✅ | |
62
- | [Fiji Airways](https://www.fijiairways.com) 🇫🇯 | FJ | FJ | FJI | oneworld | ![#473729](https://place-hold.it/10x10/473729/473729.png) | ✅ | ✅ | |
63
- | [Finnair](https://www.finnair.com) 🇫🇮 | FI | AY | FIN | oneworld | ![#0c0243](https://place-hold.it/10x10/0c0243/0c0243.png) | ✅ | ✅ | |
64
- | [Firefly](https://www.fireflyz.com.my) | MY | FY | FFM | | ![#ef5713](https://place-hold.it/10x10/ef5713/ef5713.png) | ✅ | ✅ | |
65
- | [Flybe](https://www.flybe.com) | GB | BE | BEE | | ![#5a3f99](https://place-hold.it/10x10/5a3f99/5a3f99.png) | | ✅ | |
66
- | [flydubai](https://www.flydubai.com) | AE | FZ | FDB | | ![#ff8200](https://place-hold.it/10x10/ff8200/ff8200.png) | | ☑️ | |
67
- | [flynas](https://www.flynas.com) | SA | XY | KNE | | ![#00b7ac](https://place-hold.it/10x10/00b7ac/00b7ac.png) | ✅ | ✅ | |
68
- | [Frontier Airlines](https://www.flyfrontier.com) | US | F9 | FFT | | ![#008c44](https://place-hold.it/10x10/008c44/008c44.png) | ✅ | ✅ | |
69
- | [Garuda Indonesia](https://www.garuda-indonesia.com) 🇮🇩 | ID | GA | GIA | SkyTeam | ![#002560](https://place-hold.it/10x10/002560/002560.png) | ✅ | ✅ | |
70
- | [GOL](https://www.voegol.com.br) | BR | G3 | GLO | | ![#f58220](https://place-hold.it/10x10/f58220/f58220.png) | ☑️ | ☑️ | |
71
- | [Hainan Airlines](https://www.hainanairlines.com) | CN | HU | CHH | | ![#d71920](https://place-hold.it/10x10/d71920/d71920.png) | ✅ | ✅ | |
72
- | [Hawaiian Airlines](https://www.hawaiianairlines.com) | US | HA | HAL | oneworld | ![#4e2582](https://place-hold.it/10x10/4e2582/4e2582.png) | ☑️ | ☑️ | |
73
- | [HK Express](https://www.hkexpress.com) | HK | UO | HKE | | ![#702b91](https://place-hold.it/10x10/702b91/702b91.png) | ✅ | ✅ | |
74
- | [Iberia](https://www.iberia.com) 🇪🇸 | ES | IB | IBE | oneworld | ![#dc1c2e](https://place-hold.it/10x10/dc1c2e/dc1c2e.png) | ✅ | ✅ | |
75
- | [Icelandair](https://www.icelandair.com) 🇮🇸 | IS | FI | ICE | | ![#0c0243](https://place-hold.it/10x10/0c0243/0c0243.png) | ✅ | ✅ | ✅ |
76
- | [IndiGo](https://www.goindigo.in) | IN | 6E | IGO | | ![#001b94](https://place-hold.it/10x10/001b94/001b94.png) | ✅ | ✅ | |
77
- | [Indonesia AirAsia](https://www.airasia.com) | ID | QZ | AWQ | | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | | ✅ | |
78
- | [ITA Airways](https://www.ita-airways.com) 🇮🇹 | IT | AZ | ITY | Star Alliance | ![#006643](https://place-hold.it/10x10/006643/006643.png) | | ☑️ | |
79
- | [Japan Airlines](https://www.jal.co.jp) 🇯🇵 | JP | JL | JAL | oneworld | ![#e50012](https://place-hold.it/10x10/e50012/e50012.png) | ✅ | ✅ | |
80
- | [Jet2.com](https://www.jet2.com) | GB | LS | EXS | | ![#d71920](https://place-hold.it/10x10/d71920/d71920.png) | ✅ | ✅ | |
81
- | [JetBlue Airways](https://www.jetblue.com) | US | B6 | JBU | | ![#003876](https://place-hold.it/10x10/003876/003876.png) | | ✅ | |
82
- | [Jetstar](https://www.jetstar.com) | AU | JQ | JST | | ![#ff5111](https://place-hold.it/10x10/ff5111/ff5111.png) | ✅ | ✅ | |
83
- | [Jetstar Japan](https://www.jetstar.com/jp/en/home) | JP | GK | JJP | | ![#ff5111](https://place-hold.it/10x10/ff5111/ff5111.png) | ✅ | ✅ | |
84
- | [Kenya Airways](https://www.kenya-airways.com) 🇰🇪 | KE | KQ | KQA | SkyTeam | ![#ed1c24](https://place-hold.it/10x10/ed1c24/ed1c24.png) | ✅ | ✅ | |
85
- | [KLM](https://www.klm.com) 🇳🇱 | NL | KL | KLM | SkyTeam | ![#009fd9](https://place-hold.it/10x10/009fd9/009fd9.png) | ✅ | ✅ | |
86
- | [Korean Air](https://www.koreanair.com) 🇰🇷 | KR | KE | KAL | | ![#051766](https://place-hold.it/10x10/051766/051766.png) | ✅ | ✅ | |
87
- | [Kuwait Airways](https://www.kuwaitairways.com) 🇰🇼 | KW | KU | KAC | | ![#1c285b](https://place-hold.it/10x10/1c285b/1c285b.png) | ✅ | ✅ | |
88
- | [LATAM Airlines](https://www.latamairlines.com) | CL | LA | LAN | | ![#1b0088](https://place-hold.it/10x10/1b0088/1b0088.png) | ✅ | ✅ | |
89
- | [LATAM Airlines Brasil](https://www.latam.com) | BR | JJ | TAM | | ![#1b0088](https://place-hold.it/10x10/1b0088/1b0088.png) | ✅ | ✅ | |
90
- | [LATAM Airlines Colombia](https://www.latam.com/es_co) | CO | 4C | ARE | | ![#1b0088](https://place-hold.it/10x10/1b0088/1b0088.png) | ✅ | ✅ | |
91
- | [LATAM Airlines Ecuador](https://www.latamairlines.com) | EC | XL | LNE | | ![#1b0088](https://place-hold.it/10x10/1b0088/1b0088.png) | ✅ | ✅ | |
92
- | [LATAM Airlines Paraguay](https://www.latam.com/es_py) | PY | PZ | LAP | | ![#1b0088](https://place-hold.it/10x10/1b0088/1b0088.png) | ✅ | ✅ | |
93
- | [LATAM Airlines Perú](https://www.latam.com) | PE | LP | LPE | | ![#1b0088](https://place-hold.it/10x10/1b0088/1b0088.png) | ✅ | ✅ | |
94
- | [Lion Air](https://www.lionair.co.id) | ID | JT | LNI | | ![#ed1b2f](https://place-hold.it/10x10/ed1b2f/ed1b2f.png) | ✅ | ✅ | |
95
- | [LOT Polish Airlines](https://www.lot.com) 🇵🇱 | PL | LO | LOT | Star Alliance | ![#252668](https://place-hold.it/10x10/252668/252668.png) | ✅ | ✅ | |
96
- | [Lufthansa](https://www.lufthansa.com) 🇩🇪 | DE | LH | DLH | Star Alliance | ![#05164d](https://place-hold.it/10x10/05164d/05164d.png) | ✅ | ✅ | |
97
- | [Malaysia Airlines](https://www.malaysiaairlines.com) 🇲🇾 | MY | MH | MAS | oneworld | ![#174887](https://place-hold.it/10x10/174887/174887.png) | ✅ | ✅ | |
98
- | [Myanmar National Airlines](https://www.flymna.com) 🇲🇲 | MM | UB | UBA | | ![#fdb515](https://place-hold.it/10x10/fdb515/fdb515.png) | ✅ | ✅ | |
99
- | [Norwegian Air Shuttle](https://www.norwegian.com) | NO | DY | NOZ | | ![#d81939](https://place-hold.it/10x10/d81939/d81939.png) | ☑️ | ✅ | |
100
- | [Oman Air](https://www.omanair.com) 🇴🇲 | OM | WY | OMA | oneworld | ![#b39759](https://place-hold.it/10x10/b39759/b39759.png) | ✅ | ✅ | |
101
- | [Pacific Airlines](https://www.pacificairlines.com) | VN | BL | PIC | | ![#324471](https://place-hold.it/10x10/324471/324471.png) | ✅ | ✅ | |
102
- | [Paranair](https://www.paranair.com) | PY | ZP | AZP | | ![#0037a2](https://place-hold.it/10x10/0037a2/0037a2.png) | ✅ | ✅ | |
103
- | [Peach Aviation](https://www.flypeach.com) | JP | MM | APJ | | ![#9b308c](https://place-hold.it/10x10/9b308c/9b308c.png) | | ✅ | |
104
- | [Pegasus Airlines](https://www.flypgs.com) | TR | PC | PGT | | ![#fdb913](https://place-hold.it/10x10/fdb913/fdb913.png) | ✅ | ✅ | |
105
- | [Philippine Airlines](https://www.philippineairlines.com) 🇵🇭 | PH | PR | PAL | | ![#001f60](https://place-hold.it/10x10/001f60/001f60.png) | ✅ | ✅ | |
106
- | [Philippines AirAsia](https://www.airasia.com) | PH | Z2 | APG | | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | | ✅ | |
107
- | [Qantas](https://www.qantas.com) 🇦🇺 | AU | QF | QFA | oneworld | ![#e40000](https://place-hold.it/10x10/e40000/e40000.png) | ☑️ | ✅ | |
108
- | [Qatar Airways](https://www.qatarairways.com) 🇶🇦 | QA | QR | QTR | oneworld | ![#662046](https://place-hold.it/10x10/662046/662046.png) | ✅ | ✅ | |
109
- | [Riyadh Air](https://www.riyadhair.com) 🇸🇦 | SA | RX | RXI | | ![#240852](https://place-hold.it/10x10/240852/240852.png) | ✅ | ✅ | |
110
- | [Royal Air Maroc](https://www.royalairmaroc.com) 🇲🇦 | MA | AT | RAM | SkyTeam | ![#c20831](https://place-hold.it/10x10/c20831/c20831.png) | ✅ | ✅ | ☑️ |
111
- | [Royal Brunei Airlines](https://www.flyroyalbrunei.com) | BN | BI | RBA | | ![#ffe600](https://place-hold.it/10x10/ffe600/ffe600.png) | | ☑️ | |
112
- | [Ryanair](https://www.ryanair.com) | IE | FR | RYR | | ![#073590](https://place-hold.it/10x10/073590/073590.png) | ✅ | ✅ | |
113
- | [SAS Connect](https://www.flysas.com) | IE | SL | SZS | | ![#000099](https://place-hold.it/10x10/000099/000099.png) | | ✅ | |
114
- | [Saudia](https://www.saudia.com) 🇸🇦 | SA | SV | SVA | SkyTeam | ![#0a6937](https://place-hold.it/10x10/0a6937/0a6937.png) | ✅ | ✅ | |
115
- | [Scandinavian Airlines](https://www.flysas.com) 🇩🇰 🇳🇴 🇸🇪 | DK,NO,SE | SK | SAS | Star Alliance | ![#000099](https://place-hold.it/10x10/000099/000099.png) | | ✅ | |
116
- | [Scoot](https://www.flyscoot.com) | SG | TR | TGW | Value Alliance | ![#ffe900](https://place-hold.it/10x10/ffe900/ffe900.png) | | ☑️ | |
117
- | [Singapore Airlines](https://www.singaporeair.com) 🇸🇬 | SG | SQ | SIA | Star Alliance | ![#232f67](https://place-hold.it/10x10/232f67/232f67.png) | ✅ | ✅ | |
118
- | [Southwest Airlines](https://www.southwest.com) | US | WN | SWA | | ![#304cb2](https://place-hold.it/10x10/304cb2/304cb2.png) | ☑️ | ☑️ | |
119
- | [Spirit Airlines](https://www.spirit.com) | US | NK | NKS | | ![#ffec00](https://place-hold.it/10x10/ffec00/ffec00.png) | | ✅ | |
120
- | [Starlux Airlines](https://www.starlux-airlines.com) | TW | JX | SJX | | ![#996a4e](https://place-hold.it/10x10/996a4e/996a4e.png) | ✅ | ✅ | |
121
- | [Sun PhuQuoc Airways](https://www.sunphuquocairways.com) | VN | 9G | SPQ | | ![#ca1c2a](https://place-hold.it/10x10/ca1c2a/ca1c2a.png) | ✅ | ✅ | |
122
- | [SWISS](https://www.swiss.com) 🇨🇭 | CH | LX | SWR | Star Alliance | ![#cf0a2c](https://place-hold.it/10x10/cf0a2c/cf0a2c.png) | ✅ | ✅ | |
123
- | [T'way Air](https://www.twayair.com) | KR | TW | TWB | | ![#d22c25](https://place-hold.it/10x10/d22c25/d22c25.png) | | ✅ | |
124
- | [TAP Air Portugal](https://www.tapairportugal.com) 🇵🇹 | PT | TP | TAP | Star Alliance | ![#bfd730](https://place-hold.it/10x10/bfd730/bfd730.png) | ☑️ | ☑️ | |
125
- | [TAROM](https://www.tarom.ro) 🇷🇴 | RO | RO | ROT | SkyTeam | ![#003366](https://place-hold.it/10x10/003366/003366.png) | ✅ | ✅ | ✅ |
126
- | [Thai AirAsia](https://www.airasia.com) | TH | FD | AIQ | | ![#ff0000](https://place-hold.it/10x10/ff0000/ff0000.png) | | ✅ | |
127
- | [Thai Airways](https://www.thaiairways.com) 🇹🇭 | TH | TG | THA | Star Alliance | ![#330066](https://place-hold.it/10x10/330066/330066.png) | ☑️ | ✅ | |
128
- | [Transavia](https://www.transavia.com) | NL | HV | TRA | | ![#00d66c](https://place-hold.it/10x10/00d66c/00d66c.png) | ✅ | ✅ | |
129
- | [Turkish Airlines](https://www.turkishairlines.com) 🇹🇷 | TR | TK | THY | Star Alliance | ![#c90019](https://place-hold.it/10x10/c90019/c90019.png) | ✅ | ✅ | |
130
- | [United Airlines](https://www.united.com) | US | UA | UAL | Star Alliance | ![#1414d2](https://place-hold.it/10x10/1414d2/1414d2.png) | ✅ | ✅ | |
131
- | [VietJet Air](https://www.vietjetair.com) | VN | VJ | VJC | | ![#e12127](https://place-hold.it/10x10/e12127/e12127.png) | | ✅ | |
132
- | [Vietnam Airlines](https://www.vietnamairlines.com) 🇻🇳 | VN | VN | HVN | SkyTeam | ![#d99e09](https://place-hold.it/10x10/d99e09/d99e09.png) | ✅ | ✅ | |
133
- | [Vietravel Airlines](https://www.vietravelairlines.com) | VN | VU | VAG | | ![#0066b3](https://place-hold.it/10x10/0066b3/0066b3.png) | ✅ | ✅ | |
134
- | [Virgin Atlantic](https://www.virginatlantic.com) | GB | VS | VIR | SkyTeam | ![#da0530](https://place-hold.it/10x10/da0530/da0530.png) | ✅ | ✅ | |
135
- | [Virgin Australia](https://www.virginaustralia.com) | AU | VA | VOZ | | ![#e31e23](https://place-hold.it/10x10/e31e23/e31e23.png) | ✅ | ✅ | |
136
- | [Viva](https://www.vivaaerobus.com) | MX | VB | VIV | | ![#00ae44](https://place-hold.it/10x10/00ae44/00ae44.png) | ✅ | ✅ | |
137
- | [Volaris](https://www.volaris.com) | MX | Y4 | VOI | | ![#5b2d82](https://place-hold.it/10x10/5b2d82/5b2d82.png) | ☑️ | ☑️ | |
138
- | [Volotea](https://www.volotea.com) | ES | V7 | VOE | | ![#ed164a](https://place-hold.it/10x10/ed164a/ed164a.png) | ✅ | ✅ | |
139
- | [Vueling](https://www.vueling.com) | ES | VY | VLG | | ![#ffcc00](https://place-hold.it/10x10/ffcc00/ffcc00.png) | | ☑️ | |
140
- | [WestJet](https://www.westjet.com) | CA | WS | WJA | | ![#00aaa6](https://place-hold.it/10x10/00aaa6/00aaa6.png) | ✅ | ✅ | |
141
- | [Wizz Air](https://www.wizzair.com) | HU | W6 | WZZ | | ![#c6007e](https://place-hold.it/10x10/c6007e/c6007e.png) | | ✅ | |
142
- | [Wizz Air Abu Dhabi](https://www.wizzair.com) | AE | 5W | WAZ | | ![#c6007e](https://place-hold.it/10x10/c6007e/c6007e.png) | | ✅ | |
143
- | [Wizz Air UK](https://www.wizzair.com) | GB | W9 | WUK | | ![#c6007e](https://place-hold.it/10x10/c6007e/c6007e.png) | | ✅ | |
144
- | [XiamenAir](https://www.xiamenair.com) | CN | MF | CXA | SkyTeam | ![#008cd5](https://place-hold.it/10x10/008cd5/008cd5.png) | ✅ | ✅ | |