Xuda Studio

אפליקציית Xuda היא אוסף של מסמכים מוטיפסים, ומפרש אחד מריץ אותם.

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

שנים עשר טיפוסי צמתים, צורת מסמך אחת
אין בשום מקום מסמך עץ
כל צומת הוא כתובת שאפשר לשתף
אפליקציית Xuda היא אוסף של מסמכים מוטיפסים, ומפרש אחד מריץ אותם.
12

טיפוסי צמתים שפרויקט בנוי מהם

11

טיפוסי אירועים שרכיב יכול להגיב להם

16

טיפוסי פריסה שאותו פרויקט יכול לצאת בהם

62

תוספים מתוצרת הבית, בשש קטגוריות

צורתו של פרויקט

חמישה עצים נפרדים, ובשום מקום אין מסמך עץ.

הפאנל השמאלי אינו מחזיק עץ אחד. הוא מחזיק חמישה, ובורר אייקונים בעל חמישה מצבים מעליו קובע איזה מהם מורכב, כך שטבלאות, מסכים, מצב משותף, כתובות וסוכנים הם חמישה מרחבי כתובות נפרדים ולא חמישה ענפים של אותו אחד. גם אף אחד מהחמישה אינו נשמר כעץ. כל צומת הוא מסמך שנושא מצביע parentId יחיד ומפתח מיון order_ts, ו-worker בונה מחדש את כל המבנה בזמן הטעינה על ידי סריקת ילדים רמה אחר רמה, כשהוא מקבל רק מסמכים שה-stat שלהם הוא 3. הזזת צומת למעלה או למטה אינה העברת קובץ: היא שומרת בדיוק שני מסמכים כשערכי ה-order_ts שלהם הוחלפו. מחיקת תת עץ כותבת stat 4 על כל מסמך בתוכו ומשאירה את כולם באחסון, ולכן העץ מפסיק להציג אותם ומנוע החיפוש מפסיק לאנדקס אותם באותו רגע. פתחו רכיב ועברו ללשונית ה-UI שלו ועץ שני מופיע מתחת לראשון, עץ האלמנטים של אותו רכיב, עם ידית גרירה ביניהם שמיקומה נזכר.

מגבלה אחת ראויה לציון. הבנייה מחדש סורקת כל מסמך פעם אחת בכל רמה, וזו בדיוק הסיבה שהיא רצה ב-worker ולא בתהליך הראשי.
תיקון

ארבע לשוניות נכון לגבי רכיב. כל טיפוס אחר פותח משהו אחר.

הגרסה של הדף הזה שאולי קראתם אמרה שמסכים, משימות רקע ונקודות קצה של API הם אותו סוג אובייקט עם אותן ארבע לשוניות. החצי הראשון נכון, השני לא. העורך בונה את רצועת הלשוניות שלו לפי טיפוס הצומת, והמעטפת מתכווצת וגדלה: רכיב מקבל ארבע לשוניות, batch או יחידת נתונים מקבלים שלוש ואין להם UI כלל, יחידת API מקבלת ארבע אבל הרביעית היא Output ולא UI, וחמישה מתוך שנים עשר הטיפוסים פותחים לשונית אחת או אף אחת: alert, javascript, route ו-ai_agent פותחים כל אחד עורך אחד, ותיקייה אינה פותחת דבר. הרצועה מצוירת רק כשלצומת יש יותר מלשונית אחת, ולכן חמישה אלה מגיעים בלי רצועה מעליהם. איזו לשונית פתוחה הוא פרמטר בכתובת העורך, והלשונית האחרונה שהייתה לכם על צומת מסוים נזכרת לכל צומת בנפרד, כך שחזרה אליו מחזירה אתכם למקום שבו הפסקתם. העמודה השנייה היא החצי השני של אותו דקדוק. פאנל ה-Create מציע אוסף טיפוסים שונה בהתאם לצומת שעליו לחצתם לחיצה ימנית, ותיקייה פשוט יורשת את האוסף של המקטע שבו היא יושבת.

מה זה פותח
איפה אפשר ליצור אותו
ComponentDatasource, Fields, Events, UIתחת Programs, או בתוך רכיב אחר
APIDatasource, Fields, Events, Outputתחת Programs או כל יחידה אחרת, לעולם לא בתוך רכיב
BatchDatasource, Fields, Events. אין לשונית UIPrograms, בתוך רכיב, או בתוך כל יחידה אחרת
Get data (get_data)Datasource, Fields, Events. אין לשונית UIPrograms, בתוך רכיב, או בתוך כל יחידה אחרת
Set data (set_data)Datasource, Fields, Events. אין לשונית UIPrograms, בתוך רכיב, או בתוך כל יחידה אחרת
Alertלשונית אחת, Alertתחת Programs או בתוך רכיב, בשום מקום אחר
JavaScriptלשונית אחת, Editorתחת Programs או בתוך רכיב, בשום מקום אחר
TableData, Fields, Indexesתחת Database, ושם Create הופך אפור על הטבלה עצמה
GlobalsFields, Eventsלא מהעץ: שורש Globals מסרב ילדים חדשים
Routeלשונית אחת, Routeתחת Routes, הטיפוס היחיד שהמקטע הזה מציע
AI agent (ai_agent)לשונית אחת, AI Agentתחת AI Agents, הטיפוס היחיד שהמקטע הזה מציע
Folderשום דבר בכלל. פתיחת תיקייה חוזרת מוקדםתחת Programs או Database, או בתוך רכיב או כל יחידה אחרת
בונה אפליקציות AI

העוזר כותב לתוך אותו עץ שאתם עורכים ביד, ומצב אחד אינו כותב דבר.

תארו מה אתם רוצים בפאנל ה-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 מיוצאים מאותה חבילה. מה שהחבילה הזאת עושה בתוך עצמה אינו נמצא במקור הפלטפורמה, ולכן הדף הזה מתאר את אתר הקריאה ועוצר שם.
תור אחד
  1. אתם מתארים
    בפאנל ה-AI של פרויקט פתוח, בהיקף כללי או קשור לצומת אחד
  2. הצ׳יפים קושרים את ההקשר
    התוכנית הפתוחה, צומת שנבחר במקום אחר בעץ, אלמנט ה-UI שנבחר
  3. 12 ההודעות האחרונות נוסעות איתם
    כל אחת נקטמת ל-2000 תווים לפני השליחה
  4. app_builder שומר, app_planner לא
    קבלת תוכנית שולחת אותה מחדש עם הוראה לבצע אותה מקצה לקצה
מה חוזר

תור בנייה מחזיר קבלה שמפרטת מה הוא שינה.

התשובה אינה המודל שמספר מה הוא עשה. מעצב בפלטפורמה קורא את התוצאה המובנית וכותב את ההודעה: הוא סופר את המסמכים, מקבץ אותם לפי טיפוס התפריט שלהם, מונה עד שנים עשר מהם בשם עם מזהה המסמך בסוגריים מרובעים, ומציין אם הם נשמרו. תור תכנון מצייר שדות אחרים לגמרי, סקירה, עד שתים עשרה יחידות מוצעות, שמונה צעדי סדר בנייה ושמונה בדיקות קבלה, ותמיד מציין שהוא שמר את התור במצב תכנון. כשהעוזר תקוע על משהו שהוא צריך מכם הוא גם אינו שואל בפרוזה: הוא פולט בלוק שאלות מובנה שהפאנל מצייר כשלבים, שתיים או שלוש אפשרויות לכל שאלה, עם שורת Other לטקסט חופשי שהממשק מוסיף בעצמו. בזמן שהתור רץ, טקסט הסטטוס מתחלף במחזוריות בין רשימה קבועה של שלוש הודעות, רשימה אחת לכל מצב, ומתחלף כל 1800 אלפיות שנייה.

תשובת העוזר · studio_method: app_builder
Generated 2 component, 1 table
ואז היחידות שנוצרו, עד 12 מהן:
component: <menuName> [<_id>]
component: <menuName> [<_id>]
table: <menuName> [<_id>]
ושורה אחת שמציינת אם הן נשמרו
Live-preview browser smoke test passed for 2 components.
השורה הזאת מופיעה רק כשהתור הריץ את הבדיקה
תור תכנון מציין את זה במקום, לאחר שלא יצר דבר:
Kept this turn in planning mode, so no studio docs were saved.
התוויות באפור הן שלנו. מה שהמעצב מקבע הוא שורת הספירה, שורה אחת לכל יחידה שנוצרה עם המזהה שלה, שורת בדיקת העשן ושורת מצב התכנון. גם התקרות אמיתיות: מעבר לשתים עשרה יחידות הרשימה נעצרת, כך שתור גדול מאוד נספר במלואו ומפורט בחלקו.
מקור נתונים

שני מנועים יושבים מאחורי בורר אחד.

על מקור נתונים של טבלה מצב הסינון הוא Index או Query, וכשההגדרה חסרה זמן הריצה מסיק Index אם נבחר אינדקס ו-Query אם לא. אלה אינם שני מעטפות על מתכנן אחד. מצב Index קורא מפתח מורכב שהטבלה כבר כתבה על כל רשומה. מצב Query קורא בורר שמורכב בזמן הבקשה. מה שאתם יכולים לבטא, ומה שמובטח לכם, משתנה עם מיקומו של הבורר הזה.

