refactor(font): split types into separate files for better maintainability

This commit is contained in:
Ilia Mashkov
2026-01-06 15:23:08 +03:00
parent db814f0b93
commit be14a62e83
16 changed files with 847 additions and 838 deletions

View File

@@ -10,7 +10,7 @@
import type {
FontshareApiModel,
FontshareFont,
} from '$entities/Font';
} from '$entities/Font/model/types/fontshare';
import { api } from '$shared/api/api';
import { buildQueryString } from '$shared/utils';
import type { QueryParams } from '$shared/utils';
@@ -43,7 +43,7 @@ export interface FontshareParams extends QueryParams {
/**
* Fontshare API response wrapper
* Re-exported from model/types for backward compatibility
* Re-exported from model/types/fontshare for backward compatibility
*/
export type FontshareResponse = FontshareApiModel;

View File

@@ -10,7 +10,7 @@
import type {
FontItem,
GoogleFontsApiModel,
} from '$entities/Font';
} from '$entities/Font/model/types/google';
import { api } from '$shared/api/api';
import { buildQueryString } from '$shared/utils';
import type { QueryParams } from '$shared/utils';
@@ -47,13 +47,13 @@ export interface GoogleFontsParams extends QueryParams {
/**
* Google Fonts API response wrapper
* Re-exported from model/types for backward compatibility
* Re-exported from model/types/google for backward compatibility
*/
export type GoogleFontsResponse = GoogleFontsApiModel;
/**
* Simplified font item from Google Fonts API
* Re-exported from model/types for backward compatibility
* Re-exported from model/types/google for backward compatibility
*/
export type GoogleFontItem = FontItem;

View File

@@ -2,7 +2,7 @@ import type {
FontshareFont,
GoogleFontItem,
UnifiedFont,
} from '$entities/Font';
} from '$entities/Font/model/types';
import {
describe,
expect,
@@ -136,9 +136,9 @@ describe('Font Normalization', () => {
});
it('maps variant weights correctly', () => {
const font = {
const font: GoogleFontItem = {
...mockGoogleFont,
variants: ['regular', '100', '400', '700', '900'],
variants: ['regular', '100', '400', '700', '900'] as any,
};
const result = normalizeGoogleFont(font);

View File

@@ -12,10 +12,10 @@ import type {
FontProvider,
FontStyleUrls,
FontSubset,
FontshareFont,
GoogleFontItem,
UnifiedFont,
} from '../../model/types';
} from '$entities/Font/model/types';
import type { FontshareFont } from '$entities/Font/model/types/fontshare';
import type { GoogleFontItem } from '$entities/Font/model/types/google';
import type { UnifiedFont } from '$entities/Font/model/types/normalize';
/**
* Map Google Fonts category to unified FontCategory
@@ -272,3 +272,6 @@ export function normalizeFontshareFonts(
): UnifiedFont[] {
return apiFonts.map(normalizeFontshareFont);
}
// Re-export UnifiedFont for backward compatibility
export type { UnifiedFont } from '$entities/Font/model/types/normalize';

View File

@@ -21,7 +21,7 @@ export {
normalizeFontshareFonts,
normalizeGoogleFont,
normalizeGoogleFonts,
} from './api/normalize';
} from './api/normalize/normalize';
export type {
// Domain types
FontCategory,
@@ -57,4 +57,4 @@ export type {
// Normalization types
UnifiedFont,
UnifiedFontVariant,
} from './model/types';
} from './model/types/index';

View File

@@ -16,7 +16,7 @@ import type {
FontProvider,
FontSubset,
UnifiedFont,
} from '$entities/Font';
} from '$entities/Font/model/types';
import { createCollectionCache } from '$shared/fetch/collectionCache';
import type { Writable } from 'svelte/store';
import {

View File

@@ -1,814 +0,0 @@
/**
* ============================================================================
* DOMAIN TYPES
* ============================================================================
*/
/**
* Font category
*/
export type FontCategory = 'sans-serif' | 'serif' | 'display' | 'handwriting' | 'monospace';
/**
* Font provider
*/
export type FontProvider = 'google' | 'fontshare';
/**
* Font subset
*/
export type FontSubset = 'latin' | 'latin-ext' | 'cyrillic' | 'greek' | 'arabic' | 'devanagari';
/**
* ============================================================================
* GOOGLE FONTS API TYPES
* ============================================================================ */
/**
* Model of google fonts api response
*/
export interface GoogleFontsApiModel {
/**
* Array of font items returned by the Google Fonts API
* Contains all font families matching the requested query parameters
*/
items: FontItem[];
}
/**
* Individual font from Google Fonts API
*/
export interface FontItem {
/**
* Font family name (e.g., "Roboto", "Open Sans", "Lato")
* This is the name used in CSS font-family declarations
*/
family: string;
/**
* Font category classification (e.g., "sans-serif", "serif", "display", "handwriting", "monospace")
* Useful for grouping and filtering fonts by style
*/
category: string;
/**
* Available font variants for this font family
* Array of strings representing available weights and styles
* Examples: ["regular", "italic", "100", "200", "300", "400", "500", "600", "700", "800", "900", "100italic", "900italic"]
* The keys in the `files` object correspond to these variant values
*/
variants: FontVariant[];
/**
* Supported character subsets for this font
* Examples: ["latin", "latin-ext", "cyrillic", "greek", "arabic", "devanagari", "vietnamese", "hebrew", "thai", etc.]
* Determines which character sets are included in the font files
*/
subsets: string[];
/**
* Font version identifier
* Format: "v" followed by version number (e.g., "v31", "v20", "v1")
* Used to track font updates and cache busting
*/
version: string;
/**
* Last modification date of the font
* Format: ISO 8601 date string (e.g., "2024-01-15", "2023-12-01")
* Indicates when the font was last updated by the font foundry
*/
lastModified: string;
/**
* Mapping of font variants to their downloadable URLs
* Keys correspond to values in the `variants` array
* Examples:
* - "regular" → "https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Me4W..."
* - "700" → "https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlf..."
* - "700italic" → "https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzA..."
*/
files: FontFiles;
/**
* URL to the font menu preview image
* Typically a PNG showing the font family name in the font
* Example: "https://fonts.gstatic.com/l/font?kit=KFOmCnqEu92Fr1Me4W...&s=i2"
*/
menu: string;
}
/**
* Type alias for backward compatibility
* Google Fonts API font item
*/
export type GoogleFontItem = FontItem;
/**
* Individual font from Google Fonts API
*/
export interface FontItem {
/**
* Font family name (e.g., "Roboto", "Open Sans", "Lato")
* This is the name used in CSS font-family declarations
*/
family: string;
/**
* Font category classification (e.g., "sans-serif", "serif", "display", "handwriting", "monospace")
* Useful for grouping and filtering fonts by style
*/
category: string;
/**
* Available font variants for this font family
* Array of strings representing available weights and styles
* Examples: ["regular", "italic", "100", "200", "300", "400", "500", "600", "700", "800", "900", "100italic", "900italic"]
* The keys in the `files` object correspond to these variant values
*/
variants: FontVariant[];
/**
* Supported character subsets for this font
* Examples: ["latin", "latin-ext", "cyrillic", "greek", "arabic", "devanagari", "vietnamese", "hebrew", "thai", etc.]
* Determines which character sets are included in the font files
*/
subsets: string[];
/**
* Font version identifier
* Format: "v" followed by version number (e.g., "v31", "v20", "v1")
* Used to track font updates and cache busting
*/
version: string;
/**
* Last modification date of the font
* Format: ISO 8601 date string (e.g., "2024-01-15", "2023-12-01")
* Indicates when the font was last updated by the font foundry
*/
lastModified: string;
/**
* Mapping of font variants to their downloadable URLs
* Keys correspond to values in the `variants` array
* Examples:
* - "regular" → "https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Me4W..."
* - "700" → "https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlf..."
* - "700italic" → "https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzA..."
*/
files: FontFiles;
/**
* URL to the font menu preview image
* Typically a PNG showing the font family name in the font
* Example: "https://fonts.gstatic.com/l/font?kit=KFOmCnqEu92Fr1Me4W...&s=i2"
*/
menu: string;
}
/**
* Standard font weights that can appear in Google Fonts API
*/
export type FontWeight = '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
/**
* Italic variant format: e.g., "100italic", "400italic", "700italic"
*/
export type FontWeightItalic = `${FontWeight}italic`;
/**
* All possible font variants in Google Fonts API
* - Numeric weights: "400", "700", etc.
* - Italic variants: "400italic", "700italic", etc.
* - Legacy names: "regular", "italic", "bold", "bolditalic"
*/
export type FontVariant =
| FontWeight
| FontWeightItalic
| 'regular'
| 'italic'
| 'bold'
| 'bolditalic';
/**
* Google Fonts API file mapping
* Dynamic keys that match the variants array
*
* Examples:
* - { "regular": "...", "italic": "...", "700": "...", "700italic": "..." }
* - { "400": "...", "400italic": "...", "900": "..." }
*/
export type FontFiles = Partial<Record<FontVariant, string>>;
/**
* ============================================================================
* FONTHARE API TYPES
* ============================================================================
*/
import type { CollectionApiModel } from '$shared/types/collection';
export const FONTSHARE_API_URL = 'https://api.fontshare.com/v2' as const;
/**
* Model of Fontshare API response
* @see https://fontshare.com
*/
export type FontshareApiModel = CollectionApiModel<FontshareFont>;
/**
* Individual font metadata from Fontshare API
*/
export interface FontshareFont {
/**
* Unique identifier for the font
* UUID v4 format (e.g., "20e9fcdc-1e41-4559-a43d-1ede0adc8896")
*/
id: string;
/**
* Display name of the font family
* Examples: "Satoshi", "General Sans", "Clash Display"
*/
name: string;
/**
* Native/localized name of the font (if available)
* Often null for Latin-script fonts
*/
native_name: string | null;
/**
* URL-friendly identifier for the font
* Used in URLs: e.g., "satoshi", "general-sans", "clash-display"
*/
slug: string;
/**
* Font category classification
* Examples: "Sans", "Serif", "Display", "Script"
*/
category: string;
/**
* Script/writing system supported by the font
* Examples: "latin", "arabic", "devanagari"
*/
script: string;
/**
* Font publisher/foundry information
*/
publisher: FontsharePublisher;
/**
* Array of designers who created this font
* Multiple designers may have collaborated on a single font
*/
designers: FontshareDesigner[];
/**
* Related font families (if any)
* Often null, as fonts are typically independent
*/
related_families: string | null;
/**
* Whether to display publisher as the designer instead of individual designers
*/
display_publisher_as_designer: boolean;
/**
* Whether trial downloads are enabled for this font
*/
trials_enabled: boolean;
/**
* Whether to show Latin-specific metrics
*/
show_latin_metrics: boolean;
/**
* Type of license for this font
* Examples: "itf_ffl" (ITF Free Font License)
*/
license_type: string;
/**
* Comma-separated list of languages supported by this font
* Example: "Afar, Afrikaans, Albanian, Aranese, Aromanian, Aymara, ..."
*/
languages: string;
/**
* ISO 8601 timestamp when the font was added to Fontshare
* Format: "2021-03-12T20:49:05Z"
*/
inserted_at: string;
/**
* HTML-formatted story/description about the font
* Contains marketing text, design philosophy, and usage recommendations
*/
story: string;
/**
* Version of the font family
* Format: "1.0", "1.2", etc.
*/
version: string;
/**
* Total number of times this font has been viewed
*/
views: number;
/**
* Number of views in the recent time period
*/
views_recent: number;
/**
* Whether this font is marked as "hot"/trending
*/
is_hot: boolean;
/**
* Whether this font is marked as new
*/
is_new: boolean;
/**
* Whether this font is in the shortlisted collection
*/
is_shortlisted: boolean | null;
/**
* Whether this font is marked as top/popular
*/
is_top: boolean;
/**
* Variable font axes (for variable fonts)
* Empty array [] for static fonts
*/
axes: FontshareAxis[];
/**
* Tags/categories for this font
* Examples: ["Magazines", "Branding", "Logos", "Posters"]
*/
font_tags: FontshareTag[];
/**
* OpenType features available in this font
*/
features: FontshareFeature[];
/**
* Array of available font styles/variants
* Each style represents a different font file (weight, italic, variable)
*/
styles: FontshareStyle[];
}
/**
* Publisher/foundry information
*/
export interface FontsharePublisher {
/**
* Description/bio of the publisher
* Example: "Indian Type Foundry (ITF) creates retail and custom multilingual fonts..."
*/
bio: string;
/**
* Publisher email (if available)
*/
email: string | null;
/**
* Unique publisher identifier
* UUID format
*/
id: string;
/**
* Publisher links (social media, website, etc.)
*/
links: FontshareLink[];
/**
* Publisher name
* Example: "Indian Type Foundry"
*/
name: string;
}
/**
* Designer information
*/
export interface FontshareDesigner {
/**
* Designer bio/description
*/
bio: string;
/**
* Designer links (Twitter, website, etc.)
*/
links: FontshareLink[];
/**
* Designer name
*/
name: string;
}
/**
* Link information
*/
export interface FontshareLink {
/**
* Name of the link platform/site
* Examples: "Twitter", "GitHub", "Website"
*/
name: string;
/**
* URL of the link (may be null)
*/
url: string | null;
}
/**
* Font tag/category
*/
export interface FontshareTag {
/**
* Tag name
* Examples: "Magazines", "Branding", "Logos", "Posters"
*/
name: string;
}
/**
* OpenType feature
*/
export interface FontshareFeature {
/**
* Feature name (descriptive name or null)
* Examples: "Alternate t", "All Alternates", or null
*/
name: string | null;
/**
* Whether this feature is on by default
*/
on_by_default: boolean;
/**
* OpenType feature tag (4-character code)
* Examples: "ss01", "frac", "liga", "aalt", "case"
*/
tag: string;
}
/**
* Variable font axis (for variable fonts)
* Defines the range and properties of a variable font axis (e.g., weight)
*/
export interface FontshareAxis {
/**
* Name of the axis
* Example: "wght" (weight axis)
*/
name: string;
/**
* CSS property name for the axis
* Example: "wght"
*/
property: string;
/**
* Default value for the axis
* Example: 420.0, 650.0, 700.0
*/
range_default: number;
/**
* Minimum value for the axis
* Example: 300.0, 100.0, 200.0
*/
range_left: number;
/**
* Maximum value for the axis
* Example: 900.0, 700.0, 800.0
*/
range_right: number;
}
/**
* Individual font style/variant
* Each style represents a single downloadable font file
*/
export interface FontshareStyle {
/**
* Unique identifier for this style
* UUID format
*/
id: string;
/**
* Whether this is the default style for the font family
* Typically, one style per font is marked as default
*/
default: boolean;
/**
* CDN URL to the font file
* Protocol-relative URL: "//cdn.fontshare.com/wf/..."
* Note: URL starts with "//" (protocol-relative), may need protocol prepended
*/
file: string;
/**
* Whether this style is italic
* false for upright, true for italic styles
*/
is_italic: boolean;
/**
* Whether this is a variable font
* Variable fonts have adjustable axes (weight, slant, etc.)
*/
is_variable: boolean;
/**
* Typography properties for this style
* Contains measurements like cap height, x-height, ascenders/descenders
* May be empty object {} for some styles
*/
properties: FontshareStyleProperties | Record<string, never>;
/**
* Weight information for this style
*/
weight: FontshareWeight;
}
/**
* Typography/measurement properties for a font style
*/
export interface FontshareStyleProperties {
/**
* Distance from baseline to the top of ascenders
* Example: 1010, 990, 1000
*/
ascending_leading: number | null;
/**
* Height of uppercase letters (cap height)
* Example: 710, 680, 750
*/
cap_height: number | null;
/**
* Distance from baseline to the bottom of descenders (negative value)
* Example: -203, -186, -220
*/
descending_leading: number | null;
/**
* Body height of the font
* Often null in Fontshare data
*/
body_height: number | null;
/**
* Maximum character width in the font
* Example: 1739, 1739, 1739
*/
max_char_width: number | null;
/**
* Height of lowercase x-height
* Example: 480, 494, 523
*/
x_height: number | null;
/**
* Maximum Y coordinate (top of ascenders)
* Example: 1010, 990, 1026
*/
y_max: number | null;
/**
* Minimum Y coordinate (bottom of descenders)
* Example: -240, -250, -280
*/
y_min: number | null;
}
/**
* Weight information for a font style
*/
export interface FontshareWeight {
/**
* Display label for the weight
* Examples: "Light", "Regular", "Bold", "Variable", "Variable Italic"
*/
label: string;
/**
* Internal name for the weight
* Examples: "Light", "Regular", "Bold", "Variable", "VariableItalic"
*/
name: string;
/**
* Native/localized name for the weight (if available)
* Often null for Latin-script fonts
*/
native_name: string | null;
/**
* Numeric weight value
* Examples: 300, 400, 700, 0 (for variable fonts), 1, 2
* Note: This matches the `weight` property
*/
number: number;
/**
* Numeric weight value (duplicate of `number`)
* Appears to be redundant with `number` field
*/
weight: number;
}
/**
* ============================================================================
* NORMALIZATION TYPES
* ============================================================================
*/
/**
* Font variant types (standardized)
*/
export type UnifiedFontVariant = string;
/**
* Font style URLs
*/
export interface FontStyleUrls {
/** Regular weight URL */
regular?: string;
/** Italic URL */
italic?: string;
/** Bold weight URL */
bold?: string;
/** Bold italic URL */
boldItalic?: string;
}
/**
* Font metadata
*/
export interface FontMetadata {
/** Timestamp when font was cached */
cachedAt: number;
/** Font version from provider */
version?: string;
/** Last modified date from provider */
lastModified?: string;
/** Popularity rank (if available from provider) */
popularity?: number;
}
/**
* Font features (variable fonts, axes, tags)
*/
export interface FontFeatures {
/** Whether this is a variable font */
isVariable?: boolean;
/** Variable font axes (for Fontshare) */
axes?: Array<{
name: string;
property: string;
default: number;
min: number;
max: number;
}>;
/** Usage tags (for Fontshare) */
tags?: string[];
}
/**
* Unified font model
*
* Combines Google Fonts and Fontshare data into a common interface
* for consistent font handling across the application.
*/
export interface UnifiedFont {
/** Unique identifier (Google: family name, Fontshare: slug) */
id: string;
/** Font display name */
name: string;
/** Font provider (google | fontshare) */
provider: FontProvider;
/** Font category classification */
category: FontCategory;
/** Supported character subsets */
subsets: FontSubset[];
/** Available font variants (weights, styles) */
variants: UnifiedFontVariant[];
/** URL mapping for font file downloads */
styles: FontStyleUrls;
/** Additional metadata */
metadata: FontMetadata;
/** Advanced font features */
features: FontFeatures;
}
/**
* ============================================================================
* STORE TYPES
* ============================================================================
*/
/**
* Font collection state
*/
export interface FontCollectionState {
/** All cached fonts */
fonts: Record<string, UnifiedFont>;
/** Active filters */
filters: FontCollectionFilters;
/** Sort configuration */
sort: FontCollectionSort;
}
/**
* Font collection filters
*/
export interface FontCollectionFilters {
/** Search query */
searchQuery?: string;
/** Filter by provider */
provider?: FontProvider;
/** Filter by category */
category?: FontCategory;
/** Filter by subsets */
subsets?: string[];
}
/**
* Font collection sort configuration
*/
export interface FontCollectionSort {
/** Sort field */
field: 'name' | 'popularity' | 'category';
/** Sort direction */
direction: 'asc' | 'desc';
}
/**
* Font collection store interface
*/
export interface FontCollectionStore {
/** Main state store */
state: import('svelte/store').Writable<FontCollectionState>;
/** All fonts as array */
fonts: import('svelte/store').Readable<UnifiedFont[]>;
/** Filtered fonts as array */
filteredFonts: import('svelte/store').Readable<UnifiedFont[]>;
/** Number of fonts in collection */
count: import('svelte/store').Readable<number>;
/** Loading state */
isLoading: import('svelte/store').Readable<boolean>;
/** Error state */
error: import('svelte/store').Readable<string | undefined>;
/** Add fonts to collection */
addFonts: (fonts: UnifiedFont[]) => void;
/** Add single font to collection */
addFont: (font: UnifiedFont) => void;
/** Remove font from collection */
removeFont: (fontId: string) => void;
/** Clear all fonts */
clear: () => void;
/** Update filters */
setFilters: (filters: Partial<FontCollectionFilters>) => void;
/** Clear filters */
clearFilters: () => void;
/** Update sort configuration */
setSort: (sort: FontCollectionSort) => void;
/** Get font by ID */
getFont: (fontId: string) => UnifiedFont | undefined;
/** Get fonts by provider */
getFontsByProvider: (provider: FontProvider) => UnifiedFont[];
/** Get fonts by category */
getFontsByCategory: (category: FontCategory) => UnifiedFont[];
}

View File

@@ -0,0 +1,20 @@
/**
* ============================================================================
* DOMAIN TYPES
* ============================================================================
*/
/**
* Font category
*/
export type FontCategory = 'sans-serif' | 'serif' | 'display' | 'handwriting' | 'monospace';
/**
* Font provider
*/
export type FontProvider = 'google' | 'fontshare';
/**
* Font subset
*/
export type FontSubset = 'latin' | 'latin-ext' | 'cyrillic' | 'greek' | 'arabic' | 'devanagari';

View File

@@ -0,0 +1,447 @@
/**
* ============================================================================
* FONTHARE API TYPES
* ============================================================================
*/
import type { CollectionApiModel } from '$shared/types/collection';
export const FONTSHARE_API_URL = 'https://api.fontshare.com/v2' as const;
/**
* Model of Fontshare API response
* @see https://fontshare.com
*/
export type FontshareApiModel = CollectionApiModel<FontshareFont>;
/**
* Individual font metadata from Fontshare API
*/
export interface FontshareFont {
/**
* Unique identifier for the font
* UUID v4 format (e.g., "20e9fcdc-1e41-4559-a43d-1ede0adc8896")
*/
id: string;
/**
* Display name of the font family
* Examples: "Satoshi", "General Sans", "Clash Display"
*/
name: string;
/**
* Native/localized name of the font (if available)
* Often null for Latin-script fonts
*/
native_name: string | null;
/**
* URL-friendly identifier for the font
* Used in URLs: e.g., "satoshi", "general-sans", "clash-display"
*/
slug: string;
/**
* Font category classification
* Examples: "Sans", "Serif", "Display", "Script"
*/
category: string;
/**
* Script/writing system supported by the font
* Examples: "latin", "arabic", "devanagari"
*/
script: string;
/**
* Font publisher/foundry information
*/
publisher: FontsharePublisher;
/**
* Array of designers who created this font
* Multiple designers may have collaborated on a single font
*/
designers: FontshareDesigner[];
/**
* Related font families (if any)
* Often null, as fonts are typically independent
*/
related_families: string | null;
/**
* Whether to display publisher as the designer instead of individual designers
*/
display_publisher_as_designer: boolean;
/**
* Whether trial downloads are enabled for this font
*/
trials_enabled: boolean;
/**
* Whether to show Latin-specific metrics
*/
show_latin_metrics: boolean;
/**
* Type of license for this font
* Examples: "itf_ffl" (ITF Free Font License)
*/
license_type: string;
/**
* Comma-separated list of languages supported by this font
* Example: "Afar, Afrikaans, Albanian, Aranese, Aromanian, Aymara, ..."
*/
languages: string;
/**
* ISO 8601 timestamp when the font was added to Fontshare
* Format: "2021-03-12T20:49:05Z"
*/
inserted_at: string;
/**
* HTML-formatted story/description about the font
* Contains marketing text, design philosophy, and usage recommendations
*/
story: string;
/**
* Version of the font family
* Format: "1.0", "1.2", etc.
*/
version: string;
/**
* Total number of times this font has been viewed
*/
views: number;
/**
* Number of views in the recent time period
*/
views_recent: number;
/**
* Whether this font is marked as "hot"/trending
*/
is_hot: boolean;
/**
* Whether this font is marked as new
*/
is_new: boolean;
/**
* Whether this font is in the shortlisted collection
*/
is_shortlisted: boolean | null;
/**
* Whether this font is marked as top/popular
*/
is_top: boolean;
/**
* Variable font axes (for variable fonts)
* Empty array [] for static fonts
*/
axes: FontshareAxis[];
/**
* Tags/categories for this font
* Examples: ["Magazines", "Branding", "Logos", "Posters"]
*/
font_tags: FontshareTag[];
/**
* OpenType features available in this font
*/
features: FontshareFeature[];
/**
* Array of available font styles/variants
* Each style represents a different font file (weight, italic, variable)
*/
styles: FontshareStyle[];
}
/**
* Publisher/foundry information
*/
export interface FontsharePublisher {
/**
* Description/bio of the publisher
* Example: "Indian Type Foundry (ITF) creates retail and custom multilingual fonts..."
*/
bio: string;
/**
* Publisher email (if available)
*/
email: string | null;
/**
* Unique publisher identifier
* UUID format
*/
id: string;
/**
* Publisher links (social media, website, etc.)
*/
links: FontshareLink[];
/**
* Publisher name
* Example: "Indian Type Foundry"
*/
name: string;
}
/**
* Designer information
*/
export interface FontshareDesigner {
/**
* Designer bio/description
*/
bio: string;
/**
* Designer links (Twitter, website, etc.)
*/
links: FontshareLink[];
/**
* Designer name
*/
name: string;
}
/**
* Link information
*/
export interface FontshareLink {
/**
* Name of the link platform/site
* Examples: "Twitter", "GitHub", "Website"
*/
name: string;
/**
* URL of the link (may be null)
*/
url: string | null;
}
/**
* Font tag/category
*/
export interface FontshareTag {
/**
* Tag name
* Examples: "Magazines", "Branding", "Logos", "Posters"
*/
name: string;
}
/**
* OpenType feature
*/
export interface FontshareFeature {
/**
* Feature name (descriptive name or null)
* Examples: "Alternate t", "All Alternates", or null
*/
name: string | null;
/**
* Whether this feature is on by default
*/
on_by_default: boolean;
/**
* OpenType feature tag (4-character code)
* Examples: "ss01", "frac", "liga", "aalt", "case"
*/
tag: string;
}
/**
* Variable font axis (for variable fonts)
* Defines the range and properties of a variable font axis (e.g., weight)
*/
export interface FontshareAxis {
/**
* Name of the axis
* Example: "wght" (weight axis)
*/
name: string;
/**
* CSS property name for the axis
* Example: "wght"
*/
property: string;
/**
* Default value for the axis
* Example: 420.0, 650.0, 700.0
*/
range_default: number;
/**
* Minimum value for the axis
* Example: 300.0, 100.0, 200.0
*/
range_left: number;
/**
* Maximum value for the axis
* Example: 900.0, 700.0, 800.0
*/
range_right: number;
}
/**
* Individual font style/variant
* Each style represents a single downloadable font file
*/
export interface FontshareStyle {
/**
* Unique identifier for this style
* UUID format
*/
id: string;
/**
* Whether this is the default style for the font family
* Typically, one style per font is marked as default
*/
default: boolean;
/**
* CDN URL to the font file
* Protocol-relative URL: "//cdn.fontshare.com/wf/..."
* Note: URL starts with "//" (protocol-relative), may need protocol prepended
*/
file: string;
/**
* Whether this style is italic
* false for upright, true for italic styles
*/
is_italic: boolean;
/**
* Whether this is a variable font
* Variable fonts have adjustable axes (weight, slant, etc.)
*/
is_variable: boolean;
/**
* Typography properties for this style
* Contains measurements like cap height, x-height, ascenders/descenders
* May be empty object {} for some styles
*/
properties: FontshareStyleProperties | Record<string, never>;
/**
* Weight information for this style
*/
weight: FontshareWeight;
}
/**
* Typography/measurement properties for a font style
*/
export interface FontshareStyleProperties {
/**
* Distance from baseline to the top of ascenders
* Example: 1010, 990, 1000
*/
ascending_leading: number | null;
/**
* Height of uppercase letters (cap height)
* Example: 710, 680, 750
*/
cap_height: number | null;
/**
* Distance from baseline to the bottom of descenders (negative value)
* Example: -203, -186, -220
*/
descending_leading: number | null;
/**
* Body height of the font
* Often null in Fontshare data
*/
body_height: number | null;
/**
* Maximum character width in the font
* Example: 1739, 1739, 1739
*/
max_char_width: number | null;
/**
* Height of lowercase x-height
* Example: 480, 494, 523
*/
x_height: number | null;
/**
* Maximum Y coordinate (top of ascenders)
* Example: 1010, 990, 1026
*/
y_max: number | null;
/**
* Minimum Y coordinate (bottom of descenders)
* Example: -240, -250, -280
*/
y_min: number | null;
}
/**
* Weight information for a font style
*/
export interface FontshareWeight {
/**
* Display label for the weight
* Examples: "Light", "Regular", "Bold", "Variable", "Variable Italic"
*/
label: string;
/**
* Internal name for the weight
* Examples: "Light", "Regular", "Bold", "Variable", "VariableItalic"
*/
name: string;
/**
* Native/localized name for the weight (if available)
* Often null for Latin-script fonts
*/
native_name: string | null;
/**
* Numeric weight value
* Examples: 300, 400, 700, 0 (for variable fonts), 1, 2
* Note: This matches the `weight` property
*/
number: number;
/**
* Numeric weight value (duplicate of `number`)
* Appears to be redundant with `number` field
*/
weight: number;
}

View File

@@ -0,0 +1,119 @@
/**
* ============================================================================
* GOOGLE FONTS API TYPES
* ============================================================================
*/
/**
* Model of google fonts api response
*/
export interface GoogleFontsApiModel {
/**
* Array of font items returned by the Google Fonts API
* Contains all font families matching the requested query parameters
*/
items: FontItem[];
}
/**
* Individual font from Google Fonts API
*/
export interface FontItem {
/**
* Font family name (e.g., "Roboto", "Open Sans", "Lato")
* This is the name used in CSS font-family declarations
*/
family: string;
/**
* Font category classification (e.g., "sans-serif", "serif", "display", "handwriting", "monospace")
* Useful for grouping and filtering fonts by style
*/
category: string;
/**
* Available font variants for this font family
* Array of strings representing available weights and styles
* Examples: ["regular", "italic", "100", "200", "300", "400", "500", "600", "700", "800", "900", "100italic", "900italic"]
* The keys in the `files` object correspond to these variant values
*/
variants: FontVariant[];
/**
* Supported character subsets for this font
* Examples: ["latin", "latin-ext", "cyrillic", "greek", "arabic", "devanagari", "vietnamese", "hebrew", "thai", etc.]
* Determines which character sets are included in the font files
*/
subsets: string[];
/**
* Font version identifier
* Format: "v" followed by version number (e.g., "v31", "v20", "v1")
* Used to track font updates and cache busting
*/
version: string;
/**
* Last modification date of the font
* Format: ISO 8601 date string (e.g., "2024-01-15", "2023-12-01")
* Indicates when the font was last updated by the font foundry
*/
lastModified: string;
/**
* Mapping of font variants to their downloadable URLs
* Keys correspond to values in the `variants` array
* Examples:
* - "regular" → "https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Me4W..."
* - "700" → "https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1MmWUlf..."
* - "700italic" → "https://fonts.gstatic.com/s/roboto/v30/KFOjCnqEu92Fr1Mu51TzA..."
*/
files: FontFiles;
/**
* URL to the font menu preview image
* Typically a PNG showing the font family name in the font
* Example: "https://fonts.gstatic.com/l/font?kit=KFOmCnqEu92Fr1Me4W...&s=i2"
*/
menu: string;
}
/**
* Type alias for backward compatibility
* Google Fonts API font item
*/
export type GoogleFontItem = FontItem;
/**
* Standard font weights that can appear in Google Fonts API
*/
export type FontWeight = '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
/**
* Italic variant format: e.g., "100italic", "400italic", "700italic"
*/
export type FontWeightItalic = `${FontWeight}italic`;
/**
* All possible font variants in Google Fonts API
* - Numeric weights: "400", "700", etc.
* - Italic variants: "400italic", "700italic", etc.
* - Legacy names: "regular", "italic", "bold", "bolditalic"
*/
export type FontVariant =
| FontWeight
| FontWeightItalic
| 'regular'
| 'italic'
| 'bold'
| 'bolditalic';
/**
* Google Fonts API file mapping
* Dynamic keys that match the variants array
*
* Examples:
* - { "regular": "...", "italic": "...", "700": "...", "700italic": "..." }
* - { "400": "...", "400italic": "...", "900": "..." }
*/
export type FontFiles = Partial<Record<FontVariant, string>>;

View File

@@ -0,0 +1,59 @@
/**
* ============================================================================
* SINGLE EXPORT POINT
* ============================================================================
*
* This is the single export point for all Font types.
* All imports should use: `import { X } from '$entities/Font/model/types'`
*/
// Domain types
export type {
FontCategory,
FontProvider,
FontSubset,
} from './common';
// Google Fonts API types
export type {
FontFiles,
FontItem,
FontVariant,
FontWeight,
FontWeightItalic,
GoogleFontItem,
GoogleFontsApiModel,
} from './google';
// Fontshare API types
export type {
FontshareApiModel,
FontshareAxis,
FontshareDesigner,
FontshareFeature,
FontshareFont,
FontshareLink,
FontsharePublisher,
FontshareStyle,
FontshareStyleProperties,
FontshareTag,
FontshareWeight,
} from './fontshare';
export { FONTSHARE_API_URL } from './fontshare';
// Normalization types
export type {
FontFeatures,
FontMetadata,
FontStyleUrls,
UnifiedFont,
UnifiedFontVariant,
} from './normalize';
// Store types
export type {
FontCollectionFilters,
FontCollectionSort,
FontCollectionState,
FontCollectionStore,
} from './store';

View File

@@ -0,0 +1,89 @@
/**
* ============================================================================
* NORMALIZATION TYPES
* ============================================================================
*/
import type {
FontCategory,
FontProvider,
FontSubset,
} from './common';
/**
* Font variant types (standardized)
*/
export type UnifiedFontVariant = string;
/**
* Font style URLs
*/
export interface FontStyleUrls {
/** Regular weight URL */
regular?: string;
/** Italic URL */
italic?: string;
/** Bold weight URL */
bold?: string;
/** Bold italic URL */
boldItalic?: string;
}
/**
* Font metadata
*/
export interface FontMetadata {
/** Timestamp when font was cached */
cachedAt: number;
/** Font version from provider */
version?: string;
/** Last modified date from provider */
lastModified?: string;
/** Popularity rank (if available from provider) */
popularity?: number;
}
/**
* Font features (variable fonts, axes, tags)
*/
export interface FontFeatures {
/** Whether this is a variable font */
isVariable?: boolean;
/** Variable font axes (for Fontshare) */
axes?: Array<{
name: string;
property: string;
default: number;
min: number;
max: number;
}>;
/** Usage tags (for Fontshare) */
tags?: string[];
}
/**
* Unified font model
*
* Combines Google Fonts and Fontshare data into a common interface
* for consistent font handling across the application.
*/
export interface UnifiedFont {
/** Unique identifier (Google: family name, Fontshare: slug) */
id: string;
/** Font display name */
name: string;
/** Font provider (google | fontshare) */
provider: FontProvider;
/** Font category classification */
category: FontCategory;
/** Supported character subsets */
subsets: FontSubset[];
/** Available font variants (weights, styles) */
variants: UnifiedFontVariant[];
/** URL mapping for font file downloads */
styles: FontStyleUrls;
/** Additional metadata */
metadata: FontMetadata;
/** Advanced font features */
features: FontFeatures;
}

View File

@@ -0,0 +1,86 @@
/**
* ============================================================================
* STORE TYPES
* ============================================================================
*/
import type {
FontCategory,
FontProvider,
FontSubset,
} from './common';
import type { UnifiedFont } from './normalize';
/**
* Font collection state
*/
export interface FontCollectionState {
/** All cached fonts */
fonts: Record<string, UnifiedFont>;
/** Active filters */
filters: FontCollectionFilters;
/** Sort configuration */
sort: FontCollectionSort;
}
/**
* Font collection filters
*/
export interface FontCollectionFilters {
/** Search query */
searchQuery?: string;
/** Filter by provider */
provider?: FontProvider;
/** Filter by category */
category?: FontCategory;
/** Filter by subsets */
subsets?: string[];
}
/**
* Font collection sort configuration
*/
export interface FontCollectionSort {
/** Sort field */
field: 'name' | 'popularity' | 'category';
/** Sort direction */
direction: 'asc' | 'desc';
}
/**
* Font collection store interface
*/
export interface FontCollectionStore {
/** Main state store */
state: import('svelte/store').Writable<FontCollectionState>;
/** All fonts as array */
fonts: import('svelte/store').Readable<UnifiedFont[]>;
/** Filtered fonts as array */
filteredFonts: import('svelte/store').Readable<UnifiedFont[]>;
/** Number of fonts in collection */
count: import('svelte/store').Readable<number>;
/** Loading state */
isLoading: import('svelte/store').Readable<boolean>;
/** Error state */
error: import('svelte/store').Readable<string | undefined>;
/** Add fonts to collection */
addFonts: (fonts: UnifiedFont[]) => void;
/** Add single font to collection */
addFont: (font: UnifiedFont) => void;
/** Remove font from collection */
removeFont: (fontId: string) => void;
/** Clear all fonts */
clear: () => void;
/** Update filters */
setFilters: (filters: Partial<FontCollectionFilters>) => void;
/** Clear filters */
clearFilters: () => void;
/** Update sort configuration */
setSort: (sort: FontCollectionSort) => void;
/** Get font by ID */
getFont: (fontId: string) => UnifiedFont | undefined;
/** Get fonts by provider */
getFontsByProvider: (provider: FontProvider) => UnifiedFont[];
/** Get fonts by category */
getFontsByCategory: (category: FontCategory) => UnifiedFont[];
}

View File

@@ -5,9 +5,9 @@
* and automatic refetching.
*/
import { fetchFontshareFonts } from '$entities/Font/api/fontshare';
import { normalizeFontshareFonts } from '$entities/Font/api/normalize';
import type { UnifiedFont } from '$entities/Font/api/normalize';
import { fetchFontshareFonts } from '$entities/Font/api/fontshare/fontshare';
import { normalizeFontshareFonts } from '$entities/Font/api/normalize/normalize';
import type { UnifiedFont } from '$entities/Font/model/types/normalize';
import type { QueryFunction } from '@tanstack/svelte-query';
import {
createQuery,

View File

@@ -7,13 +7,13 @@
* Uses reactive query args pattern for Svelte 5 compatibility.
*/
import { fetchGoogleFonts } from '$entities/Font/api/google/googleFonts';
import { normalizeGoogleFonts } from '$entities/Font/api/normalize/normalize';
import type {
FontCategory,
FontSubset,
} from '$entities/Font';
import { fetchGoogleFonts } from '$entities/Font/api/googleFonts';
import { normalizeGoogleFonts } from '$entities/Font/api/normalize';
import type { UnifiedFont } from '$entities/Font/api/normalize';
} from '$entities/Font/model/types';
import type { UnifiedFont } from '$entities/Font/model/types/normalize';
import type { QueryFunction } from '@tanstack/svelte-query';
import {
createQuery,

View File

@@ -8,8 +8,8 @@ import type {
FontCategory,
FontProvider,
FontSubset,
} from '$entities/Font';
import type { UnifiedFont } from '$entities/Font/api/normalize';
} from '$entities/Font/model/types';
import type { UnifiedFont } from '$entities/Font/model/types/normalize';
/**
* Combined query parameters for fetching from any provider