GenHTTP Lambda

Nasıl çalışır

Bir parça C# kodu yazarsınız. Döndürdüğü şey birkaç saniye içinde HTTPS üzerinden, herkese açık bir adreste yayına girer. Aşağıda her şey, karşılaşacağınız sırayla anlatılıyor.

Lambda nedir?

Lambda, bir GenHTTP handler döndüren bir kod parçasıdır. Platform onu derler, yükler ve döndürdüğü şeyi kendi adresinizin altına bağlar. Proje yok, build dosyası yok, using satırı yok. Tüm GenHTTP modülleri zaten içe aktarılmış durumda.

return Content.From(Resource.FromString("hello"));

Bu, eksiksiz bir lambda. /lambda/your-key/ adresinde yayına alındığında her isteğe “hello” kelimesiyle yanıt verir.

Kod parçası bir sınıf değildir, deyimlerden oluşur. Yaptığı son iş, istekleri karşılayabilen bir şey döndürmektir: bir handler ya da onu oluşturan bir builder.

İlk lambdanız

  1. 1
    Lambda oluştur düğmesine basın. Herkese açık bir adres ve bir editör anahtarı alırsınız. Anahtar, geri dönmenin tek yolu. Saklayın, çünkü kimse onu sizin için kurtaramaz.
  2. 2
    Ardından lambdanın kontrol paneline gelirsiniz. İlk sürüm olarak küçük bir REST servisi hazır bekler. Bu sadece bir başlangıç.
  3. 3
    Editör anahtarını bir ajana verin ve ne yapacağını söyleyin. Ajan MCP üzerinden yeni sürümler yazar. Ya da Kod bölümünü açıp kendiniz yazın: Kontrol et hiçbir şey kaydetmeden derler ve derleyicinin ne dediğini dosya ve satırıyla gösterir.
  4. 4
    Yayına al düğmesine basın. Artık yayında. Bundan önce hiçbir şeye erişilemez. Yeniden yayına almak da yayında kalma süresini uzatır.

Kontrol paneli

Editör linki bir metin kutusu değil, bir kontrol paneli açar. Buradaki kodun çoğunu ajanlar yazar, bu yüzden ekranda ilk gördüğünüz şey lambdanızın durumudur. Kenar çubuğunda lambdanın kendisi (yayında olup olmadığı, adresi ve daha yeni bir sürüm yayına alınmayı bekliyorsa bir düğme) ve bölümleri yer alır. Adresi değiştirmek ya da lambdayı silmek gibi nadiren yapılan işler, oradaki ⋯ menüsündedir.

Genel bakış
Yayında olup olmadığı, bugün kaç istek aldığı ve kaçının başarısız olduğu, son değişiklik ve ne kadar yer kaldığı.
Değiştir
Neyin farklı olması gerektiğini yazın, gerisini bu sunucudaki ajan siz izlerken halleder. Bir taslakta çalışır, değişikliği orada dener ve çalışınca birleştirip bir sonraki sürüm yapar. Taslağı önce kendiniz denemek isterseniz Bitince yayına al seçeneğini kapatın.
Taslaklar
Lambdanın yanında üzerinde çalışılan değişiklikler: her biri kendi adresinde denenir ve hazır olunca birleştirilip bir sonraki sürüm olur. Açıldığında bir taslağın kendi kodu, verileri ve logları vardır.
Dosyalar
Bir sürümün dosyaları: kodu ve statik dosyaları, yani programın kendisi. Kilit ya da dünya simgesi, herkesin onlara erişip erişemeyeceğini gösterir.
Veriler
Lambdanın çalışırken sakladıkları, tüm sürümler için ortak: çalışma alanı. İçine bakın, dosya yükleyip silin ya da onu kapatın.
Sürümler
Her sürümün neyi değiştirdiği, ne istendiği ve bir öncekinden farkı. Buradan yayına alabilir, eski bir sürüme dönebilir ya da herhangi bir sürümden bir taslak başlatabilirsiniz.
Yayın geçmişi
Ne zaman neyin yayında olduğu ve neden yayından kalktığı.
İstatistikler
Son bir saatin ya da günün istekleri, hataları, yanıt süreleri ve en çok istenen yolları.
Loglar
İstekler, lambdanın yazdırdıkları ve ters giden her şeyin stack trace’i, anında.
Kod
Elle yazmak için. Kontrol et derler, Kaydet bir sürüm oluşturur, Yayına al yayına alır. Bir taslakta ise Kaydet onu taslakta tutar, Önizlemeyi yayına al da taslağın adresinde yayına alır. Ctrl-S kaydeder, F12 bir tanıma gider.

