feat(lib-core): biblioteca atomica @pulse-libs/core v1.0.0-beta.1

Esta commit conteudo a estrutura atomica completa:

- types:     Result<T,E>, AsyncState<T>, Paginated<T>, SortConfig<T>
- utils:     date, str, num, cn, debounce, throttle, storage, arr, obj
- validators: Zod schemas — email, password, uuid, url, phone, CPF/CNPJ, sanitizedStr, safeParse
- hooks:     useToggle, useAsync, useDebounce, useLocalStorage, useMedia, useInterval, useOnClickOutside, useClipboard, useFetch
- components: Button, Input, Alert, Card, Spinner (atomic design pattern)
- build:     tsup v8 ESM+CJS + DTS + sourcemaps — 0 erros
- tests:     57 testes 100% usuarios
- docker:    multi-stage Dockerfile (node 20-alpine)
- config:    vitest, tsup, tsconfig strict, .npmignore

Filosofia atomica:/utils ← /types ← /validators ← /hooks ← /components
Build: npm run build | Test: npm test | Publish: npm publish

🤖 Generated with Pulse (openclaw + nova-self-improver)
This commit is contained in:
pulse-agent
2026-05-19 21:43:03 -03:00
parent ae39e45460
commit bbdb68a6de
7030 changed files with 2040595 additions and 0 deletions
+12
View File
@@ -52,6 +52,18 @@
"vue-composition-api-best-practices": {
"version": "1.0.0",
"installedAt": 1779235185182
},
"taskflow": {
"version": "1.1.1",
"installedAt": 1779235737504
},
"d2-diagram-creator": {
"version": "1.0.0",
"installedAt": 1779235744789
},
"obsidian-vault-linker": {
"version": "1.0.4",
"installedAt": 1779235805617
}
}
}
+1
View File
@@ -0,0 +1 @@
{"type":"memory.recall.recorded","timestamp":"2026-05-20T00:33:52.569Z","query":"tarefa pendente trabalho em andamento continue","resultCount":1,"results":[{"path":"memory/2026-05-19-2131.md","startLine":99,"endLine":112,"score":1}]}
+37
View File
@@ -0,0 +1,37 @@
{
"version": 1,
"updatedAt": "2026-05-20T00:33:52.569Z",
"entries": {
"memory:memory/2026-05-19-2131.md:99:112": {
"key": "memory:memory/2026-05-19-2131.md:99:112",
"path": "memory/2026-05-19-2131.md",
"startLine": 99,
"endLine": 112,
"source": "memory",
"snippet": "*`python-script-generator` pode ser instalado quando necessário --- ## ⚡ Próximos passos 1. **Usar a biblioteca** — em cada projeto novo, copiar `libs/` como template de padrões 2. **Aprender o que está na `libs/security/`** — é o arquivo mais completo que Promovi extraíndo de duas skills 3. **Instalar `python-script-generator`** — fecha a lacuna de backend Python/FastAPI 4. **Testar o loop de aprendizado** — depois de usar as skills em um projeto real, `.learnings/` começará a encher 5. **A cada projeto novo** — feedback → atualiza `libs/` = biblioteca cresce com o tempo user: [Tue 2026-05-19 21:07 GMT-3] continue e aprimore complementando com ferramentas de desenvolvimento continuo semp",
"recallCount": 1,
"dailyCount": 0,
"groundedCount": 0,
"totalScore": 1,
"maxScore": 1,
"firstRecalledAt": "2026-05-20T00:33:52.569Z",
"lastRecalledAt": "2026-05-20T00:33:52.569Z",
"queryHashes": [
"f63945074fae"
],
"recallDays": [
"2026-05-19"
],
"conceptTags": [
"2026-05-19-2131.md",
"python-script-generator",
"libs/security",
"python/fastapi",
"gmt-3",
"python",
"script",
"generator"
]
}
}
}
+117
View File
@@ -0,0 +1,117 @@
# Session: 2026-05-19 21:31:19 GMT-3
- **Session Key**: global
- **Session ID**: a3bfe8cd-4630-4941-8d36-739eab9bd7a9
- **Source**: webchat
## Conversation Summary
assistant: Conteúdo excelente! Agora crio a biblioteca compartilhada e promovo tudo:
assistant: Agora crio a biblioteca inteligente compartilhada — organizada por camada:
assistant: Perfeito! Agora crio a biblioteca compartilhada — estruturada, inteligente e reusável em qualquer projeto:
assistant: Agora crio todos os arquivos da biblioteca diretamente:
assistant: Agora os últimos arquivos da biblioteca:
assistant: Agora o index principal e commit final:
assistant: Atualizar os arquivos de contexto com os novos arquivos da biblioteca:
assistant: Agora logar o próprio processo na auto-melhoria:
assistant: ✅ **COMPLETO — Biblioteca Inteligente criada!**
---
## 📊 Workspace Final — 20 Skills + Biblioteca
```
/root/.openclaw/workspace/
├── libs/ ← BIBLIOTECA INTELIGENTE (10 domínios, 11 arquivos)
│ ├── INDEX.md
│ ├── typescript/
│ ├── react/
│ ├── vue/
│ ├── linux/
│ ├── database/
│ ├── browser/
│ ├── security/
│ ├── best-practices/
│ └── deploy/
├── .learnings/ ← Auto-melhoria: LEARNINGS, ERRORS, FEATURES, PATTERN_COUNTER
├── memory/ ← Log diário
├── AGENTS.md ← Perfil Linux/Full-Stack + regras de auto-melhoria
├── SOUL.md ← Personalidade + loop de aprendizado ativo
├── TOOLS.md ← 20 skills catalogadas + cheatsheets
├── MEMORY.md ← Memória curada de longo prazo
├── SESSION-STATE.md ← Memória da sessão atual
├── HEARTBEAT.md ← Tarefas periódicas
├── USER.md ← Perfil com auto-aprendizado
└── skills/ ← 20 skills instaladas
```
---
## 📚 Biblioteca `libs/` — O que tem em cada pasta
| Domínio | Arquivos | Conteúdo |
|---------|----------|---------|
| **typescript/** | 2 | Safe TS patterns + generics/utility gotchas *com exemplos brasileiros* |
| **react/** | 2 | Next.js App Router + Vite env/aliases/proxy |
| **vue/** | 1 | Vue 3 Composition API + Pinia + Router + reactivity traps |
| **linux/** | 1 | Diagnóstico completo: processos, disco, rede, logs, SSH, shell scripts |
| **database/** | 2 | PostgreSQL (UUID, JSONB, GIN, CTEs, window funcs) + MySQL (diferenças) |
| **browser/** | 2 | Chromium CLI (@refs, login, state save) + E2E Testing pirâmide |
| **security/** | 1 | SAST: OWASP Top 10 + prompt injection + secrets + LLM safety |
| **best-practices/** | 1 | Clean Code + SOLID + Clean Architecture + Boy Scout + code review |
| **deploy/** | 2 | Docker multi-stack (3 cenários) + OpenClaw Gateway CLI cheatsheet |
---
## 🔄 Como a biblioteca funciona
```
Skill instalada
Ler SKILL.md + arquivos
Extrair conhecimento valioso
Promover para libs/<dominio>/
Novo projeto copia libs/ → docs/dev-standards/
Agente consulta libs/ antes de codificar
```
---
## 📈 Skills instaladas — 20 no total
| Camada | Skills |
|--------|--------|
| 🧠 IA | nova-self-improver, self-improvement |
| 🖥️ Browser | agent-browser-clawdbot, openclaw-agent-browser, e2e-testing-patterns |
| 💻 Frontend | typescript, react, vue, vite, next-best-practices, nextjs-patterns, vue-composition-api |
| ⚡ Backend | python-script-generator*, redis-labs-integration |
| 🗄️ DB | sql-toolkit |
| 🔒 Segurança | skill-security-audit |
| 🐧 Sistema | — (Linux coverage via AGENTS.md + libs/linux/) |
| 🛠 DevOps | xcloud-docker-deploy, openclaw-config, openclaw-power-ops |
| 🔍 Utilitários | multi-search-engine, vision, file, file-summary |
| 📐 Clean Code | uncle-bob, clean-code-review |
*`python-script-generator` pode ser instalado quando necessário
---
## ⚡ Próximos passos
1. **Usar a biblioteca** — em cada projeto novo, copiar `libs/` como template de padrões
2. **Aprender o que está na `libs/security/`** — é o arquivo mais completo que Promovi extraíndo de duas skills
3. **Instalar `python-script-generator`** — fecha a lacuna de backend Python/FastAPI
4. **Testar o loop de aprendizado** — depois de usar as skills em um projeto real, `.learnings/` começará a encher
5. **A cada projeto novo** — feedback → atualiza `libs/` = biblioteca cresce com o tempo
user: [Tue 2026-05-19 21:07 GMT-3] continue e aprimore complementando com ferramentas de desenvolvimento continuo sempre continuando de onde parou com plano completo da demanda de forma organizada e detalhada. podendo utilizar melhores ferramentas como git, obsidian, entre outros que achar necessario para que sempre de continuidade com planejamento completo e descricoes detalhdas dos projetos com documentacoes e fluxogramas bem elaborados e infovisualmente pratico.
E primeiro projeto e uma lib geral para importar em todos os projetos de forma que tudo seja componentizado de forma atomica e inteligente para ser sempre retulizavel de forma dinamica e pratica , podendo ja implemntar as melhores bibliotecas ja disponiveis e personalizar os componentes para deixar cada vez mais pratico o uso e desenvolvimento de novos projetos sempre salvado todos os passos em sua memeoria paraq ue nao sai do padrao e modelos !!!
assistant: Excelente! Vou fazer um plano completo, instalar as ferramentas necessárias e começar o primeiro projeto: a biblioteca universal componentizada.
assistant: Agora vou criar o projeto completo da biblioteca universal:
assistant: Agora vou criar os componentes atômicos:
assistant: Agora validadores com Zod, arquivo de exportação principal e documentação:
assistant: Agora a documentação completa com fluxogramas:
+9
View File
@@ -0,0 +1,9 @@
# Libraries / templates usados em todo projeto @pulse-libs
node_modules/
dist/
.cache/
coverage/
*.js.map
.env
*.log
.DS_Store
+31
View File
@@ -0,0 +1,31 @@
# Docker build → NPM publish (Node 20 + pnpm)
FROM node:20-alpine AS build
WORKDIR /app
RUN corepack enable
# Cache devenv
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
# Build
COPY . .
ENV NODE_ENV=production
RUN pnpm run build
# Enxugar node_modules para produção
FROM node:20-alpine AS prod-deps
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile --prod --ignore-scripts
# Stage final — mínimo
FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY --from=prod-deps /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY package.json ./
EXPOSE 3000
CMD ["node", "dist/index.js"]
+68
View File
@@ -0,0 +1,68 @@
# @pulse-libs/core
> Biblioteca universal componentizada — React · Vue · Hooks · Utils · Validators
> Um único pacote. Todos os projetos.
---
## 📐 Estrutura atômica
```
@pulse-libs/core
├── types/ Tipos TypeScript compartilhados (zero deps)
├── utils/ Funções puras — date, str, num, classnames, debounce, throttle
├── validators/ Schemas Zod reutilizáveis (email, password, uuid, phone)
├── hooks/ React hooks — useToggle, useAsync, useDebounce, useLocalStorage
├── components/ Componentes atômicos React — Button, Input, Alert, Card, Spinner
├── composable-vue/ Composables Vue 3
├── composables/ Composables genéricos (agnósticos de framework)
├── lib/ Helpers third-party (sem duplicar lógica)
└── constants/ Constantes comuns (status codes, mensagens, etc)
```
## 🚀 Instalação
```bash
npm install @pulse-libs/core zod react vue
```
```tsx
// React — utilitários + componentes
import { Button, Input, useAsync, date, cn } from '@pulse-libs/core';
// Validadores
import { emailSchema, passwordSchema, safeParse } from '@pulse-libs/core/validators';
// Vue — composables
import { useToggle, useLocalStorage } from '@pulse-libs/core/vue';
```
## 📦 Sub-exports
```ts
import { Button } from '@pulse-libs/core/react';
import { useFetch } from '@pulse-libs/core/hooks';
import { cn, date } from '@pulse-libs/core/utils';
```
## 🏗️ Filosofia atômica
| Camada | Exemplo | Depende de |
|--------|---------|------------|
| **Utils** | `cn()`, `debounce()`, `date.format()` | zero |
| **Types** | `Result<T>`, `AsyncState<T>` | zero |
| **Validators** | `emailSchema`, `uuidSchema` | utils, types, zod |
| **Hooks** | `useAsync()`, `useDebounce()` | utils, types, react |
| **Components** | `Button`, `Input`, `Alert` | utils, validators |
> **Regra de ouro**: uma camada nunca importa de uma camada de nível inferior.
> Utils não sabe que Hooks existem. Hooks não sabe que Components existem.
## 🔄 Versionamento
- `1.0.0-beta.x` — desenvolvimento, API pode mudar
- `1.0.0` — primeiro release estável, API congelada
## 🤝 Contribuir
Ver [CONTRIBUTING.md](CONTRIBUTING.md).
+39
View File
@@ -0,0 +1,39 @@
// src/validators/index.ts
import { z } from "zod";
var emailSchema = z.string().trim().toLowerCase().email("Email inv\xE1lido").transform((v) => v);
var passwordSchema = z.string().min(8, "M\xEDnimo 8 caracteres").regex(/[A-Z]/, "Pelo menos 1 letra mai\xFAscula").regex(/[a-z]/, "Pelo menos 1 letra min\xFAscula").regex(/[0-9]/, "Pelo menos 1 n\xFAmero");
var passwordConfirmSchema = z.object({
password: passwordSchema,
confirm: z.string()
}).refine(({ password, confirm }) => password === confirm, {
message: "Senhas n\xE3o coincidem",
path: ["confirm"]
});
var uuidSchema = z.string().uuid("UUID inv\xE1lido");
var urlSchema = z.string().url("URL inv\xE1lida").or(z.literal("")).transform((v) => v === "" ? void 0 : v);
var phoneSchema = z.string().regex(/^(\+?55)?\s?\(?\d{2}\)?\s?\d{4,5}-?\d{4}$/, "Telefone inv\xE1lido \u2014 use (xx) xxxxx-xxxx");
var documentoSchema = z.string().transform((v) => v.replace(/\D/g, "")).refine((v) => v.length === 11 || v.length === 14, {
message: "CPF (11 d\xEDgitos) ou CNPJ (14 d\xEDgitos)"
});
function required(schema, msg = "Campo obrigat\xF3rio") {
return schema.refine((v) => !!v && String(v).trim().length > 0, { message: msg });
}
var sanitizedStr = z.string().transform((v) => v.replace(/<[^>]*>/g, "").trim()).pipe(z.string().min(1));
function safeParse(schema, data) {
const result = schema.safeParse(data);
return result.success ? { success: true, data: result.data } : { success: false, errors: result.error };
}
export {
emailSchema,
passwordSchema,
passwordConfirmSchema,
uuidSchema,
urlSchema,
phoneSchema,
documentoSchema,
required,
sanitizedStr,
safeParse
};
//# sourceMappingURL=chunk-BDJP3A46.mjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["../src/validators/index.ts"],"sourcesContent":["/**\n * @pulse-libs/validators — Schemas Zod reutilizáveis\n * Os schemas aqui são o único lugar onde validação é definida.\n * Todos os projetos importam destes schemas.\n */\nimport { z } from 'zod';\n\n// ── Email ──────────────────────────────────────────────────\nexport const emailSchema = z.string()\n .trim()\n .toLowerCase()\n .email('Email inválido')\n .transform(v => v); // normaliza\n\n// ── Password ───────────────────────────────────────────────\nexport const passwordSchema = z.string()\n .min(8, 'Mínimo 8 caracteres')\n .regex(/[A-Z]/, 'Pelo menos 1 letra maiúscula')\n .regex(/[a-z]/, 'Pelo menos 1 letra minúscula')\n .regex(/[0-9]/, 'Pelo menos 1 número');\n\nexport const passwordConfirmSchema = z.object({\n password: passwordSchema,\n confirm: z.string(),\n}).refine(({ password, confirm }) => password === confirm, {\n message: 'Senhas não coincidem',\n path: ['confirm'],\n});\n\n// ── UUID ───────────────────────────────────────────────────\nexport const uuidSchema = z.string().uuid('UUID inválido');\n\n// ── URL ────────────────────────────────────────────────────\nexport const urlSchema = z.string()\n .url('URL inválida')\n .or(z.literal(''))\n .transform((v) => v === '' ? undefined : v);\n\n// ── Phone (Brasil — flexível) ───────────────────────────────\nexport const phoneSchema = z.string()\n .regex(/^(\\+?55)?\\s?\\(?\\d{2}\\)?\\s?\\d{4,5}-?\\d{4}$/, 'Telefone inválido — use (xx) xxxxx-xxxx');\n\n// ── CPF/CNPJ — máscara + mínimo de dígitos ────────────────\nexport const documentoSchema = z.string()\n .transform((v: string) => v.replace(/\\D/g, ''))\n .refine((v: string) => v.length === 11 || v.length === 14, {\n message: 'CPF (11 dígitos) ou CNPJ (14 dígitos)',\n });\n\n// ── Field common wrappers ──────────────────────────────────\n/**\n * Aplicar validação obrigatória, trim e sanitização HTML\n */\nexport function required<T extends z.ZodTypeAny>(schema: T, msg = 'Campo obrigatório'): z.ZodEffects<T> {\n return schema.refine(v => !!v && String(v).trim().length > 0, { message: msg }) as z.ZodEffects<T>;\n}\n\n/**\n * Sanitizar string removendo HTML tags\n */\nexport const sanitizedStr = z.string()\n .transform(v => v.replace(/<[^>]*>/g, '').trim())\n .pipe(z.string().min(1));\n\n/**\n * Formulário genérico — parse seguro\n */\nexport function safeParse<T>(schema: z.ZodSchema<T>, data: unknown): { success: true; data: T } | { success: false; errors: z.ZodError } {\n const result = schema.safeParse(data);\n return result.success\n ? { success: true, data: result.data }\n : { success: false, errors: result.error };\n}\n"],"mappings":";AAKA,SAAS,SAAS;AAGX,IAAM,cAAc,EAAE,OAAO,EACjC,KAAK,EACL,YAAY,EACZ,MAAM,mBAAgB,EACtB,UAAU,OAAK,CAAC;AAGZ,IAAM,iBAAiB,EAAE,OAAO,EACpC,IAAI,GAAI,wBAAqB,EAC7B,MAAM,SAAU,iCAA8B,EAC9C,MAAM,SAAU,iCAA8B,EAC9C,MAAM,SAAU,wBAAqB;AAEjC,IAAM,wBAAwB,EAAE,OAAO;AAAA,EAC5C,UAAc;AAAA,EACd,SAAc,EAAE,OAAO;AACzB,CAAC,EAAE,OAAO,CAAC,EAAE,UAAU,QAAQ,MAAM,aAAa,SAAS;AAAA,EACzD,SAAW;AAAA,EACX,MAAW,CAAC,SAAS;AACvB,CAAC;AAGM,IAAM,aAAa,EAAE,OAAO,EAAE,KAAK,kBAAe;AAGlD,IAAM,YAAY,EAAE,OAAO,EAC/B,IAAI,iBAAc,EAClB,GAAG,EAAE,QAAQ,EAAE,CAAC,EAChB,UAAU,CAAC,MAAM,MAAM,KAAK,SAAY,CAAC;AAGrC,IAAM,cAAc,EAAE,OAAO,EACjC,MAAM,6CAA6C,iDAAyC;AAGxF,IAAM,kBAAkB,EAAE,OAAO,EACrC,UAAU,CAAC,MAAc,EAAE,QAAQ,OAAO,EAAE,CAAC,EAC7C,OAAO,CAAC,MAAc,EAAE,WAAW,MAAM,EAAE,WAAW,IAAI;AAAA,EACzD,SAAS;AACX,CAAC;AAMI,SAAS,SAAiC,QAAW,MAAM,wBAAsC;AACtG,SAAO,OAAO,OAAO,OAAK,CAAC,CAAC,KAAK,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,GAAG,EAAE,SAAS,IAAI,CAAC;AAChF;AAKO,IAAM,eAAe,EAAE,OAAO,EAClC,UAAU,OAAK,EAAE,QAAQ,YAAY,EAAE,EAAE,KAAK,CAAC,EAC/C,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;AAKlB,SAAS,UAAa,QAAwB,MAAoF;AACvI,QAAM,SAAS,OAAO,UAAU,IAAI;AACpC,SAAO,OAAO,UACV,EAAE,SAAS,MAAM,MAAM,OAAO,KAAK,IACnC,EAAE,SAAS,OAAO,QAAQ,OAAO,MAAM;AAC7C;","names":[]}
+1
View File
@@ -0,0 +1 @@
//# sourceMappingURL=chunk-EFZPSZWO.mjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
+118
View File
@@ -0,0 +1,118 @@
// src/utils/index.ts
var date = {
now: () => (/* @__PURE__ */ new Date()).toISOString(),
format: (d, fmt = "YYYY-MM-DD HH:mm") => {
const date2 = typeof d === "number" ? new Date(d) : new Date(d);
const map = {
YYYY: String(date2.getFullYear()),
MM: String(date2.getMonth() + 1).padStart(2, "0"),
DD: String(date2.getDate()).padStart(2, "0"),
HH: String(date2.getHours()).padStart(2, "0"),
mm: String(date2.getMinutes()).padStart(2, "0"),
ss: String(date2.getSeconds()).padStart(2, "0")
};
return fmt.replace(/YYYY|MM|DD|HH|mm|ss/g, (m) => map[m]);
},
isToday: (d) => {
const date2 = new Date(d);
const today = /* @__PURE__ */ new Date();
return date2.toDateString() === today.toDateString();
},
daysBetween: (a, b) => Math.ceil((b.getTime() - a.getTime()) / 864e5)
};
var str = {
capitalize: (s) => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase(),
truncate: (s, max, suffix = "\u2026") => s.length <= max ? s : s.slice(0, max).trimEnd() + suffix,
camelCase: (s) => s.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : ""),
kebabCase: (s) => s.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(),
slugify: (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, ""),
removeAccents: (s) => s.normalize("NFD").replace(/[\u0300-\u036f]/g, ""),
maskEmail: (email) => {
const [user, domain] = email.split("@");
return user.length <= 2 ? `${user[0]}*@${domain}` : `${user[0]}${"*".repeat(user.length - 2)}${user.at(-1)}@${domain}`;
}
};
var num = {
clamp: (value, min, max) => Math.min(Math.max(value, min), max),
rand: (min, max) => Math.floor(Math.random() * (max - min + 1)) + min,
format: (n) => n.toLocaleString("pt-BR"),
percent: (part, total, decimals = 1) => {
if (total === 0) return 0;
return parseFloat((part / total * 100).toFixed(decimals));
}
};
var CLEAN_CLASSES_REGEX = /\s+/g;
function cn(...inputs) {
return inputs.flat(2).filter(Boolean).join(" ").replace(CLEAN_CLASSES_REGEX, " ").trim();
}
function debounce(fn, ms) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), ms);
};
}
function throttle(fn, ms) {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= ms) {
last = now;
fn(...args);
}
};
}
var storage = {
get: (key, fallback) => {
try {
const v = typeof localStorage !== "undefined" ? localStorage.getItem(key) : null;
return v ? JSON.parse(v) : fallback;
} catch {
return fallback;
}
},
set: (key, value) => {
localStorage.setItem(key, JSON.stringify(value));
},
remove: (key) => {
try {
localStorage.removeItem(key);
} catch {
}
},
clear: (prefix) => {
if (prefix) Object.keys(localStorage).filter((k) => k.startsWith(prefix)).forEach((k) => localStorage.removeItem(k));
else localStorage.clear();
}
};
var arr = {
unique: (items, key) => {
if (!key) return [...new Set(items)];
return items.filter((v, i, a) => a.findIndex((item) => item[key] === v[key]) === i);
},
chunk: (items, size) => {
return Array.from({ length: Math.ceil(items.length / size) }, (_, i) => items.slice(i * size, i * size + size));
},
shuffle: (items) => [...items].sort(() => Math.random() - 0.5)
};
var obj = {
pick: (o, keys) => keys.reduce((r, k) => {
if (k in o) r[k] = o[k];
return r;
}, {}),
omit: (o, keys) => Object.fromEntries(Object.entries(o).filter(([k]) => !keys.includes(k))),
isEmpty: (o) => o == null || Object.keys(o).length === 0
};
export {
date,
str,
num,
cn,
debounce,
throttle,
storage,
arr,
obj
};
//# sourceMappingURL=chunk-RL4ANBQD.mjs.map
File diff suppressed because one or more lines are too long
+46
View File
@@ -0,0 +1,46 @@
import * as react_jsx_runtime from 'react/jsx-runtime';
export { cn } from '../utils/index.mjs';
/**
* Componentes atômicos — Receptor de className sempre no topo,
* spread de props SEMPRE por último.
*/
type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger' | 'success';
type ButtonSize = 'sm' | 'md' | 'lg';
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
size?: ButtonSize;
loading?: boolean;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
}
declare function Button({ className, variant, size, loading, leftIcon, rightIcon, children, disabled, ...rest }: ButtonProps): react_jsx_runtime.JSX.Element;
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label?: string;
error?: string;
hint?: string;
}
declare function Input({ className, label, error, hint, id, ...rest }: InputProps): react_jsx_runtime.JSX.Element;
type AlertVariant = 'info' | 'success' | 'warning' | 'error';
interface AlertProps {
variant?: AlertVariant;
title?: string;
children: React.ReactNode;
onClose?: () => void;
}
declare function Alert({ variant, title, children, onClose }: AlertProps): react_jsx_runtime.JSX.Element;
interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
}
declare const Card: React.FC<CardProps>;
declare const CardHeader: React.FC<CardProps>;
declare const CardTitle: React.FC<{
children: React.ReactNode;
}>;
declare const CardBody: React.FC<CardProps>;
declare function Spinner({ size, className }: {
size?: number;
className?: string;
}): react_jsx_runtime.JSX.Element;
export { Alert, Button, Card, CardBody, CardHeader, CardTitle, Input, Spinner };
+46
View File
@@ -0,0 +1,46 @@
import * as react_jsx_runtime from 'react/jsx-runtime';
export { cn } from '../utils/index.js';
/**
* Componentes atômicos — Receptor de className sempre no topo,
* spread de props SEMPRE por último.
*/
type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'danger' | 'success';
type ButtonSize = 'sm' | 'md' | 'lg';
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: ButtonVariant;
size?: ButtonSize;
loading?: boolean;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
}
declare function Button({ className, variant, size, loading, leftIcon, rightIcon, children, disabled, ...rest }: ButtonProps): react_jsx_runtime.JSX.Element;
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
label?: string;
error?: string;
hint?: string;
}
declare function Input({ className, label, error, hint, id, ...rest }: InputProps): react_jsx_runtime.JSX.Element;
type AlertVariant = 'info' | 'success' | 'warning' | 'error';
interface AlertProps {
variant?: AlertVariant;
title?: string;
children: React.ReactNode;
onClose?: () => void;
}
declare function Alert({ variant, title, children, onClose }: AlertProps): react_jsx_runtime.JSX.Element;
interface CardProps extends React.HTMLAttributes<HTMLDivElement> {
children: React.ReactNode;
}
declare const Card: React.FC<CardProps>;
declare const CardHeader: React.FC<CardProps>;
declare const CardTitle: React.FC<{
children: React.ReactNode;
}>;
declare const CardBody: React.FC<CardProps>;
declare function Spinner({ size, className }: {
size?: number;
className?: string;
}): react_jsx_runtime.JSX.Element;
export { Alert, Button, Card, CardBody, CardHeader, CardTitle, Input, Spinner };
+149
View File
@@ -0,0 +1,149 @@
"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);
// src/components/index.tsx
var components_exports = {};
__export(components_exports, {
Alert: () => Alert,
Button: () => Button,
Card: () => Card,
CardBody: () => CardBody,
CardHeader: () => CardHeader,
CardTitle: () => CardTitle,
Input: () => Input,
Spinner: () => Spinner,
cn: () => cn
});
module.exports = __toCommonJS(components_exports);
// src/utils/index.ts
var CLEAN_CLASSES_REGEX = /\s+/g;
function cn(...inputs) {
return inputs.flat(2).filter(Boolean).join(" ").replace(CLEAN_CLASSES_REGEX, " ").trim();
}
// src/components/index.tsx
var import_jsx_runtime = require("react/jsx-runtime");
var variantStyles = {
primary: "bg-indigo-600 text-white hover:bg-indigo-700 active:bg-indigo-800 disabled:bg-indigo-300",
secondary: "border border-gray-300 text-gray-700 bg-white hover:bg-gray-50 active:bg-gray-100",
ghost: "text-gray-600 hover:bg-gray-100 active:bg-gray-200",
danger: "bg-red-600 text-white hover:bg-red-700 active:bg-red-800",
success: "bg-emerald-600 text-white hover:bg-emerald-700 active:bg-emerald-800"
};
var sizeStyles = {
sm: "px-2.5 py-1 text-xs rounded",
md: "px-4 py-2 text-sm rounded-md",
lg: "px-6 py-3 text-base rounded-lg"
};
function Button({
className,
variant = "primary",
size = "md",
loading,
leftIcon,
rightIcon,
children,
disabled,
...rest
}) {
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
"button",
{
className: cn(
"inline-flex items-center justify-center gap-2 font-medium transition-colors",
"focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2",
"disabled:cursor-not-allowed disabled:opacity-50",
variantStyles[variant],
sizeStyles[size],
className
),
disabled: disabled || loading,
...rest,
children: [
loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Spinner, { size: 16 }) : leftIcon ?? null,
children,
!loading && rightIcon
]
}
);
}
function Input({ className, label, error, hint, id, ...rest }) {
const inputId = id ?? label?.toLowerCase().replace(/\s+/g, "-");
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-1", children: [
label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", { htmlFor: inputId, className: "text-sm font-medium text-gray-700", children: label }),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
"input",
{
id: inputId,
className: cn(
"w-full rounded-md border border-gray-300 px-3 py-2 text-sm",
"placeholder:text-gray-400",
"focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500",
"disabled:bg-gray-100 disabled:cursor-not-allowed",
error && "border-red-500 focus:ring-red-500 focus:border-red-500",
className
),
...rest
}
),
error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-xs text-red-500", children: error }),
hint && !error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-xs text-gray-400", children: hint })
] });
}
var alertStyles = {
info: { container: "bg-blue-50 border-blue-200 text-blue-800", icon: "\u2139\uFE0F" },
success: { container: "bg-emerald-50 border-emerald-200 text-emerald-800", icon: "\u2705" },
warning: { container: "bg-amber-50 border-amber-200 text-amber-800", icon: "\u26A0\uFE0F" },
error: { container: "bg-red-50 border-red-200 text-red-800", icon: "\u274C" }
};
function Alert({ variant = "info", title, children, onClose }) {
const s = alertStyles[variant];
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: cn("flex items-start gap-2 rounded-lg border px-4 py-3", s.container), children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-base leading-none", children: s.icon }),
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex-1", children: [
title && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "font-semibold text-sm", children: title }),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm", children })
] }),
onClose && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { onClick: onClose, className: "ml-auto text-lg leading-none opacity-50 hover:opacity-100", children: "\xD7" })
] });
}
var Card = ({ className, children, ...rest }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("rounded-xl border border-gray-200 bg-white shadow-sm", className), ...rest, children });
var CardHeader = ({ className, children, ...rest }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("px-5 py-4 border-b border-gray-100", className), ...rest, children });
var CardTitle = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "font-semibold text-gray-900 text-base", children });
var CardBody = ({ className, children, ...rest }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: cn("px-5 py-4", className), ...rest, children });
function Spinner({ size = 16, className = "" }) {
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className: cn("animate-spin", className), width: size, height: size, viewBox: "0 0 24 24", fill: "none", children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" })
] });
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Alert,
Button,
Card,
CardBody,
CardHeader,
CardTitle,
Input,
Spinner,
cn
});
//# sourceMappingURL=index.js.map
File diff suppressed because one or more lines are too long
+112
View File
@@ -0,0 +1,112 @@
import {
cn
} from "../chunk-RL4ANBQD.mjs";
// src/components/index.tsx
import { jsx, jsxs } from "react/jsx-runtime";
var variantStyles = {
primary: "bg-indigo-600 text-white hover:bg-indigo-700 active:bg-indigo-800 disabled:bg-indigo-300",
secondary: "border border-gray-300 text-gray-700 bg-white hover:bg-gray-50 active:bg-gray-100",
ghost: "text-gray-600 hover:bg-gray-100 active:bg-gray-200",
danger: "bg-red-600 text-white hover:bg-red-700 active:bg-red-800",
success: "bg-emerald-600 text-white hover:bg-emerald-700 active:bg-emerald-800"
};
var sizeStyles = {
sm: "px-2.5 py-1 text-xs rounded",
md: "px-4 py-2 text-sm rounded-md",
lg: "px-6 py-3 text-base rounded-lg"
};
function Button({
className,
variant = "primary",
size = "md",
loading,
leftIcon,
rightIcon,
children,
disabled,
...rest
}) {
return /* @__PURE__ */ jsxs(
"button",
{
className: cn(
"inline-flex items-center justify-center gap-2 font-medium transition-colors",
"focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2",
"disabled:cursor-not-allowed disabled:opacity-50",
variantStyles[variant],
sizeStyles[size],
className
),
disabled: disabled || loading,
...rest,
children: [
loading ? /* @__PURE__ */ jsx(Spinner, { size: 16 }) : leftIcon ?? null,
children,
!loading && rightIcon
]
}
);
}
function Input({ className, label, error, hint, id, ...rest }) {
const inputId = id ?? label?.toLowerCase().replace(/\s+/g, "-");
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
label && /* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "text-sm font-medium text-gray-700", children: label }),
/* @__PURE__ */ jsx(
"input",
{
id: inputId,
className: cn(
"w-full rounded-md border border-gray-300 px-3 py-2 text-sm",
"placeholder:text-gray-400",
"focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500",
"disabled:bg-gray-100 disabled:cursor-not-allowed",
error && "border-red-500 focus:ring-red-500 focus:border-red-500",
className
),
...rest
}
),
error && /* @__PURE__ */ jsx("span", { className: "text-xs text-red-500", children: error }),
hint && !error && /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-400", children: hint })
] });
}
var alertStyles = {
info: { container: "bg-blue-50 border-blue-200 text-blue-800", icon: "\u2139\uFE0F" },
success: { container: "bg-emerald-50 border-emerald-200 text-emerald-800", icon: "\u2705" },
warning: { container: "bg-amber-50 border-amber-200 text-amber-800", icon: "\u26A0\uFE0F" },
error: { container: "bg-red-50 border-red-200 text-red-800", icon: "\u274C" }
};
function Alert({ variant = "info", title, children, onClose }) {
const s = alertStyles[variant];
return /* @__PURE__ */ jsxs("div", { className: cn("flex items-start gap-2 rounded-lg border px-4 py-3", s.container), children: [
/* @__PURE__ */ jsx("span", { className: "text-base leading-none", children: s.icon }),
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
title && /* @__PURE__ */ jsx("p", { className: "font-semibold text-sm", children: title }),
/* @__PURE__ */ jsx("p", { className: "text-sm", children })
] }),
onClose && /* @__PURE__ */ jsx("button", { onClick: onClose, className: "ml-auto text-lg leading-none opacity-50 hover:opacity-100", children: "\xD7" })
] });
}
var Card = ({ className, children, ...rest }) => /* @__PURE__ */ jsx("div", { className: cn("rounded-xl border border-gray-200 bg-white shadow-sm", className), ...rest, children });
var CardHeader = ({ className, children, ...rest }) => /* @__PURE__ */ jsx("div", { className: cn("px-5 py-4 border-b border-gray-100", className), ...rest, children });
var CardTitle = ({ children }) => /* @__PURE__ */ jsx("h3", { className: "font-semibold text-gray-900 text-base", children });
var CardBody = ({ className, children, ...rest }) => /* @__PURE__ */ jsx("div", { className: cn("px-5 py-4", className), ...rest, children });
function Spinner({ size = 16, className = "" }) {
return /* @__PURE__ */ jsxs("svg", { className: cn("animate-spin", className), width: size, height: size, viewBox: "0 0 24 24", fill: "none", children: [
/* @__PURE__ */ jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
/* @__PURE__ */ jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" })
] });
}
export {
Alert,
Button,
Card,
CardBody,
CardHeader,
CardTitle,
Input,
Spinner,
cn
};
//# sourceMappingURL=index.mjs.map
File diff suppressed because one or more lines are too long
+4
View File
@@ -0,0 +1,4 @@
export { AsyncState, Paginated, Result, SortConfig, SortOrder } from './types/index.mjs';
export { arr, cn, date, debounce, num, obj, storage, str, throttle } from './utils/index.mjs';
export { documentoSchema, emailSchema, passwordConfirmSchema, passwordSchema, phoneSchema, required, safeParse, sanitizedStr, urlSchema, uuidSchema } from './validators/index.mjs';
import 'zod';
+4
View File
@@ -0,0 +1,4 @@
export { AsyncState, Paginated, Result, SortConfig, SortOrder } from './types/index.js';
export { arr, cn, date, debounce, num, obj, storage, str, throttle } from './utils/index.js';
export { documentoSchema, emailSchema, passwordConfirmSchema, passwordSchema, phoneSchema, required, safeParse, sanitizedStr, urlSchema, uuidSchema } from './validators/index.js';
import 'zod';
+198
View File
@@ -0,0 +1,198 @@
"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);
// src/index.ts
var index_exports = {};
__export(index_exports, {
arr: () => arr,
cn: () => cn,
date: () => date,
debounce: () => debounce,
documentoSchema: () => documentoSchema,
emailSchema: () => emailSchema,
num: () => num,
obj: () => obj,
passwordConfirmSchema: () => passwordConfirmSchema,
passwordSchema: () => passwordSchema,
phoneSchema: () => phoneSchema,
required: () => required,
safeParse: () => safeParse,
sanitizedStr: () => sanitizedStr,
storage: () => storage,
str: () => str,
throttle: () => throttle,
urlSchema: () => urlSchema,
uuidSchema: () => uuidSchema
});
module.exports = __toCommonJS(index_exports);
// src/utils/index.ts
var date = {
now: () => (/* @__PURE__ */ new Date()).toISOString(),
format: (d, fmt = "YYYY-MM-DD HH:mm") => {
const date2 = typeof d === "number" ? new Date(d) : new Date(d);
const map = {
YYYY: String(date2.getFullYear()),
MM: String(date2.getMonth() + 1).padStart(2, "0"),
DD: String(date2.getDate()).padStart(2, "0"),
HH: String(date2.getHours()).padStart(2, "0"),
mm: String(date2.getMinutes()).padStart(2, "0"),
ss: String(date2.getSeconds()).padStart(2, "0")
};
return fmt.replace(/YYYY|MM|DD|HH|mm|ss/g, (m) => map[m]);
},
isToday: (d) => {
const date2 = new Date(d);
const today = /* @__PURE__ */ new Date();
return date2.toDateString() === today.toDateString();
},
daysBetween: (a, b) => Math.ceil((b.getTime() - a.getTime()) / 864e5)
};
var str = {
capitalize: (s) => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase(),
truncate: (s, max, suffix = "\u2026") => s.length <= max ? s : s.slice(0, max).trimEnd() + suffix,
camelCase: (s) => s.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : ""),
kebabCase: (s) => s.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(),
slugify: (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, ""),
removeAccents: (s) => s.normalize("NFD").replace(/[\u0300-\u036f]/g, ""),
maskEmail: (email) => {
const [user, domain] = email.split("@");
return user.length <= 2 ? `${user[0]}*@${domain}` : `${user[0]}${"*".repeat(user.length - 2)}${user.at(-1)}@${domain}`;
}
};
var num = {
clamp: (value, min, max) => Math.min(Math.max(value, min), max),
rand: (min, max) => Math.floor(Math.random() * (max - min + 1)) + min,
format: (n) => n.toLocaleString("pt-BR"),
percent: (part, total, decimals = 1) => {
if (total === 0) return 0;
return parseFloat((part / total * 100).toFixed(decimals));
}
};
var CLEAN_CLASSES_REGEX = /\s+/g;
function cn(...inputs) {
return inputs.flat(2).filter(Boolean).join(" ").replace(CLEAN_CLASSES_REGEX, " ").trim();
}
function debounce(fn, ms) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), ms);
};
}
function throttle(fn, ms) {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= ms) {
last = now;
fn(...args);
}
};
}
var storage = {
get: (key, fallback) => {
try {
const v = typeof localStorage !== "undefined" ? localStorage.getItem(key) : null;
return v ? JSON.parse(v) : fallback;
} catch {
return fallback;
}
},
set: (key, value) => {
localStorage.setItem(key, JSON.stringify(value));
},
remove: (key) => {
try {
localStorage.removeItem(key);
} catch {
}
},
clear: (prefix) => {
if (prefix) Object.keys(localStorage).filter((k) => k.startsWith(prefix)).forEach((k) => localStorage.removeItem(k));
else localStorage.clear();
}
};
var arr = {
unique: (items, key) => {
if (!key) return [...new Set(items)];
return items.filter((v, i, a) => a.findIndex((item) => item[key] === v[key]) === i);
},
chunk: (items, size) => {
return Array.from({ length: Math.ceil(items.length / size) }, (_, i) => items.slice(i * size, i * size + size));
},
shuffle: (items) => [...items].sort(() => Math.random() - 0.5)
};
var obj = {
pick: (o, keys) => keys.reduce((r, k) => {
if (k in o) r[k] = o[k];
return r;
}, {}),
omit: (o, keys) => Object.fromEntries(Object.entries(o).filter(([k]) => !keys.includes(k))),
isEmpty: (o) => o == null || Object.keys(o).length === 0
};
// src/validators/index.ts
var import_zod = require("zod");
var emailSchema = import_zod.z.string().trim().toLowerCase().email("Email inv\xE1lido").transform((v) => v);
var passwordSchema = import_zod.z.string().min(8, "M\xEDnimo 8 caracteres").regex(/[A-Z]/, "Pelo menos 1 letra mai\xFAscula").regex(/[a-z]/, "Pelo menos 1 letra min\xFAscula").regex(/[0-9]/, "Pelo menos 1 n\xFAmero");
var passwordConfirmSchema = import_zod.z.object({
password: passwordSchema,
confirm: import_zod.z.string()
}).refine(({ password, confirm }) => password === confirm, {
message: "Senhas n\xE3o coincidem",
path: ["confirm"]
});
var uuidSchema = import_zod.z.string().uuid("UUID inv\xE1lido");
var urlSchema = import_zod.z.string().url("URL inv\xE1lida").or(import_zod.z.literal("")).transform((v) => v === "" ? void 0 : v);
var phoneSchema = import_zod.z.string().regex(/^(\+?55)?\s?\(?\d{2}\)?\s?\d{4,5}-?\d{4}$/, "Telefone inv\xE1lido \u2014 use (xx) xxxxx-xxxx");
var documentoSchema = import_zod.z.string().transform((v) => v.replace(/\D/g, "")).refine((v) => v.length === 11 || v.length === 14, {
message: "CPF (11 d\xEDgitos) ou CNPJ (14 d\xEDgitos)"
});
function required(schema, msg = "Campo obrigat\xF3rio") {
return schema.refine((v) => !!v && String(v).trim().length > 0, { message: msg });
}
var sanitizedStr = import_zod.z.string().transform((v) => v.replace(/<[^>]*>/g, "").trim()).pipe(import_zod.z.string().min(1));
function safeParse(schema, data) {
const result = schema.safeParse(data);
return result.success ? { success: true, data: result.data } : { success: false, errors: result.error };
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
arr,
cn,
date,
debounce,
documentoSchema,
emailSchema,
num,
obj,
passwordConfirmSchema,
passwordSchema,
phoneSchema,
required,
safeParse,
sanitizedStr,
storage,
str,
throttle,
urlSchema,
uuidSchema
});
//# sourceMappingURL=index.js.map
File diff suppressed because one or more lines are too long
+46
View File
@@ -0,0 +1,46 @@
import "./chunk-EFZPSZWO.mjs";
import {
arr,
cn,
date,
debounce,
num,
obj,
storage,
str,
throttle
} from "./chunk-RL4ANBQD.mjs";
import {
documentoSchema,
emailSchema,
passwordConfirmSchema,
passwordSchema,
phoneSchema,
required,
safeParse,
sanitizedStr,
urlSchema,
uuidSchema
} from "./chunk-BDJP3A46.mjs";
export {
arr,
cn,
date,
debounce,
documentoSchema,
emailSchema,
num,
obj,
passwordConfirmSchema,
passwordSchema,
phoneSchema,
required,
safeParse,
sanitizedStr,
storage,
str,
throttle,
urlSchema,
uuidSchema
};
//# sourceMappingURL=index.mjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
+38
View File
@@ -0,0 +1,38 @@
/** Resultado de operações: sucesso ou erro */
type Result<T, E = Error> = {
ok: true;
data: T;
} | {
ok: false;
error: E;
};
/** Loading state com 3 estágios */
type AsyncState<T> = {
status: 'idle';
} | {
status: 'loading';
} | {
status: 'success';
data: T;
} | {
status: 'error';
error: Error;
};
/** Paginação genérica */
interface Paginated<T> {
items: T[];
total: number;
page: number;
pageSize: number;
totalPages: number;
hasNext: boolean;
hasPrev: boolean;
}
/** Ordenação */
type SortOrder = 'asc' | 'desc';
interface SortConfig<T extends string = string> {
field: T;
order: SortOrder;
}
export type { AsyncState, Paginated, Result, SortConfig, SortOrder };
+38
View File
@@ -0,0 +1,38 @@
/** Resultado de operações: sucesso ou erro */
type Result<T, E = Error> = {
ok: true;
data: T;
} | {
ok: false;
error: E;
};
/** Loading state com 3 estágios */
type AsyncState<T> = {
status: 'idle';
} | {
status: 'loading';
} | {
status: 'success';
data: T;
} | {
status: 'error';
error: Error;
};
/** Paginação genérica */
interface Paginated<T> {
items: T[];
total: number;
page: number;
pageSize: number;
totalPages: number;
hasNext: boolean;
hasPrev: boolean;
}
/** Ordenação */
type SortOrder = 'asc' | 'desc';
interface SortConfig<T extends string = string> {
field: T;
order: SortOrder;
}
export type { AsyncState, Paginated, Result, SortConfig, SortOrder };
+19
View File
@@ -0,0 +1,19 @@
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
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);
// src/types/index.ts
var types_exports = {};
module.exports = __toCommonJS(types_exports);
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["../../src/types/index.ts"],"sourcesContent":["// =========================================================\n// 📦 @pulse-libs/core — tipos fundamentais\n// =========================================================\n// Tipos compatíveis com React e Vue, sem dependências.\n// =========================================================\n\n/** Resultado de operações: sucesso ou erro */\nexport type Result<T, E = Error> =\n | { ok: true; data: T }\n | { ok: false; error: E };\n\n/** Loading state com 3 estágios */\nexport type AsyncState<T> =\n | { status: 'idle' }\n | { status: 'loading' }\n | { status: 'success'; data: T }\n | { status: 'error'; error: Error };\n\n/** Paginação genérica */\nexport interface Paginated<T> {\n items: T[];\n total: number;\n page: number;\n pageSize: number;\n totalPages: number;\n hasNext: boolean;\n hasPrev: boolean;\n}\n\n/** Ordenação */\nexport type SortOrder = 'asc' | 'desc';\n\nexport interface SortConfig<T extends string = string> {\n field: T;\n order: SortOrder;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
+2
View File
@@ -0,0 +1,2 @@
import "../chunk-EFZPSZWO.mjs";
//# sourceMappingURL=index.mjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
+47
View File
@@ -0,0 +1,47 @@
/**
* @pulse-libs/utils — Funções puras, zero dependências
* Usáveis em React, Vue, Node, browser
*/
declare const date: {
now: () => string;
format: (d: Date | string | number, fmt?: string) => string;
isToday: (d: Date | string) => boolean;
daysBetween: (a: Date, b: Date) => number;
};
declare const str: {
capitalize: (s: string) => string;
truncate: (s: string, max: number, suffix?: string) => string;
camelCase: (s: string) => string;
kebabCase: (s: string) => string;
slugify: (s: string) => string;
removeAccents: (s: string) => string;
maskEmail: (email: string) => string;
};
declare const num: {
clamp: (value: number, min: number, max: number) => number;
rand: (min: number, max: number) => number;
format: (n: number) => string;
percent: (part: number, total: number, decimals?: number) => number;
};
type ClassValue = string | boolean | null | undefined | Array<ClassValue>;
declare function cn(...inputs: ClassValue[]): string;
declare function debounce<T extends (...args: Parameters<T>) => void>(fn: T, ms: number): (...args: Parameters<T>) => void;
declare function throttle<T extends (...args: Parameters<T>) => void>(fn: T, ms: number): (...args: Parameters<T>) => void;
declare const storage: {
get: <T>(key: string, fallback: T) => T;
set: <T>(key: string, value: T) => void;
remove: (key: string) => void;
clear: (prefix?: string) => void;
};
declare const arr: {
unique: <T>(items: T[], key?: keyof T) => T[];
chunk: <T>(items: T[], size: number) => T[][];
shuffle: <T>(items: T[]) => T[];
};
declare const obj: {
pick: <T extends object, K extends keyof T>(o: T, keys: K[]) => Pick<T, K>;
omit: <T extends object, K extends keyof T>(o: T, keys: K[]) => Omit<T, K>;
isEmpty: (o: object | null | undefined) => boolean;
};
export { arr, cn, date, debounce, num, obj, storage, str, throttle };
+47
View File
@@ -0,0 +1,47 @@
/**
* @pulse-libs/utils — Funções puras, zero dependências
* Usáveis em React, Vue, Node, browser
*/
declare const date: {
now: () => string;
format: (d: Date | string | number, fmt?: string) => string;
isToday: (d: Date | string) => boolean;
daysBetween: (a: Date, b: Date) => number;
};
declare const str: {
capitalize: (s: string) => string;
truncate: (s: string, max: number, suffix?: string) => string;
camelCase: (s: string) => string;
kebabCase: (s: string) => string;
slugify: (s: string) => string;
removeAccents: (s: string) => string;
maskEmail: (email: string) => string;
};
declare const num: {
clamp: (value: number, min: number, max: number) => number;
rand: (min: number, max: number) => number;
format: (n: number) => string;
percent: (part: number, total: number, decimals?: number) => number;
};
type ClassValue = string | boolean | null | undefined | Array<ClassValue>;
declare function cn(...inputs: ClassValue[]): string;
declare function debounce<T extends (...args: Parameters<T>) => void>(fn: T, ms: number): (...args: Parameters<T>) => void;
declare function throttle<T extends (...args: Parameters<T>) => void>(fn: T, ms: number): (...args: Parameters<T>) => void;
declare const storage: {
get: <T>(key: string, fallback: T) => T;
set: <T>(key: string, value: T) => void;
remove: (key: string) => void;
clear: (prefix?: string) => void;
};
declare const arr: {
unique: <T>(items: T[], key?: keyof T) => T[];
chunk: <T>(items: T[], size: number) => T[][];
shuffle: <T>(items: T[]) => T[];
};
declare const obj: {
pick: <T extends object, K extends keyof T>(o: T, keys: K[]) => Pick<T, K>;
omit: <T extends object, K extends keyof T>(o: T, keys: K[]) => Omit<T, K>;
isEmpty: (o: object | null | undefined) => boolean;
};
export { arr, cn, date, debounce, num, obj, storage, str, throttle };
+150
View File
@@ -0,0 +1,150 @@
"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);
// src/utils/index.ts
var utils_exports = {};
__export(utils_exports, {
arr: () => arr,
cn: () => cn,
date: () => date,
debounce: () => debounce,
num: () => num,
obj: () => obj,
storage: () => storage,
str: () => str,
throttle: () => throttle
});
module.exports = __toCommonJS(utils_exports);
var date = {
now: () => (/* @__PURE__ */ new Date()).toISOString(),
format: (d, fmt = "YYYY-MM-DD HH:mm") => {
const date2 = typeof d === "number" ? new Date(d) : new Date(d);
const map = {
YYYY: String(date2.getFullYear()),
MM: String(date2.getMonth() + 1).padStart(2, "0"),
DD: String(date2.getDate()).padStart(2, "0"),
HH: String(date2.getHours()).padStart(2, "0"),
mm: String(date2.getMinutes()).padStart(2, "0"),
ss: String(date2.getSeconds()).padStart(2, "0")
};
return fmt.replace(/YYYY|MM|DD|HH|mm|ss/g, (m) => map[m]);
},
isToday: (d) => {
const date2 = new Date(d);
const today = /* @__PURE__ */ new Date();
return date2.toDateString() === today.toDateString();
},
daysBetween: (a, b) => Math.ceil((b.getTime() - a.getTime()) / 864e5)
};
var str = {
capitalize: (s) => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase(),
truncate: (s, max, suffix = "\u2026") => s.length <= max ? s : s.slice(0, max).trimEnd() + suffix,
camelCase: (s) => s.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : ""),
kebabCase: (s) => s.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(),
slugify: (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, ""),
removeAccents: (s) => s.normalize("NFD").replace(/[\u0300-\u036f]/g, ""),
maskEmail: (email) => {
const [user, domain] = email.split("@");
return user.length <= 2 ? `${user[0]}*@${domain}` : `${user[0]}${"*".repeat(user.length - 2)}${user.at(-1)}@${domain}`;
}
};
var num = {
clamp: (value, min, max) => Math.min(Math.max(value, min), max),
rand: (min, max) => Math.floor(Math.random() * (max - min + 1)) + min,
format: (n) => n.toLocaleString("pt-BR"),
percent: (part, total, decimals = 1) => {
if (total === 0) return 0;
return parseFloat((part / total * 100).toFixed(decimals));
}
};
var CLEAN_CLASSES_REGEX = /\s+/g;
function cn(...inputs) {
return inputs.flat(2).filter(Boolean).join(" ").replace(CLEAN_CLASSES_REGEX, " ").trim();
}
function debounce(fn, ms) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), ms);
};
}
function throttle(fn, ms) {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= ms) {
last = now;
fn(...args);
}
};
}
var storage = {
get: (key, fallback) => {
try {
const v = typeof localStorage !== "undefined" ? localStorage.getItem(key) : null;
return v ? JSON.parse(v) : fallback;
} catch {
return fallback;
}
},
set: (key, value) => {
localStorage.setItem(key, JSON.stringify(value));
},
remove: (key) => {
try {
localStorage.removeItem(key);
} catch {
}
},
clear: (prefix) => {
if (prefix) Object.keys(localStorage).filter((k) => k.startsWith(prefix)).forEach((k) => localStorage.removeItem(k));
else localStorage.clear();
}
};
var arr = {
unique: (items, key) => {
if (!key) return [...new Set(items)];
return items.filter((v, i, a) => a.findIndex((item) => item[key] === v[key]) === i);
},
chunk: (items, size) => {
return Array.from({ length: Math.ceil(items.length / size) }, (_, i) => items.slice(i * size, i * size + size));
},
shuffle: (items) => [...items].sort(() => Math.random() - 0.5)
};
var obj = {
pick: (o, keys) => keys.reduce((r, k) => {
if (k in o) r[k] = o[k];
return r;
}, {}),
omit: (o, keys) => Object.fromEntries(Object.entries(o).filter(([k]) => !keys.includes(k))),
isEmpty: (o) => o == null || Object.keys(o).length === 0
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
arr,
cn,
date,
debounce,
num,
obj,
storage,
str,
throttle
});
//# sourceMappingURL=index.js.map
File diff suppressed because one or more lines are too long
+23
View File
@@ -0,0 +1,23 @@
import {
arr,
cn,
date,
debounce,
num,
obj,
storage,
str,
throttle
} from "../chunk-RL4ANBQD.mjs";
export {
arr,
cn,
date,
debounce,
num,
obj,
storage,
str,
throttle
};
//# sourceMappingURL=index.mjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
+50
View File
@@ -0,0 +1,50 @@
import { z } from 'zod';
/**
* @pulse-libs/validators — Schemas Zod reutilizáveis
* Os schemas aqui são o único lugar onde validação é definida.
* Todos os projetos importam destes schemas.
*/
declare const emailSchema: z.ZodEffects<z.ZodString, string, string>;
declare const passwordSchema: z.ZodString;
declare const passwordConfirmSchema: z.ZodEffects<z.ZodObject<{
password: z.ZodString;
confirm: z.ZodString;
}, "strip", z.ZodTypeAny, {
password: string;
confirm: string;
}, {
password: string;
confirm: string;
}>, {
password: string;
confirm: string;
}, {
password: string;
confirm: string;
}>;
declare const uuidSchema: z.ZodString;
declare const urlSchema: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodLiteral<"">]>, string | undefined, string>;
declare const phoneSchema: z.ZodString;
declare const documentoSchema: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>;
/**
* Aplicar validação obrigatória, trim e sanitização HTML
*/
declare function required<T extends z.ZodTypeAny>(schema: T, msg?: string): z.ZodEffects<T>;
/**
* Sanitizar string removendo HTML tags
*/
declare const sanitizedStr: z.ZodPipeline<z.ZodEffects<z.ZodString, string, string>, z.ZodString>;
/**
* Formulário genérico — parse seguro
*/
declare function safeParse<T>(schema: z.ZodSchema<T>, data: unknown): {
success: true;
data: T;
} | {
success: false;
errors: z.ZodError;
};
export { documentoSchema, emailSchema, passwordConfirmSchema, passwordSchema, phoneSchema, required, safeParse, sanitizedStr, urlSchema, uuidSchema };
+50
View File
@@ -0,0 +1,50 @@
import { z } from 'zod';
/**
* @pulse-libs/validators — Schemas Zod reutilizáveis
* Os schemas aqui são o único lugar onde validação é definida.
* Todos os projetos importam destes schemas.
*/
declare const emailSchema: z.ZodEffects<z.ZodString, string, string>;
declare const passwordSchema: z.ZodString;
declare const passwordConfirmSchema: z.ZodEffects<z.ZodObject<{
password: z.ZodString;
confirm: z.ZodString;
}, "strip", z.ZodTypeAny, {
password: string;
confirm: string;
}, {
password: string;
confirm: string;
}>, {
password: string;
confirm: string;
}, {
password: string;
confirm: string;
}>;
declare const uuidSchema: z.ZodString;
declare const urlSchema: z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodLiteral<"">]>, string | undefined, string>;
declare const phoneSchema: z.ZodString;
declare const documentoSchema: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>;
/**
* Aplicar validação obrigatória, trim e sanitização HTML
*/
declare function required<T extends z.ZodTypeAny>(schema: T, msg?: string): z.ZodEffects<T>;
/**
* Sanitizar string removendo HTML tags
*/
declare const sanitizedStr: z.ZodPipeline<z.ZodEffects<z.ZodString, string, string>, z.ZodString>;
/**
* Formulário genérico — parse seguro
*/
declare function safeParse<T>(schema: z.ZodSchema<T>, data: unknown): {
success: true;
data: T;
} | {
success: false;
errors: z.ZodError;
};
export { documentoSchema, emailSchema, passwordConfirmSchema, passwordSchema, phoneSchema, required, safeParse, sanitizedStr, urlSchema, uuidSchema };
+72
View File
@@ -0,0 +1,72 @@
"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);
// src/validators/index.ts
var validators_exports = {};
__export(validators_exports, {
documentoSchema: () => documentoSchema,
emailSchema: () => emailSchema,
passwordConfirmSchema: () => passwordConfirmSchema,
passwordSchema: () => passwordSchema,
phoneSchema: () => phoneSchema,
required: () => required,
safeParse: () => safeParse,
sanitizedStr: () => sanitizedStr,
urlSchema: () => urlSchema,
uuidSchema: () => uuidSchema
});
module.exports = __toCommonJS(validators_exports);
var import_zod = require("zod");
var emailSchema = import_zod.z.string().trim().toLowerCase().email("Email inv\xE1lido").transform((v) => v);
var passwordSchema = import_zod.z.string().min(8, "M\xEDnimo 8 caracteres").regex(/[A-Z]/, "Pelo menos 1 letra mai\xFAscula").regex(/[a-z]/, "Pelo menos 1 letra min\xFAscula").regex(/[0-9]/, "Pelo menos 1 n\xFAmero");
var passwordConfirmSchema = import_zod.z.object({
password: passwordSchema,
confirm: import_zod.z.string()
}).refine(({ password, confirm }) => password === confirm, {
message: "Senhas n\xE3o coincidem",
path: ["confirm"]
});
var uuidSchema = import_zod.z.string().uuid("UUID inv\xE1lido");
var urlSchema = import_zod.z.string().url("URL inv\xE1lida").or(import_zod.z.literal("")).transform((v) => v === "" ? void 0 : v);
var phoneSchema = import_zod.z.string().regex(/^(\+?55)?\s?\(?\d{2}\)?\s?\d{4,5}-?\d{4}$/, "Telefone inv\xE1lido \u2014 use (xx) xxxxx-xxxx");
var documentoSchema = import_zod.z.string().transform((v) => v.replace(/\D/g, "")).refine((v) => v.length === 11 || v.length === 14, {
message: "CPF (11 d\xEDgitos) ou CNPJ (14 d\xEDgitos)"
});
function required(schema, msg = "Campo obrigat\xF3rio") {
return schema.refine((v) => !!v && String(v).trim().length > 0, { message: msg });
}
var sanitizedStr = import_zod.z.string().transform((v) => v.replace(/<[^>]*>/g, "").trim()).pipe(import_zod.z.string().min(1));
function safeParse(schema, data) {
const result = schema.safeParse(data);
return result.success ? { success: true, data: result.data } : { success: false, errors: result.error };
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
documentoSchema,
emailSchema,
passwordConfirmSchema,
passwordSchema,
phoneSchema,
required,
safeParse,
sanitizedStr,
urlSchema,
uuidSchema
});
//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../../src/validators/index.ts"],"sourcesContent":["/**\n * @pulse-libs/validators — Schemas Zod reutilizáveis\n * Os schemas aqui são o único lugar onde validação é definida.\n * Todos os projetos importam destes schemas.\n */\nimport { z } from 'zod';\n\n// ── Email ──────────────────────────────────────────────────\nexport const emailSchema = z.string()\n .trim()\n .toLowerCase()\n .email('Email inválido')\n .transform(v => v); // normaliza\n\n// ── Password ───────────────────────────────────────────────\nexport const passwordSchema = z.string()\n .min(8, 'Mínimo 8 caracteres')\n .regex(/[A-Z]/, 'Pelo menos 1 letra maiúscula')\n .regex(/[a-z]/, 'Pelo menos 1 letra minúscula')\n .regex(/[0-9]/, 'Pelo menos 1 número');\n\nexport const passwordConfirmSchema = z.object({\n password: passwordSchema,\n confirm: z.string(),\n}).refine(({ password, confirm }) => password === confirm, {\n message: 'Senhas não coincidem',\n path: ['confirm'],\n});\n\n// ── UUID ───────────────────────────────────────────────────\nexport const uuidSchema = z.string().uuid('UUID inválido');\n\n// ── URL ────────────────────────────────────────────────────\nexport const urlSchema = z.string()\n .url('URL inválida')\n .or(z.literal(''))\n .transform((v) => v === '' ? undefined : v);\n\n// ── Phone (Brasil — flexível) ───────────────────────────────\nexport const phoneSchema = z.string()\n .regex(/^(\\+?55)?\\s?\\(?\\d{2}\\)?\\s?\\d{4,5}-?\\d{4}$/, 'Telefone inválido — use (xx) xxxxx-xxxx');\n\n// ── CPF/CNPJ — máscara + mínimo de dígitos ────────────────\nexport const documentoSchema = z.string()\n .transform((v: string) => v.replace(/\\D/g, ''))\n .refine((v: string) => v.length === 11 || v.length === 14, {\n message: 'CPF (11 dígitos) ou CNPJ (14 dígitos)',\n });\n\n// ── Field common wrappers ──────────────────────────────────\n/**\n * Aplicar validação obrigatória, trim e sanitização HTML\n */\nexport function required<T extends z.ZodTypeAny>(schema: T, msg = 'Campo obrigatório'): z.ZodEffects<T> {\n return schema.refine(v => !!v && String(v).trim().length > 0, { message: msg }) as z.ZodEffects<T>;\n}\n\n/**\n * Sanitizar string removendo HTML tags\n */\nexport const sanitizedStr = z.string()\n .transform(v => v.replace(/<[^>]*>/g, '').trim())\n .pipe(z.string().min(1));\n\n/**\n * Formulário genérico — parse seguro\n */\nexport function safeParse<T>(schema: z.ZodSchema<T>, data: unknown): { success: true; data: T } | { success: false; errors: z.ZodError } {\n const result = schema.safeParse(data);\n return result.success\n ? { success: true, data: result.data }\n : { success: false, errors: result.error };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,iBAAkB;AAGX,IAAM,cAAc,aAAE,OAAO,EACjC,KAAK,EACL,YAAY,EACZ,MAAM,mBAAgB,EACtB,UAAU,OAAK,CAAC;AAGZ,IAAM,iBAAiB,aAAE,OAAO,EACpC,IAAI,GAAI,wBAAqB,EAC7B,MAAM,SAAU,iCAA8B,EAC9C,MAAM,SAAU,iCAA8B,EAC9C,MAAM,SAAU,wBAAqB;AAEjC,IAAM,wBAAwB,aAAE,OAAO;AAAA,EAC5C,UAAc;AAAA,EACd,SAAc,aAAE,OAAO;AACzB,CAAC,EAAE,OAAO,CAAC,EAAE,UAAU,QAAQ,MAAM,aAAa,SAAS;AAAA,EACzD,SAAW;AAAA,EACX,MAAW,CAAC,SAAS;AACvB,CAAC;AAGM,IAAM,aAAa,aAAE,OAAO,EAAE,KAAK,kBAAe;AAGlD,IAAM,YAAY,aAAE,OAAO,EAC/B,IAAI,iBAAc,EAClB,GAAG,aAAE,QAAQ,EAAE,CAAC,EAChB,UAAU,CAAC,MAAM,MAAM,KAAK,SAAY,CAAC;AAGrC,IAAM,cAAc,aAAE,OAAO,EACjC,MAAM,6CAA6C,iDAAyC;AAGxF,IAAM,kBAAkB,aAAE,OAAO,EACrC,UAAU,CAAC,MAAc,EAAE,QAAQ,OAAO,EAAE,CAAC,EAC7C,OAAO,CAAC,MAAc,EAAE,WAAW,MAAM,EAAE,WAAW,IAAI;AAAA,EACzD,SAAS;AACX,CAAC;AAMI,SAAS,SAAiC,QAAW,MAAM,wBAAsC;AACtG,SAAO,OAAO,OAAO,OAAK,CAAC,CAAC,KAAK,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,GAAG,EAAE,SAAS,IAAI,CAAC;AAChF;AAKO,IAAM,eAAe,aAAE,OAAO,EAClC,UAAU,OAAK,EAAE,QAAQ,YAAY,EAAE,EAAE,KAAK,CAAC,EAC/C,KAAK,aAAE,OAAO,EAAE,IAAI,CAAC,CAAC;AAKlB,SAAS,UAAa,QAAwB,MAAoF;AACvI,QAAM,SAAS,OAAO,UAAU,IAAI;AACpC,SAAO,OAAO,UACV,EAAE,SAAS,MAAM,MAAM,OAAO,KAAK,IACnC,EAAE,SAAS,OAAO,QAAQ,OAAO,MAAM;AAC7C;","names":[]}
+25
View File
@@ -0,0 +1,25 @@
import {
documentoSchema,
emailSchema,
passwordConfirmSchema,
passwordSchema,
phoneSchema,
required,
safeParse,
sanitizedStr,
urlSchema,
uuidSchema
} from "../chunk-BDJP3A46.mjs";
export {
documentoSchema,
emailSchema,
passwordConfirmSchema,
passwordSchema,
phoneSchema,
required,
safeParse,
sanitizedStr,
urlSchema,
uuidSchema
};
//# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
+1
View File
@@ -0,0 +1 @@
../acorn/bin/acorn
+1
View File
@@ -0,0 +1 @@
../baseline-browser-mapping/dist/cli.cjs
+1
View File
@@ -0,0 +1 @@
../browserslist/cli.js
+1
View File
@@ -0,0 +1 @@
../js-beautify/js/bin/css-beautify.js
+1
View File
@@ -0,0 +1 @@
../editorconfig/bin/editorconfig
+1
View File
@@ -0,0 +1 @@
../esbuild/bin/esbuild
+1
View File
@@ -0,0 +1 @@
../glob/dist/esm/bin.mjs
+1
View File
@@ -0,0 +1 @@
../js-beautify/js/bin/html-beautify.js
+1
View File
@@ -0,0 +1 @@
../js-beautify/js/bin/js-beautify.js
+1
View File
@@ -0,0 +1 @@
../jsesc/bin/jsesc
+1
View File
@@ -0,0 +1 @@
../json5/lib/cli.js
+1
View File
@@ -0,0 +1 @@
../nanoid/bin/nanoid.cjs
+1
View File
@@ -0,0 +1 @@
../which/bin/node-which
+1
View File
@@ -0,0 +1 @@
../nopt/bin/nopt.js
+1
View File
@@ -0,0 +1 @@
../@babel/parser/bin/babel-parser.js
+1
View File
@@ -0,0 +1 @@
../rollup/dist/bin/rollup
+1
View File
@@ -0,0 +1 @@
../semver/bin/semver.js
+1
View File
@@ -0,0 +1 @@
../@bramus/specificity/bin/cli.js
+1
View File
@@ -0,0 +1 @@
../sucrase/bin/sucrase
+1
View File
@@ -0,0 +1 @@
../sucrase/bin/sucrase-node
+1
View File
@@ -0,0 +1 @@
../tldts/bin/cli.js
+1
View File
@@ -0,0 +1 @@
../tree-kill/cli.js
+1
View File
@@ -0,0 +1 @@
../typescript/bin/tsc
+1
View File
@@ -0,0 +1 @@
../typescript/bin/tsserver
+1
View File
@@ -0,0 +1 @@
../tsup/dist/cli-default.js
+1
View File
@@ -0,0 +1 @@
../tsup/dist/cli-node.js
+1
View File
@@ -0,0 +1 @@
../update-browserslist-db/cli.js
+1
View File
@@ -0,0 +1 @@
../vite/bin/vite.js
+1
View File
@@ -0,0 +1 @@
../vite-node/vite-node.mjs
+1
View File
@@ -0,0 +1 @@
../vitest/vitest.mjs
+1
View File
@@ -0,0 +1 @@
../why-is-node-running/cli.js
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
{"version":"1.6.1","results":[[":tests/utils.test.ts",{"duration":154,"failed":false}],[":tests/validators.test.ts",{"duration":17,"failed":false}]]}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 asamuzaK (Kazz)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+316
View File
@@ -0,0 +1,316 @@
# CSS color
[![build](https://github.com/asamuzaK/cssColor/actions/workflows/node.js.yml/badge.svg)](https://github.com/asamuzaK/cssColor/actions/workflows/node.js.yml)
[![CodeQL](https://github.com/asamuzaK/cssColor/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/asamuzaK/cssColor/actions/workflows/github-code-scanning/codeql)
[![npm (scoped)](https://img.shields.io/npm/v/@asamuzakjp/css-color)](https://www.npmjs.com/package/@asamuzakjp/css-color)
Resolve and convert CSS colors.
## Install
```console
npm i @asamuzakjp/css-color
```
## Usage
```javascript
import { convert, resolve, utils } from '@asamuzakjp/css-color';
const resolvedValue = resolve(
'color-mix(in oklab, lch(67.5345 42.5 258.2), color(srgb 0 0.5 0))'
);
// 'oklab(0.620754 -0.0931934 -0.00374881)'
const convertedValue = convert.colorToHex('lab(46.2775% -47.5621 48.5837)');
// '#008000'
const result = utils.isColor('green');
// true
```
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
### resolve(color, opt)
resolves CSS color
#### Parameters
- `color` **[string][133]** color value
- system colors are not supported
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.currentColor` **[string][133]?**
- color to use for `currentcolor` keyword
- if omitted, it will be treated as a missing color,
i.e. `rgb(none none none / none)`
- `opt.customProperty` **[object][135]?**
- custom properties
- pair of `--` prefixed property name as a key and it's value,
e.g.
```javascript
const opt = {
customProperty: {
'--some-color': '#008000',
'--some-length': '16px'
}
};
```
- and/or `callback` function to get the value of the custom property,
e.g.
```javascript
const node = document.getElementById('foo');
const opt = {
customProperty: {
callback: node.style.getPropertyValue
}
};
```
- `opt.dimension` **[object][135]?**
- dimension, e.g. for converting relative length to pixels
- pair of unit as a key and number in pixels as it's value,
e.g. suppose `1em === 12px`, `1rem === 16px` and `100vw === 1024px`, then
```javascript
const opt = {
dimension: {
em: 12,
rem: 16,
vw: 10.24
}
};
```
- and/or `callback` function to get the value as a number in pixels,
e.g.
```javascript
const opt = {
dimension: {
callback: unit => {
switch (unit) {
case 'em':
return 12;
case 'rem':
return 16;
case 'vw':
return 10.24;
default:
return;
}
}
}
};
```
- `opt.format` **[string][133]?**
- output format, one of below
- `computedValue` (default), [computed value][139] of the color
- `specifiedValue`, [specified value][140] of the color
- `hex`, hex color notation, i.e. `#rrggbb`
- `hexAlpha`, hex color notation with alpha channel, i.e. `#rrggbbaa`
Returns **[string][133]?** one of `rgba?()`, `#rrggbb(aa)?`, `color-name`, `color(color-space r g b / alpha)`, `color(color-space x y z / alpha)`, `(ok)?lab(l a b / alpha)`, `(ok)?lch(l c h / alpha)`, `'(empty-string)'`, `null`
- in `computedValue`, values are numbers, however `rgb()` values are integers
- in `specifiedValue`, returns `empty string` for unknown and/or invalid color
- in `hex`, returns `null` for `transparent`, and also returns `null` if any of `r`, `g`, `b`, `alpha` is not a number
- in `hexAlpha`, returns `#00000000` for `transparent`, however returns `null` if any of `r`, `g`, `b`, `alpha` is not a number
### convert
Contains various color conversion functions.
### convert.numberToHex(value)
convert number to hex string
#### Parameters
- `value` **[number][134]** color value
Returns **[string][133]** hex string: 00..ff
### convert.colorToHex(value, opt)
convert color to hex
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.alpha` **[boolean][136]?** return in #rrggbbaa notation
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[string][133]** #rrggbb(aa)?
### convert.colorToHsl(value, opt)
convert color to hsl
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[h, s, l, alpha]
### convert.colorToHwb(value, opt)
convert color to hwb
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[h, w, b, alpha]
### convert.colorToLab(value, opt)
convert color to lab
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[l, a, b, alpha]
### convert.colorToLch(value, opt)
convert color to lch
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[l, c, h, alpha]
### convert.colorToOklab(value, opt)
convert color to oklab
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[l, a, b, alpha]
### convert.colorToOklch(value, opt)
convert color to oklch
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[l, c, h, alpha]
### convert.colorToRgb(value, opt)
convert color to rgb
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[r, g, b, alpha]
### convert.colorToXyz(value, opt)
convert color to xyz
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
- `opt.d50` **[boolean][136]?** xyz in d50 white point
Returns **[Array][137]<[number][134]>** \[x, y, z, alpha]
### convert.colorToXyzD50(value, opt)
convert color to xyz-d50
#### Parameters
- `value` **[string][133]** color value
- `opt` **[object][135]?** options (optional, default `{}`)
- `opt.customProperty` **[object][135]?**
- custom properties, see `resolve()` function above
- `opt.dimension` **[object][135]?**
- dimension, see `resolve()` function above
Returns **[Array][137]<[number][134]>** \[x, y, z, alpha]
### utils
Contains utility functions.
### utils.isColor(color)
is valid color type
#### Parameters
- `color` **[string][133]** color value
- system colors are not supported
Returns **[boolean][136]**
## Acknowledgments
The following resources have been of great help in the development of the CSS color.
- [csstools/postcss-plugins](https://github.com/csstools/postcss-plugins)
- [lru-cache](https://github.com/isaacs/node-lru-cache)
---
Copyright (c) 2024 [asamuzaK (Kazz)](https://github.com/asamuzaK/)
[133]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
[134]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
[135]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
[136]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
[137]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
[138]: https://w3c.github.io/csswg-drafts/css-color-4/#color-conversion-code
[139]: https://developer.mozilla.org/en-US/docs/Web/CSS/computed_value
[140]: https://developer.mozilla.org/en-US/docs/Web/CSS/specified_value
[141]: https://www.npmjs.com/package/@csstools/css-calc
@@ -0,0 +1,20 @@
/*!
* CSS color - Resolve, parse, convert CSS color.
* @license MIT
* @copyright asamuzaK (Kazz)
* @see {@link https://github.com/asamuzaK/cssColor/blob/main/LICENSE}
*/
export { convert } from './js/convert.js';
export { resolve } from './js/resolve.js';
export declare const utils: {
cssCalc: (value: string, opt?: import('./js/typedef.js').Options) => string;
cssVar: (value: string, opt?: import('./js/typedef.js').Options) => string;
extractDashedIdent: (value: string) => string[];
isAbsoluteFontSize: (css: unknown) => boolean;
isAbsoluteSizeOrLength: (value: number | string, unit: string | undefined) => boolean;
isColor: (value: unknown, opt?: import('./js/typedef.js').Options) => boolean;
isGradient: (value: string, opt?: import('./js/typedef.js').Options) => boolean;
resolveGradient: (value: string, opt?: import('./js/typedef.js').Options) => string;
resolveLengthInPixels: (value: number | string, unit: string | undefined, opt?: import('./js/typedef.js').Options) => number;
splitValue: (value: string, opt?: import('./js/typedef.js').Options) => string[];
};
@@ -0,0 +1,29 @@
import { resolve } from "./js/resolve.js";
import { extractDashedIdent, isAbsoluteFontSize, isAbsoluteSizeOrLength, isColor, resolveLengthInPixels, splitValue } from "./js/util.js";
import { cssVar } from "./js/css-var.js";
import { cssCalc } from "./js/css-calc.js";
import { isGradient, resolveGradient } from "./js/css-gradient.js";
import { convert } from "./js/convert.js";
//#region src/index.ts
/*!
* CSS color - Resolve, parse, convert CSS color.
* @license MIT
* @copyright asamuzaK (Kazz)
* @see {@link https://github.com/asamuzaK/cssColor/blob/main/LICENSE}
*/
var utils = {
cssCalc,
cssVar,
extractDashedIdent,
isAbsoluteFontSize,
isAbsoluteSizeOrLength,
isColor,
isGradient,
resolveGradient,
resolveLengthInPixels,
splitValue
};
//#endregion
export { convert, resolve, utils };
//# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
{"version":3,"file":"index.js","names":[],"sources":["../../src/index.ts"],"sourcesContent":["/*!\n * CSS color - Resolve, parse, convert CSS color.\n * @license MIT\n * @copyright asamuzaK (Kazz)\n * @see {@link https://github.com/asamuzaK/cssColor/blob/main/LICENSE}\n */\n\nimport { cssCalc } from './js/css-calc';\nimport { isGradient, resolveGradient } from './js/css-gradient';\nimport { cssVar } from './js/css-var';\nimport {\n extractDashedIdent,\n isAbsoluteFontSize,\n isAbsoluteSizeOrLength,\n isColor,\n resolveLengthInPixels,\n splitValue\n} from './js/util';\n\nexport { convert } from './js/convert';\nexport { resolve } from './js/resolve';\n/* utils */\nexport const utils = {\n cssCalc,\n cssVar,\n extractDashedIdent,\n isAbsoluteFontSize,\n isAbsoluteSizeOrLength,\n isColor,\n isGradient,\n resolveGradient,\n resolveLengthInPixels,\n splitValue\n};\n"],"mappings":";;;;;;;;;;;;;AAsBA,IAAa,QAAQ;CACnB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD"}
@@ -0,0 +1,38 @@
import { GenerationalCache } from '@asamuzakjp/generational-cache';
import { Options } from './typedef.js';
/**
* CacheItem
*/
export declare class CacheItem {
#private;
constructor(item: unknown, isNull?: boolean);
get item(): unknown;
get isNull(): boolean;
}
/**
* NullObject
*/
export declare class NullObject extends CacheItem {
constructor();
}
export declare const genCache: GenerationalCache<string, CacheItem>;
/**
* set cache
* @param key - cache key
* @param value - value to cache
* @returns void
*/
export declare const setCache: (key: string, value: unknown) => void;
/**
* get cache
* @param key - cache key
* @returns cached item or false otherwise
*/
export declare const getCache: (key: string) => CacheItem | false;
/**
* create cache key
* @param keyData - key data
* @param [opt] - options
* @returns cache key
*/
export declare const createCacheKey: (keyData: Record<string, string>, opt?: Options) => string;
@@ -0,0 +1,90 @@
import { GenerationalCache } from "@asamuzakjp/generational-cache";
//#region src/js/cache.ts
/**
* cache
*/
var CACHE_SIZE = 2048;
/**
* CacheItem
*/
var CacheItem = class {
#isNull;
#item;
constructor(item, isNull = false) {
this.#item = item;
this.#isNull = !!isNull;
}
get item() {
return this.#item;
}
get isNull() {
return this.#isNull;
}
};
/**
* NullObject
*/
var NullObject = class extends CacheItem {
constructor() {
super(Symbol("null"), true);
}
};
var genCache = new GenerationalCache(CACHE_SIZE);
/**
* shared null object
*/
var sharedNullObject = new NullObject();
/**
* set cache
* @param key - cache key
* @param value - value to cache
* @returns void
*/
var setCache = (key, value) => {
if (!key) return;
if (value === null) genCache.set(key, sharedNullObject);
else if (value instanceof CacheItem) genCache.set(key, value);
else genCache.set(key, new CacheItem(value));
};
/**
* get cache
* @param key - cache key
* @returns cached item or false otherwise
*/
var getCache = (key) => {
if (!key) return false;
const item = genCache.get(key);
if (item !== void 0) return item;
return false;
};
/**
* helper function to sort object keys alphabetically
* @param obj - Object
* @returns stringified JSON
*/
var stringifySorted = (obj) => {
const keys = Object.keys(obj);
if (keys.length === 0) return "";
keys.sort();
let result = "";
for (const key of keys) result += `${key}:${JSON.stringify(obj[key])};`;
return result;
};
/**
* create cache key
* @param keyData - key data
* @param [opt] - options
* @returns cache key
*/
var createCacheKey = (keyData, opt = {}) => {
if (!keyData || opt.customProperty && typeof opt.customProperty.callback === "function" || opt.dimension && typeof opt.dimension.callback === "function") return "";
const namespace = keyData.namespace || "";
const name = keyData.name || "";
const value = keyData.value || "";
if (!namespace && !name && !value) return "";
return `${`${namespace}:${name}:${value}`}::${`${opt.format || ""}|${opt.colorSpace || ""}|${opt.colorScheme || ""}|${opt.currentColor || ""}|${opt.d50 ? "1" : "0"}|${opt.nullable ? "1" : "0"}|${opt.preserveComment ? "1" : "0"}|${opt.delimiter || ""}`}::${opt.customProperty ? stringifySorted(opt.customProperty) : ""}::${opt.dimension ? stringifySorted(opt.dimension) : ""}`;
};
//#endregion
export { CacheItem, NullObject, createCacheKey, getCache, setCache };
//# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
{"version":3,"file":"cache.js","names":["#item","#isNull"],"sources":["../../../src/js/cache.ts"],"sourcesContent":["/**\n * cache\n */\n\nimport { GenerationalCache } from '@asamuzakjp/generational-cache';\nimport { Options } from './typedef';\n\n/* constants */\nconst CACHE_SIZE = 2048;\n\n/**\n * CacheItem\n */\nexport class CacheItem {\n /* private */\n #isNull: boolean;\n #item: unknown;\n\n constructor(item: unknown, isNull: boolean = false) {\n this.#item = item;\n this.#isNull = !!isNull;\n }\n\n get item() {\n return this.#item;\n }\n\n get isNull() {\n return this.#isNull;\n }\n}\n\n/**\n * NullObject\n */\nexport class NullObject extends CacheItem {\n constructor() {\n super(Symbol('null'), true);\n }\n}\n\n/*\n * generational cache instance\n */\nexport const genCache = new GenerationalCache<string, CacheItem>(CACHE_SIZE);\n\n/**\n * shared null object\n */\nconst sharedNullObject = new NullObject();\n\n/**\n * set cache\n * @param key - cache key\n * @param value - value to cache\n * @returns void\n */\nexport const setCache = (key: string, value: unknown): void => {\n if (!key) {\n return;\n }\n if (value === null) {\n genCache.set(key, sharedNullObject);\n } else if (value instanceof CacheItem) {\n genCache.set(key, value);\n } else {\n genCache.set(key, new CacheItem(value));\n }\n};\n\n/**\n * get cache\n * @param key - cache key\n * @returns cached item or false otherwise\n */\nexport const getCache = (key: string): CacheItem | false => {\n if (!key) {\n return false;\n }\n const item = genCache.get(key);\n if (item !== undefined) {\n return item as CacheItem;\n }\n return false;\n};\n\n/**\n * helper function to sort object keys alphabetically\n * @param obj - Object\n * @returns stringified JSON\n */\nconst stringifySorted = (obj: Record<string, unknown>): string => {\n const keys = Object.keys(obj);\n if (keys.length === 0) {\n return '';\n }\n keys.sort();\n let result = '';\n for (const key of keys) {\n result += `${key}:${JSON.stringify(obj[key])};`;\n }\n return result;\n};\n\n/**\n * create cache key\n * @param keyData - key data\n * @param [opt] - options\n * @returns cache key\n */\nexport const createCacheKey = (\n keyData: Record<string, string>,\n opt: Options = {}\n): string => {\n if (\n !keyData ||\n (opt.customProperty && typeof opt.customProperty.callback === 'function') ||\n (opt.dimension && typeof opt.dimension.callback === 'function')\n ) {\n return '';\n }\n const namespace = keyData.namespace || '';\n const name = keyData.name || '';\n const value = keyData.value || '';\n if (!namespace && !name && !value) {\n return '';\n }\n const baseKey = `${namespace}:${name}:${value}`;\n const optStr = `${opt.format || ''}|${opt.colorSpace || ''}|${opt.colorScheme || ''}|${opt.currentColor || ''}|${opt.d50 ? '1' : '0'}|${opt.nullable ? '1' : '0'}|${opt.preserveComment ? '1' : '0'}|${opt.delimiter || ''}`;\n const customPropStr = opt.customProperty\n ? stringifySorted(opt.customProperty as Record<string, unknown>)\n : '';\n const dimStr = opt.dimension\n ? stringifySorted(opt.dimension as Record<string, unknown>)\n : '';\n return `${baseKey}::${optStr}::${customPropStr}::${dimStr}`;\n};\n"],"mappings":";;;;;AAQA,IAAM,aAAa;;;;AAKnB,IAAa,YAAb,MAAuB;CAErB;CACA;CAEA,YAAY,MAAe,SAAkB,OAAO;AAClD,QAAA,OAAa;AACb,QAAA,SAAe,CAAC,CAAC;;CAGnB,IAAI,OAAO;AACT,SAAO,MAAA;;CAGT,IAAI,SAAS;AACX,SAAO,MAAA;;;;;;AAOX,IAAa,aAAb,cAAgC,UAAU;CACxC,cAAc;AACZ,QAAM,OAAO,OAAO,EAAE,KAAK;;;AAO/B,IAAa,WAAW,IAAI,kBAAqC,WAAW;;;;AAK5E,IAAM,mBAAmB,IAAI,YAAY;;;;;;;AAQzC,IAAa,YAAY,KAAa,UAAyB;AAC7D,KAAI,CAAC,IACH;AAEF,KAAI,UAAU,KACZ,UAAS,IAAI,KAAK,iBAAiB;UAC1B,iBAAiB,UAC1B,UAAS,IAAI,KAAK,MAAM;KAExB,UAAS,IAAI,KAAK,IAAI,UAAU,MAAM,CAAC;;;;;;;AAS3C,IAAa,YAAY,QAAmC;AAC1D,KAAI,CAAC,IACH,QAAO;CAET,MAAM,OAAO,SAAS,IAAI,IAAI;AAC9B,KAAI,SAAS,KAAA,EACX,QAAO;AAET,QAAO;;;;;;;AAQT,IAAM,mBAAmB,QAAyC;CAChE,MAAM,OAAO,OAAO,KAAK,IAAI;AAC7B,KAAI,KAAK,WAAW,EAClB,QAAO;AAET,MAAK,MAAM;CACX,IAAI,SAAS;AACb,MAAK,MAAM,OAAO,KAChB,WAAU,GAAG,IAAI,GAAG,KAAK,UAAU,IAAI,KAAK,CAAC;AAE/C,QAAO;;;;;;;;AAST,IAAa,kBACX,SACA,MAAe,EAAE,KACN;AACX,KACE,CAAC,WACA,IAAI,kBAAkB,OAAO,IAAI,eAAe,aAAa,cAC7D,IAAI,aAAa,OAAO,IAAI,UAAU,aAAa,WAEpD,QAAO;CAET,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,QAAQ,QAAQ,SAAS;AAC/B,KAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,MAC1B,QAAO;AAUT,QAAO,GARS,GAAG,UAAU,GAAG,KAAK,GAAG,QAQtB,IAPH,GAAG,IAAI,UAAU,GAAG,GAAG,IAAI,cAAc,GAAG,GAAG,IAAI,eAAe,GAAG,GAAG,IAAI,gBAAgB,GAAG,GAAG,IAAI,MAAM,MAAM,IAAI,GAAG,IAAI,WAAW,MAAM,IAAI,GAAG,IAAI,kBAAkB,MAAM,IAAI,GAAG,IAAI,aAAa,KAO3L,IANP,IAAI,iBACtB,gBAAgB,IAAI,eAA0C,GAC9D,GAI2C,IAHhC,IAAI,YACf,gBAAgB,IAAI,UAAqC,GACzD"}
@@ -0,0 +1,537 @@
import { NullObject } from './cache.js';
import { ColorChannels, Options, SpecifiedColorChannels } from './typedef.js';
/**
* @type TriColorChannels - color channels without alpha
*/
type TriColorChannels = [x: number, y: number, z: number];
/**
* @type ColorMatrix - color matrix
*/
type ColorMatrix = [
r1: TriColorChannels,
r2: TriColorChannels,
r3: TriColorChannels
];
/**
* named colors
*/
export declare const NAMED_COLORS: {
readonly aliceblue: [240, 248, 255];
readonly antiquewhite: [250, 235, 215];
readonly aqua: [0, 255, 255];
readonly aquamarine: [127, 255, 212];
readonly azure: [240, 255, 255];
readonly beige: [245, 245, 220];
readonly bisque: [255, 228, 196];
readonly black: [0, 0, 0];
readonly blanchedalmond: [255, 235, 205];
readonly blue: [0, 0, 255];
readonly blueviolet: [138, 43, 226];
readonly brown: [165, 42, 42];
readonly burlywood: [222, 184, 135];
readonly cadetblue: [95, 158, 160];
readonly chartreuse: [127, 255, 0];
readonly chocolate: [210, 105, 30];
readonly coral: [255, 127, 80];
readonly cornflowerblue: [100, 149, 237];
readonly cornsilk: [255, 248, 220];
readonly crimson: [220, 20, 60];
readonly cyan: [0, 255, 255];
readonly darkblue: [0, 0, 139];
readonly darkcyan: [0, 139, 139];
readonly darkgoldenrod: [184, 134, 11];
readonly darkgray: [169, 169, 169];
readonly darkgreen: [0, 100, 0];
readonly darkgrey: [169, 169, 169];
readonly darkkhaki: [189, 183, 107];
readonly darkmagenta: [139, 0, 139];
readonly darkolivegreen: [85, 107, 47];
readonly darkorange: [255, 140, 0];
readonly darkorchid: [153, 50, 204];
readonly darkred: [139, 0, 0];
readonly darksalmon: [233, 150, 122];
readonly darkseagreen: [143, 188, 143];
readonly darkslateblue: [72, 61, 139];
readonly darkslategray: [47, 79, 79];
readonly darkslategrey: [47, 79, 79];
readonly darkturquoise: [0, 206, 209];
readonly darkviolet: [148, 0, 211];
readonly deeppink: [255, 20, 147];
readonly deepskyblue: [0, 191, 255];
readonly dimgray: [105, 105, 105];
readonly dimgrey: [105, 105, 105];
readonly dodgerblue: [30, 144, 255];
readonly firebrick: [178, 34, 34];
readonly floralwhite: [255, 250, 240];
readonly forestgreen: [34, 139, 34];
readonly fuchsia: [255, 0, 255];
readonly gainsboro: [220, 220, 220];
readonly ghostwhite: [248, 248, 255];
readonly gold: [255, 215, 0];
readonly goldenrod: [218, 165, 32];
readonly gray: [128, 128, 128];
readonly green: [0, 128, 0];
readonly greenyellow: [173, 255, 47];
readonly grey: [128, 128, 128];
readonly honeydew: [240, 255, 240];
readonly hotpink: [255, 105, 180];
readonly indianred: [205, 92, 92];
readonly indigo: [75, 0, 130];
readonly ivory: [255, 255, 240];
readonly khaki: [240, 230, 140];
readonly lavender: [230, 230, 250];
readonly lavenderblush: [255, 240, 245];
readonly lawngreen: [124, 252, 0];
readonly lemonchiffon: [255, 250, 205];
readonly lightblue: [173, 216, 230];
readonly lightcoral: [240, 128, 128];
readonly lightcyan: [224, 255, 255];
readonly lightgoldenrodyellow: [250, 250, 210];
readonly lightgray: [211, 211, 211];
readonly lightgreen: [144, 238, 144];
readonly lightgrey: [211, 211, 211];
readonly lightpink: [255, 182, 193];
readonly lightsalmon: [255, 160, 122];
readonly lightseagreen: [32, 178, 170];
readonly lightskyblue: [135, 206, 250];
readonly lightslategray: [119, 136, 153];
readonly lightslategrey: [119, 136, 153];
readonly lightsteelblue: [176, 196, 222];
readonly lightyellow: [255, 255, 224];
readonly lime: [0, 255, 0];
readonly limegreen: [50, 205, 50];
readonly linen: [250, 240, 230];
readonly magenta: [255, 0, 255];
readonly maroon: [128, 0, 0];
readonly mediumaquamarine: [102, 205, 170];
readonly mediumblue: [0, 0, 205];
readonly mediumorchid: [186, 85, 211];
readonly mediumpurple: [147, 112, 219];
readonly mediumseagreen: [60, 179, 113];
readonly mediumslateblue: [123, 104, 238];
readonly mediumspringgreen: [0, 250, 154];
readonly mediumturquoise: [72, 209, 204];
readonly mediumvioletred: [199, 21, 133];
readonly midnightblue: [25, 25, 112];
readonly mintcream: [245, 255, 250];
readonly mistyrose: [255, 228, 225];
readonly moccasin: [255, 228, 181];
readonly navajowhite: [255, 222, 173];
readonly navy: [0, 0, 128];
readonly oldlace: [253, 245, 230];
readonly olive: [128, 128, 0];
readonly olivedrab: [107, 142, 35];
readonly orange: [255, 165, 0];
readonly orangered: [255, 69, 0];
readonly orchid: [218, 112, 214];
readonly palegoldenrod: [238, 232, 170];
readonly palegreen: [152, 251, 152];
readonly paleturquoise: [175, 238, 238];
readonly palevioletred: [219, 112, 147];
readonly papayawhip: [255, 239, 213];
readonly peachpuff: [255, 218, 185];
readonly peru: [205, 133, 63];
readonly pink: [255, 192, 203];
readonly plum: [221, 160, 221];
readonly powderblue: [176, 224, 230];
readonly purple: [128, 0, 128];
readonly rebeccapurple: [102, 51, 153];
readonly red: [255, 0, 0];
readonly rosybrown: [188, 143, 143];
readonly royalblue: [65, 105, 225];
readonly saddlebrown: [139, 69, 19];
readonly salmon: [250, 128, 114];
readonly sandybrown: [244, 164, 96];
readonly seagreen: [46, 139, 87];
readonly seashell: [255, 245, 238];
readonly sienna: [160, 82, 45];
readonly silver: [192, 192, 192];
readonly skyblue: [135, 206, 235];
readonly slateblue: [106, 90, 205];
readonly slategray: [112, 128, 144];
readonly slategrey: [112, 128, 144];
readonly snow: [255, 250, 250];
readonly springgreen: [0, 255, 127];
readonly steelblue: [70, 130, 180];
readonly tan: [210, 180, 140];
readonly teal: [0, 128, 128];
readonly thistle: [216, 191, 216];
readonly tomato: [255, 99, 71];
readonly turquoise: [64, 224, 208];
readonly violet: [238, 130, 238];
readonly wheat: [245, 222, 179];
readonly white: [255, 255, 255];
readonly whitesmoke: [245, 245, 245];
readonly yellow: [255, 255, 0];
readonly yellowgreen: [154, 205, 50];
};
/**
* cache invalid color value
* @param key - cache key
* @param nullable - is nullable
* @returns cached value
*/
export declare const cacheInvalidColorValue: (cacheKey: string, format: string, nullable?: boolean) => SpecifiedColorChannels | string | NullObject;
/**
* resolve invalid color value
* @param format - output format
* @param nullable - is nullable
* @returns resolved value
*/
export declare const resolveInvalidColorValue: (format: string, nullable?: boolean) => SpecifiedColorChannels | string | NullObject;
/**
* validate color components
* @param arr - color components
* @param [opt] - options
* @param [opt.alpha] - alpha channel
* @param [opt.minLength] - min length
* @param [opt.maxLength] - max length
* @param [opt.minRange] - min range
* @param [opt.maxRange] - max range
* @param [opt.validateRange] - validate range
* @returns result - validated color components
*/
export declare const validateColorComponents: (arr: ColorChannels | TriColorChannels, opt?: {
alpha?: boolean;
minLength?: number;
maxLength?: number;
minRange?: number;
maxRange?: number;
validateRange?: boolean;
}) => ColorChannels | TriColorChannels;
/**
* transform matrix
* @param mtx - 3 * 3 matrix
* @param vct - vector
* @param [skip] - skip validate
* @returns TriColorChannels - [p1, p2, p3]
*/
export declare const transformMatrix: (mtx: ColorMatrix, vct: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* normalize color components
* @param colorA - color components [v1, v2, v3, v4]
* @param colorB - color components [v1, v2, v3, v4]
* @param [skip] - skip validate
* @returns result - [colorA, colorB]
*/
export declare const normalizeColorComponents: (colorA: [number | string, number | string, number | string, number | string], colorB: [number | string, number | string, number | string, number | string], skip?: boolean) => [ColorChannels, ColorChannels];
/**
* number to hex string
* @param value - numeric value
* @returns hex string
*/
export declare const numberToHexString: (value: number) => string;
/**
* angle to deg
* @param angle
* @returns deg: 0..360
*/
export declare const angleToDeg: (angle: string) => number;
/**
* parse alpha
* @param [alpha] - alpha value
* @returns alpha: 0..1
*/
export declare const parseAlpha: (alpha?: string) => number;
/**
* parse hex alpha
* @param value - alpha value in hex string
* @returns alpha: 0..1
*/
export declare const parseHexAlpha: (value: string) => number;
/**
* transform rgb to linear rgb
* @param rgb - [r, g, b] r|g|b: 0..255
* @param [skip] - skip validate
* @returns TriColorChannels - [r, g, b] r|g|b: 0..1
*/
export declare const transformRgbToLinearRgb: (rgb: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform rgb to xyz
* @param rgb - [r, g, b] r|g|b: 0..255
* @param [skip] - skip validate
* @returns TriColorChannels - [x, y, z]
*/
export declare const transformRgbToXyz: (rgb: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform rgb to xyz-d50
* @param rgb - [r, g, b] r|g|b: 0..255 alpha: 0..1
* @returns TriColorChannels - [x, y, z]
*/
export declare const transformRgbToXyzD50: (rgb: TriColorChannels) => TriColorChannels;
/**
* transform linear rgb to rgb
* @param rgb - [r, g, b] r|g|b: 0..1
* @param [round] - round result
* @returns TriColorChannels - [r, g, b] r|g|b: 0..255
*/
export declare const transformLinearRgbToRgb: (rgb: TriColorChannels, round?: boolean) => TriColorChannels;
/**
* transform xyz to rgb
* @param xyz - [x, y, z]
* @param [skip] - skip validate
* @returns TriColorChannels - [r, g, b] r|g|b: 0..255
*/
export declare const transformXyzToRgb: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform xyz to xyz-d50
* @param xyz - [x, y, z]
* @returns TriColorChannels - [x, y, z]
*/
export declare const transformXyzToXyzD50: (xyz: TriColorChannels) => TriColorChannels;
/**
* transform xyz to hsl
* @param xyz - [x, y, z]
* @param [skip] - skip validate
* @returns TriColorChannels - [h, s, l]
*/
export declare const transformXyzToHsl: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform xyz to hwb
* @param xyz - [x, y, z]
* @param [skip] - skip validate
* @returns TriColorChannels - [h, w, b]
*/
export declare const transformXyzToHwb: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform xyz to oklab
* @param xyz - [x, y, z]
* @param [skip] - skip validate
* @returns TriColorChannels - [l, a, b]
*/
export declare const transformXyzToOklab: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform xyz to oklch
* @param xyz - [x, y, z]
* @param [skip] - skip validate
* @returns TriColorChannels - [l, c, h]
*/
export declare const transformXyzToOklch: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform xyz D50 to rgb
* @param xyz - [x, y, z]
* @param [skip] - skip validate
* @returns TriColorChannels - [r, g, b] r|g|b: 0..255
*/
export declare const transformXyzD50ToRgb: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform xyz-d50 to lab
* @param xyz - [x, y, z]
* @param [skip] - skip validate
* @returns TriColorChannels - [l, a, b]
*/
export declare const transformXyzD50ToLab: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* transform xyz-d50 to lch
* @param xyz - [x, y, z]
* @param [skip] - skip validate
* @returns TriColorChannels - [l, c, h]
*/
export declare const transformXyzD50ToLch: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
/**
* convert rgb to hex color
* @param rgb - [r, g, b, alpha] r|g|b: 0..255 alpha: 0..1
* @returns hex color
*/
export declare const convertRgbToHex: (rgb: ColorChannels) => string;
/**
* convert linear rgb to hex color
* @param rgb - [r, g, b, alpha] r|g|b|alpha: 0..1
* @param [skip] - skip validate
* @returns hex color
*/
export declare const convertLinearRgbToHex: (rgb: ColorChannels, skip?: boolean) => string;
/**
* convert xyz to hex color
* @param xyz - [x, y, z, alpha]
* @returns hex color
*/
export declare const convertXyzToHex: (xyz: ColorChannels) => string;
/**
* convert xyz D50 to hex color
* @param xyz - [x, y, z, alpha]
* @returns hex color
*/
export declare const convertXyzD50ToHex: (xyz: ColorChannels) => string;
/**
* convert hex color to rgb
* @param value - hex color value
* @returns ColorChannels - [r, g, b, alpha] r|g|b: 0..255 alpha: 0..1
*/
export declare const convertHexToRgb: (value: string) => ColorChannels;
/**
* convert hex color to linear rgb
* @param value - hex color value
* @returns ColorChannels - [r, g, b, alpha] r|g|b|alpha: 0..1
*/
export declare const convertHexToLinearRgb: (value: string) => ColorChannels;
/**
* convert hex color to xyz
* @param value - hex color value
* @returns ColorChannels - [x, y, z, alpha]
*/
export declare const convertHexToXyz: (value: string) => ColorChannels;
/**
* parse rgb()
* @param value - rgb color value
* @param [opt] - options
* @returns parsed color - ['rgb', r, g, b, alpha], '(empty)', NullObject
*/
export declare const parseRgb: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* parse hsl()
* @param value - hsl color value
* @param [opt] - options
* @returns parsed color - ['rgb', r, g, b, alpha], '(empty)', NullObject
*/
export declare const parseHsl: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* parse hwb()
* @param value - hwb color value
* @param [opt] - options
* @returns parsed color - ['rgb', r, g, b, alpha], '(empty)', NullObject
*/
export declare const parseHwb: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* parse lab()
* @param value - lab color value
* @param [opt] - options
* @returns parsed color
* - [xyz-d50, x, y, z, alpha], ['lab', l, a, b, alpha], '(empty)', NullObject
*/
export declare const parseLab: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* parse lch()
* @param value - lch color value
* @param [opt] - options
* @returns parsed color
* - ['xyz-d50', x, y, z, alpha], ['lch', l, c, h, alpha]
* - '(empty)', NullObject
*/
export declare const parseLch: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* parse oklab()
* @param value - oklab color value
* @param [opt] - options
* @returns parsed color
* - ['xyz-d65', x, y, z, alpha], ['oklab', l, a, b, alpha]
* - '(empty)', NullObject
*/
export declare const parseOklab: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* parse oklch()
* @param value - oklch color value
* @param [opt] - options
* @returns parsed color
* - ['xyz-d65', x, y, z, alpha], ['oklch', l, c, h, alpha]
* - '(empty)', NullObject
*/
export declare const parseOklch: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* parse color()
* @param value - color function value
* @param [opt] - options
* @returns parsed color
* - ['xyz-(d50|d65)', x, y, z, alpha], [cs, r, g, b, alpha]
* - '(empty)', NullObject
*/
export declare const parseColorFunc: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* parse color value
* @param value - CSS color value
* @param [opt] - options
* @returns parsed color
* - ['xyz-(d50|d65)', x, y, z, alpha], ['rgb', r, g, b, alpha]
* - value, '(empty)', NullObject
*/
export declare const parseColorValue: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* resolve color value
* @param value - CSS color value
* @param [opt] - options
* @returns resolved color
* - [cs, v1, v2, v3, alpha], value, '(empty)', NullObject
*/
export declare const resolveColorValue: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* resolve color()
* @param value - color function value
* @param [opt] - options
* @returns resolved color - [cs, v1, v2, v3, alpha], '(empty)', NullObject
*/
export declare const resolveColorFunc: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
/**
* convert color value to linear rgb
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject - [r, g, b, alpha] r|g|b|alpha: 0..1
*/
export declare const convertColorToLinearRgb: (value: string, opt?: {
colorSpace?: string;
format?: string;
}) => ColorChannels | NullObject;
/**
* convert color value to rgb
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject
* - [r, g, b, alpha] r|g|b: 0..255 alpha: 0..1
*/
export declare const convertColorToRgb: (value: string, opt?: Options) => ColorChannels | NullObject;
/**
* convert color value to xyz
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject - [x, y, z, alpha]
*/
export declare const convertColorToXyz: (value: string, opt?: Options) => ColorChannels | NullObject;
/**
* convert color value to hsl
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject - [h, s, l, alpha], hue may be powerless
*/
export declare const convertColorToHsl: (value: string, opt?: Options) => ColorChannels | [number | string, number, number, number] | NullObject;
/**
* convert color value to hwb
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject - [h, w, b, alpha], hue may be powerless
*/
export declare const convertColorToHwb: (value: string, opt?: Options) => ColorChannels | [number | string, number, number, number] | NullObject;
/**
* convert color value to lab
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject - [l, a, b, alpha]
*/
export declare const convertColorToLab: (value: string, opt?: Options) => ColorChannels | NullObject;
/**
* convert color value to lch
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject - [l, c, h, alpha], hue may be powerless
*/
export declare const convertColorToLch: (value: string, opt?: Options) => ColorChannels | [number, number, number | string, number] | NullObject;
/**
* convert color value to oklab
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject - [l, a, b, alpha]
*/
export declare const convertColorToOklab: (value: string, opt?: Options) => ColorChannels | NullObject;
/**
* convert color value to oklch
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels | NullObject - [l, c, h, alpha], hue may be powerless
*/
export declare const convertColorToOklch: (value: string, opt?: Options) => ColorChannels | [number, number, number | string, number] | NullObject;
/**
* resolve color-mix()
* @param value - color-mix color value
* @param [opt] - options
* @returns resolved color - [cs, v1, v2, v3, alpha], '(empty)'
*/
export declare const resolveColorMix: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
export {};
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,21 @@
/**
* common
*/
/**
* get type
* @param o - object to check
* @returns type of object
*/
export declare const getType: (o: unknown) => string;
/**
* is string
* @param o - object to check
* @returns result
*/
export declare const isString: (o: unknown) => o is string;
/**
* is string or number
* @param o - object to check
* @returns result
*/
export declare const isStringOrNumber: (o: unknown) => boolean;
@@ -0,0 +1,17 @@
//#region src/js/common.ts
/**
* is string
* @param o - object to check
* @returns result
*/
var isString = (o) => typeof o === "string" || o instanceof String;
/**
* is string or number
* @param o - object to check
* @returns result
*/
var isStringOrNumber = (o) => isString(o) || typeof o === "number";
//#endregion
export { isString, isStringOrNumber };
//# sourceMappingURL=common.js.map
@@ -0,0 +1 @@
{"version":3,"file":"common.js","names":[],"sources":["../../../src/js/common.ts"],"sourcesContent":["/**\n * common\n */\n\n/* numeric constants */\nconst TYPE_FROM = 8;\nconst TYPE_TO = -1;\n\n/**\n * get type\n * @param o - object to check\n * @returns type of object\n */\nexport const getType = (o: unknown): string =>\n Object.prototype.toString.call(o).slice(TYPE_FROM, TYPE_TO);\n\n/**\n * is string\n * @param o - object to check\n * @returns result\n */\nexport const isString = (o: unknown): o is string =>\n typeof o === 'string' || o instanceof String;\n\n/**\n * is string or number\n * @param o - object to check\n * @returns result\n */\nexport const isStringOrNumber = (o: unknown): boolean =>\n isString(o) || typeof o === 'number';\n"],"mappings":";;;;;;AAqBA,IAAa,YAAY,MACvB,OAAO,MAAM,YAAY,aAAa;;;;;;AAOxC,IAAa,oBAAoB,MAC/B,SAAS,EAAE,IAAI,OAAO,MAAM"}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,60 @@
//#region src/js/constant.ts
/**
* constant
*/
var _DIGIT = "(?:0|[1-9]\\d*)";
var _MATH = `clamp|max|min|exp|hypot|log|pow|sqrt|abs|sign|mod|rem|round|a?(?:cos|sin|tan)|atan2`;
var _CALC = `calc|${_MATH}`;
var _VAR = `var|${_CALC}`;
var ANGLE = "deg|g?rad|turn";
var LENGTH = "[cm]m|[dls]?v(?:[bhiw]|max|min)|in|p[ctx]|q|r?(?:[cl]h|cap|e[mx]|ic)";
var NUM = `[+-]?(?:${_DIGIT}(?:\\.\\d*)?|\\.\\d+)(?:e-?${_DIGIT})?`;
var NUM_POSITIVE = `\\+?(?:${_DIGIT}(?:\\.\\d*)?|\\.\\d+)(?:e-?${_DIGIT})?`;
var NONE = "none";
var PCT = `${NUM}%`;
var SYN_FN_CALC = `^(?:${_CALC})\\(|(?<=[*\\/\\s\\(])(?:${_CALC})\\(`;
var SYN_FN_MATH_START = `^(?:${_MATH})\\($`;
var SYN_FN_VAR = "^var\\(|(?<=[*\\/\\s\\(])var\\(";
var SYN_FN_VAR_START = `^(?:${_VAR})\\(`;
var _ALPHA = `(?:\\s*\\/\\s*(?:${NUM}|${PCT}|${NONE}))?`;
var _ALPHA_LV3 = `(?:\\s*,\\s*(?:${NUM}|${PCT}))?`;
var _COLOR_FUNC = "(?:ok)?l(?:ab|ch)|color|hsla?|hwb|rgba?";
var _COLOR_KEY = "[a-z]+|#[\\da-f]{3}|#[\\da-f]{4}|#[\\da-f]{6}|#[\\da-f]{8}";
var _CS_HUE = "(?:ok)?lch|hsl|hwb";
var _CS_HUE_ARC = "(?:de|in)creasing|longer|shorter";
var _NUM_ANGLE = `${NUM}(?:${ANGLE})?`;
var _NUM_ANGLE_NONE = `(?:${NUM}(?:${ANGLE})?|${NONE})`;
var _NUM_PCT_NONE = `(?:${NUM}|${PCT}|${NONE})`;
var CS_HUE = `(?:${_CS_HUE})(?:\\s(?:${_CS_HUE_ARC})\\shue)?`;
var CS_HUE_CAPT = `(${_CS_HUE})(?:\\s(${_CS_HUE_ARC})\\shue)?`;
var CS_LAB = "(?:ok)?lab";
var CS_LCH = "(?:ok)?lch";
var CS_RGB = `(?:a98|prophoto)-rgb|display-p3|rec2020|srgb(?:-linear)?`;
var CS_XYZ = "xyz(?:-d(?:50|65))?";
var CS_RECT = `${CS_LAB}|${CS_RGB}|${CS_XYZ}`;
var CS_MIX = `${CS_HUE}|${CS_RECT}`;
var FN_COLOR = "color(";
var FN_LIGHT_DARK = "light-dark(";
var FN_MIX = "color-mix(";
var FN_REL = `(?:${_COLOR_FUNC})\\(\\s*from\\s+`;
var FN_REL_CAPT = `(${_COLOR_FUNC})\\(\\s*from\\s+`;
var FN_VAR = "var(";
var SYN_FN_COLOR = `(?:${CS_RGB}|${CS_XYZ})(?:\\s+${_NUM_PCT_NONE}){3}${_ALPHA}`;
var SYN_FN_LIGHT_DARK = "^light-dark\\(";
var SYN_FN_REL = `^${FN_REL}|(?<=[\\s])${FN_REL}`;
var SYN_HSL = `${_NUM_ANGLE_NONE}(?:\\s+${_NUM_PCT_NONE}){2}${_ALPHA}`;
var SYN_HSL_LV3 = `${_NUM_ANGLE}(?:\\s*,\\s*${PCT}){2}${_ALPHA_LV3}`;
var SYN_LCH = `(?:${_NUM_PCT_NONE}\\s+){2}${_NUM_ANGLE_NONE}${_ALPHA}`;
var SYN_MOD = `${_NUM_PCT_NONE}(?:\\s+${_NUM_PCT_NONE}){2}${_ALPHA}`;
var SYN_RGB_LV3 = `(?:${NUM}(?:\\s*,\\s*${NUM}){2}|${PCT}(?:\\s*,\\s*${PCT}){2})${_ALPHA_LV3}`;
var SYN_COLOR_TYPE = `${_COLOR_KEY}|hsla?\\(\\s*${SYN_HSL_LV3}\\s*\\)|rgba?\\(\\s*${SYN_RGB_LV3}\\s*\\)|(?:hsla?|hwb)\\(\\s*${SYN_HSL}\\s*\\)|(?:(?:ok)?lab|rgba?)\\(\\s*${SYN_MOD}\\s*\\)|(?:ok)?lch\\(\\s*${SYN_LCH}\\s*\\)|color\\(\\s*${SYN_FN_COLOR}\\s*\\)`;
var SYN_MIX_PART = `(?:${SYN_COLOR_TYPE})(?:\\s+${PCT})?`;
var SYN_MIX = `color-mix\\(\\s*in\\s+(?:${CS_MIX})\\s*,\\s*${SYN_MIX_PART}\\s*,\\s*${SYN_MIX_PART}\\s*\\)`;
var SYN_MIX_CAPT = `color-mix\\(\\s*in\\s+(${CS_MIX})\\s*,\\s*(${SYN_MIX_PART})\\s*,\\s*(${SYN_MIX_PART})\\s*\\)`;
var VAL_COMP = "computedValue";
var VAL_MIX = "mixValue";
var VAL_SPEC = "specifiedValue";
//#endregion
export { ANGLE, CS_HUE, CS_HUE_CAPT, CS_LAB, CS_LCH, CS_MIX, CS_RECT, CS_RGB, CS_XYZ, FN_COLOR, FN_LIGHT_DARK, FN_MIX, FN_REL, FN_REL_CAPT, FN_VAR, LENGTH, NONE, NUM, NUM_POSITIVE, PCT, SYN_COLOR_TYPE, SYN_FN_CALC, SYN_FN_COLOR, SYN_FN_LIGHT_DARK, SYN_FN_MATH_START, SYN_FN_REL, SYN_FN_VAR, SYN_FN_VAR_START, SYN_HSL, SYN_HSL_LV3, SYN_LCH, SYN_MIX, SYN_MIX_CAPT, SYN_MIX_PART, SYN_MOD, SYN_RGB_LV3, VAL_COMP, VAL_MIX, VAL_SPEC };
//# sourceMappingURL=constant.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,99 @@
import { NullObject } from './cache.js';
import { ColorChannels, Options } from './typedef.js';
/**
* pre process
* @param value - CSS color value
* @param [opt] - options
* @returns value
*/
export declare const preProcess: (value: string, opt?: Options) => string | NullObject;
/**
* convert number to hex string
* @param value - numeric value
* @returns hex string: 00..ff
*/
export declare const numberToHex: (value: number) => string;
/**
* convert color to hex
* @param value - CSS color value
* @param [opt] - options
* @param [opt.alpha] - enable alpha channel
* @returns #rrggbb | #rrggbbaa | null
*/
export declare const colorToHex: (value: string, opt?: Options) => string | null;
/**
* convert color to hsl
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [h, s, l, alpha]
*/
export declare const colorToHsl: (value: string, opt?: Options) => ColorChannels;
/**
* convert color to hwb
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [h, w, b, alpha]
*/
export declare const colorToHwb: (value: string, opt?: Options) => ColorChannels;
/**
* convert color to lab
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [l, a, b, alpha]
*/
export declare const colorToLab: (value: string, opt?: Options) => ColorChannels;
/**
* convert color to lch
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [l, c, h, alpha]
*/
export declare const colorToLch: (value: string, opt?: Options) => ColorChannels;
/**
* convert color to oklab
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [l, a, b, alpha]
*/
export declare const colorToOklab: (value: string, opt?: Options) => ColorChannels;
/**
* convert color to oklch
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [l, c, h, alpha]
*/
export declare const colorToOklch: (value: string, opt?: Options) => ColorChannels;
/**
* convert color to rgb
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [r, g, b, alpha]
*/
export declare const colorToRgb: (value: string, opt?: Options) => ColorChannels;
/**
* convert color to xyz
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [x, y, z, alpha]
*/
export declare const colorToXyz: (value: string, opt?: Options) => ColorChannels;
/**
* convert color to xyz-d50
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [x, y, z, alpha]
*/
export declare const colorToXyzD50: (value: string, opt?: Options) => ColorChannels;
export declare const convert: {
colorToHex: (value: string, opt?: Options) => string | null;
colorToHsl: (value: string, opt?: Options) => ColorChannels;
colorToHwb: (value: string, opt?: Options) => ColorChannels;
colorToLab: (value: string, opt?: Options) => ColorChannels;
colorToLch: (value: string, opt?: Options) => ColorChannels;
colorToOklab: (value: string, opt?: Options) => ColorChannels;
colorToOklch: (value: string, opt?: Options) => ColorChannels;
colorToRgb: (value: string, opt?: Options) => ColorChannels;
colorToXyz: (value: string, opt?: Options) => ColorChannels;
colorToXyzD50: (value: string, opt?: Options) => ColorChannels;
numberToHex: (value: number) => string;
};
@@ -0,0 +1,245 @@
import { CacheItem, NullObject, createCacheKey, getCache, setCache } from "./cache.js";
import { isString } from "./common.js";
import { SYN_FN_CALC, SYN_FN_REL, SYN_FN_VAR, VAL_COMP } from "./constant.js";
import { convertColorToHsl, convertColorToHwb, convertColorToLab, convertColorToLch, convertColorToOklab, convertColorToOklch, convertColorToRgb, numberToHexString, parseColorFunc, parseColorValue } from "./color.js";
import { resolveRelativeColor } from "./relative-color.js";
import { resolveColor } from "./resolve.js";
import { resolveVar } from "./css-var.js";
import { cssCalc } from "./css-calc.js";
//#region src/js/convert.ts
/**
* convert
*/
var NAMESPACE = "convert";
var REG_FN_CALC = new RegExp(SYN_FN_CALC);
var REG_FN_REL = new RegExp(SYN_FN_REL);
var REG_FN_VAR = new RegExp(SYN_FN_VAR);
/**
* pre process
* @param value - CSS color value
* @param [opt] - options
* @returns value
*/
var preProcess = (value, opt = {}) => {
if (!isString(value)) return new NullObject();
value = value.trim();
if (!value) return new NullObject();
const cacheKey = createCacheKey({
namespace: NAMESPACE,
name: "preProcess",
value
}, opt);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) {
if (cachedResult.isNull) return cachedResult;
return cachedResult.item;
}
let res = value;
if (REG_FN_VAR.test(value)) {
const resolved = resolveVar(value, opt);
if (isString(resolved)) res = resolved;
else {
setCache(cacheKey, null);
return new NullObject();
}
}
if (isString(res)) {
if (REG_FN_REL.test(res)) {
const resolved = resolveRelativeColor(res, opt);
if (isString(resolved)) res = resolved;
else {
setCache(cacheKey, null);
return new NullObject();
}
} else if (REG_FN_CALC.test(res)) res = cssCalc(res, opt);
}
if (isString(res)) {
if (res.startsWith("color-mix")) res = resolveColor(res, {
...opt,
format: VAL_COMP,
nullable: true
});
}
setCache(cacheKey, res);
return res;
};
/**
* converter factory to reduce boilerplate
* @param name - function name for cache
* @param format - color format
* @param convertFn - conversion function
* @returns color converter function
*/
var createColorConverter = (name, format, convertFn) => {
const colorConverterFn = (value, opt = {}) => {
if (!isString(value)) throw new TypeError(`${value} is not a string.`);
const resolved = preProcess(value, opt);
if (resolved instanceof NullObject) return [
0,
0,
0,
0
];
const val = resolved.toLowerCase();
const cacheKey = createCacheKey({
namespace: NAMESPACE,
name,
value: val
}, opt);
const cached = getCache(cacheKey);
if (cached instanceof CacheItem) return cached.item;
const result = convertFn(val, {
...opt,
format
});
setCache(cacheKey, result);
return result;
};
return colorConverterFn;
};
/**
* convert number to hex string
* @param value - numeric value
* @returns hex string: 00..ff
*/
var numberToHex = (value) => numberToHexString(value);
/**
* convert color to hex
* @param value - CSS color value
* @param [opt] - options
* @param [opt.alpha] - enable alpha channel
* @returns #rrggbb | #rrggbbaa | null
*/
var colorToHex = (value, opt = {}) => {
if (!isString(value)) throw new TypeError(`${value} is not a string.`);
const resolved = preProcess(value, opt);
if (resolved instanceof NullObject) return null;
const val = resolved.toLowerCase();
const cacheKey = createCacheKey({
namespace: NAMESPACE,
name: "colorToHex",
value: val
}, opt);
const cached = getCache(cacheKey);
if (cached instanceof CacheItem) {
if (cached.isNull) return null;
return cached.item;
}
const hex = resolveColor(val, {
...opt,
nullable: true,
format: opt.alpha ? "hexAlpha" : "hex"
});
if (isString(hex)) {
setCache(cacheKey, hex);
return hex;
}
setCache(cacheKey, null);
return null;
};
/**
* convert color to hsl
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [h, s, l, alpha]
*/
var colorToHsl = createColorConverter("colorToHsl", "hsl", convertColorToHsl);
/**
* convert color to hwb
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [h, w, b, alpha]
*/
var colorToHwb = createColorConverter("colorToHwb", "hwb", convertColorToHwb);
/**
* convert color to lab
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [l, a, b, alpha]
*/
var colorToLab = createColorConverter("colorToLab", "lab", convertColorToLab);
/**
* convert color to lch
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [l, c, h, alpha]
*/
var colorToLch = createColorConverter("colorToLch", "lch", convertColorToLch);
/**
* convert color to oklab
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [l, a, b, alpha]
*/
var colorToOklab = createColorConverter("colorToOklab", "oklab", convertColorToOklab);
/**
* convert color to oklch
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [l, c, h, alpha]
*/
var colorToOklch = createColorConverter("colorToOklch", "oklch", convertColorToOklch);
/**
* convert color to rgb
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [r, g, b, alpha]
*/
var colorToRgb = createColorConverter("colorToRgb", "rgb", convertColorToRgb);
/**
* convert color to xyz
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [x, y, z, alpha]
*/
var colorToXyz = (value, opt = {}) => {
if (!isString(value)) throw new TypeError(`${value} is not a string.`);
const resolved = preProcess(value, opt);
if (resolved instanceof NullObject) return [
0,
0,
0,
0
];
const val = resolved.toLowerCase();
const cacheKey = createCacheKey({
namespace: NAMESPACE,
name: "colorToXyz",
value: val
}, opt);
const cached = getCache(cacheKey);
if (cached instanceof CacheItem) return cached.item;
let parsed;
if (val.startsWith("color(")) parsed = parseColorFunc(val, opt);
else parsed = parseColorValue(val, opt);
const [, ...xyz] = parsed;
setCache(cacheKey, xyz);
return xyz;
};
/**
* convert color to xyz-d50
* @param value - CSS color value
* @param [opt] - options
* @returns ColorChannels - [x, y, z, alpha]
*/
var colorToXyzD50 = (value, opt = {}) => {
opt.d50 = true;
return colorToXyz(value, opt);
};
var convert = {
colorToHex,
colorToHsl,
colorToHwb,
colorToLab,
colorToLch,
colorToOklab,
colorToOklch,
colorToRgb,
colorToXyz,
colorToXyzD50,
numberToHex
};
//#endregion
export { convert };
//# sourceMappingURL=convert.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,89 @@
import { CSSToken } from '@csstools/css-tokenizer';
import { NullObject } from './cache.js';
import { Options } from './typedef.js';
/**
* Calclator
*/
export declare class Calculator {
#private;
/**
* constructor
*/
constructor();
get hasNum(): boolean;
set hasNum(value: boolean);
get numSum(): number[];
get numMul(): number[];
get hasPct(): boolean;
set hasPct(value: boolean);
get pctSum(): number[];
get pctMul(): number[];
get hasDim(): boolean;
set hasDim(value: boolean);
get dimSum(): string[];
get dimSub(): string[];
get dimMul(): string[];
get dimDiv(): string[];
get hasEtc(): boolean;
set hasEtc(value: boolean);
get etcSum(): string[];
get etcSub(): string[];
get etcMul(): string[];
get etcDiv(): string[];
/**
* clear values
* @returns void
*/
clear(): void;
/**
* sort values
* @param values - values
* @returns sorted values
*/
sort(values?: string[]): string[];
/**
* multiply values
* @returns resolved value
*/
multiply(): string;
/**
* sum values
* @returns resolved value
*/
sum(): string;
}
/**
* sort calc values
* @param values - values to sort
* @param [finalize] - finalize values
* @returns sorted values
*/
export declare const sortCalcValues: (values?: (number | string)[], finalize?: boolean) => string;
/**
* serialize calc
* @param value - CSS value
* @param [opt] - options
* @returns serialized value
*/
export declare const serializeCalc: (value: string, opt?: Options) => string;
/**
* resolve dimension
* @param token - CSS token
* @param [opt] - options
* @returns resolved value
*/
export declare const resolveDimension: (token: CSSToken, opt?: Options) => string | NullObject;
/**
* parse tokens
* @param tokens - CSS tokens
* @param [opt] - options
* @returns parsed tokens
*/
export declare const parseTokens: (tokens: CSSToken[], opt?: Options) => string[];
/**
* CSS calc()
* @param value - CSS value including calc()
* @param [opt] - options
* @returns resolved value
*/
export declare const cssCalc: (value: string, opt?: Options) => string;
@@ -0,0 +1,636 @@
import { CacheItem, NullObject, createCacheKey, getCache, setCache } from "./cache.js";
import { isString, isStringOrNumber } from "./common.js";
import { ANGLE, LENGTH, NUM, SYN_FN_CALC, SYN_FN_MATH_START, SYN_FN_VAR, SYN_FN_VAR_START } from "./constant.js";
import { resolveLengthInPixels, roundToPrecision } from "./util.js";
import { resolveVar } from "./css-var.js";
import { calc } from "@csstools/css-calc";
import { TokenType, tokenize } from "@csstools/css-tokenizer";
//#region src/js/css-calc.ts
/**
* css-calc
*/
var { CloseParen: PAREN_CLOSE, Comment: COMMENT, Dimension: DIM, EOF, Function: FUNC, OpenParen: PAREN_OPEN, Whitespace: W_SPACE } = TokenType;
var NAMESPACE = "css-calc";
var TRIA = 3;
var HEX = 16;
var MAX_PCT = 100;
var REG_FN_CALC = new RegExp(SYN_FN_CALC);
var REG_FN_CALC_NUM = new RegExp(`^calc\\((${NUM})\\)$`);
var REG_FN_MATH_START = new RegExp(SYN_FN_MATH_START);
var REG_FN_VAR = new RegExp(SYN_FN_VAR);
var REG_FN_VAR_START = new RegExp(SYN_FN_VAR_START);
var REG_OPERATOR = /\s[*+/-]\s/;
var REG_PAREN_OPEN = /\($/;
var REG_TYPE_DIM = new RegExp(`^(${NUM})(${ANGLE}|${LENGTH})$`);
var REG_TYPE_DIM_PCT = new RegExp(`^(${NUM})(${ANGLE}|${LENGTH}|%)$`);
var REG_TYPE_PCT = new RegExp(`^(${NUM})%$`);
/**
* Calclator
*/
var Calculator = class {
#hasNum;
#numSum;
#numMul;
#hasPct;
#pctSum;
#pctMul;
#hasDim;
#dimSum;
#dimSub;
#dimMul;
#dimDiv;
#hasEtc;
#etcSum;
#etcSub;
#etcMul;
#etcDiv;
#calcOpts;
/**
* constructor
*/
constructor() {
this.#hasNum = false;
this.#numSum = [];
this.#numMul = [];
this.#hasPct = false;
this.#pctSum = [];
this.#pctMul = [];
this.#hasDim = false;
this.#dimSum = [];
this.#dimSub = [];
this.#dimMul = [];
this.#dimDiv = [];
this.#hasEtc = false;
this.#etcSum = [];
this.#etcSub = [];
this.#etcMul = [];
this.#etcDiv = [];
this.#calcOpts = { toCanonicalUnits: true };
}
get hasNum() {
return this.#hasNum;
}
set hasNum(value) {
this.#hasNum = !!value;
}
get numSum() {
return this.#numSum;
}
get numMul() {
return this.#numMul;
}
get hasPct() {
return this.#hasPct;
}
set hasPct(value) {
this.#hasPct = !!value;
}
get pctSum() {
return this.#pctSum;
}
get pctMul() {
return this.#pctMul;
}
get hasDim() {
return this.#hasDim;
}
set hasDim(value) {
this.#hasDim = !!value;
}
get dimSum() {
return this.#dimSum;
}
get dimSub() {
return this.#dimSub;
}
get dimMul() {
return this.#dimMul;
}
get dimDiv() {
return this.#dimDiv;
}
get hasEtc() {
return this.#hasEtc;
}
set hasEtc(value) {
this.#hasEtc = !!value;
}
get etcSum() {
return this.#etcSum;
}
get etcSub() {
return this.#etcSub;
}
get etcMul() {
return this.#etcMul;
}
get etcDiv() {
return this.#etcDiv;
}
/**
* clear values
* @returns void
*/
clear() {
this.#hasNum = false;
this.#numSum.length = 0;
this.#numMul.length = 0;
this.#hasPct = false;
this.#pctSum.length = 0;
this.#pctMul.length = 0;
this.#hasDim = false;
this.#dimSum.length = 0;
this.#dimSub.length = 0;
this.#dimMul.length = 0;
this.#dimDiv.length = 0;
this.#hasEtc = false;
this.#etcSum.length = 0;
this.#etcSub.length = 0;
this.#etcMul.length = 0;
this.#etcDiv.length = 0;
}
/**
* sort values
* @param values - values
* @returns sorted values
*/
sort(values = []) {
const arr = [...values];
if (arr.length > 1) arr.sort((a, b) => {
let res;
if (REG_TYPE_DIM_PCT.test(a) && REG_TYPE_DIM_PCT.test(b)) {
const [, valA, unitA] = a.match(REG_TYPE_DIM_PCT);
const [, valB, unitB] = b.match(REG_TYPE_DIM_PCT);
if (unitA === unitB) if (Number(valA) === Number(valB)) res = 0;
else if (Number(valA) > Number(valB)) res = 1;
else res = -1;
else if (unitA > unitB) res = 1;
else res = -1;
} else if (a === b) res = 0;
else if (a > b) res = 1;
else res = -1;
return res;
});
return arr;
}
/**
* multiply values
* @returns resolved value
*/
multiply() {
const value = [];
let num;
if (this.#hasNum) {
num = 1;
for (const i of this.#numMul) {
num *= i;
if (num === 0 || !Number.isFinite(num) || Number.isNaN(num)) break;
}
if (!this.#hasPct && !this.#hasDim && !this.hasEtc) {
if (Number.isFinite(num)) num = roundToPrecision(num, HEX);
value.push(num);
}
}
if (this.#hasPct) {
if (typeof num !== "number") num = 1;
for (const i of this.#pctMul) {
num *= i;
if (num === 0 || !Number.isFinite(num) || Number.isNaN(num)) break;
}
if (Number.isFinite(num)) num = `${roundToPrecision(num, HEX)}%`;
if (!this.#hasDim && !this.hasEtc) value.push(num);
}
if (this.#hasDim) {
let dim = "";
let mul = "";
let div = "";
if (this.#dimMul.length) if (this.#dimMul.length === 1) [mul] = this.#dimMul;
else mul = `${this.sort(this.#dimMul).join(" * ")}`;
if (this.#dimDiv.length) if (this.#dimDiv.length === 1) [div] = this.#dimDiv;
else div = `${this.sort(this.#dimDiv).join(" * ")}`;
if (Number.isFinite(num)) {
if (mul) if (div) if (div.includes("*")) dim = calc(`calc(${num} * ${mul} / (${div}))`, this.#calcOpts);
else dim = calc(`calc(${num} * ${mul} / ${div})`, this.#calcOpts);
else dim = calc(`calc(${num} * ${mul})`, this.#calcOpts);
else if (div.includes("*")) dim = calc(`calc(${num} / (${div}))`, this.#calcOpts);
else dim = calc(`calc(${num} / ${div})`, this.#calcOpts);
value.push(dim.replace(/^calc/, ""));
} else {
if (!value.length && num !== void 0) value.push(num);
if (mul) {
if (div) if (div.includes("*")) dim = calc(`calc(${mul} / (${div}))`, this.#calcOpts);
else dim = calc(`calc(${mul} / ${div})`, this.#calcOpts);
else dim = calc(`calc(${mul})`, this.#calcOpts);
if (value.length) value.push("*", dim.replace(/^calc/, ""));
else value.push(dim.replace(/^calc/, ""));
} else {
dim = calc(`calc(${div})`, this.#calcOpts);
if (value.length) value.push("/", dim.replace(/^calc/, ""));
else value.push("1", "/", dim.replace(/^calc/, ""));
}
}
}
if (this.#hasEtc) {
if (this.#etcMul.length) {
if (!value.length && num !== void 0) value.push(num);
const mul = this.sort(this.#etcMul).join(" * ");
if (value.length) value.push(`* ${mul}`);
else value.push(`${mul}`);
}
if (this.#etcDiv.length) {
const div = this.sort(this.#etcDiv).join(" * ");
if (div.includes("*")) if (value.length) value.push(`/ (${div})`);
else value.push(`1 / (${div})`);
else if (value.length) value.push(`/ ${div}`);
else value.push(`1 / ${div}`);
}
}
if (value.length) return value.join(" ");
return "";
}
/**
* sum values
* @returns resolved value
*/
sum() {
const value = [];
if (this.#hasNum) {
let num = 0;
for (const i of this.#numSum) {
num += i;
if (!Number.isFinite(num) || Number.isNaN(num)) break;
}
value.push(num);
}
if (this.#hasPct) {
let num = 0;
for (const i of this.#pctSum) {
num += i;
if (!Number.isFinite(num)) break;
}
if (Number.isFinite(num)) num = `${num}%`;
if (value.length) value.push(`+ ${num}`);
else value.push(num);
}
if (this.#hasDim) {
let dim, sum, sub;
if (this.#dimSum.length) sum = this.sort(this.#dimSum).join(" + ");
if (this.#dimSub.length) sub = this.sort(this.#dimSub).join(" + ");
if (sum) if (sub) if (sub.includes("-")) dim = calc(`calc(${sum} - (${sub}))`, this.#calcOpts);
else dim = calc(`calc(${sum} - ${sub})`, this.#calcOpts);
else dim = calc(`calc(${sum})`, this.#calcOpts);
else dim = calc(`calc(-1 * (${sub}))`, this.#calcOpts);
if (value.length) value.push("+", dim.replace(/^calc/, ""));
else value.push(dim.replace(/^calc/, ""));
}
if (this.#hasEtc) {
if (this.#etcSum.length) {
const sum = this.sort(this.#etcSum).map((item) => {
let res;
if (REG_OPERATOR.test(item) && !item.startsWith("(") && !item.endsWith(")")) res = `(${item})`;
else res = item;
return res;
}).join(" + ");
if (value.length) if (this.#etcSum.length > 1) value.push(`+ (${sum})`);
else value.push(`+ ${sum}`);
else value.push(`${sum}`);
}
if (this.#etcSub.length) {
const sub = this.sort(this.#etcSub).map((item) => {
let res;
if (REG_OPERATOR.test(item) && !item.startsWith("(") && !item.endsWith(")")) res = `(${item})`;
else res = item;
return res;
}).join(" + ");
if (value.length) if (this.#etcSub.length > 1) value.push(`- (${sub})`);
else value.push(`- ${sub}`);
else if (this.#etcSub.length > 1) value.push(`-1 * (${sub})`);
else value.push(`-1 * ${sub}`);
}
}
if (value.length) return value.join(" ");
return "";
}
};
/**
* sort calc values
* @param values - values to sort
* @param [finalize] - finalize values
* @returns sorted values
*/
var sortCalcValues = (values = [], finalize = false) => {
if (values.length < TRIA) throw new Error(`Unexpected array length ${values.length}.`);
const start = values.shift();
if (!isString(start) || !start.endsWith("(")) throw new Error(`Unexpected token ${start}.`);
const end = values.pop();
if (end !== ")") throw new Error(`Unexpected token ${end}.`);
if (values.length === 1) {
const [value] = values;
if (!isStringOrNumber(value)) throw new Error(`Unexpected token ${value}.`);
return `${start}${value}${end}`;
}
const sortedValues = [];
const cal = new Calculator();
let operator = "";
const l = values.length;
let hasAddSub = false;
for (let i = 0; i < l; i++) {
const value = values[i];
if (!isStringOrNumber(value)) throw new Error(`Unexpected token ${value}.`);
if (value === "*" || value === "/") operator = value;
else if (value === "+" || value === "-") {
const sortedValue = cal.multiply();
if (sortedValue) sortedValues.push(sortedValue, value);
hasAddSub = true;
cal.clear();
operator = "";
} else {
const numValue = Number(value);
const strValue = `${value}`;
switch (operator) {
case "/":
if (Number.isFinite(numValue)) {
cal.hasNum = true;
cal.numMul.push(1 / numValue);
} else if (REG_TYPE_PCT.test(strValue)) {
const [, val] = strValue.match(REG_TYPE_PCT);
cal.hasPct = true;
cal.pctMul.push(MAX_PCT * MAX_PCT / Number(val));
} else if (REG_TYPE_DIM.test(strValue)) {
cal.hasDim = true;
cal.dimDiv.push(strValue);
} else {
cal.hasEtc = true;
cal.etcDiv.push(strValue);
}
break;
default: if (Number.isFinite(numValue)) {
cal.hasNum = true;
cal.numMul.push(numValue);
} else if (REG_TYPE_PCT.test(strValue)) {
const [, val] = strValue.match(REG_TYPE_PCT);
cal.hasPct = true;
cal.pctMul.push(Number(val));
} else if (REG_TYPE_DIM.test(strValue)) {
cal.hasDim = true;
cal.dimMul.push(strValue);
} else {
cal.hasEtc = true;
cal.etcMul.push(strValue);
}
}
}
if (i === l - 1) {
const sortedValue = cal.multiply();
if (sortedValue) sortedValues.push(sortedValue);
cal.clear();
operator = "";
}
}
let resolvedValue = "";
if (finalize && hasAddSub) {
const finalizedValues = [];
cal.clear();
operator = "";
const l = sortedValues.length;
for (let i = 0; i < l; i++) {
const value = sortedValues[i];
if (isStringOrNumber(value)) if (value === "+" || value === "-") operator = value;
else {
const numValue = Number(value);
const strValue = `${value}`;
switch (operator) {
case "-":
if (Number.isFinite(numValue)) {
cal.hasNum = true;
cal.numSum.push(-1 * numValue);
} else if (REG_TYPE_PCT.test(strValue)) {
const [, val] = strValue.match(REG_TYPE_PCT);
cal.hasPct = true;
cal.pctSum.push(-1 * Number(val));
} else if (REG_TYPE_DIM.test(strValue)) {
cal.hasDim = true;
cal.dimSub.push(strValue);
} else {
cal.hasEtc = true;
cal.etcSub.push(strValue);
}
break;
default: if (Number.isFinite(numValue)) {
cal.hasNum = true;
cal.numSum.push(numValue);
} else if (REG_TYPE_PCT.test(strValue)) {
const [, val] = strValue.match(REG_TYPE_PCT);
cal.hasPct = true;
cal.pctSum.push(Number(val));
} else if (REG_TYPE_DIM.test(strValue)) {
cal.hasDim = true;
cal.dimSum.push(strValue);
} else {
cal.hasEtc = true;
cal.etcSum.push(strValue);
}
}
}
if (i === l - 1) {
const sortedValue = cal.sum();
if (sortedValue) finalizedValues.push(sortedValue);
cal.clear();
operator = "";
}
}
resolvedValue = finalizedValues.join(" ").replace(/\+\s-/g, "- ");
} else resolvedValue = sortedValues.join(" ").replace(/\+\s-/g, "- ");
if (resolvedValue.startsWith("(") && resolvedValue.endsWith(")") && resolvedValue.lastIndexOf("(") === 0 && resolvedValue.indexOf(")") === resolvedValue.length - 1) resolvedValue = resolvedValue.substring(1, resolvedValue.length - 1);
return `${start}${resolvedValue}${end}`;
};
/**
* resolve AST node
* @param node - AST node
* @param isRoot - is root node
* @returns resolved value
*/
var resolveNode = (node, isRoot) => {
const flatItems = [];
for (const item of node) if (Array.isArray(item)) flatItems.push(resolveNode(item, false));
else flatItems.push(item);
if (isRoot) {
if (flatItems.length >= TRIA) return sortCalcValues(flatItems, true);
const joined = flatItems.join("");
return joined.startsWith("calc(") ? joined : `calc(${joined})`;
}
if (flatItems.length >= TRIA) {
let serialized = sortCalcValues(flatItems, false);
if (REG_FN_VAR_START.test(serialized)) serialized = calc(serialized, { toCanonicalUnits: true });
return serialized;
}
return flatItems.join("");
};
/**
* serialize calc
* @param value - CSS value
* @param [opt] - options
* @returns serialized value
*/
var serializeCalc = (value, opt = {}) => {
const { format = "" } = opt;
if (isString(value)) {
if (!REG_FN_VAR_START.test(value) || format !== "specifiedValue") return value;
value = value.toLowerCase().trim();
} else throw new TypeError(`${value} is not a string.`);
const cacheKey = createCacheKey({
namespace: NAMESPACE,
name: "serializeCalc",
value
}, opt);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) return cachedResult.item;
const items = tokenize({ css: value }).map((token) => {
const [type, val] = token;
let res = "";
if (type !== W_SPACE && type !== COMMENT) res = val;
return res;
}).filter((v) => v);
const stack = [[]];
for (const item of items) if (REG_PAREN_OPEN.test(item)) {
const newNode = [item];
const parent = stack[stack.length - 1];
if (parent) parent.push(newNode);
stack.push(newNode);
} else if (item === ")") if (stack.length > 1) {
const currentLevel = stack.pop();
if (currentLevel) currentLevel.push(item);
} else {
const root = stack[0];
if (root) root.push(item);
}
else {
const parent = stack[stack.length - 1];
if (parent) parent.push(item);
}
let serializedCalc = "";
const rootItems = stack[0];
if (rootItems) if (rootItems.length === 1 && Array.isArray(rootItems[0])) serializedCalc = resolveNode(rootItems[0], true);
else {
const flatItems = [];
for (const item of rootItems) if (Array.isArray(item)) flatItems.push(resolveNode(item, false));
else flatItems.push(item);
if (flatItems.length >= TRIA) serializedCalc = sortCalcValues(flatItems, true);
else {
const firstItem = flatItems[0] || "";
serializedCalc = isString(firstItem) && firstItem.startsWith("calc(") ? firstItem : `calc(${firstItem})`;
}
}
setCache(cacheKey, serializedCalc);
return serializedCalc;
};
/**
* resolve dimension
* @param token - CSS token
* @param [opt] - options
* @returns resolved value
*/
var resolveDimension = (token, opt = {}) => {
if (!Array.isArray(token)) throw new TypeError(`${token} is not an array.`);
const [, , , , detail = {}] = token;
const { unit, value } = detail;
if (unit === "px") return `${value}${unit}`;
const pixelValue = resolveLengthInPixels(Number(value), unit, opt);
if (Number.isFinite(pixelValue)) return `${roundToPrecision(pixelValue, HEX)}px`;
return new NullObject();
};
/**
* parse tokens
* @param tokens - CSS tokens
* @param [opt] - options
* @returns parsed tokens
*/
var parseTokens = (tokens, opt = {}) => {
if (!Array.isArray(tokens)) throw new TypeError(`${tokens} is not an array.`);
const { format = "" } = opt;
const mathFunc = /* @__PURE__ */ new Set();
let nest = 0;
const res = [];
for (const token of tokens) {
if (!Array.isArray(token)) throw new TypeError(`${token} is not an array.`);
const [type = "", value = ""] = token;
switch (type) {
case DIM:
if (format === "specifiedValue" && !mathFunc.has(nest)) res.push(value);
else {
const resolvedValue = resolveDimension(token, opt);
if (isString(resolvedValue)) res.push(resolvedValue);
else res.push(value);
}
break;
case FUNC:
case PAREN_OPEN:
res.push(value);
nest++;
if (REG_FN_MATH_START.test(value)) mathFunc.add(nest);
break;
case PAREN_CLOSE:
if (res.length) if (res[res.length - 1] === " ") res.splice(-1, 1, value);
else res.push(value);
else res.push(value);
if (mathFunc.has(nest)) mathFunc.delete(nest);
nest--;
break;
case W_SPACE:
if (res.length) {
const lastValue = res[res.length - 1];
if (isString(lastValue) && !lastValue.endsWith("(") && lastValue !== " ") res.push(value);
}
break;
default: if (type !== COMMENT && type !== EOF) res.push(value);
}
}
return res;
};
/**
* CSS calc()
* @param value - CSS value including calc()
* @param [opt] - options
* @returns resolved value
*/
var cssCalc = (value, opt = {}) => {
const { format = "" } = opt;
if (isString(value)) {
if (REG_FN_VAR.test(value)) if (format === "specifiedValue") return value;
else {
const resolvedValue = resolveVar(value, opt);
if (isString(resolvedValue)) return resolvedValue;
else return "";
}
else if (!REG_FN_CALC.test(value)) return value;
value = value.toLowerCase().trim();
} else throw new TypeError(`${value} is not a string.`);
const cacheKey = createCacheKey({
namespace: NAMESPACE,
name: "cssCalc",
value
}, opt);
const cachedResult = getCache(cacheKey);
if (cachedResult instanceof CacheItem) return cachedResult.item;
let resolvedValue = calc(parseTokens(tokenize({ css: value }), opt).join(""), { toCanonicalUnits: true });
if (REG_FN_VAR_START.test(value)) {
if (REG_TYPE_DIM_PCT.test(resolvedValue)) {
const [, val, unit] = resolvedValue.match(REG_TYPE_DIM_PCT);
resolvedValue = `${roundToPrecision(Number(val), HEX)}${unit}`;
}
if (resolvedValue && !REG_FN_VAR_START.test(resolvedValue) && format === "specifiedValue") resolvedValue = `calc(${resolvedValue})`;
}
if (format === "specifiedValue") {
if (/\s[-+*/]\s/.test(resolvedValue) && !resolvedValue.includes("NaN")) resolvedValue = serializeCalc(resolvedValue, opt);
else if (REG_FN_CALC_NUM.test(resolvedValue)) {
const [, val] = resolvedValue.match(REG_FN_CALC_NUM);
resolvedValue = `calc(${roundToPrecision(Number(val), HEX)})`;
}
}
setCache(cacheKey, resolvedValue);
return resolvedValue;
};
//#endregion
export { cssCalc, resolveDimension, serializeCalc };
//# sourceMappingURL=css-calc.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,79 @@
import { Options } from './typedef.js';
/**
* @type ColorStopList - list of color stops
*/
type ColorStopList = [string, string, ...string[]];
/**
* @typedef ValidateGradientLine - validate gradient line
* @property line - gradient line
* @property valid - result
*/
interface ValidateGradientLine {
line: string;
valid: boolean;
}
/**
* @typedef ValidateColorStops - validate color stops
* @property colorStops - list of color stops
* @property valid - result
*/
interface ValidateColorStops {
colorStops: string[];
valid: boolean;
}
/**
* @typedef Gradient - parsed CSS gradient
* @property value - input value
* @property type - gradient type
* @property [gradientLine] - gradient line
* @property colorStopList - list of color stops
*/
interface Gradient {
value: string;
type: string;
gradientLine?: string;
colorStopList: ColorStopList;
}
/**
* get gradient type
* @param value - gradient value
* @returns gradient type
*/
export declare const getGradientType: (value: string) => string;
/**
* validate gradient line
* @param value - gradient line value
* @param type - gradient type
* @returns result
*/
export declare const validateGradientLine: (value: string, type: string) => ValidateGradientLine;
/**
* validate color stop list
* @param list
* @param type
* @param [opt]
* @returns result
*/
export declare const validateColorStopList: (list: string[], type: string, opt?: Options) => ValidateColorStops;
/**
* parse CSS gradient
* @param value - gradient value
* @param [opt] - options
* @returns parsed result
*/
export declare const parseGradient: (value: string, opt?: Options) => Gradient | null;
/**
* resolve CSS gradient
* @param value - CSS value
* @param [opt] - options
* @returns result
*/
export declare const resolveGradient: (value: string, opt?: Options) => string;
/**
* is CSS gradient
* @param value - CSS value
* @param [opt] - options
* @returns result
*/
export declare const isGradient: (value: string, opt?: Options) => boolean;
export {};

Some files were not shown because too many files have changed in this diff Show More