feat: 初始提交

This commit is contained in:
2026-05-26 18:19:42 +08:00
commit 7ebf5ee5dc
107 changed files with 9317 additions and 0 deletions

View File

@@ -0,0 +1,120 @@
import type { TSchema } from "@sinclair/typebox";
import { Type } from "@sinclair/typebox";
import { variableValueSchema } from "./fragments";
type SchemaKind = "authoring" | "normalized";
const LOG_LEVELS = ["trace", "debug", "info", "warn", "error", "fatal"] as const;
const ROTATION_FREQUENCIES = ["hourly", "daily", "weekly"] as const;
const sizeSchema = Type.Union([Type.String(), Type.Integer({ minimum: 0 })]);
export function createAuthoringConfigSchema(): TSchema {
return createConfigSchemaForKind("authoring");
}
export function createExternalConfigSchema(): Record<string, unknown> {
return {
...cloneSchema(createAuthoringConfigSchema()),
$id: "https://app.local/config.schema.json",
$schema: "http://json-schema.org/draft-07/schema#",
};
}
export function createNormalizedConfigSchema(): TSchema {
return createConfigSchemaForKind("normalized");
}
function cloneSchema(schema: TSchema): Record<string, unknown> {
return JSON.parse(JSON.stringify(schema)) as Record<string, unknown>;
}
function createAuthoringFieldSchema(schema: TSchema): TSchema {
return Type.Unsafe({ anyOf: [schema, { pattern: "^\\$\\{[^}]+\\}$", type: "string" }] });
}
function createConfigSchemaForKind(kind: SchemaKind): TSchema {
const properties: Record<string, TSchema> = {
server: Type.Optional(createServerSchema(kind)),
};
if (kind === "authoring") {
properties["variables"] = Type.Optional(
Type.Record(Type.String({ pattern: "^[a-zA-Z_][a-zA-Z0-9_]*$" }), variableValueSchema),
);
}
return Type.Object(properties, { additionalProperties: false });
}
function createLoggingSchema(kind: SchemaKind): TSchema {
const logLevelSchema = Type.Union(LOG_LEVELS.map((l) => Type.Literal(l)) as unknown as [TSchema, ...TSchema[]]);
const logLevel = kind === "authoring" ? createAuthoringFieldSchema(logLevelSchema) : logLevelSchema;
const frequency =
kind === "authoring"
? createAuthoringFieldSchema(
Type.Union(ROTATION_FREQUENCIES.map((f) => Type.Literal(f)) as unknown as [TSchema, ...TSchema[]]),
)
: Type.Union(ROTATION_FREQUENCIES.map((f) => Type.Literal(f)) as unknown as [TSchema, ...TSchema[]]);
const rotationSize = kind === "authoring" ? createAuthoringFieldSchema(sizeSchema) : sizeSchema;
const rotationMaxFiles =
kind === "authoring" ? createAuthoringFieldSchema(Type.Integer({ minimum: 1 })) : Type.Integer({ minimum: 1 });
return Type.Object(
{
console: Type.Optional(Type.Object({ level: Type.Optional(logLevel) }, { additionalProperties: false })),
file: Type.Optional(
Type.Object(
{
level: Type.Optional(logLevel),
path: Type.Optional(Type.String({ minLength: 1 })),
rotation: Type.Optional(
Type.Object(
{
frequency: Type.Optional(frequency),
maxFiles: Type.Optional(rotationMaxFiles),
size: Type.Optional(rotationSize),
},
{ additionalProperties: false },
),
),
},
{ additionalProperties: false },
),
),
level: Type.Optional(logLevel),
},
{ additionalProperties: false },
);
}
function createServerSchema(kind: SchemaKind): TSchema {
return Type.Object(
{
listen: Type.Optional(
Type.Object(
{
host: Type.Optional(Type.String()),
port: Type.Optional(integerForKind(kind, { maximum: 65535, minimum: 0 })),
},
{ additionalProperties: false },
),
),
logging: Type.Optional(createLoggingSchema(kind)),
storage: Type.Optional(
Type.Object(
{
dataDir: Type.Optional(Type.String()),
},
{ additionalProperties: false },
),
),
},
{ additionalProperties: false },
);
}
function integerForKind(kind: SchemaKind, options?: Parameters<typeof Type.Integer>[0]): TSchema {
const schema = Type.Integer(options);
return kind === "authoring" ? createAuthoringFieldSchema(schema) : schema;
}