Her bölüm aynı şekilde çalışır: başlığı, onu açıklayan bir ⓘ simgesi, sağda eylemleri ve birden fazla görünümü varsa altında bir sıra sekme. Kod bölümünde bu sekmeler dosyalardır.

Trafik ve log bellekte tutulur. Saklamak için değil, izlemek içindir: sunucu yeniden başlarsa sıfırdan başlarlar. Sürümler ve yayın geçmişi ise kalıcı olarak saklanır.

Nedenini yazmak

Bir sürüm, kod ve isteğe bağlı iki nottan oluşur: spesifikasyon, yani kullanıcının ne istediği ve nedeni, mümkünse kendi sözleriyle; ve değişiklik, yani sürümün ne yaptığını anlatan tek bir satır. İkisi de sürüm geçmişinde diff’in yanında görünür. Böylece neden, ne ile yan yana kalır. Hem sizin için, hem de bir şeyi değiştirmeden önce geçmişi okuyan bir sonraki ajan için.

POST /api/v1/lambdas/{editorKey}/versions
{
  "files": [ { "name": "lambda.cs", "code": "..." } ],
  "specification": "İnsanların imza atabileceği bir ziyaretçi defteri; kayıtlar yeniden başlatmada kaybolmamalı",
  "change": "Kayıtları çalışma alanında tutar, böylece yeniden başlatmada kaybolmazlar"
}

Ajanlar da aynı iki alanı write_code aracına verir. Kod bölümünde kaydederken değişiklik sorulur. İkisi de isteğe bağlıdır. Uzun bir spesifikasyon reddedilmez, 4.000 karakterde kesilir; değişiklik ise 500 karakterde. Bir taslağın da kendi iki notu vardır; birleştirildiği sürüm bunları devralır.

Güvenle değiştirmek

Bir sürüm, kaydedildikten sonra bir daha değişmez. Her birini saklamaya değer kılan da bu: herhangi biriyle karşılaştırma yapılabilir, herhangi biri tam olduğu gibi yeniden yayına alınabilir. İnsanların kullandığı bir lambdayı değiştirmek için bunun yerine bir taslak başlatın.

  1. 1
    Taslaklar bölümünden ya da herhangi bir sürümden başlatın. Taslak, o sürümün kodunun ve statik dosyalarının, bir de lambdanın verilerinin kopyasıdır.
  2. 2
    Gerektiği kadar değiştirin: Kod bölümünde ya da ajandan isteyerek. Önizlemeyi yayına al onu kendi adresinde, /features/…/ altında, verilerin kendine ait kopyasıyla yayına alır. Lambdanın ziyaretçileri bunların hiçbirini görmez; taslağın yazdığı hiçbir şey lambdanın verilerine ulaşmaz.
  3. 3
    Hazır olunca Birleştir düğmesine basın: notlarıyla birlikte bir sonraki sürüm olur ve isterseniz hemen yayına girer. Taslak ise önizlemesi ve verilerin kopyasıyla birlikte kaldırılır.
POST /api/v1/lambdas/{editorKey}/features
{ "name": "Skor tablosu" }

PUT  /api/v1/lambdas/{editorKey}/features/{feature}/files?deploy=true
POST /api/v1/lambdas/{editorKey}/features/{feature}/merge
{ "deploy": true }

Aynı anda birden fazla taslak üzerinde çalışılabilir. Yalnızca en yeni sürümü temel alan bir taslak birleştirilebilir; böylece bir birleştirme, taslak başladıktan sonra kaydedilen bir sürümü asla geri almaz. Önce başka bir taslak birleştirildiyse onun değişikliklerini taslağa taşıyın (ya da ajandan isteyin), sonra taslağın temelini en yeni sürüm yapın. Hiçbir şey kendiliğinden birleşmez; bu bilinçli bir tercih.

Birden fazla dosya

Türlerin, onları kullanan kodun altında durması gerekmez. Kod bölümünde dosyaların yanındaki + düğmesine basın. Yeni dosya, kod parçasıyla aynı namespace içinde, onunla birlikte derlenir. Böylece erişmek için hiçbir şeyi içe aktarmanız gerekmez. Uzantısı olmayan bir ad C# dosyası sayılır.

lambda.cs
var shelf = new Shelf();

return Inline.Create()
             .Get(() => shelf.All())
             .Post((Book book) => shelf.Add(book));
Shelf.cs
public sealed class Shelf
{
    private readonly List<Book> _books = [];