המגבלה הכנה נמצאת בצד האינדקס, והיא שקטה. אם האינדקס שנבחר כבר אינו על הטבלה, או אם לאינדקס יש מפתחות אבל כל תאי הטווח הושארו ריקים, בניית המסנן מוותרת והקריאה מתרחבת לסריקה של כל הטבלה. אותו catch בולע כל דבר שנזרק בזמן הרכבת המסנן, ומתעד אותו כאזהרה ולא יותר. אינדקס שהוגדר לא נכון נקרא כאיטי, לא כשבור.
לשונית ה-UI

אין רשומת מאפיינים. כל מאפיין הוא 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.
מרחב שמות אחד, וכולו נשלח
xu-ref, השם שהאלמנט נרשם תחתיו
xu-bind, השדה שהוא קורא וכותב
xu-show, מתג תצוגה שמשאיר אותו מורכב
xu-if, xu-else-if, xu-else, שרשרת ענפים אחת
xu-for, עם xu-for-val ו-xu-for-key שנותנים שם למשתני הלולאה
xu-class, שמות class שממופים לביטויים, נשמרים כ-JSON
xu-script, xu-style ו-xu-cdn, מוצמדים לאלמנט יחיד
xu-exp: לפני כל אחד מהם, מחושב במקום ליטרלי
קנבס וזמן ריצה

הקנבס הוא משטח פריסה. זמן הריצה נמצא במרחק מתג אחד.

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

שני המשטחים כנים ברגע שיודעים מי מהם מי. בקנבס העיצוב, xu-panel ו-xu-widget לעולם אינם מציירים את התוכן שלהם, ו-attributes של תנאי מותאמים כטקסט ולא מוערכים. כל מה שבאמת רץ, רץ בצד השני של המתג.
ביצוע

תהליך עבודה הוא תור משימות, לא רשימת המתנות.

אירוע אינו מריץ את הפעולות שלו זו אחר זו. הוא עובר על תהליך העבודה פעם אחת ודוחף משימה אחת לכל צעד מאופשר לתוך מערך יחיד שמוחזק על ההפעלה, ולולאת ניקוז נפרדת שרצה כל אלפית שנייה לוקחת את המשימה הראשונה שאינה מסומנת כעסוקה ומבצעת אותה. הסדר הוא סדר המערך, מלמעלה למטה. מה שמתחיל את הצעד הבא אינו הצעד הקודם שמסתיים, אלא הצעד הקודם שמוחק את המשימה שלו עצמו. דגל ה-async הוא שורה אחת בתוך הלולאה הזאת.

מה שאינו בתמונה הזאת: אין הסתעפות, אין לולאה ואין בלוק מקבילי. חזרתיות מגיעה מאירוע מרווח בצד הלקוח או מקריאה לתוכנית אחרת, וכלי המקביליות היחיד בדף הוא אותו דגל async לכל פעולה. קינון הוא שרשור ולא מחסנית: צעד שקורא לתוכנית אחרת מכניס את משימות אותה תוכנית ישירות מאחורי שלו עצמו, כך שהן רצות לפני הצעד הבא של הקורא. שום חסם עליון על פעולות async מקבילות אינו מופיע בשום מקום במקור.
קטלוג הפעולות

עשרים וארבע פעולות בתשע קבוצות, והארגומנט שכל אחת קוראת.

בורר Add Action נבנה מקובץ קטלוג אחד, ומחוץ לרשומות ה-AI כל פעולה מסכימה בשלוש דרכים: הקטלוג, מפת המטפלים בזמן הריצה, ורישום המאמת שמתעד את מפתח המטען שכל פעולה קוראת. הרישום הזה הוא העמודה הימנית. שני דברים לפני הטבלה. Invoke Action מציע אחד עשר ערכים ברשימה הנפתחת שלו ותשעה מהם עובדים, מפני ש-act_close ו-act_back מוערים כהערה גם בזמן הריצה וגם ברשימת ההיתר של המאמת. ושבע פעולות ישנות יותר על אלמנטי UI נדחות בתוקף כמיושנות ולא נסבלות בשקט. אפשר גם ליצור פעולה בלי הבורר: גררו צומת מהעץ אל תהליך העבודה וטיפוס הצומת קובע את הפעולה, כך שצומת get data הופך ל-Get Data ורכיב הופך ל-Call Page. הפילו אותו על שורה שכבר קיימת והוא מכוון מחדש את אותה שורה במקום להוסיף חדשה.

