npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@amxts/config-core

v0.2.0

Published

Configs in INI, YAML or JSON for amxts plugins, read into typed objects and written back - and the cfg_* natives for Pawn plugins.

Readme

Config Core

Конфиги в INI, YAML или JSON: читаются в типизированные объекты и пишутся обратно

amxts module License

Возможности • Установка • Использование • Форматы файлов • INI-файлы • Pawn-плагины

English | Русский

Прочитайте файл конфига — INI, YAML или JSON, какой написал админ, — в объект той же формы, что его значения по умолчанию, типизированный в редакторе, измените его и запишите файл обратно, с комментариями на своих местах.

Возможности

  • Типизированный объект. configs.load("settings", { chat: { prefix: "[Server]" } }) даёт settings.chat.prefix того же типа, что значение по умолчанию; опечатка — ошибка редактора, а не молча пропавшая настройка.
  • Три формата, один API. settings.yaml, settings.json или settings.ini: один и тот же код читает любой из них.
  • Ошибки — с местом. Значение не того вида, имя не из своего юниона, незнакомый ключ — каждое называется в консоли сервера с файлом и строкой (в YAML и JSON — и со столбцом), с «did you mean», и остаётся значение по умолчанию.
  • Записывает то, что прочитал. configs.save(settings) кладёт значения на их места; комментарии на отдельных строках переживают сохранение, отсутствующий файл пишется в YAML.
  • И файлы любой формы. Дерево значений с путями — shop.items[0].name — для файла, ключи которого заранее неизвестны.
  • INI с блоками и строками. Секции, блоки и строки значений читаются в те же объекты — а 28 нативов cfg_* отдают их Pawn-плагинам.
  • Один экземпляр на сервер. Все плагины, на TypeScript и на Pawn, видят одни и те же загруженные файлы и одну базовую папку.

Установка

npx amxts module add config-core

Команда ставит пакет и добавляет его в modules в amxts.config.ts проекта. Настройки модуля пишутся рядом, в configs:

export default defineConfig({
	modules: ["@amxts/config-core"],
	configs: {
		baseDir: "myserver",   // имена файлов читаются из configs/myserver/
	},
});

| Опция | По умолчанию | Что делает | | --- | --- | --- | | baseDir | "" | Папка внутри configs/, из которой читаются файлы; "" — сама configs/. |

Модуль, который читает свои файлы через Config Core, например Menu Core, приносит его с собой — добавлять для него ничего не нужно.

Использование

Плагин пользуется модулем как configs, без строки импорта: сборка добавляет импорт в те плагины, которые им пользуются, и собирает модуль, только когда им пользуется хоть один.

const settings = configs.load("settings", {   // configs/myserver/settings.yaml, .json или .ini
	chat: { prefix: "[Server]" },
	round: { time: 2.5 },
	maps: ["de_dust2"],
});

console.log(`${settings.chat.prefix} ${settings.maps.length} maps`);   // из файла или по умолчанию

settings.round.time = 3;
configs.save(settings);                       // в формате файла, с комментариями

Значения по умолчанию задают форму объекта. Что есть в файле, заменяет значение по умолчанию, значение за значением; чего в файле нет, остаётся по умолчанию. Интерфейс даёт полям описания, необязательные поля и юнионы имён:

type Mode = "normal" | "dm" | "knife";

interface Settings {
	/** Текст перед каждым сообщением в чате. */
	chat: { prefix: string };
	round: { time: number; mode: Mode };
	maps: string[];
	/** Показывается при входе; если не задан, ничего не показывается. */
	motd?: string;
}

const settings = configs.load<Settings>("settings", {
	chat: { prefix: "[Server]" },
	round: { time: 2.5, mode: "normal" },
	maps: [],
});

Ошибка в файле оставляет значение по умолчанию, а консоль говорит, где она:

[ConfigCore] configs/myserver/settings.yaml:6:9: "mode" is "dmm", not one of "normal", "dm", "knife" - did you mean "dm"? - the default stays
[ConfigCore] configs/myserver/settings.yaml:2:3: unknown key "prefx" in "chat" - did you mean "prefix"?