    public IEnumerable<Book> All() => _books;

    public Book Add(Book book)
    {
        _books.Add(book);
        return book;
    }
}

public record Book(string Title, string Author);

Sayfa sunmak

Sayfa sunmanın iki yolu var. İnsanların yanına yüklediği dosyalar için de bir üçüncüsü.

Tek sayfa, kodun içinde

Küçük şeyler için yeterli. Sayfa doğrudan kodun içinde yer alır.

var page = Resource.FromString("""
                              <!doctype html>
                              <title>Mine</title>
                              <h1>It works</h1>
                              """)
                   .Type(new ContentType("text/html; charset=utf-8"));

return Content.From(page);

Gerçek dosyalarla bir klasör

Stil dosyası ve script içeren her şey için doğru seçim. Dosyalar tıpkı bir C# dosyası gibi eklenir ve tam yazıldığı gibi sunulur. Derlenmezler.

return Layout.Create()
             .Add("api", api)
             .Add(Assets.App("site"));

Yüklenen dosyalar, verilerden

İnsanların yüklediği ya da lambdanın oluşturduğu şeyler (görseller, belgeler) için; uygulamanın yanında sunulurlar. Uygulamanın kendi sayfaları için değil: onların yeri bir dosya klasörüdür, orada onlara ihtiyaç duyan kodla birlikte sürümlenirler.

return Layout.Create()
             .Add("api", api)
             .Add("uploads", Workspace.Files("uploads"))
             .Add(Assets.App("site"));

Adım adım bir frontend

Bunların ikincisi, baştan sona. Her demo, sayfasını bu şekilde web adlı bir klasörden sunar. Bir örnek görmek için demo-crud demosunu açın. Demolar salt okunurdur; editör anahtarları adlarıyla aynıdır.

  1. 1
    Kod bölümünde dosyaların yanındaki + düğmesine basın ve site/index.html yazın. Adında eğik çizgi olan bir dosya bir klasöre girer. Uzantısı olan bir ad da uzantısının söylediği türde dosya sayılır.
  2. 2
    site/app.css ve site/app.js dosyalarını da aynı şekilde ekleyin. Sayfanız onlara href="app.css" örneğindeki gibi adlarıyla başvurur. Çünkü klasör adresin bir parçası değil, sunulan içeriğin köküdür.
  3. 3
    Görsel ya da font gibi metin olmayan dosyalar için site klasöründeki bir dosyayı açın ve dosyaların yanındaki yükleme düğmesine basın. Dosya aynı klasöre gider. PNG bir metin editöründe yazılamaz, o yüzden yolu bu.
  4. 4
    lambda.cs dosyasında klasörü sunun:
    return Layout.Create().Add(Assets.App("site"));
  5. 5
    Yayına al düğmesine basın. site/index.html dosyası / adresinde, site/app.css dosyası /app.css adresinde yanıt verir. Hiçbir dosyayla eşleşmeyen her adreste sayfanın kendisi döner. Böylece kendi yönlendirmesini yapan bir frontend, biri bir deep link üzerinde sayfayı yenilediğinde de çalışır.
  6. 6
    Yanına bir API ekleyin, sayfanın konuşacağı bir şey olsun:
    var api = Inline.Create().Get("notes", () => notes);
    
    return Layout.Create()
                 .Add("api", api)
                 .Add(Assets.App("site"));

Dosyaların durduğu iki yer

Bir lambda dosyaları iki yerde tutar ve editör onları ayrı gösterir: Dosyalar bir sürümün dosyalarını (programı), Veriler ise çalışma alanını (programın sakladıklarını) tutar. Fark, kime ait olduklarında yatar. Bir sürümün dosyaları o sürüme aittir; veriler ise lambdaya aittir ve her sürüm onları paylaşır.

Bir sürümdeVerilerde
ne tutarkod ve statik dosyalar: frontend dahil programın kendisilambdanın yazdığı ya da birinin yüklediği her şey
ne zaman değişirhiçbir zaman: her değişiklik yeni bir sürümdüriçine bir şey yazıldığı anda
yayına almatam olarak bu dosyaları yayına alırona hiç dokunmaz
eski bir sürüme dönmekeski dosyaları geri getiriretkisi yok: her sürüm onu paylaşır
bir taslakonların bir kopyasıyla başlaronun bir kopyası üzerinde çalışır
ne zaman silinirsınır aşılınca, eski sürümlerle birliktelambdayla birlikte ya da siz kapattığınızda
koddan erişimAssetsWorkspace