הפעולות שבקבוצה
מה הפעולה קוראת
ניווט, 3Call Page, Call Modal, Call Popoverכל אחת מציינת את הרכיב שייפתח, וזה כל המטען.
בסיס נתונים, 3Get Data, Set Data, Batchיחידת מקור הנתונים שתרוץ. Set Data ו-Batch מציינות גם את השדה שהתוצאה שלהן נוחתת בו.
JavaScript, 4Call Native JS, Call Evaluate JS, Execute Native JS, Execute Evaluate JSשתי פעולות Call מציינות יחידת תוכנית JavaScript בעץ. שתי פעולות Execute נושאות את הקוד עצמו, מוקלד לתוך הפעולה.
מטפלי אירועים, 4Raise Event, Emit Event, Invoke Action, DelayRaise Event מציינת אירוע ואת הפרמטרים שלו, כל אחד מהם ערך ליטרלי או ביטוי. Invoke Action קוראת אחד מתשעת הערכים המותרים. Delay קוראת מספר של אלפיות שנייה.
כלי ממשק, 4Call Alert, Alert, Loader On, Loader OffCall Alert מציינת יחידת alert. Alert נושאת מצב תצוגה, טיפוס, כותרת וגוף משלה. שתי פעולות הטוען אינן קוראות דבר.
API, 3Call Project API, Call System API, Call External APIAPI של פרויקט מציין יחידה בעץ שלכם, API של מערכת מציין מתודה, ו-API חיצוני מקבל כתובת ושיטת HTTP.
מערך נתונים, 1Updateערך אחד, שנכתב כ-"@field: expression; @other: expression", או כאובייקט JSON5.
תוספים, 1Call Libraryשם התוסף והמתודה עליו, כך שספרייה שנרכשה נקראת כמו כל פעולה אחרת בטבלה הזאת.
הערה, 1Commentשום דבר. הרישום מתאר אותה כתיעוד בלבד, והמטפל בזמן הריצה אינו עושה דבר מלבד למחוק את המשימה שלו.
עד לקוד אמיתי

פתח המילוט הוא נתיב, לא תיבת טקסט.

שבעה מקומות מאפשרים לכם להפסיק להגדיר ולהתחיל לכתוב, והעמוק שבהם אינו פעולה כלל: תוכנית 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 ונקרא עם האלמנט ואירוע הדפדפן.
כל דלת, וכולן נשלחות
משבצת ביטוי, כולל שתי משבצות התנאי
יחידת תוכנית JavaScript, נבנית בשמירה
Call JavaScript, הרצת אותה יחידה מתוך תהליך עבודה
Execute JavaScript, קוד מוטבע שמקבל את האלמנט ואת האירוע
xu-script על אלמנט אחד, רץ כשהוא נעשה נראה לראשונה
xu-style ו-xu-style-global, מוגבל להיקף או לא, על אלמנט אחד
xu-cdn, כתובת CSS או JS חיצונית שנטענת לפני שהצומת מצויר
תצוגה מקדימה חיה

התצוגה המקדימה היא עמיתה של העורך שלכם, ומותר לה לוותר עליכם.

הפעלת תצוגה מקדימה נפתחת על ערוץ שנקרא על שם אסימון ההפעלה שלכם ומבקשת מהלשונית הפתוחה שלכם מסמכים מזהה אחר מזהה. Studio עונה מהזיכרון עם עותק של המסמך שעל המסך שלכם, ולכן אין באנתיב הזה שום באנדלר ושינוי שנשמר נמצא בהפעלה הרצה מיד. יש גם מועד אחרון. אם הלשונית שלכם לא ענתה תוך חמש שניות, אותה הפעלה עוברת לעותק הבנוי שעל השרת ולעולם לא שואלת אתכם שוב לכל אורך חייה.

תיקון אחד למה שהתרשים הזה נהג לומר. הדחיפה בשמירה אינה document_changed. שינוי קוד שנשמר נוסע כ-studio_doc_saved ונושא את המסמך כולו, ולכן התצוגה המקדימה מציירת מחדש את הפאנלים שקשורים לאותו מסמך במקום לטעון מחדש את הדף. document_changed הוא הערוץ האחר לגמרי, מועבר מחדר הסוקט של השרת, וכל מה שהוא עושה הוא לשלוף מחדש מקורות נתונים בזמן אמת שמזהה הטבלה שלהם תואם. תפס חמש השניות הוא המחיר הכן של כל הסידור הזה, וזו הסיבה ש-Hard Reload הוא כפתור מהשורה הראשונה ולא מחשבה שנייה לניפוי.
הקונסולה

