אפליקציית Xuda היא אוסף של מסמכים מוטיפסים, ומפרש אחד מריץ אותם.
Studio אינו מייצר עבורכם בסיס קוד שתצטרכו לתחזק. פרויקט הוא אוסף שטוח של מסמכים, כל אחד מהם מוחתם ב-docType studio, בשנים עשר טיפוסי צמתים: רכיבים, טבלאות, יחידות נתונים, נתיבים, סוכנים והשאר. העץ בפאנל השמאלי נגזר מאותם מסמכים בזמן הטעינה, ממצביע הורה אחד על כל אחד מהם, ולעולם אינו נשמר כעץ בפני עצמו. אותם מסמכים עצמם הם מה שתצוגה מקדימה מבקשת לפי מזהה, מה שבנייה עוברת עליו, ומה שזמן הריצה מבצע כשמישהו פותח את האפליקציה. שום דבר באמצע אינו מהדר אותם לדבר אחר.

טיפוסי צמתים שפרויקט בנוי מהם
טיפוסי אירועים שרכיב יכול להגיב להם
טיפוסי פריסה שאותו פרויקט יכול לצאת בהם
תוספים מתוצרת הבית, בשש קטגוריות
חמישה עצים נפרדים, ובשום מקום אין מסמך עץ.
הפאנל השמאלי אינו מחזיק עץ אחד. הוא מחזיק חמישה, ובורר אייקונים בעל חמישה מצבים מעליו קובע איזה מהם מורכב, כך שטבלאות, מסכים, מצב משותף, כתובות וסוכנים הם חמישה מרחבי כתובות נפרדים ולא חמישה ענפים של אותו אחד. גם אף אחד מהחמישה אינו נשמר כעץ. כל צומת הוא מסמך שנושא מצביע parentId יחיד ומפתח מיון order_ts, ו-worker בונה מחדש את כל המבנה בזמן הטעינה על ידי סריקת ילדים רמה אחר רמה, כשהוא מקבל רק מסמכים שה-stat שלהם הוא 3. הזזת צומת למעלה או למטה אינה העברת קובץ: היא שומרת בדיוק שני מסמכים כשערכי ה-order_ts שלהם הוחלפו. מחיקת תת עץ כותבת stat 4 על כל מסמך בתוכו ומשאירה את כולם באחסון, ולכן העץ מפסיק להציג אותם ומנוע החיפוש מפסיק לאנדקס אותם באותו רגע. פתחו רכיב ועברו ללשונית ה-UI שלו ועץ שני מופיע מתחת לראשון, עץ האלמנטים של אותו רכיב, עם ידית גרירה ביניהם שמיקומה נזכר.
ארבע לשוניות נכון לגבי רכיב. כל טיפוס אחר פותח משהו אחר.
הגרסה של הדף הזה שאולי קראתם אמרה שמסכים, משימות רקע ונקודות קצה של API הם אותו סוג אובייקט עם אותן ארבע לשוניות. החצי הראשון נכון, השני לא. העורך בונה את רצועת הלשוניות שלו לפי טיפוס הצומת, והמעטפת מתכווצת וגדלה: רכיב מקבל ארבע לשוניות, batch או יחידת נתונים מקבלים שלוש ואין להם UI כלל, יחידת API מקבלת ארבע אבל הרביעית היא Output ולא UI, וחמישה מתוך שנים עשר הטיפוסים פותחים לשונית אחת או אף אחת: alert, javascript, route ו-ai_agent פותחים כל אחד עורך אחד, ותיקייה אינה פותחת דבר. הרצועה מצוירת רק כשלצומת יש יותר מלשונית אחת, ולכן חמישה אלה מגיעים בלי רצועה מעליהם. איזו לשונית פתוחה הוא פרמטר בכתובת העורך, והלשונית האחרונה שהייתה לכם על צומת מסוים נזכרת לכל צומת בנפרד, כך שחזרה אליו מחזירה אתכם למקום שבו הפסקתם. העמודה השנייה היא החצי השני של אותו דקדוק. פאנל ה-Create מציע אוסף טיפוסים שונה בהתאם לצומת שעליו לחצתם לחיצה ימנית, ותיקייה פשוט יורשת את האוסף של המקטע שבו היא יושבת.
מה זה פותח | איפה אפשר ליצור אותו | |
|---|---|---|
| Component | Datasource, Fields, Events, UI | תחת Programs, או בתוך רכיב אחר |
| API | Datasource, Fields, Events, Output | תחת Programs או כל יחידה אחרת, לעולם לא בתוך רכיב |
| Batch | Datasource, Fields, Events. אין לשונית UI | Programs, בתוך רכיב, או בתוך כל יחידה אחרת |
| Get data (get_data) | Datasource, Fields, Events. אין לשונית UI | Programs, בתוך רכיב, או בתוך כל יחידה אחרת |
| Set data (set_data) | Datasource, Fields, Events. אין לשונית UI | Programs, בתוך רכיב, או בתוך כל יחידה אחרת |
| Alert | לשונית אחת, Alert | תחת Programs או בתוך רכיב, בשום מקום אחר |
| JavaScript | לשונית אחת, Editor | תחת Programs או בתוך רכיב, בשום מקום אחר |
| Table | Data, Fields, Indexes | תחת Database, ושם Create הופך אפור על הטבלה עצמה |
| Globals | Fields, Events | לא מהעץ: שורש Globals מסרב ילדים חדשים |
| Route | לשונית אחת, Route | תחת Routes, הטיפוס היחיד שהמקטע הזה מציע |
| AI agent (ai_agent) | לשונית אחת, AI Agent | תחת AI Agents, הטיפוס היחיד שהמקטע הזה מציע |
| Folder | שום דבר בכלל. פתיחת תיקייה חוזרת מוקדם | תחת Programs או Database, או בתוך רכיב או כל יחידה אחרת |
העוזר כותב לתוך אותו עץ שאתם עורכים ביד, ומצב אחד אינו כותב דבר.
תארו מה אתם רוצים בפאנל ה-AI של פרויקט פתוח והבונה מציע טבלאות, רכיבים, שדות ואירועים כמסמכים רגילים בעץ הרגיל, ולכן אפשר לפתוח את התוצאה אחר כך ולשנות שדה אחד. שני המתגים בפאנל אינם אותו מתג: ההיקף קובע במה השיחה עוסקת, המצב קובע אם התור רשאי לכתוב. בתוך Studio חלון החיבור גם מסתיר את בורר המודל ואת בורר הסוכן שכל שאר לוח הבקרה מראה, כך שאין מודל לבחור ואין סוכן לבחור.
- מצב Plan אינו כותב דבר. הפאנל מתאר את Auto כרץ מקצה לקצה בלי הפרעות ואת Plan כחוקר ומציע גישה בלי לבצע שינויים. הבחירה נוסעת על הבקשה בתור plan_mode, והשרת הופך אותה למתודה אחרת שהשמירה בה מכובה לכל אורך התור.
- ההיקף קובע למה התור קשור. בהיקף כללי השיחה מפנה לאפליקציה; בהיקף צומת היא מפנה לצומת שפתוח לפניכם, בתור studio_node ולא בתור אפליקציה. החלפת היקף מתחילה שיחה חדשה וטוענת מחדש את רשימת השיחות, מפני ששיחות עבר נשלפות לפי מה שהן מפנות אליו.
- צ׳יפי ההקשר הם מתגים, לא תוויות. עד שלושה מופיעים: התוכנית שפתוחה בעורך, צומת אחר שנבחר בעץ, ואלמנט ה-UI שנבחר בזמן שלשונית ה-UI פתוחה. כבו אחד מהם והוא נשמט מהבקשה. כבו את כל השלושה והתור נשלח בלי שום הקשר.
- הבונה הוא חבילת npm אחת עם מתודות בשם, ושתיים מהמתודות האלה כותבות תוספים. app_builder שומר מסמכי studio, app_planner לא, ו-create_plugin_via_ai ו-modify_plugin_via_ai מיוצאים מאותה חבילה. מה שהחבילה הזאת עושה בתוך עצמה אינו נמצא במקור הפלטפורמה, ולכן הדף הזה מתאר את אתר הקריאה ועוצר שם.
- אתם מתאריםבפאנל ה-AI של פרויקט פתוח, בהיקף כללי או קשור לצומת אחד
- הצ׳יפים קושרים את ההקשרהתוכנית הפתוחה, צומת שנבחר במקום אחר בעץ, אלמנט ה-UI שנבחר
- 12 ההודעות האחרונות נוסעות איתםכל אחת נקטמת ל-2000 תווים לפני השליחה
- app_builder שומר, app_planner לאקבלת תוכנית שולחת אותה מחדש עם הוראה לבצע אותה מקצה לקצה
תור בנייה מחזיר קבלה שמפרטת מה הוא שינה.
התשובה אינה המודל שמספר מה הוא עשה. מעצב בפלטפורמה קורא את התוצאה המובנית וכותב את ההודעה: הוא סופר את המסמכים, מקבץ אותם לפי טיפוס התפריט שלהם, מונה עד שנים עשר מהם בשם עם מזהה המסמך בסוגריים מרובעים, ומציין אם הם נשמרו. תור תכנון מצייר שדות אחרים לגמרי, סקירה, עד שתים עשרה יחידות מוצעות, שמונה צעדי סדר בנייה ושמונה בדיקות קבלה, ותמיד מציין שהוא שמר את התור במצב תכנון. כשהעוזר תקוע על משהו שהוא צריך מכם הוא גם אינו שואל בפרוזה: הוא פולט בלוק שאלות מובנה שהפאנל מצייר כשלבים, שתיים או שלוש אפשרויות לכל שאלה, עם שורת Other לטקסט חופשי שהממשק מוסיף בעצמו. בזמן שהתור רץ, טקסט הסטטוס מתחלף במחזוריות בין רשימה קבועה של שלוש הודעות, רשימה אחת לכל מצב, ומתחלף כל 1800 אלפיות שנייה.
שני מנועים יושבים מאחורי בורר אחד.
על מקור נתונים של טבלה מצב הסינון הוא Index או Query, וכשההגדרה חסרה זמן הריצה מסיק Index אם נבחר אינדקס ו-Query אם לא. אלה אינם שני מעטפות על מתכנן אחד. מצב Index קורא מפתח מורכב שהטבלה כבר כתבה על כל רשומה. מצב Query קורא בורר שמורכב בזמן הבקשה. מה שאתם יכולים לבטא, ומה שמובטח לכם, משתנה עם מיקומו של הבורר הזה.
אין רשומת מאפיינים. כל מאפיין הוא attribute.
בחרו אלמנט בעץ ופאנל המאפיינים מתמלא בשורות. השורות האלה אינן כותבות לתוך בלוב מאפיינים פרטי שמוצמד לצומת. אלמנט הוא מזהה, שם תגית, מפת attributes, ילדים ותוכן טקסט, וכל הגדרה בפאנל היא פשוטו כמשמעו attribute אחד במרחב השמות xu-. קבעו Bind והסימון מקבל xu-bind. הקלידו מה שבא לכם לתוך Add Attribute והוא יושב לצד האחרים כ-attribute רגיל. לכן class שכתבתם ביד, attribute שהמצאתם ופקד בפאנל מגיעים כולם לאותו מקום, ולכן Edit HTML מראה לכם את כל האמת על אלמנט ולא ציור שלה.
- הפאנל נפתח עם שלוש שורות, xu-show, xu-bind ו-xu-ref, וגדל ככל שאתם משתמשים באקורדיונים שמתחתיהן. חמישה עשר שמות נוספים מוחזקים מחוץ לרשימת ה-attributes החופשית מפני שלכל אחד מהם כבר יש פקד ייעודי משלו, כך ששום attribute אינו ניתן לעריכה בשני מקומות בו זמנית.
- Show ו-Render אינן שתי מילים להסתרה, וזמן הריצה אומר את זה בהערה שלו עצמו. xu-show הוא מתג תצוגה והאלמנט נשאר מורכב. שער render מעביר תת עץ שלם לתוך המסמך או החוצה ממנו, כך שהאלמנט פשוט נעדר מה-DOM.
- לכל attribute יש תאום מחושב: כתבו xu-exp: לפני השם, או לחצו על כפתור fx שליד השדה, והערך הופך לביטוי שמחושב מחדש כשהתלויות שלו משתנות. זה גנרי ולא רשימה אצורה של מאפיינים שאפשר לקשור, ולכן זה עובד על src, על attribute שהקלדתם לפני רגע, ועל שדות שתוסף הצהיר עליהם. ערך והתאום המחושב שלו הם אותה משבצת ולא ברירת מחדל ועקיפה, ולכן Studio מסמן אלמנט שנושא את שניהם ומציע להסיר את הסטטי. חריג אחד מתועד: xu-exp:class מחליף את attribute ה-class הסטטי במקום להתמזג איתו.
- המגבלה, בפשטות: אין ממשק עיצוב. אין פקדי ריווח, אין פקדי טיפוגרפיה, אין בורר צבעים, אין עורך מודל קופסה. העיצוב הוא שורת צ׳יפים של class ועוד עורך CSS גולמי, כך שערך ריפוד הוא מחרוזת class של Tailwind או Ionic שאתם מקלידים או מדביקים, ו-class מותנה הוא שם class שממופה לביטוי בתוך xu-class.
הקנבס הוא משטח פריסה. זמן הריצה נמצא במרחק מתג אחד.
שני המשטחים קוראים את אותו עץ אלמנטים, והם עושים איתו כמויות שונות. קנבס העיצוב מצייר את הסימון שלכם כדי שתוכלו לפרוס ולבחור, אבל הוא אינו מריץ את האפליקציה שלכם: רכיבים מקוננים ו-widgets מצוירים כמצייני מקום מתויגים, ותנאים מותאמים כטקסט ולא מוערכים. המתג בסרגל הכלים מחליף את הקנבס בתצוגה המקדימה החיה, שהיא זמן הריצה האמיתי עם חלונית Props להזנת ערכי בדיקה. הידיעה על איזה משטח אתם עומדים מסבירה את רוב הרגעים שבהם מסך נראה נכון במקום אחד ומתנהג אחרת בשני.
תהליך עבודה הוא תור משימות, לא רשימת המתנות.
אירוע אינו מריץ את הפעולות שלו זו אחר זו. הוא עובר על תהליך העבודה פעם אחת ודוחף משימה אחת לכל צעד מאופשר לתוך מערך יחיד שמוחזק על ההפעלה, ולולאת ניקוז נפרדת שרצה כל אלפית שנייה לוקחת את המשימה הראשונה שאינה מסומנת כעסוקה ומבצעת אותה. הסדר הוא סדר המערך, מלמעלה למטה. מה שמתחיל את הצעד הבא אינו הצעד הקודם שמסתיים, אלא הצעד הקודם שמוחק את המשימה שלו עצמו. דגל ה-async הוא שורה אחת בתוך הלולאה הזאת.
עשרים וארבע פעולות בתשע קבוצות, והארגומנט שכל אחת קוראת.
בורר Add Action נבנה מקובץ קטלוג אחד, ומחוץ לרשומות ה-AI כל פעולה מסכימה בשלוש דרכים: הקטלוג, מפת המטפלים בזמן הריצה, ורישום המאמת שמתעד את מפתח המטען שכל פעולה קוראת. הרישום הזה הוא העמודה הימנית. שני דברים לפני הטבלה. Invoke Action מציע אחד עשר ערכים ברשימה הנפתחת שלו ותשעה מהם עובדים, מפני ש-act_close ו-act_back מוערים כהערה גם בזמן הריצה וגם ברשימת ההיתר של המאמת. ושבע פעולות ישנות יותר על אלמנטי UI נדחות בתוקף כמיושנות ולא נסבלות בשקט. אפשר גם ליצור פעולה בלי הבורר: גררו צומת מהעץ אל תהליך העבודה וטיפוס הצומת קובע את הפעולה, כך שצומת get data הופך ל-Get Data ורכיב הופך ל-Call Page. הפילו אותו על שורה שכבר קיימת והוא מכוון מחדש את אותה שורה במקום להוסיף חדשה.
הפעולות שבקבוצה | מה הפעולה קוראת | |
|---|---|---|
| ניווט, 3 | Call Page, Call Modal, Call Popover | כל אחת מציינת את הרכיב שייפתח, וזה כל המטען. |
| בסיס נתונים, 3 | Get Data, Set Data, Batch | יחידת מקור הנתונים שתרוץ. Set Data ו-Batch מציינות גם את השדה שהתוצאה שלהן נוחתת בו. |
| JavaScript, 4 | Call Native JS, Call Evaluate JS, Execute Native JS, Execute Evaluate JS | שתי פעולות Call מציינות יחידת תוכנית JavaScript בעץ. שתי פעולות Execute נושאות את הקוד עצמו, מוקלד לתוך הפעולה. |
| מטפלי אירועים, 4 | Raise Event, Emit Event, Invoke Action, Delay | Raise Event מציינת אירוע ואת הפרמטרים שלו, כל אחד מהם ערך ליטרלי או ביטוי. Invoke Action קוראת אחד מתשעת הערכים המותרים. Delay קוראת מספר של אלפיות שנייה. |
| כלי ממשק, 4 | Call Alert, Alert, Loader On, Loader Off | Call Alert מציינת יחידת alert. Alert נושאת מצב תצוגה, טיפוס, כותרת וגוף משלה. שתי פעולות הטוען אינן קוראות דבר. |
| API, 3 | Call Project API, Call System API, Call External API | API של פרויקט מציין יחידה בעץ שלכם, API של מערכת מציין מתודה, ו-API חיצוני מקבל כתובת ושיטת HTTP. |
| מערך נתונים, 1 | Update | ערך אחד, שנכתב כ-"@field: expression; @other: expression", או כאובייקט JSON5. |
| תוספים, 1 | Call Library | שם התוסף והמתודה עליו, כך שספרייה שנרכשה נקראת כמו כל פעולה אחרת בטבלה הזאת. |
| הערה, 1 | Comment | שום דבר. הרישום מתאר אותה כתיעוד בלבד, והמטפל בזמן הריצה אינו עושה דבר מלבד למחוק את המשימה שלו. |
פתח המילוט הוא נתיב, לא תיבת טקסט.
שבעה מקומות מאפשרים לכם להפסיק להגדיר ולהתחיל לכתוב, והעמוק שבהם אינו פעולה כלל: תוכנית JavaScript היא יחידה בפני עצמה בעץ, עם פלט בנייה משלה ואוסף מתועד של דברים שנמסרים לה. שתי מגבלות שייכות לאותה נשימה. המאמת אינו מפענח גוף JavaScript, ולכן שגיאת תחביר עולה מהבנייה ככישלון הידור ולא מהבודק, שמזהיר רק כשהסקריפט ריק. וסקריפט או style שנכתבים כ-attribute על אלמנט אינם יכולים להכיל תו גרש כלל.
- גוף תוכנית מקבל עשרה ארגומנטים בשם: params, xu, SESSION_ID, SESSION_OBJ, jobNoP, $container, func, glb, APP_OBJ ו-evt. בלי import או export ברמה העליונה הוא נעטף בפונקציית async, ולכן await ברמה העליונה עובד.
- כתבו import או export והוא הופך למודול ES אמיתי במקום. מזהי נתיב יחסיים נכתבים מחדש מול תיקיית הסקריפטים של אותה תוכנית, כך שיחידה יכולה לייבא את קובצי האחים שלה.
- שמירת יחידת JavaScript מפעילה בניית Vite בלי לחסום את השמירה. בזמן ריצה המנוע מעדיף את המודול הבנוי, עם עקיפת מטמון לכל בנייה, טוען גיליון סגנון רק אם המודול מייצא אחד, ונופל חזרה להרצת המקור עם קוד אזהרה RUN_MSG_JS_010.
- אובייקט ה-xu שסקריפט מקבל הוא API בן חמש עשרה מתודות: קריאה וכתיבה של שדה, הפעלת אירוע, ארבע קריאות לבסיס הנתונים, קריאה לסקריפט אחר, שלושה סוגי קריאות API, alert, log, וקריאה או כתיבה לדרייב. קוד מוטבע הוא צורה אחרת לגמרי, נעטף כפונקציית async ונקרא עם האלמנט ואירוע הדפדפן.
התצוגה המקדימה היא עמיתה של העורך שלכם, ומותר לה לוותר עליכם.
הפעלת תצוגה מקדימה נפתחת על ערוץ שנקרא על שם אסימון ההפעלה שלכם ומבקשת מהלשונית הפתוחה שלכם מסמכים מזהה אחר מזהה. Studio עונה מהזיכרון עם עותק של המסמך שעל המסך שלכם, ולכן אין באנתיב הזה שום באנדלר ושינוי שנשמר נמצא בהפעלה הרצה מיד. יש גם מועד אחרון. אם הלשונית שלכם לא ענתה תוך חמש שניות, אותה הפעלה עוברת לעותק הבנוי שעל השרת ולעולם לא שואלת אתכם שוב לכל אורך חייה.
תיבת הפקודה היא eval, שרצה בתוך הדף שאתם מציגים בתצוגה מקדימה.
אין אוצר מילים של פקודות ללמוד, מפני שאין אוסף פקודות. מה שאתם מקלידים נשלח לתצוגה המקדימה כמחרוזת אחת, מוערך שם, והתוצאה נשלחת בחזרה ומצוירת כעץ בעומק שתי רמות. כל מה שהדף הרץ יכול לראות, אתם יכולים לראות: אובייקט ההפעלה, מקורות הנתונים, עוזרי זמן הריצה. זריקה חוזרת עם דגל שגיאה ומצוירת באדום. Inspect הוא החצי השני: הדליקו אותו, כל אלמנט שנושא ui id מקבל מסגרת מתחת לסמן, ולחיצה על אחד מהם מחזירה את הרכיב שמחזיק אותו ואת הצומת שבתוכו, עם כפתור שפותח את אותו רכיב בלשונית ה-UI שלו ובוחר את הצומת בעץ. זה מפיקסל לצומת בשתי לחיצות.
שמירה היא כתיבה מקומית. הגרסה נכתבת על ידי משהו אחר, אחר כך.
אין שמירה אוטומטית. לחיצה על שמירה מוסרת את המסמך ל-worker, שמחתים אותו וכותב אותו לתוך בסיס נתונים בתוך הדפדפן שלכם, וזה כל מה שהקשה שלכם עושה. כל מה שאחריה הוא מכונות אחרות: שידור ללשוניות האחרות שלכם, שכפול החוצה לבסיס הנתונים של האפליקציה, ותהליך שרת שצופה באותו בסיס נתונים והוא זה שבאמת מנפיק גרסה. הסדר חשוב, מפני שהוא אומר לכם מה שורד רשת שנופלת ומה לא.
לתג יש ארבעה מצבים, ואחד מהם אומר שהלשונית שלכם אינה מעודכנת.
התווית שליד העורך נגזרת ולא נשמרת: היא משווה את הרוויזיה ששמרתם מול התצלום החדש ביותר שעל השרת. Current פירושו שקיים תצלום מדויק לרוויזיה שלכם, או שההבדל הוא מטא נתוני בנייה בלבד. Recording פירושו שהרוויזיה שלכם מקדימה והלכידה עוד לא נחתה. Behind פירושו שהרוויזיה ששמרתם ישנה יותר מהתצלום החדש ביותר, כלומר הפעלת הדפדפן הזאת ישנה דרך רוויזיות, והחלונית אומרת זאת בכרזה במקום לטעון שאתם מעודכנים. Unknown פירושו אותו דור, תוכן שונה, מה שהמקור מכנה ענף מתפצל. בחירת שורה ישנה יותר מציגה אותה במקום כשעותק העבודה שלכם מוחזק בהפניה, ובזמן שתצוגה מקדימה למעלה שני מסלולי השמירה דוחים את המסמך על הסף. Restore היא הפעולה היחידה שכותבת: היא קוראת מחדש את הרוויזיה החיה מהשרת קודם, שואלת לפני דריסת שינוי שהגיע בזמן שהסתכלתם, שואלת שוב לפני השלכת עריכות שלא נשמרו, ואז משתרשרת על הרוויזיה הטרייה ביותר הידועה כך שהשחזור נוחת נקי בראש.
שמירה, נלכדת בשרת רגע לאחר מכן
מה שאינו כאן, במלואו: אין ענפים, אין תצוגת השוואה או הפרש, אין מיזוג, אין הודעות קיבוע. ההיסטוריה היא לכל תוכנית ולא לכל פרויקט. מספרי הגרסאות הם מיקומיים, ולכן v3 היום אינו v3 בשבוע הבא. חמישים תצלומים נשמרים לכל מסמך והשאר נמחקים ולא מאורכבים. הלכידה אסינכרונית ורצה בצד השרת, ולכן שמירה טרייה נקראת כ-recording עד שהיא נוחתת. וביטול הוא מערכת אחרת שאינה חולקת דבר עם זו: חמישים צעדים לכל מסמך, מקובצים ב-400 אלפיות שנייה כך שגרירה או רצף הקלדה הם צעד אחד, מוגבלים לצומת שבחרתם, מוחזקים בזיכרון הדפדפן בלבד, ונעלמים ברגע שאתם טוענים מחדש. אחרי טעינה מחדש, המסילה הזאת היא הדרך היחידה חזרה.
יש נעילה. אין מיזוג.
הנעילה היא check-in, והיא לכל צומת. השמירה חסומה עליה: צומת ש-check-in שלו מוחזק על ידי מישהו אחר נדחה עם שמו של מי שמחזיק בו, וצומת שאיש לא עשה לו check-in נדחה עם Please check-in לאחר טיפוס בשרשרת ההורים כדי לראות אם משהו מעליו מוחזק. טיוטות פטורות, וכך גם אפליקציה שמעולם לא נשמרה. מה שאינו נעול הוא התוצאה ברגע שרוויזיה של מישהו אחר כן מגיעה למסמך שפתוח לפניכם. Studio מכריע בין שלושה בדיוק, והוא מחשב אם יש לכם עריכות שלא נשמרו לפני שהוא מעדכן את קו הבסיס שלו, וזה הסדר שהופך את התשובה לאמינה.
- 01
העותק שלכם נקי, אז אתם לוקחים את שלהם במלואו
שום דבר משלכם אינו על הכף, ולכן Studio מקדם את המסמך הפתוח לרוויזיה שלהם במלואה ומרענן את דגל הלשונית מיד ולא מחכה לצופה המווסת. אתם מגלים מפני שהתוכן השתנה תחתיכם, לא מפני שדיאלוג שאל.
- 02
יש לכם עריכות, והכתיבה שלהם הייתה המהדר
Studio משווה את שני המסמכים כשרשימת הרוויזיות, מטא נתוני ה-studio, תוצאת הבנייה, שגיאת הבנייה, שלוש חותמות הזמן ותלויות הסקריפט מוסרות. כשמה שנשאר זהה, הרוויזיה שמגיעה היא worker הבנייה ששומר את הפלט שלו עצמו. העריכות שלכם נשארות בדיוק במקומן, רק הרוויזיה ושדות הבנייה מאומצים, ולא נאמר לכם דבר, מפני שלא קרה לכם כלום.
- 03
יש לכם עריכות, והתוכן שלהם באמת שונה
כל עריכה שלכם נשמרת ורק הרוויזיה מקודמת, כך שהשמירה הבאה שלכם משתרשרת על שלהם במקום להתנגש. אז אתם מקבלים אזהרה למשך שתים עשרה שניות: משתף פעולה שמר את המסמך הזה, והעריכות שלא שמרתם ידרסו את השינויים שלו כשתשמרו. לאזהרה יש פעולה אחת בדיוק, השלכת שלי ושימוש בשלהם, שמחליפה את עותק העבודה שלכם במסמך השרת. התעלמו ממנה ושמרו, והגרסה שלכם היא זו ששורדת. אין מיזוג ברמת שדה, אין הפרש, אין נוכחות ואין סמנים משותפים.
קובץ בודק אחד, רץ בדפדפן שלכם או בשרת תלוי איפה אתם.
האימות אינו שירות במקום אחר. הוא קובץ יחיד, runtime/js/modules/xuda-studio-checker.mjs, 4,714 שורות, מיובא ישירות על ידי הצד הקדמי של Studio ומיובא לפי נתיב בשרת על ידי מודול האפליקציה. Studio מחליט בזמן הקריאה איזה מארח מריץ אותו. במצב מקוון, על פרויקט שאינו טיוטה, הוא מפעיל את run_programs_checker כמשימת רקע. במצב לא מקוון, או בתוך טיוטה, הוא מריץ את אותה בדיקה על כל תוכנית בדפדפן ומקבע תוצאה באותה צורה. הממצאים מתפצלים לשגיאות ואזהרות על סמך שדה טיפוס של E או W ולא יותר. ואז החלק הכן: זה אינו עוצר בנייה. build_app מקבל ignore_errors עם ברירת מחדל של true, שום קורא במוצר אינו שולח false, וסך השגיאות נכתב לתוך הגרסה עצמה, כך שכל גרסה ברשומות נושאת את מספר השגיאות שהיא יצאה איתן.
- שמונה עשרה פונקציות בדיקה מתפזרות על פני אחד עשר טיפוסי תוכנית: globals, table, component, javascript, get_data, set_data, batch, api, alert, route ו-ai_agent. תיקייה היא אל פעולה מכוונת, וכל דבר שאינו מזוהה מעלה CHK_MSG_GEN_050.
- הרצת שרת כותבת מסמך בדיקה אחד לכל תוכנית, ממופתח על אותה רוויזיית תוכנית, כך שתוכנית שאיש לא נגע בה מאז המעבר האחרון מדולגת על הסף ולא נבדקת מחדש.
- כל קוד בקטלוג שנוצר נושא את אותם שדות: כותרת, חומרה, תחום, קטגוריה, תקציר, סיבות, צעדי פתרון, מזהה עזרה, דפוסים ישנים וקודים קשורים. הוא נוצר מרישום השגיאות, לא נכתב ביד.
- שני המארחים אינם מפיקים פלט זהה. הרצת השרת מאותחלת עם רשימת הפריסות ויכולה לסמן תוכנית כמשמשת פריסה. הרצת הדפדפן לא, ולכן פסק הדין של לא בשימוש שונה ביניהם.
בנייה ופריסה הם אובייקטים שונים, וגרסה יושבת ביניהם.
בנייה כותבת תצלום ממוספר. פריסה מכוונת מכונה לאחד מהם. המספר על התג, #37, הוא app_build_id, מונה לכל פרויקט שעולה באחד בכל פעם ש-build_app רץ. ה-1.4.0 שלידו הוא מחרוזת נפרדת שאתם מקלידים, שמאומתת על ידי דפוס major, minor, patch פשוט בתג עצמו. הם זזים באופן עצמאי, ושניהם מוחתמים על מסמך הפריסה כך שפריסה תמיד מתעדת בדיוק איזה תצלום היא משרתת.
לחיצה על פריסה אינה מתקינה דבר.
קריאת הפריסה היא החצי הראשון של לחיצת יד. היא יוצרת רשומות, מבקשת מכונה וחוזרת בזמן שהמכונה עדיין נוצרת. ההתקנה מתחילה כשהמכונה עצמה קוראת בחזרה לפלטפורמה, ולכן סרגל ההתקדמות יכול לעמוד במקום זמן מה באמצע ולכן בסולם הסטטוס יש בכלל שלב המתנה: 1 נוצר, 2 מתקין או ממתין, 3 פעיל, 4 נכשל או הופסק.
- 01
הטופס חוזר לפני שקיים משהו
deploy_app כותב את מסמך הפריסה, ואופציונלית מסמך datacenter, מבקש מהמארח ליצור מכונה באופן אסינכרוני, וחוזר אליכם. שום דבר לא הותקן ושום דבר לא הועתק. הפריסה יושבת בקוד סטטוס 2 תחת התיאור farm server waiting to install, ובאמת אין מה להראות עד שהצעד הבא קורה מעצמו.
- 02
המכונה קוראת בחזרה לפלטפורמה
האתחול הראשון על המכונה החדשה שולח curl ל-webhook בפלטפורמה שנושא את מזהה האפליקציה. המטפל הזה פותח מחדש את אותה משימה שאתם כבר צופים בה, מחליף את רשימת הצעדים שלה ברשימת ההתקנה בת עשרים וארבע הרשומות, ומריץ את install_hosting_resources. המשימה שהתחלתם לשלב הראשון היא המשימה שמדווחת על שלב שתיים, ולכן שמות הצעדים משתנים תחתיכם באמצע.
- 03
ההתקנה רצה, מנסה שוב אם צריך, ונוחתת בסטטוס 3
ההתקנה עטופה בארבעה ניסיונות עם השהיה של שלושים שניות, מפני שמכונה שבדיוק נוצרה עשויה עדיין לעלות מחדש כשחיבור ה-SSH הראשון מגיע. היא כותבת את קובץ המערכת של pm2, מריצה pm2 delete ואז pm2 start מולו, ומסיימת בקוד סטטוס 3, server ready. הדף הזה אינו מצטט משך זמן לאף אחד מהשלבים, מפני ששום דבר במקור אינו מתעד אחד ואנחנו לא מדדנו.
שישה עשר טיפוסי פריסה, ומה כל אחד מהם באמת מריץ.
אותו פרויקט יוצא בשישה עשר טיפוסי פריסה מוצהרים בארבע קבוצות: שישה ווב, חמישה שרת, שלושה שולחן עבודה ושניים נייד. כל טיפוס גם מצהיר על אוסף הלשוניות שלו מתוך רשימת בסיס בת ארבע עשרה לשוניות, כך שפריסת cron מוותרת על launch, domain, firewall, analytics ו-user assist ומגיעה לתשע לשוניות, בזמן ש-MPA שומר את כל הארבע עשרה וחמשת טיפוסי הארטיפקט מוסיפים במקום לשונית publish. בקיבוץ לפי מה שההתקנה באמת כותבת לתוך קובץ המערכת של pm2, חמישה עשר מהם מתקפלים לחמש צורות. השישה עשר, mcp, מוצהר לצד השאר ואין לו רשומת מערכת משלו שהצלחנו לאתר.
מה רץ על המכונה | מה זה מפעיל | |
|---|---|---|
| ווב: spa, mpa, pwa, embed | אפליקציית pm2 אחת בשם webserver, במצב ביצוע cluster עם instances שמוגדר ל-max, ועוד יחידת systemd לנתב ה-websocket | חבילת זמן הריצה מוחתמת הבנייה מעל HTTPS בפורט 443, קשורה לסוקט דו מחסניתי |
| ווב: preview | אותה אפליקציית webserver, ואפליקציית pm2 שנייה בשם preview | פלט שרת Nuxt בפורט 3023 לצד האפליקציה עצמה |
| ווב: user group | אותה אפליקציית webserver, ואפליקציית pm2 שנייה בשם user_group | פלט שרת Nuxt בפורט 3022 עבור משטח הקבוצה |
| שרת: cron, api, node, emitter | אפליקציית pm2 יחידה במצב ביצוע fork, שמריצה את קובץ הכניסה של אותו טיפוס, xuda-cron.mjs או xuda-api.mjs וכן הלאה | חבילת השרת לבנייה הזאת. הפעלת cron מפצלת תהליך בן, מחכה שידווח שהוא מוכן, שולחת לו את ההרצה, ומתעדת את מה שחוזר |
| ארטיפקטים: ios, android, macos, windows, linux | שום דבר. מסלול ה-macOS עוקף בכוונה את קריאת הפריסה וכותב מסמך פריסה בלי מכונת אירוח מאחוריו | לשונית publish, ושיגור לשירות בנייה חיצוני. איתרנו מספיק כדי לדעת שזה צינור אחר, לא מספיק כדי לתאר אותו כאן |
מפרש אחד, שני צדדים, קריאה אחת.
מה שפריסה באמת שולחת אינו מקור שנוצר ואינו בלוב מהודר. זו חבילת מפרש ועוד עץ התוכניות שלכם כ-JSON, ואותו מפרש רץ בשני צדי החוט. בדפדפן זו תגית script אחת ובנאי אחד. בשרת זה ייבוא דינמי אחד של החבילה האחות ודגל אחד. כשדף מצויר בצד השרת, השרת מבקש מה-worker להריץ תוכנית עד למחרוזת, ומקבל בחזרה גם סימון וגם מטען. המטען מוטבע לתוך הדף, ואלמנט העגינה מסומן במצב הציור ובהפעלת הלקוח שהשרת פתר עבורו.
הקטגוריה בשם החבילה קובעת איפה תוסף מופיע.
כל 62 התוספים מתוצרת הבית נקראים באותה דרך, והקטגוריה בשם הזה נמצאת גם על רשומת התוסף. זה כל כלל הניתוב: התקינו אחד והוא מופיע בבורר מקורות הנתונים, בסרגל ההוספה, בבורר האייקונים או ברשימת פעולות תהליך העבודה, בלי שום דבר לחווט. התקנה גם אינה דגל תכונה. השרת כותב רשומה על הפרויקט שנושאת את הגרסה, את הטיפוס ואת מצאי הקבצים, מריץ npm install אמיתי לתוך ספריית התוספים של אותו פרויקט, מסנכרן את הספרייה הזאת החוצה לשרתים, ומעתיק את כל עץ קובצי החבילה לתוך בסיס הנתונים של הפרויקט ממופתח לפי נתיב יחסי.
תשעה דברים ש-Studio אינו עושה.
- תיבת ההנחיה במסך הפתיחה של Studio אינה בונה דבר: היא שולחת בקשת צ׳אט פשוטה אחת שמוגבלת ל-1024 אסימונים, וכשההודעות מוסתרות התשובה אפילו אינה מצוירת, מפני שהבונה חי בפאנל ה-AI של פרויקט שכבר פתוח.
- בחירת Template אינה פותחת גלריה בתוך העורך: היא פותחת את אשף היצירה של לוח הבקרה בלשונית דפדפן שנייה, והפרויקט שהיא מפצלת הוא תצלום שאין דבר שמקשר אותו בחזרה למקור שלו אחר כך.
- טיוטה אינה נמצאת על שרת: היא חיה בפרופיל דפדפן אחד על מכשיר אחד עד שאתם לוחצים על שמירה, וניקוי נתוני האתר מסיים אותה.
- תביעת ה-check-in שחוסמת את השמירה אינה חכירה בשרת: היא שדה על המסמך, ולכן שמירה שלעולם אינה מגיעה לשרת לעולם אינה מנקה אותה, ושום דבר אינו ממזג שתי עריכות בשבילכם.
- שום דבר אינו יכול לגרום לבנייה להיכשל על שגיאות אימות: הבודק רץ קודם וסך השגיאות שלו נכתב לתוך הגרסה, אבל שום קורא אינו מבקש מהבנייה לסרב.
- בנייה שהסתיימה אינה נוגעת בשום דבר שכבר חי, ושתי פריסות של פרויקט אחד יכולות לשבת על מספרי בנייה שונים כל עוד תשאירו אותן כך.
- אין השקה מדורגת: תג הבנייה יכול לצייר כרזת מדורג אך לא חי, אבל בנייה רגילה מקצה למספר המדורג ולמספר החי אותו ערך, ולכן הכרזה הזאת אינה יכולה לרוץ.
- אין גיזום של זמן הריצה לכל אפליקציה, ולכן כל פריסה שולחת את אותה חבילת מפרש מלאה לבנייה שלה ורק ה-JSON של התוכניות שונה.
- פריסה מתוזמנת לא תריץ מסך, רק תוכניות batch, get_data ו-set_data, וצעד JavaScript בצד השרת נעצר בשישים שניות.
השאלות שמתגבשות אצלכם עכשיו
כל אחת משבע התשובות האלה מסתיימת במגבלה. הן כאן מפני שהייתם פוגשים את כולן בשבוע הראשון ממילא.
צפו ב-Studio פותח פרויקט וממלא את העץ.
פתחו פרויקט ריק וראו מה נכתב.
שני מסמכים, נתיב אחד, והעורך פתוח. שום דבר אינו מגיע לשרת עד שאתם שומרים, ושום דבר אינו מגיע למבקר עד שאתם בונים, ואז מזיזים פריסה לבנייה הזאת בכוונה.