Skip to content

Repository files navigation

js-base

Repositorio: https://github.com/MauricioPerera/js-base

CI

Backend embebido estilo PocketBase con búsqueda semántica nativa, en JavaScript puro y cero dependencias de runtime. REST + auth (JWT) + reglas de acceso por colección + realtime (SSE) + búsqueda vectorial e híbrida, todo sobre js-store (v0.1.3, vendorizado y congelado en src/vendor/js-store/; su integridad se verifica por hash en CI).

Su diferenciador frente a PocketBase: cada colección puede tener embeddings y responder búsquedas por similitud vectorial o híbrida (vector + BM25) sin ningún servicio externo. Su alcance es un solo proceso (1 escritor + N lectores), pensado para apps chicas/medianas y agentes.

🧩 Ecosistema completo (js-doc-store → js-store → js-base + js-vector-store): docs/ECOSYSTEM.md — qué es cada capa y cuál usar.

📄 Reporte de análisis de la jornada (cubre los tres repos de la cadena js-doc-store → js-store → js-base): docs/reports/ANALISIS-2026-07-06.md.

Demo: búsqueda semántica por HTTP devolviendo resultados rankeados por similitud coseno

Búsqueda semántica real sobre HTTP: un POST .../search con un vector devuelve los documentos rankeados por similitud. Los scores del demo son los que produce el motor (verificados end-to-end).

Correr el servidor

# El SECRET es obligatorio (mínimo 16 chars); sin él el server no arranca.
SECRET="cambia-esto-por-un-secreto-largo" DATA_DIR=./data PORT=3000 npm start
# → js-base escuchando en :3000

Programáticamente:

const { createServer } = require("js-base");
const srv = await createServer({ dataDir: "./data", secret: process.env.SECRET });
await srv.listen(3000);
// ...
await srv.close();

API HTTP

Área Endpoints
Auth POST /api/auth/register · /login · /logout · GET /api/auth/me · POST /api/auth/change-password
Records (CRUD) GET/POST /api/collections/:col/records · GET/PATCH/DELETE /api/collections/:col/records/:id
Files POST/GET/DELETE /api/files/:name
Búsqueda semántica POST /api/collections/:col/vectors · /search · /search/hybrid · DELETE .../vectors/:id · POST .../reindex
Realtime GET /api/realtime/:collection (SSE)

Autenticación por Authorization: Bearer <token>. Las reglas de acceso por colección/operación se evalúan con filtros tipo Mongo sobre { auth, record, request } (deny por defecto; { "auth.id": { "$exists": true } } exige login).

Ejemplos de uso

1. Definir colecciones y arrancar

Las colecciones se declaran por código con CollectionRegistry (no hay API HTTP de schema en el MVP). Este script registra una colección de documentos protegida por login y una colección con embeddings, y deja el server escuchando:

// server.js
const { createServer } = require("js-base");

(async () => {
  const srv = await createServer({ dataDir: "./data", secret: process.env.SECRET });

  // Colección de notas: crear/editar/borrar exige login; lectura pública.
  srv.registry.create({
    name: "notes",
    fields: [
      { name: "title", type: "string", required: true },
      { name: "body", type: "string" },
    ],
    rules: {
      list: null, view: null,
      create: { "auth.id": { $exists: true } },
      update: { "auth.id": { $exists: true } },
      delete: { "auth.id": { $exists: true } },
    },
    vector: null,
  });

  // Colección semántica: documentos con embedding de dimensión 3.
  srv.registry.create({
    name: "docs",
    fields: [{ name: "text", type: "string" }],
    rules: { list: null, view: null, create: null, update: null, delete: null },
    vector: { dim: 3 },
  });

  await srv.listen(3000);
  console.log("listo en :3000");
})();
SECRET="cambia-esto-por-un-secreto-largo" node server.js

2. Auth (registro y login)

# Registro → 201 { "user": { ... } }
curl -s -X POST localhost:3000/api/auth/register \
  -H 'content-type: application/json' \
  -d '{"email":"ada@example.com","password":"contrasena-larga-123"}'

# Login → { "token": "...", "user": { ... } }
TOKEN=$(curl -s -X POST localhost:3000/api/auth/login \
  -H 'content-type: application/json' \
  -d '{"email":"ada@example.com","password":"contrasena-larga-123"}' \
  | sed -n 's/.*"token":"\([^"]*\)".*/\1/p')

3. Records (CRUD con reglas)

# Sin token → 403 (la regla create exige login)
curl -s -o /dev/null -w '%{http_code}\n' -X POST localhost:3000/api/collections/notes/records \
  -H 'content-type: application/json' -d '{"title":"hola"}'          # → 403

