@shipindays/shipindays 0.1.2

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/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nikhil sai ankilla
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,505 @@
1
+ # ⚡ create-shipindays
2
+
3
+ > Scaffold a production-ready Next.js SaaS in seconds. Pick your stack, get auth + email wired up and working.
4
+
5
+ ```bash
6
+ npx @shipindays/shipindays@latest my-app
7
+ ```
8
+
9
+ [![npm version](https://img.shields.io/npm/v/create-shipindays)](https://www.npmjs.com/org/shipindays/shipindays)
10
+ [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](./LICENSE)
11
+ [![GitHub stars](https://img.shields.io/github/stars/nikhilsaiankilla/shipindays)](https://github.com/nikhilsaiankilla/shipindays)
12
+
13
+ ---
14
+
15
+ ## What is this?
16
+
17
+ Most SaaS boilerplates give you one fixed stack. You either use it or you don't.
18
+
19
+ `create-shipindays` asks you what you want and builds it for you:
20
+
21
+ ```
22
+ ? Auth provider
23
+ ❯ Supabase Auth
24
+ NextAuth v5
25
+
26
+ ? Email provider
27
+ ❯ Resend
28
+ Nodemailer
29
+
30
+ ✓ Auth: supabase injected.
31
+ ✓ Email: resend injected.
32
+ ✓ Your SaaS is scaffolded.
33
+ ```
34
+
35
+ Every combination produces a working Next.js 15 app with the right files, the right dependencies, and a pre-filled `.env.example`.
36
+
37
+ ---
38
+
39
+ ## What's included
40
+
41
+ Every scaffold includes:
42
+
43
+ - **Next.js 15** with App Router and TypeScript
44
+ - **Tailwind CSS** + **shadcn/ui** components
45
+ - Auth — your choice of provider
46
+ - Email — your choice of provider
47
+ - Protected `/dashboard` route
48
+ - Login + Signup pages
49
+ - `.env.example` pre-filled for your chosen stack
50
+ - `.gitignore` that won't leak your secrets
51
+
52
+ ---
53
+
54
+ ## Quick start
55
+
56
+ ```bash
57
+ # Create a new project
58
+ npx @shipindays/shipindays@latest my-app
59
+
60
+ # Move into it
61
+ cd my-app
62
+
63
+ # Copy env file and fill in your keys
64
+ cp .env.example .env.local
65
+
66
+ # Push database schema (if using Drizzle)
67
+ npm run db:push
68
+
69
+ # Start dev server
70
+ npm run dev
71
+ ```
72
+
73
+ ---
74
+
75
+ ## Supported providers
76
+
77
+ | Feature | Providers |
78
+ |---------|-----------|
79
+ | Auth | Supabase Auth, NextAuth v5 |
80
+ | Email | Resend, Nodemailer |
81
+
82
+ More coming. PRs welcome — see [Contributing](#contributing).
83
+
84
+ ---
85
+
86
+ ## Support
87
+
88
+ If `create-shipindays` saved you time, consider supporting the project.
89
+
90
+ [![Buy Me A Coffee](https://img.shields.io/badge/Buy%20Me%20A%20Coffee-donate-yellow)](https://buymeacoffee.com/nikhilsaiankilla)
91
+ [![Ko-fi](https://img.shields.io/badge/Ko--fi-donate-blue)](https://ko-fi.com/nikhilsaiankilla)
92
+
93
+ ## Hiring
94
+
95
+ I build full stack products and open source tooling. If your team is looking for a developer, let's talk.
96
+
97
+ → [x.com/itzznikhilsai](https://x.com/itzznikhilsai)
98
+ → [nikhilsaiankilla@gmail.com](mailto:nikhilsaiankilla@gmail.com)
99
+
100
+ ---
101
+
102
+ ## How it works
103
+
104
+ The CLI is built on a **base + blocks** pattern.
105
+
106
+ ### Base template
107
+
108
+ `templates/base/` is always copied first. It contains:
109
+
110
+ - The full Next.js app skeleton
111
+ - **Placeholder files** for auth and email that throw errors if called
112
+ - App code that imports from fixed paths (`@/lib/auth`, `@/lib/email`) — these paths never change
113
+
114
+ ### Blocks
115
+
116
+ `templates/blocks/<feature>/<provider>/` contains the real implementation for each provider.
117
+
118
+ When you pick a provider, the CLI copies that block's files **on top of the base**. Because both the base placeholder and the block use the same file paths, the block overwrites the placeholder.
119
+
120
+ ```
121
+ base/src/lib/auth/index.ts ← placeholder (throws error)
122
+
123
+ blocks/auth/supabase/src/lib/auth/index.ts ← real Supabase implementation
124
+
125
+ copies and overwrites
126
+
127
+ your-project/src/lib/auth/index.ts ← Supabase code, ready to use
128
+ ```
129
+
130
+ Your app always imports `from "@/lib/auth"`. It never knows or cares which provider is underneath.
131
+
132
+ ---
133
+
134
+ ## Project structure
135
+
136
+ ```
137
+ create-shipindays/
138
+ ├── index.js ← CLI entry point (the npx command)
139
+ ├── package.json ← CLI's own dependencies
140
+
141
+ └── templates/
142
+ ├── base/ ← always scaffolded first
143
+ │ └── src/
144
+ │ ├── middleware.ts ← placeholder
145
+ │ ├── lib/
146
+ │ │ ├── auth/index.ts ← placeholder
147
+ │ │ └── email/index.ts ← placeholder
148
+ │ └── app/
149
+ │ ├── dashboard/page.tsx
150
+ │ └── (auth)/
151
+ │ ├── login/page.tsx
152
+ │ └── signup/page.tsx
153
+
154
+ └── blocks/
155
+ ├── auth/
156
+ │ ├── supabase/ ← injected when user picks Supabase Auth
157
+ │ │ ├── package.json
158
+ │ │ └── src/
159
+ │ │ ├── middleware.ts
160
+ │ │ ├── lib/auth/index.ts
161
+ │ │ └── lib/supabase/
162
+ │ │ ├── server.ts
163
+ │ │ └── client.ts
164
+ │ │
165
+ │ └── nextauth/ ← injected when user picks NextAuth v5
166
+ │ ├── package.json
167
+ │ └── src/
168
+ │ ├── middleware.ts
169
+ │ ├── lib/auth/index.ts
170
+ │ └── app/api/auth/
171
+ │ └── [...nextauth]/
172
+ │ └── route.ts ← ONLY nextauth has this
173
+
174
+ └── email/
175
+ ├── resend/
176
+ │ ├── package.json
177
+ │ └── index.ts
178
+ └── nodemailer/
179
+ ├── package.json
180
+ └── index.ts
181
+ ```
182
+
183
+ ---
184
+
185
+ ## Contributing
186
+
187
+ PRs are very welcome. The most valuable contributions are **new provider blocks**.
188
+
189
+ ### Setup
190
+
191
+ ```bash
192
+ git clone https://github.com/nikhilsaiankilla/shipindays
193
+ cd shipindays
194
+ npm install
195
+ npm link # makes "create-shipindays" available as a local command
196
+ ```
197
+
198
+ Test your changes:
199
+
200
+ ```bash
201
+ create-shipindays test-app
202
+ cd test-app
203
+ cat src/lib/auth/index.ts # verify the right block was injected
204
+ cat .env.example # verify the right env vars were added
205
+ ```
206
+
207
+ ---
208
+
209
+ ## How to add a new provider block
210
+
211
+ There are two cases depending on whether your provider needs files at **paths that already exist in base**, or needs **extra files that base doesn't have**.
212
+
213
+ ---
214
+
215
+ ### CASE 1 — Provider only needs files that already exist in base
216
+
217
+ **Example: adding Postmark as an email provider.**
218
+
219
+ Postmark only needs `src/lib/email/index.ts` — a file that already exists in base as a placeholder. This is the simple case.
220
+
221
+ **Step 1 — Create the block folder**
222
+
223
+ ```
224
+ templates/blocks/email/postmark/
225
+ ```
226
+
227
+ **Step 2 — Add `index.ts` with the exact same exported functions as other email providers**
228
+
229
+ ```ts
230
+ // templates/blocks/email/postmark/index.ts
231
+
232
+ export async function sendWelcomeEmail({ to, name }: { to: string; name: string }) {
233
+ // your Postmark implementation
234
+ }
235
+
236
+ export async function sendPasswordResetEmail({ to, resetUrl }: { to: string; resetUrl: string }) {
237
+ // your Postmark implementation
238
+ }
239
+ ```
240
+
241
+ > ⚠️ **The exported function names must be identical to every other email block.**
242
+ > The rest of the app calls these functions by name. If the name is different, it breaks.
243
+
244
+ **Step 3 — Add `package.json` with only this provider's dependencies**
245
+
246
+ ```json
247
+ {
248
+ "dependencies": {
249
+ "postmark": "^4.0.0"
250
+ }
251
+ }
252
+ ```
253
+
254
+ **Step 4 — Register it in `index.js`**
255
+
256
+ Add to `EMAIL_PROVIDERS`:
257
+
258
+ ```js
259
+ postmark: {
260
+ label: "Postmark",
261
+ hint: "postmarkapp.com — great deliverability",
262
+ },
263
+ ```
264
+
265
+ Add to `ENV_VARS.email`:
266
+
267
+ ```js
268
+ postmark: {
269
+ "# ── Postmark (Email) ──────────────────────────────────────────────────": [
270
+ "POSTMARK_API_TOKEN=",
271
+ ],
272
+ },
273
+ ```
274
+
275
+ **That's it.** The CLI will show Postmark as an option and inject it correctly.
276
+
277
+ ---
278
+
279
+ ### CASE 2 — Provider needs extra files that base doesn't have
280
+
281
+ **Example 1: NextAuth needs `src/app/api/auth/[...nextauth]/route.ts`**
282
+ **Example 2: Supabase needs `src/lib/supabase/server.ts` and `src/lib/supabase/client.ts`**
283
+
284
+ Base doesn't have these files at all. The block needs to add them from scratch.
285
+
286
+ This is handled automatically — the `injectBlock()` function for `auth` blocks copies the **entire `src/` folder** of the block into the project's `src/` folder. So any extra files your block includes just get created.
287
+
288
+ **Step 1 — Create the block folder with a `src/` subfolder**
289
+
290
+ ```
291
+ templates/blocks/auth/myauth/
292
+ src/ ← must be named src/
293
+ lib/
294
+ auth/
295
+ index.ts ← replaces base placeholder (required)
296
+ middleware.ts ← replaces base placeholder (required)
297
+ ```
298
+
299
+ **Step 2 — Add any extra files your provider needs inside `src/`**
300
+
301
+ For example, if your provider needs a server client and browser client:
302
+
303
+ ```
304
+ templates/blocks/auth/myauth/
305
+ src/
306
+ lib/
307
+ auth/
308
+ index.ts ← replaces base placeholder
309
+ myauth/
310
+ server.ts ← EXTRA file — doesn't exist in base, gets created
311
+ client.ts ← EXTRA file — doesn't exist in base, gets created
312
+ middleware.ts ← replaces base placeholder
313
+ ```
314
+
315
+ For example, if your provider needs an API route:
316
+
317
+ ```
318
+ templates/blocks/auth/myauth/
319
+ src/
320
+ lib/
321
+ auth/
322
+ index.ts
323
+ middleware.ts
324
+ app/
325
+ api/
326
+ auth/
327
+ [...myauth]/
328
+ route.ts ← EXTRA file — gets created in the project
329
+ ```
330
+
331
+ **Step 3 — The contract: `lib/auth/index.ts` must export these exact 3 functions**
332
+
333
+ No matter what else your block does internally, `src/lib/auth/index.ts` must export:
334
+
335
+ ```ts
336
+ // Every auth block MUST export these 3 functions with these exact signatures.
337
+ // The dashboard, middleware, and login page call these — they never change.
338
+
339
+ export async function getCurrentUser(): Promise<User | null>
340
+ // Returns the logged-in user, or null if not logged in.
341
+ // Used when you want to show different UI for logged-in vs logged-out users.
342
+
343
+ export async function requireUser(): Promise<User>
344
+ // Returns the logged-in user, or redirects to /login if not logged in.
345
+ // Used on any page that requires authentication.
346
+
347
+ export async function signOut(): Promise<void>
348
+ // Signs the user out and redirects to /.
349
+ // Called from logout buttons.
350
+ ```
351
+
352
+ **Step 4 — Add `package.json` with only this provider's dependencies**
353
+
354
+ ```json
355
+ {
356
+ "dependencies": {
357
+ "my-auth-package": "^1.0.0"
358
+ }
359
+ }
360
+ ```
361
+
362
+ **Step 5 — Register it in `index.js`**
363
+
364
+ Add to `AUTH_PROVIDERS`:
365
+
366
+ ```js
367
+ myauth: {
368
+ label: "MyAuth",
369
+ hint: "Description of your provider",
370
+ },
371
+ ```
372
+
373
+ Add to `ENV_VARS.auth`:
374
+
375
+ ```js
376
+ myauth: {
377
+ "# ── MyAuth ───────────────────────────────────────────────────────────────": [
378
+ "MYAUTH_API_KEY=",
379
+ "MYAUTH_SECRET=",
380
+ ],
381
+ },
382
+ ```
383
+
384
+ **That's it.** The CLI handles everything else — injecting the files, merging deps, writing env vars.
385
+
386
+ ---
387
+
388
+ ## How `injectBlock()` works internally
389
+
390
+ This is the function that makes the whole system work. Understanding it helps you debug issues.
391
+
392
+ ```
393
+ EMAIL blocks:
394
+ Copies block files → project/src/lib/<feature>/
395
+ e.g. blocks/email/resend/index.ts → project/src/lib/email/index.ts
396
+
397
+ AUTH blocks:
398
+ Copies block's src/ folder → project/src/
399
+ e.g. blocks/auth/nextauth/src/middleware.ts → project/src/middleware.ts
400
+ blocks/auth/nextauth/src/lib/auth/index.ts → project/src/lib/auth/index.ts
401
+ blocks/auth/nextauth/src/app/api/auth/[...nextauth]/route.ts → project/src/app/api/auth/[...nextauth]/route.ts
402
+ ```
403
+
404
+ `overwrite: true` is set on every copy. This means:
405
+ - Files that exist in base get overwritten by the block (placeholder → real implementation)
406
+ - Files that don't exist in base get created fresh (extra files like API routes)
407
+
408
+ ---
409
+
410
+ ## The contract system
411
+
412
+ Every feature has a contract — a set of functions that **every provider for that feature must export**.
413
+
414
+ The rest of the app only ever calls contract functions. It never imports from the provider directly.
415
+
416
+ ### Auth contract
417
+
418
+ ```ts
419
+ // src/lib/auth/index.ts — every auth block must export these
420
+ export async function getCurrentUser(): Promise<User | null>
421
+ export async function requireUser(): Promise<User>
422
+ export async function signOut(): Promise<void>
423
+ ```
424
+
425
+ ### Email contract
426
+
427
+ ```ts
428
+ // src/lib/email/index.ts — every email block must export these
429
+ export async function sendWelcomeEmail(args: { to: string; name: string }): Promise<void>
430
+ export async function sendPasswordResetEmail(args: { to: string; resetUrl: string }): Promise<void>
431
+ ```
432
+
433
+ If you add a new email type (e.g. `sendInvoiceEmail`), add it to the base placeholder first, then implement it in every existing email block, then in your new block.
434
+
435
+ ---
436
+
437
+ ## Adding a completely new feature (e.g. payments)
438
+
439
+ Follow this checklist:
440
+
441
+ ```
442
+ 1. Create block folders:
443
+ templates/blocks/payments/stripe/
444
+ templates/blocks/payments/lemonsqueezy/
445
+
446
+ 2. Add placeholder to base:
447
+ templates/base/src/lib/payments/index.ts
448
+ (same pattern — exports functions that throw errors)
449
+
450
+ 3. Implement the contract in each block:
451
+ blocks/payments/stripe/index.ts
452
+ blocks/payments/lemonsqueezy/index.ts
453
+ (each must export the same function names)
454
+
455
+ 4. Add package.json to each block with its deps
456
+
457
+ 5. In index.js:
458
+ a. Add PAYMENT_PROVIDERS object (same shape as EMAIL_PROVIDERS)
459
+ b. Add ENV_VARS.payments object (same shape as ENV_VARS.email)
460
+ c. Add a p.select() prompt in main() for payments
461
+ d. Add choices.payments = paymentsProvider to the choices object
462
+ e. Add injectBlock("payments", choices.payments, targetPath) call
463
+ f. Add mergePackageJson(targetPath, "payments", choices.payments) call
464
+ ```
465
+
466
+ ---
467
+
468
+ ## FAQ
469
+
470
+ **Q: Why not just have separate full templates like `create-t3-app`?**
471
+
472
+ Full templates mean if you have 3 auth providers × 3 email providers × 2 payment providers, you need 18 separate templates. Any bug fix needs to be made 18 times. Blocks mean you fix it once.
473
+
474
+ **Q: What if two blocks conflict?**
475
+
476
+ They shouldn't if they respect the contract. Both blocks write to `src/lib/auth/index.ts` but only one is ever injected per project. They never coexist.
477
+
478
+ **Q: What if I want to add a provider that needs to modify an existing file (not just replace it)?**
479
+
480
+ This is the one case blocks don't handle cleanly. For example, if you needed to add a line to `layout.tsx`. For now, document it as a manual step in the block's README. Code generation (writing files programmatically) is planned for v2.
481
+
482
+ ---
483
+
484
+ ## Roadmap
485
+
486
+ - [ ] Payments — Stripe block
487
+ - [ ] Payments — Lemon Squeezy block
488
+ - [ ] Auth — Clerk block
489
+ - [ ] Database — Drizzle + Supabase Postgres block
490
+ - [ ] Database — Mongoose + MongoDB block
491
+ - [ ] Code generation for layout-level injections
492
+
493
+ ---
494
+
495
+ ## License
496
+
497
+ MIT — free forever.
498
+
499
+ ---
500
+
501
+ ## Author
502
+
503
+ Built by [Nikhil Sai](https://x.com/itzznikhilsai).
504
+
505
+ If this helped you ship faster, a ⭐ on GitHub goes a long way.