@jant/core 0.3.30 → 0.3.31

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.
@@ -0,0 +1,112 @@
1
+ import { execSync } from "node:child_process";
2
+ import { writeFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import { parseArgs } from "node:util";
5
+
6
+ function sqlValue(v) {
7
+ if (v === null) return "NULL";
8
+ if (typeof v === "number") return String(v);
9
+ return "'" + String(v).replaceAll("'", "''") + "'";
10
+ }
11
+
12
+ function query(sql, flag) {
13
+ let stdout;
14
+ try {
15
+ stdout = execSync(
16
+ `npx wrangler d1 execute DB ${flag} --command "${sql}" --json`,
17
+ { encoding: "utf-8" },
18
+ );
19
+ } catch (err) {
20
+ const output = err.stdout || err.stderr || "";
21
+ try {
22
+ const errJson = JSON.parse(output.trim());
23
+ if (errJson.error?.text) {
24
+ console.error(`Wrangler error: ${errJson.error.text}`);
25
+ process.exit(1);
26
+ }
27
+ } catch {
28
+ // Not JSON, fall through
29
+ }
30
+ console.error(`Failed to query database: ${output || err.message}`);
31
+ process.exit(1);
32
+ }
33
+ const parsed = JSON.parse(stdout);
34
+ if (parsed.error?.text) {
35
+ console.error(`Wrangler error: ${parsed.error.text}`);
36
+ process.exit(1);
37
+ }
38
+ return parsed[0]?.results || [];
39
+ }
40
+
41
+ function dumpTable(name, flag, customQuery) {
42
+ const rows = query(customQuery || `SELECT * FROM ${name}`, flag);
43
+ return rows
44
+ .map(
45
+ (row) =>
46
+ `INSERT INTO ${name} VALUES(${Object.values(row).map(sqlValue).join(",")});`,
47
+ )
48
+ .join("\n");
49
+ }
50
+
51
+ export async function run(argv) {
52
+ const { values } = parseArgs({
53
+ args: argv,
54
+ options: {
55
+ remote: { type: "boolean", default: false },
56
+ output: { type: "string", short: "o", default: "jant-export.sql" },
57
+ help: { type: "boolean", short: "h" },
58
+ },
59
+ });
60
+
61
+ if (values.help) {
62
+ console.log("Usage: jant export [--remote] [--output <file>]");
63
+ console.log("");
64
+ console.log("Export D1 database to a SQL file.");
65
+ console.log("");
66
+ console.log("Options:");
67
+ console.log(
68
+ " --remote Export from remote D1 database (default: local)",
69
+ );
70
+ console.log(
71
+ " --output, -o Output file path (default: jant-export.sql)",
72
+ );
73
+ process.exit(0);
74
+ }
75
+
76
+ const flag = values.remote ? "--remote" : "--local";
77
+ const output = values.output;
78
+
79
+ // Order matters for foreign key constraints
80
+ const tables = [
81
+ // Auth
82
+ ["settings"],
83
+ ["user"],
84
+ ["account"],
85
+ // Content
86
+ ["pages"],
87
+ ["collections"],
88
+ ["posts", "SELECT * FROM posts WHERE deleted_at IS NULL"],
89
+ ["post_collections"],
90
+ ["collection_dividers"],
91
+ ["nav_items"],
92
+ ["media"],
93
+ ["redirects"],
94
+ ];
95
+
96
+ const timestamp = new Date().toISOString();
97
+ const source = values.remote ? "remote" : "local";
98
+ let sql = `-- Jant database export\n`;
99
+ sql += `-- Exported: ${timestamp}\n`;
100
+ sql += `-- Source: ${source}\n\n`;
101
+
102
+ for (const [name, customQuery] of tables) {
103
+ const data = dumpTable(name, flag, customQuery);
104
+ if (data) {
105
+ sql += `-- ${name}\n${data}\n\n`;
106
+ }
107
+ }
108
+
109
+ const outPath = resolve(process.cwd(), output);
110
+ writeFileSync(outPath, sql);
111
+ console.log(`Exported ${source} database to ${output}`);
112
+ }
@@ -0,0 +1,40 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { execSync } from "node:child_process";
3
+ import { parseArgs } from "node:util";
4
+
5
+ export async function run(argv) {
6
+ const { values } = parseArgs({
7
+ args: argv,
8
+ options: {
9
+ remote: { type: "boolean", default: false },
10
+ help: { type: "boolean", short: "h" },
11
+ },
12
+ });
13
+
14
+ if (values.help) {
15
+ console.log("Usage: jant reset-password [--remote]");
16
+ console.log("");
17
+ console.log("Generate a password reset token (expires in 15 minutes).");
18
+ console.log("");
19
+ console.log("Options:");
20
+ console.log(" --remote Run against remote D1 database (default: local)");
21
+ process.exit(0);
22
+ }
23
+
24
+ const flag = values.remote ? "--remote" : "--local";
25
+
26
+ const token = randomBytes(32).toString("hex");
27
+ const expiry = Math.floor(Date.now() / 1000) + 15 * 60;
28
+ const value = `${token}:${expiry}`;
29
+ const timestamp = Math.floor(Date.now() / 1000);
30
+
31
+ const sql = `INSERT OR REPLACE INTO settings (key, value, updated_at) VALUES ('PASSWORD_RESET_TOKEN', '${value}', ${timestamp})`;
32
+
33
+ execSync(`npx wrangler d1 execute DB ${flag} --command "${sql}"`, {
34
+ stdio: "inherit",
35
+ });
36
+
37
+ console.log("");
38
+ console.log("Password reset token generated (expires in 15 minutes).");
39
+ console.log(`Visit: /reset?token=${token}`);
40
+ }
package/bin/jant.js ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readdir } from "node:fs/promises";
4
+ import { fileURLToPath } from "node:url";
5
+ import { dirname, join, basename } from "node:path";
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const commandsDir = join(__dirname, "commands");
9
+
10
+ async function listCommands() {
11
+ const files = await readdir(commandsDir);
12
+ return files
13
+ .filter((f) => f.endsWith(".js"))
14
+ .map((f) => basename(f, ".js"));
15
+ }
16
+
17
+ async function showHelp() {
18
+ const commands = await listCommands();
19
+ console.log("Usage: jant <command> [options]");
20
+ console.log("");
21
+ console.log("Commands:");
22
+ for (const cmd of commands) {
23
+ console.log(` ${cmd}`);
24
+ }
25
+ console.log("");
26
+ console.log("Run 'jant <command> --help' for command-specific help.");
27
+ }
28
+
29
+ // First non-flag argument is the command name
30
+ const argv = process.argv.slice(2);
31
+ const command = argv.find((arg) => !arg.startsWith("-"));
32
+
33
+ if (!command) {
34
+ await showHelp();
35
+ process.exit(0);
36
+ }
37
+
38
+ const commands = await listCommands();
39
+ if (!commands.includes(command)) {
40
+ console.error(`Unknown command: ${command}`);
41
+ console.error("");
42
+ await showHelp();
43
+ process.exit(1);
44
+ }
45
+
46
+ // Pass everything after the command name to the subcommand
47
+ const commandIndex = argv.indexOf(command);
48
+ const mod = await import(join(commandsDir, `${command}.js`));
49
+ await mod.run(argv.slice(commandIndex + 1));
package/dist/index.js CHANGED
@@ -4692,7 +4692,7 @@ const I18nProvider = ({ c, children })=>{
4692
4692
  }