# Con token → 201, devuelve el doc creado con su _id
curl -s -X POST localhost:3000/api/collections/notes/records \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"title":"hola","body":"mi primera nota"}'

# Listar (público) → { "page":1, "perPage":30, "totalItems":1, "items":[ ... ] }
curl -s localhost:3000/api/collections/notes/records
# Filtro tipo Mongo + paginación:
curl -s 'localhost:3000/api/collections/notes/records?filter=%7B%22title%22%3A%22hola%22%7D&page=1&perPage=10'

4. Búsqueda semántica (el diferenciador)

# Insertar vectores (dim 3)
curl -s -X POST localhost:3000/api/collections/docs/vectors -H 'content-type: application/json' \
  -d '{"id":"gato","doc":{"text":"felino"},"vector":[1,0,0]}'
curl -s -X POST localhost:3000/api/collections/docs/vectors -H 'content-type: application/json' \
  -d '{"id":"perro","doc":{"text":"canino"},"vector":[0,1,0]}'

# Buscar por similitud → { "items":[ { "id","score","doc" }, ... ] } rankeado
curl -s -X POST localhost:3000/api/collections/docs/search \
  -H 'content-type: application/json' -d '{"vector":[0.9,0.1,0],"limit":2}'
# → el más cercano es "gato"

# Híbrida (vector + BM25 sobre un campo de texto)
curl -s -X POST localhost:3000/api/collections/docs/search/hybrid \
  -H 'content-type: application/json' \
  -d '{"vector":[0.9,0.1,0],"query":"felino","textField":"text","limit":2}'

5. Realtime (SSE)

# En una terminal: suscribirse a los eventos de la colección
curl -N localhost:3000/api/realtime/notes
# Cada create/update/delete en "notes" llega como:
#   event: create
#   data: {"collection":"notes","op":"create","record":{ ... }}

6. Agente con contexto dinámico (capa agent/)

Sobre js-base vive un agente LLM con memoria dinámica (CONTRACT-10/11): log de interacciones y conocimiento curado en colecciones de js-base, contexto ensamblado por slots ordenados por volatilidad (caché KV amigable), corrección por supersede sin editar la historia, compactación con promoción de hechos y régimen esporádico por TTL.

# Demo end-to-end 100% real (requiere Ollama local con embeddinggemma y gemma4):
node examples/agent-demo.js
# → retrieval alimenta la inferencia; una corrección posterior PREVALECE sobre lo
#   dicho antes en el chat; compacta y promueve hechos; sobrevive al reinicio.

Contratos de la capa: knowledge/contracts/agent-*.md; ejecución y evidencia: specs/CONTRACT-10* / CONTRACT-11* con sus reportes en docs/reports/.

Desarrollo

# Suite JS completa (incluye e2e de integración y el harness adversarial)
node --test

# Validadores KDD + suite Python (stdlib — sin npm install)
python scripts/validate_contracts.py knowledge/contracts
python scripts/validate_okf.py knowledge
python -m unittest discover -s tests -p "test_*.py"

Requisitos: Node ≥18, Python 3.11+. No hay npm install: cero dependencias de runtime.

Límites conocidos

  • Un solo proceso (1 escritor + N lectores): no hay multi-escritor ni cluster.
  • Sin ACID multi-documento en disco: las transacciones de js-store son del modo memoria (límite heredado). Operaciones que abarquen varias colecciones no son atómicas.
  • searchHybrid materializa documentos en RAM en modo disco (caveat heredado de js-store): la búsqueda vectorial pura con IVF sí escala en disco.
  • Sin admin UI ni OAuth2 en este MVP (solo email/password).
  • Sin rate limiting ni protección de fuerza bruta: /api/auth/login (y el resto de endpoints) se pueden probar sin límite. Poné un reverse proxy con throttling delante para un despliegue expuesto.
  • Las sesiones crecen sin límite: cada login persiste una fila en _sessions y la verificación de token es stateless (valida el exp del JWT, no la fila); solo logout borra. Las sesiones expiradas no se purgan solas — en modo disco solo un compact() manual recupera el espacio.
  • Files: la lectura (GET) es pública; la escritura y el borrado (POST/DELETE) exigen un usuario autenticado.
  • Las colecciones se definen vía el registro (CollectionRegistry); no hay API de administración de schema por HTTP en este MVP.

Proyectos relacionados

