remoção do filtro 26 por filtro delta
This commit is contained in:
parent
98a531dbd3
commit
b0b76562b7
12 changed files with 348 additions and 542 deletions
|
|
@ -40,7 +40,7 @@ src/
|
||||||
├── constantes.ts # Constantes globais (enums, valores fixos)
|
├── constantes.ts # Constantes globais (enums, valores fixos)
|
||||||
├── situacoes.ts # Status/situações dos processos
|
├── situacoes.ts # Status/situações dos processos
|
||||||
├── tipagemRotas.ts # Tipagem forte de rotas da API
|
├── tipagemRotas.ts # Tipagem forte de rotas da API
|
||||||
├── tipoFiltro.26.ts # Sistema de filtros tipados (operadores PG)
|
├── filtroDelta.ts # Sistema de filtros tipados (operadores PG)
|
||||||
├── uuid.ts # Geração e validação de UUIDs
|
├── uuid.ts # Geração e validação de UUIDs
|
||||||
├── dayjs26.ts # Wrapper do dayjs com locale e plugins
|
├── dayjs26.ts # Wrapper do dayjs com locale e plugins
|
||||||
├── extensoes.ts # Extensões de tipos nativos
|
├── extensoes.ts # Extensões de tipos nativos
|
||||||
|
|
|
||||||
10
README.md
10
README.md
|
|
@ -118,9 +118,9 @@ pnpm add --save-dev --save-exact @biomejs/biome@2.4.0
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ✅ Sistema de Filtros (tipoFiltro26)
|
## ✅ Sistema de Filtros (filtroDelta)
|
||||||
|
|
||||||
O sistema `tipoFiltro26` foi projetado para gerar automaticamente a tipagem de filtros compatíveis com operadores padrão do PostgreSQL, a partir de um tipo base `T`.
|
O sistema `filtroDelta` foi projetado para gerar automaticamente a tipagem de filtros compatíveis com operadores padrão do PostgreSQL, a partir de um tipo base `T`.
|
||||||
|
|
||||||
**Principais características:**
|
**Principais características:**
|
||||||
- Tipagem forte e segura (TypeScript)
|
- Tipagem forte e segura (TypeScript)
|
||||||
|
|
@ -178,7 +178,7 @@ O sistema `tipoFiltro26` foi projetado para gerar automaticamente a tipagem de f
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Operadores Suportados (`operadores26`)
|
### Operadores Suportados (`operadoresDelta`)
|
||||||
|
|
||||||
- **Number**: `=`, `!=`, `>`, `>=`, `<`, `<=`, `in`
|
- **Number**: `=`, `!=`, `>`, `>=`, `<`, `<=`, `in`
|
||||||
- **String**: `=`, `!=`, `like`, `in`
|
- **String**: `=`, `!=`, `like`, `in`
|
||||||
|
|
@ -187,10 +187,10 @@ O sistema `tipoFiltro26` foi projetado para gerar automaticamente a tipagem de f
|
||||||
### Validação em Tempo de Execução (Zod)
|
### Validação em Tempo de Execução (Zod)
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { zFiltro26 } from "p-comuns"
|
import { zFiltroDelta } from "p-comuns"
|
||||||
|
|
||||||
// Valida a estrutura (não checa existência de colunas no DB)
|
// Valida a estrutura (não checa existência de colunas no DB)
|
||||||
zFiltro26.parse(objetoFiltro)
|
zFiltroDelta.parse(objetoFiltro)
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
|
||||||
109
dist-back/filtroDelta.js
Normal file
109
dist-back/filtroDelta.js
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
"use strict";
|
||||||
|
var __create = Object.create;
|
||||||
|
var __defProp = Object.defineProperty;
|
||||||
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||||
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||||
|
var __getProtoOf = Object.getPrototypeOf;
|
||||||
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||||
|
var __export = (target, all) => {
|
||||||
|
for (var name in all)
|
||||||
|
__defProp(target, name, { get: all[name], enumerable: true });
|
||||||
|
};
|
||||||
|
var __copyProps = (to, from, except, desc) => {
|
||||||
|
if (from && typeof from === "object" || typeof from === "function") {
|
||||||
|
for (let key of __getOwnPropNames(from))
|
||||||
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||||
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||||
|
}
|
||||||
|
return to;
|
||||||
|
};
|
||||||
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
||||||
|
// If the importer is in node compatibility mode or this is not an ESM
|
||||||
|
// file that has been converted to a CommonJS file using a Babel-
|
||||||
|
// compatible transform (i.e. "__esModule" has not been set), then set
|
||||||
|
// "default" to the CommonJS "module.exports" for node compatibility.
|
||||||
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
||||||
|
mod
|
||||||
|
));
|
||||||
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||||
|
var filtroDelta_exports = {};
|
||||||
|
__export(filtroDelta_exports, {
|
||||||
|
zColunaFiltroDelta: () => zColunaFiltroDelta,
|
||||||
|
zCondicaoFiltroDelta: () => zCondicaoFiltroDelta,
|
||||||
|
zFiltroDelta: () => zFiltroDelta,
|
||||||
|
zOperadorFiltroDelta: () => zOperadorFiltroDelta
|
||||||
|
});
|
||||||
|
module.exports = __toCommonJS(filtroDelta_exports);
|
||||||
|
var import_zod = __toESM(require("zod"));
|
||||||
|
const zOperadorFiltroDelta = import_zod.default.enum([">", "<", ">=", "<=", "=", "!=", "in", "like"]);
|
||||||
|
const regexIdCondicaoFiltroDelta = /^\{[\w:]+\}$/;
|
||||||
|
const regexColunaFiltroDelta = /^\w+(\.\w+)*$/;
|
||||||
|
const zColunaFiltroDelta = import_zod.default.string().regex(regexColunaFiltroDelta, "Coluna inv\xE1lida").describe("nome da coluna ou caminho aninhado do jsonb");
|
||||||
|
const zCondicaoFiltroDelta = import_zod.default.object({
|
||||||
|
/**
|
||||||
|
* Identificador único da condição.
|
||||||
|
*
|
||||||
|
* Exemplo:
|
||||||
|
* {1}
|
||||||
|
*/
|
||||||
|
id: import_zod.default.string().describe("id da condi\xE7\xE3o do filtro").regex(regexIdCondicaoFiltroDelta, "O id deve seguir o formato {identificador}"),
|
||||||
|
/**
|
||||||
|
* Nome da coluna ou caminho jsonb.
|
||||||
|
*/
|
||||||
|
coluna: zColunaFiltroDelta,
|
||||||
|
/**
|
||||||
|
* Operador da comparação.
|
||||||
|
*/
|
||||||
|
operador: zOperadorFiltroDelta,
|
||||||
|
/**
|
||||||
|
* Valor utilizado na comparação.
|
||||||
|
*/
|
||||||
|
valor: import_zod.default.any().describe("valor de compara\xE7\xE3o")
|
||||||
|
});
|
||||||
|
const formulaFiltroDeltaValida = (formula) => {
|
||||||
|
const tokensPermitidos = formula.match(/\{[\w:]+\}|\bNAO\b|\bE\b|\bOU\b|\(|\)/g) || [];
|
||||||
|
const formulaReconstruida = tokensPermitidos.join("");
|
||||||
|
const formulaOriginalSemEspacos = formula.replace(/\s+/g, "");
|
||||||
|
const formulaReconstruidaSemEspacos = formulaReconstruida.replace(/\s+/g, "");
|
||||||
|
if (formulaOriginalSemEspacos !== formulaReconstruidaSemEspacos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const expressaoLogica = formula.replace(/\{[\w:]+\}/g, "true").replace(/\bNAO\b/g, "!").replace(/\bE\b/g, "&&").replace(/\bOU\b/g, "||");
|
||||||
|
try {
|
||||||
|
new Function(`return (${expressaoLogica})`)();
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const zFiltroDelta = import_zod.default.object({
|
||||||
|
/**
|
||||||
|
* Lista de condições disponíveis.
|
||||||
|
*/
|
||||||
|
condicoes: import_zod.default.array(zCondicaoFiltroDelta),
|
||||||
|
/**
|
||||||
|
* Fórmula lógica que combina as condições.
|
||||||
|
*/
|
||||||
|
formula: import_zod.default.string().refine(formulaFiltroDeltaValida, {
|
||||||
|
message: "F\xF3rmula inv\xE1lida. Use apenas refer\xEAncias de condi\xE7\xF5es {id} ou {id:sufixo}, operadores E/OU/NAO e par\xEAnteses."
|
||||||
|
})
|
||||||
|
}).superRefine((value, ctx) => {
|
||||||
|
const idsCondicoes = new Set(value.condicoes.map((condicao) => condicao.id));
|
||||||
|
const referenciasFormula = value.formula.match(/\{[\w:]+\}/g) || [];
|
||||||
|
for (const referencia of referenciasFormula) {
|
||||||
|
if (!idsCondicoes.has(referencia)) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: "custom",
|
||||||
|
path: ["formula"],
|
||||||
|
message: `A refer\xEAncia ${referencia} n\xE3o existe em condicoes.`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Annotate the CommonJS export names for ESM import in node:
|
||||||
|
0 && (module.exports = {
|
||||||
|
zColunaFiltroDelta,
|
||||||
|
zCondicaoFiltroDelta,
|
||||||
|
zFiltroDelta,
|
||||||
|
zOperadorFiltroDelta
|
||||||
|
});
|
||||||
|
|
@ -20,10 +20,10 @@ __reExport(index_exports, require("./auditoria"), module.exports);
|
||||||
__reExport(index_exports, require("./cacheMemoria"), module.exports);
|
__reExport(index_exports, require("./cacheMemoria"), module.exports);
|
||||||
__reExport(index_exports, require("./constantes"), module.exports);
|
__reExport(index_exports, require("./constantes"), module.exports);
|
||||||
__reExport(index_exports, require("./consulta"), module.exports);
|
__reExport(index_exports, require("./consulta"), module.exports);
|
||||||
__reExport(index_exports, require("./dayjs26"), module.exports);
|
|
||||||
__reExport(index_exports, require("./ecosistema"), module.exports);
|
__reExport(index_exports, require("./ecosistema"), module.exports);
|
||||||
__reExport(index_exports, require("./extensoes"), module.exports);
|
__reExport(index_exports, require("./extensoes"), module.exports);
|
||||||
__reExport(index_exports, require("./extensoes"), module.exports);
|
__reExport(index_exports, require("./extensoes"), module.exports);
|
||||||
|
__reExport(index_exports, require("./filtroDelta"), module.exports);
|
||||||
__reExport(index_exports, require("./local"), module.exports);
|
__reExport(index_exports, require("./local"), module.exports);
|
||||||
__reExport(index_exports, require("./postgres"), module.exports);
|
__reExport(index_exports, require("./postgres"), module.exports);
|
||||||
__reExport(index_exports, require("./produtos"), module.exports);
|
__reExport(index_exports, require("./produtos"), module.exports);
|
||||||
|
|
@ -32,7 +32,6 @@ __reExport(index_exports, require("./testes-de-variaveis"), module.exports);
|
||||||
__reExport(index_exports, require("./texto_busca"), module.exports);
|
__reExport(index_exports, require("./texto_busca"), module.exports);
|
||||||
__reExport(index_exports, require("./tipagemRotas"), module.exports);
|
__reExport(index_exports, require("./tipagemRotas"), module.exports);
|
||||||
__reExport(index_exports, require("./tipagemRotas"), module.exports);
|
__reExport(index_exports, require("./tipagemRotas"), module.exports);
|
||||||
__reExport(index_exports, require("./tipoFiltro.26"), module.exports);
|
|
||||||
__reExport(index_exports, require("./unidades_medida"), module.exports);
|
__reExport(index_exports, require("./unidades_medida"), module.exports);
|
||||||
__reExport(index_exports, require("./uuid"), module.exports);
|
__reExport(index_exports, require("./uuid"), module.exports);
|
||||||
__reExport(index_exports, require("./variaveisComuns"), module.exports);
|
__reExport(index_exports, require("./variaveisComuns"), module.exports);
|
||||||
|
|
@ -43,10 +42,10 @@ __reExport(index_exports, require("./variaveisComuns"), module.exports);
|
||||||
...require("./cacheMemoria"),
|
...require("./cacheMemoria"),
|
||||||
...require("./constantes"),
|
...require("./constantes"),
|
||||||
...require("./consulta"),
|
...require("./consulta"),
|
||||||
...require("./dayjs26"),
|
|
||||||
...require("./ecosistema"),
|
...require("./ecosistema"),
|
||||||
...require("./extensoes"),
|
...require("./extensoes"),
|
||||||
...require("./extensoes"),
|
...require("./extensoes"),
|
||||||
|
...require("./filtroDelta"),
|
||||||
...require("./local"),
|
...require("./local"),
|
||||||
...require("./postgres"),
|
...require("./postgres"),
|
||||||
...require("./produtos"),
|
...require("./produtos"),
|
||||||
|
|
@ -55,7 +54,6 @@ __reExport(index_exports, require("./variaveisComuns"), module.exports);
|
||||||
...require("./texto_busca"),
|
...require("./texto_busca"),
|
||||||
...require("./tipagemRotas"),
|
...require("./tipagemRotas"),
|
||||||
...require("./tipagemRotas"),
|
...require("./tipagemRotas"),
|
||||||
...require("./tipoFiltro.26"),
|
|
||||||
...require("./unidades_medida"),
|
...require("./unidades_medida"),
|
||||||
...require("./uuid"),
|
...require("./uuid"),
|
||||||
...require("./variaveisComuns")
|
...require("./variaveisComuns")
|
||||||
|
|
|
||||||
|
|
@ -1,72 +0,0 @@
|
||||||
"use strict";
|
|
||||||
var __defProp = Object.defineProperty;
|
|
||||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
||||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
||||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
||||||
var __export = (target, all) => {
|
|
||||||
for (var name in all)
|
|
||||||
__defProp(target, name, { get: all[name], enumerable: true });
|
|
||||||
};
|
|
||||||
var __copyProps = (to, from, except, desc) => {
|
|
||||||
if (from && typeof from === "object" || typeof from === "function") {
|
|
||||||
for (let key of __getOwnPropNames(from))
|
|
||||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
||||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
||||||
}
|
|
||||||
return to;
|
|
||||||
};
|
|
||||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
||||||
var tipoFiltro_26_exports = {};
|
|
||||||
__export(tipoFiltro_26_exports, {
|
|
||||||
agrupadores26: () => agrupadores26,
|
|
||||||
criarFiltro26: () => criarFiltro26,
|
|
||||||
operadores26: () => operadores26,
|
|
||||||
zFiltro26: () => zFiltro26
|
|
||||||
});
|
|
||||||
module.exports = __toCommonJS(tipoFiltro_26_exports);
|
|
||||||
var import_zod = require("zod");
|
|
||||||
var operadores26 = /* @__PURE__ */ ((operadores262) => {
|
|
||||||
operadores262["="] = "=";
|
|
||||||
operadores262["!="] = "!=";
|
|
||||||
operadores262[">"] = ">";
|
|
||||||
operadores262[">="] = ">=";
|
|
||||||
operadores262["<"] = "<";
|
|
||||||
operadores262["<="] = "<=";
|
|
||||||
operadores262["like"] = "like";
|
|
||||||
operadores262["in"] = "in";
|
|
||||||
return operadores262;
|
|
||||||
})(operadores26 || {});
|
|
||||||
var agrupadores26 = /* @__PURE__ */ ((agrupadores262) => {
|
|
||||||
agrupadores262["E"] = "E";
|
|
||||||
agrupadores262["OU"] = "OU";
|
|
||||||
return agrupadores262;
|
|
||||||
})(agrupadores26 || {});
|
|
||||||
const zOperadores = import_zod.z.nativeEnum(operadores26);
|
|
||||||
const zValor = import_zod.z.any();
|
|
||||||
const zCondicao = import_zod.z.record(zOperadores, zValor);
|
|
||||||
const zFiltro26 = import_zod.z.lazy(
|
|
||||||
() => import_zod.z.object({
|
|
||||||
E: import_zod.z.array(zFiltro26).optional(),
|
|
||||||
OU: import_zod.z.array(zFiltro26).optional()
|
|
||||||
}).catchall(import_zod.z.union([zCondicao, zFiltro26]))
|
|
||||||
);
|
|
||||||
const criarFiltro26 = (filtro) => filtro;
|
|
||||||
const _filtro = criarFiltro26({
|
|
||||||
idade: { [">=" /* >= */]: 18 },
|
|
||||||
["OU" /* OU */]: [
|
|
||||||
{ nome: { ["like" /* like */]: "%pa%" } },
|
|
||||||
{
|
|
||||||
["E" /* E */]: [
|
|
||||||
{ carro: { ano: { ["=" /* = */]: 2020 } } },
|
|
||||||
{ carro: { modelo: { ["in" /* in */]: ["Civic", "Corolla"] } } }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
// Annotate the CommonJS export names for ESM import in node:
|
|
||||||
0 && (module.exports = {
|
|
||||||
agrupadores26,
|
|
||||||
criarFiltro26,
|
|
||||||
operadores26,
|
|
||||||
zFiltro26
|
|
||||||
});
|
|
||||||
|
|
@ -1,14 +1,4 @@
|
||||||
import z, { z as z$1 } from 'zod';
|
import z from 'zod';
|
||||||
import _dayjs from 'dayjs';
|
|
||||||
export { Dayjs, ManipulateType } from 'dayjs';
|
|
||||||
import _duration from 'dayjs/plugin/duration';
|
|
||||||
import _isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
|
|
||||||
import _isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
|
|
||||||
import _minMax from 'dayjs/plugin/minMax';
|
|
||||||
import _relativeTime from 'dayjs/plugin/relativeTime';
|
|
||||||
import _timezone from 'dayjs/plugin/timezone';
|
|
||||||
import _utc from 'dayjs/plugin/utc';
|
|
||||||
import _weekOfYear from 'dayjs/plugin/weekOfYear';
|
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
|
|
||||||
declare const aleatorio: (tamanho?: number) => string;
|
declare const aleatorio: (tamanho?: number) => string;
|
||||||
|
|
@ -124,70 +114,6 @@ declare const zFiltro: z.ZodObject<{
|
||||||
ou: z.ZodOptional<z.ZodBoolean>;
|
ou: z.ZodOptional<z.ZodBoolean>;
|
||||||
}, z.core.$strip>;
|
}, z.core.$strip>;
|
||||||
|
|
||||||
/**
|
|
||||||
* Utilitário de configuração do Dayjs focado em compatibilidade com SSR.
|
|
||||||
*
|
|
||||||
* PROBLEMA:
|
|
||||||
* A importação direta do `dayjs` e seus plugins frequentemente causa conflitos em ambientes
|
|
||||||
* de Renderização do Lado do Servidor (SSR), como Nuxt ou Next.js, devido a discrepâncias
|
|
||||||
* na resolução de módulos (ESM vs CJS) e instabilidades de importação.
|
|
||||||
*
|
|
||||||
* SOLUÇÃO:
|
|
||||||
* Este módulo utiliza o padrão de Injeção de Dependência. Ele expõe apenas tipagens e
|
|
||||||
* uma função de configuração (`defineDayjsBr`). A responsabilidade de importar as
|
|
||||||
* bibliotecas "vivas" é delegada à aplicação consumidora (o cliente da função).
|
|
||||||
*
|
|
||||||
* Isso permite que o bundler da aplicação principal (Vite, Webpack, etc.) gerencie as
|
|
||||||
* instâncias, garantindo consistência e evitando erros de "module not found" ou
|
|
||||||
* instâncias duplicadas/não inicializadas adequadamente.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inicializa e configura o Dayjs com o locale 'pt-br' e plugins essenciais.
|
|
||||||
*
|
|
||||||
* MODO DE USO:
|
|
||||||
* Importe os pacotes reais na sua aplicação e passe-os para esta função.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```ts
|
|
||||||
* // Em seu arquivo de configuração (ex: plugins/dayjs.ts):
|
|
||||||
* import dayjs from "dayjs"
|
|
||||||
* import duration from "dayjs/plugin/duration"
|
|
||||||
* import isSameOrAfter from "dayjs/plugin/isSameOrAfter"
|
|
||||||
* import isSameOrBefore from "dayjs/plugin/isSameOrBefore"
|
|
||||||
* import minMax from "dayjs/plugin/minMax"
|
|
||||||
* import relativeTime from "dayjs/plugin/relativeTime"
|
|
||||||
* import timezone from "dayjs/plugin/timezone"
|
|
||||||
* import utc from "dayjs/plugin/utc"
|
|
||||||
* import weekOfYear from "dayjs/plugin/weekOfYear"
|
|
||||||
* import { defineDayjsBr } from "p-comuns"
|
|
||||||
* import "dayjs/locale/pt-br" // Importante: importar o locale!
|
|
||||||
|
|
||||||
* export const dayjsbr = defineDayjsBr({
|
|
||||||
* dayjs,
|
|
||||||
* duration,
|
|
||||||
* isSameOrAfter,
|
|
||||||
* isSameOrBefore,
|
|
||||||
* minMax,
|
|
||||||
* relativeTime,
|
|
||||||
* timezone,
|
|
||||||
* utc,
|
|
||||||
* weekOfYear,
|
|
||||||
* })
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
declare const defineDayjsBr: ({ dayjs, duration, isSameOrAfter, isSameOrBefore, minMax, relativeTime, timezone, utc, weekOfYear, }: {
|
|
||||||
dayjs: typeof _dayjs;
|
|
||||||
duration: typeof _duration;
|
|
||||||
isSameOrAfter: typeof _isSameOrAfter;
|
|
||||||
isSameOrBefore: typeof _isSameOrBefore;
|
|
||||||
minMax: typeof _minMax;
|
|
||||||
relativeTime: typeof _relativeTime;
|
|
||||||
timezone: typeof _timezone;
|
|
||||||
utc: typeof _utc;
|
|
||||||
weekOfYear: typeof _weekOfYear;
|
|
||||||
}) => typeof _dayjs;
|
|
||||||
|
|
||||||
declare const link_paiol = "https://paiol.idz.one";
|
declare const link_paiol = "https://paiol.idz.one";
|
||||||
|
|
||||||
type tiposArquivo = "imagem" | "documento" | "vídeo" | "outros";
|
type tiposArquivo = "imagem" | "documento" | "vídeo" | "outros";
|
||||||
|
|
@ -203,6 +129,67 @@ declare const extensoes: {
|
||||||
*/
|
*/
|
||||||
declare const tipoArquivo: (nomeArquivo: string | null | undefined) => tiposArquivo;
|
declare const tipoArquivo: (nomeArquivo: string | null | undefined) => tiposArquivo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operadores permitidos para comparação.
|
||||||
|
*/
|
||||||
|
declare const zOperadorFiltroDelta: z.ZodEnum<{
|
||||||
|
"=": "=";
|
||||||
|
"!=": "!=";
|
||||||
|
">": ">";
|
||||||
|
">=": ">=";
|
||||||
|
"<": "<";
|
||||||
|
"<=": "<=";
|
||||||
|
like: "like";
|
||||||
|
in: "in";
|
||||||
|
}>;
|
||||||
|
/**
|
||||||
|
* Coluna simples ou caminho aninhado de jsonb.
|
||||||
|
*/
|
||||||
|
declare const zColunaFiltroDelta: z.ZodString;
|
||||||
|
/**
|
||||||
|
* Estrutura individual de uma condição do filtro.
|
||||||
|
*/
|
||||||
|
declare const zCondicaoFiltroDelta: z.ZodObject<{
|
||||||
|
id: z.ZodString;
|
||||||
|
coluna: z.ZodString;
|
||||||
|
operador: z.ZodEnum<{
|
||||||
|
"=": "=";
|
||||||
|
"!=": "!=";
|
||||||
|
">": ">";
|
||||||
|
">=": ">=";
|
||||||
|
"<": "<";
|
||||||
|
"<=": "<=";
|
||||||
|
like: "like";
|
||||||
|
in: "in";
|
||||||
|
}>;
|
||||||
|
valor: z.ZodAny;
|
||||||
|
}, z.core.$strip>;
|
||||||
|
/**
|
||||||
|
* Estrutura principal do filtro delta.
|
||||||
|
*/
|
||||||
|
declare const zFiltroDelta: z.ZodObject<{
|
||||||
|
condicoes: z.ZodArray<z.ZodObject<{
|
||||||
|
id: z.ZodString;
|
||||||
|
coluna: z.ZodString;
|
||||||
|
operador: z.ZodEnum<{
|
||||||
|
"=": "=";
|
||||||
|
"!=": "!=";
|
||||||
|
">": ">";
|
||||||
|
">=": ">=";
|
||||||
|
"<": "<";
|
||||||
|
"<=": "<=";
|
||||||
|
like: "like";
|
||||||
|
in: "in";
|
||||||
|
}>;
|
||||||
|
valor: z.ZodAny;
|
||||||
|
}, z.core.$strip>>;
|
||||||
|
formula: z.ZodString;
|
||||||
|
}, z.core.$strip>;
|
||||||
|
type tipoOperadorFiltroDelta = z.infer<typeof zOperadorFiltroDelta>;
|
||||||
|
type tipoColunaFiltroDelta = z.infer<typeof zColunaFiltroDelta>;
|
||||||
|
type tipoCondicaoFiltroDelta = z.infer<typeof zCondicaoFiltroDelta>;
|
||||||
|
type tipoFiltroDelta = z.infer<typeof zFiltroDelta>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LocalStorage Tipado
|
* LocalStorage Tipado
|
||||||
* Lê ou grava um valor no localStorage, mantendo o tipo genérico <T>.
|
* Lê ou grava um valor no localStorage, mantendo o tipo genérico <T>.
|
||||||
|
|
@ -347,151 +334,6 @@ declare class TipagemRotas<T extends {
|
||||||
parametros(urlEntrada?: string): Partial<T>;
|
parametros(urlEntrada?: string): Partial<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* =============================================================================
|
|
||||||
* tipoFiltro26<T>
|
|
||||||
* =============================================================================
|
|
||||||
*
|
|
||||||
* OBJETIVO
|
|
||||||
* -----------------------------------------------------------------------------
|
|
||||||
* Gerar automaticamente a tipagem de filtros compatíveis com operadores
|
|
||||||
* padrão do PostgreSQL, a partir de um tipo base T.
|
|
||||||
*
|
|
||||||
* Este tipo foi projetado para:
|
|
||||||
* - Construção de filtros dinâmicos
|
|
||||||
* - Geração posterior de WHERE (Knex / SQL)
|
|
||||||
* - Uso seguro por IA (evita filtros inválidos em nível de tipo)
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* FORMATO DO FILTRO
|
|
||||||
* -----------------------------------------------------------------------------
|
|
||||||
* 1) Campos simples:
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* idade: { ">=": 18 }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* 2) Campos aninhados:
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* carro: {
|
|
||||||
* ano: { "=": 2020 }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* 3) Operador E (AND):
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* E: [
|
|
||||||
* { idade: { ">=": 18 } },
|
|
||||||
* { nome: { like: "%pa%" } }
|
|
||||||
* ]
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* 4) Operador OU (OR):
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* OU: [
|
|
||||||
* { idade: { "<": 18 } },
|
|
||||||
* { idade: { ">=": 60 } }
|
|
||||||
* ]
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* 5) Combinação complexa:
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* idade: { ">=": 18 },
|
|
||||||
* OU: [
|
|
||||||
* { nome: { like: "%pa%" } },
|
|
||||||
* {
|
|
||||||
* E: [
|
|
||||||
* { carro: { ano: { "=": 2020 } } },
|
|
||||||
* { carro: { modelo: { in: ["Civic"] } } }
|
|
||||||
* ]
|
|
||||||
* }
|
|
||||||
* ]
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* REGRAS IMPORTANTES (PARA IA)
|
|
||||||
* -----------------------------------------------------------------------------
|
|
||||||
* - Apenas campos existentes em T podem ser usados.
|
|
||||||
* - Operadores são restritos por tipo do campo.
|
|
||||||
* - Objetos são tratados recursivamente.
|
|
||||||
* - Arrays NÃO são tratados como objeto recursivo.
|
|
||||||
* - Funções NÃO são consideradas campos filtráveis.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* OPERADORES SUPORTADOS
|
|
||||||
* -----------------------------------------------------------------------------
|
|
||||||
* number:
|
|
||||||
* =, !=, >, >=, <, <=, in
|
|
||||||
*
|
|
||||||
* string:
|
|
||||||
* =, !=, like, in
|
|
||||||
*
|
|
||||||
* boolean:
|
|
||||||
* =, !=, in
|
|
||||||
*
|
|
||||||
* Não há suporte automático a:
|
|
||||||
* - null
|
|
||||||
* - date
|
|
||||||
* - jsonb
|
|
||||||
* - arrays
|
|
||||||
*
|
|
||||||
* Essas extensões devem ser adicionadas explicitamente.
|
|
||||||
*
|
|
||||||
* =============================================================================
|
|
||||||
*/
|
|
||||||
declare enum operadores26 {
|
|
||||||
"=" = "=",
|
|
||||||
"!=" = "!=",
|
|
||||||
">" = ">",
|
|
||||||
">=" = ">=",
|
|
||||||
"<" = "<",
|
|
||||||
"<=" = "<=",
|
|
||||||
like = "like",
|
|
||||||
in = "in"
|
|
||||||
}
|
|
||||||
declare enum agrupadores26 {
|
|
||||||
E = "E",
|
|
||||||
OU = "OU"
|
|
||||||
}
|
|
||||||
type PgOpsNumber = {
|
|
||||||
[K in Extract<operadores26, "=" | "!=" | ">" | ">=" | "<" | "<=">]?: number;
|
|
||||||
} & {
|
|
||||||
[K in Extract<operadores26, "in">]?: number[];
|
|
||||||
};
|
|
||||||
type PgOpsString = {
|
|
||||||
[K in Extract<operadores26, "=" | "!=" | "like">]?: string;
|
|
||||||
} & {
|
|
||||||
[K in Extract<operadores26, "in">]?: string[];
|
|
||||||
};
|
|
||||||
type PgOpsBoolean = {
|
|
||||||
[K in Extract<operadores26, "=" | "!=">]?: boolean;
|
|
||||||
} & {
|
|
||||||
[K in Extract<operadores26, "in">]?: boolean[];
|
|
||||||
};
|
|
||||||
type PgOpsFor<V> = V extends number ? PgOpsNumber : V extends string ? PgOpsString : V extends boolean ? PgOpsBoolean : never;
|
|
||||||
type IsPlainObject<T> = T extends object ? T extends Function ? false : T extends readonly any[] ? false : true : false;
|
|
||||||
type FiltroCampos<T> = {
|
|
||||||
[K in keyof T]?: IsPlainObject<T[K]> extends true ? tipoFiltro26<T[K]> : PgOpsFor<T[K]>;
|
|
||||||
};
|
|
||||||
type tipoFiltro26<T> = FiltroCampos<T> & {
|
|
||||||
/**
|
|
||||||
* E => AND lógico
|
|
||||||
* Todos os filtros dentro do array devem ser verdadeiros.
|
|
||||||
*/
|
|
||||||
E?: tipoFiltro26<T>[];
|
|
||||||
/**
|
|
||||||
* OU => OR lógico
|
|
||||||
* Pelo menos um filtro dentro do array deve ser verdadeiro.
|
|
||||||
*/
|
|
||||||
OU?: tipoFiltro26<T>[];
|
|
||||||
};
|
|
||||||
declare const zFiltro26: z$1.ZodType<any>;
|
|
||||||
declare const criarFiltro26: <T>(filtro: tipoFiltro26<T>) => tipoFiltro26<T>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Essa variável se conecta a tabela_lidades
|
* Essa variável se conecta a tabela_lidades
|
||||||
*
|
*
|
||||||
|
|
@ -560,4 +402,4 @@ declare const nomeVariavel: (v: {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}) => string;
|
}) => string;
|
||||||
|
|
||||||
export { Produtos, TipagemRotas, type TipoPayloadAuditoria, agrupadores26, aleatorio, cacheM, cacheMFixo, cacheMemoria, camposComuns, criarFiltro26, defineDayjsBr, erUuid, esperar, extensoes, type interfaceConsulta, link_paiol, localValor, nomeVariavel, objetoPg, operadores, operadores26, paraObjetoRegistroPg, pgObjeto, siglas_unidades_medida, texto_busca, tipoArquivo, type tipoFiltro, type tipoFiltro26, tipoUsuarioResiduos, tiposSituacoesElicencie, tx, umaFuncao, umaVariavel, unidades_medida, uuid, uuidV3, uuidV4, uuid_null, validarUuid, verCacheM, zFiltro, zFiltro26, zOperadores };
|
export { Produtos, TipagemRotas, type TipoPayloadAuditoria, aleatorio, cacheM, cacheMFixo, cacheMemoria, camposComuns, erUuid, esperar, extensoes, type interfaceConsulta, link_paiol, localValor, nomeVariavel, objetoPg, operadores, paraObjetoRegistroPg, pgObjeto, siglas_unidades_medida, texto_busca, tipoArquivo, type tipoColunaFiltroDelta, type tipoCondicaoFiltroDelta, type tipoFiltro, type tipoFiltroDelta, type tipoOperadorFiltroDelta, tipoUsuarioResiduos, tiposSituacoesElicencie, tx, umaFuncao, umaVariavel, unidades_medida, uuid, uuidV3, uuidV4, uuid_null, validarUuid, verCacheM, zColunaFiltroDelta, zCondicaoFiltroDelta, zFiltro, zFiltroDelta, zOperadorFiltroDelta, zOperadores };
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "p-comuns",
|
"name": "p-comuns",
|
||||||
"version": "0.336.0",
|
"version": "0.339.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "./dist-front/index.mjs",
|
"main": "./dist-front/index.mjs",
|
||||||
"module": "./dist-front/index.mjs",
|
"module": "./dist-front/index.mjs",
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"zod": "4.3.6",
|
"zod": "4.4.3",
|
||||||
"dayjs": "^1.11.18",
|
"dayjs": "^1.11.18",
|
||||||
"@biomejs/biome": "2.4.12",
|
"@biomejs/biome": "2.4.12",
|
||||||
"@types/node": "^22",
|
"@types/node": "^22",
|
||||||
|
|
@ -41,6 +41,6 @@
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"dayjs": "^1.11.18",
|
"dayjs": "^1.11.18",
|
||||||
"uuid": "^11.1.0",
|
"uuid": "^11.1.0",
|
||||||
"zod": "4.3.6"
|
"zod": "4.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
pacote.tgz
BIN
pacote.tgz
Binary file not shown.
162
src/filtroDelta.ts
Normal file
162
src/filtroDelta.ts
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
import z from "zod"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operadores permitidos para comparação.
|
||||||
|
*/
|
||||||
|
export const zOperadorFiltroDelta = z.enum([">", "<", ">=", "<=", "=", "!=", "in", "like"])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valida o identificador da condição.
|
||||||
|
*
|
||||||
|
* Formato esperado:
|
||||||
|
* {identificador} ou {identificador:sufixo}
|
||||||
|
*/
|
||||||
|
const regexIdCondicaoFiltroDelta = /^\{[\w:]+\}$/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valida nomes de colunas e caminhos jsonb.
|
||||||
|
*/
|
||||||
|
const regexColunaFiltroDelta = /^\w+(\.\w+)*$/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Coluna simples ou caminho aninhado de jsonb.
|
||||||
|
*/
|
||||||
|
export const zColunaFiltroDelta = z
|
||||||
|
.string()
|
||||||
|
.regex(regexColunaFiltroDelta, "Coluna inválida")
|
||||||
|
.describe("nome da coluna ou caminho aninhado do jsonb")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estrutura individual de uma condição do filtro.
|
||||||
|
*/
|
||||||
|
export const zCondicaoFiltroDelta = z.object({
|
||||||
|
/**
|
||||||
|
* Identificador único da condição.
|
||||||
|
*
|
||||||
|
* Exemplo:
|
||||||
|
* {1}
|
||||||
|
*/
|
||||||
|
id: z
|
||||||
|
.string()
|
||||||
|
.describe("id da condição do filtro")
|
||||||
|
.regex(regexIdCondicaoFiltroDelta, "O id deve seguir o formato {identificador}"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nome da coluna ou caminho jsonb.
|
||||||
|
*/
|
||||||
|
coluna: zColunaFiltroDelta,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operador da comparação.
|
||||||
|
*/
|
||||||
|
operador: zOperadorFiltroDelta,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Valor utilizado na comparação.
|
||||||
|
*/
|
||||||
|
valor: z.any().describe("valor de comparação"),
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica se a fórmula possui uma estrutura lógica válida.
|
||||||
|
*/
|
||||||
|
const formulaFiltroDeltaValida = (formula: string) => {
|
||||||
|
/**
|
||||||
|
* Extrai apenas os tokens permitidos:
|
||||||
|
* - referências de condição: {id} ou {id:sufixo}
|
||||||
|
* - operadores lógicos: E, OU
|
||||||
|
* - negação: NAO(...)
|
||||||
|
* - parênteses
|
||||||
|
*/
|
||||||
|
const tokensPermitidos = formula.match(/\{[\w:]+\}|\bNAO\b|\bE\b|\bOU\b|\(|\)/g) || []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reconstrói a fórmula usando apenas os tokens válidos.
|
||||||
|
*/
|
||||||
|
const formulaReconstruida = tokensPermitidos.join("")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove espaços para comparar o texto original com o texto reconstruído.
|
||||||
|
*/
|
||||||
|
const formulaOriginalSemEspacos = formula.replace(/\s+/g, "")
|
||||||
|
const formulaReconstruidaSemEspacos = formulaReconstruida.replace(/\s+/g, "")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Se forem diferentes, há conteúdo não permitido na fórmula.
|
||||||
|
*/
|
||||||
|
if (formulaOriginalSemEspacos !== formulaReconstruidaSemEspacos) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converte a fórmula para uma expressão JavaScript equivalente.
|
||||||
|
*
|
||||||
|
* NAO(x) → !x
|
||||||
|
* E → &&
|
||||||
|
* OU → ||
|
||||||
|
*/
|
||||||
|
const expressaoLogica = formula
|
||||||
|
.replace(/\{[\w:]+\}/g, "true")
|
||||||
|
.replace(/\bNAO\b/g, "!")
|
||||||
|
.replace(/\bE\b/g, "&&")
|
||||||
|
.replace(/\bOU\b/g, "||")
|
||||||
|
|
||||||
|
try {
|
||||||
|
/**
|
||||||
|
* Valida a ordem dos operadores, parênteses e estrutura lógica geral.
|
||||||
|
*/
|
||||||
|
new Function(`return (${expressaoLogica})`)()
|
||||||
|
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Estrutura principal do filtro delta.
|
||||||
|
*/
|
||||||
|
export const zFiltroDelta = z
|
||||||
|
.object({
|
||||||
|
/**
|
||||||
|
* Lista de condições disponíveis.
|
||||||
|
*/
|
||||||
|
condicoes: z.array(zCondicaoFiltroDelta),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fórmula lógica que combina as condições.
|
||||||
|
*/
|
||||||
|
formula: z.string().refine(formulaFiltroDeltaValida, {
|
||||||
|
message:
|
||||||
|
"Fórmula inválida. Use apenas referências de condições {id} ou {id:sufixo}, operadores E/OU/NAO e parênteses.",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.superRefine((value, ctx) => {
|
||||||
|
/**
|
||||||
|
* IDs das condições cadastradas.
|
||||||
|
*/
|
||||||
|
const idsCondicoes = new Set(value.condicoes.map((condicao) => condicao.id))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Referências usadas dentro da fórmula.
|
||||||
|
*/
|
||||||
|
const referenciasFormula = value.formula.match(/\{[\w:]+\}/g) || []
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifica se toda referência da fórmula existe em condicoes.
|
||||||
|
*/
|
||||||
|
for (const referencia of referenciasFormula) {
|
||||||
|
if (!idsCondicoes.has(referencia)) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: "custom",
|
||||||
|
path: ["formula"],
|
||||||
|
message: `A referência ${referencia} não existe em condicoes.`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export type tipoOperadorFiltroDelta = z.infer<typeof zOperadorFiltroDelta>
|
||||||
|
export type tipoColunaFiltroDelta = z.infer<typeof zColunaFiltroDelta>
|
||||||
|
export type tipoCondicaoFiltroDelta = z.infer<typeof zCondicaoFiltroDelta>
|
||||||
|
export type tipoFiltroDelta = z.infer<typeof zFiltroDelta>
|
||||||
|
|
@ -3,10 +3,10 @@ export * from "./auditoria"
|
||||||
export * from "./cacheMemoria"
|
export * from "./cacheMemoria"
|
||||||
export * from "./constantes"
|
export * from "./constantes"
|
||||||
export * from "./consulta"
|
export * from "./consulta"
|
||||||
export * from "./dayjs26"
|
|
||||||
export * from "./ecosistema"
|
export * from "./ecosistema"
|
||||||
export * from "./extensoes"
|
export * from "./extensoes"
|
||||||
export * from "./extensoes"
|
export * from "./extensoes"
|
||||||
|
export * from "./filtroDelta"
|
||||||
export * from "./local"
|
export * from "./local"
|
||||||
export * from "./postgres"
|
export * from "./postgres"
|
||||||
export * from "./produtos"
|
export * from "./produtos"
|
||||||
|
|
@ -15,7 +15,7 @@ export * from "./testes-de-variaveis"
|
||||||
export * from "./texto_busca"
|
export * from "./texto_busca"
|
||||||
export * from "./tipagemRotas"
|
export * from "./tipagemRotas"
|
||||||
export * from "./tipagemRotas"
|
export * from "./tipagemRotas"
|
||||||
export * from "./tipoFiltro.26"
|
|
||||||
export * from "./unidades_medida"
|
export * from "./unidades_medida"
|
||||||
export * from "./uuid"
|
export * from "./uuid"
|
||||||
export * from "./variaveisComuns"
|
export * from "./variaveisComuns"
|
||||||
|
|
|
||||||
|
|
@ -1,233 +0,0 @@
|
||||||
import { z } from "zod"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* =============================================================================
|
|
||||||
* tipoFiltro26<T>
|
|
||||||
* =============================================================================
|
|
||||||
*
|
|
||||||
* OBJETIVO
|
|
||||||
* -----------------------------------------------------------------------------
|
|
||||||
* Gerar automaticamente a tipagem de filtros compatíveis com operadores
|
|
||||||
* padrão do PostgreSQL, a partir de um tipo base T.
|
|
||||||
*
|
|
||||||
* Este tipo foi projetado para:
|
|
||||||
* - Construção de filtros dinâmicos
|
|
||||||
* - Geração posterior de WHERE (Knex / SQL)
|
|
||||||
* - Uso seguro por IA (evita filtros inválidos em nível de tipo)
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* FORMATO DO FILTRO
|
|
||||||
* -----------------------------------------------------------------------------
|
|
||||||
* 1) Campos simples:
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* idade: { ">=": 18 }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* 2) Campos aninhados:
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* carro: {
|
|
||||||
* ano: { "=": 2020 }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* 3) Operador E (AND):
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* E: [
|
|
||||||
* { idade: { ">=": 18 } },
|
|
||||||
* { nome: { like: "%pa%" } }
|
|
||||||
* ]
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* 4) Operador OU (OR):
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* OU: [
|
|
||||||
* { idade: { "<": 18 } },
|
|
||||||
* { idade: { ">=": 60 } }
|
|
||||||
* ]
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* 5) Combinação complexa:
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* idade: { ">=": 18 },
|
|
||||||
* OU: [
|
|
||||||
* { nome: { like: "%pa%" } },
|
|
||||||
* {
|
|
||||||
* E: [
|
|
||||||
* { carro: { ano: { "=": 2020 } } },
|
|
||||||
* { carro: { modelo: { in: ["Civic"] } } }
|
|
||||||
* ]
|
|
||||||
* }
|
|
||||||
* ]
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* REGRAS IMPORTANTES (PARA IA)
|
|
||||||
* -----------------------------------------------------------------------------
|
|
||||||
* - Apenas campos existentes em T podem ser usados.
|
|
||||||
* - Operadores são restritos por tipo do campo.
|
|
||||||
* - Objetos são tratados recursivamente.
|
|
||||||
* - Arrays NÃO são tratados como objeto recursivo.
|
|
||||||
* - Funções NÃO são consideradas campos filtráveis.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* OPERADORES SUPORTADOS
|
|
||||||
* -----------------------------------------------------------------------------
|
|
||||||
* number:
|
|
||||||
* =, !=, >, >=, <, <=, in
|
|
||||||
*
|
|
||||||
* string:
|
|
||||||
* =, !=, like, in
|
|
||||||
*
|
|
||||||
* boolean:
|
|
||||||
* =, !=, in
|
|
||||||
*
|
|
||||||
* Não há suporte automático a:
|
|
||||||
* - null
|
|
||||||
* - date
|
|
||||||
* - jsonb
|
|
||||||
* - arrays
|
|
||||||
*
|
|
||||||
* Essas extensões devem ser adicionadas explicitamente.
|
|
||||||
*
|
|
||||||
* =============================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* =============================================================================
|
|
||||||
OPERADORES POSTGRESQL POR TIPO
|
|
||||||
============================================================================= */
|
|
||||||
|
|
||||||
export enum operadores26 {
|
|
||||||
"=" = "=",
|
|
||||||
"!=" = "!=",
|
|
||||||
">" = ">",
|
|
||||||
">=" = ">=",
|
|
||||||
"<" = "<",
|
|
||||||
"<=" = "<=",
|
|
||||||
like = "like",
|
|
||||||
in = "in",
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum agrupadores26 {
|
|
||||||
E = "E",
|
|
||||||
OU = "OU",
|
|
||||||
}
|
|
||||||
|
|
||||||
type PgOpsNumber = {
|
|
||||||
[K in Extract<operadores26, "=" | "!=" | ">" | ">=" | "<" | "<=">]?: number
|
|
||||||
} & {
|
|
||||||
[K in Extract<operadores26, "in">]?: number[]
|
|
||||||
}
|
|
||||||
|
|
||||||
type PgOpsString = {
|
|
||||||
[K in Extract<operadores26, "=" | "!=" | "like">]?: string
|
|
||||||
} & {
|
|
||||||
[K in Extract<operadores26, "in">]?: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
type PgOpsBoolean = {
|
|
||||||
[K in Extract<operadores26, "=" | "!=">]?: boolean
|
|
||||||
} & {
|
|
||||||
[K in Extract<operadores26, "in">]?: boolean[]
|
|
||||||
}
|
|
||||||
|
|
||||||
/* =============================================================================
|
|
||||||
SELEÇÃO AUTOMÁTICA DE OPERADORES BASEADA NO TIPO DO CAMPO
|
|
||||||
============================================================================= */
|
|
||||||
|
|
||||||
type PgOpsFor<V> = V extends number
|
|
||||||
? PgOpsNumber
|
|
||||||
: V extends string
|
|
||||||
? PgOpsString
|
|
||||||
: V extends boolean
|
|
||||||
? PgOpsBoolean
|
|
||||||
: never
|
|
||||||
|
|
||||||
/* =============================================================================
|
|
||||||
UTILITÁRIO: DETECTAR OBJETO PLANO
|
|
||||||
============================================================================= */
|
|
||||||
|
|
||||||
type IsPlainObject<T> = T extends object
|
|
||||||
? T extends Function
|
|
||||||
? false
|
|
||||||
: T extends readonly any[]
|
|
||||||
? false
|
|
||||||
: true
|
|
||||||
: false
|
|
||||||
|
|
||||||
/* =============================================================================
|
|
||||||
FILTRO RECURSIVO POR CAMPOS
|
|
||||||
============================================================================= */
|
|
||||||
|
|
||||||
type FiltroCampos<T> = {
|
|
||||||
[K in keyof T]?: IsPlainObject<T[K]> extends true ? tipoFiltro26<T[K]> : PgOpsFor<T[K]>
|
|
||||||
}
|
|
||||||
|
|
||||||
/* =============================================================================
|
|
||||||
TIPO PRINCIPAL EXPORTADO
|
|
||||||
============================================================================= */
|
|
||||||
|
|
||||||
export type tipoFiltro26<T> = FiltroCampos<T> & {
|
|
||||||
/**
|
|
||||||
* E => AND lógico
|
|
||||||
* Todos os filtros dentro do array devem ser verdadeiros.
|
|
||||||
*/
|
|
||||||
E?: tipoFiltro26<T>[]
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OU => OR lógico
|
|
||||||
* Pelo menos um filtro dentro do array deve ser verdadeiro.
|
|
||||||
*/
|
|
||||||
OU?: tipoFiltro26<T>[]
|
|
||||||
}
|
|
||||||
|
|
||||||
/* =============================================================================
|
|
||||||
VALIDAÇÃO ESTRUTURAL (ZOD)
|
|
||||||
============================================================================= */
|
|
||||||
|
|
||||||
const zOperadores = z.nativeEnum(operadores26)
|
|
||||||
const zValor = z.any()
|
|
||||||
const zCondicao = z.record(zOperadores, zValor)
|
|
||||||
|
|
||||||
export const zFiltro26: z.ZodType<any> = z.lazy(() =>
|
|
||||||
z
|
|
||||||
.object({
|
|
||||||
E: z.array(zFiltro26).optional(),
|
|
||||||
OU: z.array(zFiltro26).optional(),
|
|
||||||
})
|
|
||||||
.catchall(z.union([zCondicao, zFiltro26])),
|
|
||||||
)
|
|
||||||
|
|
||||||
/* =============================================================================
|
|
||||||
EXEMPLO DE USO
|
|
||||||
============================================================================= */
|
|
||||||
|
|
||||||
type Pessoa = {
|
|
||||||
codigo: string
|
|
||||||
nome: string
|
|
||||||
idade: number
|
|
||||||
carro: {
|
|
||||||
modelo: string
|
|
||||||
ano: number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const criarFiltro26 = <T>(filtro: tipoFiltro26<T>) => filtro
|
|
||||||
|
|
||||||
const _filtro = criarFiltro26<Pessoa>({
|
|
||||||
idade: { [operadores26[">="]]: 18 },
|
|
||||||
|
|
||||||
[agrupadores26.OU]: [
|
|
||||||
{ nome: { [operadores26.like]: "%pa%" } },
|
|
||||||
{
|
|
||||||
[agrupadores26.E]: [
|
|
||||||
{ carro: { ano: { [operadores26["="]]: 2020 } } },
|
|
||||||
{ carro: { modelo: { [operadores26.in]: ["Civic", "Corolla"] } } },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue