create-nexwire 4.2.4 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/README.md +20 -8
  2. package/express/.env +1 -1
  3. package/express/.vscode/extensions.json +6 -0
  4. package/express/.vscode/settings.json +37 -26
  5. package/express/biome.json +99 -66
  6. package/express/package.json +31 -29
  7. package/express/src/config/database.ts +48 -29
  8. package/express/src/config/index.ts +83 -83
  9. package/express/src/config/openapi.ts +6 -24
  10. package/express/src/config/queue.ts +30 -30
  11. package/express/src/config/validate.ts +50 -40
  12. package/express/src/database/migrations/sqlite/0000_init.sql +47 -47
  13. package/express/src/database/migrations/sqlite/meta/0000_snapshot.json +1 -1
  14. package/express/src/database/migrations/sqlite/meta/_journal.json +1 -1
  15. package/express/src/database/schema.ts +2 -2
  16. package/express/src/framework/cache/cache.ts +40 -8
  17. package/express/src/framework/circuit-breaker/cb.ts +88 -60
  18. package/express/src/framework/database/connection.ts +52 -27
  19. package/express/src/framework/events/dispatcher.ts +61 -49
  20. package/express/src/framework/facade.ts +46 -3
  21. package/express/src/framework/http/app.ts +28 -22
  22. package/express/src/framework/http/logger.ts +27 -16
  23. package/express/src/framework/http/openapi.ts +9 -11
  24. package/express/src/framework/modules/routes.ts +26 -21
  25. package/express/src/framework/notification/channels/broadcast.ts +15 -0
  26. package/express/src/framework/notification/channels/mail.ts +40 -0
  27. package/express/src/framework/notification/contracts.ts +29 -0
  28. package/express/src/framework/notification/index.ts +19 -94
  29. package/express/src/framework/notification/registry.ts +15 -0
  30. package/express/src/framework/providers/adapter.ts +27 -17
  31. package/express/src/framework/queue/queue.ts +43 -44
  32. package/express/src/framework/realtime/broadcast.ts +29 -5
  33. package/express/src/framework/realtime/socket.ts +149 -119
  34. package/express/src/framework/redis/client.ts +148 -151
  35. package/express/src/framework/runtime/server-adapter.ts +39 -34
  36. package/express/src/framework/scheduler/lock.ts +128 -116
  37. package/express/src/framework/server.ts +33 -49
  38. package/express/src/framework/session/session.ts +69 -14
  39. package/express/src/framework/storage/storage.ts +744 -785
  40. package/express/src/framework/support/mail.ts +15 -7
  41. package/express/src/providers/notification.ts +4 -0
  42. package/express/src/resources/src/components/Checkbox.vue +32 -29
  43. package/express/src/resources/src/components/Input.vue +243 -223
  44. package/express/src/resources/src/components/InputGroup.vue +147 -145
  45. package/express/src/resources/src/components/InputPasswordToggle.vue +229 -213
  46. package/express/src/resources/src/components/Modal.vue +71 -55
  47. package/express/src/resources/src/components/Refresh.vue +19 -0
  48. package/express/src/resources/src/helpers/assets.ts +51 -0
  49. package/express/src/resources/src/layouts/Layout/Header.vue +124 -105
  50. package/express/src/resources/src/main.ts +3 -1
  51. package/express/src/resources/src/plugins/gum.ts +1 -1
  52. package/express/src/resources/src/plugins/pulse.ts +157 -147
  53. package/express/src/resources/src/stores/auth.ts +184 -164
  54. package/express/src/resources/vite.config.ts +54 -36
  55. package/express/src/storage/database/nexwire.sqlite +0 -0
  56. package/express/src/storage/logs/app.log +0 -0
  57. package/express/src/storage/logs/fatal.log +0 -0
  58. package/express/src/types/global.d.ts +32 -26
  59. package/hono/.env +1 -1
  60. package/hono/.vscode/extensions.json +6 -0
  61. package/hono/.vscode/settings.json +37 -26
  62. package/hono/biome.json +25 -2
  63. package/hono/package.json +25 -23
  64. package/hono/src/config/database.ts +48 -29
  65. package/hono/src/config/index.ts +83 -83
  66. package/hono/src/config/openapi.ts +6 -24
  67. package/hono/src/config/queue.ts +30 -30
  68. package/hono/src/config/validate.ts +50 -40
  69. package/hono/src/database/migrations/sqlite/0000_init.sql +47 -47
  70. package/hono/src/database/migrations/sqlite/meta/0000_snapshot.json +1 -1
  71. package/hono/src/database/migrations/sqlite/meta/_journal.json +1 -1
  72. package/hono/src/database/schema.ts +2 -2
  73. package/hono/src/framework/cache/cache.ts +40 -8
  74. package/hono/src/framework/circuit-breaker/cb.ts +88 -60
  75. package/hono/src/framework/database/connection.ts +52 -27
  76. package/hono/src/framework/events/dispatcher.ts +61 -49
  77. package/hono/src/framework/facade.ts +38 -1
  78. package/hono/src/framework/http/openapi.ts +7 -9
  79. package/hono/src/framework/notification/channels/broadcast.ts +15 -0
  80. package/hono/src/framework/notification/channels/mail.ts +40 -0
  81. package/hono/src/framework/notification/contracts.ts +29 -0
  82. package/hono/src/framework/notification/index.ts +19 -94
  83. package/hono/src/framework/notification/registry.ts +15 -0
  84. package/hono/src/framework/providers/adapter.ts +27 -17
  85. package/hono/src/framework/queue/queue.ts +43 -44
  86. package/hono/src/framework/realtime/broadcast.ts +29 -5
  87. package/hono/src/framework/realtime/socket.ts +217 -196
  88. package/hono/src/framework/redis/client.ts +148 -151
  89. package/hono/src/framework/scheduler/lock.ts +128 -116
  90. package/hono/src/framework/server.ts +27 -29
  91. package/hono/src/framework/session/session.ts +62 -14
  92. package/hono/src/framework/storage/storage.ts +744 -785
  93. package/hono/src/framework/support/mail.ts +56 -48
  94. package/hono/src/providers/notification.ts +4 -0
  95. package/hono/src/resources/src/components/Checkbox.vue +32 -29
  96. package/hono/src/resources/src/components/Input.vue +243 -234
  97. package/hono/src/resources/src/components/InputGroup.vue +147 -149
  98. package/hono/src/resources/src/components/InputPasswordToggle.vue +229 -222
  99. package/hono/src/resources/src/components/Modal.vue +71 -55
  100. package/hono/src/resources/src/components/Refresh.vue +19 -0
  101. package/hono/src/resources/src/helpers/assets.ts +51 -0
  102. package/hono/src/resources/src/layouts/Layout/Header.vue +124 -105
  103. package/hono/src/resources/src/main.ts +3 -1
  104. package/hono/src/resources/src/plugins/gum.ts +1 -1
  105. package/hono/src/resources/src/plugins/pulse.ts +157 -147
  106. package/hono/src/resources/src/stores/auth.ts +184 -164
  107. package/hono/src/resources/vite.config.ts +54 -36
  108. package/hono/src/storage/database/nexwire.sqlite +0 -0
  109. package/hono/src/storage/logs/app.log +0 -0
  110. package/hono/src/storage/logs/fatal.log +0 -0
  111. package/hono/src/types/global.d.ts +32 -26
  112. package/package.json +2 -2
  113. package/src/index.js +139 -14
