pure-schemify 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [Unreleased]
6
+
7
+ ### What's Changed
8
+
9
+ - **Documentation**: Created `README.md` to explain the library's purpose, implementation details, and usage.
10
+ - **Configuration**: Added `.gitignore` to maintain a clean repository.
11
+ - **Initial Commit**: documented existing source code.
package/README.md ADDED
@@ -0,0 +1,153 @@
1
+ # purecore-schemify
2
+
3
+ **purecore-schemify** is a **High Fidelity Polyfill** for Zod-like schema validation, designed specifically for the `@purecore` ecosystem. It provides a lightweight, zero-dependency, type-safe runtime validation library that follows the fluent API design pattern familiar to TypeScript developers.
4
+
5
+ This library allows you to define schemas for your data, infer TypeScript types automatically, and validate data at runtime with informative error reporting.
6
+
7
+ ## 🚀 Features
8
+
9
+ - **Zero Dependencies**: Built entirely with native TypeScript, ensuring minimal footprint and maximum portability.
10
+ - **Fluent API**: Chainable methods (e.g., `.min()`, `.optional()`, `.refine()`) for intuitive schema definition.
11
+ - **Static Type Inference**: Automatic TypeScript type inference using `z.infer<typeof schema>`.
12
+ - **Runtime Validation**: Robust `parse` and `safeParse` methods to ensure data integrity.
13
+ - **Nominal Typing Support**: Built-in support for Nominal Types via `z.nominal` and atomic behaviors, enforcing strict domain modeling.
14
+ - **Extensible**: check mechanisms, refinements, and transformations.
15
+
16
+ ## 📦 Installation
17
+
18
+ Since this is part of the `@purecore` monorepo structure, you can typically use it by importing the source directly or linking the package.
19
+
20
+ ```bash
21
+ bun install @purecore/schemify
22
+ ```
23
+
24
+ _(Note: Adjust installation method based on your specific workspace configuration)_
25
+
26
+ ## 🛠 Usage
27
+
28
+ ### Basic Primitives
29
+
30
+ ```typescript
31
+ import { z } from "./src/index";
32
+
33
+ // String Schema
34
+ const emailSchema = z.string().email().min(5);
35
+
36
+ // Number Schema
37
+ const ageSchema = z.number().int().min(18).max(120);
38
+
39
+ // Parse
40
+ emailSchema.parse("user@example.com"); // "user@example.com"
41
+ ageSchema.parse(25); // 25
42
+ ```
43
+
44
+ ### Object Schemas
45
+
46
+ ```typescript
47
+ const UserSchema = z.object({
48
+ id: z.number(),
49
+ username: z.string().min(3),
50
+ isActive: z.boolean().default(true),
51
+ roles: z.array(z.string()).optional(),
52
+ });
53
+
54
+ type User = z.infer<typeof UserSchema>;
55
+ // inferred as: { id: number; username: string; isActive: boolean; roles?: string[] }
56
+
57
+ const result = UserSchema.safeParse({
58
+ id: 1,
59
+ username: "alice",
60
+ });
61
+
62
+ if (result.success) {
63
+ console.log(result.data);
64
+ } else {
65
+ console.error(result.error);
66
+ }
67
+ ```
68
+
69
+ ## 🧠 Advanced Concepts: Nominal & Atomic Types
70
+
71
+ One of the unique features of `purecore-schemify` is its first-class support for **Nominal Types** and **Atomic Behaviors**. This allows you to create opaque types that are structurally strings/numbers but semantically distinct.
72
+
73
+ ### Behaviors
74
+
75
+ ```typescript
76
+ // Define a behavior for a UserID
77
+ const UserId = z.behavior("UserId", z.string().uuid());
78
+
79
+ // Forge a value (validates and brands it)
80
+ const id = UserId.forge("123e4567-e89b-12d3-a456-426614174000");
81
+
82
+ // 'id' is now typed as AtomicType<string, "UserId">
83
+ // It cannot be accidentally assigned to a generic string or another nominal type.
84
+ ```
85
+
86
+ ### Nominal Wrapper
87
+
88
+ ```typescript
89
+ const Email = z.nominal("Email", z.string().email());
90
+
91
+ const myEmail = Email.validate("test@test.com");
92
+ ```
93
+
94
+ ## 📚 Technical Deep Dive: How it Works
95
+
96
+ ### The Core: `SchemifyType`
97
+
98
+ At the heart of the library is the abstract base class `SchemifyType<Output, Def, Input>`.
99
+
100
+ - **`_parseSync`**: Every specific type (String, Number, Object) implements this abstract method to perform its core validation logic.
101
+ - **`_process`**: This method handles the common logic for all types:
102
+ 1. **Optional/Nullable/Default**: Checks if the value is missing or null and handles it based on configuration.
103
+ 2. **Validation**: Calls `_parseSync`.
104
+ 3. **Refinements**: Executes custom `.refine()` checks.
105
+ 4. **Transforms**: Applies `.transform()` functions to modify the output.
106
+
107
+ ### The Facade: `z`
108
+
109
+ The `z` export acts as a singleton factory. It provides convenient accessors to instantiate specific `SchemifyType` subclasses (`SchemifyString`, `SchemifyObject`, etc.), mimicking the API surface of popular validation libraries for ease of adoption.
110
+
111
+ ### Error Handling
112
+
113
+ Errors are aggregated into a `SchemifyError` class, which contains an array of `SchemifyIssue` objects. Each issue pinpoints the `path` (e.g., `user.address.street`) and the `code` of the error, allowing for precise UI feedback (like form validation errors).
114
+
115
+ ## 🧪 How to Test
116
+
117
+ You can test the library using `bun test` or by running a simple script to verify behavior.
118
+
119
+ **Example Test Script (`test.ts`):**
120
+
121
+ ```typescript
122
+ import { z } from "./src/index";
123
+
124
+ const schema = z.object({
125
+ name: z.string(),
126
+ age: z.number().min(0),
127
+ });
128
+
129
+ try {
130
+ const data = schema.parse({ name: "Bob", age: 30 });
131
+ console.log("✅ Validation passed:", data);
132
+ } catch (e) {
133
+ console.error("❌ Validation failed:", e);
134
+ }
135
+
136
+ try {
137
+ schema.parse({ name: "Bob", age: -5 });
138
+ } catch (e) {
139
+ console.log("✅ Expected failure caught:", e.message);
140
+ }
141
+ ```
142
+
143
+ Run it with:
144
+
145
+ ```bash
146
+ wsl bun run test.ts
147
+ ```
148
+
149
+ ---
150
+
151
+ _Created by Antigravity for the @purecore open-source initiative._
152
+
153
+ [View Changelog](./CHANGELOG.md)
package/TODO.md ADDED
@@ -0,0 +1,238 @@
1
+
2
+ **Contexto**
3
+ Você é um agente de codificação encarregado de **inferir tipos semânticos atômicos** a partir de código TypeScript existente que hoje usa apenas **primitivos** (`boolean`, `number`, `string`, `Date`). Não assuma bibliotecas externas instaladas. Seu objetivo é:
4
+
5
+ 1. detectar candidatos a **tipos semânticos**;
6
+ 2. propor **nomes canônicos** no padrão `dominio.entidade.nome` (com ponto);
7
+ 3. sugerir **regras e validações** mínimas;
8
+ 4. gerar **artefatos** auto-contidos (sem dependências externas);
9
+ 5. listar **até 5 novos tipos** que ainda **não existem** no repositório e que valem a pena padronizar.
10
+ 5. listar **até 5 novos tipos** que ainda **não existem** no repositório e que valem a pena padronizar, que sejam os mais específicos daquele domínio e/ou entidade.
11
+
12
+ > Use linguagem e comentários em **português**.
13
+
14
+ ---
15
+
16
+ ## Entradas que você tem
17
+
18
+ * Árvore de arquivos `.ts/.tsx` e testes.
19
+ * Nomes de variáveis, propriedades, funções, schemas, migrações, seeds.
20
+ * Strings literais, sufixos/prefixos, nomes de colunas/IDs e chamadas de API.
21
+
22
+ ---
23
+
24
+ ## Saídas obrigatórias (em ORDEM)
25
+
26
+ 1. **Relatório** em Markdown: achados, suposições e conflitos.
27
+ 2. **Manifesto** `semantic-types.manifest.json` com as inferências.
28
+ 3. **Stubs** de tipos canônicos (arquivos `.ts` auto-contidos).
29
+ 4. **Patches** (diff unificado) mostrando como aplicar os tipos.
30
+ 5. **Roadmap** com até **5** tipos novos recomendados (ainda não mapeados).
31
+
32
+ ---
33
+
34
+ ## Padrão de Nomes (canônicos)
35
+
36
+ Use `kebab` no arquivo e **ponto** no AtomicType:
37
+
38
+ * Arquivo: `domains/{Domain}/{Entity}/{name}.ts`
39
+ * AtomicType interno: `{domain}.{entity}.{name}`
40
+ Ex.: `domains/ecommerce/order/total.ts` → AtomicType `ecommerce.order.total`.
41
+
42
+ ---
43
+
44
+ ## Heurísticas de Inferência
45
+
46
+ ### A) Booleans
47
+
48
+ Detecção por **prefixos** e contexto de uso:
49
+
50
+ * `is*`, `has*`, `can*`, `should*`, `enable*`, `allow*`, `show*`
51
+ * Exemplos canônicos:
52
+
53
+ * `isDone` → `project.task.isDone`
54
+ * `hasAllergies` → `health.patient.hasAllergies`
55
+ * `showWeekViewCalendar` → `ui.calendar.showWeekView`
56
+ Valide: coação para boolean, proibir `null/undefined` (ou explicitar `Maybe`), e **combinadores** só via funções (`and`, `or`) — nunca `&&` direto entre AtomicTypes.
57
+
58
+ ### B) Numbers
59
+
60
+ Use nomes, unidades, e padrões:
61
+
62
+ * Sufixos/palavras-chave: `total`, `amount`, `price`, `quantity`, `count`, `size`, `capacity`, `rate`, `percentage`, `score`, `weight`, `length`, `height`, `width`, `radius`, `duration`.
63
+ * Padrões literais: `%`, `ms`, `s`, `min`, `h`, `kg`, `g`, `m`, `cm`, `km`, `px`, `rem`, `brl`, `usd`.
64
+ * Exemplos:
65
+
66
+ * `totalAppointments` → `clinic.schedule.totalAppointments` (inteiro ≥ 0)
67
+ * `revenueTotal` → `ecommerce.order.revenueTotal.brl` (moeda BRL)
68
+ * `teethExtracted` → `dentistry.procedure.teethExtracted` (inteiro 0–32)
69
+ * `percentage` → `metrics.kpi.percentage` (0–100 ou 0–1 — explicitar escala)
70
+ * `durationMs` → `time.duration.ms`
71
+ Valide: **faixas** (min/max), **inteireza** quando nome implicar contagem, e **unidade** no nome canônico (ex.: `.brl`, `.usd`, `.kg`, `.ms`).
72
+
73
+ ### C) Strings
74
+
75
+ Detecte **formatos**:
76
+
77
+ * `email`, `phone`, `url`, `slug`, `isoCode`, `cpf/cnpj`, `uuid`, `id` (se houver padrão).
78
+ * Regex úteis (documente no stub):
79
+
80
+ * Email (robusto suficiente): `/^[^\s@]+@[^\s@]+\.[^\s@]+$/`
81
+ * URL: usar `new URL()` no validador (cair no catch se inválida)
82
+ * UUID v4: `/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i`
83
+ * Exemplos:
84
+
85
+ * `customerEmail` → `crm.customer.email`
86
+ * `orderId` (uuid) → `ecommerce.order.id`
87
+ * `countryIso2` → `geo.country.iso2`
88
+
89
+ ### D) Date/Tempo
90
+
91
+ Mapeie granularidade: `createdAt`, `updatedAt`, `startAt`, `endAt`, `birthday`, `scheduledFor`, `dueAt`.
92
+
93
+ * Exemplos:
94
+
95
+ * `scheduledFor` → `calendar.event.scheduledAt` (Date)
96
+ * `birthday` → `identity.person.birthDate` (Date sem hora — normalize)
97
+
98
+ ---
99
+
100
+ ## Sem biblioteca externa? Forneça **Shim** local (TS puro)
101
+
102
+ Crie **um** utilitário interno para os Atomic Behavior Types de Semânticos **sem runtime**:
103
+
104
+ ```ts
105
+ // src/AtomicBehaviorTypes/forge.ts
106
+ declare const __AtomicType: unique symbol;
107
+
108
+ export type AtomicType<T, Name extends string> = T & { readonly [__AtomicType]: Name };
109
+
110
+ export function BehaviorType<Name extends string>() {
111
+ return {
112
+ of: <T>(v: T) => v as AtomicType<T, Name>,
113
+ un: <T>(v: AtomicType<T, Name>) => v as unknown as T,
114
+ };
115
+ }
116
+ ```
117
+
118
+
119
+ ## Facilitando a Instanciação com Funções 'forge'
120
+
121
+ Para tornar a criação de tipos semânticos mais intuitiva e menos verbosa, recomenda-se adicionar funções auxiliares simples, conhecidas como "forgers" ou "factories", diretamente nos módulos de cada tipo. Essas funções permitem instanciações rápidas, como `forgeEmail('sussu@gmail.com')`, evitando a necessidade de chamar métodos mais complexos como `Email.of(...)` em contextos cotidianos.
122
+
123
+
124
+ ### Por que Usar Funções 'forge'?
125
+
126
+ - **Simplicidade:** Reduz a curva de aprendizado e o boilerplate, especialmente em código de produção onde a validação já é implícita.
127
+ - **Legibilidade:** Torna o código mais declarativo, focando no valor em vez da construção do tipo.
128
+ - **Compatibilidade:** Pode coexistir com as funções existentes (como `of` e `un`), servindo como atalhos para casos comuns.
129
+ - **Aplicação Gradual:** Facilita a migração de primitivos para tipos semânticos sem quebrar o fluxo de desenvolvimento.
130
+
131
+
132
+ ### Recomendações
133
+
134
+ - **Padronização:** Sempre inclua `forge` em novos tipos para promover adoção rápida.
135
+ - **Validação:** A função `forge` deve herdar as validações de `of`, garantindo consistência.
136
+ - **Documentação:** No README de cada domínio, exemplifique usos com `forge` para onboarding.
137
+ - **Evolução:** Se necessário, expanda `forge` com overloads para aceitar múltiplos formatos (ex.: `forge` para datas aceitando string ou timeBehaviorType).
138
+
139
+ Essa abordagem torna os tipos semânticos mais acessíveis, acelerando a migração e reduzindo erros em projetos reais.
140
+
141
+
142
+ ### Como Implementar
143
+
144
+ Adicione uma função `forge` em cada stub de tipo, que internamente chame a função `of` existente. Isso mantém a validação e o AtomicTypeing, mas oferece uma interface mais amigável.
145
+
146
+ #### Exemplo para Boolean (ex.: `project.task.isDone`)
147
+
148
+ ```ts
149
+ // domains/project/task/is-done.ts
150
+ import { AtomicType, BehaviorType } from "../../src/AtomicBehaviorTypes/forge";
151
+ export type IsDone = AtomicType<boolean, "project.task.isDone">;
152
+
153
+ export const IsDone = (() => {
154
+ const f = BehaviorType<"project.task.isDone">();
155
+ return {
156
+ of: (v: unknown): IsDone => f.of(Boolean(v)),
157
+ un: (v: IsDone): boolean => f.un(v),
158
+ and: (a: IsDone, b: IsDone): IsDone => f.of(f.un(a) && f.un(b)),
159
+ forge: (value: boolean): IsDone => f.of(value),
160
+ };
161
+ })();
162
+ ```
163
+ Uso: `const isCompleted = IsDone.forge(true);` (equivalente a `IsDone.of(true)`).
164
+
165
+ ### Number com moeda (ex.: `ecommerce.order.revenueTotal.brl`)
166
+
167
+ ```ts
168
+ // domains/ecommerce/order/revenue-total.brl.ts
169
+ import { AtomicType, BehaviorType } from "../../src/AtomicBehaviorTypes/forge";
170
+ export type RevenueTotalBRL = AtomicType<number, "ecommerce.order.revenueTotal.brl">;
171
+
172
+ export const RevenueTotalBRL = (() => {
173
+ const f = BehaviorType<"ecommerce.order.revenueTotal.brl">();
174
+ return {
175
+ of: (v: unknown): RevenueTotalBRL => {
176
+ const n = Number(v);
177
+ if (!Number.isFinite(n) || n < 0) throw new TypeError("revenue must be >= 0");
178
+ return f.of(n);
179
+ },
180
+ un: (v: RevenueTotalBRL) => f.un(v),
181
+ add: (a: RevenueTotalBRL, b: RevenueTotalBRL): RevenueTotalBRL => f.of(f.un(a) + f.un(b)),
182
+ forge: (value: number): RevenueTotalBRL => f.of(value), // Nova função forge
183
+ };
184
+ })();
185
+ ```
186
+
187
+ ### String formatada (ex.: `crm.customer.email`)
188
+
189
+ ```ts
190
+ // domains/universal/user/email.ts
191
+ import { AtomicType, BehaviorType } from "../../src/AtomicBehaviorTypes/forge";
192
+ export type Email = AtomicType<string, "crm.customer.email">;
193
+
194
+ export const Email = (() => {
195
+ const f = BehaviorType<"crm.customer.email">();
196
+ const emailRx = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
197
+ return {
198
+ of: (v: unknown): Email => {
199
+ const s = String(v).trim();
200
+ if (!emailRx.test(s)) throw new TypeError("invalid email");
201
+ return f.of(s);
202
+ },
203
+ un: (v: Email) => f.un(v),
204
+ forge: (value: string): Email => f.of(value),
205
+ };
206
+ })();
207
+ ```
208
+
209
+ ### Date (ex.: `calendar.event.scheduledAt`)
210
+
211
+ ```ts
212
+ // domains/calendar/event/scheduled-at.ts
213
+ import { AtomicType, BehaviorType } from "../../src/AtomicBehaviorTypes/forge";
214
+ export type ScheduledAt = AtomicType<Date, "calendar.event.scheduledAt">;
215
+
216
+ export const ScheduledAt = (() => {
217
+ const f = BehaviorType<"calendar.event.scheduledAt">();
218
+ return {
219
+ of: (v: unknown): ScheduledAt => {
220
+ const d = v instanceof Date ? v : new Date(String(v));
221
+ if (Number.isNaN(d.getTime())) throw new TypeError("invalid date");
222
+ return f.of(d);
223
+ },
224
+ un: (v: ScheduledAt) => f.un(v),
225
+ forge: (value: string | Date): ScheduledAt => f.of(value),
226
+ };
227
+ })();
228
+ ```
229
+
230
+
231
+
232
+ ## Regras de qualidade que o agente deve aplicar
233
+
234
+ * **Nunca** substituir primitivo por tipo semântico sem **stub** + **import**.
235
+ * Apontar ambiguidade (`rate`, `size`, `code`) e sugerir **AtomicType** com **unidade ou domínio** explícitos.
236
+ * Sugerir **faixas**: contagens (≥ 0), porcentagens (0–1 ou 0–100 — escolha e documente).
237
+ * Não criar tipos transdomínio: `ecommerce.*` não reage com `crm.*` (a menos que o projeto já defina).
238
+ * Se detectar `any` em locais críticos, abrir **nota** para migração gradual com marcas semânticas.
package/dist/healer.js ADDED
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ /**
3
+ * Core Healer Library
4
+ * Uma biblioteca para validação e "cura" automática de tipos de dados.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.healAndValidate = exports.Strategy = exports.Reflector = exports.Pharmacy = void 0;
8
+ // --- 1. Pharmacy: Encapsula o conhecimento de conversão de tipos ---
9
+ // Responsável por saber como transformar um tipo "A" num tipo "B".
10
+ exports.Pharmacy = {
11
+ /** Dicionário de "vacinas" (conversões) disponíveis */
12
+ vaccines: {
13
+ string: (value) => ({
14
+ to: {
15
+ number: !isNaN(parseFloat(value)) && isFinite(value)
16
+ ? Number(value)
17
+ : undefined,
18
+ boolean: value.toLowerCase() === "true"
19
+ ? true
20
+ : value.toLowerCase() === "false"
21
+ ? false
22
+ : undefined,
23
+ },
24
+ }),
25
+ number: (value) => ({
26
+ to: {
27
+ string: String(value),
28
+ },
29
+ }),
30
+ boolean: (value) => ({
31
+ to: {
32
+ number: value ? 1 : 0,
33
+ string: String(value),
34
+ },
35
+ }),
36
+ },
37
+ /**
38
+ * Tenta administrar uma conversão de tipo segura.
39
+ * @returns O valor convertido ou undefined se não houver conversão disponível.
40
+ */
41
+ administer: (value, fromType, toType) => {
42
+ try {
43
+ return exports.Pharmacy.vaccines[fromType]?.(value).to[toType];
44
+ }
45
+ catch {
46
+ return undefined;
47
+ }
48
+ },
49
+ };
50
+ // --- 2. Reflector: Lida com a introspecção de tipos ---
51
+ exports.Reflector = {
52
+ /** Obtém a estrutura actual do payload com os seus tipos */
53
+ getStructure: (obj) => Object.entries(obj).map(([key, value]) => [key, typeof value]),
54
+ /** Obtém o tipo alvo definido no esquema para uma determinada chave */
55
+ getTargetType: (schema, key) => {
56
+ const target = schema[key];
57
+ // Se o esquema define um valor padrão em vez de uma string de tipo
58
+ return typeof target === "string" &&
59
+ ["string", "number", "boolean"].includes(target)
60
+ ? target
61
+ : typeof target;
62
+ },
63
+ };
64
+ // --- 3. Strategy: Gere as transições de estado e recursão ---
65
+ exports.Strategy = {
66
+ /** Verifica se o limite de tentativas de cura foi atingido */
67
+ isLimitReached: (rounds, limit) => rounds >= limit,
68
+ /** Prepara o próximo contexto com o valor "curado" */
69
+ prepareNextAttempt: (ctx, key, healedValue) => ({
70
+ ...ctx,
71
+ payload: { ...ctx.payload, [key]: healedValue },
72
+ rounds: (ctx.rounds || 0) + 1,
73
+ }),
74
+ };
75
+ // --- 4. Logic: Redutor de Validação e Processamento ---
76
+ const processField = (ctx) => (failures, [key, currentType]) => {
77
+ const targetType = exports.Reflector.getTargetType(ctx.schema, key);
78
+ // Caso de Sucesso: Os tipos coincidem
79
+ if (currentType === targetType)
80
+ return failures;
81
+ // Caso de Cura: Tenta converter o valor
82
+ const healedValue = exports.Pharmacy.administer(ctx.payload[key], currentType, targetType);
83
+ // Verifica se a cura foi bem-sucedida E se o novo payload passa na validação (recursão)
84
+ const isFixed = healedValue !== undefined &&
85
+ (0, exports.healAndValidate)({
86
+ ...exports.Strategy.prepareNextAttempt(ctx, key, healedValue),
87
+ });
88
+ if (!isFixed) {
89
+ failures.push([key, currentType]);
90
+ }
91
+ else {
92
+ // Aplica a cura no payload do contexto actual (mutação controlada para o resultado final)
93
+ ctx.payload[key] = healedValue;
94
+ }
95
+ return failures;
96
+ };
97
+ // --- 5. Ponto de Entrada Principal ---
98
+ /**
99
+ * Tenta validar e curar um payload baseando-se num esquema.
100
+ * @param ctx Contexto contendo o payload e o esquema alvo.
101
+ * @returns Verdadeiro se o payload estiver válido (ou foi curado com sucesso).
102
+ */
103
+ const healAndValidate = (ctx) => {
104
+ const { rounds = 0, LIMIT = 3, payload } = ctx;
105
+ if (exports.Strategy.isLimitReached(rounds, LIMIT))
106
+ return false;
107
+ const failures = exports.Reflector.getStructure(payload).reduce(processField(ctx), []);
108
+ return failures.length === 0;
109
+ };
110
+ exports.healAndValidate = healAndValidate;