void 0.7.11 → 0.7.13

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/AGENT_PROMPT.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ## Void
2
2
 
3
- This project uses [Void](https://void.cloud) — a fullstack Vite plugin + deployment platform for Cloudflare. `voidPlugin()` in `vite.config.ts` gives you file-based API routing on Hono (`routes/`), Inertia-inspired server-rendered pages with co-located loaders/actions (`pages/` + `@void/vue` or `@void/react`), auto-provisioned D1/KV/R2 bindings, first-class Drizzle ORM integration (schema in `db/schema.ts` -> `void/db` Drizzle instance -> typed routes -> typed fetch client), built-in auth, queues, cron jobs, edge caching (ISR), and one-command deploys via `npx void deploy`. For first-time setup, prefer `npx void init`; in an empty directory, install `void` first and let the interactive flow scaffold the starter, add the matching framework adapter, configure project files, handle auth, and link or create the deploy project before the first deploy. In an existing app, `void init` configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`. Use `void` and `@void/*` package names in source code and package manifests.
3
+ This project uses [Void](https://void.cloud) — a fullstack Vite plugin + deployment platform for Cloudflare. `voidPlugin()` in `vite.config.ts` gives you file-based API routing on Hono (`routes/`), Inertia-inspired server-rendered pages with co-located loaders/actions (`pages/` + `@void/vue` or `@void/react`), auto-provisioned D1/KV/R2 bindings, first-class Drizzle ORM integration (schema in `db/schema.ts` -> `void/db` Drizzle instance -> typed routes -> typed fetch client), built-in auth, queues, cron jobs, edge caching (ISR), and one-command deploys via `npx void deploy`. For first-time setup, prefer `npx void init`; in an empty directory, install `void` first and let the interactive flow scaffold the starter with Vite+ by default, add the matching framework adapter, configure project files, handle auth, and link or create the deploy project before the first deploy. In an existing app, `void init` configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`. Use `void` and `@void/*` package names in source code and package manifests.
4
4
 
5
5
  Database: define Drizzle tables in `db/schema.ts`, import `db` from `void/db` and tables from `@schema`. Use `void db push` for prototyping, `void db generate` for production migrations. `drizzle-orm` and `drizzle-kit` ship with void (no extra install). Migrations live in `db/migrations/`.
6
6
 
package/README.md CHANGED
@@ -21,7 +21,7 @@ Install the Void CLI:
21
21
  npm install -D void
22
22
  ```
23
23
 
24
- If you run `void init` in an empty directory, the scaffold flow adds the matching Pages adapter and Vite dependencies for you.
24
+ If you run `void init` in an empty directory, the scaffold flow adds the matching Pages adapter and Vite+ by default.
25
25
 
26
26
  Then run `npx void init`. In an empty directory, the full interactive flow can scaffold a starter, configure local project files, log you in, and link or create your Void project so the next step is just `npx void deploy`. In an existing app, it configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`.
27
27
 
@@ -82,7 +82,7 @@ There is Vinext-specific source in the repo, but it is intentionally on hold and
82
82
 
83
83
  ## Documentation
84
84
 
85
- Full docs live at [void.cloud/docs](https://void.cloud/docs).
85
+ Full docs live at [void.cloud/guide](https://void.cloud/guide).
86
86
 
87
87
  ## License
88
88
 
package/dist/cli/cli.mjs CHANGED
@@ -1519,7 +1519,7 @@ else if (command === "deploy") {
1519
1519
  else if (command === "project") runSubcommandGroup("project", projectSubcommands, parseProjectArgs, (root, parsed) => import("../project-cmd-CeXO06hG.mjs").then(({ runProjectCommand }) => runProjectCommand(root, parsed)));
1520
1520
  else if (command === "secret") runSubcommandGroup("secret", secretSubcommands, parseSecretArgs, (root, parsed) => import("../secret-NDvlqAN2.mjs").then(({ runSecretCommand }) => runSecretCommand(root, parsed)));
1521
1521
  else if (command === "domain") runSubcommandGroup("domain", domainSubcommands, parseDomainArgs, (root, parsed) => import("../domain-Dvj7u8tr.mjs").then(({ runDomainCommand }) => runDomainCommand(root, parsed)));
1522
- else if (command === "db") runSubcommandGroup("db", dbSubcommands, parseDbArgs, (root, parsed) => import("../db-j-XdTjQD.mjs").then(({ runDbCommand }) => runDbCommand(root, parsed)));
1522
+ else if (command === "db") runSubcommandGroup("db", dbSubcommands, parseDbArgs, (root, parsed) => import("../db-YEYSTfGw.mjs").then(({ runDbCommand }) => runDbCommand(root, parsed)));
1523
1523
  else if (command === "env") runSubcommandGroup("env", envSubcommands, parseEnvArgs, (root, parsed) => import("../env-Bjl0rzuf.mjs").then(({ runEnvCommand }) => runEnvCommand(root, parsed)));
1524
1524
  else if (command === "gen") runSubcommandGroup("gen", genSubcommands, parseGenArgs, (root, parsed) => import("../gen-DU8NIdrH.mjs").then(({ runGenCommand }) => runGenCommand(root, parsed)));
1525
1525
  else if (command === "prepare") {
@@ -1539,7 +1539,7 @@ else if (command === "prepare") {
1539
1539
  });
1540
1540
  } else if (command === "init") {
1541
1541
  const root = process.cwd();
1542
- import("../init-iIiQWzyB.mjs").then(({ runInitCommand, parseInitArgs }) => {
1542
+ import("../init-Br5DTaG7.mjs").then(({ runInitCommand, parseInitArgs }) => {
1543
1543
  let initArgs;
1544
1544
  try {
1545
1545
  initArgs = parseInitArgs(args.slice(1));
@@ -1628,7 +1628,7 @@ else if (command === "-v" || command === "--version") {
1628
1628
  const root = process.cwd();
1629
1629
  switch (cmd) {
1630
1630
  case "init": {
1631
- const { runInitCommand, parseInitArgs } = await import("../init-iIiQWzyB.mjs");
1631
+ const { runInitCommand, parseInitArgs } = await import("../init-Br5DTaG7.mjs");
1632
1632
  await runInitCommand(root, parseInitArgs([]));
1633
1633
  return;
1634
1634
  }
@@ -1646,7 +1646,7 @@ else if (command === "-v" || command === "--version") {
1646
1646
  case "project": return runSubcommandGroup("project", projectSubcommands, parseProjectArgs, (r, p) => import("../project-cmd-CeXO06hG.mjs").then(({ runProjectCommand }) => runProjectCommand(r, p)));
1647
1647
  case "secret": return runSubcommandGroup("secret", secretSubcommands, parseSecretArgs, (r, p) => import("../secret-NDvlqAN2.mjs").then(({ runSecretCommand }) => runSecretCommand(r, p)));
1648
1648
  case "domain": return runSubcommandGroup("domain", domainSubcommands, parseDomainArgs, (r, p) => import("../domain-Dvj7u8tr.mjs").then(({ runDomainCommand }) => runDomainCommand(r, p)));
1649
- case "db": return runSubcommandGroup("db", dbSubcommands, parseDbArgs, (r, p) => import("../db-j-XdTjQD.mjs").then(({ runDbCommand }) => runDbCommand(r, p)));
1649
+ case "db": return runSubcommandGroup("db", dbSubcommands, parseDbArgs, (r, p) => import("../db-YEYSTfGw.mjs").then(({ runDbCommand }) => runDbCommand(r, p)));
1650
1650
  case "env": return runSubcommandGroup("env", envSubcommands, parseEnvArgs, (r, p) => import("../env-Bjl0rzuf.mjs").then(({ runEnvCommand }) => runEnvCommand(r, p)));
1651
1651
  case "gen": return runSubcommandGroup("gen", genSubcommands, parseGenArgs, (r, p) => import("../gen-DU8NIdrH.mjs").then(({ runGenCommand }) => runGenCommand(r, p)));
1652
1652
  case "prepare": {
@@ -100,7 +100,7 @@ async function runPush(root, dialect) {
100
100
  else {
101
101
  const { mkdirSync } = await import("node:fs");
102
102
  const { dirname } = await import("node:path");
103
- const { getMiniflareDatabasePath } = await import("./runner-BaWsik5f.mjs");
103
+ const { getMiniflareDatabasePath } = await import("./runner-C0d3lJo1.mjs");
104
104
  const dbPath = getMiniflareDatabasePath(getLocalD1PersistRoot(root), "local");
105
105
  mkdirSync(dirname(dbPath), { recursive: true });
106
106
  configPath = writeDrizzleConfig(root, dbPath);
@@ -157,7 +157,7 @@ async function runStatus(root, dialect) {
157
157
  localApplied = [];
158
158
  }
159
159
  else {
160
- const { getMiniflareDatabasePath, getAppliedMigrations } = await import("./runner-BaWsik5f.mjs");
160
+ const { getMiniflareDatabasePath, getAppliedMigrations } = await import("./runner-C0d3lJo1.mjs");
161
161
  localApplied = await getAppliedMigrations(getMiniflareDatabasePath(getLocalD1PersistRoot(root), "local"));
162
162
  }
163
163
  const localSet = new Set(localApplied.map((m) => m.name));
@@ -215,7 +215,7 @@ async function resetDatabase(root, dialect) {
215
215
  });
216
216
  return migrations.length;
217
217
  }
218
- const { getMiniflareDatabasePath, runMigrations } = await import("./runner-BaWsik5f.mjs");
218
+ const { getMiniflareDatabasePath, runMigrations } = await import("./runner-C0d3lJo1.mjs");
219
219
  const dbPath = getMiniflareDatabasePath(getLocalD1PersistRoot(root), "local");
220
220
  rmSync(dbPath, { force: true });
221
221
  rmSync(dbPath + "-wal", { force: true });
@@ -242,7 +242,7 @@ async function runSeed(root, file, dialect) {
242
242
  ye("Done.");
243
243
  return;
244
244
  }
245
- const { getMiniflareDatabasePath, openSqliteDatabase } = await import("./runner-BaWsik5f.mjs");
245
+ const { getMiniflareDatabasePath, openSqliteDatabase } = await import("./runner-C0d3lJo1.mjs");
246
246
  const db = await openSqliteDatabase(getMiniflareDatabasePath(getLocalD1PersistRoot(root), "local"));
247
247
  try {
248
248
  db.exec(sql);
@@ -265,7 +265,7 @@ async function runExecute(root, command, file, dialect) {
265
265
  ye("Done.");
266
266
  return;
267
267
  }
268
- const { getMiniflareDatabasePath, openSqliteDatabase } = await import("./runner-BaWsik5f.mjs");
268
+ const { getMiniflareDatabasePath, openSqliteDatabase } = await import("./runner-C0d3lJo1.mjs");
269
269
  const dbPath = getMiniflareDatabasePath(getLocalD1PersistRoot(root), "local");
270
270
  if (!existsSync(dbPath)) {
271
271
  R.error("No local database found. Run `void dev` first.");
@@ -386,7 +386,7 @@ async function runMigrate(root, dialect, remote) {
386
386
  ye("Done.");
387
387
  return;
388
388
  }
389
- const { getMiniflareDatabasePath, getAppliedMigrations, runMigrations } = await import("./runner-BaWsik5f.mjs");
389
+ const { getMiniflareDatabasePath, getAppliedMigrations, runMigrations } = await import("./runner-C0d3lJo1.mjs");
390
390
  const dbPath = getMiniflareDatabasePath(getLocalD1PersistRoot(root), "local");
391
391
  try {
392
392
  const existingRows = await getAppliedMigrations(dbPath);
@@ -421,7 +421,7 @@ async function runStudio(root, dialect) {
421
421
  return;
422
422
  }
423
423
  const { writeDrizzleConfig } = await import("./config-DyUSwbeX.mjs").then((n) => n.t);
424
- const { getMiniflareDatabasePath } = await import("./runner-BaWsik5f.mjs");
424
+ const { getMiniflareDatabasePath } = await import("./runner-C0d3lJo1.mjs");
425
425
  const dbPath = getMiniflareDatabasePath(getLocalD1PersistRoot(root), "local");
426
426
  if (!existsSync(dbPath)) {
427
427
  R.error("No local database found. Run `void dev` first.");
@@ -641,7 +641,7 @@ async function runRenameMigrations(root, dialect) {
641
641
  process.exit(1);
642
642
  }
643
643
  else {
644
- const { getMiniflareDatabasePath, openSqliteDatabase } = await import("./runner-BaWsik5f.mjs");
644
+ const { getMiniflareDatabasePath, openSqliteDatabase } = await import("./runner-C0d3lJo1.mjs");
645
645
  const dbPath = getMiniflareDatabasePath(getLocalD1PersistRoot(root), "local");
646
646
  if (existsSync(dbPath)) try {
647
647
  const db = await openSqliteDatabase(dbPath);
@@ -719,7 +719,7 @@ async function runExport(root, args, dialect) {
719
719
  } else process.stdout.write(output);
720
720
  return;
721
721
  }
722
- const { getMiniflareDatabasePath, openSqliteDatabase } = await import("./runner-BaWsik5f.mjs");
722
+ const { getMiniflareDatabasePath, openSqliteDatabase } = await import("./runner-C0d3lJo1.mjs");
723
723
  const dbPath = getMiniflareDatabasePath(getLocalD1PersistRoot(root), "local");
724
724
  if (!existsSync(dbPath)) {
725
725
  R.error("No local database found. Run `void dev` first.");
@@ -846,7 +846,7 @@ async function runSeedModule(root, seedPath, dialect) {
846
846
  return;
847
847
  }
848
848
  const { drizzle } = await import("drizzle-orm/better-sqlite3");
849
- const { getMiniflareDatabasePath, openSqliteDatabase } = await import("./runner-BaWsik5f.mjs");
849
+ const { getMiniflareDatabasePath, openSqliteDatabase } = await import("./runner-C0d3lJo1.mjs");
850
850
  const sqlite = await openSqliteDatabase(getMiniflareDatabasePath(getLocalD1PersistRoot(root), "local"));
851
851
  try {
852
852
  const options = getDrizzleSchemaOptions(schema);
package/dist/index.mjs CHANGED
@@ -163,6 +163,7 @@ var DevCssProvider = class {
163
163
  const seenCssUrls = /* @__PURE__ */ new Set();
164
164
  const cssUrls = [];
165
165
  const cssViteDevIds = /* @__PURE__ */ new Map();
166
+ const transformedCss = /* @__PURE__ */ new Map();
166
167
  const addByUrl = async (rawUrl) => {
167
168
  const url = normalizeDevCssUrl(rawUrl, this.root);
168
169
  if (isOptimizedDepUrl(url)) return;
@@ -185,8 +186,13 @@ var DevCssProvider = class {
185
186
  seenCssUrls.add(node.url);
186
187
  cssUrls.push(node.url);
187
188
  }
188
- const viteDevId = transformedCode ? extractViteDevId(transformedCode) : void 0;
189
- cssViteDevIds.set(node.url, viteDevId ?? node.id ?? node.url);
189
+ if (transformedCode) {
190
+ const viteDevId = extractViteDevId(transformedCode);
191
+ cssViteDevIds.set(node.url, viteDevId ?? node.id ?? node.url);
192
+ try {
193
+ transformedCss.set(node.url, extractInlineCss(transformedCode));
194
+ } catch {}
195
+ } else cssViteDevIds.set(node.url, node.id ?? node.url);
190
196
  return;
191
197
  }
192
198
  if (isOptimizedDepUrl(node.url)) return;
@@ -200,14 +206,33 @@ var DevCssProvider = class {
200
206
  for (const roots of normalizedPhases) for (const root of roots) await addByUrl(root);
201
207
  const assets = [];
202
208
  for (const url of cssUrls) {
209
+ const existingCss = transformedCss.get(url);
210
+ if (existingCss !== void 0) {
211
+ assets.push({
212
+ url,
213
+ id: cssViteDevIds.get(url) ?? url,
214
+ css: existingCss
215
+ });
216
+ continue;
217
+ }
203
218
  const inlineUrl = appendInlineQuery(url);
204
- const result = await environment.transformRequest(inlineUrl);
219
+ let result;
220
+ try {
221
+ result = await environment.transformRequest(inlineUrl);
222
+ } catch (error) {
223
+ this.server.config.logger.warnOnce(`[void] Skipping dev CSS asset for ${url}: ${error instanceof Error ? error.message : String(error)}`);
224
+ continue;
225
+ }
205
226
  if (!result) continue;
206
- assets.push({
207
- url,
208
- id: cssViteDevIds.get(url) ?? url,
209
- css: extractInlineCss(result.code)
210
- });
227
+ try {
228
+ assets.push({
229
+ url,
230
+ id: cssViteDevIds.get(url) ?? url,
231
+ css: extractInlineCss(result.code)
232
+ });
233
+ } catch (error) {
234
+ this.server.config.logger.warnOnce(`[void] Skipping dev CSS asset for ${url}: ${error instanceof Error ? error.message : String(error)}`);
235
+ }
211
236
  }
212
237
  this.cache.set(cacheKey, assets);
213
238
  if (this.cache.size > 100) {
@@ -564,10 +589,16 @@ export function createDb(d1) {
564
589
  `;
565
590
  }
566
591
  },
567
- configureServer(server) {
592
+ async configureServer(server) {
568
593
  if (!schema) return;
569
594
  const migrationsDir = join(root, "db", "migrations");
570
- if (!(existsSync(migrationsDir) && readdirSync(migrationsDir).some((f) => f.endsWith(".sql")))) voidWarn("Schema found but no migrations in db/migrations/.\n Run `void db push` to apply to local DB, or\n `void db generate` to create migration files.");
595
+ if (!(existsSync(migrationsDir) && readdirSync(migrationsDir).some((f) => f.endsWith(".sql")))) {
596
+ if (dialect === "sqlite") {
597
+ const { getApplicationTables, getMiniflareDatabasePath } = await import("./runner-C0d3lJo1.mjs");
598
+ if ((await getApplicationTables(getMiniflareDatabasePath(options.localD1PersistRoot ?? join(root, ".void"), options.localD1DatabaseId ?? "local")).catch(() => [])).length > 0) return;
599
+ }
600
+ voidWarn("Schema found but no migrations in db/migrations/.\n `void db push` syncs the schema to your local DB without creating migration files.\n Run `void db generate` to create migration files for dev startup and deploy.");
601
+ }
571
602
  const watchPath = schema.path;
572
603
  const regenerate = (path) => {
573
604
  if (path === watchPath || schema.type === "directory" && path.startsWith(watchPath)) generateDbDts(outDir, schema, dialect);
@@ -1551,7 +1582,7 @@ function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join
1551
1582
  const applied = await runPgMigrations(migrationsDir, url);
1552
1583
  if (applied.length > 0) voidLog(`Applied ${applied.length} migration(s): ${applied.join(", ")}`);
1553
1584
  } else if (hasSqlMigrations) {
1554
- const { runMigrations, getMiniflareDatabasePath, getAppliedMigrations } = await import("./runner-BaWsik5f.mjs");
1585
+ const { runMigrations, getMiniflareDatabasePath, getAppliedMigrations } = await import("./runner-C0d3lJo1.mjs");
1555
1586
  const { collectMigrations } = await import("./collect-HqAuilH6.mjs").then((n) => n.n);
1556
1587
  const { validateMigrations } = await import("./validate-DW3ViKTA.mjs").then((n) => n.i);
1557
1588
  const dbPath = getMiniflareDatabasePath(persistRoot, databaseId);
@@ -1568,7 +1599,7 @@ function migrationPlugin(root, dialect, databaseId = "local", persistRoot = join
1568
1599
  if (applied.length > 0) voidLog(`Applied ${applied.length} migration(s): ${applied.join(", ")}`);
1569
1600
  }
1570
1601
  if (auth?.enabled) {
1571
- const { runLocalVoidAuthMigrations } = await import("./node-DDxMsKPm.mjs");
1602
+ const { runLocalVoidAuthMigrations } = await import("./node-gxRQXIRT.mjs");
1572
1603
  await runLocalVoidAuthMigrations(root, dialect, persistRoot, databaseId, auth.configPath);
1573
1604
  voidLog("Applied Better Auth migrations");
1574
1605
  }
@@ -4785,7 +4816,10 @@ function voidPlugin(options) {
4785
4816
  envSchemaPlugin(root),
4786
4817
  envClientGuardPlugin(root),
4787
4818
  envClientFoldPlugin(),
4788
- drizzlePlugin(root, false, dialect, { forceVirtualDb: authEnabled }),
4819
+ drizzlePlugin(root, false, dialect, {
4820
+ forceVirtualDb: authEnabled,
4821
+ localD1PersistRoot: frameworkPersistRoot
4822
+ }),
4789
4823
  migrationPlugin(root, dialect, "local", frameworkPersistRoot, { enabled: false }),
4790
4824
  devTriggersHintPlugin(root, {
4791
4825
  framework: true,
@@ -5061,6 +5095,7 @@ function voidPlugin(options) {
5061
5095
  clientStubsPlugin,
5062
5096
  drizzlePlugin(root, false, getDatabaseDialect(config), {
5063
5097
  forceVirtualDb: authEnabled,
5098
+ localD1PersistRoot: options?.persistTo ?? outDir,
5064
5099
  redirectDbTypePaths
5065
5100
  }),
5066
5101
  migrationPlugin(root, getDatabaseDialect(config)),
@@ -5145,7 +5180,10 @@ function voidPlugin(options) {
5145
5180
  sandboxClassName: sandboxConfig?.className,
5146
5181
  live: liveConfig
5147
5182
  }),
5148
- drizzlePlugin(root, true, dialect, { forceVirtualDb: authEnabled }),
5183
+ drizzlePlugin(root, true, dialect, {
5184
+ forceVirtualDb: authEnabled,
5185
+ localD1PersistRoot: options?.persistTo ?? outDir
5186
+ }),
5149
5187
  migrationPlugin(root, dialect, "local", join(root, ".void"), {
5150
5188
  enabled: authEnabled,
5151
5189
  configPath: authConfigPath
@@ -1,7 +1,7 @@
1
1
  import { a as join, i as isAbsolute, n as dirname, o as relative, s as resolve, t as basename } from "./pathe.M-eThtNZ-D-kmWkCS.mjs";
2
2
  import { n as cliTitle } from "./output-D3g5otMC.mjs";
3
3
  import { c as getToken, n as PlatformClient, u as isStagingMode } from "./client-C_DYiXeu.mjs";
4
- import { _ as me, c as R, g as ge, i as Ee, u as Se, v as ue, x as q, y as ye } from "./dist-D2L3_KTK.mjs";
4
+ import { _ as me, c as R, g as ge, i as Ee, l as Re, u as Se, v as ue, x as q, y as ye } from "./dist-D2L3_KTK.mjs";
5
5
  import { a as writeProjectConfig, r as readProjectConfig } from "./project-DhB4A8_g.mjs";
6
6
  import { c as getDatabaseDialect, f as readConfig } from "./config-CeRQg3Lz.mjs";
7
7
  import { r as detectFramework } from "./plugin-inference-BexV6LL3.mjs";
@@ -208,6 +208,16 @@ const SCAFFOLD_BASE_DEV_DEPENDENCIES = {
208
208
  vite: "^8.0.10",
209
209
  typescript: "^5.9.3"
210
210
  };
211
+ const SCAFFOLD_VITE_PLUS_DEV_DEPENDENCIES = { "vite-plus": "^0.1.21" };
212
+ const SCAFFOLD_TOOLCHAIN_OPTIONS = [{
213
+ value: "vite-plus",
214
+ label: "Vite+",
215
+ hint: "Uses the Vite+ toolchain and vp commands."
216
+ }, {
217
+ value: "vite",
218
+ label: "Vite",
219
+ hint: "Uses plain Vite commands without Vite+."
220
+ }];
211
221
  const REACT_SCAFFOLD_COMPILER_DEV_DEPENDENCIES = {
212
222
  "@rolldown/plugin-babel": "^0.2.3",
213
223
  "@vitejs/plugin-react": "^6.0.1",
@@ -294,7 +304,7 @@ function getScaffoldTsconfigCompilerOptions(framework) {
294
304
  return framework ? { ...SCAFFOLD_FRAMEWORKS[framework].tsconfigCompilerOptions } : {};
295
305
  }
296
306
  function createPagesStarterTemplate(options) {
297
- const { framework, starterMode, voidPackageSpec, adapterPackageSpec } = options;
307
+ const { framework, starterMode, toolchain = "vite", voidPackageSpec, adapterPackageSpec } = options;
298
308
  const config = SCAFFOLD_FRAMEWORKS[framework];
299
309
  const files = {
300
310
  "vite.config.ts": getViteConfig(framework, config),
@@ -308,9 +318,9 @@ function createPagesStarterTemplate(options) {
308
318
  }
309
319
  return {
310
320
  scripts: {
311
- dev: "vite",
312
- build: "vite build",
313
- preview: "vite preview"
321
+ dev: toolchain === "vite-plus" ? "vp dev" : "vite",
322
+ build: toolchain === "vite-plus" ? "vp build" : "vite build",
323
+ preview: toolchain === "vite-plus" ? "vp preview" : "vite preview"
314
324
  },
315
325
  dependencies: {
316
326
  ...SCAFFOLD_BASE_DEPENDENCIES,
@@ -319,6 +329,7 @@ function createPagesStarterTemplate(options) {
319
329
  devDependencies: {
320
330
  void: voidPackageSpec,
321
331
  ...SCAFFOLD_BASE_DEV_DEPENDENCIES,
332
+ ...toolchain === "vite-plus" ? SCAFFOLD_VITE_PLUS_DEV_DEPENDENCIES : {},
322
333
  [config.adapterPackage]: adapterPackageSpec,
323
334
  ...config.devDependencies
324
335
  },
@@ -823,6 +834,53 @@ function isScaffoldableEmptyProject(root) {
823
834
  if (Object.keys(scripts).some((name) => name !== "test")) return false;
824
835
  return true;
825
836
  }
837
+ function shouldPromptForInitLocation(root) {
838
+ if (isScaffoldableEmptyProject(root)) return false;
839
+ if (existsSync(join(root, "package.json"))) return false;
840
+ if (existsSync(join(root, VOID_CONFIG_FILE))) return false;
841
+ return findViteConfig(root) === void 0;
842
+ }
843
+ function validateSubfolderName(root, value) {
844
+ const name = value?.trim() ?? "";
845
+ if (!name) return "Enter a folder name.";
846
+ if (name === "." || name === ".." || name.includes("/") || name.includes("\\")) return "Enter a single folder name, not a path.";
847
+ if (existsSync(join(root, name))) return "That folder already exists.";
848
+ }
849
+ async function resolveInitRoot(root) {
850
+ if (!shouldPromptForInitLocation(root)) return root;
851
+ const choice = await Ee({
852
+ message: "This folder is not empty. Where should Void initialize?",
853
+ initialValue: "subfolder",
854
+ options: [{
855
+ value: "subfolder",
856
+ label: "Create a new subfolder",
857
+ hint: "Scaffold a new app inside this directory."
858
+ }, {
859
+ value: "current",
860
+ label: "Continue here",
861
+ hint: "Add package.json, vite.config.ts, and Void files to this directory."
862
+ }]
863
+ });
864
+ if (q(choice)) {
865
+ me("Setup cancelled.");
866
+ process.exit(0);
867
+ }
868
+ if (choice === "current") return root;
869
+ const folder = await Re({
870
+ message: "New project folder name:",
871
+ placeholder: "my-void-app",
872
+ initialValue: "my-void-app",
873
+ validate: (value) => validateSubfolderName(root, value)
874
+ });
875
+ if (q(folder)) {
876
+ me("Setup cancelled.");
877
+ process.exit(0);
878
+ }
879
+ const nextRoot = join(root, folder.trim());
880
+ mkdirSync(nextRoot, { recursive: true });
881
+ R.info(`Scaffolding in ${nextRoot}`);
882
+ return nextRoot;
883
+ }
826
884
  function parsePackageManagerField(pkg) {
827
885
  if (!pkg || typeof pkg !== "object") return;
828
886
  const raw = pkg.packageManager;
@@ -1566,6 +1624,18 @@ async function promptStarterMode() {
1566
1624
  }
1567
1625
  return choice;
1568
1626
  }
1627
+ async function promptScaffoldToolchain() {
1628
+ const choice = await Ee({
1629
+ message: "Choose a Pages scaffold toolchain:",
1630
+ initialValue: "vite-plus",
1631
+ options: [...SCAFFOLD_TOOLCHAIN_OPTIONS]
1632
+ });
1633
+ if (q(choice)) {
1634
+ me("Setup cancelled.");
1635
+ process.exit(0);
1636
+ }
1637
+ return choice;
1638
+ }
1569
1639
  async function promptScaffoldFramework() {
1570
1640
  const choice = await Ee({
1571
1641
  message: "Choose a Pages framework to scaffold:",
@@ -1850,11 +1920,12 @@ function generateStarterMigration(root, starterMode) {
1850
1920
  }
1851
1921
  R.success("Generated initial Drizzle migration");
1852
1922
  }
1853
- function scaffoldPagesApp(root, packageManager, framework, starterMode) {
1923
+ function scaffoldPagesApp(root, packageManager, framework, starterMode, toolchain) {
1854
1924
  const packageJsonPath = join(root, "package.json");
1855
1925
  const template = createPagesStarterTemplate({
1856
1926
  framework,
1857
1927
  starterMode,
1928
+ toolchain,
1858
1929
  voidPackageSpec: resolveScaffoldDependencySpec(root, "void", getOwnVersion()),
1859
1930
  adapterPackageSpec: resolveScaffoldDependencySpec(root, SCAFFOLD_FRAMEWORKS[framework].adapterPackage, getOwnVersion())
1860
1931
  });
@@ -1893,7 +1964,7 @@ function scaffoldPagesApp(root, packageManager, framework, starterMode) {
1893
1964
  for (const [name, version] of Object.entries(template.dependencies)) ensureDependency("dependencies", name, version);
1894
1965
  for (const [name, version] of Object.entries(template.devDependencies)) ensureDependency("devDependencies", name, version);
1895
1966
  if (isPnpmProject(root, pkg)) ensurePnpmAllowBuilds(root, pkg);
1896
- if (packageManager === "pnpm" && (!pkg.packageManager || typeof pkg.packageManager !== "string")) pkg.packageManager = "pnpm@11.0.9";
1967
+ if (packageManager === "pnpm" && resolveWorkspaceDeferral(root, pkg) === void 0 && (!pkg.packageManager || typeof pkg.packageManager !== "string")) pkg.packageManager = "pnpm@11.0.9";
1897
1968
  writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + "\n");
1898
1969
  R.success("Updated package.json with starter scripts and dependencies");
1899
1970
  for (const [filePath, content] of Object.entries(template.files)) writeFileIfMissing(root, filePath, content);
@@ -2547,6 +2618,7 @@ async function runInitCommand(root, initArgs) {
2547
2618
  console.log();
2548
2619
  ge(cliTitle("init"));
2549
2620
  const targeted = Boolean(initArgs && (initArgs.tsconfig || initArgs.github || initArgs.agents));
2621
+ if (!targeted) root = await resolveInitRoot(root);
2550
2622
  const framework = detectFramework(root);
2551
2623
  const preScaffoldState = detectProjectState(root);
2552
2624
  const detectedPagesFramework = detectPagesFramework(root);
@@ -2558,12 +2630,14 @@ async function runInitCommand(root, initArgs) {
2558
2630
  if (framework) R.info(`Detected ${framework.name} framework`);
2559
2631
  if (!targeted && !framework && isScaffoldableEmptyProject(root)) {
2560
2632
  R.info("Empty project detected. Scaffolding a Pages starter...");
2633
+ const scaffoldToolchain = await promptScaffoldToolchain();
2561
2634
  if (detectedPagesFramework) {
2562
2635
  scaffoldedPagesFramework = detectedPagesFramework;
2563
2636
  R.info(`Detected ${SCAFFOLD_FRAMEWORKS[detectedPagesFramework].label} adapter in package.json`);
2564
2637
  } else scaffoldedPagesFramework = await promptScaffoldFramework();
2565
2638
  const starterMode = await promptStarterMode();
2566
- const scaffoldWroteDeps = scaffoldPagesApp(root, preScaffoldState.packageManager, scaffoldedPagesFramework, starterMode);
2639
+ const scaffoldPackageManager = scaffoldToolchain === "vite-plus" ? "pnpm" : preScaffoldState.packageManager;
2640
+ const scaffoldWroteDeps = scaffoldPagesApp(root, scaffoldPackageManager, scaffoldedPagesFramework, starterMode, scaffoldToolchain);
2567
2641
  const deferral = resolveWorkspaceDeferral(root, readPackageJson(root));
2568
2642
  const voidResolvable = isVoidResolvableFrom(root);
2569
2643
  if (deferral && (!voidResolvable || scaffoldWroteDeps)) {
@@ -3,7 +3,7 @@ import { n as loadVoidAuthConfig, t as findVoidAuthConfig } from "./config-CvHtT
3
3
  import { f as readConfig } from "./config-CeRQg3Lz.mjs";
4
4
  import { r as resolveVoidAuthMigrationSchema } from "./drizzle-Cx7cVwrE.mjs";
5
5
  import { n as applySqliteAuthMigrations, t as applyPgAuthMigrations } from "./auth-migrations-BwLPwRgH.mjs";
6
- import { getMiniflareDatabasePath } from "./runner-BaWsik5f.mjs";
6
+ import { getMiniflareDatabasePath } from "./runner-C0d3lJo1.mjs";
7
7
  import { loadEnv } from "vite";
8
8
  //#region src/auth/node.ts
9
9
  async function runLocalVoidAuthMigrations(root, dialect, persistRoot = join(root, ".void"), databaseId = "local", configPath = findVoidAuthConfig(root)) {
@@ -5,6 +5,11 @@ import { normalizePath } from "vite";
5
5
  //#region src/pages/islands-plugin.ts
6
6
  const ISLANDS_RENDERER_ID = "virtual:void-islands-renderer";
7
7
  const ISLANDS_CLIENT_ENTRY_ID = "virtual:void-islands-client";
8
+ const ISLAND_IMPORT_RE = /import\s+([$_A-Za-z][$_\w]*)\s+from\s+("[^"]+"|'[^']+')\s+with\s*\{\s*island\s*:\s*("[^"]+"|'[^']+')\s*,?\s*\}/g;
9
+ function hasIslandImports(code) {
10
+ ISLAND_IMPORT_RE.lastIndex = 0;
11
+ return ISLAND_IMPORT_RE.test(code);
12
+ }
8
13
  /** Resolve a relative import specifier to an absolute path by checking extensions. */
9
14
  function resolveSpecifier(specifier, importerDir, extensions) {
10
15
  const base = normalizePath(resolve(importerDir, specifier));
@@ -18,7 +23,6 @@ function resolveSpecifier(specifier, importerDir, extensions) {
18
23
  * or any .md file has a `<script>` block.
19
24
  */
20
25
  function needsIslandClientBundle(dir, islandFilePattern, checkStyleBlocks) {
21
- const importAttrRe = /import\s+\w+\s+from\s+("[^"]+"|'[^']+')\s+with\s*\{\s*island\s*:/;
22
26
  const cssImportRe = /import\b[^"']*["'][^"']+\.(?:css|scss|sass|less|styl|stylus|pcss|postcss)["']/;
23
27
  const mdScriptRe = /^<script\b/m;
24
28
  const styleRe = checkStyleBlocks ? /<style\b/ : null;
@@ -31,7 +35,7 @@ function needsIslandClientBundle(dir, islandFilePattern, checkStyleBlocks) {
31
35
  const name = entry.name;
32
36
  if (islandFilePattern.test(name) || name.startsWith("layout.") || name.startsWith("_")) {
33
37
  const content = readFileSync(resolve(entry.parentPath, name), "utf-8");
34
- if (importAttrRe.test(content)) return true;
38
+ if (hasIslandImports(content)) return true;
35
39
  if (styleRe && styleRe.test(content)) return true;
36
40
  if (cssImportRe.test(content)) return true;
37
41
  }
@@ -67,13 +71,10 @@ function extractCssImports(filePath) {
67
71
  function computePagesNeedingClientJS(pageScan, pagesDir, resolvedConfig) {
68
72
  const mdPlugin = resolvedConfig?.plugins?.find((plugin) => plugin.name === "void-md");
69
73
  const mdScriptIds = new Set(mdPlugin?.api?.getClientScripts?.()?.keys() ?? []);
70
- const importAttrRe = /import\s+\w+\s+from\s+("[^"]+"|'[^']+')\s+with\s*\{\s*island\s*:\s*("[^"]+"|'[^']+')\s*\}/g;
71
74
  const layoutDirsWithIslands = /* @__PURE__ */ new Set();
72
75
  for (const layout of pageScan.layouts) {
73
76
  if (!layout.island) continue;
74
- const code = readFileSync(resolve(pagesDir, layout.filePath), "utf-8");
75
- importAttrRe.lastIndex = 0;
76
- if (importAttrRe.test(code)) layoutDirsWithIslands.add(layout.directory);
77
+ if (hasIslandImports(readFileSync(resolve(pagesDir, layout.filePath), "utf-8"))) layoutDirsWithIslands.add(layout.directory);
77
78
  }
78
79
  const result = /* @__PURE__ */ new Set();
79
80
  for (const page of pageScan.pages) {
@@ -82,9 +83,7 @@ function computePagesNeedingClientJS(pageScan, pagesDir, resolvedConfig) {
82
83
  result.add(page.componentId);
83
84
  continue;
84
85
  }
85
- const code = readFileSync(resolve(pagesDir, page.componentPath), "utf-8");
86
- importAttrRe.lastIndex = 0;
87
- if (importAttrRe.test(code)) {
86
+ if (hasIslandImports(readFileSync(resolve(pagesDir, page.componentPath), "utf-8"))) {
88
87
  result.add(page.componentId);
89
88
  continue;
90
89
  }
@@ -3,6 +3,12 @@ import { t as collectMigrations } from "./collect-HqAuilH6.mjs";
3
3
  import { existsSync, mkdirSync } from "node:fs";
4
4
  import { createHash, createHmac } from "node:crypto";
5
5
  //#region src/migrations/runner.ts
6
+ const SQLITE_INTERNAL_TABLES = new Set([
7
+ "_cf_METADATA",
8
+ "_migrations",
9
+ "_void_migrations",
10
+ "__drizzle_migrations"
11
+ ]);
6
12
  function toBetterSqlite3Error(error) {
7
13
  const message = error instanceof Error ? error.message : String(error);
8
14
  if (!message.includes("Could not locate the bindings file")) return error instanceof Error ? error : new Error(message);
@@ -70,6 +76,39 @@ function upgradeTrackingTable(db) {
70
76
  db.exec(`DROP TABLE IF EXISTS _migrations`);
71
77
  }).immediate();
72
78
  }
79
+ function getApplicationTableNames(db) {
80
+ return db.prepare("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name").all().map((row) => row.name).filter((name) => !name.startsWith("sqlite_") && !SQLITE_INTERNAL_TABLES.has(name));
81
+ }
82
+ async function getApplicationTables(dbPath) {
83
+ if (!existsSync(dbPath)) return [];
84
+ const db = await openSqliteDatabase(dbPath, { readonly: true });
85
+ try {
86
+ return getApplicationTableNames(db);
87
+ } finally {
88
+ db.close();
89
+ }
90
+ }
91
+ function assertLocalMigrationState(migrations, appliedNames, applicationTables) {
92
+ const migrationNames = new Set(migrations.map((migration) => migration.name));
93
+ const missingFiles = [...appliedNames].filter((name) => !migrationNames.has(name)).sort();
94
+ if (missingFiles.length > 0) throw new Error([
95
+ "Local database migration history does not match db/migrations/.",
96
+ "",
97
+ "These migrations are recorded as applied locally but no longer exist on disk:",
98
+ ...missingFiles.map((name) => ` - ${name}`),
99
+ "",
100
+ "If the migration files were renamed, run `void db rename-migrations`.",
101
+ "If the migration history was intentionally rewritten, run `void db reset` to recreate the local database from the current files."
102
+ ].join("\n"));
103
+ if (appliedNames.size === 0 && migrations.length > 0 && applicationTables.length > 0) throw new Error([
104
+ "Local database already has application tables but no Void migration history.",
105
+ "",
106
+ `Existing tables: ${applicationTables.join(", ")}`,
107
+ "",
108
+ "This usually means the schema was applied with `void db push` or the local database was created before migration tracking existed.",
109
+ "Run `void db reset` to recreate the local database from db/migrations/, or delete the local D1 state if you do not need its data."
110
+ ].join("\n"));
111
+ }
73
112
  /**
74
113
  * Apply unapplied SQL migrations from the migrations directory.
75
114
  * Tracks applied migrations in the `_void_migrations` table.
@@ -103,6 +142,7 @@ async function runMigrations(migrationsDir, dbPath) {
103
142
  )
104
143
  `);
105
144
  const appliedSet = new Set(db.prepare("SELECT name FROM _void_migrations").all().map((r) => r.name));
145
+ assertLocalMigrationState(allMigrations, appliedSet, getApplicationTableNames(db));
106
146
  const pending = allMigrations.filter((m) => !appliedSet.has(m.name));
107
147
  if (pending.length === 0) return [];
108
148
  const insertStmt = db.prepare("INSERT INTO _void_migrations (name) VALUES (?)");
@@ -120,4 +160,4 @@ async function runMigrations(migrationsDir, dbPath) {
120
160
  }
121
161
  }
122
162
  //#endregion
123
- export { getAppliedMigrations, getMiniflareDatabasePath, openSqliteDatabase, runMigrations };
163
+ export { getApplicationTables, getAppliedMigrations, getMiniflareDatabasePath, openSqliteDatabase, runMigrations };
@@ -63,7 +63,8 @@ async function runD1Migrations(db, migrations) {
63
63
  const start = Date.now();
64
64
  try {
65
65
  const statements = splitMigrationSql(m.sql);
66
- if (statements.length === 1 && statements[0] === m.sql) await db.exec(m.sql);
66
+ if (statements.length === 1) if (isSingleSqlStatement(statements[0])) await db.prepare(statements[0]).run();
67
+ else await db.exec(m.sql);
67
68
  else for (const statement of statements) await db.prepare(statement).run();
68
69
  await db.prepare("INSERT OR IGNORE INTO _void_migrations (name, applied_at) VALUES (?, ?)").bind(m.name, (/* @__PURE__ */ new Date()).toISOString()).run();
69
70
  migrationResults.push({
@@ -121,5 +122,71 @@ function isStatementBreakpointLine(line) {
121
122
  function hasInlineStatementBreakpoint(line) {
122
123
  return /;\s*-->\s*statement-breakpoint\s*$/.test(line.trim());
123
124
  }
125
+ function isSingleSqlStatement(sql) {
126
+ let statements = 0;
127
+ let inStatement = false;
128
+ let index = 0;
129
+ while (index < sql.length) {
130
+ const character = sql[index];
131
+ const next = index + 1 < sql.length ? sql[index + 1] : "";
132
+ if (character === "-" && next === "-") {
133
+ index += 2;
134
+ while (index < sql.length && sql[index] !== "\n") index++;
135
+ continue;
136
+ }
137
+ if (character === "/" && next === "*") {
138
+ index += 2;
139
+ while (index < sql.length - 1 && !(sql[index] === "*" && sql[index + 1] === "/")) index++;
140
+ index = Math.min(index + 2, sql.length);
141
+ continue;
142
+ }
143
+ if (character === "'") {
144
+ inStatement = true;
145
+ index++;
146
+ while (index < sql.length) {
147
+ if (sql[index] === "'" && sql[index + 1] === "'") {
148
+ index += 2;
149
+ continue;
150
+ }
151
+ if (sql[index] === "'") {
152
+ index++;
153
+ break;
154
+ }
155
+ index++;
156
+ }
157
+ continue;
158
+ }
159
+ if (character === "\"" || character === "`" || character === "[") {
160
+ inStatement = true;
161
+ const close = character === "[" ? "]" : character;
162
+ index++;
163
+ while (index < sql.length) {
164
+ if (close !== "]" && sql[index] === close && sql[index + 1] === close) {
165
+ index += 2;
166
+ continue;
167
+ }
168
+ if (sql[index] === close) {
169
+ index++;
170
+ break;
171
+ }
172
+ index++;
173
+ }
174
+ continue;
175
+ }
176
+ if (character === ";") {
177
+ if (inStatement) {
178
+ statements++;
179
+ if (statements > 1) return false;
180
+ inStatement = false;
181
+ }
182
+ index++;
183
+ continue;
184
+ }
185
+ if (character !== " " && character !== " " && character !== "\n" && character !== "\r") inStatement = true;
186
+ index++;
187
+ }
188
+ if (inStatement) statements++;
189
+ return statements === 1;
190
+ }
124
191
  //#endregion
125
192
  export { createMigrationHandler };
@@ -7,9 +7,9 @@ Then run:
7
7
 
8
8
  npx void init
9
9
 
10
- In an empty directory, `void init` will add the matching Pages adapter and starter dependencies after you choose a framework. In an existing app, it configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`.
10
+ In an empty directory, `void init` will add the matching Pages adapter and starter dependencies after you choose a scaffold toolchain and framework. Vite+ is the default toolchain. In an existing app, it configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`.
11
11
 
12
- During `void init`, first choose a Pages framework (React, Vue, Svelte, or Solid), then choose D1 for a zero-config, fully managed default or PostgreSQL if you already have Postgres infrastructure or expect heavier writes and more complex queries.
12
+ During `void init`, choose Vite+ unless you specifically want plain Vite scripts, then choose a Pages framework (React, Vue, Svelte, or Solid). Choose D1 for a zero-config, fully managed default or PostgreSQL if you already have Postgres infrastructure or expect heavier writes and more complex queries.
13
13
 
14
14
  At the end of the interactive flow, `void init` can also log you in and link or create your Void project so your first deploy is just:
15
15
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "void",
3
- "version": "0.7.11",
3
+ "version": "0.7.13",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/voidzero-dev/void.git",
@@ -332,7 +332,7 @@
332
332
  "valibot": ">=1.0.0-beta.7",
333
333
  "vite": "^8.0.0",
334
334
  "zod": "^3.25.0 || ^4.0.0",
335
- "@void/md": "0.7.11"
335
+ "@void/md": "0.7.13"
336
336
  },
337
337
  "peerDependenciesMeta": {
338
338
  "@void/md": {
@@ -13,7 +13,7 @@ Docs in this skill are bundled from `docs/` during `void` package build and live
13
13
 
14
14
  ## Command Naming
15
15
 
16
- Use `void` in examples and commands in this skill. For first-time setup, prefer `void init` followed by `void deploy`; in an empty directory, install `void` first and let `void init` add the matching Pages adapter and starter dependencies. In an existing app, `void init` configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`. `void init` can also handle auth and project linking interactively.
16
+ Use `void` in examples and commands in this skill. For first-time setup, prefer `void init` followed by `void deploy`; in an empty directory, install `void` first and let `void init` add the matching Pages adapter and starter dependencies with Vite+ as the default scaffold toolchain. In an existing app, `void init` configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`. `void init` can also handle auth and project linking interactively.
17
17
 
18
18
  Use `void` and `@void/*` in code examples and package manifests.
19
19
 
@@ -12,7 +12,7 @@ Pages mode is also entirely optional - you can use any client-side router to bui
12
12
 
13
13
  ## Setup
14
14
 
15
- If you start in a scaffoldable empty directory, `void init` can generate this setup for you: it asks whether you want React, Vue, Svelte, or Solid Pages mode, then lets you pick a D1 starter, a PostgreSQL starter, or Static Pages. The database-backed starters write the adapter-aware `vite.config.ts`, `pages/`, and `db/` starter files; Static Pages writes just the basic `pages/` setup so you can grow into server features later.
15
+ If you start in a scaffoldable empty directory, `void init` can generate this setup for you: it asks whether to scaffold with Vite+ (the default) or plain Vite, asks whether you want React, Vue, Svelte, or Solid Pages mode, then lets you pick a D1 starter, a PostgreSQL starter, or Static Pages. The database-backed starters write the adapter-aware `vite.config.ts`, `pages/`, and `db/` starter files; Static Pages writes just the basic `pages/` setup so you can grow into server features later.
16
16
 
17
17
  If you're adding Pages mode manually, install a framework adapter alongside `void`:
18
18
 
@@ -32,9 +32,9 @@ yarn add -D void
32
32
  bun add -D void
33
33
  ```
34
34
 
35
- In an empty directory, `void init` adds the matching Pages adapter and starter dependencies after you choose a framework.
35
+ In an empty directory, `void init` adds the matching Pages adapter and starter dependencies after you choose a scaffold toolchain and framework. Vite+ is the default toolchain and uses `vp` scripts.
36
36
 
37
- As part of `void init`, you'll choose a Pages framework (React, Vue, Svelte, or Solid) and a starter type. D1 is the default top option and scaffolds a DB-backed page loader, schema, generated migration, `db/seed.ts`, and API route. PostgreSQL scaffolds the same starter and writes `"database": "pg"` to `void.json`. Static Pages skips the database and server starter files so you can start with static content and add Void features later.
37
+ As part of `void init`, you'll choose Vite+ or plain Vite, then a Pages framework (React, Vue, Svelte, or Solid) and a starter type. D1 is the default top option and scaffolds a DB-backed page loader, schema, generated migration, `db/seed.ts`, and API route. PostgreSQL scaffolds the same starter and writes `"database": "pg"` to `void.json`. Static Pages skips the database and server starter files so you can start with static content and add Void features later.
38
38
 
39
39
  After installation, run the setup flow:
40
40
 
@@ -1,6 +1,6 @@
1
1
  ## Void
2
2
 
3
- This project uses [Void](https://void.cloud) — a fullstack Vite plugin + deployment platform for Cloudflare. `voidPlugin()` in `vite.config.ts` gives you file-based API routing on Hono (`routes/`), Inertia-inspired server-rendered pages with co-located loaders/actions (`pages/` + `@void/vue` or `@void/react`), auto-provisioned D1/KV/R2 bindings, first-class Drizzle ORM integration (schema in `db/schema.ts` -> `void/db` Drizzle instance -> typed routes -> typed fetch client), built-in auth, queues, cron jobs, edge caching (ISR), and one-command deploys via `npx void deploy`. For first-time setup, prefer `npx void init`; in an empty directory, install `void` first and let the interactive flow scaffold the starter, add the matching framework adapter, configure project files, handle auth, and link or create the deploy project before the first deploy. In an existing app, `void init` configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`. Use `void` and `@void/*` package names in source code and package manifests.
3
+ This project uses [Void](https://void.cloud) — a fullstack Vite plugin + deployment platform for Cloudflare. `voidPlugin()` in `vite.config.ts` gives you file-based API routing on Hono (`routes/`), Inertia-inspired server-rendered pages with co-located loaders/actions (`pages/` + `@void/vue` or `@void/react`), auto-provisioned D1/KV/R2 bindings, first-class Drizzle ORM integration (schema in `db/schema.ts` -> `void/db` Drizzle instance -> typed routes -> typed fetch client), built-in auth, queues, cron jobs, edge caching (ISR), and one-command deploys via `npx void deploy`. For first-time setup, prefer `npx void init`; in an empty directory, install `void` first and let the interactive flow scaffold the starter with Vite+ by default, add the matching framework adapter, configure project files, handle auth, and link or create the deploy project before the first deploy. In an existing app, `void init` configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`. Use `void` and `@void/*` package names in source code and package manifests.
4
4
 
5
5
  Database: define Drizzle tables in `db/schema.ts`, import `db` from `void/db` and tables from `@schema`. Use `void db push` for prototyping, `void db generate` for production migrations. `drizzle-orm` and `drizzle-kit` ship with void (no extra install). Migrations live in `db/migrations/`.
6
6
 
@@ -21,7 +21,7 @@ Install the Void CLI:
21
21
  npm install -D void
22
22
  ```
23
23
 
24
- If you run `void init` in an empty directory, the scaffold flow adds the matching Pages adapter and Vite dependencies for you.
24
+ If you run `void init` in an empty directory, the scaffold flow adds the matching Pages adapter and Vite+ by default.
25
25
 
26
26
  Then run `npx void init`. In an empty directory, the full interactive flow can scaffold a starter, configure local project files, log you in, and link or create your Void project so the next step is just `npx void deploy`. In an existing app, it configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`.
27
27
 
@@ -82,7 +82,7 @@ There is Vinext-specific source in the repo, but it is intentionally on hold and
82
82
 
83
83
  ## Documentation
84
84
 
85
- Full docs live at [void.cloud/docs](https://void.cloud/docs).
85
+ Full docs live at [void.cloud/guide](https://void.cloud/guide).
86
86
 
87
87
  ## License
88
88
 
@@ -13,7 +13,7 @@ Docs in this skill are bundled from `docs/` during `void` package build and live
13
13
 
14
14
  ## Command Naming
15
15
 
16
- Use `void` in examples and commands in this skill. For first-time setup, prefer `void init` followed by `void deploy`; in an empty directory, install `void` first and let `void init` add the matching Pages adapter and starter dependencies. In an existing app, `void init` configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`. `void init` can also handle auth and project linking interactively.
16
+ Use `void` in examples and commands in this skill. For first-time setup, prefer `void init` followed by `void deploy`; in an empty directory, install `void` first and let `void init` add the matching Pages adapter and starter dependencies with Vite+ as the default scaffold toolchain. In an existing app, `void init` configures Void in place by adding missing Vite scripts and creating or patching `vite.config.*` with `voidPlugin()`. `void init` can also handle auth and project linking interactively.
17
17
 
18
18
  Use `void` and `@void/*` in code examples and package manifests.
19
19
 
@@ -66,7 +66,9 @@ void init [--tsconfig] [--github] [--agents]
66
66
 
67
67
  Setup wizard for Void projects (new or existing).
68
68
 
69
- If run in a scaffoldable empty directory, `void init` scaffolds a Pages starter. If a single Pages adapter is already installed, it reuses that framework; otherwise it asks which framework to scaffold (React, Vue, Svelte, or Solid). It then asks which starter you want: D1, PostgreSQL, or Static Pages. The D1 and PostgreSQL starters write a framework-specific `vite.config.ts`, a `pages/` home page plus `.server.ts` loader, `db/schema.ts`, `db/seed.ts`, a generated initial migration under `db/migrations/`, and `routes/api/hello.ts`. The Static Pages starter writes just the framework-specific `vite.config.ts` and a `pages/` home page so you can add server features later.
69
+ If run in a scaffoldable empty directory, `void init` scaffolds a Pages starter. It asks which scaffold toolchain to use, with Vite+ as the default top option and plain Vite as the alternative. If a single Pages adapter is already installed, it reuses that framework; otherwise it asks which framework to scaffold (React, Vue, Svelte, or Solid). It then asks which starter you want: D1, PostgreSQL, or Static Pages. The D1 and PostgreSQL starters write a framework-specific `vite.config.ts`, a `pages/` home page plus `.server.ts` loader, `db/schema.ts`, `db/seed.ts`, a generated initial migration under `db/migrations/`, and `routes/api/hello.ts`. The Static Pages starter writes just the framework-specific `vite.config.ts` and a `pages/` home page so you can add server features later. Vite+ starters add `vite-plus` and use `vp dev`, `vp build`, and `vp preview` scripts.
70
+
71
+ If run in a non-empty folder that does not look like an app yet, such as a parent `Projects/` folder with subdirectories but no `package.json`, `void init` asks whether to create a new subfolder or continue in the current folder. Creating a subfolder is the default selection.
70
72
 
71
73
  If run in an existing project, `void init` configures the project in place: it ensures `void` and `vite` are declared, adds missing `dev` (`vite`) and `build` (`vite build`) scripts without overwriting existing scripts, and creates or patches `vite.config.*` with `voidPlugin()` when the config shape is safe to edit. If the Vite config is too dynamic to patch confidently, it prints the manual snippet instead of rewriting it.
72
74