Compare commits

...
3 Commits
Author SHA1 Message Date
apairon f26f8c7802 feat(schema): add tool schema, demo and admin tool types
continuous-integration/drone/push Build is passing
2026-10-20 22:56:04 +00:00
apairon 439c8773e6 feat(schema): add custom tools config and runtime types
continuous-integration/drone/push Build is passing
2026-10-20 20:45:19 +00:00
apairon 21472bdc72 Schema: ngram.sizes auf 2–10 begrenzen, combinedCache ergänzen
continuous-integration/drone/push Build is passing
Die Gesamtabnahme fand zwei Lücken im Config-Vertrag:

- ngram.sizes erlaubte minimum 1 und hatte kein maximum; der Server akzeptiert
  nur 2–10 (collection_setup.go) — das Schema ließ also Configs durch, die beim
  Projektload scheitern.
- combinedCache fehlte komplett, obwohl Server (models.CombinedCacheConfig) und
  Doku den Block als Projekt-YAML führen; additionalProperties:false lehnte
  gültige, dokumentierte Configs ab.

Ergänzt in collection.schema.json und index.d.ts (CombinedCacheConfig mit
enabled/ttl/maxEntries). Die Demo-Collection ngram-stopwords.yml enthält jetzt
eine Combined-Config mit combinedCache, sodass validate:schemas den Block
abdeckt. Gegenprobe per AJV: sizes [1] und [11] werden abgelehnt, [2,3] und
combinedCache akzeptiert.
2026-10-19 22:34:56 +00:00
6 changed files with 272 additions and 1 deletions

No files matched your search

