@opensas/xml-rest-adapter 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,3 +2,9 @@
2
2
 
3
3
  Proyecto _de uso interno_ que expone mediante una API REST las bases de datos que utilizan el
4
4
  formato propio de `xmlEntidad_Accion` para encapsular la lógica de negocio.
5
+
6
+ npm: https://www.npmjs.com/package/@opensas/xml-rest-adapter
7
+
8
+ ```bash
9
+ pnpm add @opensas/xml-rest-adapter
10
+ ```
@@ -1,8 +1,7 @@
1
- import type { RequestEvent } from '@sveltejs/kit';
2
1
  import type { AppError } from './errors.js';
3
2
  import type { Result } from './result.js';
4
3
  import type { Aplicacion, Usuario } from './types.js';
5
4
  export declare function createXmlRestAdapter(app: Aplicacion): {
6
5
  resolveUser: (identity: string | Request) => Promise<Result<Usuario, AppError>>;
7
- handle: (event: RequestEvent, usuario: Usuario) => Promise<Response>;
6
+ handle: (request: Request, path: string, usuario: Usuario) => Promise<Response>;
8
7
  };
@@ -1,7 +1,19 @@
1
- import { json } from '@sveltejs/kit';
1
+ // createXmlRestAdapter(app) — la función pública principal de la librería. Valida la Aplicacion
2
+ // configurada y devuelve un adapter con dos métodos: `resolveUser(domainUser)` (identidad +
3
+ // permisos, ver auth.ts) y `handle(request, path, usuario)`, que atiende cualquier request
4
+ // GET/POST/PUT/DELETE contra esa app — juntos reemplazan `authUser` (hooks.server.ts) y los 4
5
+ // handlers que hoy viven repartidos en dos `+server.ts` distintos en convenios_web
6
+ // (routes/api/[aplicacion]/[objeto]/+server.ts y .../[objeto]/[id]/+server.ts). `usuario` se pasa
7
+ // separado, no vía `event.locals`: no le impone al consumidor que cablee nada en su propio
8
+ // `hooks.server.ts` (ver docs/proximos-pasos.md). `request`/`path` sueltos, no un objeto tipo
9
+ // `RequestEvent` — `handle` nunca necesitó más que estos dos datos, y un objeto `{params,request}`
10
+ // es la forma que le da SvelteKit a su evento, no un estándar: `Request` (Fetch API) sí lo es,
11
+ // `path` es un `string` plano porque nunca hay más de un segmento que leer (ver
12
+ // docs/proximos-pasos.md, "agnóstico de framework").
2
13
  import { resolveUser } from './auth.js';
3
14
  import { execXmlConsulta, execXmlSentencia } from './db.js';
4
15
  import { appError, jsonError } from './errors.js';
16
+ import { json } from './json.js';
5
17
  import { parseRequest } from './parse-request.js';
