@timber-js/app 0.1.33 → 0.1.35

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timber-js/app",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "description": "Vite-native React framework for Cloudflare Workers — correct HTTP semantics, real status codes, pages that work without JavaScript",
5
5
  "keywords": [
6
6
  "cloudflare-workers",
@@ -87,7 +87,8 @@
87
87
  "dependencies": {
88
88
  "@opentelemetry/api": "^1.9.0",
89
89
  "@opentelemetry/context-async-hooks": "^2.6.0",
90
- "@opentelemetry/sdk-trace-base": "^2.6.0"
90
+ "@opentelemetry/sdk-trace-base": "^2.6.0",
91
+ "nitro": "^3.0.0"
91
92
  },
92
93
  "peerDependencies": {
93
94
  "@content-collections/core": "^0.14.0",
@@ -6,8 +6,9 @@
6
6
  // See design/11-platform.md and design/25-production-deployments.md.
7
7
 
8
8
  import { writeFile, mkdir, cp } from 'node:fs/promises';
9
- import { execFile } from 'node:child_process';
9
+ import { execFile, execFileSync } from 'node:child_process';
10
10
  import { join, relative } from 'node:path';
11
+ import { createRequire } from 'node:module';
11
12
  import type { TimberPlatformAdapter, TimberConfig } from './types';
12
13
  // Inlined from server/asset-headers.ts — adapters are loaded by Node at
13
14
  // Vite startup time, before Vite's module resolver is available, so cross-
@@ -206,6 +207,10 @@ export function nitro(options: NitroAdapterOptions = {}): TimberPlatformAdapter
206
207
  // Generate the Nitro config with static asset cache rules
207
208
  const nitroConfig = generateNitroConfig(preset, options.nitroConfig);
208
209
  await writeFile(join(outDir, 'nitro.config.ts'), nitroConfig);
210
+
211
+ // Run the Nitro build to produce a production-ready server bundle.
212
+ // The output goes to dist/nitro/.output/server/index.mjs (for node-server preset).
213
+ await runNitroBuild(outDir);
209
214
  },
210
215
 
211
216
  // Only presets that produce a locally-runnable server get preview().
@@ -276,7 +281,7 @@ export function generateNitroEntry(
276
281
  return `// Generated by @timber-js/app/adapters/nitro
277
282
  // Do not edit — this file is regenerated on each build.
278
283
 
279
- ${manifestImport}import { defineEventHandler, toWebRequest, sendWebResponse } from 'h3'
284
+ ${manifestImport}import { defineEventHandler, toWebRequest, sendWebResponse } from 'nitro/h3'
280
285
  import handler, { runWithEarlyHintsSender } from '${serverEntryRelative}'
281
286
 
282
287
  // Set TIMBER_RUNTIME for instrumentation.ts conditional SDK initialization.
@@ -300,6 +305,7 @@ export function generateNitroConfig(
300
305
 
301
306
  const config: Record<string, unknown> = {
302
307
  preset: presetConfig.nitroPreset,
308
+ entry: './entry.ts',
303
309
  output: { dir: presetConfig.outputDir },
304
310
  // Static asset cache headers — hashed assets are immutable, others get 1h.
305
311
  // See design/25-production-deployments.md §"CDN / Edge Cache"
@@ -315,7 +321,7 @@ export function generateNitroConfig(
315
321
  return `// Generated by @timber-js/app/adapters/nitro
316
322
  // Do not edit — this file is regenerated on each build.
317
323
 
318
- import { defineNitroConfig } from 'nitropack/config'
324
+ import { defineNitroConfig } from 'nitro/config'
319
325
 
320
326
  export default defineNitroConfig(${configJson})
321
327
  `;
@@ -391,9 +397,10 @@ const MIME_TYPES = {
391
397
 
392
398
  const publicDir = join(__dirname, '${publicDir}');
393
399
  const port = parseInt(process.env.PORT || '3000', 10);
400
+ const host = process.env.HOST || process.env.HOSTNAME || 'localhost';
394
401
 
395
402
  const server = createServer(async (req, res) => {
396
- const url = new URL(req.url || '/', \`http://localhost:\${port}\`);
403
+ const url = new URL(req.url || '/', \`http://\${host}:\${port}\`);
397
404
 
398
405
  // Try serving static files from the public directory first.
399
406
  const filePath = join(publicDir, url.pathname);
@@ -485,11 +492,11 @@ const server = createServer(async (req, res) => {
485
492
  }
486
493
  });
487
494
 
488
- server.listen(port, () => {
495
+ server.listen(port, host, () => {
489
496
  console.log();
490
497
  console.log(' ⚡ timber preview server running at:');
491
498
  console.log();
492
- console.log(\` ➜ http://localhost:\${port}\`);
499
+ console.log(\` ➜ http://\${host}:\${port}\`);
493
500
  console.log();
494
501
  });
495
502
  `;
@@ -520,6 +527,29 @@ export function generateNitroPreviewCommand(
520
527
  };
521
528
  }
522
529
 
530
+ /**
531
+ * Run the Nitro production build.
532
+ * Resolves the `nitro` CLI binary from node_modules and runs `nitro build`
533
+ * in the given directory, which reads nitro.config.ts and produces
534
+ * .output/server/index.mjs (for node-server preset).
535
+ */
536
+ function runNitroBuild(nitroDir: string): Promise<void> {
537
+ // Resolve the nitro CLI binary from the dependency graph rather than
538
+ // relying on npx, which may not be available in all environments.
539
+ const require = createRequire(import.meta.url);
540
+ const nitroPkg = require.resolve('nitro/package.json');
541
+ const nitroBin = join(nitroPkg, '..', 'dist', 'cli', 'index.mjs');
542
+
543
+ return new Promise<void>((resolve, reject) => {
544
+ const child = execFile('node', [nitroBin, 'build'], { cwd: nitroDir }, (err) => {
545
+ if (err) reject(new Error(`Nitro build failed: ${err.message}`));
546
+ else resolve();
547
+ });
548
+ child.stdout?.pipe(process.stdout);
549
+ child.stderr?.pipe(process.stderr);
550
+ });
551
+ }
552
+
523
553
  /** Spawn a Nitro preview process and pipe stdio. */
524
554
  function spawnNitroPreview(command: string, args: string[], cwd: string): Promise<void> {
525
555
  return new Promise<void>((resolve, reject) => {
@@ -22,7 +22,7 @@ const IS_PENDING: LinkStatus = { pending: true };
22
22
  * and provides a scoped LinkStatusContext to children. Renders no extra DOM —
23
23
  * just a context provider around children.
24
24
  */
25
- export function LinkStatusProvider({ href, children }: { href: string; children: ReactNode }) {
25
+ export function LinkStatusProvider({ href, children }: { href: string; children?: ReactNode }) {
26
26
  const pendingUrl = usePendingNavigationUrl();
27
27
  const status = pendingUrl === href ? IS_PENDING : NOT_PENDING;
28
28