services/api/src/scripts/inventoryDeletionState.js
Overview
Este archivo define una CLI de inventario administrativo de solo lectura para el estado de eliminaciones de Requirements bajo D-09. Su objetivo es clasificar registros, no recuperarlos, y emitir resultados en jsonl o en un resumen textual. El script se expone como inventory:deletions en package.json y está cubierto por pruebas que verifican que no escribe en MongoDB, no usa $merge ni $out, y no importa dependencias prohibidas
[!IMPORTANT] La ejecución está diseñada para fallar cerrada. Solo acepta una base local autorizada, validada contra
INVENTORY_ALLOWED_DB, y rechaza opciones desconocidas con código 64. También rechaza destinos no permitidos con código 65
[!NOTE] El script no lee
.env. La URI se pasa manualmente enINVENTORY_MONGODB_URI. Además, fijaautoIndex:falseyautoCreate:falseantes de cargar modelos, y conecta condirectConnection:true
How it works
flowchart TD
Start[main]
Parse[parseInventoryArgs]
Usage[io error uso]
Target[assertInventoryTarget]
Refused[io error destino]
Safe[mongoose safe flags]
Import[runDeletionInventory import]
Connect[mongoose connect]
ConnFail[io error conexion]
Hello[hello command]
LocalCheck[assertConnectedServerIsLocal]
LocalRefused[io error destino]
Pseudonym[createPseudonymizer]
Run[runDeletionInventory]
Summary[summaryText]
Record[formatRecord]
Write[io write]
Finish[mongoose disconnect]
Start --> Parse
Parse -->|invalid args| Usage
Parse --> Target
Target -->|rejected| Refused
Target --> Safe
Safe --> Import
Import --> Connect
Connect -->|fail| ConnFail
Connect --> Hello
Hello --> LocalCheck
LocalCheck -->|rejected| LocalRefused
LocalCheck --> Pseudonym
Pseudonym --> Run
Run -->|summary item| Summary
Run -->|regular item| Record
Summary --> Write
Record --> Write
Write --> Finish
La secuencia principal valida argumentos, valida el destino, desactiva la creación automática de índices y colecciones, conecta a MongoDB y verifica que el servidor reportado también sea local. Después, ejecuta runDeletionInventory y formatea cada salida según jsonl o summary
Key components
{
"title": "UsageError",
"content": "Clase de error usada para rechazar opciones de CLI no permitidas o inv\u00e1lidas.",
"type": "class",
"filePath": "services/api/src/scripts/inventoryDeletionState.js",
"badges": [
"public"
]
}
{
"title": "parseInventoryArgs",
"content": "[badge:public] Parsea `argv`, acepta solo las opciones permitidas y valida secci\u00f3n, lote, l\u00edmite y formato.",
"type": "function",
"filePath": "services/api/src/scripts/inventoryDeletionState.js",
"badges": [
"public"
]
}
{
"title": "assertInventoryTarget",
"content": "[badge:public] Envuelve `assertLocalMongoTarget` y aplica la base permitida desde `INVENTORY_ALLOWED_DB`.",
"type": "function",
"filePath": "services/api/src/scripts/inventoryDeletionState.js",
"badges": [
"public"
]
}
{
"title": "createPseudonymizer",
"content": "[badge:public] Crea un pseudonimizador HMAC SHA-256 estable dentro de una corrida o con una clave dada.",
"type": "function",
"filePath": "services/api/src/scripts/inventoryDeletionState.js",
"badges": [
"public"
]
}
{
"title": "formatRecord",
"content": "[badge:public] Aplica la pol\u00edtica de pseudonimizaci\u00f3n y preserva sin cambios los registros `summary` y `error`.",
"type": "function",
"filePath": "services/api/src/scripts/inventoryDeletionState.js",
"badges": [
"public"
]
}
{
"title": "summaryText",
"content": "Convierte el resumen estructurado del inventario en texto legible para salida `summary`.",
"type": "function",
"filePath": "services/api/src/scripts/inventoryDeletionState.js",
"badges": [
"public"
]
}
{
"title": "main",
"content": "[badge:public] Punto de entrada testeable. Coordina validaci\u00f3n, conexi\u00f3n, ejecuci\u00f3n del inventario, salida y c\u00f3digo final.",
"type": "function",
"filePath": "services/api/src/scripts/inventoryDeletionState.js",
"badges": [
"public"
]
}
Data & fields
Environment inputs
[
{
"name": "INVENTORY_MONGODB_URI",
"type": "string",
"required": true,
"description": "URI de MongoDB que el CLI recibe manualmente."
},
{
"name": "INVENTORY_ALLOWED_DB",
"type": "string",
"required": true,
"description": "Nombre de base autorizado para `assertInventoryTarget`."
},
{
"name": "INVENTORY_PSEUDONYM_KEY",
"type": "string",
"description": "Clave opcional para generar pseudónimos estables dentro de una corrida."
}
]
CLI options
[
{
"name": "--section",
"type": "string",
"description": "Lista separada por comas. Acepta `requirements`, `claims`, `conversations` y `shared`."
},
{
"name": "--batch-size",
"type": "number",
"description": "Tamaño de lote entre 1 y 1000. El valor por defecto es 200."
},
{
"name": "--limit",
"type": "number",
"description": "Límite total de elementos a procesar. El valor por defecto es 10000."
},
{
"name": "--format",
"type": "string",
"description": "Formato de salida. Solo acepta `jsonl` o `summary`."
},
{
"name": "--share",
"type": "boolean",
"description": "Activa la pseudonimización adicional de identificadores de listings y conversations."
}
]
Output bootstrap in jsonl
[
{
"name": "kind",
"type": "string",
"required": true,
"description": "Siempre `run` en la primera línea de salida JSONL."
},
{
"name": "runId",
"type": "string",
"required": true,
"description": "UUID de la corrida, añadido a cada línea emitida."
},
{
"name": "schemaVersion",
"type": "number",
"required": true,
"description": "Versión de esquema fija. El código usa `1`."
},
{
"name": "sections",
"type": "string[]",
"required": true,
"description": "Secciones elegidas para el inventario."
},
{
"name": "share",
"type": "boolean",
"required": true,
"description": "Indica si se activó la pseudonimización ampliada."
},
{
"name": "readOnly",
"type": "boolean",
"required": true,
"description": "Marca la corrida como lectura בלבד."
}
]
[
{
"name": "observedAt",
"type": "string",
"required": true,
"description": "Marca temporal que `summaryText` imprime como cabecera."
},
{
"name": "failed",
"type": "boolean",
"required": true,
"description": "Se refleja en la cabecera como `CON ERRORES` si es verdadero."
},
{
"name": "sections",
"type": "object",
"required": true,
"description": "Mapa por sección inventariada.",
"children": [
{
"name": "scanned",
"type": "number",
"required": true,
"description": "Cantidad revisada."
},
{
"name": "emitted",
"type": "number",
"required": true,
"description": "Cantidad emitida."
},
{
"name": "errors",
"type": "number",
"required": true,
"description": "Cantidad de errores observados."
},
{
"name": "limitReached",
"type": "boolean",
"required": true,
"description": "Indica si se alcanzó el límite de trabajo."
},
{
"name": "classifications",
"type": "object",
"required": true,
"description": "Conteo por clasificación observada."
}
]
}
]
Record shaping rules
summaryyerrorse devuelven sin cambios porformatRecord.related.ownerIdse transforma enrelated.ownerRef.media_claimyshared_filesiempre usan pseudónimos de tipofile.- Con
--share, también se pseudonimizanid,related.listingId,related.requirementIdyrelated.listingIds. - Sin
--share, solo se pseudonimizaownerIdy los tipos de archivo mencionados. - El pseudónimo se genera con
HMAC-SHA256y se recorta a 16 hex caracteres.
[!NOTE] El pseudonimizador devuelve
nullsi el valor original esnulloundefined. Eso evita convertir ausencias en cadenas falsas.
Behaviors
EXIT— expone los códigosOK: 0,CONNECTION: 1,INVENTORY_ERRORS: 2,USAGE: 64yTARGET_REFUSED: 65.UsageError— representa errores de uso de la CLI.parseInventoryArgs— acepta solo--section,--batch-size,--limit,--formaty--share.parseInventoryArgs— rechaza cualquier otra opción conUsageError.assertInventoryTarget— valida que la URI apunte a una base local permitida.assertConnectedServerIsLocal— se reexporta desde./lib/localMongoTarget.js.createPseudonymizer— construye un closure de pseudonimización por corrida.formatRecord— aplica la política de ocultación y reescritura de identificadores.summaryText— genera un resumen plano por secciones.main— ejecuta la corrida completa y devuelve el código final.main— recibeioinyectable, lo que facilita pruebas deterministas.main— desconecta siempre enfinally.
Workflows
1. Define el destino | Establece `INVENTORY_MONGODB_URI` y `INVENTORY_ALLOWED_DB`.
2. Elige el alcance | Pasa `--section`, `--batch-size`, `--limit`, `--format` o `--share`.
3. Ejecuta la CLI | Usa `node src/scripts/inventoryDeletionState.js` o `npm run inventory:deletions`.
4. Lee la salida | Consume líneas `jsonl` o el resumen textual y revisa el código de salida.
La secuencia real usa mongoose.connect con directConnection:true, verifica el hello del servidor, y luego consume runDeletionInventory. El script termina con process.exitCode cuando se ejecuta como módulo principal
Gotchas & edge cases
[!WARNING] No existe ninguna opción de escritura. Cualquier opción desconocida, incluida cualquier forma de
apply, se rechaza con código 64. El inventario también puede devolver 65 si el destino no pasa la validación local
[!CAUTION] La validación local no demuestra que la base sea de pruebas. Un túnel local hacia otra base se ve igual desde esta guardia. El código lo reconoce explícitamente en sus comentarios operativos.
[!NOTE] El script no importa servicios de eliminación ni ImageKit. Las pruebas estáticas confirman que tampoco usa métodos de escritura,
$mergeo$outen este flujo
[
{
"title": "0 OK",
"body": "La corrida terminó sin errores de conexión ni de inventario."
},
{
"title": "1 Connection",
"body": "Falló `mongoose.connect` o la conexión no pudo establecerse."
},
{
"title": "2 Inventory errors",
"body": "El inventario se interrumpió o emitió errores durante la ejecución."
},
{
"title": "64 Usage",
"body": "La CLI recibió una opción no permitida o un valor inválido."
},
{
"title": "65 Target refused",
"body": "La URI o el servidor conectado no cumplieron las reglas de destino local."
}
]
Related
Este archivo se conecta con services/api/src/scripts/lib/localMongoTarget.js, que aporta las guardas de destino local y el reexport de assertConnectedServerIsLocal. También depende de services/api/src/modules/reconciliation/deletion-inventory.service.js, que entrega el iterador runDeletionInventory consumido por la CLI
Las pruebas de apoyo están en services/api/test/deletion-inventory-classify.test.js, donde se importan parseInventoryArgs, assertInventoryTarget, createPseudonymizer, formatRecord y main. La validación de no escritura y de imports prohibidos aparece en services/api/test/e6-1-inventory-static.test.js
La exposición operativa también aparece en package.json como inventory:deletions. Esa entrada apunta directamente a este script, sin envoltorios adicionales