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 +1 -1
- package/README.md +2 -2
- package/dist/cli/cli.mjs +4 -4
- package/dist/{db-j-XdTjQD.mjs → db-YEYSTfGw.mjs} +10 -10
- package/dist/index.mjs +52 -14
- package/dist/{init-iIiQWzyB.mjs → init-Br5DTaG7.mjs} +82 -8
- package/dist/{node-DDxMsKPm.mjs → node-gxRQXIRT.mjs} +1 -1
- package/dist/pages/islands-plugin.mjs +8 -9
- package/dist/{runner-BaWsik5f.mjs → runner-C0d3lJo1.mjs} +41 -1
- package/dist/runtime/migration-handler.mjs +68 -1
- package/getting-started-prompt.txt +2 -2
- package/package.json +2 -2
- package/skills/void/SKILL.md +1 -1
- package/skills/void/docs/guide/pages-routing/overview.md +1 -1
- package/skills/void/docs/guide/quickstart.md +2 -2
- package/skills/void/docs/node_modules/void/AGENT_PROMPT.md +1 -1
- package/skills/void/docs/node_modules/void/README.md +2 -2
- package/skills/void/docs/node_modules/void/skills/void/SKILL.md +1 -1
- package/skills/void/docs/reference/cli.md +3 -1
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
|
|
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/
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
189
|
-
|
|
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
|
-
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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"))))
|
|
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-
|
|
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-
|
|
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, {
|
|
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, {
|
|
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
|
|
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-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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`,
|
|
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.
|
|
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.
|
|
335
|
+
"@void/md": "0.7.13"
|
|
336
336
|
},
|
|
337
337
|
"peerDependenciesMeta": {
|
|
338
338
|
"@void/md": {
|
package/skills/void/SKILL.md
CHANGED
|
@@ -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
|
|
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/
|
|
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
|
|