@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 +1 -1
- package/corpus/core/CHANGELOG.md +13 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/cli/deploy-build.ts +50 -0
- package/corpus/core/src/cli/index.ts +9 -4
- package/corpus/core/src/deploy/build.ts +90 -0
- package/dist/cli/deploy-build.d.ts +12 -0
- package/dist/cli/deploy-build.d.ts.map +1 -0
- package/dist/cli/deploy-build.js +30 -0
- package/dist/cli/deploy-build.js.map +1 -0
- package/dist/cli/index.js +8 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/deploy/build.d.ts +7 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +68 -0
- package/dist/deploy/build.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/resources/handlers.d.ts +3 -3
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/package.json +1 -1
package/corpus/README.md
CHANGED
package/corpus/core/CHANGELOG.md
CHANGED
|
@@ -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
|
package/corpus/core/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/core",
|
|
3
|
-
"version": "0.90.
|
|
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 =
|
|
581
|
-
|
|
582
|
-
|
|
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
|
-
|
|
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 =
|
|
508
|
-
|
|
509
|
-
|
|
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(
|
|
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.");
|