package/src/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  import { readFileSync, existsSync, cpSync, mkdirSync, writeFileSync, renameSync, readdirSync, rmSync } from "node:fs";
4
4
  import { join, dirname, basename } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
- import { createInterface } from "node:readline";
6
+ import { createInterface, emitKeypressEvents } from "node:readline";
7
7
  import { gunzipSync } from "node:zlib";
8
8
  import https from "node:https";
9
9
 
@@ -18,6 +18,104 @@ function ask(query) {
18
18
  return new Promise((resolve) => rl.question(query, (a) => { rl.close(); resolve(a.trim()); }));
19
19
  }
20
20
 
21
+ // Keep this in sync with the packages/ directories shipped in the tarball and
22
+ // with ENGINE_PREFIXES below. A future frontend adds its own list here and its
23
+ // own prefix inside extractTarGz.
24
+ const BACKENDS = [
25
+ { value: "hono", label: "Hono", hint: "Hono-based HTTP layer and OpenAPI" },
26
+ { value: "express", label: "Express", hint: "Express-based HTTP layer and OpenAPI" }
27
+ ];
28
+
29
+ const BACKEND_NAMES = BACKENDS.map((b) => b.value);
30
+
31
+ function backendIndex(value) {
32
+ return BACKENDS.findIndex((b) => b.value === String(value).toLowerCase());
33
+ }
34
+
35
+ const useColor = Boolean(process.stdout.isTTY && !process.env.NO_COLOR);
36
+ const paint = (code, text) => (useColor ? `\x1b[${code}m${text}\x1b[0m` : text);
37
+
38
+ /**
39
+ * Render an arrow-key list picker. Falls back to the default choice when
40
+ * stdin is not a TTY (CI, piped input) so `npm create nexwire@latest .`
41
+ * keeps working in scripts without hanging on a prompt.
42
+ */
43
+ async function selectOption(title, options, defaultIndex = 0) {
44
+ if (!process.stdin.isTTY || typeof process.stdin.setRawMode !== "function") {
45
+ return options[defaultIndex].value;
46
+ }
47
+
48
+ const stdin = process.stdin;
49
+ const out = process.stdout;
50
+ let index = defaultIndex;
51
+ const lines = options.length + 2; // title + options + trailing help line
52
+
53
+ emitKeypressEvents(stdin);
54
+ stdin.setRawMode(true);
55
+ stdin.resume();
56
+ out.write("\x1b[?25l"); // hide the cursor while the list is live
57
+
58
+ const render = (first) => {
59
+ if (!first) out.write(`\x1b[${lines}A`);
60
+ out.write(`${paint(1, title)}\n`);
61
+ options.forEach((option, i) => {
62
+ const active = i === index;
63
+ const marker = active ? paint(32, "\u276F") : " ";
64
+ const label = paint(active ? 1 : 0, option.label);
65
+ const hint = option.hint ? ` ${paint(2, option.hint)}` : "";
66
+ out.write(`${marker} ${label}${hint}\x1b[K\n`);
67
+ });
68
+ out.write(`${paint(2, "Arrow keys choose, 1-2 jump, Enter confirm")}\n`);
69
+ };
70
+
71
+ render(true);
72
+
73
+ return new Promise((resolve) => {
74
+ const cleanup = () => {
75
+ stdin.setRawMode(false);
76
+ stdin.removeListener("keypress", onKeypress);
77
+ stdin.pause();
78
+ out.write("\x1b[?25h"); // restore the cursor
79
+ };
80
+
81
+ const onKeypress = (str, key = {}) => {
82
+ if (key.ctrl && key.name === "c") {
83
+ cleanup();
84
+ out.write("\n");
85
+ process.exit(130);
86
+ }
87
+ if (key.name === "escape") {
88
+ cleanup();
89
+ out.write("\n");
90
+ process.exit(130);
91
+ }
92
+ if (key.name === "up" || key.name === "k") {
93
+ index = (index - 1 + options.length) % options.length;
94
+ render(false);
95
+ return;
96
+ }
97
+ if (key.name === "down" || key.name === "j") {
98
+ index = (index + 1) % options.length;
99
+ render(false);
100
+ return;
101
+ }
102
+ if (key.name === "return" || key.name === "enter") {
103
+ cleanup();
104
+ out.write("\n");
105
+ resolve(options[index].value);
106
+ return;
107
+ }
108
+ const digit = Number(str);
109
+ if (Number.isInteger(digit) && digit >= 1 && digit <= options.length) {
110
+ index = digit - 1;
111
+ render(false);
112
+ }
113
+ };
114
+
115
+ stdin.on("keypress", onKeypress);
116
+ });
117
+ }
118
+
21
119
  function detectPackageManager() {
22
120
  if (typeof process !== "undefined" && process.isBun) return "bun";
23
121
  const ua = process.env.npm_config_user_agent || "";
@@ -116,26 +214,26 @@ function fetchTarball(pkgName, pkgVersion) {
116
214
 
117
215
  async function main() {
118
216
  const args = process.argv.slice(2);
119
- let projectName = args.find((a) => !a.startsWith("--"));
120
-
217
+ const yes = args.includes("--yes") || args.includes("-y");
121
218
  const engineArg = args.find((a) => a.startsWith("--engine="));
122
- let engine = engineArg ? engineArg.split("=")[1] : "hono";
123
-
124
- if (!ENGINE_PREFIXES[engine]) {
125
- console.error(`Error: Unknown engine "${engine}". Supported engines: hono, express.`);
126
- process.exit(1);
127
- }
219
+ let projectName = args.find((a) => !a.startsWith("--"));
128
220
 
221
+ // --help works even with --engine set, and before any prompt or network call.
129
222
  if (args.includes("--help") || args.includes("-h")) {
130
223
  console.log(`create-nexwire v${version}`);
131
224
  console.log();
132
225
  console.log("Usage:");
133
- console.log(" npm create nexwire@latest <project-name|.> [--engine=<hono|express>]");
134
- console.log(" pnpm create nexwire@latest <project-name|.> [--engine=<hono|express>]");
135
- console.log(" bun create nexwire@latest <project-name|.> [--engine=<hono|express>]");
136
- console.log(" npx create-nexwire@latest <project-name|.> [--engine=<hono|express>]");
226
+ console.log(" npm create nexwire@latest <project-name|.> [--engine=<hono|express>] [--yes]");
227
+ console.log(" pnpm create nexwire@latest <project-name|.> [--engine=<hono|express>] [--yes]");
228
+ console.log(" bun create nexwire@latest <project-name|.> [--engine=<hono|express>] [--yes]");
229
+ console.log(" npx create-nexwire@latest <project-name|.> [--engine=<hono|express>] [--yes]");
137
230
  console.log();
138
- console.log("Engines:");
231
+ console.log("Options:");
232
+ console.log(" --engine=<hono|express> Backend to scaffold. Asked interactively when omitted.");
233
+ console.log(" -y, --yes Skip prompts and accept the defaults (requires a project name).");
234
+ console.log(" -h, --help Show this help.");
235
+ console.log();
236
+ console.log("Backends:");
139
237
  console.log(" hono (default) Hono-based HTTP layer and OpenAPI");
140
238
  console.log(" express Express-based HTTP layer and OpenAPI");
141
239
  console.log();
@@ -144,7 +242,25 @@ async function main() {
144
242
  process.exit(0);
145
243
  }
146
244
 
245
+ if (engineArg === undefined && args.includes("--engine")) {
246
+ console.error('Error: Missing value for --engine. Use --engine=hono or --engine=express.');
247
+ process.exit(1);
248
+ }
249
+
250
+ if (engineArg) {
251
+ const engine = engineArg.split("=")[1].toLowerCase();
252
+ if (backendIndex(engine) === -1) {
253
+ console.error(`Error: Unknown engine "${engine}". Supported backends: ${BACKEND_NAMES.join(", ")}.`);
254
+ process.exit(1);
255
+ }
256
+ }
257
+
147
258
  if (!projectName) {
259
+ if (!process.stdin.isTTY || yes) {
260
+ console.error("Error: Please provide a project name or '.' to scaffold into the current directory.");
261
+ console.error(" A project name is required when the CLI runs without an interactive terminal.");
262
+ process.exit(1);
263
+ }
148
264
  projectName = await ask("Project name or '.' for the current directory: ");
149
265
  }
150
266
 
@@ -158,6 +274,15 @@ async function main() {
158
274
  process.exit(1);
159
275
  }
160
276
 
277
+ let engine = engineArg ? engineArg.split("=")[1].toLowerCase() : null;
278
+ if (!engine) {
279
+ if (yes) {
280
+ engine = "hono";
281
+ } else {
282
+ engine = await selectOption("Choose a backend:", BACKENDS, 0);
283
+ }
284
+ }
285
+
161
286
  const targetDir = resolveTargetDir(projectName);
162
287
  const isCurrentDir = targetDir === process.cwd();
163
288