mirror of https://github.com/iconify/api.git
Compare commits
55 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
a3d158f99c | |
|
|
407792e2bc | |
|
|
8e35844326 | |
|
|
f79638ef5f | |
|
|
23b5dc510e | |
|
|
238e12a016 | |
|
|
26828c1152 | |
|
|
d24e229645 | |
|
|
62fa26b541 | |
|
|
65af386d3c | |
|
|
ae0e4c929d | |
|
|
116d8e20df | |
|
|
a7e3d99d9e | |
|
|
2521142eca | |
|
|
84590145c9 | |
|
|
88b88251aa | |
|
|
262783f704 | |
|
|
48b0da6d55 | |
|
|
119538fbff | |
|
|
3141131ab7 | |
|
|
ba72402310 | |
|
|
6f33b7c4c5 | |
|
|
44884118f3 | |
|
|
9ec6b365a0 | |
|
|
5e1dbe8d4f | |
|
|
9b7b1a4279 | |
|
|
871028175b | |
|
|
a752e1ee7f | |
|
|
39e5bb4817 | |
|
|
2afff788df | |
|
|
8300891cba | |
|
|
f62b8ba8e6 | |
|
|
65b0eca32e | |
|
|
a02dd19d51 | |
|
|
e44822a40d | |
|
|
c5260541b8 | |
|
|
1d55e2dafd | |
|
|
5d296cd782 | |
|
|
9c6a063676 | |
|
|
c153237943 | |
|
|
283142a8d7 | |
|
|
c1ac0a5609 | |
|
|
49a077bc7b | |
|
|
cb8baf9acf | |
|
|
0b9427baa4 | |
|
|
95423f118b | |
|
|
a391ce96cf | |
|
|
2963c7a666 | |
|
|
ceb3fc4394 | |
|
|
2181b8022c | |
|
|
6471e051e9 | |
|
|
32582d8dae | |
|
|
ceaea7ceca | |
|
|
5994184446 | |
|
|
c53a1e7a82 |
|
|
@ -1 +0,0 @@
|
|||
github: cyberalien
|
||||
|
|
@ -15,10 +15,10 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 'latest'
|
||||
|
||||
- name: 📦 Install dependencies
|
||||
run: npm ci
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
/.idea
|
||||
/.vscode
|
||||
.DS_Store
|
||||
/.env
|
||||
/.editorconfig
|
||||
/.prettierrc
|
||||
*.map
|
||||
/docker.sh
|
||||
/Dockerfile
|
||||
/tsconfig*.*
|
||||
/vitest.config.*
|
||||
/.github
|
||||
/src
|
||||
/node_modules
|
||||
/cache
|
||||
/tmp
|
||||
/icons
|
||||
/tests
|
||||
10
Dockerfile
10
Dockerfile
|
|
@ -1,11 +1,11 @@
|
|||
ARG ARCH=amd64
|
||||
ARG NODE_VERSION=18
|
||||
ARG NODE_VERSION=22
|
||||
ARG OS=bullseye-slim
|
||||
ARG ICONIFY_API_VERSION=3.0.0
|
||||
ARG ICONIFY_API_VERSION=3.2.0
|
||||
ARG SRC_PATH=./
|
||||
|
||||
#### Stage BASE ########################################################################################################
|
||||
FROM ${ARCH}/node:${NODE_VERSION}-${OS} AS base
|
||||
FROM --platform=${ARCH} node:${NODE_VERSION}-${OS} AS base
|
||||
|
||||
# This gives node.js apps access to the OS CAs
|
||||
ENV NODE_EXTRA_CA_CERTS=/etc/ssl/certs/ca-certificates.crt
|
||||
|
|
@ -53,8 +53,8 @@ COPY ${SRC_PATH}icons/ /data/iconify-api/icons/
|
|||
# Build API
|
||||
RUN npm run build
|
||||
|
||||
#### Stage RELEASE #####################################################################################################
|
||||
FROM iconify-api-install AS RELEASE
|
||||
#### Stage release #####################################################################################################
|
||||
FROM iconify-api-install AS release
|
||||
ARG BUILD_DATE
|
||||
ARG BUILD_VERSION
|
||||
ARG BUILD_REF
|
||||
|
|
|
|||
12
README.md
12
README.md
|
|
@ -6,6 +6,12 @@ This repository contains Iconify API script. It is a HTTP server, written in Nod
|
|||
- Generates SVG, which you can link to in HTML or stylesheet.
|
||||
- Provides search engine for hosted icons, which can be used by icon pickers.
|
||||
|
||||
## NPM Package
|
||||
|
||||
This package is also available at NPM, allowing using API code in custom wrappers.
|
||||
|
||||
NPM package contains only compiled files, to build custom Docker image you need to use source files from Git repository, not NPM package.
|
||||
|
||||
## Docker
|
||||
|
||||
To build a Docker image, run `./docker.sh`.
|
||||
|
|
@ -85,7 +91,7 @@ By default, API will use memory management functions. It stores only recently us
|
|||
000
|
||||
If your API gets a lot of traffic (above 1k requests per minute), it is better to not use memory management. With such high number of queries, disc read/write operations might cause degraded performance. API can easily handle 10 times more traffic on a basic VPS if everything is in memory and can be accessed instantly.
|
||||
|
||||
See [memory management in full API docs](https://docs.iconify.design/api/hosting-js/config.html).
|
||||
See [memory management in full API docs](https://iconify.design/docs/api/hosting-js/config.html).
|
||||
|
||||
### Updating icons
|
||||
|
||||
|
|
@ -125,7 +131,7 @@ Previous version of API was also available as PHP script. This has been disconti
|
|||
|
||||
This file is basic.
|
||||
|
||||
Full documentation is available on [Iconify documentation website](https://docs.iconify.design/api/).
|
||||
Full documentation is available on [Iconify documentation website](https://iconify.design/docs/api/).
|
||||
|
||||
## Sponsors
|
||||
|
||||
|
|
@ -143,4 +149,4 @@ Iconify API is licensed under MIT license.
|
|||
|
||||
This licence does not apply to icons hosted on API and files generated by API. You can host icons with any license, without any restrictions. Common decency applies, such as not hosting pirated versions of commercial icon sets (not sure why anyone would use commercial icon sets when so many excellent open source icon sets are available, but anyway...).
|
||||
|
||||
© 2022 Vjacheslav Trushkin / Iconify OÜ
|
||||
© 2022-PRESENT Vjacheslav Trushkin
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 Vjacheslav Trushkin
|
||||
Copyright (c) 2022-PRESENT Vjacheslav Trushkin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
26
package.json
26
package.json
|
|
@ -3,17 +3,17 @@
|
|||
"description": "Iconify API",
|
||||
"author": "Vjacheslav Trushkin",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"version": "3.0.2",
|
||||
"version": "3.2.0",
|
||||
"type": "module",
|
||||
"bugs": "https://github.com/iconify/api/issues",
|
||||
"homepage": "https://github.com/iconify/api",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/iconify/api.git"
|
||||
},
|
||||
"packageManager": "npm@9.6.6",
|
||||
"packageManager": "npm@11.6.4",
|
||||
"engines": {
|
||||
"node": ">=16.15.0"
|
||||
"node": ">=22.20.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc -b",
|
||||
|
|
@ -26,17 +26,17 @@
|
|||
"docker:publish": "docker push iconify/api"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/formbody": "^7.4.0",
|
||||
"@iconify/tools": "^2.2.6",
|
||||
"@fastify/formbody": "^8.0.2",
|
||||
"@iconify/tools": "^5.0.0",
|
||||
"@iconify/types": "^2.0.0",
|
||||
"@iconify/utils": "^2.1.5",
|
||||
"dotenv": "^16.0.3",
|
||||
"fastify": "^4.17.0"
|
||||
"@iconify/utils": "^3.1.0",
|
||||
"dotenv": "^17.2.3",
|
||||
"fastify": "^5.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.1",
|
||||
"@types/node": "^18.16.12",
|
||||
"typescript": "^5.0.4",
|
||||
"vitest": "^0.31.1"
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^24.10.1",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "^4.0.14"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["config:recommended"],
|
||||
"rangeStrategy": "bump",
|
||||
"packageRules": [
|
||||
{
|
||||
"matchDepTypes": ["peerDependencies"],
|
||||
"enabled": false
|
||||
},
|
||||
{
|
||||
"matchPackageNames": ["fastify"],
|
||||
"allowedVersions": "<4.0.0"
|
||||
},
|
||||
{
|
||||
"matchPackageNames": ["@fastify/formbody"],
|
||||
"allowedVersions": "<8.0.0"
|
||||
},
|
||||
{
|
||||
"matchPackageNames": ["@iconify/utils"],
|
||||
"allowedVersions": "<3.0.0"
|
||||
},
|
||||
{
|
||||
"matchPackageNames": ["@iconify/tools"],
|
||||
"allowedVersions": "<5.0.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
import type { AppConfig } from '../types/config/app';
|
||||
import type { SplitIconSetConfig } from '../types/config/split';
|
||||
import type { MemoryStorageConfig } from '../types/storage';
|
||||
import type { AppConfig } from '../types/config/app.js';
|
||||
import type { SplitIconSetConfig } from '../types/config/split.js';
|
||||
import type { MemoryStorageConfig } from '../types/storage.js';
|
||||
|
||||
/**
|
||||
* Main configuration
|
||||
*/
|
||||
export const appConfig: AppConfig = {
|
||||
// Index page
|
||||
redirectIndex: 'https://docs.iconify.design/api/',
|
||||
redirectIndex: 'https://iconify.design/docs/api/',
|
||||
|
||||
// Region to add to `/version` response
|
||||
// Used to tell which server is responding when running multiple servers
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { DirectoryDownloader } from '../downloaders/directory';
|
||||
import { createJSONDirectoryImporter } from '../importers/full/directory-json';
|
||||
import { directoryExists } from '../misc/files';
|
||||
import type { Importer } from '../types/importers';
|
||||
import type { ImportedData } from '../types/importers/common';
|
||||
import { fullPackageImporter } from './importers/full-package';
|
||||
import { splitPackagesImporter } from './importers/split-packages';
|
||||
import { DirectoryDownloader } from '../downloaders/directory.js';
|
||||
import { createJSONDirectoryImporter } from '../importers/full/directory-json.js';
|
||||
import { directoryExists } from '../misc/files.js';
|
||||
import type { Importer } from '../types/importers.js';
|
||||
import type { ImportedData } from '../types/importers/common.js';
|
||||
import { fullPackageImporter } from './importers/full-package.js';
|
||||
import { splitPackagesImporter } from './importers/split-packages.js';
|
||||
|
||||
/**
|
||||
* Sources
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
import { createRequire } from 'node:module';
|
||||
import { dirname } from 'node:path';
|
||||
import { Importer } from '../../types/importers.js';
|
||||
import { createIconSetsPackageImporter } from '../../importers/full/json.js';
|
||||
import { ImportedData } from '../../types/importers/common.js';
|
||||
import { DirectoryDownloader } from '../../downloaders/directory.js';
|
||||
import type { RemoteDownloaderOptions } from '../../types/downloaders/remote.js';
|
||||
import { RemoteDownloader } from '../../downloaders/remote.js';
|
||||
|
||||
/**
|
||||
* Create importer for package
|
||||
*/
|
||||
export function createPackageIconSetImporter(
|
||||
packageName = '@iconify/json',
|
||||
useRemoteFallback = false,
|
||||
autoUpdateRemotePackage = false
|
||||
): Importer {
|
||||
// Try to locate package
|
||||
let dir: string | undefined;
|
||||
try {
|
||||
const req = createRequire(import.meta.url);
|
||||
const filename = req.resolve(`${packageName}/package.json`);
|
||||
dir = filename ? dirname(filename) : undefined;
|
||||
} catch (err) {
|
||||
//
|
||||
}
|
||||
if (dir) {
|
||||
return createIconSetsPackageImporter(new DirectoryDownloader<ImportedData>(dir), {});
|
||||
}
|
||||
if (!useRemoteFallback) {
|
||||
throw new Error(`Cannot find package "${packageName}"`);
|
||||
}
|
||||
|
||||
// Try to download it, update if
|
||||
const npm: RemoteDownloaderOptions = {
|
||||
downloadType: 'npm',
|
||||
package: packageName,
|
||||
};
|
||||
return createIconSetsPackageImporter(new RemoteDownloader<ImportedData>(npm, autoUpdateRemotePackage));
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { RemoteDownloader } from '../../downloaders/remote';
|
||||
import { createIconSetsPackageImporter } from '../../importers/full/json';
|
||||
import type { RemoteDownloaderOptions } from '../../types/downloaders/remote';
|
||||
import type { ImportedData } from '../../types/importers/common';
|
||||
import { RemoteDownloader } from '../../downloaders/remote.js';
|
||||
import { createIconSetsPackageImporter } from '../../importers/full/json.js';
|
||||
import type { RemoteDownloaderOptions } from '../../types/downloaders/remote.js';
|
||||
import type { ImportedData } from '../../types/importers/common.js';
|
||||
|
||||
/**
|
||||
* Importer for all icon sets from `@iconify/json` package
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { RemoteDownloader } from '../../downloaders/remote';
|
||||
import { createJSONCollectionsListImporter } from '../../importers/collections/collections';
|
||||
import { createJSONPackageIconSetImporter } from '../../importers/icon-set/json-package';
|
||||
import type { IconSetImportedData, ImportedData } from '../../types/importers/common';
|
||||
import { RemoteDownloader } from '../../downloaders/remote.js';
|
||||
import { createJSONCollectionsListImporter } from '../../importers/collections/collections.js';
|
||||
import { createJSONPackageIconSetImporter } from '../../importers/icon-set/json-package.js';
|
||||
import type { IconSetImportedData, ImportedData } from '../../types/importers/common.js';
|
||||
|
||||
// Automatically update on startup: boolean
|
||||
const autoUpdate = true;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { IconifyJSON } from '@iconify/types';
|
||||
import type { IconSetIconsListIcons, IconSetAPIv2IconsList } from '../../../types/icon-set/extra';
|
||||
import type { IconSetIconsListIcons, IconSetAPIv2IconsList } from '../../../types/icon-set/extra.js';
|
||||
|
||||
/**
|
||||
* Prepare data for icons list API v2 response
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import type { IconifyAliases, IconifyJSON, IconifyOptional } from '@iconify/types';
|
||||
import { defaultIconProps } from '@iconify/utils/lib/icon/defaults';
|
||||
import { appConfig } from '../../../config/app';
|
||||
import { appConfig } from '../../../config/app.js';
|
||||
import type {
|
||||
IconSetIconNames,
|
||||
IconSetIconsListIcons,
|
||||
IconSetIconsListTag,
|
||||
IconStyle,
|
||||
} from '../../../types/icon-set/extra';
|
||||
import { getIconStyle } from './style';
|
||||
} from '../../../types/icon-set/extra.js';
|
||||
import { getIconStyle } from './style.js';
|
||||
|
||||
const customisableProps = Object.keys(defaultIconProps) as (keyof IconifyOptional)[];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { IconStyle } from '../../../types/icon-set/extra';
|
||||
import type { IconStyle } from '../../../types/icon-set/extra.js';
|
||||
|
||||
function getValues(body: string, prop: string): string[] {
|
||||
const chunks = body.split(prop + '="');
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { IconifyJSON } from '@iconify/types';
|
||||
import type { IconSetIconsListIcons } from '../../../types/icon-set/extra';
|
||||
import type { IconSetIconsListIcons } from '../../../types/icon-set/extra.js';
|
||||
|
||||
/**
|
||||
* Removes bad items
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { IconifyIcons, IconifyJSON } from '@iconify/types';
|
||||
import { defaultIconDimensions } from '@iconify/utils/lib/icon/defaults';
|
||||
import type { SplitIconSetConfig } from '../../../types/config/split';
|
||||
import type { SplitIconifyJSONMainData } from '../../../types/icon-set/split';
|
||||
import type { SplitIconSetConfig } from '../../../types/config/split.js';
|
||||
import type { SplitIconifyJSONMainData } from '../../../types/icon-set/split.js';
|
||||
|
||||
const iconDimensionProps = Object.keys(defaultIconDimensions) as (keyof typeof defaultIconDimensions)[];
|
||||
|
||||
|
|
@ -21,7 +21,14 @@ export function splitIconSetMainData(iconSet: IconifyJSON): SplitIconifyJSONMain
|
|||
for (let i = 0; i < iconSetMainDataProps.length; i++) {
|
||||
const prop = iconSetMainDataProps[i];
|
||||
if (iconSet[prop]) {
|
||||
result[prop as 'prefix'] = iconSet[prop as 'prefix'];
|
||||
const value = iconSet[prop as 'prefix'];
|
||||
if (typeof value === 'object') {
|
||||
// Make sure object has null as constructor
|
||||
result[prop as 'prefix'] = Object.create(null);
|
||||
Object.assign(result[prop as 'prefix'], iconSet[prop as 'prefix']);
|
||||
} else {
|
||||
result[prop as 'prefix'] = iconSet[prop as 'prefix'];
|
||||
}
|
||||
} else if (prop === 'aliases') {
|
||||
result[prop] = Object.create(null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,16 @@
|
|||
import type { IconifyIcons, IconifyJSON } from '@iconify/types';
|
||||
import { appConfig, splitIconSetConfig, storageConfig } from '../../../config/app';
|
||||
import type { SplitIconSetConfig } from '../../../types/config/split';
|
||||
import type { StorageIconSetThemes, StoredIconSet, StoredIconSetDone } from '../../../types/icon-set/storage';
|
||||
import type { SplitRecord } from '../../../types/split';
|
||||
import type { MemoryStorage, MemoryStorageItem } from '../../../types/storage';
|
||||
import { createSplitRecordsTree, splitRecords } from '../../storage/split';
|
||||
import { createStorage, createStoredItem } from '../../storage/create';
|
||||
import { getIconSetSplitChunksCount, splitIconSetMainData } from './split';
|
||||
import { removeBadIconSetItems } from '../lists/validate';
|
||||
import { prepareAPIv2IconsList } from '../lists/icons-v2';
|
||||
import { generateIconSetIconsTree } from '../lists/icons';
|
||||
import { themeKeys } from './themes';
|
||||
import { findIconSetThemes } from './themes';
|
||||
import { appConfig, splitIconSetConfig, storageConfig } from '../../../config/app.js';
|
||||
import type { SplitIconSetConfig } from '../../../types/config/split.js';
|
||||
import type { StorageIconSetThemes, StoredIconSet, StoredIconSetDone } from '../../../types/icon-set/storage.js';
|
||||
import type { SplitRecord } from '../../../types/split.js';
|
||||
import type { MemoryStorage, MemoryStorageItem } from '../../../types/storage.js';
|
||||
import { createSplitRecordsTree, splitRecords } from '../../storage/split.js';
|
||||
import { createStorage, createStoredItem } from '../../storage/create.js';
|
||||
import { getIconSetSplitChunksCount, splitIconSetMainData } from './split.js';
|
||||
import { removeBadIconSetItems } from '../lists/validate.js';
|
||||
import { prepareAPIv2IconsList } from '../lists/icons-v2.js';
|
||||
import { generateIconSetIconsTree } from '../lists/icons.js';
|
||||
import { themeKeys, findIconSetThemes } from './themes.js';
|
||||
|
||||
/**
|
||||
* Storage
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { IconifyJSON } from '@iconify/types';
|
||||
import { StorageIconSetThemes } from '../../../types/icon-set/storage';
|
||||
import { StorageIconSetThemes } from '../../../types/icon-set/storage.js';
|
||||
|
||||
/**
|
||||
* Themes to copy
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import type { ExtendedIconifyAlias, ExtendedIconifyIcon, IconifyIcons } from '@iconify/types';
|
||||
import { mergeIconData } from '@iconify/utils/lib/icon/merge';
|
||||
import type { SplitIconifyJSONMainData } from '../../../types/icon-set/split';
|
||||
import type { StoredIconSet } from '../../../types/icon-set/storage';
|
||||
import { searchSplitRecordsTree } from '../../storage/split';
|
||||
import { getStoredItem } from '../../storage/get';
|
||||
import type { SplitIconifyJSONMainData } from '../../../types/icon-set/split.js';
|
||||
import type { StoredIconSet } from '../../../types/icon-set/storage.js';
|
||||
import { searchSplitRecordsTree } from '../../storage/split.js';
|
||||
import { getStoredItem } from '../../storage/get.js';
|
||||
|
||||
interface PrepareResult {
|
||||
// Merged properties
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { IconifyJSON, IconifyAliases, IconifyIcons } from '@iconify/types';
|
||||
import type { StoredIconSet } from '../../../types/icon-set/storage';
|
||||
import { searchSplitRecordsTreeForSet } from '../../storage/split';
|
||||
import { getStoredItem } from '../../storage/get';
|
||||
import type { StoredIconSet } from '../../../types/icon-set/storage.js';
|
||||
import { searchSplitRecordsTreeForSet } from '../../storage/split.js';
|
||||
import { getStoredItem } from '../../storage/get.js';
|
||||
|
||||
/**
|
||||
* Get list of icons that must be retrieved
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { StoredIconSet } from '../types/icon-set/storage';
|
||||
import type { IconSetEntry, Importer } from '../types/importers';
|
||||
import { updateSearchIndex } from './search';
|
||||
import type { StoredIconSet } from '../types/icon-set/storage.js';
|
||||
import type { IconSetEntry, Importer } from '../types/importers.js';
|
||||
import { updateSearchIndex } from './search.js';
|
||||
|
||||
/**
|
||||
* All importers
|
||||
|
|
@ -125,8 +125,9 @@ export function updateIconSets(): number {
|
|||
/**
|
||||
* Trigger update
|
||||
*/
|
||||
export function triggerIconSetsUpdate() {
|
||||
export function triggerIconSetsUpdate(index?: number | null, done?: (success?: boolean) => void) {
|
||||
if (!importers) {
|
||||
done?.();
|
||||
return;
|
||||
}
|
||||
console.log('Checking for updates...');
|
||||
|
|
@ -140,6 +141,9 @@ export function triggerIconSetsUpdate() {
|
|||
// Check for updates
|
||||
let updated = false;
|
||||
for (let i = 0; i < importers?.length; i++) {
|
||||
if (typeof index === 'number' && i !== index) {
|
||||
continue;
|
||||
}
|
||||
updated = (await importers[i].checkForUpdate()) || updated;
|
||||
}
|
||||
return updated;
|
||||
|
|
@ -147,6 +151,10 @@ export function triggerIconSetsUpdate() {
|
|||
.then((updated) => {
|
||||
console.log(updated ? 'Update complete' : 'Nothing to update');
|
||||
updateIconSets();
|
||||
done?.(true);
|
||||
})
|
||||
.catch(console.error);
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
done?.(false);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,13 @@ export function loaded() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get state
|
||||
*/
|
||||
export function isLoading() {
|
||||
return loading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run when app is ready
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { appConfig } from '../config/app';
|
||||
import type { IconSetEntry } from '../types/importers';
|
||||
import type { SearchIndexData } from '../types/search';
|
||||
import { appConfig } from '../config/app.js';
|
||||
import type { IconSetEntry } from '../types/importers.js';
|
||||
import type { SearchIndexData } from '../types/search.js';
|
||||
|
||||
interface SearchIndex {
|
||||
data?: SearchIndexData;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { appConfig } from '../../config/app';
|
||||
import type { IconSetIconNames } from '../../types/icon-set/extra';
|
||||
import type { IconSetEntry } from '../../types/importers';
|
||||
import type { SearchIndexData, SearchKeywordsEntry, SearchParams, SearchResultsData } from '../../types/search';
|
||||
import { getPartialKeywords } from './partial';
|
||||
import { filterSearchPrefixes, filterSearchPrefixesList } from './prefixes';
|
||||
import { splitKeyword } from './split';
|
||||
import { appConfig } from '../../config/app.js';
|
||||
import type { IconSetIconNames } from '../../types/icon-set/extra.js';
|
||||
import type { IconSetEntry } from '../../types/importers.js';
|
||||
import type { SearchIndexData, SearchKeywordsEntry, SearchParams, SearchResultsData } from '../../types/search.js';
|
||||
import { getPartialKeywords } from './partial.js';
|
||||
import { filterSearchPrefixes, filterSearchPrefixesList } from './prefixes.js';
|
||||
import { splitKeyword } from './split.js';
|
||||
|
||||
/**
|
||||
* Run search
|
||||
|
|
@ -69,6 +69,7 @@ export function search(
|
|||
return newItem;
|
||||
};
|
||||
const limit = params.limit;
|
||||
const softLimit = params.softLimit;
|
||||
|
||||
interface ExtendedSearchKeywordsEntry extends SearchKeywordsEntry {
|
||||
// Add prefixes cache to avoid re-calculating it for every partial keyword
|
||||
|
|
@ -256,9 +257,9 @@ export function search(
|
|||
// Extract results
|
||||
const results: string[] = [];
|
||||
const prefixes: Set<string> = new Set();
|
||||
for (let i = 0; i < allMatches.length && results.length < limit; i++) {
|
||||
for (let i = 0; i < allMatches.length && (softLimit || results.length < limit); i++) {
|
||||
const { names } = allMatches[i];
|
||||
for (let j = 0; j < names.length && results.length < limit; j++) {
|
||||
for (let j = 0; j < names.length && (softLimit || results.length < limit); j++) {
|
||||
const name = names[j];
|
||||
results.push(name);
|
||||
prefixes.add(name.split(':').shift() as string);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { PartialKeywords, SearchIndexData } from '../../types/search';
|
||||
import { searchIndex } from '../search';
|
||||
import type { PartialKeywords, SearchIndexData } from '../../types/search.js';
|
||||
import { searchIndex } from '../search.js';
|
||||
|
||||
export const minPartialKeywordLength = 2;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { appConfig } from '../../config/app';
|
||||
import type { IconSetEntry } from '../../types/importers';
|
||||
import type { SearchIndexData, SearchParams } from '../../types/search';
|
||||
import { appConfig } from '../../config/app.js';
|
||||
import type { IconSetEntry } from '../../types/importers.js';
|
||||
import type { SearchIndexData, SearchParams } from '../../types/search.js';
|
||||
|
||||
/**
|
||||
* Filter prefixes by keyword
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { matchIconName } from '@iconify/utils/lib/icon/name';
|
||||
import { paramToBoolean } from '../../misc/bool';
|
||||
import type { IconStyle } from '../../types/icon-set/extra';
|
||||
import type { SearchKeywords, SearchKeywordsEntry } from '../../types/search';
|
||||
import { minPartialKeywordLength } from './partial';
|
||||
import { paramToBoolean } from '../../misc/bool.js';
|
||||
import type { IconStyle } from '../../types/icon-set/extra.js';
|
||||
import type { SearchKeywords, SearchKeywordsEntry } from '../../types/search.js';
|
||||
import { minPartialKeywordLength } from './partial.js';
|
||||
|
||||
interface SplitOptions {
|
||||
// Can include prefix
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { MemoryStorageItem, MemoryStorageCallback } from '../../types/storage';
|
||||
import type { MemoryStorageItem, MemoryStorageCallback } from '../../types/storage.js';
|
||||
|
||||
/**
|
||||
* Run all callbacks from storage
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { MemoryStorage, MemoryStorageItem } from '../../types/storage';
|
||||
import { runStorageCallbacks } from './callbacks';
|
||||
import type { MemoryStorage, MemoryStorageItem } from '../../types/storage.js';
|
||||
import { runStorageCallbacks } from './callbacks.js';
|
||||
|
||||
/**
|
||||
* Stop timer
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { appConfig } from '../../config/app';
|
||||
import type { MemoryStorage, MemoryStorageConfig, MemoryStorageItem } from '../../types/storage';
|
||||
import { cleanupStoredItem } from './cleanup';
|
||||
import { writeStoredItem } from './write';
|
||||
import { appConfig } from '../../config/app.js';
|
||||
import type { MemoryStorage, MemoryStorageConfig, MemoryStorageItem } from '../../types/storage.js';
|
||||
import { cleanupStoredItem } from './cleanup.js';
|
||||
import { writeStoredItem } from './write.js';
|
||||
|
||||
/**
|
||||
* Create storage
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { MemoryStorageItem, MemoryStorageCallback, MemoryStorage } from '../../types/storage';
|
||||
import { loadStoredItem } from './load';
|
||||
import type { MemoryStorageItem, MemoryStorageCallback, MemoryStorage } from '../../types/storage.js';
|
||||
import { loadStoredItem } from './load.js';
|
||||
|
||||
/**
|
||||
* Get storage data when ready
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { readFile, readFileSync } from 'node:fs';
|
||||
import type { MemoryStorage, MemoryStorageItem } from '../../types/storage';
|
||||
import { runStorageCallbacks } from './callbacks';
|
||||
import { addStorageToCleanup } from './cleanup';
|
||||
import type { MemoryStorage, MemoryStorageItem } from '../../types/storage.js';
|
||||
import { runStorageCallbacks } from './callbacks.js';
|
||||
import { addStorageToCleanup } from './cleanup.js';
|
||||
|
||||
/**
|
||||
* Load data
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { SplitDataTree, SplitRecord, SplitRecordCallback } from '../../types/split';
|
||||
import type { SplitDataTree, SplitRecord, SplitRecordCallback } from '../../types/split.js';
|
||||
|
||||
/**
|
||||
* Split records into `count` chunks
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { rm } from 'node:fs/promises';
|
||||
import { appConfig } from '../../config/app';
|
||||
import type { MemoryStorage } from '../../types/storage';
|
||||
import { appConfig } from '../../config/app.js';
|
||||
import type { MemoryStorage } from '../../types/storage.js';
|
||||
|
||||
/**
|
||||
* Remove old cache
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { writeFile, mkdir } from 'node:fs';
|
||||
import { dirname } from 'node:path';
|
||||
import type { MemoryStorage, MemoryStorageItem } from '../../types/storage';
|
||||
import { addStorageToCleanup } from './cleanup';
|
||||
import type { MemoryStorage, MemoryStorageItem } from '../../types/storage.js';
|
||||
import { addStorageToCleanup } from './cleanup.js';
|
||||
|
||||
const createdDirs: Set<string> = new Set();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { DownloaderStatus, DownloaderType } from '../types/downloaders/base';
|
||||
import type { DownloaderStatus, DownloaderType } from '../types/downloaders/base.js';
|
||||
|
||||
/**
|
||||
* loadDataFromDirectory()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { BaseDownloader } from './base';
|
||||
import { BaseDownloader } from './base.js';
|
||||
|
||||
/**
|
||||
* Custom downloader
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { directoryExists, hashFiles, listFilesInDirectory } from '../misc/files';
|
||||
import { BaseDownloader } from './base';
|
||||
import { directoryExists, hashFiles, listFilesInDirectory } from '../misc/files.js';
|
||||
import { BaseDownloader } from './base.js';
|
||||
|
||||
/**
|
||||
* Directory downloader
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { directoryExists } from '../misc/files';
|
||||
import type { RemoteDownloaderOptions, RemoteDownloaderVersion } from '../types/downloaders/remote';
|
||||
import { BaseDownloader } from './base';
|
||||
import { downloadRemoteArchive } from './remote/download';
|
||||
import { getRemoteDownloaderCacheKey } from './remote/key';
|
||||
import { getDownloaderVersion, saveDownloaderVersion } from './remote/versions';
|
||||
import { directoryExists } from '../misc/files.js';
|
||||
import type { RemoteDownloaderOptions, RemoteDownloaderVersion } from '../types/downloaders/remote.js';
|
||||
import { BaseDownloader } from './base.js';
|
||||
import { downloadRemoteArchive } from './remote/download.js';
|
||||
import { getRemoteDownloaderCacheKey } from './remote/key.js';
|
||||
import { getDownloaderVersion, saveDownloaderVersion } from './remote/versions.js';
|
||||
|
||||
/**
|
||||
* Remote downloader
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { execAsync } from '@iconify/tools/lib/misc/exec';
|
|||
import { getGitHubRepoHash } from '@iconify/tools/lib/download/github/hash';
|
||||
import { getGitLabRepoHash } from '@iconify/tools/lib/download/gitlab/hash';
|
||||
import { getNPMVersion, getPackageVersion } from '@iconify/tools/lib/download/npm/version';
|
||||
import { directoryExists } from '../../misc/files';
|
||||
import { directoryExists } from '../../misc/files.js';
|
||||
import type {
|
||||
GitDownloaderOptions,
|
||||
GitDownloaderVersion,
|
||||
|
|
@ -12,7 +12,7 @@ import type {
|
|||
GitLabDownloaderVersion,
|
||||
NPMDownloaderOptions,
|
||||
NPMDownloaderVersion,
|
||||
} from '../../types/downloaders/remote';
|
||||
} from '../../types/downloaders/remote.js';
|
||||
|
||||
/**
|
||||
* Check git repo for update
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@ import { downloadGitRepo } from '@iconify/tools/lib/download/git';
|
|||
import { downloadGitHubRepo } from '@iconify/tools/lib/download/github';
|
||||
import { downloadGitLabRepo } from '@iconify/tools/lib/download/gitlab';
|
||||
import { downloadNPMPackage } from '@iconify/tools/lib/download/npm';
|
||||
import { appConfig } from '../../config/app';
|
||||
import type { RemoteDownloaderOptions, RemoteDownloaderVersion } from '../../types/downloaders/remote';
|
||||
import { appConfig } from '../../config/app.js';
|
||||
import type { RemoteDownloaderOptions, RemoteDownloaderVersion } from '../../types/downloaders/remote.js';
|
||||
import {
|
||||
isGitHubUpdateAvailable,
|
||||
isGitLabUpdateAvailable,
|
||||
isGitUpdateAvailable,
|
||||
isNPMUpdateAvailable,
|
||||
} from './check-update';
|
||||
import { getDownloadDirectory } from './target';
|
||||
} from './check-update.js';
|
||||
import { getDownloadDirectory } from './target.js';
|
||||
|
||||
/**
|
||||
* Download files from remote archive
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { hashString } from '../../misc/hash';
|
||||
import type { RemoteDownloaderOptions } from '../../types/downloaders/remote';
|
||||
import { hashString } from '../../misc/hash.js';
|
||||
import type { RemoteDownloaderOptions } from '../../types/downloaders/remote.js';
|
||||
|
||||
/**
|
||||
* Get cache key
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { appConfig } from '../../config/app';
|
||||
import type { RemoteDownloaderOptions } from '../../types/downloaders/remote';
|
||||
import { getRemoteDownloaderCacheKey } from './key';
|
||||
import { appConfig } from '../../config/app.js';
|
||||
import type { RemoteDownloaderOptions } from '../../types/downloaders/remote.js';
|
||||
import { getRemoteDownloaderCacheKey } from './key.js';
|
||||
|
||||
/**
|
||||
* Get directory
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
||||
import { dirname } from 'node:path';
|
||||
import { appConfig } from '../../config/app';
|
||||
import { appConfig } from '../../config/app.js';
|
||||
import type {
|
||||
RemoteDownloaderType,
|
||||
RemoteDownloaderVersion,
|
||||
RemoteDownloaderVersionMixin,
|
||||
} from '../../types/downloaders/remote';
|
||||
} from '../../types/downloaders/remote.js';
|
||||
|
||||
// Storage
|
||||
type StoredVersions = Record<string, RemoteDownloaderVersion>;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
export function errorText(code: number) {
|
||||
switch (code) {
|
||||
case 404:
|
||||
return 'Not found';
|
||||
|
||||
case 400:
|
||||
return 'Bad request';
|
||||
}
|
||||
return 'Internal server error';
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* Basic cleanup for parameters
|
||||
*/
|
||||
export function cleanupQueryValue(value: string | undefined) {
|
||||
return value ? value.replace(/['"<>&]/g, '') : undefined;
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { checkJSONPQuery, sendJSONResponse } from './json.js';
|
||||
import { createIconsDataResponse } from '../responses/icons.js';
|
||||
|
||||
type CallbackResult = object | number;
|
||||
|
||||
/**
|
||||
* Handle icons data API response
|
||||
*/
|
||||
export function handleIconsDataResponse(
|
||||
prefix: string,
|
||||
wrapJS: boolean,
|
||||
query: FastifyRequest['query'],
|
||||
res: FastifyReply
|
||||
) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
|
||||
// Check for JSONP
|
||||
const wrap = checkJSONPQuery(q, wrapJS, 'SimpleSVG._loaderCallback');
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
// Function to send response
|
||||
const respond = (result: CallbackResult) => {
|
||||
if (typeof result === 'number') {
|
||||
res.send(result);
|
||||
} else {
|
||||
sendJSONResponse(result, q, wrap, res);
|
||||
}
|
||||
};
|
||||
|
||||
// Get result
|
||||
const result = createIconsDataResponse(prefix, q);
|
||||
if (result instanceof Promise) {
|
||||
result.then(respond).catch((err) => {
|
||||
console.error(err);
|
||||
respond(500);
|
||||
});
|
||||
} else {
|
||||
respond(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { checkJSONPQuery, sendJSONResponse } from './json.js';
|
||||
import { errorText } from './errors.js';
|
||||
|
||||
type CallbackResult = object | number;
|
||||
|
||||
/**
|
||||
* Handle JSON API response generated by a callback
|
||||
*/
|
||||
export function handleJSONResponse(
|
||||
req: FastifyRequest,
|
||||
res: FastifyReply,
|
||||
callback: (query: Record<string, string>) => CallbackResult | Promise<CallbackResult>
|
||||
) {
|
||||
const q = (req.query || {}) as Record<string, string>;
|
||||
|
||||
// Check for JSONP
|
||||
const wrap = checkJSONPQuery(q);
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.code(400).send(errorText(400));
|
||||
return;
|
||||
}
|
||||
|
||||
// Function to send response
|
||||
const respond = (result: CallbackResult) => {
|
||||
if (typeof result === 'number') {
|
||||
res.code(result).send(errorText(result));
|
||||
} else {
|
||||
sendJSONResponse(result, q, wrap, res);
|
||||
}
|
||||
};
|
||||
|
||||
// Get result
|
||||
const result = callback(q);
|
||||
if (result instanceof Promise) {
|
||||
result.then(respond).catch((err) => {
|
||||
console.error(err);
|
||||
respond(500);
|
||||
});
|
||||
} else {
|
||||
respond(result);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +1,21 @@
|
|||
import fastify from 'fastify';
|
||||
import fastifyFormBody from '@fastify/formbody';
|
||||
import { appConfig, httpHeaders } from '../config/app';
|
||||
import { runWhenLoaded } from '../data/loading';
|
||||
import { iconNameRoutePartialRegEx, iconNameRouteRegEx, splitIconName } from '../misc/name';
|
||||
import { generateAPIv1IconsListResponse } from './responses/collection-v1';
|
||||
import { generateAPIv2CollectionResponse } from './responses/collection-v2';
|
||||
import { generateCollectionsListResponse } from './responses/collections';
|
||||
import { generateIconsDataResponse } from './responses/icons';
|
||||
import { generateKeywordsResponse } from './responses/keywords';
|
||||
import { generateLastModifiedResponse } from './responses/modified';
|
||||
import { generateAPIv2SearchResponse } from './responses/search';
|
||||
import { generateSVGResponse } from './responses/svg';
|
||||
import { generateUpdateResponse } from './responses/update';
|
||||
import { initVersionResponse, versionResponse } from './responses/version';
|
||||
import { generateIconsStyleResponse } from './responses/css';
|
||||
import { appConfig, httpHeaders } from '../config/app.js';
|
||||
import { runWhenLoaded } from '../data/loading.js';
|
||||
import { iconNameRoutePartialRegEx, iconNameRouteRegEx, splitIconName } from '../misc/name.js';
|
||||
import { createAPIv1IconsListResponse } from './responses/collection-v1.js';
|
||||
import { createAPIv2CollectionResponse } from './responses/collection-v2.js';
|
||||
import { createCollectionsListResponse } from './responses/collections.js';
|
||||
import { handleIconsDataResponse } from './helpers/send-icons.js';
|
||||
import { createKeywordsResponse } from './responses/keywords.js';
|
||||
import { createLastModifiedResponse } from './responses/modified.js';
|
||||
import { createAPIv2SearchResponse } from './responses/search.js';
|
||||
import { generateSVGResponse } from './responses/svg.js';
|
||||
import { generateUpdateResponse } from './responses/update.js';
|
||||
import { initVersionResponse, versionResponse } from './responses/version.js';
|
||||
import { generateIconsStyleResponse } from './responses/css.js';
|
||||
import { handleJSONResponse } from './helpers/send.js';
|
||||
import { errorText } from './helpers/errors.js';
|
||||
|
||||
/**
|
||||
* Start HTTP server
|
||||
|
|
@ -21,7 +23,9 @@ import { generateIconsStyleResponse } from './responses/css';
|
|||
export async function startHTTPServer() {
|
||||
// Create HTP server
|
||||
const server = fastify({
|
||||
caseSensitive: true,
|
||||
routerOptions: {
|
||||
caseSensitive: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Support `application/x-www-form-urlencoded`
|
||||
|
|
@ -81,19 +85,19 @@ export async function startHTTPServer() {
|
|||
generateSVGResponse(name.prefix, name.name, req.query, res);
|
||||
});
|
||||
} else {
|
||||
res.send(404);
|
||||
res.code(404).send(errorText(404));
|
||||
}
|
||||
});
|
||||
|
||||
// Icons data: /prefix/icons.json, /prefix.json
|
||||
server.get('/:prefix(' + iconNameRoutePartialRegEx + ')/icons.json', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateIconsDataResponse((req.params as PrefixParams).prefix, false, req.query, res);
|
||||
handleIconsDataResponse((req.params as PrefixParams).prefix, false, req.query, res);
|
||||
});
|
||||
});
|
||||
server.get('/:prefix(' + iconNameRoutePartialRegEx + ').json', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateIconsDataResponse((req.params as PrefixParams).prefix, false, req.query, res);
|
||||
handleIconsDataResponse((req.params as PrefixParams).prefix, false, req.query, res);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -107,19 +111,19 @@ export async function startHTTPServer() {
|
|||
// Icons data: /prefix/icons.js, /prefix.js
|
||||
server.get('/:prefix(' + iconNameRoutePartialRegEx + ')/icons.js', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateIconsDataResponse((req.params as PrefixParams).prefix, true, req.query, res);
|
||||
handleIconsDataResponse((req.params as PrefixParams).prefix, true, req.query, res);
|
||||
});
|
||||
});
|
||||
server.get('/:prefix(' + iconNameRoutePartialRegEx + ').js', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateIconsDataResponse((req.params as PrefixParams).prefix, true, req.query, res);
|
||||
handleIconsDataResponse((req.params as PrefixParams).prefix, true, req.query, res);
|
||||
});
|
||||
});
|
||||
|
||||
// Last modification time
|
||||
server.get('/last-modified', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateLastModifiedResponse(req.query, res);
|
||||
handleJSONResponse(req, res, createLastModifiedResponse);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -127,26 +131,26 @@ export async function startHTTPServer() {
|
|||
// Icon sets list
|
||||
server.get('/collections', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateCollectionsListResponse(req.query, res);
|
||||
handleJSONResponse(req, res, createCollectionsListResponse);
|
||||
});
|
||||
});
|
||||
|
||||
// Icons list, API v2
|
||||
server.get('/collection', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateAPIv2CollectionResponse(req.query, res);
|
||||
handleJSONResponse(req, res, createAPIv2CollectionResponse);
|
||||
});
|
||||
});
|
||||
|
||||
// Icons list, API v1
|
||||
server.get('/list-icons', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateAPIv1IconsListResponse(req.query, res, false);
|
||||
handleJSONResponse(req, res, (q) => createAPIv1IconsListResponse(q, false));
|
||||
});
|
||||
});
|
||||
server.get('/list-icons-categorized', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateAPIv1IconsListResponse(req.query, res, true);
|
||||
handleJSONResponse(req, res, (q) => createAPIv1IconsListResponse(q, true));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -154,14 +158,14 @@ export async function startHTTPServer() {
|
|||
// Search, currently version 2
|
||||
server.get('/search', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateAPIv2SearchResponse(req.query, res);
|
||||
handleJSONResponse(req, res, createAPIv2SearchResponse);
|
||||
});
|
||||
});
|
||||
|
||||
// Keywords
|
||||
server.get('/keywords', (req, res) => {
|
||||
runWhenLoaded(() => {
|
||||
generateKeywordsResponse(req.query, res);
|
||||
handleJSONResponse(req, res, createKeywordsResponse);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
@ -177,7 +181,7 @@ export async function startHTTPServer() {
|
|||
|
||||
// Options
|
||||
server.options('/*', (req, res) => {
|
||||
res.send(200);
|
||||
res.code(204).header('Content-Length', '0').send();
|
||||
});
|
||||
|
||||
// Robots
|
||||
|
|
@ -197,21 +201,21 @@ export async function startHTTPServer() {
|
|||
|
||||
// Redirect
|
||||
server.get('/', (req, res) => {
|
||||
res.redirect(301, appConfig.redirectIndex);
|
||||
res.redirect(appConfig.redirectIndex, 301);
|
||||
});
|
||||
|
||||
// Error handling
|
||||
server.setDefaultRoute((req, res) => {
|
||||
server.setNotFoundHandler((req, res) => {
|
||||
res.statusCode = 404;
|
||||
console.log('404:', req.url);
|
||||
|
||||
// Need to set custom headers because hooks don't work here
|
||||
for (let i = 0; i < headers.length; i++) {
|
||||
const header = headers[i];
|
||||
res.setHeader(header.key, header.value);
|
||||
res.header(header.key, header.value);
|
||||
}
|
||||
|
||||
res.end();
|
||||
res.send();
|
||||
});
|
||||
|
||||
// Start it
|
||||
|
|
|
|||
|
|
@ -1,17 +1,18 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { getPrefixes, iconSets } from '../../data/icon-sets';
|
||||
import type { IconSetAPIv2IconsList } from '../../types/icon-set/extra';
|
||||
import type { StoredIconSet } from '../../types/icon-set/storage';
|
||||
import { getPrefixes, iconSets } from '../../data/icon-sets.js';
|
||||
import type { IconSetAPIv2IconsList } from '../../types/icon-set/extra.js';
|
||||
import type { StoredIconSet } from '../../types/icon-set/storage.js';
|
||||
import type {
|
||||
APIv1ListIconsBaseResponse,
|
||||
APIv1ListIconsCategorisedResponse,
|
||||
APIv1ListIconsResponse,
|
||||
} from '../../types/server/v1';
|
||||
import { checkJSONPQuery, sendJSONResponse } from '../helpers/json';
|
||||
import { filterPrefixesByPrefix } from '../helpers/prefixes';
|
||||
} from '../../types/server/v1.js';
|
||||
import { filterPrefixesByPrefix } from '../helpers/prefixes.js';
|
||||
|
||||
// Response results, depends on `categorised` option
|
||||
type PossibleResults = APIv1ListIconsResponse | APIv1ListIconsCategorisedResponse;
|
||||
|
||||
/**
|
||||
* Send API v2 response
|
||||
* Create API v1 response
|
||||
*
|
||||
* This response ignores the following parameters:
|
||||
* - `aliases` -> always enabled
|
||||
|
|
@ -19,27 +20,15 @@ import { filterPrefixesByPrefix } from '../helpers/prefixes';
|
|||
*
|
||||
* Those parameters are always requested anyway, so does not make sense to re-create data in case they are disabled
|
||||
*/
|
||||
export function generateAPIv1IconsListResponse(
|
||||
query: FastifyRequest['query'],
|
||||
res: FastifyReply,
|
||||
export function createAPIv1IconsListResponse(
|
||||
query: Record<string, string>,
|
||||
categorised: boolean
|
||||
) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
|
||||
const wrap = checkJSONPQuery(q);
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
): PossibleResults | Record<string, PossibleResults> | number {
|
||||
function parse(
|
||||
prefix: string,
|
||||
iconSet: StoredIconSet,
|
||||
v2Cache: IconSetAPIv2IconsList
|
||||
): APIv1ListIconsResponse | APIv1ListIconsCategorisedResponse {
|
||||
const icons = iconSet.icons;
|
||||
|
||||
// Generate common data
|
||||
const base: APIv1ListIconsBaseResponse = {
|
||||
prefix,
|
||||
|
|
@ -48,13 +37,13 @@ export function generateAPIv1IconsListResponse(
|
|||
if (v2Cache.title) {
|
||||
base.title = v2Cache.title;
|
||||
}
|
||||
if (q.info && v2Cache.info) {
|
||||
if (query.info && v2Cache.info) {
|
||||
base.info = v2Cache.info;
|
||||
}
|
||||
if (q.aliases && v2Cache.aliases) {
|
||||
if (query.aliases && v2Cache.aliases) {
|
||||
base.aliases = v2Cache.aliases;
|
||||
}
|
||||
if (q.chars && v2Cache.chars) {
|
||||
if (query.chars && v2Cache.chars) {
|
||||
base.chars = v2Cache.chars;
|
||||
}
|
||||
|
||||
|
|
@ -81,22 +70,20 @@ export function generateAPIv1IconsListResponse(
|
|||
return result;
|
||||
}
|
||||
|
||||
if (q.prefix) {
|
||||
const prefix = q.prefix;
|
||||
if (query.prefix) {
|
||||
const prefix = query.prefix;
|
||||
const iconSet = iconSets[prefix]?.item;
|
||||
if (!iconSet || !iconSet.apiV2IconsCache) {
|
||||
res.send(404);
|
||||
return;
|
||||
return 404;
|
||||
}
|
||||
sendJSONResponse(parse(prefix, iconSet, iconSet.apiV2IconsCache), q, wrap, res);
|
||||
return;
|
||||
return parse(prefix, iconSet, iconSet.apiV2IconsCache);
|
||||
}
|
||||
|
||||
if (q.prefixes) {
|
||||
if (query.prefixes) {
|
||||
const prefixes = filterPrefixesByPrefix(
|
||||
getPrefixes(),
|
||||
{
|
||||
prefixes: q.prefixes,
|
||||
prefixes: query.prefixes,
|
||||
},
|
||||
false
|
||||
);
|
||||
|
|
@ -125,20 +112,18 @@ export function generateAPIv1IconsListResponse(
|
|||
|
||||
if (!items.length) {
|
||||
// Empty list
|
||||
res.send(404);
|
||||
return;
|
||||
return 404;
|
||||
}
|
||||
|
||||
// Get all items
|
||||
const result = Object.create(null) as Record<string, ReturnType<typeof parse>>;
|
||||
const result = Object.create(null) as Record<string, PossibleResults>;
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
result[item.prefix] = parse(item.prefix, item.iconSet, item.v2Cache);
|
||||
}
|
||||
sendJSONResponse(result, q, wrap, res);
|
||||
return;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Invalid
|
||||
res.send(400);
|
||||
return 400;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { iconSets } from '../../data/icon-sets';
|
||||
import type { APIv2CollectionResponse } from '../../types/server/v2';
|
||||
import { checkJSONPQuery, sendJSONResponse } from '../helpers/json';
|
||||
import { iconSets } from '../../data/icon-sets.js';
|
||||
import type { APIv2CollectionResponse } from '../../types/server/v2.js';
|
||||
|
||||
/**
|
||||
* Send API v2 response
|
||||
|
|
@ -12,29 +10,18 @@ import { checkJSONPQuery, sendJSONResponse } from '../helpers/json';
|
|||
*
|
||||
* Those parameters are always requested anyway, so does not make sense to re-create data in case they are disabled
|
||||
*/
|
||||
export function generateAPIv2CollectionResponse(query: FastifyRequest['query'], res: FastifyReply) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
|
||||
const wrap = checkJSONPQuery(q);
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
export function createAPIv2CollectionResponse(q: Record<string, string>): APIv2CollectionResponse | number {
|
||||
// Get icon set
|
||||
const prefix = q.prefix;
|
||||
if (!prefix || !iconSets[prefix]) {
|
||||
res.send(404);
|
||||
return;
|
||||
return 404;
|
||||
}
|
||||
|
||||
const iconSet = iconSets[prefix].item;
|
||||
const apiV2IconsCache = iconSet.apiV2IconsCache;
|
||||
if (!apiV2IconsCache) {
|
||||
// Disabled
|
||||
res.send(404);
|
||||
return;
|
||||
return 404;
|
||||
}
|
||||
|
||||
// Generate response
|
||||
|
|
@ -52,5 +39,5 @@ export function generateAPIv2CollectionResponse(query: FastifyRequest['query'],
|
|||
delete response.chars;
|
||||
}
|
||||
|
||||
sendJSONResponse(response, q, wrap, res);
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { getPrefixes, iconSets } from '../../data/icon-sets';
|
||||
import type { APIv2CollectionsResponse } from '../../types/server/v2';
|
||||
import { checkJSONPQuery, sendJSONResponse } from '../helpers/json';
|
||||
import { filterPrefixesByPrefix } from '../helpers/prefixes';
|
||||
import { getPrefixes, iconSets } from '../../data/icon-sets.js';
|
||||
import type { APIv2CollectionsResponse } from '../../types/server/v2.js';
|
||||
import { filterPrefixesByPrefix } from '../helpers/prefixes.js';
|
||||
|
||||
/**
|
||||
* Send response
|
||||
|
|
@ -12,15 +10,7 @@ import { filterPrefixesByPrefix } from '../helpers/prefixes';
|
|||
* Ignored parameters:
|
||||
* - hidden (always enabled)
|
||||
*/
|
||||
export function generateCollectionsListResponse(query: FastifyRequest['query'], res: FastifyReply) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
const wrap = checkJSONPQuery(q);
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
export function createCollectionsListResponse(q: Record<string, string>): APIv2CollectionsResponse {
|
||||
// Filter prefixes
|
||||
const prefixes = filterPrefixesByPrefix(getPrefixes('info'), q, false);
|
||||
const response = Object.create(null) as APIv2CollectionsResponse;
|
||||
|
|
@ -33,5 +23,5 @@ export function generateCollectionsListResponse(query: FastifyRequest['query'],
|
|||
}
|
||||
}
|
||||
|
||||
sendJSONResponse(response, q, wrap, res);
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { stringToColor } from '@iconify/utils/lib/colors';
|
||||
import { getIconsCSS } from '@iconify/utils/lib/css/icons';
|
||||
import { getStoredIconsData } from '../../data/icon-set/utils/get-icons';
|
||||
import { iconSets } from '../../data/icon-sets';
|
||||
import type { IconCSSIconSetOptions } from '@iconify/utils/lib/css/types';
|
||||
import { paramToBoolean } from '../../misc/bool';
|
||||
import { getStoredIconsData } from '../../data/icon-set/utils/get-icons.js';
|
||||
import { iconSets } from '../../data/icon-sets.js';
|
||||
import { paramToBoolean } from '../../misc/bool.js';
|
||||
import { errorText } from '../helpers/errors.js';
|
||||
import { cleanupQueryValue } from '../helpers/query.js';
|
||||
|
||||
/**
|
||||
* Check selector for weird stuff
|
||||
|
|
@ -26,7 +28,7 @@ export function generateIconsStyleResponse(prefix: string, query: FastifyRequest
|
|||
|
||||
if (!names || !names.length) {
|
||||
// Missing or invalid icons parameter
|
||||
res.send(404);
|
||||
res.code(404).send(errorText(404));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +36,7 @@ export function generateIconsStyleResponse(prefix: string, query: FastifyRequest
|
|||
const iconSet = iconSets[prefix];
|
||||
if (!iconSet) {
|
||||
// No such icon set
|
||||
res.send(404);
|
||||
res.code(404).send(errorText(404));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +58,7 @@ export function generateIconsStyleResponse(prefix: string, query: FastifyRequest
|
|||
|
||||
// 'color': string
|
||||
// Sets color for monotone images
|
||||
const color = qOptions.color;
|
||||
const color = cleanupQueryValue(qOptions.color);
|
||||
if (typeof color === 'string' && stringToColor(color)) {
|
||||
options.color = color;
|
||||
}
|
||||
|
|
@ -97,7 +99,7 @@ export function generateIconsStyleResponse(prefix: string, query: FastifyRequest
|
|||
// 'commonSelector': string
|
||||
// Common selector for all requested icons
|
||||
// Alias: 'common'
|
||||
const commonSelector = qOptions.commonSelector || q.common;
|
||||
const commonSelector = cleanupQueryValue(qOptions.commonSelector || q.common);
|
||||
if (checkSelector(commonSelector)) {
|
||||
options.commonSelector = commonSelector;
|
||||
}
|
||||
|
|
@ -105,7 +107,7 @@ export function generateIconsStyleResponse(prefix: string, query: FastifyRequest
|
|||
// 'iconSelector': string
|
||||
// Icon selector
|
||||
// Alias: 'selector'
|
||||
const iconSelector = qOptions.iconSelector || q.selector;
|
||||
const iconSelector = cleanupQueryValue(qOptions.iconSelector || q.selector);
|
||||
if (checkSelector(iconSelector)) {
|
||||
options.iconSelector = iconSelector;
|
||||
}
|
||||
|
|
@ -113,7 +115,7 @@ export function generateIconsStyleResponse(prefix: string, query: FastifyRequest
|
|||
// 'overrideSelector': string
|
||||
// Selector for rules in icon that override common rules
|
||||
// Alias: 'override'
|
||||
const overrideSelector = qOptions.overrideSelector || q.override;
|
||||
const overrideSelector = cleanupQueryValue(qOptions.overrideSelector || q.override);
|
||||
if (checkSelector(overrideSelector)) {
|
||||
options.overrideSelector = overrideSelector;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +1,63 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { getStoredIconsData } from '../../data/icon-set/utils/get-icons';
|
||||
import { iconSets } from '../../data/icon-sets';
|
||||
import { checkJSONPQuery, sendJSONResponse } from '../helpers/json';
|
||||
import { IconifyJSON } from '@iconify/types';
|
||||
import { getStoredIconsData } from '../../data/icon-set/utils/get-icons.js';
|
||||
import { iconSets } from '../../data/icon-sets.js';
|
||||
|
||||
/**
|
||||
* Generate icons data
|
||||
*/
|
||||
export function generateIconsDataResponse(
|
||||
export function createIconsDataResponse(
|
||||
prefix: string,
|
||||
wrapJS: boolean,
|
||||
query: FastifyRequest['query'],
|
||||
res: FastifyReply
|
||||
) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
const names = q.icons?.split(',');
|
||||
q: Record<string, string | string[]>
|
||||
): number | IconifyJSON | Promise<IconifyJSON | number> {
|
||||
const iconNames = q.icons;
|
||||
const names = typeof iconNames === 'string' ? iconNames.split(',') : iconNames;
|
||||
|
||||
if (!names || !names.length) {
|
||||
// Missing or invalid icons parameter
|
||||
res.send(404);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for JSONP
|
||||
const wrap = checkJSONPQuery(q, wrapJS, 'SimpleSVG._loaderCallback');
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
return 404;
|
||||
}
|
||||
|
||||
// Get icon set
|
||||
const iconSet = iconSets[prefix];
|
||||
if (!iconSet) {
|
||||
// No such icon set
|
||||
res.send(404);
|
||||
return;
|
||||
return 404;
|
||||
}
|
||||
|
||||
// Get icons
|
||||
// Get icons, possibly sync
|
||||
let syncData: IconifyJSON | undefined;
|
||||
let resolveData: undefined | ((data: IconifyJSON) => void);
|
||||
|
||||
getStoredIconsData(iconSet.item, names, (data) => {
|
||||
// Send data
|
||||
sendJSONResponse(data, q, wrap, res);
|
||||
if (resolveData) {
|
||||
resolveData(data);
|
||||
} else {
|
||||
syncData = data;
|
||||
}
|
||||
});
|
||||
|
||||
if (syncData) {
|
||||
return syncData;
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
resolveData = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Awaitable version of createIconsDataResponse()
|
||||
*/
|
||||
export function createIconsDataResponseAsync(
|
||||
prefix: string,
|
||||
q: Record<string, string | string[]>
|
||||
): Promise<IconifyJSON | number> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const result = createIconsDataResponse(prefix, q);
|
||||
if (result instanceof Promise) {
|
||||
result.then(resolve).catch(reject);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,16 @@
|
|||
import { matchIconName } from '@iconify/utils';
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { searchIndex } from '../../data/search';
|
||||
import { getPartialKeywords } from '../../data/search/partial';
|
||||
import type { APIv3KeywordsQuery, APIv3KeywordsResponse } from '../../types/server/keywords';
|
||||
import { checkJSONPQuery, sendJSONResponse } from '../helpers/json';
|
||||
import { matchIconName } from '@iconify/utils/lib/icon/name';
|
||||
import { searchIndex } from '../../data/search.js';
|
||||
import { getPartialKeywords } from '../../data/search/partial.js';
|
||||
import type { APIv3KeywordsQuery, APIv3KeywordsResponse } from '../../types/server/keywords.js';
|
||||
|
||||
/**
|
||||
* Generate icons data
|
||||
* Find full keywords for partial keyword
|
||||
*/
|
||||
export function generateKeywordsResponse(query: FastifyRequest['query'], res: FastifyReply) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
const wrap = checkJSONPQuery(q);
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
export function createKeywordsResponse(q: Record<string, string>): number | APIv3KeywordsResponse {
|
||||
// Check if search data is available
|
||||
const searchIndexData = searchIndex.data;
|
||||
if (!searchIndexData) {
|
||||
res.send(404);
|
||||
return;
|
||||
return 404;
|
||||
}
|
||||
const keywords = searchIndexData.keywords;
|
||||
|
||||
|
|
@ -32,15 +21,16 @@ export function generateKeywordsResponse(query: FastifyRequest['query'], res: Fa
|
|||
let failed = false;
|
||||
|
||||
if (typeof q.prefix === 'string') {
|
||||
// Keywords should start with prefix
|
||||
test = q.prefix;
|
||||
suffixes = false;
|
||||
} else if (typeof q.keyword === 'string') {
|
||||
// All keywords that contain keyword
|
||||
test = q.keyword;
|
||||
suffixes = true;
|
||||
} else {
|
||||
// Invalid query
|
||||
res.send(400);
|
||||
return;
|
||||
return 400;
|
||||
}
|
||||
test = test.toLowerCase().trim();
|
||||
|
||||
|
|
@ -71,5 +61,5 @@ export function generateKeywordsResponse(query: FastifyRequest['query'], res: Fa
|
|||
matches: failed || invalid ? [] : getPartialKeywords(test, suffixes, searchIndexData)?.slice(0) || [],
|
||||
};
|
||||
|
||||
sendJSONResponse(response, q, wrap, res);
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,11 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { getPrefixes, iconSets } from '../../data/icon-sets';
|
||||
import type { APIv3LastModifiedResponse } from '../../types/server/modified';
|
||||
import { checkJSONPQuery, sendJSONResponse } from '../helpers/json';
|
||||
import { filterPrefixesByPrefix } from '../helpers/prefixes';
|
||||
import { getPrefixes, iconSets } from '../../data/icon-sets.js';
|
||||
import type { APIv3LastModifiedResponse } from '../../types/server/modified.js';
|
||||
import { filterPrefixesByPrefix } from '../helpers/prefixes.js';
|
||||
|
||||
/**
|
||||
* Generate icons data
|
||||
* Get last modified time for all icon sets
|
||||
*/
|
||||
export function generateLastModifiedResponse(query: FastifyRequest['query'], res: FastifyReply) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
const wrap = checkJSONPQuery(q);
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
export function createLastModifiedResponse(q: Record<string, string>): number | APIv3LastModifiedResponse {
|
||||
// Filter prefixes
|
||||
const prefixes = filterPrefixesByPrefix(getPrefixes(), q, false);
|
||||
|
||||
|
|
@ -36,5 +26,5 @@ export function generateLastModifiedResponse(query: FastifyRequest['query'], res
|
|||
}
|
||||
}
|
||||
|
||||
sendJSONResponse(response, q, wrap, res);
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { iconSets } from '../../data/icon-sets';
|
||||
import { searchIndex } from '../../data/search';
|
||||
import { search } from '../../data/search/index';
|
||||
import { paramToBoolean } from '../../misc/bool';
|
||||
import type { SearchParams } from '../../types/search';
|
||||
import type { APIv2SearchParams, APIv2SearchResponse } from '../../types/server/v2';
|
||||
import { checkJSONPQuery, sendJSONResponse } from '../helpers/json';
|
||||
import { iconSets } from '../../data/icon-sets.js';
|
||||
import { searchIndex } from '../../data/search.js';
|
||||
import { search } from '../../data/search/index.js';
|
||||
import { paramToBoolean } from '../../misc/bool.js';
|
||||
import type { SearchParams } from '../../types/search.js';
|
||||
import type { APIv2SearchParams, APIv2SearchResponse } from '../../types/server/v2.js';
|
||||
|
||||
const minSearchLimit = 32;
|
||||
const maxSearchLimit = 999;
|
||||
|
|
@ -14,28 +12,17 @@ const defaultSearchLimit = minSearchLimit * 2;
|
|||
/**
|
||||
* Send API v2 response
|
||||
*/
|
||||
export function generateAPIv2SearchResponse(query: FastifyRequest['query'], res: FastifyReply) {
|
||||
const q = (query || {}) as Record<string, string>;
|
||||
|
||||
const wrap = checkJSONPQuery(q);
|
||||
if (!wrap) {
|
||||
// Invalid JSONP callback
|
||||
res.send(400);
|
||||
return;
|
||||
}
|
||||
|
||||
export function createAPIv2SearchResponse(q: Record<string, string>): number | APIv2SearchResponse {
|
||||
// Check if search data is available
|
||||
const searchIndexData = searchIndex.data;
|
||||
if (!searchIndexData) {
|
||||
res.send(404);
|
||||
return;
|
||||
return 404;
|
||||
}
|
||||
|
||||
// Get query
|
||||
const keyword = q.query;
|
||||
if (!keyword) {
|
||||
res.send(400);
|
||||
return;
|
||||
return 400;
|
||||
}
|
||||
|
||||
// Convert to params
|
||||
|
|
@ -49,18 +36,24 @@ export function generateAPIv2SearchResponse(query: FastifyRequest['query'], res:
|
|||
if (v2Query.limit) {
|
||||
const limit = parseInt(v2Query.limit);
|
||||
if (!limit) {
|
||||
res.send(400);
|
||||
return;
|
||||
return 400;
|
||||
}
|
||||
params.limit = Math.max(minSearchLimit, Math.min(limit, maxSearchLimit));
|
||||
}
|
||||
if (v2Query.min) {
|
||||
const limit = parseInt(v2Query.min);
|
||||
if (!limit) {
|
||||
return 400;
|
||||
}
|
||||
params.limit = Math.max(minSearchLimit, Math.min(limit, maxSearchLimit));
|
||||
params.softLimit = true;
|
||||
}
|
||||
|
||||
let start = 0;
|
||||
if (v2Query.start) {
|
||||
start = parseInt(v2Query.start);
|
||||
if (isNaN(start) || start < 0 || start >= params.limit) {
|
||||
res.send(400);
|
||||
return;
|
||||
return 400;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -121,5 +114,5 @@ export function generateAPIv2SearchResponse(query: FastifyRequest['query'], res:
|
|||
};
|
||||
}
|
||||
|
||||
sendJSONResponse(response, q, wrap, res);
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
import { defaultIconDimensions, flipFromString, iconToHTML, iconToSVG, rotateFromString } from '@iconify/utils';
|
||||
import { iconToHTML } from '@iconify/utils/lib/svg/html';
|
||||
import { iconToSVG } from '@iconify/utils/lib/svg/build';
|
||||
import { flipFromString } from '@iconify/utils/lib/customisations/flip';
|
||||
import { rotateFromString } from '@iconify/utils/lib/customisations/rotate';
|
||||
import { defaultIconDimensions } from '@iconify/utils/lib/icon/defaults';
|
||||
import { defaultIconCustomisations, IconifyIconCustomisations } from '@iconify/utils/lib/customisations/defaults';
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { getStoredIconData } from '../../data/icon-set/utils/get-icon';
|
||||
import { iconSets } from '../../data/icon-sets';
|
||||
import { getStoredIconData } from '../../data/icon-set/utils/get-icon.js';
|
||||
import { iconSets } from '../../data/icon-sets.js';
|
||||
import { errorText } from '../helpers/errors.js';
|
||||
import { cleanupQueryValue } from '../helpers/query.js';
|
||||
|
||||
/**
|
||||
* Generate SVG
|
||||
|
|
@ -12,7 +18,7 @@ export function generateSVGResponse(prefix: string, name: string, query: Fastify
|
|||
const iconSetItem = iconSets[prefix]?.item;
|
||||
if (!iconSetItem) {
|
||||
// No such icon set
|
||||
res.send(404);
|
||||
res.code(404).send(errorText(404));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -20,7 +26,7 @@ export function generateSVGResponse(prefix: string, name: string, query: Fastify
|
|||
const icons = iconSetItem.icons;
|
||||
if (!(icons.visible[name] || icons.hidden[name]) && !iconSetItem.icons.chars?.[name]) {
|
||||
// No such icon
|
||||
res.send(404);
|
||||
res.code(404).send(errorText(404));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -28,7 +34,7 @@ export function generateSVGResponse(prefix: string, name: string, query: Fastify
|
|||
getStoredIconData(iconSetItem, name, (data) => {
|
||||
if (!data) {
|
||||
// Invalid icon
|
||||
res.send(404);
|
||||
res.code(404).send(errorText(404));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -38,8 +44,8 @@ export function generateSVGResponse(prefix: string, name: string, query: Fastify
|
|||
const customisations: IconifyIconCustomisations = {};
|
||||
|
||||
// Dimensions
|
||||
customisations.width = q.width || defaultIconCustomisations.width;
|
||||
customisations.height = q.height || defaultIconCustomisations.height;
|
||||
customisations.width = cleanupQueryValue(q.width) || defaultIconCustomisations.width;
|
||||
customisations.height = cleanupQueryValue(q.height) || defaultIconCustomisations.height;
|
||||
|
||||
// Rotation
|
||||
customisations.rotate = q.rotate ? rotateFromString(q.rotate, 0) : 0;
|
||||
|
|
@ -70,7 +76,7 @@ export function generateSVGResponse(prefix: string, name: string, query: Fastify
|
|||
let html = iconToHTML(body, svg.attributes);
|
||||
|
||||
// Change color
|
||||
const color = q.color;
|
||||
const color = cleanupQueryValue(q.color);
|
||||
if (color && html.indexOf('currentColor') !== -1 && color.indexOf('"') === -1) {
|
||||
html = html.split('currentColor').join(color);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { appConfig } from '../../config/app';
|
||||
import { triggerIconSetsUpdate } from '../../data/icon-sets';
|
||||
import { runWhenLoaded } from '../../data/loading';
|
||||
import { appConfig } from '../../config/app.js';
|
||||
import { triggerIconSetsUpdate } from '../../data/icon-sets.js';
|
||||
import { runWhenLoaded } from '../../data/loading.js';
|
||||
|
||||
let pendingUpdate = false;
|
||||
let lastError = 0;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { readFile } from 'node:fs/promises';
|
||||
import type { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { appConfig } from '../../config/app';
|
||||
import { appConfig } from '../../config/app.js';
|
||||
|
||||
let version: string | undefined;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import type { BaseDownloader } from '../../downloaders/base';
|
||||
import { maybeAwait } from '../../misc/async';
|
||||
import type { BaseDownloader } from '../../downloaders/base.js';
|
||||
import { maybeAwait } from '../../misc/async.js';
|
||||
import type {
|
||||
BaseCollectionsImporter,
|
||||
CreateIconSetImporter,
|
||||
CreateIconSetImporterResult,
|
||||
} from '../../types/importers/collections';
|
||||
import type { ImportedData } from '../../types/importers/common';
|
||||
} from '../../types/importers/collections.js';
|
||||
import type { ImportedData } from '../../types/importers/common.js';
|
||||
|
||||
/**
|
||||
* Base collections list importer
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { readFile } from 'node:fs/promises';
|
||||
import { matchIconName } from '@iconify/utils/lib/icon/name';
|
||||
import type { BaseDownloader } from '../../downloaders/base';
|
||||
import type { BaseCollectionsImporter, CreateIconSetImporter } from '../../types/importers/collections';
|
||||
import type { ImportedData } from '../../types/importers/common';
|
||||
import { createBaseCollectionsListImporter } from './base';
|
||||
import type { IconifyInfo } from '@iconify/types';
|
||||
import { matchIconName } from '@iconify/utils/lib/icon/name';
|
||||
import type { BaseDownloader } from '../../downloaders/base.js';
|
||||
import type { BaseCollectionsImporter, CreateIconSetImporter } from '../../types/importers/collections.js';
|
||||
import type { ImportedData } from '../../types/importers/common.js';
|
||||
import { createBaseCollectionsListImporter } from './base.js';
|
||||
|
||||
interface JSONCollectionsListImporterOptions {
|
||||
// File to load
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { CustomDownloader } from '../../downloaders/custom';
|
||||
import type { BaseCollectionsImporter, CreateIconSetImporter } from '../../types/importers/collections';
|
||||
import type { ImportedData } from '../../types/importers/common';
|
||||
import { createBaseCollectionsListImporter } from './base';
|
||||
import { CustomDownloader } from '../../downloaders/custom.js';
|
||||
import type { BaseCollectionsImporter, CreateIconSetImporter } from '../../types/importers/collections.js';
|
||||
import type { ImportedData } from '../../types/importers/common.js';
|
||||
import { createBaseCollectionsListImporter } from './base.js';
|
||||
|
||||
/**
|
||||
* Create importer for hardcoded list of icon sets
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { readFile } from 'node:fs/promises';
|
||||
import { quicklyValidateIconSet } from '@iconify/utils/lib/icon-set/validate-basic';
|
||||
import { asyncStoreLoadedIconSet } from '../../data/icon-set/store/storage';
|
||||
import type { StoredIconSet } from '../../types/icon-set/storage';
|
||||
import { prependSlash } from '../../misc/files';
|
||||
import { asyncStoreLoadedIconSet } from '../../data/icon-set/store/storage.js';
|
||||
import type { StoredIconSet } from '../../types/icon-set/storage.js';
|
||||
import { prependSlash } from '../../misc/files.js';
|
||||
|
||||
export interface IconSetJSONOptions {
|
||||
// Ignore bad prefix?
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { readFile } from 'node:fs/promises';
|
||||
import { quicklyValidateIconSet } from '@iconify/utils/lib/icon-set/validate-basic';
|
||||
import { asyncStoreLoadedIconSet } from '../../data/icon-set/store/storage';
|
||||
import type { StoredIconSet } from '../../types/icon-set/storage';
|
||||
import { appConfig } from '../../config/app';
|
||||
import { asyncStoreLoadedIconSet } from '../../data/icon-set/store/storage.js';
|
||||
import type { StoredIconSet } from '../../types/icon-set/storage.js';
|
||||
import { appConfig } from '../../config/app.js';
|
||||
|
||||
export interface IconSetJSONPackageOptions {
|
||||
// Ignore bad prefix?
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { readdir, stat } from 'node:fs/promises';
|
||||
import { matchIconName } from '@iconify/utils/lib/icon/name';
|
||||
import type { BaseDownloader } from '../../downloaders/base';
|
||||
import { DirectoryDownloader } from '../../downloaders/directory';
|
||||
import type { StoredIconSet } from '../../types/icon-set/storage';
|
||||
import type { BaseCollectionsImporter, CreateIconSetImporter } from '../../types/importers/collections';
|
||||
import type { ImportedData } from '../../types/importers/common';
|
||||
import { createJSONIconSetImporter } from '../icon-set/json';
|
||||
import { createBaseCollectionsListImporter } from '../collections/base';
|
||||
import type { BaseDownloader } from '../../downloaders/base.js';
|
||||
import { DirectoryDownloader } from '../../downloaders/directory.js';
|
||||
import type { StoredIconSet } from '../../types/icon-set/storage.js';
|
||||
import type { BaseCollectionsImporter, CreateIconSetImporter } from '../../types/importers/collections.js';
|
||||
import type { ImportedData } from '../../types/importers/common.js';
|
||||
import { createJSONIconSetImporter } from '../icon-set/json.js';
|
||||
import { createBaseCollectionsListImporter } from '../collections/base.js';
|
||||
|
||||
interface JSONDirectoryImporterOptions {
|
||||
// Icon set filter
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { readFile } from 'node:fs/promises';
|
||||
import { matchIconName } from '@iconify/utils/lib/icon/name';
|
||||
import type { BaseDownloader } from '../../downloaders/base';
|
||||
import { DirectoryDownloader } from '../../downloaders/directory';
|
||||
import type { StoredIconSet } from '../../types/icon-set/storage';
|
||||
import type { BaseCollectionsImporter, CreateIconSetImporter } from '../../types/importers/collections';
|
||||
import type { ImportedData } from '../../types/importers/common';
|
||||
import { createJSONIconSetImporter } from '../icon-set/json';
|
||||
import { createBaseCollectionsListImporter } from '../collections/base';
|
||||
import type { BaseDownloader } from '../../downloaders/base.js';
|
||||
import { DirectoryDownloader } from '../../downloaders/directory.js';
|
||||
import type { StoredIconSet } from '../../types/icon-set/storage.js';
|
||||
import type { BaseCollectionsImporter, CreateIconSetImporter } from '../../types/importers/collections.js';
|
||||
import type { ImportedData } from '../../types/importers/common.js';
|
||||
import { createJSONIconSetImporter } from '../icon-set/json.js';
|
||||
import { createBaseCollectionsListImporter } from '../collections/base.js';
|
||||
|
||||
interface IconSetsPackageImporterOptions {
|
||||
// Icon set filter
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { BaseDownloader } from '../../downloaders/base';
|
||||
import type { StoredIconSet } from '../../types/icon-set/storage';
|
||||
import type { ImportedData } from '../../types/importers/common';
|
||||
import type { BaseFullImporter } from '../../types/importers/full';
|
||||
import type { BaseDownloader } from '../../downloaders/base.js';
|
||||
import type { StoredIconSet } from '../../types/icon-set/storage.js';
|
||||
import type { ImportedData } from '../../types/importers/common.js';
|
||||
import type { BaseFullImporter } from '../../types/importers/full.js';
|
||||
|
||||
/**
|
||||
* Base full importer
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { readdir, stat } from 'node:fs/promises';
|
||||
import { matchIconName } from '@iconify/utils/lib/icon/name';
|
||||
import type { BaseDownloader } from '../../downloaders/base';
|
||||
import type { ImportedData } from '../../types/importers/common';
|
||||
import type { BaseFullImporter } from '../../types/importers/full';
|
||||
import { createBaseImporter } from './base';
|
||||
import { IconSetJSONOptions, importIconSetFromJSON } from '../common/icon-set-json';
|
||||
import type { BaseDownloader } from '../../downloaders/base.js';
|
||||
import type { ImportedData } from '../../types/importers/common.js';
|
||||
import type { BaseFullImporter } from '../../types/importers/full.js';
|
||||
import { createBaseImporter } from './base.js';
|
||||
import { type IconSetJSONOptions, importIconSetFromJSON } from '../common/icon-set-json.js';
|
||||
|
||||
interface JSONDirectoryImporterOptions extends IconSetJSONOptions {
|
||||
// Icon set filter
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { readFile } from 'node:fs/promises';
|
||||
import { matchIconName } from '@iconify/utils/lib/icon/name';
|
||||
import type { BaseDownloader } from '../../downloaders/base';
|
||||
import type { ImportedData } from '../../types/importers/common';
|
||||
import type { BaseFullImporter } from '../../types/importers/full';
|
||||
import { createBaseImporter } from './base';
|
||||
import { IconSetJSONOptions, importIconSetFromJSON } from '../common/icon-set-json';
|
||||
import type { IconifyInfo } from '@iconify/types';
|
||||
import { matchIconName } from '@iconify/utils/lib/icon/name';
|
||||
import type { BaseDownloader } from '../../downloaders/base.js';
|
||||
import type { ImportedData } from '../../types/importers/common.js';
|
||||
import type { BaseFullImporter } from '../../types/importers/full.js';
|
||||
import { createBaseImporter } from './base.js';
|
||||
import { IconSetJSONOptions, importIconSetFromJSON } from '../common/icon-set-json.js';
|
||||
|
||||
interface IconSetsPackageImporterOptions extends IconSetJSONOptions {
|
||||
// Icon set filter
|
||||
|
|
@ -23,6 +23,17 @@ export function createIconSetsPackageImporter<Downloader extends BaseDownloader<
|
|||
|
||||
// Load collections list
|
||||
obj._loadCollectionsListFromDirectory = async (path: string) => {
|
||||
// Log version
|
||||
try {
|
||||
const packageJSON = JSON.parse(await readFile(path + '/package.json', 'utf8'));
|
||||
if (packageJSON.name && packageJSON.version) {
|
||||
console.log(`Loading ${packageJSON.name} ${packageJSON.version}`);
|
||||
}
|
||||
} catch {
|
||||
//
|
||||
}
|
||||
|
||||
// Get prefixes
|
||||
let prefixes: string[];
|
||||
let data: Record<string, IconifyInfo>;
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { BaseDownloader } from '../../downloaders/base';
|
||||
import type { BaseIconSetImporter } from '../../types/importers/icon-set';
|
||||
import type { IconSetImportedData } from '../../types/importers/common';
|
||||
import { IconSetJSONPackageOptions, importIconSetFromJSONPackage } from '../common/json-package';
|
||||
import type { BaseDownloader } from '../../downloaders/base.js';
|
||||
import type { BaseIconSetImporter } from '../../types/importers/icon-set.js';
|
||||
import type { IconSetImportedData } from '../../types/importers/common.js';
|
||||
import { IconSetJSONPackageOptions, importIconSetFromJSONPackage } from '../common/json-package.js';
|
||||
|
||||
interface JSONPackageIconSetImporterOptions extends IconSetJSONPackageOptions {
|
||||
// Icon set prefix
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { BaseDownloader } from '../../downloaders/base';
|
||||
import type { BaseIconSetImporter } from '../../types/importers/icon-set';
|
||||
import type { IconSetImportedData } from '../../types/importers/common';
|
||||
import { IconSetJSONOptions, importIconSetFromJSON } from '../common/icon-set-json';
|
||||
import type { BaseDownloader } from '../../downloaders/base.js';
|
||||
import type { BaseIconSetImporter } from '../../types/importers/icon-set.js';
|
||||
import type { IconSetImportedData } from '../../types/importers/common.js';
|
||||
import { IconSetJSONOptions, importIconSetFromJSON } from '../common/icon-set-json.js';
|
||||
|
||||
interface JSONIconSetImporterOptions extends IconSetJSONOptions {
|
||||
// Icon set prefix
|
||||
|
|
|
|||
23
src/index.ts
23
src/index.ts
|
|
@ -1,30 +1,19 @@
|
|||
import { config } from 'dotenv';
|
||||
import { getImporters } from './config/icon-sets';
|
||||
import { iconSetsStorage } from './data/icon-set/store/storage';
|
||||
import { setImporters, updateIconSets } from './data/icon-sets';
|
||||
import { loaded } from './data/loading';
|
||||
import { cleanupStorageCache } from './data/storage/startup';
|
||||
import { startHTTPServer } from './http';
|
||||
import { loadEnvConfig } from './misc/load-config';
|
||||
import { loaded } from './data/loading.js';
|
||||
import { startHTTPServer } from './http/index.js';
|
||||
import { loadEnvConfig } from './misc/load-config.js';
|
||||
import { initAPI } from './init.js';
|
||||
|
||||
(async () => {
|
||||
// Configure environment
|
||||
config();
|
||||
loadEnvConfig();
|
||||
|
||||
// Reset old cache
|
||||
await cleanupStorageCache(iconSetsStorage);
|
||||
|
||||
// Start HTTP server
|
||||
startHTTPServer();
|
||||
|
||||
// Get all importers and load data
|
||||
const importers = await getImporters();
|
||||
for (let i = 0; i < importers.length; i++) {
|
||||
await importers[i].init();
|
||||
}
|
||||
setImporters(importers);
|
||||
updateIconSets();
|
||||
// Init API
|
||||
await initAPI();
|
||||
|
||||
// Loaded
|
||||
loaded();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
import { getImporters } from './config/icon-sets.js';
|
||||
import { iconSetsStorage } from './data/icon-set/store/storage.js';
|
||||
import { setImporters, updateIconSets } from './data/icon-sets.js';
|
||||
import { cleanupStorageCache } from './data/storage/startup.js';
|
||||
import { Importer } from './types/importers.js';
|
||||
|
||||
interface InitOptions {
|
||||
// Cleanup storage cache
|
||||
cleanup?: boolean;
|
||||
|
||||
// Importers
|
||||
importers?: Importer[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Init API
|
||||
*/
|
||||
export async function initAPI(options: InitOptions = {}) {
|
||||
// Reset old cache
|
||||
if (options.cleanup !== false) {
|
||||
await cleanupStorageCache(iconSetsStorage);
|
||||
}
|
||||
|
||||
// Get all importers and load data
|
||||
let importers = options.importers;
|
||||
if (!importers) {
|
||||
importers = await getImporters();
|
||||
}
|
||||
for (let i = 0; i < importers.length; i++) {
|
||||
await importers[i].init();
|
||||
}
|
||||
|
||||
// Update
|
||||
setImporters(importers);
|
||||
updateIconSets();
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { stat } from 'node:fs/promises';
|
||||
import { scanDirectory } from '@iconify/tools/lib/misc/scan';
|
||||
import type { FileEntry } from '../types/files';
|
||||
import { hashString } from './hash';
|
||||
import type { FileEntry } from '../types/files.js';
|
||||
import { hashString } from './hash.js';
|
||||
|
||||
/**
|
||||
* List all files in directory
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { appConfig, splitIconSetConfig, storageConfig } from '../config/app';
|
||||
import { paramToBoolean } from './bool';
|
||||
import { appConfig, splitIconSetConfig, storageConfig } from '../config/app.js';
|
||||
import { paramToBoolean } from './bool.js';
|
||||
|
||||
interface ConfigurableItem {
|
||||
config: unknown;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { StoredIconSet } from '../icon-set/storage';
|
||||
import type { StoredIconSet } from '../icon-set/storage.js';
|
||||
|
||||
/**
|
||||
* Generated data
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import type { IconifyIcons, IconifyInfo, IconifyJSON, IconifyMetaData } from '@iconify/types';
|
||||
import type { SplitDataTree } from '../split';
|
||||
import type { MemoryStorage, MemoryStorageItem } from '../storage';
|
||||
import type { IconSetIconsListIcons, IconSetAPIv2IconsList } from './extra';
|
||||
import type { SplitIconifyJSONMainData } from './split';
|
||||
import type { IconifyIcons, IconifyInfo, IconifyMetaData } from '@iconify/types';
|
||||
import type { SplitDataTree } from '../split.js';
|
||||
import type { MemoryStorage, MemoryStorageItem } from '../storage.js';
|
||||
import type { IconSetIconsListIcons, IconSetAPIv2IconsList } from './extra.js';
|
||||
import type { SplitIconifyJSONMainData } from './split.js';
|
||||
|
||||
/**
|
||||
* Themes
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { BaseDownloader } from '../downloaders/base';
|
||||
import type { StoredIconSet } from './icon-set/storage';
|
||||
import type { ImportedData } from './importers/common';
|
||||
import type { BaseDownloader } from '../downloaders/base.js';
|
||||
import type { StoredIconSet } from './icon-set/storage.js';
|
||||
import type { ImportedData } from './importers/common.js';
|
||||
|
||||
/**
|
||||
* Importer
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { BaseDownloader } from '../../downloaders/base';
|
||||
import type { MaybeAsync } from '../async';
|
||||
import type { BaseMainImporter, IconSetImportedData } from './common';
|
||||
import type { BaseIconSetImporter } from './icon-set';
|
||||
import type { BaseDownloader } from '../../downloaders/base.js';
|
||||
import type { MaybeAsync } from '../async.js';
|
||||
import type { BaseMainImporter, IconSetImportedData } from './common.js';
|
||||
import type { BaseIconSetImporter } from './icon-set.js';
|
||||
|
||||
/**
|
||||
* Loader for child element
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { DownloaderType } from '../downloaders/base';
|
||||
import type { StoredIconSet } from '../icon-set/storage';
|
||||
import type { DownloaderType } from '../downloaders/base.js';
|
||||
import type { StoredIconSet } from '../icon-set/storage.js';
|
||||
|
||||
/**
|
||||
* Base icon set importer interface
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { BaseMainImporter, IconSetImportedData } from './common';
|
||||
import type { BaseMainImporter, IconSetImportedData } from './common.js';
|
||||
|
||||
/**
|
||||
* Base full importer
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { BaseImporter, IconSetImportedData } from './common';
|
||||
import type { BaseImporter, IconSetImportedData } from './common.js';
|
||||
|
||||
/**
|
||||
* Base icon set importer interface
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { IconStyle } from './icon-set/extra';
|
||||
import type { IconStyle } from './icon-set/extra.js';
|
||||
|
||||
/**
|
||||
* List of keywords that can be used to autocomplete keyword
|
||||
|
|
@ -50,6 +50,7 @@ export interface SearchParams {
|
|||
|
||||
// Search results limit
|
||||
limit: number;
|
||||
softLimit?: boolean; // True if limit can be exceeded
|
||||
|
||||
// Toggle partial matches
|
||||
partial?: boolean;
|
||||
|
|
|
|||
|
|
@ -103,7 +103,9 @@ export interface APIv2SearchParams extends APIv2CommonParams {
|
|||
query: SearchQuery;
|
||||
|
||||
// Maximum number of items in response
|
||||
limit?: number;
|
||||
// If `min` is set, `limit` is ignored
|
||||
limit?: number; // Hard limit. Number of results will not exceed `limit`.
|
||||
min?: number; // Soft limit. Number of results can exceed `limit` if function already retrieved more icons.
|
||||
|
||||
// Start index for results
|
||||
start?: number;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "CommonJS",
|
||||
"module": "ESNext",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "node",
|
||||
|
|
|
|||
Loading…
Reference in New Issue