@codexsploitx/schemaapi 1.0.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/.prettierignore +5 -0
- package/.prettierrc +7 -0
- package/bin/schemaapi +302 -0
- package/build.md +246 -0
- package/dist/core/contract.d.ts +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/schemaapi.cjs.js +13 -0
- package/dist/schemaapi.cjs.js.map +1 -0
- package/dist/schemaapi.esm.js +11 -0
- package/dist/schemaapi.esm.js.map +1 -0
- package/dist/schemaapi.umd.js +19 -0
- package/dist/schemaapi.umd.js.map +1 -0
- package/docs/adapters/deno.md +51 -0
- package/docs/adapters/express.md +67 -0
- package/docs/adapters/fastify.md +64 -0
- package/docs/adapters/hapi.md +67 -0
- package/docs/adapters/koa.md +61 -0
- package/docs/adapters/nest.md +66 -0
- package/docs/adapters/next.md +66 -0
- package/docs/adapters/remix.md +72 -0
- package/docs/cli.md +18 -0
- package/docs/consepts.md +18 -0
- package/docs/getting_started.md +149 -0
- package/docs/sdk.md +25 -0
- package/docs/validation.md +228 -0
- package/docs/versioning.md +28 -0
- package/eslint.config.mjs +34 -0
- package/estructure.md +55 -0
- package/libreria.md +319 -0
- package/package.json +61 -0
- package/readme.md +89 -0
- package/resumen.md +188 -0
- package/rollup.config.js +19 -0
- package/src/adapters/deno.ts +139 -0
- package/src/adapters/express.ts +134 -0
- package/src/adapters/fastify.ts +133 -0
- package/src/adapters/hapi.ts +140 -0
- package/src/adapters/index.ts +9 -0
- package/src/adapters/koa.ts +128 -0
- package/src/adapters/nest.ts +122 -0
- package/src/adapters/next.ts +175 -0
- package/src/adapters/remix.ts +145 -0
- package/src/adapters/ws.ts +132 -0
- package/src/core/client.ts +104 -0
- package/src/core/contract.ts +534 -0
- package/src/core/versioning.test.ts +174 -0
- package/src/docs.ts +535 -0
- package/src/index.ts +5 -0
- package/src/playground.test.ts +98 -0
- package/src/playground.ts +13 -0
- package/src/sdk.ts +17 -0
- package/tests/adapters.deno.test.ts +70 -0
- package/tests/adapters.express.test.ts +67 -0
- package/tests/adapters.fastify.test.ts +63 -0
- package/tests/adapters.hapi.test.ts +66 -0
- package/tests/adapters.koa.test.ts +58 -0
- package/tests/adapters.nest.test.ts +85 -0
- package/tests/adapters.next.test.ts +39 -0
- package/tests/adapters.remix.test.ts +52 -0
- package/tests/adapters.ws.test.ts +91 -0
- package/tests/cli.test.ts +156 -0
- package/tests/client.test.ts +110 -0
- package/tests/contract.handle.test.ts +267 -0
- package/tests/docs.test.ts +96 -0
- package/tests/sdk.test.ts +34 -0
- package/tsconfig.json +15 -0
package/.prettierignore
ADDED
package/.prettierrc
ADDED
package/bin/schemaapi
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
let schemaapi = {};
|
|
5
|
+
try {
|
|
6
|
+
// When installed as a package, this resolves to the SchemaApi entrypoint.
|
|
7
|
+
// During local development, it points to the built dist file.
|
|
8
|
+
// If it fails, we fall back to an internal minimal HTML renderer.
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
10
|
+
schemaapi = require("..");
|
|
11
|
+
} catch {
|
|
12
|
+
schemaapi = {};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
|
|
17
|
+
function showHelp() {
|
|
18
|
+
console.log("SchemaApi CLI");
|
|
19
|
+
console.log("");
|
|
20
|
+
console.log("Usage:");
|
|
21
|
+
console.log(" schemaapi generate docs");
|
|
22
|
+
console.log(" schemaapi generate sdk");
|
|
23
|
+
console.log(" schemaapi generate tests");
|
|
24
|
+
console.log(" schemaapi audit");
|
|
25
|
+
console.log(" schemaapi init <adapter>");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function loadConfig() {
|
|
29
|
+
const cwd = process.cwd();
|
|
30
|
+
const configPath = path.join(cwd, "schemaapi.config.json");
|
|
31
|
+
if (!fs.existsSync(configPath)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const raw = fs.readFileSync(configPath, "utf8");
|
|
36
|
+
return JSON.parse(raw);
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function loadContractsModule(contractsDir) {
|
|
43
|
+
const cwd = process.cwd();
|
|
44
|
+
const baseDir = path.join(cwd, contractsDir || "contracts");
|
|
45
|
+
const candidates = ["index.js", "index.cjs"];
|
|
46
|
+
|
|
47
|
+
for (const file of candidates) {
|
|
48
|
+
const fullPath = path.join(baseDir, file);
|
|
49
|
+
if (fs.existsSync(fullPath)) {
|
|
50
|
+
try {
|
|
51
|
+
return require(fullPath);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error(
|
|
54
|
+
`Failed to load contracts module at ${fullPath}:`,
|
|
55
|
+
(error && error.message) || error
|
|
56
|
+
);
|
|
57
|
+
process.exitCode = 1;
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
console.error(
|
|
64
|
+
`Could not find contracts entry in ${baseDir}. Expected one of: ${candidates.join(
|
|
65
|
+
", "
|
|
66
|
+
)}`
|
|
67
|
+
);
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function renderDocsHtmlFallback(docs) {
|
|
73
|
+
const items = Array.isArray(docs.routes)
|
|
74
|
+
? docs.routes
|
|
75
|
+
.map((route) => {
|
|
76
|
+
const method = route && route.method ? String(route.method) : "";
|
|
77
|
+
const pathValue = route && route.path ? String(route.path) : "";
|
|
78
|
+
return `<li><code>${method} ${pathValue}</code></li>`;
|
|
79
|
+
})
|
|
80
|
+
.join("\n")
|
|
81
|
+
: "";
|
|
82
|
+
|
|
83
|
+
return `<!doctype html>
|
|
84
|
+
<html lang="en">
|
|
85
|
+
<head>
|
|
86
|
+
<meta charset="utf-8" />
|
|
87
|
+
<title>SchemaApi Docs</title>
|
|
88
|
+
</head>
|
|
89
|
+
<body>
|
|
90
|
+
<h1>SchemaApi Docs</h1>
|
|
91
|
+
<ul>
|
|
92
|
+
${items}
|
|
93
|
+
</ul>
|
|
94
|
+
</body>
|
|
95
|
+
</html>`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function generateDocsFromContracts(config) {
|
|
99
|
+
const adapter = config && config.adapter ? String(config.adapter) : null;
|
|
100
|
+
const contractsDir =
|
|
101
|
+
config && config.contractsDir ? String(config.contractsDir) : "contracts";
|
|
102
|
+
|
|
103
|
+
console.log("Generating docs from contract");
|
|
104
|
+
if (adapter || contractsDir) {
|
|
105
|
+
console.log(
|
|
106
|
+
`Using config: adapter=${adapter || "unknown"}, contractsDir=${contractsDir}`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const contractsModule = loadContractsModule(contractsDir);
|
|
111
|
+
if (!contractsModule) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const candidates = Object.values(contractsModule).filter(
|
|
116
|
+
(value) =>
|
|
117
|
+
value &&
|
|
118
|
+
typeof value === "object" &&
|
|
119
|
+
typeof value.docs === "function"
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
if (candidates.length === 0) {
|
|
123
|
+
console.error(
|
|
124
|
+
"No contracts with a docs() method were found in the contracts entry file."
|
|
125
|
+
);
|
|
126
|
+
process.exitCode = 1;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const combinedDocs = {
|
|
131
|
+
routes: candidates.flatMap((contract) => {
|
|
132
|
+
try {
|
|
133
|
+
const docs = contract.docs();
|
|
134
|
+
return Array.isArray(docs.routes) ? docs.routes : [];
|
|
135
|
+
} catch {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
}),
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
if (!combinedDocs.routes.length) {
|
|
142
|
+
console.error(
|
|
143
|
+
"Contracts loaded, but no routes were found when calling docs()."
|
|
144
|
+
);
|
|
145
|
+
process.exitCode = 1;
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const outDir = path.join(process.cwd(), "schemaapi-docs");
|
|
150
|
+
if (!fs.existsSync(outDir)) {
|
|
151
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
152
|
+
}
|
|
153
|
+
const outPath = path.join(outDir, "index.html");
|
|
154
|
+
|
|
155
|
+
const hasRenderDocs =
|
|
156
|
+
schemaapi && typeof schemaapi.renderDocs === "function";
|
|
157
|
+
const html = hasRenderDocs
|
|
158
|
+
? schemaapi.renderDocs(combinedDocs, {
|
|
159
|
+
format: "html",
|
|
160
|
+
title: "SchemaApi Docs",
|
|
161
|
+
theme: "dark",
|
|
162
|
+
})
|
|
163
|
+
: renderDocsHtmlFallback(combinedDocs);
|
|
164
|
+
|
|
165
|
+
fs.writeFileSync(outPath, html, "utf8");
|
|
166
|
+
console.log(`Docs generated at: ${path.relative(process.cwd(), outPath)}`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function handleGenerate(target) {
|
|
170
|
+
const config = loadConfig();
|
|
171
|
+
const adapter = config && config.adapter ? String(config.adapter) : null;
|
|
172
|
+
const contractsDir =
|
|
173
|
+
config && config.contractsDir ? String(config.contractsDir) : null;
|
|
174
|
+
|
|
175
|
+
if (target === "docs") {
|
|
176
|
+
generateDocsFromContracts(config || {});
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (target === "sdk") {
|
|
180
|
+
console.log("Generating SDK from contract");
|
|
181
|
+
if (adapter || contractsDir) {
|
|
182
|
+
console.log(
|
|
183
|
+
`Using config: adapter=${adapter || "unknown"}, contractsDir=${contractsDir || "contracts"}`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (target === "tests") {
|
|
189
|
+
console.log("Generating tests from contract");
|
|
190
|
+
if (adapter || contractsDir) {
|
|
191
|
+
console.log(
|
|
192
|
+
`Using config: adapter=${adapter || "unknown"}, contractsDir=${contractsDir || "contracts"}`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
console.error(`Unknown generate target: ${target || ""}`);
|
|
198
|
+
process.exitCode = 1;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function handleAudit() {
|
|
202
|
+
console.log("Running SchemaApi audit");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function normalizeAdapter(value) {
|
|
206
|
+
if (!value) return "generic";
|
|
207
|
+
const v = String(value).toLowerCase();
|
|
208
|
+
if (v === "next" || v === "nextjs") return "next";
|
|
209
|
+
if (v === "remix") return "remix";
|
|
210
|
+
if (v === "express") return "express";
|
|
211
|
+
if (v === "fastify") return "fastify";
|
|
212
|
+
if (v === "nest" || v === "nestjs") return "nest";
|
|
213
|
+
if (v === "koa") return "koa";
|
|
214
|
+
if (v === "hapi") return "hapi";
|
|
215
|
+
if (v === "deno") return "deno";
|
|
216
|
+
return v;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function handleInit(adapterArg) {
|
|
220
|
+
const adapter = normalizeAdapter(adapterArg);
|
|
221
|
+
const cwd = process.cwd();
|
|
222
|
+
const contractsDir = path.join(cwd, "contracts");
|
|
223
|
+
|
|
224
|
+
if (!fs.existsSync(contractsDir)) {
|
|
225
|
+
fs.mkdirSync(contractsDir, { recursive: true });
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const usersContractPath = path.join(contractsDir, "usersContract.ts");
|
|
229
|
+
if (!fs.existsSync(usersContractPath)) {
|
|
230
|
+
const usersContractContent = [
|
|
231
|
+
'import { createContract } from "schemaapi";',
|
|
232
|
+
'import { z } from "zod";',
|
|
233
|
+
"",
|
|
234
|
+
"export const usersContract = createContract({",
|
|
235
|
+
' "/users/:id": {',
|
|
236
|
+
" GET: {",
|
|
237
|
+
" params: z.object({ id: z.string() }),",
|
|
238
|
+
" response: z.object({ id: z.string(), name: z.string() }),",
|
|
239
|
+
" },",
|
|
240
|
+
" },",
|
|
241
|
+
"});",
|
|
242
|
+
"",
|
|
243
|
+
].join("\n");
|
|
244
|
+
fs.writeFileSync(usersContractPath, usersContractContent);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const indexPath = path.join(contractsDir, "index.ts");
|
|
248
|
+
if (!fs.existsSync(indexPath)) {
|
|
249
|
+
fs.writeFileSync(indexPath, 'export * from "./usersContract";\n');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const configPath = path.join(cwd, "schemaapi.config.json");
|
|
253
|
+
let existingConfig = null;
|
|
254
|
+
if (fs.existsSync(configPath)) {
|
|
255
|
+
try {
|
|
256
|
+
const raw = fs.readFileSync(configPath, "utf8");
|
|
257
|
+
existingConfig = JSON.parse(raw);
|
|
258
|
+
} catch {
|
|
259
|
+
existingConfig = null;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const config = Object.assign({}, existingConfig || {}, {
|
|
264
|
+
adapter,
|
|
265
|
+
contractsDir: existingConfig && existingConfig.contractsDir ? existingConfig.contractsDir : "contracts",
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
269
|
+
|
|
270
|
+
console.log(`Initialized SchemaApi project for adapter: ${adapter}`);
|
|
271
|
+
console.log("SchemaApi init completed. Contracts directory: ./contracts");
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function main() {
|
|
275
|
+
const [command, subcommand] = args;
|
|
276
|
+
|
|
277
|
+
if (!command) {
|
|
278
|
+
showHelp();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
if (command === "generate") {
|
|
283
|
+
handleGenerate(subcommand);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (command === "audit") {
|
|
288
|
+
handleAudit();
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (command === "init") {
|
|
293
|
+
handleInit(subcommand);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
console.error(`Unknown command: ${command}`);
|
|
298
|
+
showHelp();
|
|
299
|
+
process.exitCode = 1;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
main();
|
package/build.md
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# Guía de build y publicación de SchemaApi
|
|
2
|
+
|
|
3
|
+
Este documento resume los pasos para:
|
|
4
|
+
|
|
5
|
+
- Preparar el paquete para producción
|
|
6
|
+
- Generar el build
|
|
7
|
+
- Verificar que todo está OK
|
|
8
|
+
- Publicar en registries tipo **npm** (y usarlo desde **npm**, **pnpm**, **yarn**, etc.)
|
|
9
|
+
|
|
10
|
+
> Nota: Los ejemplos asumen que estás en la raíz del repo:
|
|
11
|
+
> `/Users/admin/Desktop/DEV/SchemaApi`
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. Requisitos previos
|
|
16
|
+
|
|
17
|
+
- Node.js `>= 18` (ya está definido en `package.json` → `engines.node`).
|
|
18
|
+
- Tener una cuenta en el registry donde quieras publicar (por defecto `https://registry.npmjs.org/`).
|
|
19
|
+
- Tener configurado tu cliente:
|
|
20
|
+
- Para **npm**:
|
|
21
|
+
```bash
|
|
22
|
+
npm login
|
|
23
|
+
```
|
|
24
|
+
- Para **pnpm**:
|
|
25
|
+
```bash
|
|
26
|
+
pnpm login
|
|
27
|
+
```
|
|
28
|
+
- Para **yarn**:
|
|
29
|
+
```bash
|
|
30
|
+
yarn npm login
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Todos ellos acaban hablando con el mismo registry (npmjs) salvo que configures otro.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 2. Revisar `package.json`
|
|
38
|
+
|
|
39
|
+
Puntos importantes del `package.json` actual:
|
|
40
|
+
|
|
41
|
+
- `name`: pon aquí el nombre definitivo del paquete en npm.
|
|
42
|
+
- Ejemplo: `"name": "schemaapi"` o `"name": "@tu-org/schemaapi"`.
|
|
43
|
+
- Entradas de build ya configuradas:
|
|
44
|
+
- `"main": "dist/schemaapi.cjs.js"` (CommonJS)
|
|
45
|
+
- `"module": "dist/schemaapi.esm.js"` (ESM)
|
|
46
|
+
- `"browser": "dist/schemaapi.umd.js"` (bundle UMD)
|
|
47
|
+
- `"types": "dist/index.d.ts"` (tipos TypeScript)
|
|
48
|
+
- `"bin": { "schemaapi": "bin/schemaapi" }` (CLI global)
|
|
49
|
+
- Scripts:
|
|
50
|
+
- `"build": "rollup -c"`
|
|
51
|
+
- `"test": "vitest"`
|
|
52
|
+
- `"lint": "eslint ."`
|
|
53
|
+
|
|
54
|
+
Antes de publicar:
|
|
55
|
+
|
|
56
|
+
1. Asegúrate de que `name`, `version`, `description`, `repository`, `author` y `license` son los definitivos.
|
|
57
|
+
2. Si quieres controlar qué ficheros se publican, usa:
|
|
58
|
+
- Campo `"files"` en `package.json`, o
|
|
59
|
+
- Un `.npmignore`.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 3. Pasos de build y verificación
|
|
64
|
+
|
|
65
|
+
Desde la raíz del proyecto:
|
|
66
|
+
|
|
67
|
+
1. Instalar dependencias (si no están ya):
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
npm install
|
|
71
|
+
# o
|
|
72
|
+
pnpm install
|
|
73
|
+
# o
|
|
74
|
+
yarn install
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
2. Ejecutar los tests:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm test
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
3. Ejecutar el linter (opcional pero recomendado):
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npm run lint
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
4. Generar el build:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npm run build
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Esto generará:
|
|
96
|
+
|
|
97
|
+
- `dist/schemaapi.cjs.js`
|
|
98
|
+
- `dist/schemaapi.esm.js`
|
|
99
|
+
- `dist/schemaapi.umd.js`
|
|
100
|
+
- `dist/index.d.ts`
|
|
101
|
+
- `dist/core/...` (tipos intermedios)
|
|
102
|
+
|
|
103
|
+
5. Validar que el CLI funciona con el build:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
node bin/schemaapi
|
|
107
|
+
node bin/schemaapi generate docs
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
En un proyecto consumidor (fuera de este repo) podrás ejecutar:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npx schemaapi generate docs
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 4. Publicar en npm (registry por defecto)
|
|
119
|
+
|
|
120
|
+
### 4.1. Login
|
|
121
|
+
|
|
122
|
+
Si no lo has hecho ya:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
npm login
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Introduce usuario, contraseña y email de tu cuenta de npm.
|
|
129
|
+
|
|
130
|
+
### 4.2. Subir la versión
|
|
131
|
+
|
|
132
|
+
1. Decide el número de versión siguiendo **semver**:
|
|
133
|
+
- `1.0.0` → versión inicial estable
|
|
134
|
+
- `1.0.1` → bugfix
|
|
135
|
+
- `1.1.0` → nuevas features que no rompen compatibilidad
|
|
136
|
+
- `2.0.0` → cambios rompientes
|
|
137
|
+
|
|
138
|
+
2. Actualiza la versión en `package.json` (puedes usar comandos de npm):
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# Ejemplos:
|
|
142
|
+
npm version patch # 1.0.0 -> 1.0.1
|
|
143
|
+
npm version minor # 1.0.0 -> 1.1.0
|
|
144
|
+
npm version major # 1.0.0 -> 2.0.0
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Esto actualiza `package.json` y crea un tag git (si el repo está bajo git).
|
|
148
|
+
|
|
149
|
+
3. Publicar:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
npm publish
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
- Si usas un **scope** público (ej. `@tu-org/schemaapi`), asegúrate de tener `"publishConfig": { "access": "public" }` si es necesario.
|
|
156
|
+
- Si quieres una etiqueta distinta de `latest`, puedes usar:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
npm publish --tag next
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 5. Usar el paquete publicado con npm, pnpm, yarn
|
|
165
|
+
|
|
166
|
+
Una vez publicado en npm, podrás instalarlo desde cualquier cliente:
|
|
167
|
+
|
|
168
|
+
- Con **npm**:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npm install schemaapi
|
|
172
|
+
# o
|
|
173
|
+
npm install @tu-org/schemaapi
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- Con **pnpm**:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
pnpm add schemaapi
|
|
180
|
+
# o
|
|
181
|
+
pnpm add @tu-org/schemaapi
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
- Con **yarn**:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
yarn add schemaapi
|
|
188
|
+
# o
|
|
189
|
+
yarn add @tu-org/schemaapi
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
El contenido publicado (dist, bin, types) será el mismo; solo cambia el cliente que lo descarga.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## 6. Publicar en otros registries (GitHub Packages, privados, etc.)
|
|
197
|
+
|
|
198
|
+
El proceso es muy similar; cambia principalmente:
|
|
199
|
+
|
|
200
|
+
- El `registry` que configuramos.
|
|
201
|
+
- El nombre del paquete (por ejemplo, GitHub Packages suele usar `@OWNER/NAME`).
|
|
202
|
+
|
|
203
|
+
Ejemplo básico para **GitHub Packages**:
|
|
204
|
+
|
|
205
|
+
1. En tu `~/.npmrc` o `.npmrc` del proyecto:
|
|
206
|
+
|
|
207
|
+
```ini
|
|
208
|
+
@tu-org:registry=https://npm.pkg.github.com
|
|
209
|
+
//npm.pkg.github.com/:_authToken=GITHUB_PERSONAL_ACCESS_TOKEN
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
2. En `package.json`:
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"name": "@tu-org/schemaapi",
|
|
217
|
+
"publishConfig": {
|
|
218
|
+
"registry": "https://npm.pkg.github.com"
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
3. Mismo flujo:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
npm run build
|
|
227
|
+
npm publish
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## 7. Checklist rápido antes de publicar
|
|
233
|
+
|
|
234
|
+
1. `npm test` pasa.
|
|
235
|
+
2. `npm run build` genera los artefactos en `dist/`.
|
|
236
|
+
3. `node bin/schemaapi` muestra la ayuda sin errores.
|
|
237
|
+
4. `node bin/schemaapi generate docs` funciona en un proyecto de prueba.
|
|
238
|
+
5. `package.json` tiene:
|
|
239
|
+
- `name` correcto (sin conflictos en el registry).
|
|
240
|
+
- `version` según semver.
|
|
241
|
+
- `main`, `module`, `browser`, `types`, `bin` apuntando a `dist/` y `bin/`.
|
|
242
|
+
- Datos de `repository`, `author`, `license` rellenados.
|
|
243
|
+
6. Has hecho `npm login` (o el login equivalente si usas otro registry).
|
|
244
|
+
|
|
245
|
+
Con esto deberías tener todo listo para subir la librería a npm y consumirla desde npm/pnpm/yarn como cualquier paquete profesional.
|
|
246
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./core/contract";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaapi.cjs.js","sources":["../src/core/contract.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport function createContract(schema: any) {\n return {\n handle(endpoint: string, handler: Function) {\n return handler;\n },\n schema,\n };\n}\n"],"names":[],"mappings":";;AAEM,SAAU,cAAc,CAAC,MAAW,EAAA;IACxC,OAAO;QACL,MAAM,CAAC,QAAgB,EAAE,OAAiB,EAAA;AACxC,YAAA,OAAO,OAAO;QAChB,CAAC;QACD,MAAM;KACP;AACH;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaapi.esm.js","sources":["../src/core/contract.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport function createContract(schema: any) {\n return {\n handle(endpoint: string, handler: Function) {\n return handler;\n },\n schema,\n };\n}\n"],"names":[],"mappings":"AAEM,SAAU,cAAc,CAAC,MAAW,EAAA;IACxC,OAAO;QACL,MAAM,CAAC,QAAgB,EAAE,OAAiB,EAAA;AACxC,YAAA,OAAO,OAAO;QAChB,CAAC;QACD,MAAM;KACP;AACH;;;;"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
(function (global, factory) {
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.SchemaApi = {}));
|
|
5
|
+
})(this, (function (exports) { 'use strict';
|
|
6
|
+
|
|
7
|
+
function createContract(schema) {
|
|
8
|
+
return {
|
|
9
|
+
handle(endpoint, handler) {
|
|
10
|
+
return handler;
|
|
11
|
+
},
|
|
12
|
+
schema,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
exports.createContract = createContract;
|
|
17
|
+
|
|
18
|
+
}));
|
|
19
|
+
//# sourceMappingURL=schemaapi.umd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaapi.umd.js","sources":["../src/core/contract.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport function createContract(schema: any) {\n return {\n handle(endpoint: string, handler: Function) {\n return handler;\n },\n schema,\n };\n}\n"],"names":[],"mappings":";;;;;;IAEM,SAAU,cAAc,CAAC,MAAW,EAAA;QACxC,OAAO;YACL,MAAM,CAAC,QAAgB,EAAE,OAAiB,EAAA;IACxC,YAAA,OAAO,OAAO;YAChB,CAAC;YACD,MAAM;SACP;IACH;;;;;;;;"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Deno Adapter
|
|
2
|
+
|
|
3
|
+
Adaptador para usar `SchemaApi` en Deno (usando `Deno.serve`).
|
|
4
|
+
|
|
5
|
+
## Uso
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { createContract, adapters } from "npm:schemaapi"; // Asumiendo importación desde npm o path relativo
|
|
9
|
+
|
|
10
|
+
const contract = createContract({
|
|
11
|
+
"/api/data": {
|
|
12
|
+
GET: {
|
|
13
|
+
// ...
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const handler = adapters.deno.handleContract(contract, {
|
|
19
|
+
"GET /api/data": async (ctx) => {
|
|
20
|
+
return { data: "Hello form Deno" };
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
Deno.serve(handler);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Estructura recomendada de contratos
|
|
28
|
+
|
|
29
|
+
Cuando usas Deno (o entornos similares como Workers, Bun, etc.) es habitual compartir contratos entre varias funciones/lambdas y entre servidor y cliente. Es **muy recomendable** centralizar los contratos en una carpeta o paquete dedicado:
|
|
30
|
+
|
|
31
|
+
```txt
|
|
32
|
+
contracts/
|
|
33
|
+
├─ usersContract.ts
|
|
34
|
+
├─ clientsContract.ts
|
|
35
|
+
├─ productsContract.ts
|
|
36
|
+
├─ facturasContract.ts
|
|
37
|
+
├─ ofertasContract.ts
|
|
38
|
+
└─ index.ts
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Cada handler puede importar solo el contrato que necesita:
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
import { adapters } from "npm:schemaapi";
|
|
45
|
+
import { usersContract } from "./contracts/usersContract.ts";
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Características
|
|
49
|
+
|
|
50
|
+
- Usa la API estándar de Web (`Request`, `Response`).
|
|
51
|
+
- Compatible con cualquier entorno que use `(req: Request) => Response`, incluyendo Bun, Cloudflare Workers, etc.
|