Repositorio: https://github.com/MauricioPerera/js-base
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.
Búsqueda semántica real sobre HTTP: un
POST .../searchcon un vector devuelve los documentos rankeados por similitud. Los scores del demo son los que produce el motor (verificados end-to-end).
# 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 :3000Programáticamente:
const { createServer } = require("js-base");
const srv = await createServer({ dataDir: "./data", secret: process.env.SECRET });
await srv.listen(3000);
// ...
await srv.close();| Á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).
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# 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')# 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'# 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}'# 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":{ ... }}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/.
# 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.
- 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.
searchHybridmaterializa 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
loginpersiste una fila en_sessionsy la verificación de token es stateless (valida elexpdel JWT, no la fila); solologoutborra. Las sesiones expiradas no se purgan solas — en modo disco solo uncompact()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.
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,
AuthJWT); vendorizado dentro de js-store. - js-vector-store — el core vectorial (IVF, BM25, híbrida); vendorizado dentro de js-store.
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.
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).
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/andtests/: 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/anddocs/reports/: Project-level execution contracts and their verified, in-repo reports (templates included). Task-level evidence stays local in.agents/logs/; seeknowledge/metodologia-ejecucion.md.scripts/assemble_context.py+ccdd/context.json: budgeted, deterministic context assembler over the OKF knowledge base (CCDD Level 2).
- Use this repository as a "Template" on GitHub or clone it locally.
- Explore
knowledge/index.mdto see how concepts are structured. - When delegating work to an agent (e.g. an AI coding agent), the agent will read
.agents/AGENTS.mdand immediately understand that it must respect the CCDD contracts of this repository. - Drop the example artifacts and rewire
knowledge/index.mdwithpython scripts/init_project.py --apply --name "<Your Project>"(it removessrc/hello.py,src/users.py, the sample tests, and the OKF example nodes; without--applyit only prints the plan).
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, andscripts/init_project.pyremain Python; they validate contracts and produce the gate export regardless of your project's language. - Adapted: each contract's
test_commandmust 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.ymlinstalls 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 --applyremoves 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.
Validation has two levels. Only level 1 is mandatory and is included in the template.
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 inspecs/have machine-checkable acceptance criteria, a perimeter, and abort conditions (open vs. closed bydocs/reports/CONTRACT-NN-REPORT.md).- The
test_commanddeclared 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").
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.
- With CCDD gate available (level 2): the config signed by the gate takes precedence. The frontmatter
budgetcan only be <= the signed limits; on any conflict, the gate's signed config wins. - Without gate (level 1 only): the contract's
budgetis declarative/informative. The included validator only checks its presence in the frontmatter; it does not enforce the limits.
- draft — contract written in
knowledge/contracts/<task>.md. - validated —
python scripts/validate_contracts.py knowledge/contracts(andlint_task_contractif the gate is present) green. - implemented — the contract's
test_commandgreen. - 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.
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).
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/ytests/: 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/ydocs/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/; verknowledge/metodologia-ejecucion.md.scripts/assemble_context.py+ccdd/context.json: ensamblador de contexto presupuestado y determinista sobre la KB OKF (CCDD Nivel 2).
- Usa este repositorio como "Template" en GitHub o clónalo localmente.
- Explora
knowledge/index.mdpara ver cómo se estructuran los conceptos. - Al delegar trabajo a un agente (ej. un agente de IA), el agente leerá
.agents/AGENTS.mdy entenderá inmediatamente que debe respetar los contratos CCDD de este repositorio. - Quita los artefactos de ejemplo y reescribe
knowledge/index.mdconpython scripts/init_project.py --apply --name "<Tu Proyecto>"(eliminasrc/hello.py,src/users.py, los tests de ejemplo y los nodos OKF de ejemplo; sin--applysolo imprime el plan).
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.pyyscripts/init_project.pysiguen siendo Python; validan contratos y generan el export del gate sin importar el lenguaje de tu proyecto. - Se adapta: el
test_commandde 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.ymlinstala 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 --applyborra 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.
La validación tiene dos niveles. Solo el nivel 1 es obligatorio y viene incluido en la plantilla.
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 enspecs/tengan criterios de aceptación verificables por máquina, perímetro y condiciones de aborto (abierto vs. cerrado segúndocs/reports/CONTRACT-NN-REPORT.md).- El
test_commanddeclarado 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").
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.
- Con gate CCDD disponible (nivel 2): la config firmada por el gate manda. El
budgetdel frontmatter solo puede ser <= los topes firmados; ante cualquier conflicto gana la config firmada del gate. - Sin gate (solo nivel 1): el
budgetdel contrato es declarativo/informativo. El validador incluido solo verifica su presencia en el frontmatter; no aplica (enforce) los topes.
- draft — contrato redactado en
knowledge/contracts/<task>.md. - validated —
python scripts/validate_contracts.py knowledge/contracts(ylint_task_contractsi hay gate) en verde. - implemented —
test_commanddel contrato en verde. - 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.
