tagliatelle 1.0.0-beta.1
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/README.md +488 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +609 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/jsx-runtime.d.ts +31 -0
- package/dist/jsx-runtime.d.ts.map +1 -0
- package/dist/jsx-runtime.js +46 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/router.d.ts +59 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +487 -0
- package/dist/router.js.map +1 -0
- package/dist/security.d.ts +84 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +233 -0
- package/dist/security.js.map +1 -0
- package/dist/tagliatelle.d.ts +90 -0
- package/dist/tagliatelle.d.ts.map +1 -0
- package/dist/tagliatelle.js +684 -0
- package/dist/tagliatelle.js.map +1 -0
- package/dist/types.d.ts +144 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +32 -0
- package/dist/types.js.map +1 -0
- package/package.json +79 -0
package/README.md
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
# 🍝 `<Tag>liatelle.js`
|
|
2
|
+
|
|
3
|
+
> **The Declarative Backend Framework.** Build APIs with JSX. Yes, really.
|
|
4
|
+
|
|
5
|
+
`<Tag>liatelle.js` is a **TypeScript** backend framework built on top of **Fastify** that treats your API architecture like a component tree. Using JSX/TSX, you define your routes, middleware, and responses as a visual hierarchy.
|
|
6
|
+
|
|
7
|
+
**If you can write React, you can build a high-performance backend.**
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
import { render, Server, Logger, Cors, Routes } from 'tagliatelle';
|
|
11
|
+
|
|
12
|
+
const App = () => (
|
|
13
|
+
<Server port={3000}>
|
|
14
|
+
<Logger level="info" />
|
|
15
|
+
<Cors origin="*">
|
|
16
|
+
<Routes dir="./routes" />
|
|
17
|
+
</Cors>
|
|
18
|
+
</Server>
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
render(<App />);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 🤔 The Origin Story
|
|
27
|
+
|
|
28
|
+
This project started as a **joke**.
|
|
29
|
+
|
|
30
|
+
I noticed that every frontend framework is racing to become more server-oriented. React added Server Components. Next.js gave us `"use server"`. Remix is basically a backend framework wearing a React costume. The JavaScript ecosystem is slowly but surely... **becoming PHP**.
|
|
31
|
+
|
|
32
|
+
So I thought: *"If frontend devs want to write server code so badly, why not go all the way?"*
|
|
33
|
+
|
|
34
|
+
Instead of sneaking server code into your React components, let's do the **opposite** — write your entire backend in pure TSX. Routes? JSX. Middleware? JSX. Responses? You guessed it... JSX.
|
|
35
|
+
|
|
36
|
+
**Tagliatelle.js: Because if we're going to make everything look like PHP anyway, we might as well make it delicious.** 🍝
|
|
37
|
+
|
|
38
|
+
### Why "Tagliatelle"?
|
|
39
|
+
|
|
40
|
+
- **`<Tag>`** — Because we write everything in JSX tags. `<Server>`, `<Route>`, `<Response>`... it's tags all the way down.
|
|
41
|
+
- **Tagliatelle** — It's pasta. Because frontend developers clearly want to write spaghetti code in the backend. 🍝
|
|
42
|
+
|
|
43
|
+
*At least this spaghetti is type-safe and al dente.*
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 🚀 Quick Start
|
|
48
|
+
|
|
49
|
+
### Create a new project
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
npx tagliatelle init my-api
|
|
53
|
+
cd my-api
|
|
54
|
+
npm install
|
|
55
|
+
npm run dev
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
That's it! Your API is running at `http://localhost:3000` 🍝
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
curl http://localhost:3000/health
|
|
62
|
+
# {"status":"Al Dente 🍝","timestamp":"..."}
|
|
63
|
+
|
|
64
|
+
curl http://localhost:3000/posts
|
|
65
|
+
# {"success":true,"count":2,"data":[...]}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 🤌 Why `<Tag>liatelle.js`?
|
|
71
|
+
|
|
72
|
+
| Feature | Description |
|
|
73
|
+
|---------|-------------|
|
|
74
|
+
| **File-Based Routing** | Next.js-style routing — your file structure IS your API |
|
|
75
|
+
| **JSX Responses** | Return `<Response><Status code={201} /><Body data={...} /></Response>` |
|
|
76
|
+
| **JSX Middleware** | Use `<Err>` and `<Augment>` for clean auth flows |
|
|
77
|
+
| **JSX Config** | Configure routes with `<Logger>`, `<Middleware>`, `<RateLimiter>` |
|
|
78
|
+
| **Full TypeScript** | End-to-end type safety with `HandlerProps<TParams, TBody, TQuery>` |
|
|
79
|
+
| **Zero Boilerplate** | Handlers return data or JSX — no `res.send()` needed |
|
|
80
|
+
| **CLI Scaffolding** | `npx tagliatelle init` creates a ready-to-run project |
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 📦 Installation
|
|
85
|
+
|
|
86
|
+
### New Project (Recommended)
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
npx tagliatelle init my-api
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Add to Existing Project
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npm install tagliatelle
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Then configure your `tsconfig.json`:
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"compilerOptions": {
|
|
103
|
+
"jsx": "react-jsx",
|
|
104
|
+
"jsxImportSource": "tagliatelle"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## 📂 Project Structure
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
my-api/
|
|
115
|
+
├── server.tsx # Server entry point
|
|
116
|
+
├── routes/ # File-based routing
|
|
117
|
+
│ ├── _config.tsx # Global route config
|
|
118
|
+
│ ├── index.tsx # GET /
|
|
119
|
+
│ ├── health.tsx # GET /health
|
|
120
|
+
│ └── posts/
|
|
121
|
+
│ ├── _config.tsx # Config for /posts/*
|
|
122
|
+
│ ├── _data.ts # Shared data (not a route)
|
|
123
|
+
│ ├── index.tsx # GET/POST /posts
|
|
124
|
+
│ └── [id].tsx # GET/PUT/DELETE /posts/:id
|
|
125
|
+
├── middleware/
|
|
126
|
+
│ └── auth.tsx # JSX middleware
|
|
127
|
+
├── tsconfig.json
|
|
128
|
+
└── package.json
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 🍽️ Server Configuration
|
|
134
|
+
|
|
135
|
+
### `server.tsx`
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
import path from 'node:path';
|
|
139
|
+
import { fileURLToPath } from 'node:url';
|
|
140
|
+
import { render, Server, Logger, Cors, Routes } from 'tagliatelle';
|
|
141
|
+
|
|
142
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
143
|
+
|
|
144
|
+
const App = () => (
|
|
145
|
+
<Server port={3000}>
|
|
146
|
+
<Logger level="info" />
|
|
147
|
+
<Cors origin="*">
|
|
148
|
+
<Routes dir={path.join(__dirname, 'routes')} />
|
|
149
|
+
</Cors>
|
|
150
|
+
</Server>
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
render(<App />);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Server Components
|
|
157
|
+
|
|
158
|
+
| Component | Description |
|
|
159
|
+
|-----------|-------------|
|
|
160
|
+
| `<Server port={3000}>` | Main server wrapper |
|
|
161
|
+
| `<Logger level="info" />` | Configure logging level |
|
|
162
|
+
| `<Cors origin="*">` | Enable CORS |
|
|
163
|
+
| `<Routes dir="./routes" />` | Load file-based routes |
|
|
164
|
+
| `<RateLimiter max={100} timeWindow="1 minute" />` | Rate limiting |
|
|
165
|
+
| `<Middleware use={fn} />` | Add global middleware |
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## 📁 File-Based Routing
|
|
170
|
+
|
|
171
|
+
Your file structure becomes your API:
|
|
172
|
+
|
|
173
|
+
| File | Route |
|
|
174
|
+
|------|-------|
|
|
175
|
+
| `routes/index.tsx` | `GET /` |
|
|
176
|
+
| `routes/health.tsx` | `GET /health` |
|
|
177
|
+
| `routes/posts/index.tsx` | `GET/POST /posts` |
|
|
178
|
+
| `routes/posts/[id].tsx` | `GET/PUT/DELETE /posts/:id` |
|
|
179
|
+
| `routes/users/[id]/posts.tsx` | `GET /users/:id/posts` |
|
|
180
|
+
|
|
181
|
+
### Route File Example
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
// routes/posts/[id].tsx
|
|
185
|
+
import { Response, Status, Body, Err } from 'tagliatelle';
|
|
186
|
+
import type { HandlerProps } from 'tagliatelle';
|
|
187
|
+
|
|
188
|
+
interface PostParams { id: string }
|
|
189
|
+
|
|
190
|
+
export async function GET({ params, log }: HandlerProps<PostParams>) {
|
|
191
|
+
log.info(`Fetching post ${params.id}`);
|
|
192
|
+
|
|
193
|
+
const post = await db.posts.find(params.id);
|
|
194
|
+
|
|
195
|
+
if (!post) {
|
|
196
|
+
return <Err code={404} message="Post not found" />;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<Response>
|
|
201
|
+
<Status code={200} />
|
|
202
|
+
<Body data={{ success: true, data: post }} />
|
|
203
|
+
</Response>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export async function DELETE({ params }: HandlerProps<PostParams>) {
|
|
208
|
+
await db.posts.delete(params.id);
|
|
209
|
+
|
|
210
|
+
return (
|
|
211
|
+
<Response>
|
|
212
|
+
<Status code={200} />
|
|
213
|
+
<Body data={{ success: true, message: "Deleted" }} />
|
|
214
|
+
</Response>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## 🎛️ Route Configuration
|
|
222
|
+
|
|
223
|
+
Create `_config.tsx` files to configure routes per directory:
|
|
224
|
+
|
|
225
|
+
### `routes/_config.tsx` (Global)
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
import { Logger } from 'tagliatelle';
|
|
229
|
+
|
|
230
|
+
export default () => (
|
|
231
|
+
<>
|
|
232
|
+
<Logger level="info" />
|
|
233
|
+
</>
|
|
234
|
+
);
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### `routes/posts/_config.tsx` (Posts-specific)
|
|
238
|
+
|
|
239
|
+
```tsx
|
|
240
|
+
import { Logger, Middleware, RateLimiter } from 'tagliatelle';
|
|
241
|
+
import type { HandlerProps } from 'tagliatelle';
|
|
242
|
+
import { authMiddleware } from '../middleware/auth.js';
|
|
243
|
+
|
|
244
|
+
// Only require auth for write operations
|
|
245
|
+
const writeAuthMiddleware = async (props: HandlerProps, request, reply) => {
|
|
246
|
+
if (['GET', 'HEAD', 'OPTIONS'].includes(request.method)) {
|
|
247
|
+
return; // Skip auth for reads
|
|
248
|
+
}
|
|
249
|
+
return authMiddleware(props, request, reply);
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
export default () => (
|
|
253
|
+
<>
|
|
254
|
+
<Logger level="debug" />
|
|
255
|
+
<RateLimiter max={100} timeWindow="1 minute" />
|
|
256
|
+
<Middleware use={writeAuthMiddleware} />
|
|
257
|
+
</>
|
|
258
|
+
);
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Config Inheritance
|
|
262
|
+
|
|
263
|
+
Configs are **inherited** and **merged**:
|
|
264
|
+
- Child configs override parent settings
|
|
265
|
+
- Middleware is **additive** (stacks)
|
|
266
|
+
- Nested directories inherit from parents
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## 📤 JSX Responses
|
|
271
|
+
|
|
272
|
+
Return beautiful, declarative responses:
|
|
273
|
+
|
|
274
|
+
```tsx
|
|
275
|
+
// Success response
|
|
276
|
+
return (
|
|
277
|
+
<Response>
|
|
278
|
+
<Status code={201} />
|
|
279
|
+
<Body data={{
|
|
280
|
+
success: true,
|
|
281
|
+
message: "Created!",
|
|
282
|
+
data: newItem
|
|
283
|
+
}} />
|
|
284
|
+
</Response>
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
// Error response (shorthand)
|
|
288
|
+
return <Err code={404} message="Not found" />;
|
|
289
|
+
|
|
290
|
+
// With custom headers
|
|
291
|
+
return (
|
|
292
|
+
<Response>
|
|
293
|
+
<Status code={200} />
|
|
294
|
+
<Headers headers={{ 'X-Custom': 'value' }} />
|
|
295
|
+
<Body data={result} />
|
|
296
|
+
</Response>
|
|
297
|
+
);
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Response Components
|
|
301
|
+
|
|
302
|
+
| Component | Description |
|
|
303
|
+
|-----------|-------------|
|
|
304
|
+
| `<Response>` | Wrapper for composing responses |
|
|
305
|
+
| `<Status code={201} />` | Set HTTP status code |
|
|
306
|
+
| `<Body data={{...}} />` | Set JSON response body |
|
|
307
|
+
| `<Headers headers={{...}} />` | Set custom headers |
|
|
308
|
+
| `<Err code={404} message="..." />` | Error response shorthand |
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## 🌶️ Middleware
|
|
313
|
+
|
|
314
|
+
Middleware can use JSX components for responses and prop augmentation!
|
|
315
|
+
|
|
316
|
+
### Creating Middleware
|
|
317
|
+
|
|
318
|
+
```tsx
|
|
319
|
+
// middleware/auth.tsx
|
|
320
|
+
import { Augment, Err, authFailureTracker, isSafeString } from 'tagliatelle';
|
|
321
|
+
import type { HandlerProps, MiddlewareFunction } from 'tagliatelle';
|
|
322
|
+
|
|
323
|
+
export const authMiddleware: MiddlewareFunction = async (props, request, reply) => {
|
|
324
|
+
const apiKey = request.headers['x-api-key'];
|
|
325
|
+
|
|
326
|
+
// Return JSX error response
|
|
327
|
+
if (!apiKey || typeof apiKey !== 'string') {
|
|
328
|
+
return <Err code={401} message="Authentication required" />;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const user = await verifyToken(apiKey);
|
|
332
|
+
|
|
333
|
+
if (!user) {
|
|
334
|
+
return <Err code={401} message="Invalid credentials" />;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Augment props with user data
|
|
338
|
+
return <Augment user={user} />;
|
|
339
|
+
};
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Middleware Factory Pattern
|
|
343
|
+
|
|
344
|
+
```tsx
|
|
345
|
+
// Role-based authorization factory
|
|
346
|
+
export function requireRole(role: string): MiddlewareFunction {
|
|
347
|
+
return async (props, request, reply) => {
|
|
348
|
+
const user = props.user;
|
|
349
|
+
|
|
350
|
+
if (!user || user.role !== role) {
|
|
351
|
+
return <Err code={403} message="Access denied" />;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return; // Continue to handler
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Usage in _config.tsx
|
|
359
|
+
<Middleware use={requireRole('admin')} />
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### Middleware Components
|
|
363
|
+
|
|
364
|
+
| Component | Description |
|
|
365
|
+
|-----------|-------------|
|
|
366
|
+
| `<Err code={401} message="..." />` | Return error and halt chain |
|
|
367
|
+
| `<Augment user={...} />` | Add data to handler props |
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## ⚙️ Handler Props
|
|
372
|
+
|
|
373
|
+
Every handler receives typed props:
|
|
374
|
+
|
|
375
|
+
```tsx
|
|
376
|
+
interface HandlerProps<TParams, TBody, TQuery> {
|
|
377
|
+
params: TParams; // URL parameters
|
|
378
|
+
query: TQuery; // Query string
|
|
379
|
+
body: TBody; // Request body
|
|
380
|
+
headers: Record<string, string>; // Request headers
|
|
381
|
+
request: FastifyRequest; // Raw Fastify request
|
|
382
|
+
reply: FastifyReply; // Raw Fastify reply
|
|
383
|
+
log: Logger; // Fastify logger
|
|
384
|
+
user?: unknown; // From auth middleware
|
|
385
|
+
db?: unknown; // From DB provider
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Example with Types
|
|
390
|
+
|
|
391
|
+
```tsx
|
|
392
|
+
interface CreatePostBody {
|
|
393
|
+
title: string;
|
|
394
|
+
content: string;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
export async function POST({ body, user, log }: HandlerProps<unknown, CreatePostBody>) {
|
|
398
|
+
log.info('Creating post');
|
|
399
|
+
|
|
400
|
+
if (!body.title) {
|
|
401
|
+
return <Err code={400} message="Title required" />;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const post = await createPost({ ...body, author: user.id });
|
|
405
|
+
|
|
406
|
+
return (
|
|
407
|
+
<Response>
|
|
408
|
+
<Status code={201} />
|
|
409
|
+
<Body data={{ success: true, data: post }} />
|
|
410
|
+
</Response>
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
---
|
|
416
|
+
|
|
417
|
+
## 📜 Naming Conventions
|
|
418
|
+
|
|
419
|
+
| Pattern | Description |
|
|
420
|
+
|---------|-------------|
|
|
421
|
+
| `index.tsx` | Root of directory (`/posts/index.tsx` → `/posts`) |
|
|
422
|
+
| `[param].tsx` | Dynamic parameter (`/posts/[id].tsx` → `/posts/:id`) |
|
|
423
|
+
| `[...slug].tsx` | Catch-all (`/docs/[...slug].tsx` → `/docs/*`) |
|
|
424
|
+
| `_config.tsx` | Directory configuration (not a route) |
|
|
425
|
+
| `_*.ts` | Private files (ignored by router) |
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
## 🛡️ Security Utilities
|
|
430
|
+
|
|
431
|
+
Tagliatelle includes security helpers:
|
|
432
|
+
|
|
433
|
+
```tsx
|
|
434
|
+
import {
|
|
435
|
+
authFailureTracker, // Rate limit auth failures by IP
|
|
436
|
+
isSafeString, // Validate string safety
|
|
437
|
+
sanitizeErrorMessage, // Clean error messages
|
|
438
|
+
safeErrorResponse, // Safe error responses
|
|
439
|
+
withTimeout, // Add timeouts to async operations
|
|
440
|
+
} from 'tagliatelle';
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## 🚀 Performance
|
|
446
|
+
|
|
447
|
+
Built on Fastify, you get:
|
|
448
|
+
- **100k+ requests/second** throughput
|
|
449
|
+
- **Low latency** JSON serialization
|
|
450
|
+
- **Schema validation** support
|
|
451
|
+
- **Automatic logging**
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## 📋 CLI Reference
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
# Create a new project
|
|
459
|
+
npx tagliatelle init my-api
|
|
460
|
+
|
|
461
|
+
# Create without installing dependencies
|
|
462
|
+
npx tagliatelle init my-api --skip-install
|
|
463
|
+
|
|
464
|
+
# Show help
|
|
465
|
+
npx tagliatelle --help
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
---
|
|
469
|
+
|
|
470
|
+
## 🤝 Contributing
|
|
471
|
+
|
|
472
|
+
Got a new "ingredient"? Open a Pull Request! We're looking for:
|
|
473
|
+
|
|
474
|
+
- [ ] WebSocket support (`<WebSocket />`)
|
|
475
|
+
- [ ] OpenAPI schema generation
|
|
476
|
+
- [ ] Static file serving (`<Static />`)
|
|
477
|
+
- [ ] GraphQL integration
|
|
478
|
+
- [ ] Database adapters
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## 📜 License
|
|
483
|
+
|
|
484
|
+
MIT
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
**Made with ❤️ and plenty of carbs. Chahya Tayba !** 🇹🇳
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 🍝 Tagliatelle CLI
|
|
4
|
+
*
|
|
5
|
+
* Create new Tagliatelle projects with a single command!
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* npx tagliatelle init my-api
|
|
9
|
+
* npx tagliatelle init my-api --skip-install
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG"}
|