js-base es la capa superior de una cadena de librerías embebidas, cero dependencias, de Mauricio Perera:

  • js-store — la capa doc+vector que js-base vendoriza; provee SemanticCollection, el motor de búsqueda semántica.
  • js-doc-store — el core documental en la base de la cadena (queries Mongo, índices, Auth JWT); vendorizado dentro de js-store.
  • js-vector-store — el core vectorial (IVF, BM25, híbrida); vendorizado dentro de js-store.

Licencia

MIT © Mauricio Perera. js-store (vendorizado en src/vendor/js-store/) se distribuye bajo su propia licencia MIT, incluida en src/vendor/js-store/LICENSE.


Debajo, la metodología KDD con la que se construyó este repo.

English | Español

English

This is a template repository for projects that implement the Knowledge-Driven Development (KDD) methodology, which unifies:

  • OKF (Open Knowledge Format): A minimalist format for structuring knowledge, design, and architecture as markdown files with YAML frontmatter. The normative spec for OKF nodes lives in knowledge/OKF-SPEC.md.
  • CCDD (Contract-Driven Development): A methodology for governing development with ephemeral AI agents through strict contracts and deterministic thresholds (complexity, frozen tests).

Repository Structure

  • knowledge/: Where your OKF knowledge base lives. Every file here is an indexable node.
  • knowledge/contracts/: Where tasks for developers (human or AI) are defined using the hybrid OKF+CCDD format.
  • src/ and tests/: Implementation code and automated tests.
  • scripts/validate_contracts.py: Deterministic contract validator (stdlib, no LLM, no network).
  • .agents/: Local rules for AI agents that clone this repository.
  • specs/ and docs/reports/: Project-level execution contracts and their verified, in-repo reports (templates included). Task-level evidence stays local in .agents/logs/; see knowledge/metodologia-ejecucion.md.
  • scripts/assemble_context.py + ccdd/context.json: budgeted, deterministic context assembler over the OKF knowledge base (CCDD Level 2).

How to use this template

  1. Use this repository as a "Template" on GitHub or clone it locally.
  2. Explore knowledge/index.md to see how concepts are structured.
  3. When delegating work to an agent (e.g. an AI coding agent), the agent will read .agents/AGENTS.md and immediately understand that it must respect the CCDD contracts of this repository.
  4. Drop the example artifacts and rewire knowledge/index.md with python scripts/init_project.py --apply --name "<Your Project>" (it removes src/hello.py, src/users.py, the sample tests, and the OKF example nodes; without --apply it only prints the plan).

Instantiating for a non-Python project

This template's KDD tooling is Python and stays Python even if your project is not — these are two separate planes (template tooling vs. your project's code).

  • Kept unchanged: scripts/validate_contracts.py, scripts/validate_okf.py, scripts/validate_specs.py, scripts/export_gate_contract.py, and scripts/init_project.py remain Python; they validate contracts and produce the gate export regardless of your project's language.
  • Adapted: each contract's test_command must use your language's runner (node --test ..., cargo test ..., etc. — see the multi-language support under Level 2 above). The CI workflow .github/workflows/validate.yml installs Python and runs only the template's Python suite (python -m unittest discover -s tests); if your project adds code/tests in another language, you add an extra CI step (e.g. actions/setup-node + npm test) so that suite also runs in CI. The existing Python step is still required because it validates the KDD tooling itself.
  • Example artifacts: scripts/init_project.py --apply removes Python-written EXAMPLE artifacts (src/hello.py, src/users.py, the sample tests, the example OKF nodes) — they are only illustrative examples of the contract pattern, not a language dependency: they are removed the same way regardless of your project's language, and afterwards you add your own contracts/tests in your language.

Contract Validation

Validation has two levels. Only level 1 is mandatory and is included in the template.

Level 1 — Included and mandatory (local + CI)

  • python scripts/validate_contracts.py knowledge/contracts — validates frontmatter, mandatory sections, and examples of each contract.
  • python scripts/validate_specs.py specs — validates that project-level execution contracts in specs/ have machine-checkable acceptance criteria, a perimeter, and abort conditions (open vs. closed by docs/reports/CONTRACT-NN-REPORT.md).
  • The test_command declared in the contract frontmatter — must finish green.

Both run locally and in CI (.github/workflows/validate.yml runs the validator and python -m unittest discover -s tests -p "test_*.py").

Level 2 — Optional (if the agent environment has it)

If the agent has the ccdd-complexity MCP server available, the real CCDD gate is invoked with its tools lint_task_contract (contract lint) and run_integration_gate (complexity/integration gate). If it is not available, level 1 is sufficient to consider a contract valid.

