@jeff-aporta/apiservers 0.1.0
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.
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export declare const ISS_LIST_FILTER_QUERY_PARAM = "f";
|
|
2
|
+
export interface IIssListFilter {
|
|
3
|
+
search?: string;
|
|
4
|
+
limit?: number;
|
|
5
|
+
offset?: number;
|
|
6
|
+
eq?: Record<string, string | number | boolean>;
|
|
7
|
+
sort?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const ISS_LIST_FILTER_DEFAULT_LIMIT = 25;
|
|
10
|
+
export declare const ISS_LIST_FILTER_MAX_LIMIT = 100;
|
|
11
|
+
export declare const ISS_LIST_FILTER_SCHEMA: {
|
|
12
|
+
readonly type: "object";
|
|
13
|
+
readonly description: "Filtro ISS para listados (query `f` = JSON en Base64).";
|
|
14
|
+
readonly properties: {
|
|
15
|
+
readonly search: {
|
|
16
|
+
readonly type: "string";
|
|
17
|
+
readonly description: "Texto libre; el recurso define en qué columnas busca.";
|
|
18
|
+
};
|
|
19
|
+
readonly limit: {
|
|
20
|
+
readonly type: "integer";
|
|
21
|
+
readonly minimum: 1;
|
|
22
|
+
readonly maximum: 100;
|
|
23
|
+
readonly default: 25;
|
|
24
|
+
};
|
|
25
|
+
readonly offset: {
|
|
26
|
+
readonly type: "integer";
|
|
27
|
+
readonly minimum: 0;
|
|
28
|
+
readonly default: 0;
|
|
29
|
+
};
|
|
30
|
+
readonly eq: {
|
|
31
|
+
readonly type: "object";
|
|
32
|
+
readonly additionalProperties: {
|
|
33
|
+
readonly oneOf: readonly [{
|
|
34
|
+
readonly type: "string";
|
|
35
|
+
}, {
|
|
36
|
+
readonly type: "number";
|
|
37
|
+
}, {
|
|
38
|
+
readonly type: "boolean";
|
|
39
|
+
}];
|
|
40
|
+
};
|
|
41
|
+
readonly description: "Filtros de igualdad exacta (AND). Claves permitidas dependen del recurso.";
|
|
42
|
+
};
|
|
43
|
+
readonly sort: {
|
|
44
|
+
readonly type: "string";
|
|
45
|
+
readonly description: "Campo de orden. Prefijo `-` = descendente.";
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
readonly additionalProperties: false;
|
|
49
|
+
};
|
|
50
|
+
export declare function encodeIssListFilterB64(filter: Record<string, unknown> | IIssListFilter): string;
|
|
51
|
+
export declare function decodeIssListFilterB64(raw: string): string;
|
|
52
|
+
export declare function parseIssListFilterFromQuery(raw: string | null | undefined): IIssListFilter;
|
|
53
|
+
export declare function parseIssListFilter(raw: string | null | undefined): IIssListFilter;
|
|
54
|
+
export declare function normalizeLimit(value: unknown): number;
|
|
55
|
+
export declare function normalizeOffset(value: unknown): number;
|
|
56
|
+
export declare function resolveIssListLimit(filter?: IIssListFilter): number;
|
|
57
|
+
export declare function resolveIssListOffset(filter?: IIssListFilter): number;
|
|
58
|
+
export declare function resolveIssListSort(sort: string | undefined, allowed: Set<string>, defaultField: string): {
|
|
59
|
+
field: string;
|
|
60
|
+
desc: boolean;
|
|
61
|
+
};
|
|
62
|
+
export declare function pickEqFilter(eq: Record<string, string | number | boolean> | undefined, allowed: Set<string>): Record<string, string | number | boolean>;
|
|
63
|
+
export declare function escapeLikePattern(search: string): string;
|
|
64
|
+
export declare function likeSearchPattern(search: string): string;
|
|
65
|
+
export declare function likeSearchPrefix(search: string): string;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ISS_LIST_FILTER_SCHEMA = exports.ISS_LIST_FILTER_MAX_LIMIT = exports.ISS_LIST_FILTER_DEFAULT_LIMIT = exports.ISS_LIST_FILTER_QUERY_PARAM = void 0;
|
|
4
|
+
exports.encodeIssListFilterB64 = encodeIssListFilterB64;
|
|
5
|
+
exports.decodeIssListFilterB64 = decodeIssListFilterB64;
|
|
6
|
+
exports.parseIssListFilterFromQuery = parseIssListFilterFromQuery;
|
|
7
|
+
exports.parseIssListFilter = parseIssListFilter;
|
|
8
|
+
exports.normalizeLimit = normalizeLimit;
|
|
9
|
+
exports.normalizeOffset = normalizeOffset;
|
|
10
|
+
exports.resolveIssListLimit = resolveIssListLimit;
|
|
11
|
+
exports.resolveIssListOffset = resolveIssListOffset;
|
|
12
|
+
exports.resolveIssListSort = resolveIssListSort;
|
|
13
|
+
exports.pickEqFilter = pickEqFilter;
|
|
14
|
+
exports.escapeLikePattern = escapeLikePattern;
|
|
15
|
+
exports.likeSearchPattern = likeSearchPattern;
|
|
16
|
+
exports.likeSearchPrefix = likeSearchPrefix;
|
|
17
|
+
const ispserver_1 = require("@ingenieria_insoft/ispserver");
|
|
18
|
+
const ispgen_1 = require("@ingenieria_insoft/ispgen");
|
|
19
|
+
exports.ISS_LIST_FILTER_QUERY_PARAM = "f";
|
|
20
|
+
exports.ISS_LIST_FILTER_DEFAULT_LIMIT = 25;
|
|
21
|
+
exports.ISS_LIST_FILTER_MAX_LIMIT = 100;
|
|
22
|
+
exports.ISS_LIST_FILTER_SCHEMA = {
|
|
23
|
+
type: "object",
|
|
24
|
+
description: "Filtro ISS para listados (query `f` = JSON en Base64).",
|
|
25
|
+
properties: {
|
|
26
|
+
search: { type: "string", description: "Texto libre; el recurso define en qué columnas busca." },
|
|
27
|
+
limit: { type: "integer", minimum: 1, maximum: exports.ISS_LIST_FILTER_MAX_LIMIT, default: exports.ISS_LIST_FILTER_DEFAULT_LIMIT },
|
|
28
|
+
offset: { type: "integer", minimum: 0, default: 0 },
|
|
29
|
+
eq: {
|
|
30
|
+
type: "object",
|
|
31
|
+
additionalProperties: { oneOf: [{ type: "string" }, { type: "number" }, { type: "boolean" }] },
|
|
32
|
+
description: "Filtros de igualdad exacta (AND). Claves permitidas dependen del recurso.",
|
|
33
|
+
},
|
|
34
|
+
sort: { type: "string", description: "Campo de orden. Prefijo `-` = descendente." },
|
|
35
|
+
},
|
|
36
|
+
additionalProperties: false,
|
|
37
|
+
};
|
|
38
|
+
function encodeIssListFilterB64(filter) {
|
|
39
|
+
return Buffer.from(JSON.stringify(filter), "utf8").toString("base64");
|
|
40
|
+
}
|
|
41
|
+
function decodeIssListFilterB64(raw) {
|
|
42
|
+
try {
|
|
43
|
+
return Buffer.from(String(raw).trim(), "base64").toString("utf8");
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
throw (0, ispserver_1.Rst400)(ispgen_1.TErr400.paramBody, "f (Base64 inválido)");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function parseIssListFilterFromQuery(raw) {
|
|
50
|
+
if (raw == null || !String(raw).trim())
|
|
51
|
+
return {};
|
|
52
|
+
const json = decodeIssListFilterB64(raw);
|
|
53
|
+
return parseIssListFilter(json);
|
|
54
|
+
}
|
|
55
|
+
function parseIssListFilter(raw) {
|
|
56
|
+
if (raw == null || !String(raw).trim())
|
|
57
|
+
return {};
|
|
58
|
+
let parsed;
|
|
59
|
+
try {
|
|
60
|
+
parsed = JSON.parse(String(raw).trim());
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
throw (0, ispserver_1.Rst400)(ispgen_1.TErr400.paramBody, "f (JSON inválido)");
|
|
64
|
+
}
|
|
65
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
66
|
+
throw (0, ispserver_1.Rst400)(ispgen_1.TErr400.paramBody, "f (debe ser un objeto JSON)");
|
|
67
|
+
}
|
|
68
|
+
const src = parsed;
|
|
69
|
+
const out = {};
|
|
70
|
+
if (src.search !== undefined) {
|
|
71
|
+
const search = String(src.search).trim();
|
|
72
|
+
if (search)
|
|
73
|
+
out.search = search.slice(0, 200);
|
|
74
|
+
}
|
|
75
|
+
if (src.limit !== undefined)
|
|
76
|
+
out.limit = normalizeLimit(src.limit);
|
|
77
|
+
if (src.offset !== undefined)
|
|
78
|
+
out.offset = normalizeOffset(src.offset);
|
|
79
|
+
if (src.sort !== undefined) {
|
|
80
|
+
const sort = String(src.sort).trim();
|
|
81
|
+
if (sort)
|
|
82
|
+
out.sort = sort.slice(0, 64);
|
|
83
|
+
}
|
|
84
|
+
if (src.eq !== undefined) {
|
|
85
|
+
if (!src.eq || typeof src.eq !== "object" || Array.isArray(src.eq)) {
|
|
86
|
+
throw (0, ispserver_1.Rst400)(ispgen_1.TErr400.paramBody, "f.eq");
|
|
87
|
+
}
|
|
88
|
+
const eq = {};
|
|
89
|
+
for (const [key, value] of Object.entries(src.eq)) {
|
|
90
|
+
if (value === null || value === undefined)
|
|
91
|
+
continue;
|
|
92
|
+
const t = typeof value;
|
|
93
|
+
if (t === "string" || t === "number" || t === "boolean") {
|
|
94
|
+
eq[key] = value;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
throw (0, ispserver_1.Rst400)(ispgen_1.TErr400.paramBody, `f.eq.${key}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (Object.keys(eq).length)
|
|
101
|
+
out.eq = eq;
|
|
102
|
+
}
|
|
103
|
+
const extra = Object.keys(src).filter((k) => !["search", "limit", "offset", "eq", "sort"].includes(k));
|
|
104
|
+
if (extra.length) {
|
|
105
|
+
throw (0, ispserver_1.Rst400)(ispgen_1.TErr400.paramBody, `f (${extra.join(", ")})`);
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
function normalizeLimit(value) {
|
|
110
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
111
|
+
if (!Number.isFinite(n))
|
|
112
|
+
throw (0, ispserver_1.Rst400)(ispgen_1.TErr400.paramBody, "f.limit");
|
|
113
|
+
return Math.min(exports.ISS_LIST_FILTER_MAX_LIMIT, Math.max(1, Math.floor(n)));
|
|
114
|
+
}
|
|
115
|
+
function normalizeOffset(value) {
|
|
116
|
+
const n = typeof value === "number" ? value : Number(value);
|
|
117
|
+
if (!Number.isFinite(n) || n < 0)
|
|
118
|
+
throw (0, ispserver_1.Rst400)(ispgen_1.TErr400.paramBody, "f.offset");
|
|
119
|
+
return Math.floor(n);
|
|
120
|
+
}
|
|
121
|
+
function resolveIssListLimit(filter) {
|
|
122
|
+
if (!filter?.limit)
|
|
123
|
+
return exports.ISS_LIST_FILTER_DEFAULT_LIMIT;
|
|
124
|
+
return normalizeLimit(filter.limit);
|
|
125
|
+
}
|
|
126
|
+
function resolveIssListOffset(filter) {
|
|
127
|
+
if (!filter?.offset)
|
|
128
|
+
return 0;
|
|
129
|
+
return normalizeOffset(filter.offset);
|
|
130
|
+
}
|
|
131
|
+
function resolveIssListSort(sort, allowed, defaultField) {
|
|
132
|
+
if (!sort)
|
|
133
|
+
return { field: defaultField, desc: true };
|
|
134
|
+
const desc = sort.startsWith("-");
|
|
135
|
+
const field = desc ? sort.slice(1) : sort;
|
|
136
|
+
if (!allowed.has(field)) {
|
|
137
|
+
throw (0, ispserver_1.Rst400)(ispgen_1.TErr400.paramBody, "f.sort");
|
|
138
|
+
}
|
|
139
|
+
return { field, desc };
|
|
140
|
+
}
|
|
141
|
+
function pickEqFilter(eq, allowed) {
|
|
142
|
+
if (!eq)
|
|
143
|
+
return {};
|
|
144
|
+
const out = {};
|
|
145
|
+
for (const [key, value] of Object.entries(eq)) {
|
|
146
|
+
if (!allowed.has(key)) {
|
|
147
|
+
throw (0, ispserver_1.Rst400)(ispgen_1.TErr400.paramBody, `f.eq.${key}`);
|
|
148
|
+
}
|
|
149
|
+
out[key] = value;
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
function escapeLikePattern(search) {
|
|
154
|
+
return search.replace(/[%_[\]]/g, (ch) => `[${ch}]`);
|
|
155
|
+
}
|
|
156
|
+
function likeSearchPattern(search) {
|
|
157
|
+
return `%${escapeLikePattern(search)}%`;
|
|
158
|
+
}
|
|
159
|
+
function likeSearchPrefix(search) {
|
|
160
|
+
return `${escapeLikePattern(search)}%`;
|
|
161
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jeff-aporta/apiservers",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Utilidades comunes para backends InSoft / Azure Functions (filtros, convenciones API).",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"README.md"
|
|
10
|
+
],
|
|
11
|
+
"typesVersions": {
|
|
12
|
+
"*": {
|
|
13
|
+
"filter/iss-list-filter": ["dist/filter/iss-list-filter.d.ts"]
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
"./package.json": "./package.json",
|
|
18
|
+
"./filter/iss-list-filter": {
|
|
19
|
+
"types": "./dist/filter/iss-list-filter.d.ts",
|
|
20
|
+
"require": "./dist/filter/iss-list-filter.js",
|
|
21
|
+
"default": "./dist/filter/iss-list-filter.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc -p tsconfig.json",
|
|
26
|
+
"publish:npm": "node scripts/publish.mjs",
|
|
27
|
+
"prepublishOnly": "npm run build"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@ingenieria_insoft/ispgen": "^1.x",
|
|
31
|
+
"@ingenieria_insoft/ispserver": "^1.x"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^22.10.0",
|
|
35
|
+
"typescript": "^5.8.2"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"license": "UNLICENSED",
|
|
41
|
+
"private": false
|
|
42
|
+
}
|