פריסה
לפריסה יש להשתמש ב-text build --adapter static, bun, vercel או cloudflare, או להריץ text start מאחורי פרוקסי.
text build יוצר ספריית dist/ ניידת. מתאם הופך אותה לפלט שהפלטפורמה מצפה לו.
מתאמים
text build --adapter <name>| מתאם | פלט | הרצה |
|---|---|---|
| (ללא) | dist/ + dist/.text/server | text start (Node) |
static | dist/ | כל שירות אירוח סטטי |
bun | dist/server.js | bun dist/server.js |
vercel | .vercel/output | Vercel (Build Output API v3) |
cloudflare | dist/ + _worker.js | Cloudflare Pages |
אפשר להגדיר את המתאם גם באמצעות TEXTJS_ADAPTER. כאשר text build רץ ב-Vercel (VERCEL=1) או ב-Cloudflare Pages (CF_PAGES=1), המתאם המתאים נבחר אוטומטית.
מתאמי השרת (bun, vercel ו-cloudflare) מפעילים את אותו רצף עיבוד כמו text start: middleware, rewrites, מטפלי נתיב עם מטמון הנתיבים, משאבים סטטיים, ואז מסמך המסוף עבור כתובות URL של דפים. לקוחות טרמינל כמו curl מקבלים במקום זאת את הפריים הראשון של הדף כטקסט ANSI. ב-vercel וב-cloudflare זה חל על דפים סטטיים בלבד (ראו curl ולקוחות טרמינל). מתאמי השרת מגישים קבצים ששמם כולל גיבוב תחת /assets/ עם Cache-Control: public, max-age=31536000, immutable.
אירוח סטטי
text build --adapter staticיש להשתמש בו בשירותי אירוח ללא שרת, כמו GitHub Pages, S3 או CDN. מטפלי נתיב מסוג GET עם dynamic = "force-static" או revalidate עוברים רינדור מראש לקבצים: app/api/feed.json/route.ts הופך ל-dist/api/feed.json. עותק של index.html נכתב אל 404.html כדי שקישורים עמוקים יפתחו את אפליקציית המסוף. שירותי אירוח סטטיים לא יכולים להבחין בין curl לדפדפן, ולכן curl מקבל את דף ה-HTML.
הבנייה נכשלת ומפרטת את כל הסיבות אם האפליקציה צריכה שרת: middleware.ts, נתיבים מרונדרים בצד השרת, מטפלי נתיב דינמיים, מטפלי נתיב עם מקטעים דינמיים, או מתודות שאינן GET. יש להגדיר rewrites בשירות האירוח במקום זאת.
Bun
text build --adapter bunPORT=8080 bun dist/server.jsהמתאם bun כותב שרת יחיד ועצמאי שלא זקוק ל-node_modules בזמן הריצה. הוא קורא את PORT (ברירת מחדל 3000) ואת HOSTNAME (ברירת מחדל 0.0.0.0), ולעולם אינו מגיש את server.js או את .text/. זהו תהליך ארוך חיים, ולכן הוא מארח גם נתיבים מרונדרים בצד השרת (export const runtime = "server") ואת הסשנים הפעילים שלהם, כמו text start. לשימוש בקונטיינר:
FROM oven/bun:1COPY dist /appCMD ["bun", "/app/server.js"]Vercel
text build --adapter vercelvercel deploy --prebuiltאפשר גם לדחוף למאגר Git שמחובר ל-Vercel: הבנייה מריצה את text build, מזהה את Vercel וכותבת את .vercel/output.
| פלט | תוכן |
|---|---|
static/ | תוצר הבנייה של הלקוח (תחת basePath) |
functions/_textjs/server.func | פונקציית Node.js 22 אחת שמשרתת כל מטפל נתיב, עם הזרמה |
functions/_middleware.func | Edge Routing Middleware: middleware.ts, ונתיבים קנוניים לכל אפליקציה |
config.json | כותרות משאבים, כללי התאמה של middleware, שכתובים, נתיבי מטפלים ומסמך המסוף |
ה-edge middleware משתמש בפרוטוקול x-middleware-next / x-middleware-rewrite, ולכן דריסות כותרות ושכתובים מתבצעים בנתב של Vercel לפני ההגעה לפונקציה או ל-CDN. כל כללי ההתאמה חולקים תבנית src אחת של נתיב. בקשות שאף כלל התאמה לא מכסה מדלגות על ה-middleware, אלא אם ייתכן שהנתיב שלהן אינו בצורה קנונית (קידודי אחוזים, תווים שמנתח URL מקודד, //, מקטעי . או .., לוכסן בסוף). לאחר מכן ה-middleware מתאים את הנתיב הקנוני, בודק has / missing, ומשכתב את הבקשה לנתיב הזה כדי ש-Vercel ינתב אותה באותו אופן. אפליקציות ללא middleware.ts מקבלות את אותה פונקציית edge, שמבצעת רק את השכתוב. מכיוון שכל בנייה כוללת פונקציית Edge, יש לפרוס מחשבון Vercel מחובר: פריסות אנונימיות (vercel deploy --temporary) לא מקבלות פונקציות Edge.
פונקציית המטפל היא פנימית. כל נתיב מטפל מעביר לה את הנתיב התואם בכותרת הבקשה x-textjs-path. הכותרת הזאת מוסרת מכל בקשה לפני ואחרי middleware, וה-edge middleware לעולם אינו מעביר כותרת כזאת (מהלקוח או מ-middleware.ts), כך שרק נתיבי המטפלים יכולים להגדיר אותה. בקשות אל /_textjs/* מחזירות 404 בלי להריץ את הפונקציה, לפני ואחרי middleware ושכתובים. הנתיבים שנוצרים מותאמים תוך הבחנה בין אותיות גדולות לקטנות, כמו כללי ההתאמה והשכתובים בכל שירות אירוח אחר.
בדיקות המתאם מריצות את הפלט הזה מול אמולציה מקומית של הנתב של Vercel. ההתנהגות שלו בהתאמת src, בלכידות dest, בהמרות כותרות ובנתיבי status נבדקה מול פריסות אמיתיות; Edge middleware לא נבדק כך, משום שאי אפשר לפרוס אותו באופן אנונימי.
Cloudflare Pages
text build --adapter cloudflarebunx wrangler pages dev dist --compatibility-flags=nodejs_compatbunx wrangler pages deploy distהפלט משתמש במצב המתקדם של Pages:
| קובץ | מטרה |
|---|---|
_worker.js | Middleware, שכתובים, מטפלי נתיב ומסמך המסוף |
_routes.json | שולח כל בקשה ל-worker. ללא middleware או basePath, הנתיב /assets/* נשאר ב-CDN הסטטי של Pages |
_headers | שמירה בלתי משתנה במטמון עבור /assets/* (נוספת לסוף הקובץ הקיים) |
404.html | גורם ל-Pages להשיב לקבצים חסרים עם 404 במקום עם האפליקציה (נשמר אם כבר קיים) |
.text/ (חבילות שרת ומניפסטים של הבנייה) מוסרת מ-dist/, משום ש-Pages מפרסם את הספרייה כפי שהיא.
יש להפעיל את דגל התאימות nodejs_compat בפרויקט Pages (Settings → Runtime) או ב-wrangler.toml; מטמון הנתונים משתמש ב-AsyncLocalStorage. קבצים סטטיים מוגשים דרך הקישור ASSETS, ואימות מחדש ברקע משתמש ב-ctx.waitUntil().
עם middleware, כל בקשה מפעילה את ה-worker, כולל משאבים ששמם כולל גיבוב, כדי שה-middleware יראה כל נתיב שדרכו אפשר להגיע לקובץ. כל בקשה שמפעילה את ה-worker היא הפעלה אחת של Functions ונספרת במכסת הבקשות של Workers. בטעינת דף, מדובר במסמך ובכל קובץ שהוא מביא (2 עבור אפליקציית בדיקה קטנה עם חבילת JavaScript אחת; יותר עבור אפליקציות עם יותר קבצים). בתוכנית Workers Free, לאחר מיצוי המכסה היומית Pages מגיש קבצים סטטיים ללא ה-worker, וכך מדלג על middleware. אם middleware מגן על משהו ב-dist/, יש להגדיר את Settings → Runtime → Fail open / closed לערך Fail closed.
name = "my-console"pages_build_output_dir = "dist"compatibility_date = "2025-01-01"compatibility_flags = ["nodejs_compat"]שדרוג
תיקוני אבטחה ב-@textjs/app מגיעים לפריסה רק לאחר בנייה ופריסה מחדש:
- Vercel: יש לפרוס את
.vercel/outputהחדש. - Cloudflare Pages: יש לפרוס את
dist/החדש, עם_worker.jsו-_routes.json. - Bun: יש לפרוס את כל
dist/החדש (server.js, משאבים וחבילות פעילות) ולהפעיל מחדש את השרת. text start: יש להריץtext buildולהפעיל מחדש.
פריסות ישנות נשארות נגישות בכתובות ה-URL שלהן (למשל כתובות תצוגה מקדימה של Vercel ושל Cloudflare Pages). יש למחוק אותן או להציב אותן מאחורי הגנת הגישה של הפלטפורמה.
נתיבים מרונדרים בצד השרת
נתיבים מרונדרים בצד השרת שומרים סשן לכל כרטיסייה פתוחה, ולכן הם זקוקים לשרת ארוך חיים: text start או המתאם bun. המתאמים static, vercel ו-cloudflare מכשילים את הבנייה עם TX030 כאשר דף מייצא runtime = "server".
מאחורי שרת מתווך
text start נקשר ל-127.0.0.1, וכל עוד הוא קשור לכתובת loopback, הוא עונה רק ל-localhost, ל-*.localhost ולמארחים שכתובתם היא כתובת IP (הגנה מפני DNS rebinding). מארחים אחרים מקבלים 403 עם TX028. יש למקם את ההגדרות הציבוריות ב-text.config.ts:
import { defineConfig } from "@textjs/app";export default defineConfig({ server: { origin: "https://console.example.com", // request.url always uses it (or TEXTJS_ORIGIN) trustProxy: true, // read X-Forwarded-Host / -Proto from your proxy (or TRUST_PROXY=1) allowedHosts: [".example.com"], },});ללא origin או trustProxy, הערך request.url נבנה מכותרת Host שאומתה, בפרוטוקול http.
סייגים
proxyב-text.config.tsרץ רק ב-text devוב-text start; המתאמים לא כוללים אותו. יש להשתמש ב-rewrites, שכל מתאם שרת תומך בהם.- לכל מופע serverless יש מטמון משלו בזיכרון. ראו שמירה במטמון.
rewritesשמשתמשים בקבוצות גולמיות של ביטויים רגולריים מתורגמים לנתיבי Vercel במידת האפשר; פרמטרים בעלי שם (:slug,:path*) מתורגמים במדויק.