תיבת הפקודה היא eval, שרצה בתוך הדף שאתם מציגים בתצוגה מקדימה.

אין אוצר מילים של פקודות ללמוד, מפני שאין אוסף פקודות. מה שאתם מקלידים נשלח לתצוגה המקדימה כמחרוזת אחת, מוערך שם, והתוצאה נשלחת בחזרה ומצוירת כעץ בעומק שתי רמות. כל מה שהדף הרץ יכול לראות, אתם יכולים לראות: אובייקט ההפעלה, מקורות הנתונים, עוזרי זמן הריצה. זריקה חוזרת עם דגל שגיאה ומצוירת באדום. Inspect הוא החצי השני: הדליקו אותו, כל אלמנט שנושא ui id מקבל מסגרת מתחת לסמן, ולחיצה על אחד מהם מחזירה את הרכיב שמחזיק אותו ואת הצומת שבתוכו, עם כפתור שפותח את אותו רכיב בלשונית ה-UI שלו ובוחר את הצומת בעץ. זה מפיקסל לצומת בשתי לחיצות.

... מתחבר להפעלה
$ new URLSearchParams(location.search).get("engine_mode")
"live_preview"
$ typeof func.index.delete_pouch
"function"
$ SESSION_OB.root_element
SESSION_OB is not defined error: true
inspect on . כל אלמנט עם ui id מקבל מסגרת מתחת לסמן
לחיצה על פיקסל בתצוגה המקדימה
live_preview_element_reference_result { prog_id, node } [ Open ]
Open . הרכיב שמחזיק אותו נפתח בלשונית ה-UI שלו, ואותו צומת נבחר בעץ
get session . live_preview_session_result
{ __type: "Element", tagName, id, className } "[Circular]" "[Function name]"
root_element, WORKER_OBJ ו-DS_UI_EVENTS_GLB מוסרים לפני השליחה
הסימן בשורת הפקודה הוא סימן דולר ברגע שההפעלה מדווחת שהיא מוכנה, ושלוש נקודות בזמן שהיא עדיין מתחברת. חץ למעלה עובר על הפקודות שהקלדתם, בזיכרון בלבד, ולכן טעינה מחדש מתחילה אתכם עם היסטוריה ריקה.
שמירה

שמירה היא כתיבה מקומית. הגרסה נכתבת על ידי משהו אחר, אחר כך.

אין שמירה אוטומטית. לחיצה על שמירה מוסרת את המסמך ל-worker, שמחתים אותו וכותב אותו לתוך בסיס נתונים בתוך הדפדפן שלכם, וזה כל מה שהקשה שלכם עושה. כל מה שאחריה הוא מכונות אחרות: שידור ללשוניות האחרות שלכם, שכפול החוצה לבסיס הנתונים של האפליקציה, ותהליך שרת שצופה באותו בסיס נתונים והוא זה שבאמת מנפיק גרסה. הסדר חשוב, מפני שהוא אומר לכם מה שורד רשת שנופלת ומה לא.

התוצאה היא החלק הכן. העבודה שלכם בטוחה מרשת שנופלת מפני שהכתיבה הראשונה מקומית, אבל הגרסה אינה שלכם לתזמן: התג יכול לקרוא v ושלוש נקודות לרגע בזמן שהלכידה נוחתת, והחלונית בודקת מחדש בהשהיה קבועה של 1200, 3000, 6000, 12000 ו-24000 אלפיות שנייה עד שזה נפתר. לאותה צורה יש גם תמורה. מפני שהלכידה צופה בבסיס הנתונים ולא בעורך שלכם, עריכה שנכתבה על ידי תוסף או על ידי תהליך שרת מקבלת גרסה בדיוק כמו עריכה שהקלדתם.
היסטוריה

לתג יש ארבעה מצבים, ואחד מהם אומר שהלשונית שלכם אינה מעודכנת.