İkisi tek bir yer olamaz. Olsaydı, her yayına alma ya lambdanızın o zamandan beri yazdığı her şeyi silerdi ya da yayına aldığınız dosyalardan hiçbir şey kaldırılamazdı. Skor tablosu tutan bir oyun ikincisini ister, sunduğu sayfa ise birincisini. Bu yüzden sayfa sürüme, skor tablosu da verilere girer.

Veri saklamak

Workspace, lambdanızın okuyup yazabildiği özel bir klasördür. Bir istekten ya da bir yayından daha uzun yaşaması gereken her şeyin yeri burasıdır.

var notes = new List<string>();

if (Workspace.Exists("notes.json"))
{
    notes.AddRange(JsonSerializer.Deserialize<List<string>>(Workspace.ReadText("notes.json")) ?? []);
}

return Inline.Create()
             .Get("notes", () => notes)
             .Post("notes", (string text) =>
             {
                 notes.Add(text);
                 Workspace.WriteText("notes.json", JsonSerializer.Serialize(notes));
                 return notes.Count;
             });

Ayrıca ReadBytes, WriteBytes, Delete, List, CreateFolder ve içeriği sunmak için Tree/Files/App da var. Dosya sisteminin geri kalanına erişilemez.

Websocket’ler

Destekleniyor, hem de sonradan akla gelmiş bir özellik olarak değil. demo-game demosu oyuncuları eşleştirir ve her oyunu sunucuda yürütür. En basit hâli üç callback’ten oluşur:

var room = new ConcurrentDictionary<IReactiveConnection, string>();

var socket = Websocket.Functional()
                      .OnOpen(c => { room[c] = "someone"; return ValueTask.CompletedTask; })
                      .OnMessage(async (c, text) =>
                      {
                          foreach (var other in room.Keys)
                          {
                              await other.WritePayloadAsync(text);
                          }
                      })
                      .OnClose((c, _) => { room.TryRemove(c, out _); return ValueTask.CompletedTask; });

return Layout.Create().Add("chat", socket);

Herkesin takıldığı bir nokta var: tarayıcı, websocket el sıkışmasında header ekleyemez. Handler’ın ihtiyaç duyduğu bilgiyi sorgu parametrelerinde gönderin; handler onu connection.Request.Header.Query üzerinden okur. Ya da gizli bilgileri ilk mesaj olarak gönderin.

İzin verilmeyenler

Kodunuz ortak bir sunucuda çalışır. Bu yüzden C# dilinin bazı kısımları derlenmeden önce reddedilir: süreç başlatmak, kendi soketlerinizi açmak, assembly yüklemek, çalışma alanınızın dışında dosya sistemine erişmek ve bunları aşmak için reflection kullanmak.

Geri kalan her şey mevcut, GenHTTP modül API’sinin tamamı dahil. Bir şey reddedilirse sadece başarısız olduğu değil, hangi satırda ve neden reddedildiği de söylenir.

Kodunuzu alıp gitmek

Editördeki .NET projesi olarak indir ile lambdanın tamamını alırsınız: açabileceğiniz, dotnet run ile çalıştırabileceğiniz ve saklayabileceğiniz bir solution. İçinde tek bir paket referansı var, bu platformdan ise hiçbir iz yok.

Kod parçanız Program.cs dosyasının gövdesi olur ve döndürdüğü şeyi sunan bir host içine yerleşir. Diğer dosyalarınız tam yazdığınız gibi gelir. Workspace ve Assets kodun yanında iki klasör olur ve aynı metotlarla çalışır. Yani kodunuzda hiçbir şeyi değiştirmeniz gerekmez.

Burada bir şey yapmadan önce bilmekte fayda var: yazdığınız kod sizindir ve eksiksiz olarak sizinle gelir. Onu bu makinede çalıştırmak, onu bu makineye bağlamaz.

İşi bir ajana bırakmak

/mcp adresinde bir MCP endpoint’i var. Bir ajanı buraya bağlayın, editörün yaptığı her şeyi yapabilir: kılavuzu okur, bir demoyu baştan sona inceler, dosya yazar, derler ve yayına alır. Altta aynı API çalışır.

Ajan çalışırken nedenini de söyler (write_code aracı spesifikasyonu ve değişikliği alır) ve yayına aldığı şeye bakabilir: read_logs aracı lambdanın son isteklerini, yazdırdıklarını ve fırlattığı her hatanın stack trace’ini döndürür. Ajan, kodunun çalıştığını varsaymak yerine böyle öğrenir. Siz de aynı şeyi kontrol panelinde izlersiniz.

Daha fazlası →