toiljs 0.0.115 → 0.0.117

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 (326) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +16 -7
  3. package/TYPESCRIPT_LAW.md +1946 -2073
  4. package/build/backend/.tsbuildinfo +1 -1
  5. package/build/cli/.tsbuildinfo +1 -1
  6. package/build/cli/index.js +2375 -258
  7. package/build/client/.tsbuildinfo +1 -1
  8. package/build/client/dev/devtools.js +1 -1
  9. package/build/client/dev/error-overlay.js +1 -1
  10. package/build/client/routing/mount.js +1 -1
  11. package/build/compiler/.tsbuildinfo +1 -1
  12. package/build/compiler/auth-emails.js +7 -3
  13. package/build/compiler/emails.js +1 -1
  14. package/build/compiler/index.d.ts +1 -0
  15. package/build/compiler/index.js +25 -4
  16. package/build/compiler/pages.js +2 -3
  17. package/build/compiler/prerender.d.ts +3 -8
  18. package/build/compiler/prerender.js +88 -127
  19. package/build/compiler/ssg.js +3 -4
  20. package/build/compiler/template-build.js +3 -4
  21. package/build/compiler/toil-docs.generated.js +6 -5
  22. package/build/compiler/vite.js +3 -6
  23. package/build/devserver/.tsbuildinfo +1 -1
  24. package/build/devserver/db/database.js +7 -3
  25. package/build/devserver/db/types.js +1 -1
  26. package/build/devserver/production.js +4 -3
  27. package/build/devserver/runtime/crypto.d.ts +1 -1
  28. package/build/io/.tsbuildinfo +1 -1
  29. package/build/logger/.tsbuildinfo +1 -1
  30. package/build/shared/.tsbuildinfo +1 -1
  31. package/examples/basic/client/routes/analytics.tsx +7 -5
  32. package/examples/basic/server/tsconfig.json +1 -3
  33. package/package.json +45 -44
  34. package/presets/no-uint8array-tostring.js +120 -153
  35. package/presets/oxlint.d.ts +5 -0
  36. package/presets/oxlint.js +224 -0
  37. package/server/runtime/tsconfig.json +1 -3
  38. package/tsconfig.json +2 -11
  39. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
  40. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -90
  41. package/.github/ISSUE_TEMPLATE/config.yml +0 -8
  42. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  43. package/.github/PULL_REQUEST_TEMPLATE.md +0 -43
  44. package/.github/changelog-config.json +0 -45
  45. package/.github/dependabot.yml +0 -27
  46. package/.github/workflows/ci.yml +0 -222
  47. package/.vscode/settings.json +0 -10
  48. package/docs/README.md +0 -107
  49. package/docs/auth/README.md +0 -104
  50. package/docs/auth/configuration.md +0 -105
  51. package/docs/auth/emails.md +0 -92
  52. package/docs/auth/extending.md +0 -202
  53. package/docs/auth/how-it-works.md +0 -138
  54. package/docs/auth/usage.md +0 -188
  55. package/docs/backend/README.md +0 -143
  56. package/docs/backend/data.md +0 -351
  57. package/docs/backend/rest.md +0 -402
  58. package/docs/backend/rpc.md +0 -226
  59. package/docs/background/README.md +0 -114
  60. package/docs/background/daemons.md +0 -276
  61. package/docs/background/derive.md +0 -179
  62. package/docs/cli/README.md +0 -379
  63. package/docs/concepts/ai-guide.md +0 -132
  64. package/docs/concepts/config.md +0 -416
  65. package/docs/concepts/decorators.md +0 -127
  66. package/docs/concepts/security.md +0 -108
  67. package/docs/concepts/tiers.md +0 -166
  68. package/docs/concepts/types.md +0 -216
  69. package/docs/database/README.md +0 -143
  70. package/docs/database/capacity.md +0 -350
  71. package/docs/database/counters.md +0 -174
  72. package/docs/database/documents.md +0 -277
  73. package/docs/database/events.md +0 -307
  74. package/docs/database/membership.md +0 -246
  75. package/docs/database/setup.md +0 -155
  76. package/docs/database/unique.md +0 -216
  77. package/docs/database/views.md +0 -246
  78. package/docs/frontend/README.md +0 -104
  79. package/docs/frontend/components.md +0 -286
  80. package/docs/frontend/data-fetching.md +0 -260
  81. package/docs/frontend/images.md +0 -148
  82. package/docs/frontend/metadata.md +0 -344
  83. package/docs/frontend/navigation.md +0 -296
  84. package/docs/frontend/rendering.md +0 -236
  85. package/docs/frontend/routing.md +0 -346
  86. package/docs/frontend/scripts.md +0 -118
  87. package/docs/frontend/search.md +0 -191
  88. package/docs/frontend/styling.md +0 -147
  89. package/docs/frontend/toil-global.md +0 -216
  90. package/docs/getting-started/README.md +0 -81
  91. package/docs/getting-started/create-project.md +0 -131
  92. package/docs/getting-started/deploy.md +0 -101
  93. package/docs/getting-started/first-app.md +0 -215
  94. package/docs/getting-started/installation.md +0 -124
  95. package/docs/getting-started/migrating.md +0 -125
  96. package/docs/getting-started/project-structure.md +0 -163
  97. package/docs/introduction/README.md +0 -66
  98. package/docs/introduction/design-principles.md +0 -81
  99. package/docs/introduction/distributed.md +0 -91
  100. package/docs/introduction/how-it-works.md +0 -121
  101. package/docs/introduction/hyperscale.md +0 -70
  102. package/docs/introduction/modern-stack.md +0 -85
  103. package/docs/introduction/vs-other-frameworks.md +0 -64
  104. package/docs/introduction/why-toil.md +0 -99
  105. package/docs/llms.txt +0 -95
  106. package/docs/realtime/README.md +0 -102
  107. package/docs/realtime/channels.md +0 -211
  108. package/docs/realtime/streams.md +0 -369
  109. package/docs/services/README.md +0 -60
  110. package/docs/services/analytics.md +0 -279
  111. package/docs/services/caching.md +0 -175
  112. package/docs/services/cookies.md +0 -235
  113. package/docs/services/crypto.md +0 -209
  114. package/docs/services/email.md +0 -289
  115. package/docs/services/environment.md +0 -141
  116. package/docs/services/ratelimit.md +0 -174
  117. package/docs/services/time.md +0 -85
  118. package/eslint.config.js +0 -57
  119. package/examples/basic/client/routes/docs/[...slug].tsx +0 -12
  120. package/presets/eslint.js +0 -88
  121. package/src/backend/index.ts +0 -204
  122. package/src/cli/configure.ts +0 -363
  123. package/src/cli/create.ts +0 -924
  124. package/src/cli/db.ts +0 -158
  125. package/src/cli/diagnostics.ts +0 -665
  126. package/src/cli/doctor.ts +0 -982
  127. package/src/cli/features.ts +0 -170
  128. package/src/cli/index.ts +0 -326
  129. package/src/cli/notify.ts +0 -120
  130. package/src/cli/proc.ts +0 -56
  131. package/src/cli/ui.ts +0 -169
  132. package/src/cli/update.ts +0 -248
  133. package/src/cli/updates.ts +0 -92
  134. package/src/cli/validate.ts +0 -31
  135. package/src/cli/version-check.ts +0 -125
  136. package/src/client/auth.ts +0 -869
  137. package/src/client/channel/channel.ts +0 -146
  138. package/src/client/components/Form.tsx +0 -65
  139. package/src/client/components/Image.tsx +0 -160
  140. package/src/client/components/Script.tsx +0 -113
  141. package/src/client/components/Slot.tsx +0 -21
  142. package/src/client/dev/devtools.tsx +0 -1089
  143. package/src/client/dev/error-overlay.tsx +0 -225
  144. package/src/client/errors.ts +0 -11
  145. package/src/client/head/head.ts +0 -182
  146. package/src/client/head/metadata.ts +0 -110
  147. package/src/client/index.ts +0 -130
  148. package/src/client/navigation/Link.tsx +0 -108
  149. package/src/client/navigation/NavLink.tsx +0 -86
  150. package/src/client/navigation/navigation.ts +0 -235
  151. package/src/client/navigation/prefetch.ts +0 -169
  152. package/src/client/navigation/scroll.ts +0 -53
  153. package/src/client/routing/Router.tsx +0 -216
  154. package/src/client/routing/action.ts +0 -122
  155. package/src/client/routing/error-boundary.tsx +0 -43
  156. package/src/client/routing/hooks.ts +0 -140
  157. package/src/client/routing/lazy.ts +0 -77
  158. package/src/client/routing/loader.ts +0 -381
  159. package/src/client/routing/match.ts +0 -47
  160. package/src/client/routing/mount.tsx +0 -90
  161. package/src/client/routing/params-context.ts +0 -10
  162. package/src/client/routing/slot-context.ts +0 -7
  163. package/src/client/rpc.ts +0 -97
  164. package/src/client/search/search.ts +0 -189
  165. package/src/client/search/use-page-search.ts +0 -73
  166. package/src/client/ssr/markers.tsx +0 -172
  167. package/src/client/stream/client.ts +0 -237
  168. package/src/client/types.ts +0 -73
  169. package/src/compiler/auth-emails.ts +0 -312
  170. package/src/compiler/config.ts +0 -369
  171. package/src/compiler/docs.ts +0 -127
  172. package/src/compiler/email-preview.ts +0 -314
  173. package/src/compiler/emails.ts +0 -404
  174. package/src/compiler/fonts.ts +0 -89
  175. package/src/compiler/generate.ts +0 -471
  176. package/src/compiler/image-blur.ts +0 -42
  177. package/src/compiler/image-report.ts +0 -88
  178. package/src/compiler/index.ts +0 -1362
  179. package/src/compiler/pages.ts +0 -49
  180. package/src/compiler/plugin.ts +0 -391
  181. package/src/compiler/prerender.ts +0 -263
  182. package/src/compiler/routes.ts +0 -170
  183. package/src/compiler/seo.ts +0 -415
  184. package/src/compiler/sri.ts +0 -144
  185. package/src/compiler/ssg.ts +0 -177
  186. package/src/compiler/ssr-codegen.ts +0 -85
  187. package/src/compiler/template-build.ts +0 -676
  188. package/src/compiler/template.ts +0 -262
  189. package/src/compiler/toil-docs.generated.ts +0 -77
  190. package/src/compiler/vite.ts +0 -245
  191. package/src/devserver/analytics/index.ts +0 -317
  192. package/src/devserver/config/dotenv.ts +0 -102
  193. package/src/devserver/config/env.ts +0 -30
  194. package/src/devserver/config/ratelimit.ts +0 -123
  195. package/src/devserver/daemon/catalog.ts +0 -120
  196. package/src/devserver/daemon/cron.ts +0 -87
  197. package/src/devserver/daemon/host.ts +0 -134
  198. package/src/devserver/daemon/index.ts +0 -369
  199. package/src/devserver/daemon/runtime.ts +0 -48
  200. package/src/devserver/db/catalog.ts +0 -109
  201. package/src/devserver/db/database.ts +0 -1834
  202. package/src/devserver/db/derives.ts +0 -121
  203. package/src/devserver/db/index.ts +0 -20
  204. package/src/devserver/db/routeKinds.ts +0 -191
  205. package/src/devserver/db/types.ts +0 -168
  206. package/src/devserver/email/caps.ts +0 -0
  207. package/src/devserver/email/config.ts +0 -141
  208. package/src/devserver/email/console.ts +0 -348
  209. package/src/devserver/email/index.ts +0 -155
  210. package/src/devserver/email/providers.ts +0 -131
  211. package/src/devserver/email/status.ts +0 -23
  212. package/src/devserver/email/validate.ts +0 -37
  213. package/src/devserver/email/wire.ts +0 -55
  214. package/src/devserver/http/cache.ts +0 -0
  215. package/src/devserver/http/envelope.ts +0 -157
  216. package/src/devserver/http/proxy.ts +0 -172
  217. package/src/devserver/http/runtime.ts +0 -387
  218. package/src/devserver/index.ts +0 -43
  219. package/src/devserver/production-ipc.ts +0 -63
  220. package/src/devserver/production-worker.ts +0 -83
  221. package/src/devserver/production.ts +0 -718
  222. package/src/devserver/runtime/crypto.ts +0 -587
  223. package/src/devserver/runtime/host.ts +0 -427
  224. package/src/devserver/runtime/module.ts +0 -442
  225. package/src/devserver/server.ts +0 -251
  226. package/src/devserver/ssr.ts +0 -208
  227. package/src/devserver/stream/catalog.ts +0 -106
  228. package/src/devserver/stream/host.ts +0 -42
  229. package/src/devserver/stream/index.ts +0 -301
  230. package/src/devserver/stream/manager.ts +0 -167
  231. package/src/devserver/stream/router.ts +0 -101
  232. package/src/devserver/stream/wire.ts +0 -58
  233. package/src/devserver/stream/ws.ts +0 -78
  234. package/src/devserver/wasm/sections.ts +0 -59
  235. package/src/devserver/wasm/surface.ts +0 -115
  236. package/src/io/FastMap.ts +0 -151
  237. package/src/io/FastSet.ts +0 -113
  238. package/src/io/codec.ts +0 -362
  239. package/src/io/index.ts +0 -10
  240. package/src/io/lengths.ts +0 -14
  241. package/src/io/types.ts +0 -19
  242. package/src/logger/index.ts +0 -22
  243. package/src/shared/index.ts +0 -46
  244. package/test/analytics-dev.test.ts +0 -113
  245. package/test/assembly/aspect-shim.ts +0 -33
  246. package/test/assembly/cookie.spec.ts +0 -303
  247. package/test/assembly/example.spec.ts +0 -22
  248. package/test/assembly/ssr.spec.ts +0 -95
  249. package/test/assembly.test.ts +0 -59
  250. package/test/auth-emails.test.ts +0 -118
  251. package/test/built-ssr.test.ts +0 -148
  252. package/test/channel.test.ts +0 -21
  253. package/test/config-host-port.test.ts +0 -59
  254. package/test/configure.test.ts +0 -94
  255. package/test/daemon-build.test.ts +0 -278
  256. package/test/daemon-catalog.test.ts +0 -349
  257. package/test/daemon-emulation.test.ts +0 -256
  258. package/test/db.test.ts +0 -0
  259. package/test/devserver-database.test.ts +0 -1102
  260. package/test/devserver-email.test.ts +0 -306
  261. package/test/devserver-pqauth.test.ts +0 -93
  262. package/test/devserver-secrets.test.ts +0 -63
  263. package/test/devserver.test.ts +0 -259
  264. package/test/doctor.test.ts +0 -298
  265. package/test/dom/Image.test.tsx +0 -144
  266. package/test/dom/Link.test.tsx +0 -67
  267. package/test/dom/NavLink.test.tsx +0 -37
  268. package/test/dom/Script.test.tsx +0 -48
  269. package/test/dom/action.test.tsx +0 -146
  270. package/test/dom/error-overlay.test.tsx +0 -44
  271. package/test/dom/loader.test.tsx +0 -121
  272. package/test/dom/navigation.test.ts +0 -59
  273. package/test/dom/revalidate.test.tsx +0 -38
  274. package/test/dom/route-head.test.tsx +0 -56
  275. package/test/dom/router-loading.test.tsx +0 -44
  276. package/test/dom/scroll.test.ts +0 -56
  277. package/test/dom/slot.test.tsx +0 -131
  278. package/test/dom/use-metadata.test.tsx +0 -58
  279. package/test/dom/view-transitions.test.tsx +0 -53
  280. package/test/dynamic-routing.test.ts +0 -132
  281. package/test/email-console.test.ts +0 -86
  282. package/test/email-preview.test.ts +0 -73
  283. package/test/emails.test.ts +0 -58
  284. package/test/errors.test.ts +0 -21
  285. package/test/example-guestbook.test.ts +0 -123
  286. package/test/features.test.ts +0 -149
  287. package/test/fixtures/bignum-wire/spec.ts +0 -25
  288. package/test/fixtures/daemon-app.ts +0 -65
  289. package/test/fixtures/stream-echo.ts +0 -26
  290. package/test/fixtures/stream-gate.ts +0 -28
  291. package/test/fixtures/stream-trap.ts +0 -18
  292. package/test/fixtures/stream-typed.ts +0 -41
  293. package/test/fonts.test.ts +0 -28
  294. package/test/global-setup.ts +0 -17
  295. package/test/head.test.ts +0 -42
  296. package/test/image-blur.test.ts +0 -50
  297. package/test/io.test.ts +0 -117
  298. package/test/metadata.test.ts +0 -40
  299. package/test/navlink.test.ts +0 -28
  300. package/test/pages.test.ts +0 -105
  301. package/test/placeholder.test.ts +0 -9
  302. package/test/pqauth-e2e.test.ts +0 -216
  303. package/test/pqauth-email.test.ts +0 -801
  304. package/test/prerender.test.ts +0 -116
  305. package/test/prettier-plugin.test.ts +0 -46
  306. package/test/routes.test.ts +0 -76
  307. package/test/rpc-bignum-wire.test.ts +0 -164
  308. package/test/rpc-dispatch.test.ts +0 -132
  309. package/test/rpc-kinds.test.ts +0 -18
  310. package/test/rpc.test.ts +0 -66
  311. package/test/search.test.ts +0 -114
  312. package/test/seo.test.ts +0 -175
  313. package/test/slot-layouts.test.ts +0 -69
  314. package/test/sri.test.ts +0 -129
  315. package/test/ssg.test.ts +0 -36
  316. package/test/ssr-hydration.test.tsx +0 -122
  317. package/test/ssr-render.test.ts +0 -197
  318. package/test/ssr-template.test.tsx +0 -482
  319. package/test/stream-emulation.test.ts +0 -479
  320. package/test/ui.test.ts +0 -40
  321. package/test/update.test.ts +0 -58
  322. package/test/validate.test.ts +0 -42
  323. package/test/version-check.test.ts +0 -130
  324. package/tests/data-parity/generated-parity.ts +0 -99
  325. package/tests/data-parity/parity.ts +0 -80
  326. package/tests/data-parity/spec.ts +0 -46