| В объекте | В файле | | --- | --- | | string, number, boolean | значение; число можно текстом, логическое — true/false, yes/no, on/off в любом регистре или число (0 — ложь) | | юнион имён, "normal" \| "dm" | одно из них | | string[], number[], boolean[], список имён | список — элемент другого вида пропускается | | string[][] (текста, чисел, логических значений или имён) | список списков: строки значений; в INI — блок строк | | объект любой вложенности | объект | | список объектов | список объектов — поле, которого нет у элемента, пустое, и об этом сказано | | Map<string, number> (текста, чисел, логических значений или имён) | объект | | field?: T | можно не писать: undefined |

  • configs.load() отдаёт копию значений по умолчанию со значениями из файла; второй вызов читает файл заново. configs.save(settings) пишет каждое поле в файл, из которого объект прочитан.
  • У объекта, который load() прочитал без типа, нет имени типа, чтобы написать его в параметре функции: чтобы передавать настройки дальше, объявите интерфейс и пишите configs.load<Settings>(...).
  • Сборка читает форму из исходника: значения по умолчанию — объектным литералом (в списке — элемент), или указан тип — configs.load<Settings>(...). Чего так не прочитать — пустой список без типа, список списков списков, — останавливает сборку с местом и тем, как исправить.

Какой файл читается

load("settings") и read("settings") читают первый из settings.ini, settings.yaml, settings.yml, settings.json и settings.jsonc, который есть. Если есть два, читается первый, а консоль сервера об этом говорит: оставьте один. Имя с расширением — load("settings.json", ...) — читает этот файл. Файла нет — читаются значения по умолчанию, а сохраняется он как settings.yaml.

Файлы неизвестной формы

Файл, ключи которого заранее неизвестны, — список карт со своими настройками, файл, который пишет другой плагин, — читается деревом значений:

const maps = configs.read("maps");

for (const map of maps.values()) {
	console.log(`${map.key}: ${map.getNumber("rounds", 30)} rounds`);
}

maps.setNumber("de_dust2.rounds", 20);
maps.save();

Значение — это ConfigNode: объект, массив, текст, число, логическое значение или null (kind), с файлом, строкой и столбцом, где оно прочитано. Путь ведёт внутрь — chat.prefix, items[0].name, — а геттер принимает запасное значение на случай, если значения нет или оно другого вида.

API

| Функция | Что делает | | --- | --- | | load(name, defaults) | Читает файл конфига в объект той же формы, что defaults. | | save(settings) | Записывает объект, прочитанный load(), обратно в его файл. | | read(name) | Читает файл конфига деревом значений; верхнее значение файла. | | resolve(name) | Файл, который читают load(name) и read(name), например "settings.yaml". | | parse(text, format) | Читает текст — "yaml", "json" или "ini" — деревом. |

| ConfigNode | Что делает | | --- | --- | | kind · key · file · format · line · column | Что это за значение и где оно прочитано. | | get(path) · has(path) | Значение, к которому ведёт путь, или null; есть ли оно. | | keys(path?) · values(path?) | Ключи объекта; элементы массива или значения объекта. | | getString(path?, fallback?) · getNumber · getBoolean · getStrings | Значение как текст, число, логическое значение, список текста. | | set(path, text) · setNumber · setBoolean · setStrings | Задаёт значение, создавая объекты на пути; false, если путь идёт через текст или число. | | remove(path) · save() | Удаляет значение; записывает весь файл обратно в его формате. |

Форматы файлов

Одни и те же настройки в каждом из них:

# settings.yaml
chat:
  prefix: "[Server]"
  rules:
    - Be nice
    - No cheats
round:
  time: 2.5
hud:
  enabled: true
// settings.json или settings.jsonc
{
  "chat": {
    "prefix": "[Server]",
    "rules": ["Be nice", "No cheats"],
  },
  "round": { "time": 2.5 },
  "hud": { "enabled": true },
}
; settings.ini
[chat]
prefix = [Server]
rules = "Be nice" "No cheats"