התווית שליד העורך נגזרת ולא נשמרת: היא משווה את הרוויזיה ששמרתם מול התצלום החדש ביותר שעל השרת. Current פירושו שקיים תצלום מדויק לרוויזיה שלכם, או שההבדל הוא מטא נתוני בנייה בלבד. Recording פירושו שהרוויזיה שלכם מקדימה והלכידה עוד לא נחתה. Behind פירושו שהרוויזיה ששמרתם ישנה יותר מהתצלום החדש ביותר, כלומר הפעלת הדפדפן הזאת ישנה דרך רוויזיות, והחלונית אומרת זאת בכרזה במקום לטעון שאתם מעודכנים. Unknown פירושו אותו דור, תוכן שונה, מה שהמקור מכנה ענף מתפצל. בחירת שורה ישנה יותר מציגה אותה במקום כשעותק העבודה שלכם מוחזק בהפניה, ובזמן שתצוגה מקדימה למעלה שני מסלולי השמירה דוחים את המסמך על הסף. Restore היא הפעולה היחידה שכותבת: היא קוראת מחדש את הרוויזיה החיה מהשרת קודם, שואלת לפני דריסת שינוי שהגיע בזמן שהסתכלתם, שואלת שוב לפני השלכת עריכות שלא נשמרו, ואז משתרשרת על הרוויזיה הטרייה ביותר הידועה כך שהשחזור נוחת נקי בראש.

תצוגה מקדימה
תצוגה מקדימה במקום, השמירה חסומה, escape מחזיר את עותק העבודה שלכם
שחזור
שחזור שומר קדימה, כך שגם השחזור הפיך

שמירה, נלכדת בשרת רגע לאחר מכן

מה שאינו כאן, במלואו: אין ענפים, אין תצוגת השוואה או הפרש, אין מיזוג, אין הודעות קיבוע. ההיסטוריה היא לכל תוכנית ולא לכל פרויקט. מספרי הגרסאות הם מיקומיים, ולכן v3 היום אינו v3 בשבוע הבא. חמישים תצלומים נשמרים לכל מסמך והשאר נמחקים ולא מאורכבים. הלכידה אסינכרונית ורצה בצד השרת, ולכן שמירה טרייה נקראת כ-recording עד שהיא נוחתת. וביטול הוא מערכת אחרת שאינה חולקת דבר עם זו: חמישים צעדים לכל מסמך, מקובצים ב-400 אלפיות שנייה כך שגרירה או רצף הקלדה הם צעד אחד, מוגבלים לצומת שבחרתם, מוחזקים בזיכרון הדפדפן בלבד, ונעלמים ברגע שאתם טוענים מחדש. אחרי טעינה מחדש, המסילה הזאת היא הדרך היחידה חזרה.

שני עורכים

יש נעילה. אין מיזוג.

הנעילה היא check-in, והיא לכל צומת. השמירה חסומה עליה: צומת ש-check-in שלו מוחזק על ידי מישהו אחר נדחה עם שמו של מי שמחזיק בו, וצומת שאיש לא עשה לו check-in נדחה עם Please check-in לאחר טיפוס בשרשרת ההורים כדי לראות אם משהו מעליו מוחזק. טיוטות פטורות, וכך גם אפליקציה שמעולם לא נשמרה. מה שאינו נעול הוא התוצאה ברגע שרוויזיה של מישהו אחר כן מגיעה למסמך שפתוח לפניכם. Studio מכריע בין שלושה בדיוק, והוא מחשב אם יש לכם עריכות שלא נשמרו לפני שהוא מעדכן את קו הבסיס שלו, וזה הסדר שהופך את התשובה לאמינה.

  1. 01

    העותק שלכם נקי, אז אתם לוקחים את שלהם במלואו

    שום דבר משלכם אינו על הכף, ולכן Studio מקדם את המסמך הפתוח לרוויזיה שלהם במלואה ומרענן את דגל הלשונית מיד ולא מחכה לצופה המווסת. אתם מגלים מפני שהתוכן השתנה תחתיכם, לא מפני שדיאלוג שאל.

  2. 02

    יש לכם עריכות, והכתיבה שלהם הייתה המהדר

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

  3. 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.
  • הרצת שרת כותבת מסמך בדיקה אחד לכל תוכנית, ממופתח על אותה רוויזיית תוכנית, כך שתוכנית שאיש לא נגע בה מאז המעבר האחרון מדולגת על הסף ולא נבדקת מחדש.
  • כל קוד בקטלוג שנוצר נושא את אותם שדות: כותרת, חומרה, תחום, קטגוריה, תקציר, סיבות, צעדי פתרון, מזהה עזרה, דפוסים ישנים וקודים קשורים. הוא נוצר מרישום השגיאות, לא נכתב ביד.
  • שני המארחים אינם מפיקים פלט זהה. הרצת השרת מאותחלת עם רשימת הפריסות ויכולה לסמן תוכנית כמשמשת פריסה. הרצת הדפדפן לא, ולכן פסק הדין של לא בשימוש שונה ביניהם.
