create-mikstack 0.1.36 → 0.1.38

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.
@@ -180,6 +180,10 @@ function scaffold(config, onStatus) {
180
180
  fs.rmSync(path.join(target, "agents.md"), { force: true });
181
181
  fs.rmSync(path.join(target, ".npmrc"), { force: true });
182
182
  fs.rmSync(path.join(target, "src", "lib", "index.ts"), { force: true });
183
+ const pkgPath = path.join(target, "package.json");
184
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
185
+ delete pkg.devDependencies?.["@sveltejs/adapter-auto"];
186
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
183
187
  onStatus?.("Applying templates...");
184
188
  const overlay = (dir) => {
185
189
  copyDir(dir, target);
@@ -377,12 +381,14 @@ async function cli() {
377
381
  ]
378
382
  });
379
383
  const cols = process.stdout.columns || 80;
384
+ const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
380
385
  const handleData = (data) => {
381
386
  if (!onLine) return;
382
387
  const line = data.toString().trim().split("\n").pop();
383
388
  if (!line) return;
389
+ const clean = stripAnsi(line);
384
390
  const maxLen = cols - 5;
385
- onLine(line.length > maxLen ? line.slice(0, maxLen - 1) + "…" : line);
391
+ onLine(clean.length > maxLen ? clean.slice(0, maxLen - 1) + "…" : clean);
386
392
  };
387
393
  child.stdout.on("data", handleData);
388
394
  child.stderr.on("data", handleData);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-mikstack",
3
- "version": "0.1.36",
3
+ "version": "0.1.38",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,7 +8,7 @@
8
8
  "directory": "packages/create-mikstack"
9
9
  },
10
10
  "bin": {
11
- "create-mikstack": "./dist/index.js"
11
+ "create-mikstack": "./dist/index.mjs"
12
12
  },
13
13
  "files": [
14
14
  "dist",
@@ -25,7 +25,7 @@
25
25
  "test": "bun test"
26
26
  },
27
27
  "dependencies": {
28
- "@clack/prompts": "^0.10.0",
28
+ "@clack/prompts": "^1.0.0",
29
29
  "picocolors": "^1.1.1",
30
30
  "sv": "0.12.1"
31
31
  },
@@ -37,7 +37,7 @@
37
37
  "oxfmt": "^0.28.0",
38
38
  "oxlint": "^1.43.0",
39
39
  "oxlint-tsgolint": "^0.11.4",
40
- "tsdown": "^0.12.0",
40
+ "tsdown": "^0.20.0",
41
41
  "typescript": "^5.9.3"
42
42
  }
43
43
  }
@@ -1,5 +1,11 @@
1
1
  FROM node:22-slim AS base
2
2
  WORKDIR /app
3
+ # {{#if:pmIsPnpm}}
4
+ RUN corepack enable
5
+ # {{/if:pmIsPnpm}}
6
+ # {{#if:pmIsBun}}
7
+ RUN npm i -g bun
8
+ # {{/if:pmIsBun}}
3
9
 
4
10
  FROM base AS deps
5
11
  COPY package.json {{lockfile}} ./
@@ -7,10 +13,10 @@ COPY package.json {{lockfile}} ./
7
13
  RUN npm ci
8
14
  # {{/if:pmIsNpm}}
9
15
  # {{#if:pmIsPnpm}}
10
- RUN corepack enable && pnpm install --frozen-lockfile
16
+ RUN pnpm install --frozen-lockfile
11
17
  # {{/if:pmIsPnpm}}
12
18
  # {{#if:pmIsBun}}
13
- RUN npm i -g bun && bun install --frozen-lockfile
19
+ RUN bun install --frozen-lockfile
14
20
  # {{/if:pmIsBun}}
15
21
 
16
22
  FROM base AS build
@@ -22,11 +28,11 @@ RUN npm run build
22
28
  RUN npm prune --omit=dev
23
29
  # {{/if:pmIsNpm}}