+11
View File
@@ -30,3 +30,14 @@ search:
enabled: true
maxDocs: 150000
maxMemoryMB: 512
- name: combined
mode: combined
rrf:
k: 60
topK: 100
weights:
fuzzy: 1.0
combinedCache:
enabled: true
ttl: 45s
maxEntries: 1000
+12
View File
@@ -4,6 +4,18 @@ namespace: myapi
meta:
# wird in der Admin-UI zum Projekt aufgegeben
imageUrl: https://source.unsplash.com/random/?internet
tools:
- name: demo-overview
label:
de: Demo-Übersicht
en: Demo overview
description:
de: Beispiel-Tool für die Projekt-Übersicht
en: Example project overview tool
icon: view-dashboard-outline
group: tools
module: _/assets/tools/demo-overview/index.js
capabilities: [db, ui, log]
# Liste aller möglichen Kollektionen (Listen, Seiten...) zum Projekt
collections:
Vendored
+123
View File
@@ -90,6 +90,12 @@ declare global {
minMatchRatio?: number
}
export interface CombinedCacheConfig {
enabled?: boolean
ttl?: string
maxEntries?: number
}
export interface NgramInMemoryConfig {
enabled?: boolean
maxDocs?: number
@@ -193,6 +199,8 @@ declare global {
filter?: { [key: string]: any }
/** Ngram search configuration */
ngram?: NgramConfig
/** Result cache for combined searches */
combinedCache?: CombinedCacheConfig
/** Regex search configuration */
regex?: RegexConfig
/** Custom scoring for eval/filter search */
@@ -585,6 +593,121 @@ declare global {
[key: string]: any
}
// ── Custom Tools (Admin) ─────────────────────────────────────────────────
/** Project config entry (`meta.tools[]`). */
export interface AdminToolConfig {
name: string
label?: string | { [lang: string]: string }
description?: string | { [lang: string]: string }
icon?: string
group?: string
module: string | { eval: string }
permissions?: string[]
capabilities?: Array<"db" | "files" | "xlsx" | "ui" | "log" | "progress" | "state">
settings?: Record<string, any>
hidden?: boolean
}
/** File handle handed to tools (data URL, no live Blob). */
export interface AdminToolFile {
src: string
name: string
type?: string
size?: number
}
/** Fail-fast batch result; no rollback. */
export interface AdminToolBatchResult {
ok: boolean
failedIndex?: number
error?: unknown
}
export interface AdminToolDb {
find(
collection: string,
opts?: Record<string, any>
): Promise<CollectionDocument[]>
count(collection: string, opts?: Record<string, any>): Promise<number>
loadAll(
collection: string,
opts?: Record<string, any>,
onProgress?: (done: number, total: number) => void
): Promise<CollectionDocument[]>
create(collection: string, data: Record<string, any>): Promise<CollectionDocument>
update(collection: string, id: string, data: Record<string, any>): Promise<Partial<CollectionDocument>>
delete(collection: string, id: string): Promise<void>
updateMany(collection: string, items: Array<{ id: string; data: Record<string, any> }>): Promise<AdminToolBatchResult>
deleteMany(collection: string, ids: string[]): Promise<AdminToolBatchResult>
}
export interface AdminToolUi {
supports(type: string): boolean
slot(el: HTMLElement, element: any): void
styles: CSSStyleSheet[]
registerWidget(type: string, factory: (...args: any[]) => any, opts?: { replace?: boolean }): () => void
render(el: HTMLElement, widget: Record<string, any>): void | (() => void)
field(opts: Record<string, any>): any
form(opts: Record<string, any>): any
[key: string]: any
}
export interface AdminToolI18n {
lang: string
languages: string[]
t(key: string, vars?: Record<string, unknown>): string
text(value: string | Record<string, string> | undefined | null, vars?: Record<string, unknown>): string
has(key: string): boolean
add(lang: string, messages: Record<string, unknown>): () => void
add(messages: Record<string, unknown>, lang?: string): () => void
extend(lang: string, messages: Record<string, unknown>): () => void
extend(messages: Record<string, unknown>, lang?: string): () => void
onChange(cb: (lang: string) => void): () => void
dispose?(): void
}
/** Browser context handed to a tool module. `context === ctx`. */
export interface AdminToolContext {
context: AdminToolContext
apiVersion: 1
uiVersion: 1
host: "tool" | "macro" | "server"
capabilities: string[]
project: ProjectInfo
tool: AdminToolConfig
auth: { id: string; username: string; role: number; permissions: string[] } | null
db: AdminToolDb
files: { get(input: File | Blob | string, name?: string): Promise<AdminToolFile> }
xlsx: any
state: Map<string, unknown>
lang: string
languages: string[]
i18n: AdminToolI18n
theme: { mode: "light" | "dark"; tokens: Record<string, string> }
log(message: string, details?: unknown): void
progress(done: number, total: number, label?: string): void
signal: AbortSignal
hostData: Record<string, any>
asset(specifier: string): string
ui: AdminToolUi
}
export interface AdminToolModule {
mount(
el: HTMLElement,
ctx: AdminToolContext
): void | (() => void) | Promise<void | (() => void)>
messages?: Record<string, Record<string, any>>
}
export interface AdminToolException {
status?: number
msg: string
json?: any
}
/** Represents a single audit log entry as received in audit.return hooks */
export interface AuditLogEntry {
id?: string
+18 -1
View File
@@ -321,7 +321,8 @@
"type": "array",
"items": {
"type": "integer",
"minimum": 1
"minimum": 2,
"maximum": 10
}
},
"normalize": {
@@ -387,6 +388,22 @@
}
}
},
"combinedCache": {
"type": "object",
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean"
},
"ttl": {
"type": "string"
},
"maxEntries": {
"type": "integer",
"minimum": 0
}
}
},
"regex": {
"type": "object",
"additionalProperties": false,
+29
View File
@@ -65,6 +65,21 @@
]
}
},
"ui": {
"type": "object",
"additionalProperties": false,
"properties": {
"css": {
"type": "string"
}
}
},
"tools": {
"type": "array",
"items": {
"$ref": "tool.schema.json"
}
},
"i18n": {
"type": "object",
"additionalProperties": false,
@@ -89,6 +104,20 @@
"code"
]
}
},
"messages": {
"type": "object",
"additionalProperties": {
"oneOf": [
{
"type": "string"
},
{
"type": "object",
"additionalProperties": true
}
]
}
}
},
"required": [
+79
View File
@@ -0,0 +1,79 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Custom Tool",
"description": "Project custom tool entry (meta.tools[]).",
"type": "object",
"additionalProperties": false,
"required": [
"name",
"module"
],
"properties": {
"name": {
"type": "string",
"pattern": "^[a-z0-9][a-z0-9-_]*$"
},
"label": {
"oneOf": [
{
"type": "string"
},
{
"$ref": "defs.schema.json#/definitions/i18nText"
}
]
},
"description": {
"oneOf": [
{
"type": "string"
},
{
"$ref": "defs.schema.json#/definitions/i18nText"
}
]
},
"icon": {
"type": "string"
},
"group": {
"type": "string"
},
"module": {
"oneOf": [
{
"type": "string"
},
{
"$ref": "defs.schema.json#/definitions/evalString"
}
]
},
"permissions": {
"type": "array",
"items": {
"type": "string"
}
},
"capabilities": {
"type": "array",
"items": {
"enum": [
"db",
"files",
"xlsx",
"ui",
"log",
"progress",
"state"
]
}
},
"settings": {
"type": "object"
},
"hidden": {
"type": "boolean"
}
}
}