6
18
  function validarAplicacion(app) {
7
19
  if (!app.name)
@@ -55,12 +67,9 @@ export function createXmlRestAdapter(app) {
55
67
  function resolveUserFn(identity) {
56
68
  return resolveUser(app, identity);
57
69
  }
58
- async function handle(event, usuario) {
70
+ async function handle(request, path, usuario) {
59
71
  try {
60
- // Genérico a propósito: cada consumidor define su propia carpeta `[...path]`, así que
61
- // el nombre real del param lo tipa `$types` del lado del consumidor, no acá.
62
- const path = event.params.path ?? '';
63
- const parsed = await parseRequest(app, event.request, path, usuario);
72
+ const parsed = await parseRequest(app, request, path, usuario);
64
73
  if (!parsed.ok)
65
74
  return jsonError(parsed.error);
66
75
  const { objeto, id, sentencia, archivos, archivoParams } = parsed.data;
package/dist/errors.js CHANGED
@@ -1,8 +1,8 @@
1
1
  // Error de negocio estructurado y homogéneo para toda la librería. Se arma con `appError(...)`
2
2
  // (título + código + detalle opcional) y se devuelve al cliente con `jsonError(...)`, que lo
3
3
  // envuelve en un `Response` JSON con el status HTTP que corresponde al código.
4
- import { json } from '@sveltejs/kit';
5
4
  import { BAD_REQUEST, FORBIDDEN, INTERNAL_ERROR, NOT_FOUND, UNAUTHORIZED, UNPROCESSABLE_ENTITY, } from './http-status.js';
5
+ import { json } from './json.js';
6
6
  export const APP_ERROR_CODES = [
7
7
  'VALIDATION_ERROR',
8
8
  'BAD_REQUEST',
package/dist/index.d.ts CHANGED
@@ -4,5 +4,5 @@ export { getDomainUser } from './auth.js';
4
4
  export { appError, jsonError, type AppError, type AppErrorCode } from './errors.js';
5
5
  export { closeAllPools } from './db.js';
6
6
  export type { Result } from './result.js';
7
- export type { Accion, Aplicacion, Authentication, Authorization, Endpoint, Permisos, Usuario, } from './types.js';
7
+ export type { Accion, Aplicacion, Authentication, Authorization, Endpoint, HandleEvent, Permisos, Usuario, } from './types.js';
8
8
  export type { DbInfo } from './db.js';
package/dist/json.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function json(data: unknown, init?: ResponseInit): Response;
package/dist/json.js ADDED
@@ -0,0 +1,19 @@
1
+ // Reimplementación mínima de `json()` de `@sveltejs/kit` — evita que la librería dependa de
2
+ // `@sveltejs/kit` en runtime (ver docs/proximos-pasos.md, "peerDependencies real vs. de tipos"):
3
+ // antes, `import { json } from '@sveltejs/kit'` en errors.ts/create-adapter.ts obligaba a instalar
4
+ // el paquete completo (y, por peerDependency, arrastraba `svelte`) aunque el consumidor no use
5
+ // SvelteKit para nada — el propio `RequestEvent` que sí necesita la librería es solo un tipo,
6
+ // borrado en tiempo de compilación. Comportamiento idéntico al original (mismo orden de headers,
7
+ // mismo default de `content-type`/`content-length` solo si el caller no los puso ya).
8
+ const textEncoder = new TextEncoder();
9
+ export function json(data, init) {
10
+ const body = JSON.stringify(data);
11
+ const headers = new Headers(init?.headers);
12
+ if (!headers.has('content-length')) {
13
+ headers.set('content-length', textEncoder.encode(body).byteLength.toString());
14
+ }
15
+ if (!headers.has('content-type')) {
16
+ headers.set('content-type', 'application/json');
17
+ }
18
+ return new Response(body, { ...init, headers });
19
+ }
package/dist/types.d.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  import type { DbInfo } from './db.js';
2
+ export type HandleEvent = {
3
+ params: Record<string, string | undefined>;
4
+ request: Request;
5
+ };
2
6
  export type Accion = 'consulta' | 'alta' | 'baja' | 'modificacion';
3
7
  export type Permisos = Record<string, Accion[]>;
4
8
  export type Usuario = {
package/dist/valid.js CHANGED
@@ -23,7 +23,25 @@ function toPascalCase(str) {
23
23
  })
24
24
  .join('');
25
25
  }
26
- const getEndpoint = (app, objeto) => app.endpoints.find((ep) => ep.name === toPascalCase(objeto));
26
+ // Match exacto primero. Si falla y el segmento tiene un separador `_` explícito, reintenta
27
+ // case-insensitive: un `Endpoint.name` con un acrónimo en mayúsculas (`ActividadCIIU`,
28
+ // `CategoriaCIIU`) nunca coincide con lo que arma `toPascalCase` a partir del segmento de URL
29
+ // (`actividad_ciiu` → `ActividadCiiu`, con "Ciiu" no "CIIU") — ningún algoritmo genérico de
30
+ // snake_case→PascalCase puede adivinar qué partes de un nombre compuesto son sigla.
31
+ // convenios_web nunca tuvo este problema porque su comparación (`isValidObjeto`) ya era
32
+ // case-insensitive — portado acá, pero acotado al caso con separador: sin `_` en el segmento
33
+ // mantenemos el exacto a propósito (`tipodato`, sin límite de palabra explícito, sigue sin
34
+ // matchear `TipoDato` — ver custom-url-casing.fixture.test.ts, cualquier coincidencia ahí sería
35
+ // casualidad, no una reconstrucción real). Encontrado migrando `CctDigitalizacion` en
36
+ // convenios_web (ver docs/todos/rest-baseline-pre-migracion.md).
37
+ const getEndpoint = (app, objeto) => {
38
+ const pascal = toPascalCase(objeto);
39
+ const exact = app.endpoints.find((ep) => ep.name === pascal);
40
+ if (exact || !objeto.includes('_'))
41
+ return exact;
42
+ const normalized = pascal.toLowerCase();
43
+ return app.endpoints.find((ep) => ep.name.toLowerCase() === normalized);
44
+ };
27
45
  export function validParams(app, path, withId = 'noId') {
28
46
  const [objetoRaw, idRaw] = path.split('/');
29
47
  if (!objetoRaw)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opensas/xml-rest-adapter",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -33,7 +33,7 @@
33
33
  "fixture:purge": "docker compose -f docker/fixture/docker-compose.yaml down -v",
34
34
  "fixture:start": "docker compose -f docker/fixture/docker-compose.yaml up -d",
35
35
  "fixture:stop": "docker compose -f docker/fixture/docker-compose.yaml stop",
36
- "fixture:status": "docker ps --filter name=fixture-db --format 'table {{.Names}}\\t{{.Status}}'",
36
+ "fixture:status": "docker ps --filter name=xml-rest-adapter-fixture-db --format 'table {{.Names}}\\t{{.Status}}'",
37
37
  "gitlab-runner:start": "sudo gitlab-runner start",
38
38
  "gitlab-runner:stop": "sudo gitlab-runner stop",
39
39
  "gitlab-runner:status": "sudo gitlab-runner status",
@@ -47,20 +47,14 @@
47
47
  "sideEffects": [
48
48
  "**/*.css"
49
49
  ],
50
- "svelte": "./dist/index.js",
51
50
  "types": "./dist/index.d.ts",
52
51
  "type": "module",
53
52
  "exports": {
54
53
  ".": {
55
54
  "types": "./dist/index.d.ts",
56
- "svelte": "./dist/index.js",
57
55
  "default": "./dist/index.js"
58
56
  }
59
57
  },
60
- "peerDependencies": {
61
- "@sveltejs/kit": "^2.0.0",
62
- "svelte": "^5.0.0"
63
- },
64
58
  "devDependencies": {
65
59
  "@eslint/js": "^10.0.1",
66
60
  "@playwright/test": "^1.62.1",