The gate is multi-language. Python has a complete native signature parser (strictly validated). Other supported languages — JavaScript among them, with measure_complexity coverage that also includes TS/TSX/Rust/Go/Java/C#/PHP at the time of writing (this list is not exhaustive nor fixed; check the real gate for the current list) — route through a tree-sitter backend that applies the same complexity budget (cyclomatic/nesting/params) as Python. The test_command declared in the contract is run verbatim by the gate (the gate executes the declared command, with cwd = the target's directory). Tests must be self-runnable by that test_command; for JavaScript this means ESM (.mjs or "type": "module" in package.json) with a test_command like "node --test <path>". With language set to something other than python, the signature is validated by generic arity (parameter count), not by a native parser of that language — Python is the only one with full signature parsing. scan_dependencies reasons in Python terms (imports/stdlib) and should NOT be used as part of the gate for non-Python languages.

The gate runs over the export produced by scripts/export_gate_contract.py (ASCII normalization + target/tests rewritten relative to the export): lint_task_contract takes the export text + tests, and run_integration_gate takes the export path on disk. By default the export is written to the repo root as <task>.gate.md (gitignored via *.gate.md) so the rewritten paths have no .., which the real gate requires.

Budget Precedence

  • With CCDD gate available (level 2): the config signed by the gate takes precedence. The frontmatter budget can only be <= the signed limits; on any conflict, the gate's signed config wins.
  • Without gate (level 1 only): the contract's budget is declarative/informative. The included validator only checks its presence in the frontmatter; it does not enforce the limits.

Contract Lifecycle

  1. draft — contract written in knowledge/contracts/<task>.md.
  2. validatedpython scripts/validate_contracts.py knowledge/contracts (and lint_task_contract if the gate is present) green.
  3. implemented — the contract's test_command green.
  4. verified — the REAL output of the commands (validator + test_command, and gate if it runs) is pasted into .agents/logs/<task>-REPORT.md. That directory is gitignored on purpose: it is local evidence, not part of the repo.

Español

Este repositorio plantilla es para proyectos que implementan la metodología Knowledge-Driven Development (KDD), la cual unifica:

  • OKF (Open Knowledge Format): Un formato minimalista para estructurar el conocimiento, diseño y arquitectura como archivos markdown con frontmatter YAML. La spec normativa de los nodos OKF está en knowledge/OKF-SPEC.md.
  • CCDD (Contract-Driven Development): Una metodología para gobernar el desarrollo con agentes de IA efímeros mediante contratos estrictos y umbrales deterministas (complejidad, tests congelados).

Estructura del Repositorio

  • knowledge/: Aquí vive tu base de conocimiento OKF. Todo archivo aquí es un nodo indexable.
  • knowledge/contracts/: Donde se definen las tareas para los desarrolladores (humanos o IA) usando el formato híbrido OKF+CCDD.
  • src/ y tests/: Código de implementación y pruebas automatizadas.
  • scripts/validate_contracts.py: Validador determinista de contratos (stdlib, sin LLM, sin red).
  • .agents/: Reglas locales para agentes de IA que clonen este repositorio.
  • specs/ y docs/reports/: contratos de ejecución de nivel proyecto y sus reportes verificados en-repo (plantillas incluidas). La evidencia de tarea sigue siendo local en .agents/logs/; ver knowledge/metodologia-ejecucion.md.
  • scripts/assemble_context.py + ccdd/context.json: ensamblador de contexto presupuestado y determinista sobre la KB OKF (CCDD Nivel 2).

Cómo usar esta plantilla

  1. Usa este repositorio como "Template" en GitHub o clónalo localmente.
  2. Explora knowledge/index.md para ver cómo se estructuran los conceptos.
  3. Al delegar trabajo a un agente (ej. un agente de IA), el agente leerá .agents/AGENTS.md y entenderá inmediatamente que debe respetar los contratos CCDD de este repositorio.
  4. Quita los artefactos de ejemplo y reescribe knowledge/index.md con python scripts/init_project.py --apply --name "<Tu Proyecto>" (elimina src/hello.py, src/users.py, los tests de ejemplo y los nodos OKF de ejemplo; sin --apply solo imprime el plan).

Instanciar para un proyecto no-Python