24
30
  # {{#if:pmIsPnpm}}
25
- RUN corepack enable && pnpm run build
31
+ RUN pnpm run build
26
32
  RUN pnpm prune --prod
27
33
  # {{/if:pmIsPnpm}}
28
34
  # {{#if:pmIsBun}}
29
- RUN npm i -g bun && bun run build
35
+ RUN bun run build
30
36
  RUN bun install --production
31
37
  # {{/if:pmIsBun}}
32
38
 
@@ -40,7 +40,7 @@ services:
40
40
  BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET}
41
41
  BETTER_AUTH_URL: ${BETTER_AUTH_URL}
42
42
  ORIGIN: ${BETTER_AUTH_URL}
43
- PUBLIC_SERVER: http://zero-cache:4848
43
+ PUBLIC_SERVER: ${PUBLIC_SERVER}
44
44
  depends_on:
45
45
  - db
46
46
  - zero-cache
@@ -5,6 +5,9 @@ declare global {
5
5
  interface Locals {
6
6
  user: User | null;
7
7
  session: Session | null;
8
+ // {{#if:i18n}}
9
+ locale: string;
10
+ // {{/if:i18n}}
8
11
  }
9
12
  }
10
13
  }
@@ -4,6 +4,10 @@ import { svelteKitHandler } from "better-auth/svelte-kit";
4
4
  import { building } from "$app/environment";
5
5
 