4693
4693
 
4694
4694
  const IS_VITE_DEV = typeof __JANT_DEV__ !== "undefined" && __JANT_DEV__ === true;
4695
- const CORE_VERSION = "0.3.30";
4695
+ const CORE_VERSION = "0.3.31";
4696
4696
 
4697
4697
  const BaseLayout = ({ title, description, lang, c, toast, faviconUrl, faviconVersion, noindex, isAuthenticated = false, children })=>{
4698
4698
  // Read lang from Hono context if available, otherwise use prop or default
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jant/core",
3
- "version": "0.3.30",
3
+ "version": "0.3.31",
4
4
  "description": "A modern, open-source microblogging platform built on Cloudflare Workers",
5
5
  "type": "module",
6
6
  "exports": {
@@ -11,7 +11,7 @@
11
11
  "./i18n": "./src/i18n/index.ts"
12
12
  },
13
13
  "bin": {
14
- "jant-reset-password": "bin/reset-password.js"
14
+ "jant": "bin/jant.js"
15
15
  },
16
16
  "files": [
17
17
  "bin",
@@ -1,22 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { randomBytes } from "crypto";
4
- import { execSync } from "child_process";
5
-
6
- const isRemote = process.argv.includes("--remote");
7
- const flag = isRemote ? "--remote" : "--local";
8
-
9
- const token = randomBytes(32).toString("hex");
10
- const expiry = Math.floor(Date.now() / 1000) + 15 * 60; // 15 minutes
11
- const value = `${token}:${expiry}`;
12
- const timestamp = Math.floor(Date.now() / 1000);
13
-
14
- const sql = `INSERT OR REPLACE INTO settings (key, value, updated_at) VALUES ('PASSWORD_RESET_TOKEN', '${value}', ${timestamp})`;
15
-
16
- execSync(`npx wrangler d1 execute DB ${flag} --command "${sql}"`, {
17
- stdio: "inherit",
18
- });
19
-
20
- console.log("");
21
- console.log("Password reset token generated (expires in 15 minutes).");
22
- console.log(`Visit: /reset?token=${token}`);