feat: switch config to JSONC + split secrets.json

Load business config as JSONC (comments + trailing commas) via an
in-repo stripper then stdlib json — no new deps. Default path prefers
config.jsonc, falls back to legacy config.json. Auth write-back targets
only sibling secrets.json so JSONC comments are never wiped; secrets
overlay merges on load. Examples, compose, Docker, docs, favsort and
tools all share the same loader. Phase A only — no DB settings migration.
This commit was merged in pull request #4.
This commit is contained in:
bot-ci committed 2026-11-02 12:58:20 +08:00
1 parent b4f0f45c4e
commit 8e7ead1669
21 files changed
+652 -182

No files matched your search

+107
View File
@@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
"""JSONC(JSON with Comments)加载。
选择**自研轻量剥离**而非引入第三方依赖:本仓库运行依赖目前只有 ``httpx``,
配置加载属于启动/热更新热路径,JSONC 只需注释与尾逗号。实现按字符扫描,
在字符串字面量内保留 ``//``、``/*``、引号转义,避免误伤。
支持:
- ``//`` 行注释、``/* ... */`` 块注释
- 对象/数组中的尾逗号
- UTF-8 BOM
不支持 JSON5 扩展(无引号键、单引号、十六进制、``Infinity`` 等)。
"""
from __future__ import annotations
import json
from typing import Any
def strip_jsonc(text: str) -> str:
"""去掉 JSONC 注释与尾逗号,返回可交给 ``json.loads`` 的文本。"""
if text.startswith("\ufeff"):
text = text[1:]
out: list[str] = []
i = 0
n = len(text)
in_string = False
escape = False
while i < n:
ch = text[i]
if in_string:
out.append(ch)
if escape:
escape = False
elif ch == "\\":
escape = True
elif ch == '"':
in_string = False
i += 1
continue
if ch == '"':
in_string = True
out.append(ch)
i += 1
continue
if ch == "/" and i + 1 < n:
nxt = text[i + 1]
if nxt == "/":
i += 2
while i < n and text[i] not in "\r\n":
i += 1
continue
if nxt == "*":
i += 2
while i + 1 < n and not (text[i] == "*" and text[i + 1] == "/"):
i += 1
i = min(i + 2, n)
continue
out.append(ch)
i += 1
stripped = "".join(out)
# 去掉尾逗号:`,}` / `,]`(可夹空白)
cleaned: list[str] = []
i = 0
m = len(stripped)
in_string = False
escape = False
while i < m:
ch = stripped[i]
if in_string:
cleaned.append(ch)
if escape:
escape = False
elif ch == "\\":
escape = True
elif ch == '"':
in_string = False
i += 1
continue
if ch == '"':
in_string = True
cleaned.append(ch)
i += 1
continue
if ch == ",":
j = i + 1
while j < m and stripped[j] in " \t\r\n":
j += 1
if j < m and stripped[j] in "}]":
i += 1
continue
cleaned.append(ch)
i += 1
return "".join(cleaned)
def loads(text: str) -> Any:
"""解析 JSONC 文本。"""
return json.loads(strip_jsonc(text))
def load(path: str | bytes, *, encoding: str = "utf-8") -> Any:
"""从路径读取并解析 JSONC / JSON。"""
with open(path, encoding=encoding) as fh:
return loads(fh.read())