rahman-resources 0.2.1 → 0.3.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/README.md CHANGED
@@ -1,18 +1,27 @@
1
1
  # rahman-resources
2
2
 
3
- Template installer for the [Rahman Resources kitab](https://github.com/rahmanef63/resource-site).
3
+ Scaffolder + template installer for the [Rahman Resources kitab](https://github.com/rahmanef63/resource-site).
4
4
 
5
- ## Usage
5
+ ## Quick start
6
6
 
7
7
  ```bash
8
- npx rahman-resources add <template> [target-dir]
9
- pnpm dlx rahman-resources add <template> [target-dir]
8
+ npx rahman-resources init my-app
9
+ cd my-app
10
+ cp .env.example .env.local # fill NEXT_PUBLIC_CONVEX_URL
11
+ npm install --legacy-peer-deps
12
+ npx convex dev --once # generates convex/_generated
13
+ npm run dev
10
14
  ```
11
15
 
12
- ### List templates
16
+ `init` ships a minimal Next 16 + React 19 + Convex + Tailwind 4 + shadcn/ui skeleton (~18 files). Then drop in any layout/recipe/feature with `add`.
17
+
18
+ ## Commands
13
19
 
14
20
  ```bash
15
- npx rahman-resources list
21
+ npx rahman-resources init <app-name> # scaffold fresh project
22
+ npx rahman-resources add <slug> [target-dir] # drop in a layout/recipe/feature
23
+ npx rahman-resources list [layouts|recipes|features]
24
+ npx rahman-resources info <slug>
16
25
  ```
17
26
 
18
27
  ### Inspect a template
@@ -24,11 +33,11 @@ npx rahman-resources info personal-brand-os
24
33
  ### Install into a project
25
34
 
26
35
  ```bash
27
- # new project
28
- mkdir my-app && cd my-app && pnpm init
29
- npx rahman-resources add personal-brand-os .
36
+ # fresh
37
+ npx rahman-resources init my-app
38
+ cd my-app && npx rahman-resources add personal-brand-os .
30
39
 
31
- # existing project
40
+ # existing
32
41
  cd existing-app
33
42
  npx rahman-resources add personal-brand-os .
34
43
  ```
package/bin/cli.js CHANGED
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  // rahman-resources — installer for the Rahman kitab.
3
3
  // Usage:
4
+ // npx rahman-resources init <app-name> [--lite]
4
5
  // npx rahman-resources add <slug> [target-dir]
5
6
  // npx rahman-resources list [layouts|recipes|features]
6
7
  // npx rahman-resources info <slug>
7
8
 
8
9
  import { createRequire } from "node:module";
9
10
  import { spawn } from "node:child_process";
10
- import { existsSync } from "node:fs";
11
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, statSync } from "node:fs";
11
12
  import path from "node:path";
12
13
  import { fileURLToPath } from "node:url";
13
14
 
@@ -25,13 +26,21 @@ const KINDS = /** @type {const} */ (["layout", "recipe", "feature"]);
25
26
 
26
27
  const [, , cmd, ...rest] = process.argv;
27
28
 
28
- main().catch((err) => {
29
- console.error(kleur.red("✖"), err.message ?? err);
30
- process.exit(1);
31
- });
29
+ // Defer to next tick so all module-level const declarations finish initializing
30
+ // before the dispatch reaches functions that reference them.
31
+ queueMicrotask(() =>
32
+ main().catch((err) => {
33
+ console.error(kleur.red("✖"), err.message ?? err);
34
+ process.exit(1);
35
+ }),
36
+ );
32
37
 
33
38
  async function main() {
34
39
  switch (cmd) {
40
+ case "init":
41
+ case "create":
42
+ case "new":
43
+ return runInit(rest);
35
44
  case "add":
36
45
  return runAdd(rest);
37
46
  case "list":
@@ -62,14 +71,16 @@ function printVersion() {
62
71
 
63
72
  function printHelp() {
64
73
  console.log(`
65
- ${kleur.bold("rahman-resources")} — install templates, recipes, and features
74
+ ${kleur.bold("rahman-resources")} — scaffold + install templates, recipes, features
66
75
 
67
76
  ${kleur.bold("Usage:")}
77
+ npx rahman-resources init <app-name> [--lite]
68
78
  npx rahman-resources add <slug> [target-dir]
69
79
  npx rahman-resources list [layouts|recipes|features]
70
80
  npx rahman-resources info <slug>
71
81
 
72
82
  ${kleur.bold("Examples:")}
83
+ npx rahman-resources init my-app --lite ${kleur.dim("# scaffold from template-base, prune notion+builder+communications")}
73
84
  npx rahman-resources add personal-brand-os my-app ${kleur.dim("# layout — pulls folders + installs deps")}
74
85
  npx rahman-resources add ai-sdk-openrouter my-app ${kleur.dim("# feature — runs npm install")}
75
86
  npx rahman-resources add block-editor ${kleur.dim("# recipe — prints code + source URL")}
@@ -136,6 +147,79 @@ ${t.description}
136
147
  console.log(`${kleur.dim(`Source: ${t.source ?? "—"}`)}\n`);
137
148
  }
138
149
 
150
+ // Files in lib/starter prefixed with `_` get renamed on copy.
151
+ // Done because npm pack filters .gitignore and warns on nested package.json.
152
+ const STARTER_RENAME_PAIRS = [
153
+ ["_package", "package"],
154
+ ["_gitignore", ".gitignore"],
155
+ ["_env", ".env"],
156
+ ["_README", "README"],
157
+ ];
158
+
159
+ function renameStarterFile(name) {
160
+ for (const pair of STARTER_RENAME_PAIRS) {
161
+ const f = pair[0];
162
+ const t = pair[1];
163
+ if (name === f || name.startsWith(f + ".")) return t + name.slice(f.length);
164
+ }
165
+ return name;
166
+ }
167
+
168
+ function copyStarterTree(src, dest, appName, slug) {
169
+ mkdirSync(dest, { recursive: true });
170
+ for (const entry of readdirSync(src)) {
171
+ const sFull = path.join(src, entry);
172
+ const dEntry = renameStarterFile(entry);
173
+ const dFull = path.join(dest, dEntry);
174
+ const stat = statSync(sFull);
175
+ if (stat.isDirectory()) {
176
+ copyStarterTree(sFull, dFull, appName, slug);
177
+ } else {
178
+ let body = readFileSync(sFull, "utf8");
179
+ body = body.replaceAll("__APP_NAME__", appName).replaceAll("__APP_SLUG__", slug);
180
+ writeFileSync(dFull, body);
181
+ }
182
+ }
183
+ }
184
+
185
+ async function runInit([appName, ...flags]) {
186
+ if (!appName || appName.startsWith("-")) {
187
+ throw new Error("Usage: rahman-resources init <app-name>");
188
+ }
189
+ const slug = appName.replace(/[^a-z0-9-_]/gi, "-").toLowerCase();
190
+ const target = path.resolve(process.cwd(), appName);
191
+ if (existsSync(target)) {
192
+ throw new Error(`Directory already exists: ${target}`);
193
+ }
194
+
195
+ console.log(kleur.bold(`\n→ Scaffolding ${kleur.cyan(slug)} (Next 16 + Convex + shadcn)\n`));
196
+
197
+ const starter = path.join(__dirname, "../lib/starter");
198
+ if (!existsSync(starter)) {
199
+ throw new Error(`Starter not found at ${starter}`);
200
+ }
201
+
202
+ process.stdout.write(` copying starter ... `);
203
+ copyStarterTree(starter, target, appName, slug);
204
+ console.log(kleur.green("ok"));
205
+
206
+ // Ignore --lite flag for now (no template-base pull yet — starter is minimal already).
207
+ if (flags.includes("--lite")) {
208
+ console.log(kleur.dim(` (--lite is a no-op — starter is already minimal)`));
209
+ }
210
+
211
+ console.log(`\n${kleur.green("✓")} Done. ${kleur.bold(slug)} scaffolded.\n`);
212
+ console.log(`${kleur.bold("Next:")}`);
213
+ console.log(` cd ${appName}`);
214
+ console.log(` cp .env.example .env.local ${kleur.dim("# fill NEXT_PUBLIC_CONVEX_URL")}`);
215
+ console.log(` npm install --legacy-peer-deps`);
216
+ console.log(` npx convex dev --once ${kleur.dim("# generates convex/_generated")}`);
217
+ console.log(` npm run dev\n`);
218
+ console.log(
219
+ `${kleur.dim("Then drop in a layout:")} ${kleur.cyan("npx rahman-resources add personal-brand-os .")}\n`,
220
+ );
221
+ }
222
+
139
223
  async function runAdd([slug, targetArg = "."]) {
140
224
  if (!slug) {
141
225
  console.error(kleur.red("Missing slug."));
package/lib/manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 2,
3
- "generatedAt": "2026-05-05T11:50:32.844Z",
3
+ "generatedAt": "2026-05-05T12:19:53.839Z",
4
4
  "repo": "rahmanef63/resource-site",
5
5
  "branch": "main",
6
6
  "layouts": [
@@ -0,0 +1,37 @@
1
+ # __APP_NAME__
2
+
3
+ Scaffolded with [`rahman-resources`](https://www.npmjs.com/package/rahman-resources) — Next 16 + React 19 + Convex (self-hosted) + Tailwind 4 + shadcn/ui.
4
+
5
+ ## Setup
6
+
7
+ ```bash
8
+ npm install --legacy-peer-deps
9
+ cp .env.example .env.local # fill NEXT_PUBLIC_CONVEX_URL etc.
10
+ npx convex dev --once # generates convex/_generated
11
+ npm run dev
12
+ ```
13
+
14
+ ## Add a layout / recipe / feature
15
+
16
+ ```bash
17
+ npx rahman-resources list
18
+ npx rahman-resources info <slug>
19
+ npx rahman-resources add personal-brand-os . # full-app template (T1)
20
+ npx rahman-resources add ai-sdk-openrouter . # feature (npm install)
21
+ ```
22
+
23
+ ## Hard rules
24
+
25
+ - **NO Clerk.** Auth = `@convex-dev/auth`.
26
+ - **shadcn primitives only** — no raw `<dialog>`, `<input type=date|file>`.
27
+ - Use `proxy.ts` (not `middleware.ts`) on Next 16.
28
+ - `convex/_generated` MUST be committed before deploy.
29
+
30
+ ## Stack
31
+
32
+ | | |
33
+ |---|---|
34
+ | Framework | Next.js 16 (App Router + cacheComponents) |
35
+ | UI | React 19 + Tailwind 4 + shadcn |
36
+ | Backend | Convex (self-hosted compatible) |
37
+ | Auth | `@convex-dev/auth` (Password provider by default) |
@@ -0,0 +1,16 @@
1
+ # Convex (self-hosted or cloud)
2
+ NEXT_PUBLIC_CONVEX_URL=
3
+
4
+ # @convex-dev/auth — required for auth signing
5
+ JWKS=
6
+ JWT_PRIVATE_KEY=
7
+ SITE_URL=http://localhost:3000
8
+
9
+ # Server actions encryption (multi-instance — pin once, share across instances)
10
+ NEXT_SERVER_ACTIONS_ENCRYPTION_KEY=
11
+
12
+ # Optional OAuth providers
13
+ AUTH_GITHUB_ID=
14
+ AUTH_GITHUB_SECRET=
15
+ AUTH_GOOGLE_ID=
16
+ AUTH_GOOGLE_SECRET=
@@ -0,0 +1,12 @@
1
+ node_modules
2
+ .next
3
+ .turbo
4
+ out
5
+ dist
6
+ .env*.local
7
+ .env
8
+ *.tsbuildinfo
9
+ next-env.d.ts
10
+ convex/_generated
11
+ .vercel
12
+ .DS_Store
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "__APP_NAME__",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "next dev --turbo --port 3000",
8
+ "build": "next build",
9
+ "start": "next start",
10
+ "lint": "next lint",
11
+ "typecheck": "tsc --noEmit",
12
+ "convex:dev": "convex dev",
13
+ "convex:codegen": "convex dev --once"
14
+ },
15
+ "dependencies": {
16
+ "@convex-dev/auth": "^0.0.84",
17
+ "@radix-ui/react-label": "^2.1.0",
18
+ "@radix-ui/react-slot": "^1.1.0",
19
+ "class-variance-authority": "^0.7.1",
20
+ "clsx": "^2.1.1",
21
+ "convex": "^1.18.0",
22
+ "lucide-react": "^0.460.0",
23
+ "next": "^16.0.0",
24
+ "next-themes": "^0.4.4",
25
+ "react": "^19.0.0",
26
+ "react-dom": "^19.0.0",
27
+ "sonner": "^1.7.1",
28
+ "tailwind-merge": "^2.5.5"
29
+ },
30
+ "devDependencies": {
31
+ "@tailwindcss/postcss": "^4.0.0",
32
+ "@types/node": "^22.10.0",
33
+ "@types/react": "^19.0.0",
34
+ "@types/react-dom": "^19.0.0",
35
+ "postcss": "^8.5.0",
36
+ "tailwindcss": "^4.0.0",
37
+ "typescript": "^5.7.0"
38
+ }
39
+ }
@@ -0,0 +1,85 @@
1
+ @import "tailwindcss";
2
+
3
+ @theme {
4
+ --font-sans: var(--font-sans), ui-sans-serif, system-ui, sans-serif;
5
+ }
6
+
7
+ :root {
8
+ --background: oklch(1 0 0);
9
+ --foreground: oklch(0.145 0 0);
10
+ --card: oklch(1 0 0);
11
+ --card-foreground: oklch(0.145 0 0);
12
+ --popover: oklch(1 0 0);
13
+ --popover-foreground: oklch(0.145 0 0);
14
+ --primary: oklch(0.205 0 0);
15
+ --primary-foreground: oklch(0.985 0 0);
16
+ --secondary: oklch(0.97 0 0);
17
+ --secondary-foreground: oklch(0.205 0 0);
18
+ --muted: oklch(0.97 0 0);
19
+ --muted-foreground: oklch(0.556 0 0);
20
+ --accent: oklch(0.97 0 0);
21
+ --accent-foreground: oklch(0.205 0 0);
22
+ --destructive: oklch(0.577 0.245 27.325);
23
+ --destructive-foreground: oklch(0.985 0 0);
24
+ --border: oklch(0.922 0 0);
25
+ --input: oklch(0.922 0 0);
26
+ --ring: oklch(0.708 0 0);
27
+ --radius: 0.625rem;
28
+ }
29
+
30
+ .dark {
31
+ --background: oklch(0.145 0 0);
32
+ --foreground: oklch(0.985 0 0);
33
+ --card: oklch(0.205 0 0);
34
+ --card-foreground: oklch(0.985 0 0);
35
+ --popover: oklch(0.205 0 0);
36
+ --popover-foreground: oklch(0.985 0 0);
37
+ --primary: oklch(0.985 0 0);
38
+ --primary-foreground: oklch(0.205 0 0);
39
+ --secondary: oklch(0.269 0 0);
40
+ --secondary-foreground: oklch(0.985 0 0);
41
+ --muted: oklch(0.269 0 0);
42
+ --muted-foreground: oklch(0.708 0 0);
43
+ --accent: oklch(0.269 0 0);
44
+ --accent-foreground: oklch(0.985 0 0);
45
+ --destructive: oklch(0.704 0.191 22.216);
46
+ --destructive-foreground: oklch(0.985 0 0);
47
+ --border: oklch(1 0 0 / 0.1);
48
+ --input: oklch(1 0 0 / 0.15);
49
+ --ring: oklch(0.556 0 0);
50
+ }
51
+
52
+ @theme inline {
53
+ --color-background: var(--background);
54
+ --color-foreground: var(--foreground);
55
+ --color-card: var(--card);
56
+ --color-card-foreground: var(--card-foreground);
57
+ --color-popover: var(--popover);
58
+ --color-popover-foreground: var(--popover-foreground);
59
+ --color-primary: var(--primary);
60
+ --color-primary-foreground: var(--primary-foreground);
61
+ --color-secondary: var(--secondary);
62
+ --color-secondary-foreground: var(--secondary-foreground);
63
+ --color-muted: var(--muted);
64
+ --color-muted-foreground: var(--muted-foreground);
65
+ --color-accent: var(--accent);
66
+ --color-accent-foreground: var(--accent-foreground);
67
+ --color-destructive: var(--destructive);
68
+ --color-destructive-foreground: var(--destructive-foreground);
69
+ --color-border: var(--border);
70
+ --color-input: var(--input);
71
+ --color-ring: var(--ring);
72
+ --radius-sm: calc(var(--radius) - 4px);
73
+ --radius-md: calc(var(--radius) - 2px);
74
+ --radius-lg: var(--radius);
75
+ --radius-xl: calc(var(--radius) + 4px);
76
+ }
77
+
78
+ * {
79
+ border-color: var(--color-border);
80
+ }
81
+
82
+ body {
83
+ background: var(--color-background);
84
+ color: var(--color-foreground);
85
+ }
@@ -0,0 +1,23 @@
1
+ import type { Metadata } from "next";
2
+ import { Inter } from "next/font/google";
3
+ import { ConvexClientProvider } from "@/components/convex-provider";
4
+ import { Toaster } from "sonner";
5
+ import "./globals.css";
6
+
7
+ const inter = Inter({ subsets: ["latin"], variable: "--font-sans" });
8
+
9
+ export const metadata: Metadata = {
10
+ title: { default: "__APP_NAME__", template: "%s — __APP_NAME__" },
11
+ description: "Built with rahman-resources kitab.",
12
+ };
13
+
14
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
15
+ return (
16
+ <html lang="en" suppressHydrationWarning>
17
+ <body className={`${inter.variable} font-sans antialiased`}>
18
+ <ConvexClientProvider>{children}</ConvexClientProvider>
19
+ <Toaster position="bottom-right" />
20
+ </body>
21
+ </html>
22
+ );
23
+ }
@@ -0,0 +1,26 @@
1
+ import Link from "next/link";
2
+ import { Button } from "@/components/ui/button";
3
+
4
+ export default function HomePage() {
5
+ return (
6
+ <main className="flex min-h-screen flex-col items-center justify-center gap-6 p-8 text-center">
7
+ <div className="max-w-xl space-y-3">
8
+ <h1 className="text-4xl font-bold tracking-tight">__APP_NAME__</h1>
9
+ <p className="text-muted-foreground">
10
+ Scaffolded with <code className="rounded bg-muted px-1.5 py-0.5 font-mono text-sm">rahman-resources</code>.
11
+ Drop in a layout next:
12
+ </p>
13
+ </div>
14
+ <pre className="rounded-md bg-muted px-4 py-3 text-left text-sm">
15
+ {`npx rahman-resources add personal-brand-os .
16
+ npx rahman-resources add landing-bento .
17
+ npx rahman-resources list`}
18
+ </pre>
19
+ <Button asChild>
20
+ <Link href="https://github.com/rahmanef63/resource-site" target="_blank" rel="noreferrer">
21
+ Browse the kitab
22
+ </Link>
23
+ </Button>
24
+ </main>
25
+ );
26
+ }
@@ -0,0 +1,13 @@
1
+ "use client";
2
+
3
+ import { ConvexAuthNextjsProvider } from "@convex-dev/auth/nextjs";
4
+ import { ConvexReactClient } from "convex/react";
5
+ import type { ReactNode } from "react";
6
+
7
+ const url = process.env.NEXT_PUBLIC_CONVEX_URL;
8
+ const convex = url ? new ConvexReactClient(url) : null;
9
+
10
+ export function ConvexClientProvider({ children }: { children: ReactNode }) {
11
+ if (!convex) return <>{children}</>;
12
+ return <ConvexAuthNextjsProvider client={convex}>{children}</ConvexAuthNextjsProvider>;
13
+ }
@@ -0,0 +1,43 @@
1
+ import * as React from "react";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+ import { cn } from "@/lib/utils";
5
+
6
+ const buttonVariants = cva(
7
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0",
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
12
+ destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
13
+ outline: "border bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
14
+ secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
15
+ ghost: "hover:bg-accent hover:text-accent-foreground",
16
+ link: "text-primary underline-offset-4 hover:underline",
17
+ },
18
+ size: {
19
+ default: "h-9 px-4 py-2",
20
+ sm: "h-8 rounded-md px-3 text-xs",
21
+ lg: "h-10 rounded-md px-8",
22
+ icon: "h-9 w-9",
23
+ },
24
+ },
25
+ defaultVariants: { variant: "default", size: "default" },
26
+ },
27
+ );
28
+
29
+ export interface ButtonProps
30
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
31
+ VariantProps<typeof buttonVariants> {
32
+ asChild?: boolean;
33
+ }
34
+
35
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
36
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
37
+ const Comp = asChild ? Slot : "button";
38
+ return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />;
39
+ },
40
+ );
41
+ Button.displayName = "Button";
42
+
43
+ export { Button, buttonVariants };
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema.json",
3
+ "style": "new-york",
4
+ "rsc": true,
5
+ "tsx": true,
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "app/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
13
+ "aliases": {
14
+ "components": "@/components",
15
+ "utils": "@/lib/utils",
16
+ "ui": "@/components/ui",
17
+ "lib": "@/lib",
18
+ "hooks": "@/hooks"
19
+ },
20
+ "iconLibrary": "lucide"
21
+ }
@@ -0,0 +1,6 @@
1
+ import { convexAuth } from "@convex-dev/auth/server";
2
+ import { Password } from "@convex-dev/auth/providers/Password";
3
+
4
+ export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
5
+ providers: [Password()],
6
+ });
@@ -0,0 +1,7 @@
1
+ import { httpRouter } from "convex/server";
2
+ import { auth } from "./auth";
3
+
4
+ const http = httpRouter();
5
+ auth.addHttpRoutes(http);
6
+
7
+ export default http;
@@ -0,0 +1,14 @@
1
+ import { authTables } from "@convex-dev/auth/server";
2
+ import { defineSchema, defineTable } from "convex/server";
3
+ import { v } from "convex/values";
4
+
5
+ export default defineSchema({
6
+ ...authTables,
7
+ // Add app tables here. Example:
8
+ // notes: defineTable({
9
+ // userId: v.id("users"),
10
+ // title: v.string(),
11
+ // body: v.string(),
12
+ // createdAt: v.number(),
13
+ // }).index("by_user", ["userId"]),
14
+ });
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,16 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ cacheComponents: true,
4
+ experimental: {
5
+ serverActions: {
6
+ bodySizeLimit: "5mb",
7
+ },
8
+ },
9
+ images: {
10
+ remotePatterns: [
11
+ { protocol: "https", hostname: "images.unsplash.com" },
12
+ ],
13
+ },
14
+ };
15
+
16
+ export default nextConfig;
@@ -0,0 +1,3 @@
1
+ export default {
2
+ plugins: { "@tailwindcss/postcss": {} },
3
+ };
@@ -0,0 +1,12 @@
1
+ // Next 16 proxy (renamed from middleware.ts).
2
+ // Stub — extend with auth gate via convexAuthNextjsToken() when ready.
3
+ import { NextResponse } from "next/server";
4
+ import type { NextRequest } from "next/server";
5
+
6
+ export function proxy(_req: NextRequest) {
7
+ return NextResponse.next();
8
+ }
9
+
10
+ export const config = {
11
+ matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
12
+ };
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "preserve",
15
+ "incremental": true,
16
+ "plugins": [{ "name": "next" }],
17
+ "paths": {
18
+ "@/*": ["./*"]
19
+ }
20
+ },
21
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
22
+ "exclude": ["node_modules", "convex/_generated"]
23
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rahman-resources",
3
- "version": "0.2.1",
4
- "description": "Template installer for Rahman Resources kitab — npx rahman-resources add <template> <target>",
3
+ "version": "0.3.0",
4
+ "description": "Scaffolder + installer for Rahman Resources kitab — npx rahman-resources init <app>; add <template>; list; info",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "Rahman <casadezian@gmail.com>",