@agent-native/core 0.90.8 → 0.90.9

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/corpus/README.md CHANGED
@@ -27,5 +27,5 @@ rg -n "defineAction|useActionQuery" node_modules/@agent-native/core/corpus
27
27
 
28
28
  ## Generated Counts
29
29
 
30
- - core files: 2144
30
+ - core files: 2145
31
31
  - template files: 4835
@@ -1,5 +1,11 @@
1
1
  # @agent-native/core
2
2
 
3
+ ## 0.90.9
4
+
5
+ ### Patch Changes
6
+
7
+ - a1db610: Fix Netlify single-template deploy previews by keeping Nitro's `preferStatic` true (so `/assets/*` is served from `dist`) and stripping the harmful default-function URL rewrite that is incompatible with `config.path: "/*"`.
8
+
3
9
  ## 0.90.8
4
10
 
5
11
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.90.8",
3
+ "version": "0.90.9",
4
4
  "description": "Framework for agent-native application development — where AI agents and UI share SQL state, actions, and context",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -0,0 +1,50 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export interface DeployPostBuildInvocation {
5
+ command: string;
6
+ args: string[];
7
+ scriptPath: string;
8
+ }
9
+
10
+ export function hasDeployPreset(env: NodeJS.ProcessEnv = process.env): boolean {
11
+ return Boolean(env.NITRO_PRESET?.trim());
12
+ }
13
+
14
+ export function resolveDeployPostBuildInvocation({
15
+ cliDir,
16
+ env = process.env,
17
+ findTsxBin,
18
+ }: {
19
+ cliDir: string;
20
+ env?: NodeJS.ProcessEnv;
21
+ findTsxBin: () => string;
22
+ }): DeployPostBuildInvocation | undefined {
23
+ const builtDeployBuild = path.resolve(cliDir, "../deploy/build.js");
24
+ if (fs.existsSync(builtDeployBuild)) {
25
+ return {
26
+ command: "node",
27
+ args: [builtDeployBuild],
28
+ scriptPath: builtDeployBuild,
29
+ };
30
+ }
31
+
32
+ const sourceDeployBuild = path.resolve(cliDir, "../deploy/build.ts");
33
+ if (fs.existsSync(sourceDeployBuild)) {
34
+ return {
35
+ command: findTsxBin(),
36
+ args: [sourceDeployBuild],
37
+ scriptPath: sourceDeployBuild,
38
+ };
39
+ }
40
+
41
+ if (hasDeployPreset(env)) {
42
+ throw new Error(
43
+ `[build] Deploy build script not found. Expected ${builtDeployBuild} ` +
44
+ `or ${sourceDeployBuild}; refusing to publish an incomplete ` +
45
+ `NITRO_PRESET=${env.NITRO_PRESET} build.`,
46
+ );
47
+ }
48
+
49
+ return undefined;
50
+ }
@@ -8,6 +8,8 @@ import { fileURLToPath } from "url";
8
8
 
9
9
  import * as Sentry from "@sentry/node";
10
10
 
11
+ import { resolveDeployPostBuildInvocation } from "./deploy-build.js";
12
+
11
13
  // Resolve version once at module scope — used by both --version and --help
12
14
  let _version = "unknown";
13
15
  try {
@@ -577,15 +579,18 @@ switch (command) {
577
579
  // `agent-native start` and for serverless presets.
578
580
  if (isReactRouterFramework()) {
579
581
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
580
- const deployBuild = path.resolve(__dirname, "../deploy/build.js");
581
- if (fs.existsSync(deployBuild)) {
582
- await runBuildStep("node", [deployBuild], {
582
+ const deployBuild = resolveDeployPostBuildInvocation({
583
+ cliDir: __dirname,
584
+ findTsxBin,
585
+ });
586
+ if (deployBuild) {
587
+ await runBuildStep(deployBuild.command, deployBuild.args, {
583
588
  label: "deploy-build",
584
589
  env: process.env,
585
590
  });
586
591
  } else {
587
592
  console.warn(
588
- `[build] Deploy build script not found at ${deployBuild}. Skipping post-build step.`,
593
+ "[build] Deploy build script not found and no deploy preset is configured. Skipping post-build step.",
589
594
  );
590
595
  }
591
596
  }
@@ -2427,6 +2427,16 @@ export const config = {
2427
2427
  );
2428
2428
  }
2429
2429
 
2430
+ /**
2431
+ * Nitro's Netlify preset can emit a harmful fallback rewrite to
2432
+ * `/.netlify/functions/server`. With `config.path: "/*"`, that default URL is
2433
+ * removed, so the rewrite publishes platform 404s. Single-template deploys keep
2434
+ * Nitro's `preferStatic: true` so hashed `/assets/*` files in dist win before
2435
+ * the SSR catch-all runs.
2436
+ */
2437
+ const NETLIFY_DEFAULT_FUNCTION_URL_REDIRECT =
2438
+ "/* /.netlify/functions/server 200";
2439
+
2430
2440
  export function assertSingleTemplateNetlifyBuildOutput(
2431
2441
  projectCwd: string,
2432
2442
  ): void {
@@ -2440,21 +2450,31 @@ export function assertSingleTemplateNetlifyBuildOutput(
2440
2450
 
2441
2451
  if (!fs.existsSync(publishDir)) {
2442
2452
  failures.push("missing publish directory: dist");
2443
- } else if (!fs.existsSync(redirectsPath)) {
2444
- failures.push("missing Netlify fallback redirects file: dist/_redirects");
2445
2453
  } else {
2454
+ const assetsDir = path.join(publishDir, "assets");
2455
+ if (
2456
+ !fs.existsSync(assetsDir) ||
2457
+ fs.readdirSync(assetsDir).every((name) => name.startsWith("."))
2458
+ ) {
2459
+ failures.push(
2460
+ "dist/assets is missing hashed client assets — the publish dir would load an infinite spinner",
2461
+ );
2462
+ }
2463
+ }
2464
+
2465
+ if (fs.existsSync(publishDir) && fs.existsSync(redirectsPath)) {
2446
2466
  const redirects = fs.readFileSync(redirectsPath, "utf-8");
2447
2467
  if (
2448
- !redirects
2468
+ redirects
2449
2469
  .split(/\r?\n/)
2450
2470
  .some(
2451
2471
  (line) =>
2452
2472
  line.trim().replace(/\s+/g, " ") ===
2453
- "/* /.netlify/functions/server 200",
2473
+ NETLIFY_DEFAULT_FUNCTION_URL_REDIRECT,
2454
2474
  )
2455
2475
  ) {
2456
2476
  failures.push(
2457
- 'dist/_redirects is missing "/* /.netlify/functions/server 200"',
2477
+ 'dist/_redirects must not contain "/* /.netlify/functions/server 200" — Nitro\'s custom config.path: "/*" removes that default function URL',
2458
2478
  );
2459
2479
  }
2460
2480
  }
@@ -2492,6 +2512,11 @@ export function assertSingleTemplateNetlifyBuildOutput(
2492
2512
  "Netlify server entry does not reference the generated main.mjs bundle",
2493
2513
  );
2494
2514
  }
2515
+ if (!/\bpreferStatic:\s*true\b/.test(serverEntry)) {
2516
+ failures.push(
2517
+ "Netlify server entry must keep preferStatic: true so /assets/* is served from dist before the SSR catch-all",
2518
+ );
2519
+ }
2495
2520
  }
2496
2521
 
2497
2522
  if (isDurableBackgroundDeployEnabled()) {
@@ -2544,37 +2569,49 @@ export function assertSingleTemplateNetlifyBuildOutput(
2544
2569
  );
2545
2570
  }
2546
2571
 
2572
+ /**
2573
+ * Strip the harmful single-template catch-all rewrite that points at
2574
+ * `/.netlify/functions/server`. Nitro declares `config.path: "/*"`, which
2575
+ * removes the default function URL, so rewriting to that URL publishes
2576
+ * Netlify platform 404s. Preserve any real redirects from `public/_redirects`.
2577
+ */
2547
2578
  export function writeSingleTemplateNetlifyRedirects(projectCwd: string): void {
2548
2579
  const publishDir = path.join(projectCwd, "dist");
2549
- if (!fs.existsSync(publishDir)) return;
2550
-
2551
2580
  const redirectsPath = path.join(publishDir, "_redirects");
2552
- const existing = fs.existsSync(redirectsPath)
2553
- ? fs.readFileSync(redirectsPath, "utf-8").trimEnd()
2554
- : "";
2555
- const lines = existing ? existing.split(/\r?\n/) : [];
2556
-
2557
- function hasRedirect(line: string): boolean {
2558
- return lines.some(
2559
- (existingLine) => existingLine.trim().replace(/\s+/g, " ") === line,
2560
- );
2561
- }
2581
+ if (!fs.existsSync(redirectsPath)) return;
2562
2582
 
2563
- const fallbackRules = [
2564
- "# Generated by agent-native build for Netlify single-template deploys",
2565
- "# Static files are served first; dynamic routes fall through to the server function.",
2566
- "/* /.netlify/functions/server 200",
2567
- ];
2583
+ const existing = fs.readFileSync(redirectsPath, "utf-8");
2584
+ const kept: string[] = [];
2585
+ let removed = 0;
2568
2586
 
2569
- for (const line of fallbackRules) {
2570
- if (line.startsWith("#") || !hasRedirect(line)) {
2571
- lines.push(line);
2587
+ for (const line of existing.split(/\r?\n/)) {
2588
+ const normalized = line.trim().replace(/\s+/g, " ");
2589
+ if (
2590
+ normalized === NETLIFY_DEFAULT_FUNCTION_URL_REDIRECT ||
2591
+ normalized ===
2592
+ "# Generated by agent-native build for Netlify single-template deploys" ||
2593
+ normalized ===
2594
+ "# Static files are served first; dynamic routes fall through to the server function."
2595
+ ) {
2596
+ removed += 1;
2597
+ continue;
2572
2598
  }
2599
+ kept.push(line);
2600
+ }
2601
+
2602
+ while (kept.length > 0 && kept[kept.length - 1].trim() === "") {
2603
+ kept.pop();
2573
2604
  }
2574
2605
 
2575
- fs.writeFileSync(redirectsPath, lines.join("\n") + "\n");
2606
+ if (removed === 0) return;
2607
+
2608
+ if (kept.every((line) => line.trim() === "")) {
2609
+ fs.rmSync(redirectsPath, { force: true });
2610
+ } else {
2611
+ fs.writeFileSync(redirectsPath, kept.join("\n").trimEnd() + "\n");
2612
+ }
2576
2613
  console.log(
2577
- "[deploy] Wrote Netlify fallback redirect /* -> /.netlify/functions/server.",
2614
+ '[deploy] Removed Netlify fallback rewrite to /.netlify/functions/server (incompatible with Nitro config.path: "/*").',
2578
2615
  );
2579
2616
  }
2580
2617
 
@@ -0,0 +1,12 @@
1
+ export interface DeployPostBuildInvocation {
2
+ command: string;
3
+ args: string[];
4
+ scriptPath: string;
5
+ }
6
+ export declare function hasDeployPreset(env?: NodeJS.ProcessEnv): boolean;
7
+ export declare function resolveDeployPostBuildInvocation({ cliDir, env, findTsxBin, }: {
8
+ cliDir: string;
9
+ env?: NodeJS.ProcessEnv;
10
+ findTsxBin: () => string;
11
+ }): DeployPostBuildInvocation | undefined;
12
+ //# sourceMappingURL=deploy-build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy-build.d.ts","sourceRoot":"","sources":["../../src/cli/deploy-build.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,yBAAyB;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAE7E;AAED,wBAAgB,gCAAgC,CAAC,EAC/C,MAAM,EACN,GAAiB,EACjB,UAAU,GACX,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,UAAU,EAAE,MAAM,MAAM,CAAC;CAC1B,GAAG,yBAAyB,GAAG,SAAS,CA4BxC"}
@@ -0,0 +1,30 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ export function hasDeployPreset(env = process.env) {
4
+ return Boolean(env.NITRO_PRESET?.trim());
5
+ }
6
+ export function resolveDeployPostBuildInvocation({ cliDir, env = process.env, findTsxBin, }) {
7
+ const builtDeployBuild = path.resolve(cliDir, "../deploy/build.js");
8
+ if (fs.existsSync(builtDeployBuild)) {
9
+ return {
10
+ command: "node",
11
+ args: [builtDeployBuild],
12
+ scriptPath: builtDeployBuild,
13
+ };
14
+ }
15
+ const sourceDeployBuild = path.resolve(cliDir, "../deploy/build.ts");
16
+ if (fs.existsSync(sourceDeployBuild)) {
17
+ return {
18
+ command: findTsxBin(),
19
+ args: [sourceDeployBuild],
20
+ scriptPath: sourceDeployBuild,
21
+ };
22
+ }
23
+ if (hasDeployPreset(env)) {
24
+ throw new Error(`[build] Deploy build script not found. Expected ${builtDeployBuild} ` +
25
+ `or ${sourceDeployBuild}; refusing to publish an incomplete ` +
26
+ `NITRO_PRESET=${env.NITRO_PRESET} build.`);
27
+ }
28
+ return undefined;
29
+ }
30
+ //# sourceMappingURL=deploy-build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy-build.js","sourceRoot":"","sources":["../../src/cli/deploy-build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAQxB,MAAM,UAAU,eAAe,CAAC,GAAG,GAAsB,OAAO,CAAC,GAAG;IAClE,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,gCAAgC,CAAC,EAC/C,MAAM,EACN,GAAG,GAAG,OAAO,CAAC,GAAG,EACjB,UAAU,GAKX;IACC,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IACpE,IAAI,EAAE,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACpC,OAAO;YACL,OAAO,EAAE,MAAM;YACf,IAAI,EAAE,CAAC,gBAAgB,CAAC;YACxB,UAAU,EAAE,gBAAgB;SAC7B,CAAC;IACJ,CAAC;IAED,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IACrE,IAAI,EAAE,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACrC,OAAO;YACL,OAAO,EAAE,UAAU,EAAE;YACrB,IAAI,EAAE,CAAC,iBAAiB,CAAC;YACzB,UAAU,EAAE,iBAAiB;SAC9B,CAAC;IACJ,CAAC;IAED,IAAI,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,mDAAmD,gBAAgB,GAAG;YACpE,MAAM,iBAAiB,sCAAsC;YAC7D,gBAAgB,GAAG,CAAC,YAAY,SAAS,CAC5C,CAAC;IACJ,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC","sourcesContent":["import fs from \"fs\";\nimport path from \"path\";\n\nexport interface DeployPostBuildInvocation {\n command: string;\n args: string[];\n scriptPath: string;\n}\n\nexport function hasDeployPreset(env: NodeJS.ProcessEnv = process.env): boolean {\n return Boolean(env.NITRO_PRESET?.trim());\n}\n\nexport function resolveDeployPostBuildInvocation({\n cliDir,\n env = process.env,\n findTsxBin,\n}: {\n cliDir: string;\n env?: NodeJS.ProcessEnv;\n findTsxBin: () => string;\n}): DeployPostBuildInvocation | undefined {\n const builtDeployBuild = path.resolve(cliDir, \"../deploy/build.js\");\n if (fs.existsSync(builtDeployBuild)) {\n return {\n command: \"node\",\n args: [builtDeployBuild],\n scriptPath: builtDeployBuild,\n };\n }\n\n const sourceDeployBuild = path.resolve(cliDir, \"../deploy/build.ts\");\n if (fs.existsSync(sourceDeployBuild)) {\n return {\n command: findTsxBin(),\n args: [sourceDeployBuild],\n scriptPath: sourceDeployBuild,\n };\n }\n\n if (hasDeployPreset(env)) {\n throw new Error(\n `[build] Deploy build script not found. Expected ${builtDeployBuild} ` +\n `or ${sourceDeployBuild}; refusing to publish an incomplete ` +\n `NITRO_PRESET=${env.NITRO_PRESET} build.`,\n );\n }\n\n return undefined;\n}\n"]}
package/dist/cli/index.js CHANGED
@@ -5,6 +5,7 @@ import { createRequire } from "module";
5
5
  import path from "path";
6
6
  import { fileURLToPath } from "url";
7
7
  import * as Sentry from "@sentry/node";
8
+ import { resolveDeployPostBuildInvocation } from "./deploy-build.js";
8
9
  // Resolve version once at module scope — used by both --version and --help
9
10
  let _version = "unknown";
10
11
  try {
@@ -504,15 +505,18 @@ switch (command) {
504
505
  // `agent-native start` and for serverless presets.
505
506
  if (isReactRouterFramework()) {
506
507
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
507
- const deployBuild = path.resolve(__dirname, "../deploy/build.js");
508
- if (fs.existsSync(deployBuild)) {
509
- await runBuildStep("node", [deployBuild], {
508
+ const deployBuild = resolveDeployPostBuildInvocation({
509
+ cliDir: __dirname,
510
+ findTsxBin,
511
+ });
512
+ if (deployBuild) {
513
+ await runBuildStep(deployBuild.command, deployBuild.args, {
510
514
  label: "deploy-build",
511
515
  env: process.env,
512
516
  });
513
517
  }
514
518
  else {
515
- console.warn(`[build] Deploy build script not found at ${deployBuild}. Skipping post-build step.`);
519
+ console.warn("[build] Deploy build script not found and no deploy preset is configured. Skipping post-build step.");
516
520
  }
517
521
  }
518
522
  console.log("\nBuild complete.");