[round]
time = 2.5

[hud]
enabled = 1
  • YAML: отображения и списки — с отступами или в { } и [ ]; простой текст, текст в 'одинарных' и "двойных" кавычках со своими экранированиями; блоки | и > для текста в несколько строк; числа, true/false, null и ~, как их читает YAML 1.2, — yes это текст; комментарии #; --- перед документом и ... после.
  • Чего в YAML нет: якорей и ссылок (&, *), тегов (!), сложных ключей (?), директив (%YAML), нескольких документов в одном файле и простого значения, которое продолжается на следующих строках. Тогда файл читается пустым, а консоль называет строку и столбец: configs/settings.yaml:4:9: anchors and aliases (& and *) are not supported - write the value out.
  • JSON: как пишется JSON, плюс комментарии // и /* */ из JSONC и запятая после последнего поля или элемента — и в файлах .json тоже.
  • INI: каждая [секция] — объект верхнего уровня: [chat] с prefix = [Server] — это settings.chat.prefix; строка из нескольких значений — список; блок key = { ... } — объект или список строк. Правила, которые добавляет INI, — в INI-файлах.
  • Сохранение пишет файл в его формате: комментарии на отдельных строках и пустые строки остаются; комментарий после значения на той же строке и комментарии внутри { } и [ ] в YAML — нет. JSON сохраняет свои отступы.

INI-файлы

INI-конфиг читается в такой же объект: каждая [секция] — объект, блок ключей key = { ... } — объект в нём, строка из нескольких значений — список, а блок строк в кавычках — список списков:

; configs/myserver/settings.ini
[MAIN]
CHAT_PREFIX = [MYPLUGIN]
MAPS = de_dust2 de_inferno de_nuke

HUD = {
	HIDE_TIME = 255 50 50
}

CVARS = {
	"mp_timelimit" "30"
	"mp_freezetime" "3"
}
const settings = configs.load("settings", {
	MAIN: {
		CHAT_PREFIX: "[Server]",
		MAPS: ["de_dust2"],
		HUD: { HIDE_TIME: [255, 255, 255] },
		CVARS: [["mp_timelimit", "20"]],
	},
});

for (const row of settings.MAIN.CVARS) {
	const cvar = new Cvar(row[0]);
	cvar.value = row[1];
}
  • Блок строк остаётся блоком, когда объект сохраняется, — и строки из одного значения тоже; комментарии над его строками остаются.
  • Путь в дереве такого файла (configs.read()) пишется так же: MAIN.HUD.HIDE_TIME[0], MAIN.CVARS[1][0]. Элемент списка — это [0]: MAIN.MAPS.0 ищет ключ 0.

[!WARNING] В INI-файле:

  • Ключи ищутся без учёта регистра, как их ищут Pawn-нативы, имена секций — нет: [main] — не MAIN. Из двух секций с одним именем берётся последняя.
  • Значение с пробелами берётся в кавычки: TITLE = "Main menu". Без кавычек это список слов: текстовое поле остаётся со значением по умолчанию, а консоль говорит почему, и нативы cfg_* Pawn и Menu Core читают первое слово.
  • Нет места значению на верхнем уровне объекта, которое не объект, и списку объектов: в INI-файле такое поле остаётся по умолчанию, и консоль один раз об этом говорит — такой конфиг пишите в YAML или JSON.

Pawn-плагины

Pawn-плагин тоже читает и пишет конфиги через Config Core — 28 нативами, имена которых начинаются с cfg_: cfg_load_file, cfg_get_value, cfg_set_int и остальные. Они читают INI-файлы. Их include лежит в пакете в include/:

#include <universal_config>

Pawn-плагин, уже собранный с этим include, работает как есть — пересобирать ничего не нужно.

Config Core работает на сервере как один из плагинов проекта. Если им не пользуется ни один плагин проекта на TypeScript, оставьте его в сборке для Pawn-плагинов: pawn: ["@amxts/config-core"] в amxts.config.ts. Все нативы с сигнатурами — в PAWN.ru.md.