6
6
  export const handle: Handle = async ({ event, resolve }) => {
7
+ // {{#if:i18n}}
8
+ event.locals.locale = event.cookies.get("locale") ?? "en";
9
+ // {{/if:i18n}}
10
+
7
11
  const session = await auth.api.getSession({
8
12
  headers: event.request.headers,
9
13
  });
@@ -4,7 +4,7 @@ import { magicLink } from "better-auth/plugins";
4
4
  import { sveltekitCookies } from "better-auth/svelte-kit";
5
5
  import { getRequestEvent } from "$app/server";
6
6
  import { building } from "$app/environment";
7
- import { env } from "$env/dynamic/private";
7
+ import { env } from "$lib/server/env";
8
8
  import { db } from "./db";
9
9
  import * as schema from "./db/schema";
10
10
  import { notif } from "./notifications";
@@ -1,7 +1,7 @@
1
1
  import { drizzle } from "drizzle-orm/postgres-js";
2
2
  import postgres from "postgres";
3
3
  import * as schema from "./schema";
4
- import { env } from "$env/dynamic/private";
4
+ import { env } from "$lib/server/env";
5
5
 
6
6
  export type DrizzleDB = ReturnType<typeof drizzle<typeof schema>>;
7
7
 
@@ -10,7 +10,6 @@ let _db: DrizzleDB | undefined;
10
10
  export const db = new Proxy({} as DrizzleDB, {
11
11
  get(_, prop) {
12
12
  if (!_db) {
13
- if (!env.DATABASE_URL) throw new Error("DATABASE_URL is not set");
14
13
  const client = postgres(env.DATABASE_URL);
15
14
  _db = drizzle(client, { schema, casing: "snake_case" });
16
15
  }
@@ -19,7 +19,7 @@ export const session = pgTable("session", {
19
19
  userAgent: text(),
20
20
  userId: text()
21
21
  .notNull()
22
- .references(() => user.id),
22
+ .references(() => user.id, { onDelete: "cascade" }),
23
23
  createdAt: timestamp().notNull().defaultNow(),
24
24
  updatedAt: timestamp().notNull().defaultNow(),
25
25
  });
@@ -30,7 +30,7 @@ export const account = pgTable("account", {
30
30
  providerId: text().notNull(),
31
31
  userId: text()
32
32
  .notNull()
33
- .references(() => user.id),
33
+ .references(() => user.id, { onDelete: "cascade" }),
34
34
  accessToken: text(),
35
35
  refreshToken: text(),
36
36
  idToken: text(),
@@ -56,7 +56,7 @@ export const notificationDelivery = pgTable("notification_delivery", {
56
56
  id: text()
57
57
  .primaryKey()
58
58
  .$defaultFn(() => crypto.randomUUID()),
59
- userId: text().references(() => user.id),
59
+ userId: text().references(() => user.id, { onDelete: "set null" }),
60
60
  type: text().notNull(),
61
61
  channel: text().notNull(),
62
62
  status: text({ enum: ["pending", "sent", "delivered", "failed"] })
@@ -78,7 +78,7 @@ export const inAppNotification = pgTable("in_app_notification", {
78
78
  .$defaultFn(() => crypto.randomUUID()),
79
79
  userId: text()
80
80
  .notNull()
81
- .references(() => user.id),
81
+ .references(() => user.id, { onDelete: "cascade" }),
82
82
  type: text().notNull(),
83
83
  title: text().notNull(),
84
84
  body: text(),
@@ -94,7 +94,7 @@ export const notificationPreference = pgTable("notification_preference", {
94
94
  .$defaultFn(() => crypto.randomUUID()),
95
95
  userId: text()
96
96
  .notNull()
97
- .references(() => user.id),
97
+ .references(() => user.id, { onDelete: "cascade" }),
98
98
  notificationType: text().notNull(),
99
99
  channel: text().notNull(),
100
100
  enabled: boolean().notNull(),
@@ -109,7 +109,7 @@ export const note = pgTable("note", {
109
109
  content: text().notNull().default(""),
110
110
  userId: text()
111
111
  .notNull()
112
- .references(() => user.id),
112
+ .references(() => user.id, { onDelete: "cascade" }),
113
113
  createdAt: timestamp().notNull(),
114
114
  updatedAt: timestamp().notNull(),
115
115
  });
@@ -1,10 +1,12 @@
1
- import { createTransport } from "nodemailer";
1
+ import { createTransport, type Transporter } from "nodemailer";
2
2
  import { desc, eq } from "drizzle-orm";
3
3
  import { dev } from "$app/environment";
4
- import { env } from "$env/dynamic/private";
4
+ import { env } from "$lib/server/env";
5
5
  import { db } from "../db";
6
6
  import { notificationDelivery } from "../db/schema";
7
7
 
8
+ let cachedTransport: Transporter | undefined;
9
+
8
10
  interface Email {
9
11
  subject: string;
10
12
  html: string;
@@ -58,14 +60,16 @@ export async function sendEmail(to: string, email: Email): Promise<void> {
58
60
  throw new Error("SMTP not configured. Set SMTP_HOST, SMTP_USER, and SMTP_PASS in .env");
59
61
  }
60
62
 
61
- const transport = createTransport({
62
- host,
63
- port,
64
- secure: port === 465,
65
- auth: { user, pass },
66
- });
63
+ if (!cachedTransport) {
64
+ cachedTransport = createTransport({
65
+ host,
66
+ port,
67
+ secure: port === 465,
68
+ auth: { user, pass },
69
+ });
70
+ }
67
71
 
68
- await transport.sendMail({
72
+ await cachedTransport.sendMail({
69
73
  from,
70
74
  to,
71
75
  subject: email.subject,
@@ -0,0 +1,32 @@
1
+ import { env as privateEnv } from "$env/dynamic/private";
2
+ import * as v from "valibot";
3
+
4
+ const envSchema = v.object({
5
+ DATABASE_URL: v.pipe(v.string(), v.minLength(1, "DATABASE_URL is required")),
6
+ BETTER_AUTH_SECRET: v.pipe(v.string(), v.minLength(1, "BETTER_AUTH_SECRET is required")),
7
+ BETTER_AUTH_URL: v.optional(v.string(), "http://localhost:5173"),
8
+ SMTP_HOST: v.optional(v.string()),
9
+ SMTP_PORT: v.optional(v.string()),
10
+ SMTP_USER: v.optional(v.string()),
11
+ SMTP_PASS: v.optional(v.string()),
12
+ SMTP_FROM: v.optional(v.string()),
13
+ });
14
+
15
+ const parsed = v.safeParse(envSchema, {
16
+ DATABASE_URL: privateEnv.DATABASE_URL,
17
+ BETTER_AUTH_SECRET: privateEnv.BETTER_AUTH_SECRET,
18
+ BETTER_AUTH_URL: privateEnv.BETTER_AUTH_URL || undefined,
19
+ SMTP_HOST: privateEnv.SMTP_HOST || undefined,
20
+ SMTP_PORT: privateEnv.SMTP_PORT || undefined,
21
+ SMTP_USER: privateEnv.SMTP_USER || undefined,
22
+ SMTP_PASS: privateEnv.SMTP_PASS || undefined,
23
+ SMTP_FROM: privateEnv.SMTP_FROM || undefined,
24
+ });
25
+
26
+ if (!parsed.success) {
27
+ console.error("Invalid environment variables:");
28
+ console.error(JSON.stringify(v.flatten(parsed.issues), null, 2));
29
+ throw new Error("Environment validation failed. Check the errors above.");
30
+ }
31
+
32
+ export const env = parsed.output;
@@ -24,7 +24,7 @@ export const mutators = defineMutators({
24
24
  const note = await tx.run(
25
25
  zql.note.where("id", args.id).where("userId", ctx.userID).one(),
26
26
  );
27
- if (!note) return;
27
+ if (!note) throw new Error("Note not found");
28
28
  await tx.mutate.note.update({
29
29
  id: args.id,
30
30
  title: args.title,
@@ -37,7 +37,7 @@ export const mutators = defineMutators({
37
37
  const note = await tx.run(
38
38
  zql.note.where("id", args.id).where("userId", ctx.userID).one(),
39
39
  );
40
- if (!note) return;
40
+ if (!note) throw new Error("Note not found");
41
41
  await tx.mutate.note.delete({ id: args.id });
42
42
  }),
43
43
  },
@@ -0,0 +1,5 @@
1
+ import { json } from "@sveltejs/kit";
2
+
3
+ export function GET() {
4
+ return json({ status: "ok" });
5
+ }
@@ -20,3 +20,6 @@ jobs:
20
20
  - run: bun run format:check
21
21
  - run: bun run check
22
22
  - run: bun run build
23
+ # {{#if:testing}}
24
+ - run: bun run test
25
+ # {{/if:testing}}
@@ -23,3 +23,6 @@ jobs:
23
23
  - run: npm run format:check
24
24
  - run: npm run check
25
25
  - run: npm run build
26
+ # {{#if:testing}}
27
+ - run: npm run test
28
+ # {{/if:testing}}
@@ -25,3 +25,6 @@ jobs:
25
25
  - run: pnpm run format:check
26
26
  - run: pnpm run check
27
27
  - run: pnpm run build
28
+ # {{#if:testing}}
29
+ - run: pnpm run test
30
+ # {{/if:testing}}
@@ -0,0 +1,5 @@
1
+ import type { LayoutServerLoad } from "./$types";
2
+
3
+ export const load: LayoutServerLoad = ({ locals }) => {
4
+ return { locale: locals.locale };
5
+ };
@@ -3,10 +3,15 @@
3
3
  import "../app.css";
4
4
  // {{#if:i18n}}
5
5
  import { initI18n } from "$lib/i18n";
6
- initI18n();
7
- // {{/if:i18n}}
8
6
 
7
+ let { children, data }: { children: Snippet; data: { locale: string } } = $props();
8
+ $effect(() => {
9
+ initI18n(data.locale);
10
+ });
11
+ // {{/if:i18n}}
12
+ // {{#if:!i18n}}
9
13
  let { children }: { children: Snippet } = $props();
14
+ // {{/if:!i18n}}
10
15
  </script>
11
16
 
12
17
  {@render children()}