El tooling KDD de esta plantilla es Python y sigue siéndolo aunque tu proyecto no lo sea — son dos planos distintos (tooling de la plantilla vs. código de tu proyecto).

  • Se conserva sin cambios: scripts/validate_contracts.py, scripts/validate_okf.py, scripts/validate_specs.py, scripts/export_gate_contract.py y scripts/init_project.py siguen siendo Python; validan contratos y generan el export del gate sin importar el lenguaje de tu proyecto.
  • Se adapta: el test_command de cada contrato debe usar el runner de tu lenguaje (node --test ..., cargo test ..., etc. — ver el soporte multi-lenguaje del Nivel 2 arriba). El workflow de CI .github/workflows/validate.yml instala Python y corre solo la suite Python de la plantilla (python -m unittest discover -s tests); si tu proyecto agrega código/tests en otro lenguaje, agregas un paso de CI adicional (ej. actions/setup-node + npm test) para que esa suite también corra en CI. El paso Python existente sigue siendo necesario porque valida el tooling KDD mismo.
  • Artefactos de ejemplo: scripts/init_project.py --apply borra artefactos de EJEMPLO escritos en Python (src/hello.py, src/users.py, los tests de ejemplo, los nodos OKF de ejemplo) — son solo ejemplos ilustrativos del patrón de contratos, no una dependencia de lenguaje: se borran igual sin importar el lenguaje de tu proyecto, y después agregas tus propios contratos/tests en tu lenguaje.

Validación de Contratos

La validación tiene dos niveles. Solo el nivel 1 es obligatorio y viene incluido en la plantilla.

Nivel 1 — Incluido y obligatorio (local + CI)

  • python scripts/validate_contracts.py knowledge/contracts — valida frontmatter, secciones obligatorias y examples de cada contrato.
  • python scripts/validate_specs.py specs — valida que los contratos de ejecución de nivel proyecto en specs/ tengan criterios de aceptación verificables por máquina, perímetro y condiciones de aborto (abierto vs. cerrado según docs/reports/CONTRACT-NN-REPORT.md).
  • El test_command declarado en el frontmatter del contrato — debe terminar en verde.

Ambos corren localmente y en CI (.github/workflows/validate.yml ejecuta el validador y python -m unittest discover -s tests -p "test_*.py").

Nivel 2 — Opcional (si el entorno del agente lo tiene)

Si el agente dispone del servidor MCP ccdd-complexity, el gate CCDD real se invoca con sus tools lint_task_contract (lint del contrato) y run_integration_gate (gate de complejidad/integración). Si no está disponible, el nivel 1 es suficiente para considerar un contrato válido.

El gate es multi-lenguaje. Python tiene un parser de firma nativo completo (validado estrictamente). Otros lenguajes soportados — JavaScript entre ellos, con cobertura de measure_complexity que además incluye TS/TSX/Rust/Go/Java/C#/PHP a la fecha (la lista no es exhaustiva ni fija; consulta el gate real para la lista vigente) — enrutan a un backend tree-sitter que aplica el mismo budget de complejidad (cyclomatic/nesting/params) que Python. El test_command declarado en el contrato se corre verbatim (el gate ejecuta el comando declarado, con cwd = directorio del target). Los tests deben ser auto-ejecutables por ese test_command; para JavaScript esto implica ESM (.mjs o "type": "module" en package.json) con un test_command como "node --test <ruta>". Con language distinto de python, la signature se valida por aridad genérica (cantidad de parámetros), no con un parser nativo de ese lenguaje — Python es el único con parsing de firma completo. scan_dependencies razona en clave Python (imports/stdlib) y NO debe usarse como parte del gate para lenguajes no-Python.

El gate se corre sobre el export generado por scripts/export_gate_contract.py (normalización ASCII + target/tests reescritos relativos al export): lint_task_contract recibe el texto del export + tests, y run_integration_gate recibe la ruta del export en disco. Por defecto el export se escribe en la raíz del repo como <task>.gate.md (gitignorado vía *.gate.md) para que las rutas reescritas no contengan .., como exige el gate real.

Precedencia del Budget

  • Con gate CCDD disponible (nivel 2): la config firmada por el gate manda. El budget del frontmatter solo puede ser <= los topes firmados; ante cualquier conflicto gana la config firmada del gate.
  • Sin gate (solo nivel 1): el budget del contrato es declarativo/informativo. El validador incluido solo verifica su presencia en el frontmatter; no aplica (enforce) los topes.

Ciclo de Vida del Contrato

  1. draft — contrato redactado en knowledge/contracts/<task>.md.
  2. validatedpython scripts/validate_contracts.py knowledge/contracts (y lint_task_contract si hay gate) en verde.
  3. implementedtest_command del contrato en verde.
  4. verified — la salida REAL de los comandos (validador + test_command, y gate si corre) se pega en .agents/logs/<task>-REPORT.md. Ese directorio está gitignorado a propósito: es evidencia local, no parte del repo.

About

Backend embebido estilo PocketBase con búsqueda semántica nativa (vectorial + híbrida BM25), auth JWT, reglas por colección y realtime SSE — JavaScript puro, cero dependencias de runtime, sobre js-store.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages