@agent-native/core 0.90.7 → 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,18 @@
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
+
9
+ ## 0.90.8
10
+
11
+ ### Patch Changes
12
+
13
+ - 1de8510: Allow public Builder waitlist signups to submit an explicit email address for docs build-online CTAs.
14
+ - 1de8510: Emit and require a Netlify fallback redirect so single-template deploys route dynamic app requests to the server function instead of publishing platform 404s.
15
+
3
16
  ## 0.90.7
4
17
 
5
18
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/core",
3
- "version": "0.90.7",
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,11 +2427,22 @@ 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 {
2433
2443
  const failures: string[] = [];
2434
2444
  const publishDir = path.join(projectCwd, "dist");
2445
+ const redirectsPath = path.join(publishDir, "_redirects");
2435
2446
  const internalDir = path.join(projectCwd, ".netlify", "functions-internal");
2436
2447
  const serverDir = path.join(internalDir, "server");
2437
2448
  const serverEntryPath = path.join(serverDir, "server.mjs");
@@ -2439,6 +2450,33 @@ export function assertSingleTemplateNetlifyBuildOutput(
2439
2450
 
2440
2451
  if (!fs.existsSync(publishDir)) {
2441
2452
  failures.push("missing publish directory: dist");
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)) {
2466
+ const redirects = fs.readFileSync(redirectsPath, "utf-8");
2467
+ if (
2468
+ redirects
2469
+ .split(/\r?\n/)
2470
+ .some(
2471
+ (line) =>
2472
+ line.trim().replace(/\s+/g, " ") ===
2473
+ NETLIFY_DEFAULT_FUNCTION_URL_REDIRECT,
2474
+ )
2475
+ ) {
2476
+ failures.push(
2477
+ 'dist/_redirects must not contain "/* /.netlify/functions/server 200" — Nitro\'s custom config.path: "/*" removes that default function URL',
2478
+ );
2479
+ }
2442
2480
  }
2443
2481
 
2444
2482
  if (!fs.existsSync(serverDir)) {
@@ -2474,6 +2512,11 @@ export function assertSingleTemplateNetlifyBuildOutput(
2474
2512
  "Netlify server entry does not reference the generated main.mjs bundle",
2475
2513
  );
2476
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
+ }
2477
2520
  }
2478
2521
 
2479
2522
  if (isDurableBackgroundDeployEnabled()) {
@@ -2526,6 +2569,52 @@ export function assertSingleTemplateNetlifyBuildOutput(
2526
2569
  );
2527
2570
  }
2528
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
+ */
2578
+ export function writeSingleTemplateNetlifyRedirects(projectCwd: string): void {
2579
+ const publishDir = path.join(projectCwd, "dist");
2580
+ const redirectsPath = path.join(publishDir, "_redirects");
2581
+ if (!fs.existsSync(redirectsPath)) return;
2582
+
2583
+ const existing = fs.readFileSync(redirectsPath, "utf-8");
2584
+ const kept: string[] = [];
2585
+ let removed = 0;
2586
+
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;
2598
+ }
2599
+ kept.push(line);
2600
+ }
2601
+
2602
+ while (kept.length > 0 && kept[kept.length - 1].trim() === "") {
2603
+ kept.pop();
2604
+ }
2605
+
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
+ }
2613
+ console.log(
2614
+ '[deploy] Removed Netlify fallback rewrite to /.netlify/functions/server (incompatible with Nitro config.path: "/*").',
2615
+ );
2616
+ }
2617
+
2529
2618
  function copyInstalledLibsqlNativePackages(serverDir: string | undefined) {
2530
2619
  if (!serverDir || !fs.existsSync(serverDir)) return;
2531
2620
  const nodeModulesRoots = nodeModulesAncestors(cwd);
@@ -3076,6 +3165,7 @@ export default bundle;
3076
3165
  }
3077
3166
 
3078
3167
  if (preset === "netlify") {
3168
+ writeSingleTemplateNetlifyRedirects(cwd);
3079
3169
  assertSingleTemplateNetlifyBuildOutput(cwd);
3080
3170
  }
3081
3171
 
@@ -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.");