package/src/cli/ui.ts DELETED
@@ -1,169 +0,0 @@
1
- /**
2
- * Shared CLI presentation: the toiljs brand banner, gradient text, and small helpers.
3
- * Kept dependency-light (only picocolors); the gradient is hand-rolled truecolor ANSI so
4
- * the logo pops without pulling in a gradient library.
5
- */
6
- import fs from 'node:fs';
7
- import path from 'node:path';
8
- import { fileURLToPath } from 'node:url';
9
-
10
- import pc from 'picocolors';
11
-
12
- type RGB = readonly [number, number, number];
13
-
14
- /** toiljs brand palette. */
15
- const PRIMARY: RGB = [37, 99, 255];
16
- const SECONDARY: RGB = [124, 58, 237];
17
- const ACCENT: RGB = [34, 227, 171];
18
-
19
- /** Logo gradient stops: blue → purple → teal. */
20
- const GRADIENT: readonly RGB[] = [PRIMARY, SECONDARY, ACCENT];
21
-
22
- /** ANSI-shadow "TOIL" wordmark. */
23
- const ART: readonly string[] = [
24
- '████████╗ ██████╗ ██╗ ██╗ ',
25
- '╚══██╔══╝ ██╔═══██╗ ██║ ██║ ',
26
- ' ██║ ██║ ██║ ██║ ██║ ',
27
- ' ██║ ██║ ██║ ██║ ██║ ',
28
- ' ██║ ╚██████╔╝ ██║ ███████╗',
29
- ' ╚═╝ ╚═════╝ ╚═╝ ╚══════╝',
30
- ];
31
-
32
- export const dim = pc.dim;
33
- export const bold = pc.bold;
34
-
35
- /** True when we should emit ANSI color: a TTY (or FORCE_COLOR), and not disabled via NO_COLOR. */
36
- function colorEnabled(): boolean {
37
- if (process.env.NO_COLOR) return false;
38
- if (process.env.FORCE_COLOR) return true;
39
- return process.stdout.isTTY;
40
- }
41
-
42
- function rgb(color: RGB, s: string): string {
43
- return colorEnabled() ? `\x1b[38;2;${color[0]};${color[1]};${color[2]}m${s}\x1b[39m` : s;
44
- }
45
-
46
- /** The primary brand accent (blue). No-ops to plain text when color is disabled. */
47
- export function brand(s: string): string {
48
- return rgb(PRIMARY, s);
49
- }
50
- export const accent = brand;
51
-
52
- /** Success/positive accent (teal). */
53
- export function success(s: string): string {
54
- return rgb(ACCENT, s);
55
- }
56
-
57
- /** Error accent (red, kept outside the brand palette since errors should read as errors). */
58
- export const danger = pc.red;
59
-
60
- /** Warning accent (yellow, outside the brand palette so warnings read as warnings). */
61
- export const warn = pc.yellow;
62
-
63
- function lerp(a: number, b: number, t: number): number {
64
- return Math.round(a + (b - a) * t);
65
- }
66
-
67
- /** Samples the multi-stop brand gradient at `t` in [0, 1]. */
68
- function gradientAt(t: number): RGB {
69
- const segments = GRADIENT.length - 1;
70
- const scaled = t * segments;
71
- const i = Math.min(Math.floor(scaled), segments - 1);
72
- const a = GRADIENT[i];
73
- const b = GRADIENT[i + 1];
74
- const localT = scaled - i;
75
- return [lerp(a[0], b[0], localT), lerp(a[1], b[1], localT), lerp(a[2], b[2], localT)];
76
- }
77
-
78
- /** Colors each character of `line` along the left→right brand gradient. */
79
- function gradientLine(line: string): string {
80
- const n = line.length;
81
- let out = '';
82
- for (let i = 0; i < n; i++) {
83
- const [r, g, b] = gradientAt(n > 1 ? i / (n - 1) : 0);
84
- out += `\x1b[38;2;${r};${g};${b}m${line[i]}`;
85
- }
86
- return out + '\x1b[39m';
87
- }
88
-
89
- /** Reads the toiljs package version (CLI lives at build/cli/, package root is two up). */
90
- export function version(): string {
91
- try {
92
- const pkgPath = path.resolve(
93
- path.dirname(fileURLToPath(import.meta.url)),
94
- '..',
95
- '..',
96
- 'package.json',
97
- );
98
- const raw = fs.readFileSync(pkgPath, 'utf8');
99
- const match = /"version"\s*:\s*"([^"]+)"/.exec(raw);
100
- if (match && match[1]) return match[1];
101
- } catch {}
102
- return '0.0.0';
103
- }
104
-
105
- // eslint-disable-next-line no-control-regex -- matching our own escape sequences is the point
106
- const ANSI_RE = /\x1b\[[0-9;]*m/g;
107
-
108
- /** The on-screen width of `s`, ignoring ANSI color codes. */
109
- function visibleWidth(s: string): number {
110
- return s.replace(ANSI_RE, '').length;
111
- }
112
-
113
- /**
114
- * Frames already-colored lines in a rounded box sized to the widest line. `paint` colors the
115
- * border (the content keeps its own colors); padding is measured on the visible text, so ANSI
116
- * codes inside the lines never skew the right edge. Returns the box without a trailing newline.
117
- */
118
- export function box(lines: readonly string[], paint: (s: string) => string = (s) => s): string {
119
- const width = lines.reduce((w, l) => Math.max(w, visibleWidth(l)), 0);
120
- const side = paint('│');
121
- const body = lines.map((l) => ` ${side} ${l}${' '.repeat(width - visibleWidth(l))} ${side}`);
122
- return [
123
- ' ' + paint(`╭${'─'.repeat(width + 4)}╮`),
124
- ...body,
125
- ' ' + paint(`╰${'─'.repeat(width + 4)}╯`),
126
- ].join('\n');
127
- }
128
-
129
- /**
130
- * Banner taglines, one is picked at random per invocation. Each is a function so the accented
131
- * words pick up the brand color (or stay plain when color is disabled). The theme: TOIL is the
132
- * first full-stack framework for a globally distributed application delivery network.
133
- */
134
- const TAGLINES: ReadonlyArray<(a: (s: string) => string) => string> = [
135
- (a) => `bringing ${a('hyper scale')} to anyone`,
136
- (a) => `the first full-stack ${a('application delivery network')}`,
137
- (a) => `your app, ${a('globally distributed')} by default`,
138
- (a) => `one build, ${a('the whole planet')}`,
139
- (a) => `full stack, ${a('zero distance')} to your users`,
140
- (a) => `${a('react')} up front, ${a('wasm')} at every edge`,
141
- (a) => `deployed where your ${a('users')} are`,
142
- (a) => `the framework with a ${a('delivery network')} built in`,
143
- (a) => `no regions, just ${a('the world')}`,
144
- (a) => `${a('planet-scale')} apps from a single repo`,
145
- (a) => `every request served ${a('next door')}`,
146
- (a) => `frontend, backend, ${a('worldwide')}`,
147
- (a) => `${a('hyper scale')} without the ops team`,
148
- (a) => `your backend, ${a('compiled to wasm')}, running everywhere`,
149
- (a) => `the internet is your ${a('runtime')}`,
150
- (a) => `the speed of light is the ${a('only bottleneck')}`,
151
- (a) => `static speed, ${a('dynamic everything')}`,
152
- (a) => `scale to ${a('millions')} before lunch`,
153
- (a) => `latency is a choice, choose ${a('zero')}`,
154
- (a) => `build ${a('better')}, ship ${a('faster')}`,
155
- ];
156
-
157
- /** A random brand tagline, accent words colored. */
158
- export function tagline(): string {
159
- return TAGLINES[Math.floor(Math.random() * TAGLINES.length)](brand);
160
- }
161
-
162
- /** Prints the brand banner: gradient logo + random tagline + version. */
163
- export function banner(): void {
164
- const lines = colorEnabled() ? ART.map(gradientLine) : ART.slice();
165
- const ver = `${dim(' v')}${brand(version())}`;
166
- process.stdout.write('\n' + lines.join('\n') + '\n\n ' + tagline() + ' ' + ver + '\n\n');
167
- }
168
-
169
-
package/src/cli/update.ts DELETED
@@ -1,248 +0,0 @@
1
- /**
2
- * `toiljs update`, a friendly wrapper over `npm-check-updates`: checks the registry for newer
3
- * dependency versions, shows them grouped by semver bump (major / minor / patch) with colors, lets
4
- * you pick which to apply (or `-y` for all), bumps package.json, and runs the project's package
5
- * manager install. ncu is invoked via `npx --yes` so it isn't a permanent dependency of toiljs.
6
- */
7
- import fs from 'node:fs';
8
- import path from 'node:path';
9
-
10
- import { cancel, intro, isCancel, multiselect, note, outro, spinner } from '@clack/prompts';
11
-
12
- import { MIGRATIONS_README } from './create.js';
13
- import { capture, run } from './proc.js';
14
- import { buildRows, type Bump, parseNcuJson, type UpdateRow, withheldUpgrades } from './updates.js';
15
- import { accent, danger, dim, success, warn } from './ui.js';
16
-
17
- export interface UpdateOptions {
18
- readonly root?: string;
19
- readonly cwd: string;
20
- /** Apply all available updates without the interactive picker. */
21
- readonly yes?: boolean;
22
- /** ncu `--target` (latest | minor | patch | newest | greatest). Default latest. */
23
- readonly target?: string;
24
- }
25
-
26
- export interface PackageManager {
27
- readonly name: string;
28
- readonly ncuName: string;
29
- }
30
-
31
- /** Detects the package manager from the project's lockfile (defaults to npm). */
32
- export function detectPackageManager(root: string): PackageManager {
33
- if (fs.existsSync(path.join(root, 'pnpm-lock.yaml'))) return { name: 'pnpm', ncuName: 'pnpm' };
34
- if (fs.existsSync(path.join(root, 'yarn.lock'))) return { name: 'yarn', ncuName: 'yarn' };
35
- if (fs.existsSync(path.join(root, 'bun.lockb'))) return { name: 'bun', ncuName: 'bun' };
36
- return { name: 'npm', ncuName: 'npm' };
37
- }
38
-
39
- /** Reads the merged dependency ranges (deps + devDeps) from a package.json. */
40
- function readDependencies(pkgPath: string): Record<string, string> {
41
- const parsed: unknown = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
42
- if (typeof parsed !== 'object' || parsed === null) return {};
43
- const pkg = parsed as Record<string, unknown>;
44
- const merge = (v: unknown): Record<string, string> => {
45
- if (typeof v !== 'object' || v === null) return {};
46
- const out: Record<string, string> = {};
47
- for (const [k, val] of Object.entries(v)) if (typeof val === 'string') out[k] = val;
48
- return out;
49
- };
50
- return { ...merge(pkg.dependencies), ...merge(pkg.devDependencies) };
51
- }
52
-
53
- /**
54
- * The server dir(s) of a project: the directories of the toilconfig.json `entries`, conventionally
55
- * `server/`. Falls back to `<root>/server` when there is no toilconfig (or it has no string entries),
56
- * matching how `doctor` locates the server tree.
57
- */
58
- function serverDirs(root: string): string[] {
59
- const dirs = new Set<string>();
60
- try {
61
- const parsed: unknown = JSON.parse(
62
- fs.readFileSync(path.join(root, 'toilconfig.json'), 'utf8'),
63
- );
64
- const entries =
65
- typeof parsed === 'object' &&
66
- parsed !== null &&
67
- Array.isArray((parsed as { entries?: unknown }).entries)
68
- ? (parsed as { entries: unknown[] }).entries.filter(
69
- (e): e is string => typeof e === 'string',
70
- )
71
- : [];
72
- for (const e of entries) dirs.add(path.dirname(path.resolve(root, e)));
73
- } catch {
74
- // no/unreadable toilconfig.json: fall back to the conventional server/ below
75
- }
76
- if (dirs.size === 0) dirs.add(path.join(root, 'server'));
77
- return [...dirs];
78
- }
79
-
80
- /**
81
- * Ensures the ToilDB `migrations/` folder exists under each server dir that exists. `@migrate`
82
- * functions MUST live in a `*.migration.ts` file under `migrations/` (folder + extension is a
83
- * compile error otherwise) and the build auto-discovers them, so an up-to-date project keeps the
84
- * folder ready. Idempotent: only writes the folder + README where the server dir exists and the
85
- * folder is absent. Returns the project-relative paths created (for the note), empty if nothing.
86
- */
87
- function ensureMigrationsDirs(root: string): string[] {
88
- const created: string[] = [];
89
- for (const dir of serverDirs(root)) {
90
- if (!fs.existsSync(dir)) continue;
91
- const migrations = path.join(dir, 'migrations');
92
- if (fs.existsSync(migrations)) continue;
93
- fs.mkdirSync(migrations, { recursive: true });
94
- fs.writeFileSync(path.join(migrations, 'README.md'), MIGRATIONS_README);
95
- created.push(path.relative(root, migrations) || 'migrations');
96
- }
97
- return created;
98
- }
99
-
100
- function bumpColor(bump: Bump, text: string): string {
101
- if (bump === 'major') return danger(text);
102
- if (bump === 'minor') return warn(text);
103
- if (bump === 'patch') return success(text);
104
- return dim(text);
105
- }
106
-
107
- /** Renders a row as `name from -> to`, the version part colored by bump. */
108
- function rowLine(row: UpdateRow): string {
109
- return `${row.name} ${dim(row.from)} ${dim('->')} ${bumpColor(row.bump, row.to)}`;
110
- }
111
-
112
- /** Tells the user which upgrades were held back, and why they are not simply missing. */
113
- function noteWithheld(names: readonly string[]): void {
114
- note(
115
- names.map((n) => `${dim('-')} ${n}`).join('\n') +
116
- '\n\n' +
117
- dim(
118
- 'Held back: this major is not supported by toiljs yet. TypeScript 7 is the native\n' +
119
- 'port and ships no JavaScript compiler API, so route metadata would stop being\n' +
120
- 'baked into the built HTML and typescript-eslint would not load.',
121
- ),
122
- warn('Not upgraded'),
123
- );
124
- }
125
-
126
- const TARGETS = new Set(['latest', 'minor', 'patch', 'newest', 'greatest']);
127
-
128
- export async function runUpdate(opts: UpdateOptions): Promise<void> {
129
- const root = path.resolve(opts.root ?? opts.cwd);
130
- const pkgPath = path.join(root, 'package.json');
131
- if (!fs.existsSync(pkgPath)) {
132
- throw new Error('No package.json here. Run from your project root or pass --root <dir>.');
133
- }
134
- const currentDeps = readDependencies(pkgPath);
135
- const pm = detectPackageManager(root);
136
- const target = opts.target && TARGETS.has(opts.target) ? opts.target : 'latest';
137
-
138
- const ncuArgs = (extra: string[]): string[] => [
139
- '--yes',
140
- 'npm-check-updates',
141
- '--packageManager',
142
- pm.ncuName,
143
- '--target',
144
- target,
145
- ...extra,
146
- ];
147
-
148
- intro(accent('toiljs update'));
149
-
150
- // Bring the project's structure up to date: ensure the ToilDB migrations folder exists (it is
151
- // newer than some projects, and the compiler requires @migrate functions to live under it).
152
- const createdMigrations = ensureMigrationsDirs(root);
153
- if (createdMigrations.length > 0) {
154
- note(
155
- createdMigrations.map((p) => `${dim('+')} ${p}/`).join('\n'),
156
- 'Created ToilDB migrations folder',
157
- );
158
- }
159
-
160
- const s = spinner();
161
- s.start('Checking the registry for updates');
162
- const res = await capture('npx', ncuArgs(['--jsonUpgraded']), root);
163
- if (res.code !== 0 && res.stdout.indexOf('{') === -1) {
164
- s.stop('Could not check for updates');
165
- note(dim(res.stderr.trim() || 'npm-check-updates failed.'), 'Error');
166
- process.exitCode = 1;
167
- return;
168
- }
169
- // Hold back upgrades into a major toiljs cannot run on (typescript 7), so neither the picker nor
170
- // a `-y` run can install one. `--reject` keeps ncu from applying them during `-u` below.
171
- const upgraded = parseNcuJson(res.stdout);
172
- const withheld = withheldUpgrades(upgraded);
173
- for (const name of withheld) delete upgraded[name];
174
-
175
- const rows = buildRows(upgraded, currentDeps);
176
- if (rows.length === 0) {
177
- s.stop('Everything is up to date');
178
- if (withheld.length > 0) noteWithheld(withheld);
179
- outro(success('Nothing to update.'));
180
- return;
181
- }
182
- s.stop(`${String(rows.length)} update${rows.length === 1 ? '' : 's'} available`);
183
- if (withheld.length > 0) noteWithheld(withheld);
184
-
185
- const counts = { major: 0, minor: 0, patch: 0, other: 0 };
186
- for (const r of rows) counts[r.bump]++;
187
- note(
188
- rows.map(rowLine).join('\n'),
189
- `${danger(`${String(counts.major)} major`)} ${warn(`${String(counts.minor)} minor`)} ${success(`${String(counts.patch)} patch`)}`,
190
- );
191
-
192
- let selected: string[];
193
- if (opts.yes) {
194
- selected = rows.map((r) => r.name);
195
- } else {
196
- const answer = await multiselect<string>({
197
- message: 'Select packages to update (space to toggle, enter to confirm)',
198
- options: rows.map((r) => ({
199
- value: r.name,
200
- label: r.name,
201
- hint: `${r.from} -> ${r.to}`,
202
- })),
203
- initialValues: rows.map((r) => r.name),
204
- required: false,
205
- });
206
- if (isCancel(answer)) {
207
- cancel('Update cancelled.');
208
- return;
209
- }
210
- selected = answer;
211
- }
212
-
213
- if (selected.length === 0) {
214
- outro(dim('No packages selected.'));
215
- return;
216
- }
217
-
218
- s.start('Updating package.json');
219
- const applyAll = selected.length === rows.length;
220
- const reject = withheld.length > 0 ? ['--reject', withheld.join(' ')] : [];
221
- await run(
222
- 'npx',
223
- ncuArgs(applyAll ? ['-u', ...reject] : ['-u', '--filter', selected.join(' '), ...reject]),
224
- root,
225
- );
226
- s.stop('package.json updated');
227
-
228
- // Run the install with VISIBLE output (so a failure is diagnosable — npm
229
- // prints the real error) and handle a non-zero exit gracefully, instead of
230
- // leaving the spinner spinning forever on a failed install.
231
- note(dim(`Running ${pm.name} install…`), 'Install');
232
- try {
233
- await run(pm.name, ['install'], root, { stdio: 'inherit' });
234
- } catch {
235
- outro(
236
- danger(
237
- `${pm.name} install failed — package.json was updated to the new versions, but the ` +
238
- `install did not finish. Fix the error printed above, then run \`${pm.name} install\`.`,
239
- ),
240
- );
241
- process.exitCode = 1;
242
- return;
243
- }
244
-
245
- outro(
246
- success(`Updated ${String(selected.length)} package${selected.length === 1 ? '' : 's'}.`),
247
- );
248
- }
@@ -1,92 +0,0 @@
1
- /**
2
- * Pure helpers for `toiljs update`: parse `npm-check-updates --jsonUpgraded` output and classify the
3
- * semver bump of each upgrade. IO-free so it can be unit-tested; the spawn/UI live in `update.ts`.
4
- */
5
-
6
- /** The kind of version jump an upgrade represents. */
7
- export type Bump = 'major' | 'minor' | 'patch' | 'other';
8
-
9
- /** One available upgrade: package, current range, target range, and bump kind. */
10
- export interface UpdateRow {
11
- readonly name: string;
12
- readonly from: string;
13
- readonly to: string;
14
- readonly bump: Bump;
15
- }
16
-
17
- /** Extracts a version's leading `x.y.z` (ignoring `^`, `~`, `>=`, etc.); missing parts become 0. */
18
- function parseVersion(v: string): [number, number, number] {
19
- const m = /(\d+)(?:\.(\d+))?(?:\.(\d+))?/.exec(v);
20
- if (!m) return [0, 0, 0];
21
- return [Number(m[1]), Number(m[2] ?? 0), Number(m[3] ?? 0)];
22
- }
23
-
24
- /** Classifies the bump from `from` to `to` (both may be ranges like `^1.2.3`). */
25
- export function classifyBump(from: string, to: string): Bump {
26
- const [fa, fb, fc] = parseVersion(from);
27
- const [ta, tb, tc] = parseVersion(to);
28
- if (ta !== fa) return 'major';
29
- if (tb !== fb) return 'minor';
30
- if (tc !== fc) return 'patch';
31
- return 'other';
32
- }
33
-
34
- /**
35
- * Parses the JSON object `npm-check-updates --jsonUpgraded` prints (a `{ name: range }` map). Tolerant
36
- * of leading/trailing noise (npx banners) by slicing to the outermost braces. Returns `{}` on failure.
37
- */
38
- export function parseNcuJson(stdout: string): Record<string, string> {
39
- const start = stdout.indexOf('{');
40
- const end = stdout.lastIndexOf('}');
41
- if (start === -1 || end <= start) return {};
42
- try {
43
- const parsed: unknown = JSON.parse(stdout.slice(start, end + 1));
44
- if (typeof parsed !== 'object' || parsed === null) return {};
45
- const out: Record<string, string> = {};
46
- for (const [k, v] of Object.entries(parsed)) if (typeof v === 'string') out[k] = v;
47
- return out;
48
- } catch {
49
- return {};
50
- }
51
- }
52
-
53
- /**
54
- * Majors `toiljs update` refuses to upgrade into, by package. TypeScript 7 is the native (Go) port:
55
- * it ships `tsc` but no JavaScript compiler API, which toiljs's route-metadata extractor, the eslint
56
- * preset, and typedoc all load. Upgrading into it silently stops SEO metadata being baked into the
57
- * built HTML and hard-crashes typescript-eslint, so the upgrade is withheld until the ecosystem
58
- * catches up. Bumps *within* the supported major are still offered.
59
- */
60
- export const UNSUPPORTED_MAJOR: Readonly<Record<string, number>> = { typescript: 7 };
61
-
62
- /**
63
- * The names ncu wants to upgrade into a major toiljs does not support (see {@link UNSUPPORTED_MAJOR}).
64
- * These are dropped from the picker and passed to ncu's `--reject`, so `-u` cannot apply them either.
65
- */
66
- export function withheldUpgrades(upgraded: Record<string, string>): string[] {
67
- return Object.entries(upgraded)
68
- .filter(([name, to]) => {
69
- const ceiling = UNSUPPORTED_MAJOR[name];
70
- return ceiling !== undefined && parseVersion(to)[0] >= ceiling;
71
- })
72
- .map(([name]) => name)
73
- .sort();
74
- }
75
-
76
- const SEVERITY: Record<Bump, number> = { major: 0, minor: 1, patch: 2, other: 3 };
77
-
78
- /**
79
- * Builds the upgrade rows from the ncu map and the project's current dependency ranges, sorted by
80
- * bump severity (major first) then name.
81
- */
82
- export function buildRows(
83
- upgraded: Record<string, string>,
84
- currentDeps: Record<string, string>,
85
- ): UpdateRow[] {
86
- return Object.entries(upgraded)
87
- .map(([name, to]) => {
88
- const from = currentDeps[name] ?? '?';
89
- return { name, from, to, bump: classifyBump(from, to) };
90
- })
91
- .sort((a, b) => SEVERITY[a.bump] - SEVERITY[b.bump] || a.name.localeCompare(b.name));
92
- }
@@ -1,31 +0,0 @@
1
- /**
2
- * Pure input validation for `toiljs create`, kept dependency-light (only node:path) so it can be
3
- * unit-tested without pulling in the rest of the CLI.
4
- */
5
- import path from 'node:path';
6
-
7
- /** Package managers the scaffolder may invoke. Allowlisted so a hostile `--pm` can't inject a shell command. */
8
- export const PACKAGE_MANAGERS = ['npm', 'pnpm', 'yarn', 'bun'];
9
-
10
- /** Validates a project name's characters. Returns `true`, or an error message. */
11
- export function isValidName(name: string): true | string {
12
- if (!name.trim()) return 'Please enter a project name.';
13
- if (!/^[a-z0-9._@/-]+$/i.test(name)) return 'Use letters, numbers, dashes, dots or slashes.';
14
- return true;
15
- }
16
-
17
- /**
18
- * Resolves `name` to an absolute directory under `cwd`, refusing to escape it (a name like
19
- * `../x` or an absolute path). Returns the resolved dir, or `null` if it would escape `cwd`.
20
- */
21
- export function resolveProjectDir(cwd: string, name: string): string | null {
22
- const target = path.resolve(cwd, name);
23
- const rel = path.relative(cwd, target);
24
- if (rel.startsWith('..') || path.isAbsolute(rel)) return null;
25
- return target;
26
- }
27
-
28
- /** Whether `pm` is a supported package manager (guards shell use of `--pm`). */
29
- export function isPackageManager(pm: string): boolean {
30
- return PACKAGE_MANAGERS.includes(pm);
31
- }
@@ -1,125 +0,0 @@
1
- /**
2
- * Pure helpers for the automatic toiljs update check that runs on every CLI invocation
3
- * (and therefore on `npm run dev` / `npm run build`, which call the CLI). IO-free so it
4
- * can be unit-tested; the registry fetch, cache file, and printing live in `notify.ts`.
5
- */
6
-
7
- /** How long a registry answer is trusted before we ask npm again. */
8
- export const CHECK_TTL_MS = 60 * 60 * 1000;
9
-
10
- /** What we persist between runs: the latest known version and when we asked. */
11
- export interface CheckCache {
12
- readonly latest: string | null;
13
- readonly checkedAt: number;
14
- }
15
-
16
- /** Parses the cache file contents; returns null when malformed (forces a fresh check). */
17
- export function parseCheckCache(raw: string): CheckCache | null {
18
- try {
19
- const parsed: unknown = JSON.parse(raw);
20
- if (typeof parsed !== 'object' || parsed === null) return null;
21
- const o = parsed as Record<string, unknown>;
22
- const latest = typeof o.latest === 'string' ? o.latest : null;
23
- const checkedAt = typeof o.checkedAt === 'number' ? o.checkedAt : NaN;
24
- if (!Number.isFinite(checkedAt)) return null;
25
- return { latest, checkedAt };
26
- } catch {
27
- return null;
28
- }
29
- }
30
-
31
- /** True when the cached answer is still trustworthy (also stale if the clock went backwards). */
32
- export function isCacheFresh(
33
- cache: CheckCache,
34
- now: number,
35
- ttlMs: number = CHECK_TTL_MS,
36
- ): boolean {
37
- return cache.checkedAt <= now && now - cache.checkedAt < ttlMs;
38
- }
39
-
40
- interface Parsed {
41
- readonly nums: readonly [number, number, number];
42
- readonly pre: string | null;
43
- }
44
-
45
- function parseSemver(v: string): Parsed {
46
- const m = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/.exec(v.trim());
47
- if (!m) return { nums: [0, 0, 0], pre: null };
48
- return { nums: [Number(m[1]), Number(m[2]), Number(m[3])], pre: m[4] ?? null };
49
- }
50
-
51
- /**
52
- * Compares two semver strings: negative when `a < b`, 0 when equal, positive when `a > b`.
53
- * A prerelease sorts below its release (`0.1.0-beta.1 < 0.1.0`); two prereleases compare
54
- * lexicographically, which is enough for an update nudge.
55
- */
56
- export function compareSemver(a: string, b: string): number {
57
- const pa = parseSemver(a);
58
- const pb = parseSemver(b);
59
- for (let i = 0; i < 3; i++) {
60
- if (pa.nums[i] !== pb.nums[i]) return pa.nums[i] < pb.nums[i] ? -1 : 1;
61
- }
62
- if (pa.pre === pb.pre) return 0;
63
- if (pa.pre === null) return 1;
64
- if (pb.pre === null) return -1;
65
- return pa.pre < pb.pre ? -1 : 1;
66
- }
67
-
68
- /** True when `installed` is behind `latest`. */
69
- export function isOutdated(installed: string, latest: string): boolean {
70
- return compareSemver(installed, latest) < 0;
71
- }
72
-
73
- /** The command that updates toiljs for the project's package manager, or globally. */
74
- export function installCommand(pm: string, scope: 'project' | 'global'): string {
75
- if (scope === 'global') {
76
- if (pm === 'pnpm') return 'pnpm add -g toiljs@latest';
77
- if (pm === 'yarn') return 'yarn global add toiljs@latest';
78
- if (pm === 'bun') return 'bun add -g toiljs@latest';
79
- return 'npm install -g toiljs@latest';
80
- }
81
- if (pm === 'pnpm') return 'pnpm add toiljs@latest';
82
- if (pm === 'yarn') return 'yarn add toiljs@latest';
83
- if (pm === 'bun') return 'bun add toiljs@latest';
84
- return 'npm install toiljs@latest';
85
- }
86
-
87
- /** One out-of-date install we want to nag about. */
88
- export interface OutdatedRow {
89
- /** Where the stale copy lives: the project's node_modules or the global CLI. */
90
- readonly scope: 'project' | 'global';
91
- readonly installed: string;
92
- readonly command: string;
93
- }
94
-
95
- /**
96
- * Decides what (if anything) to warn about. `projectVersion` is the toiljs resolved in the
97
- * project's node_modules (null when not installed there), `cliVersion` is the copy of the CLI
98
- * actually running, and `cliIsLocal` tells us whether those are the same install.
99
- */
100
- export function findOutdated(
101
- latest: string,
102
- projectVersion: string | null,
103
- cliVersion: string,
104
- cliIsLocal: boolean,
105
- pm: string,
106
- ): OutdatedRow[] {
107
- const rows: OutdatedRow[] = [];
108
- if (projectVersion !== null && isOutdated(projectVersion, latest)) {
109
- rows.push({
110
- scope: 'project',
111
- installed: projectVersion,
112
- command: installCommand(pm, 'project'),
113
- });
114
- }
115
- // The running CLI only gets its own row when it is not the project install we already
116
- // reported (global installs, or `npx toiljs` outside a project).
117
- if (!cliIsLocal && isOutdated(cliVersion, latest)) {
118
- rows.push({
119
- scope: 'global',
120
- installed: cliVersion,
121
- command: installCommand('npm', 'global'),
122
- });
123
- }
124
- return rows;
125
- }