משפחות קודי הבודק הגדולות ביותר, לפי הפניות במקור הבודק
CHK_MSG_OBJ_GEN
81 הפניות
כללי אובייקט כלליים, בפער ניכר המשפחה שתפגשו ראשונה
CHK_MSG_PRG_TRG
48 הפניות
טריגרים של תהליך עבודה, המשטח השני בכובדו בעץ
CHK_MSG_PRG_FLD
17 הפניות
שדות, כולל ממה מותר לשדה להיות מחושב
CHK_MSG_PRG_AGN
17 הפניות
סוכנים, שווים לשדות במקום השלישי מבין שבע עשרה משפחות
שילוח

בנייה ופריסה הם אובייקטים שונים, וגרסה יושבת ביניהם.

בנייה כותבת תצלום ממוספר. פריסה מכוונת מכונה לאחד מהם. המספר על התג, #37, הוא app_build_id, מונה לכל פרויקט שעולה באחד בכל פעם ש-build_app רץ. ה-1.4.0 שלידו הוא מחרוזת נפרדת שאתם מקלידים, שמאומתת על ידי דפוס major, minor, patch פשוט בתג עצמו. הם זזים באופן עצמאי, ושניהם מוחתמים על מסמך הפריסה כך שפריסה תמיד מתעדת בדיוק איזה תצלום היא משרתת.

כשבנייה #38 נוחתת, כל פריסה של הפרויקט תחת קוד סטטוס 4 מקבלת update_required בערך true והתראת new_version_available שנושאת את מזהה הבנייה החדש. שום דבר אינו מועתק לשום מכונה בעקבות זה, והפריסה הזאת ממשיכה לשרת את #37 עד שמישהו מזיז אותה. הזזת פריסה אחת לבנייה חדשה היא update_app_version, משימה נפרדת בת עשרה צעדים בהרשאת admin. היא מתחברת מחדש ב-SSH עם עד עשרה ניסיונות במרווחים של עשר שניות, מסנכרנת את הדרייב, מחשבת מחדש לאילו תוכניות וטבלאות הפריסה הזאת רשאית להגיע, מוחקת ומעתיקה מחדש גם את ספריית התוכניות וגם את ספריית זמן הריצה, מתקינה מחדש את חבילת התוספים, מריצה pm2 delete all, מפעילה מחדש את נתב ה-websocket ואת המערכת, מנקה את ה-CDN וכותבת חבילת אתחול טרייה. התהליכים יורדים וחוזרים. שתי פריסות של אותו פרויקט יכולות לשבת על מספרי בנייה שונים ללא הגבלת זמן, ושום דבר אינו מיישב ביניהן בשבילכם.
הקצאה

לחיצה על פריסה אינה מתקינה דבר.

קריאת הפריסה היא החצי הראשון של לחיצת יד. היא יוצרת רשומות, מבקשת מכונה וחוזרת בזמן שהמכונה עדיין נוצרת. ההתקנה מתחילה כשהמכונה עצמה קוראת בחזרה לפלטפורמה, ולכן סרגל ההתקדמות יכול לעמוד במקום זמן מה באמצע ולכן בסולם הסטטוס יש בכלל שלב המתנה: 1 נוצר, 2 מתקין או ממתין, 3 פעיל, 4 נכשל או הופסק.

  1. 01

    הטופס חוזר לפני שקיים משהו

    deploy_app כותב את מסמך הפריסה, ואופציונלית מסמך datacenter, מבקש מהמארח ליצור מכונה באופן אסינכרוני, וחוזר אליכם. שום דבר לא הותקן ושום דבר לא הועתק. הפריסה יושבת בקוד סטטוס 2 תחת התיאור farm server waiting to install, ובאמת אין מה להראות עד שהצעד הבא קורה מעצמו.

  2. 02

    המכונה קוראת בחזרה לפלטפורמה

    האתחול הראשון על המכונה החדשה שולח curl ל-webhook בפלטפורמה שנושא את מזהה האפליקציה. המטפל הזה פותח מחדש את אותה משימה שאתם כבר צופים בה, מחליף את רשימת הצעדים שלה ברשימת ההתקנה בת עשרים וארבע הרשומות, ומריץ את install_hosting_resources. המשימה שהתחלתם לשלב הראשון היא המשימה שמדווחת על שלב שתיים, ולכן שמות הצעדים משתנים תחתיכם באמצע.

  3. 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 להריץ תוכנית עד למחרוזת, ומקבל בחזרה גם סימון וגם מטען. המטען מוטבע לתוך הדף, ואלמנט העגינה מסומן במצב הציור ובהפעלת הלקוח שהשרת פתר עבורו.

שתי מגבלות ראויות לקחת מהתמונה הזאת. אין גיזום עץ לכל אפליקציה: כל פריסה שולחת את החבילה המלאה לבנייה שלה ורק ה-JSON של התוכניות שונה, ולמרות שחבילות slim ו-mini קיימות בספריית זמן הריצה, מנועי הציור של הפריסה מפנים למלאה. ואם הנתיב הימני קיים בכלל הוא הגדרה לכל פריסה שנפתרת בכל ציור דף: מצב המחשוב נפתר ל-main, worker או server, ו-main ו-worker שניהם כופים ציור בצד הלקוח בלי הפעלת שרת, מה שמשאיר לדפדפן לעשות את הכול.
תוספים

הקטגוריה בשם החבילה קובעת איפה תוסף מופיע.

כל 62 התוספים מתוצרת הבית נקראים באותה דרך, והקטגוריה בשם הזה נמצאת גם על רשומת התוסף. זה כל כלל הניתוב: התקינו אחד והוא מופיע בבורר מקורות הנתונים, בסרגל ההוספה, בבורר האייקונים או ברשימת פעולות תהליך העבודה, בלי שום דבר לחווט. התקנה גם אינה דגל תכונה. השרת כותב רשומה על הפרויקט שנושאת את הגרסה, את הטיפוס ואת מצאי הקבצים, מריץ npm install אמיתי לתוך ספריית התוספים של אותו פרויקט, מסנכרן את הספרייה הזאת החוצה לשרתים, ומעתיק את כל עץ קובצי החבילה לתוך בסיס הנתונים של הפרויקט ממופתח לפי נתיב יחסי.

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

תשעה דברים ש-Studio אינו עושה.

  • תיבת ההנחיה במסך הפתיחה של Studio אינה בונה דבר: היא שולחת בקשת צ׳אט פשוטה אחת שמוגבלת ל-1024 אסימונים, וכשההודעות מוסתרות התשובה אפילו אינה מצוירת, מפני שהבונה חי בפאנל ה-AI של פרויקט שכבר פתוח.
  • בחירת Template אינה פותחת גלריה בתוך העורך: היא פותחת את אשף היצירה של לוח הבקרה בלשונית דפדפן שנייה, והפרויקט שהיא מפצלת הוא תצלום שאין דבר שמקשר אותו בחזרה למקור שלו אחר כך.
  • טיוטה אינה נמצאת על שרת: היא חיה בפרופיל דפדפן אחד על מכשיר אחד עד שאתם לוחצים על שמירה, וניקוי נתוני האתר מסיים אותה.
  • תביעת ה-check-in שחוסמת את השמירה אינה חכירה בשרת: היא שדה על המסמך, ולכן שמירה שלעולם אינה מגיעה לשרת לעולם אינה מנקה אותה, ושום דבר אינו ממזג שתי עריכות בשבילכם.
  • שום דבר אינו יכול לגרום לבנייה להיכשל על שגיאות אימות: הבודק רץ קודם וסך השגיאות שלו נכתב לתוך הגרסה, אבל שום קורא אינו מבקש מהבנייה לסרב.
  • בנייה שהסתיימה אינה נוגעת בשום דבר שכבר חי, ושתי פריסות של פרויקט אחד יכולות לשבת על מספרי בנייה שונים כל עוד תשאירו אותן כך.
  • אין השקה מדורגת: תג הבנייה יכול לצייר כרזת מדורג אך לא חי, אבל בנייה רגילה מקצה למספר המדורג ולמספר החי אותו ערך, ולכן הכרזה הזאת אינה יכולה לרוץ.
  • אין גיזום של זמן הריצה לכל אפליקציה, ולכן כל פריסה שולחת את אותה חבילת מפרש מלאה לבנייה שלה ורק ה-JSON של התוכניות שונה.
  • פריסה מתוזמנת לא תריץ מסך, רק תוכניות batch, get_data ו-set_data, וצעד JavaScript בצד השרת נעצר בשישים שניות.
לפני שאתם מתחייבים

השאלות שמתגבשות אצלכם עכשיו

כל אחת משבע התשובות האלה מסתיימת במגבלה. הן כאן מפני שהייתם פוגשים את כולן בשבוע הראשון ממילא.

צפייה

צפו ב-Studio פותח פרויקט וממלא את העץ.

פתחו פרויקט ריק וראו מה נכתב.

שני מסמכים, נתיב אחד, והעורך פתוח. שום דבר אינו מגיע לשרת עד שאתם שומרים, ושום דבר אינו מגיע למבקר עד שאתם בונים, ואז מזיזים פריסה לבנייה הזאת בכוונה.