Start the setup
Documentation menu

This page is written in Turkish. The navigation is in English. A version written for AI tools is linked below as Markdown.

@devoplus/one-sdk

Devoplus One uygulamalarının (Devoplus Portal içinde iframe ile gösterilen kullanıcı uygulamaları) platform servislerine ince istemcisi. SDK sır taşımaz, kiracı seçmez ve belirteç üretmez: kimliği platform kapısının yazdığı başlıklardan okur, backend'e kullanıcının uygulama belirteciyle gider, dosyayı kuruluma bağlı dosya deposuna yazar.

Platformun tamamı: devoplus/devoplus-one (mimari docs/architecture.md, örnek uygulama apps/hello).

Kurulum

bash
pnpm add @devoplus/one-sdk        # kararlı sürüm (latest)
pnpm add @devoplus/one-sdk@next   # ön sürüm (test kanalı)

Kararlı sürümle (latest) kurun; @next yalnız test kanalındaki ön sürümü denemek içindir. İki kanal Sürümleme bölümünde anlatılır.

Paket yalnız ESM dağıtılır (dist/index.js + dist/index.d.ts); Node ≥ 22. Çalışma ortamı tipleri (OneDatabase, OneFilesBinding, OneCacheBinding, OneApp vb.) paketin içindedir; ek bir tip paketi gerekmez. tsconfig içinde "lib": ["ES2022", "WebWorker"] ve "types": [] yeterlidir (standart Web API'leri WebWorker kütüphanesinden gelir).

Yerel geliştirme: one komutu

Paket one komutunu da getirir; uygulamanın kök dizininde çalışır ve one.manifest.json'u okur:

KomutNe yapar
one dev [--as kişi] [--port n] [--api-base url]Uygulamayı yerel Portal içinde açar (http://127.0.0.1:8787/): kimlik bağlamı seçilen kişiden üretilir, e-posta, SMS ve mobil bildirim posta kutusuna yazılır, DEXA ve organizasyon şeması one.dev.json örnek verisinden gelir (Yerel Portal ve servis taklidi). --api-base (ya da ONE_API_BASE) servis taklidini kapatır ve servis isteklerini o adrese yönlendirir (kendi taklit sunucunuz için); yerel oturum belirteci gerçek Devoplus servislerinde geçmez.
one migratemigrations/ altındaki SQL dosyalarını yerel veritabanına uygular (one dev aynı veriyi görür).
one buildYükleme provası: kodu paketler, hiçbir yere göndermez (çıktı .one/dist/).
one data generate [--check]one.data.json veri modelinden yeni migration + src/data/types.ts üretir; yalnız ekleyen değişiklik (Veri modeli).
one upgrade [--no-install] [--json]SDK'yı izlediği kanalın (next ya da latest) son sürümüne tam sürümle sabitler, kurulu sürümü one.manifest.json sdkVersion alanına ve yükseltme durumunu one.platform-state.json'a yazar, ardından yeni SDK'nın one agents sync ve one doctor komutlarını çalıştırır (SDK sürümü beyanı ve yükseltme).
one upgrade --complete --from <sdk>@<rev> --catalog-revision <n> [--sdk-version <v>] [--env test|uat|prod] [--json]Platform güncellemeleri özeti işlenip düzeltmeler bitince yükseltme durumundaki işlenmiş değeri ilerletir (Güncelleme döngüsü).
one doctor [--json]Uygulamayı kurulu SDK'nın taşıdığı platform kuralları kataloğuna karşı tarar; yayın hattının kataloğa bağlı kapılarıyla aynı denetimdir, dosyaya yazmaz, zorunlu bulguda çıkış kodu 1'dir (Platform kuralları).
one agents sync [--json]AGENTS.md'nin platform kuralları bölümünü kurulu SDK'nın kataloğuyla yeniler; bölümün dışına dokunmaz (Platform kuralları).

Yerel yapılandırma ve veri .one/ altındadır, depoya girmez ve her komutta manifest'ten yeniden üretilir. Gerçek yükleme Devoplus GitLab hattıyla yapılır; geliştirici makinesinden yükleme yoktur. Komutun ekrana yazdığı ayrıntılar (araç adı, sürüm satırı) yerel çalışma ortamına aittir ve gizlenmez.

Güncelleme uyarısı

one, her komuttan önce kurulu SDK sürümünü paket deposundaki kanal etiketiyle karşılaştırır: ön sürüm (ör. 0.2.0-next.150) next etiketiyle, kararlı sürüm latest etiketiyle. Kurulu sürüm eskiyse hata akışına (stderr) şu uyarıyı yazar; komut yine çalışır ve çıkış kodu değişmez:

text
one: Devoplus One SDK'nın daha yeni bir sürümü var: kurulu 0.2.0-next.150, güncel 0.2.0-next.183 (ön sürüm kanalı "next").
one: Güncellemek için uygulamanın kök dizininde şunu çalıştırın: pnpm add @devoplus/one-sdk@next
one: Ardından npx one upgrade çalıştırın: sürümü tam sabitler ve one.manifest.json sdkVersion alanına yazar.
one: Bu denetimi kapatmak için ONE_NO_UPDATE_CHECK=1 tanımlayın.
KonuDavranış
Ne zaman sorulurYanıt 24 saat önbellekte tutulur; önbellek tazeyse ağa çıkılmaz. Bayatsa komut en çok 1,5 saniye bekler. Yanıt alınamayan deneme de kaydedilir ve 24 saat yinelenmez.
Önbellek$XDG_CACHE_HOME/devoplus-one/update-check.json; değişken yoksa Linux ~/.cache/devoplus-one/, macOS ~/Library/Caches/devoplus-one/, Windows %LOCALAPPDATA%\devoplus-one\Cache\. Önbellek yazılamıyorsa denetim ağa çıkmaz.
Paket deposuVarsayılan https://registry.npmjs.org/. Komut paket yöneticisi betiğiyle (npm run, npm exec, pnpm run) çalışıyorsa ve yapılandırmada başka bir depo tanımlıysa (npm_config_registry) o depoya sorulur.
Güncelleme komutuUygulamanın kilit dosyasına göre pnpm add, yarn add ya da npm install; komut uygulamanın değil genel (global) kurulumun SDK'sıyla çalışıyorsa npm install -g.
KapatmaCI tanımlıyken (0 ve false dışında) ya da ONE_NO_UPDATE_CHECK=1 iken denetim hiç çalışmaz. Çıktının uçbirime gitmemesi denetimi kapatmaz.
SessizlikHer hata (çevrimdışı, ulaşılamayan depo, bozuk yanıt, yazılamayan önbellek) sessizdir. Uyarı yalnız stderr'e yazılır; --json çıktısı bozulmaz.

Uyarıyı görürseniz başka bir işe geçmeden SDK'yı güncelleyin ve npx one upgrade çalıştırın, ardından pnpm type-check && pnpm test && pnpm build çalıştırın ve package.json, kilit dosyası ile one.manifest.json'u kodla birlikte yükleyin. Yayın hattının SDK sürüm denetimi Sürümleme bölümündedir.

SDK sürümü beyanı ve yükseltme (one upgrade)

one.manifest.json'daki sdkVersion alanı uygulamanın derlendiği SDK sürümüdür ve elle yazılmaz. Yayın hattı onu kurulu SDK sürümüyle karşılaştırır: farklıysa yayın durur; SDK 0.3.0 ve sonrasında alan eksikse de durur. Yazan iki yol vardır: iskelet üretici (create-devoplus-one-app, güncel sürümü paket deposundan okur) ve npx one upgrade.

Yükseltme iki adımdır; ilk adım eski SDK'nın komutunu gerektirmez:

bash
pnpm add @devoplus/one-sdk@latest # 1. SDK'yı uygulamanın paket yöneticisiyle ve izlediği kanalla güncelleyin (ön sürümde @next)
npx one upgrade                   # 2. yeni SDK'nın komutu: tam sürümle sabitler, sdkVersion'ı yazar
KonuDavranış
Paket yöneticisiKilit dosyasından: pnpm-lock.yaml ise pnpm, package-lock.json ise npm, yarn.lock ise yarn. İkinci bir kilit dosyası varsa ya da hiç yoksa komut hiçbir şeye dokunmadan durur (başka bir yöneticiyle kurmak ikinci kilit dosyası üretir ve yayın hattı durur).
KanalKurulu sürüm ön sürümse next, kararlıysa latest izlenir (komut kanalı değiştirmez). Kanalın güncel sürümü paket deposundan okunur ve package.json'a tam yazılır (--save-exact). Paket deposuna ulaşılamazsa komut hiçbir şeye dokunmadan durur (kanal etiketi kurulmaz); SDK'yı kendiniz tam sürümle kurup --no-install ile çalıştırabilirsiniz. Kurulumun çıktısı stderr'e gider (--json çıktısı bozulmaz).
Eski sürüme inmezTaban, kurulu sürüm, package.json'daki tam sürüm, manifestteki sdkVersion ve one.platform-state.json'daki işlenmiş/bekleyen SDK sürümünün (legacy hariç) en büyüğüdür. Kanaldaki sürüm tabandan eskiyse kurulum atlanır. Kurulu sürüm tabandan eskiyse (git pull sonrası node_modules güncel değil) komut durur ve önce pnpm install ister; taban yalnız one.platform-state.json'dan geliyorsa (SDK hiç kurulu değilse de) bağımlılıkları yeniden kurmak yetmez: komut SDK'yı bağımlılık tanımınızda o sürüme getirmenizi ya da dosyayı düzeltmenizi ister; paket yöneticisine özgü komut önermez. Kurulum istenen sürümü kurmadıysa manifest ve yükseltme durumu yazılmaz.
Çalışma alanıworkspace: ya da yerel dosya tanımında kurulum yapılmaz; kurulu sürüm yazılır. Kurulum yapılmadığında kilit dosyası aranmaz (çalışma alanı üyesinin kilit dosyası kökte durur).
--no-installKurmadan, kurulu sürümü yazar (SDK'yı az önce kendiniz kurduysanız).
Yükseltme durumuUygulama kökündeki one.platform-state.json: processed (değişiklik özeti işlenmiş son durum) ve pending (kurulan SDK sürümü ve o SDK'nın taşıdığı kural kataloğu revizyonu). processed bu komutla ilerlemez; yalnız --complete ilerletir. Dosya yoksa (yeni üretilen uygulamada da) önceki durum legacy sayılır ve ilk yükseltmenin özeti bütün geçmişi listeler: fazla bilgi verir, hiçbir şeyi atlamaz. Dosya var ama okunamıyorsa (ör. birleştirme çatışması) komut hiçbir şeye dokunmadan durur; işlenmiş durum ezilmez. Dosya depoya girer: ekip ve farklı makinelerdeki ajanlar aynı noktadan devam eder. one.manifest.json, one.platform-state.json, package.json ve kilit dosyası birlikte commit'lenir.
Kurallar ve denetimManifest ve yükseltme durumu yazıldıktan sonra yeni kurulan SDK'nın one agents sync ve one doctor komutları çalıştırılır: AGENTS.md'nin platform kuralları bölümü yeni katalogla yenilenir ve uygulama yeni kurallara karşı taranır. İkisinden biri düşerse yükseltme yine tamamlanmış sayılır; uyarı yazılır ve komutu elle çalıştırmanız istenir. Zorunlu bulgu yükseltmeyi durdurmaz, ama düzelmeden yayın hattı durur.
ÇıktıÖnceki ve bekleyen durum, AGENTS.md bölümünün yenilenip yenilenmediği ve one doctor özeti; --json ile aynı bilgi JSON olarak (agents, doctor). Uyarılar stderr'e yazılır.

Komut düşerse (kurulum hatası, okunamayan kurulu sürüm) manifest ve yükseltme durumu değiştirilmez. AGENTS.md, manifest ve yükseltme durumuyla birlikte commit'lenir.

Güncelleme döngüsü ve --complete

Yükseltmeden sonra yapay zeka aracınız Devoplus One sunucusundan platform güncellemeleri özetini, one upgrade raporundaki önceki işlenmiş durumla (previous) ister. Özet işlenmiş durumdan bu yana yeni ve metni değişen kuralları, bugün zorunlu düzeydeki bütün kuralları ve o tarihten bu yana çıkan SDK sürümlerini (sürüm ve başlık; ayrıntılı notlar uygulamanın kurulu SDK'sındaki node_modules/@devoplus/one-sdk/CHANGELOG.md dosyasında) verir. Özetin ilk satırı her zaman aynı uyarıdır: liste sunucunun bildiği katalogdan üretilir ve yayın hattınınkinden eski olabilir, yayında esas olan hattın doğrulama çıktısıdır. Özet hiçbir uygulamanın kurallara uyduğunu söylemez.

Bulgular düzeltilince özetin sonundaki komut değiştirilmeden çalıştırılır:

bash
npx one upgrade --complete --from legacy@0 --catalog-revision 2 --sdk-version 0.3.0 --env test
KonuDavranış
Özetin durumu--from, özetin hesaplandığı işlenmiş durumdur (<sdk>@<revizyon>). Dosyadaki processed ile birebir aynı değilse hiçbir şey yazılmaz: özet başka bir durumdan istendiyse arada kalan kurallar ve sürümler hiç gösterilmemiştir.
Doğrulama--catalog-revision ve --sdk-version aynı ortamın Devoplus One sunucusundaki kural kataloğuyla karşılaştırılır (--env, varsayılan test; ONE_ENV ya da ONE_AGENT_BASE ile de verilir). Revizyon sunucunun yayımladığından büyükse, sürüm sunucunun sürüm notlarının kapsadığından yeniyse ya da sunucuya ulaşılamazsa hiçbir şey yazılmaz.
Katalog revizyonuprocessed.catalogRevision verilen revizyon olur; daha küçükse değişmez.
SDK sürümüprocessed.sdkVersion, verilen sürüm ile kurulu (pending) sürümün küçüğü olur; işlenmiş sürümden eskiyse değişmez. Sunucu geride kaldıysa kalan sürüm notlarını bir sonraki çağrı listeler. --sdk-version verilmezse değişmez.
Ön koşulone.platform-state.json ve içinde pending olmalıdır (önce npx one upgrade). Kurulu SDK'nın kural kataloğu okunamadıysa pending.catalogRevision boştur; --complete yine çalışır.
Eski SDK--complete'i tanımayan bir SDK komutu tam bir yükseltme olarak çalıştırır ve işlenmiş durumu ilerletmez. Çıktıda "İşlenmiş durum:" satırı yoksa önce SDK'yı güncelleyin.
Geri gitmezİşlenmiş durum hiçbir değerle geri alınmaz.

Döngü yarıda kesilirse işlenmiş durum değişmez ve sonraki yükseltme aynı yerden başlar. one.platform-state.json dosyasını depoya ekleyin.

Platform kuralları: one doctor ve AGENTS.md bölümü

Platformun uygulamalardan beklediği kurallar bir kural kataloğunda tutulur. Her kuralın bir kimliği (ONE-RULE-008 gibi), düzeyi (zorunlu, uyarı, öneri), hangi SDK sürümünden itibaren geçerli olduğu ve bir düzeltme yönergesi vardır. Katalog bir revizyon numarası taşır; her SDK sürümü yayımlandığı anki kataloğun bir kopyasını getirir. Yayın hattı aynı kataloğu kullanır ve doğrulama çıktısında uygulanan revizyonu yazar.

KomutDavranış
npx one doctorUygulamayı kurulu SDK'nın katalog kopyasına karşı tarar. Denetim, yayın hattının kataloğa bağlı kapılarıyla aynı koddur: manifest doğrulaması, veri modeli, arayüz ve SDK beyanı. Hattın diğer kapıları (belge ve ajan rehberi kapıları, giriş dosyası denetimi, SDK asgari sürüm denetimi, sır taraması, bağımlılık zafiyet taraması, kod güvenlik taraması, migration sürekliliği) yalnız hatta çalışır; one doctor onları çalıştırmaz. Uymayan her kural kimliği, düzeyi, konumu ve düzeltme yönergesiyle yazılır (hattın çıktısıyla aynı bilgi, daha kısa biçimde); kuralın metni bir kez yazılır. Çıktı --json ile bile tek seferde ve eksiksiz yazılır, başka bir komuta aktarılabilir. Dosyaya yazmaz. Zorunlu bulgu ya da manifest sorunu varsa çıkış kodu 1, yoksa 0'dır. --json raporu (catalogRevision, sdkVersion, manifestProblems, findings, summary, passes) JSON olarak yazar.
npx one agents syncAGENTS.md'nin platform kuralları bölümünü kurulu SDK'nın katalog kopyasıyla yeniler. Bölüm iki işaret satırının arasındadır (<!-- devoplus-one:platform-rules:start --> ve <!-- devoplus-one:platform-rules:end -->); yalnız aralarındaki metin değişir. İşaret yoksa bölüm dosyanın sonuna eklenir. İşaretlerden biri eksikse, birden çok varsa ya da sırası tersse hiçbir şey yazılmaz ve komut düşer. İşaret yalnız tek başına bir satırda ve kod bloğunun dışında sayılır: belgede alıntılanan işaret bölüm sanılmaz. Kapanmamış bir kod bloğu varsa hiçbir şey yazılmaz. AGENTS.md yoksa komut düşer (dosyayı uydurmaz). Satır sonu dosyanınkiyle aynıdır. Değişiklik yoksa dosyaya dokunulmaz.

Bölümü elle düzenlemeyin; bir sonraki eşitlemede yeniden yazılır. Uygulamaya özgü notlarınızı bölümün dışına yazın. Yeni üretilen uygulamanın AGENTS.md'si bölümü baştan taşır.

one doctor uygulamanın "uyumlu" olduğunu söylemez: kurulu SDK'nın getirdiği katalog kopyası, yayın hattının kullandığı güncel katalogdan eski olabilir. Yayında esas olan hattın doğrulama çıktısıdır. Güncel kuralları görmek için SDK'yı npx one upgrade ile yükseltin.

SDK sürüm başlığı

SDK kendi sürümünü SDK_VERSION sabitinde taşır (yayımlanan pakette paketin tam sürümü, ör. 0.3.0-next.214) ve yalnız kendi ürettiği yanıtlara (problemResponse, kayıt işlemleri, dışa aktarma, bileşen dosyaları, iş ve ajan araçlarının yanıtları) ve Devoplus servislerine giden kendi isteklerine X-One-Sdk-Version başlığını yazar. Uygulamanın kendi kurduğu yanıta dokunulmaz; aynı başlığı taşımasını istediğiniz yanıtı sdkResponse(gövde, seçenekler) ile kurun. Platform bu başlıktan yalnız uyumluluk seçimleri yapabilir; güvenlik kuralları başlığa bağlanmaz.

Yerel Portal ve servis taklidi (one dev)

one dev üç yerel adres açar ve üçü de yalnız 127.0.0.1'de dinler (aynı ağdaki başka bir bilgisayar erişemez). Yerel Portal ve uygulamanın kapısı yalnız 127.0.0.1 ya da localhost adıyla gelen isteği kabul eder; çalışma ortamının iç adresi bu ad denetimini YAPMAZ (aşağıdaki tablo):

AdresNe
http://127.0.0.1:8787/ (--port)Yerel Portal: uygulamayı Portal gibi çerçevede açar ve köprünün Portal tarafını oynar (one:token, one:context, menü sekmeleri, bildirim, onay penceresi, indirme, dış bağlantı, karartma, kaydedilmemiş değişiklik). Sayfanın yolu uygulamanın yoludur (/bridge uygulamanın /bridge'ini açar). Kişi, oturum türü (kendi oturumu, Vekalet, Yerine Görüntüle), dil ve tema sayfadan seçilir. Devoplus API taklidi de buradadır (/api/v1/...); uygulamanın API_BASE'i yerelde bu adrestir.
http://127.0.0.1:8788/ (--port artı bir)Uygulamanın kapısı: platform kapısının kurallarını uygular. İstemcinin yazdığı X-One-* başlıklarını siler, kimlik bağlamını yerel belirteçten yazar (belirteç yoksa anonim), belirteçsiz yazmayı ONE-DSP-E-007, geçersiz belirteci ONE-DSP-E-003, /__one/ yolunu ONE-DSP-E-011, başkası adına oturumda yazmayı (actingSessions: "write" değilse) ONE-DSP-E-009, başka bir portalın kişisine verilmiş belirteci ONE-DSP-E-005 ile reddeder; yanıta platformun içerik güvenliği politikasını (satır içi betik kapalı), aygıt izinlerini ve güvenlik başlıklarını ekler. Platformda her kurulumun kendi adresi olduğu gibi yerelde de her portalın kendi uygulama adresi vardır: açılış kişisinin portalı bu porttadır, portals listesindeki diğer portallar boş bir portta dinler ve Portal kabuğu seçilen kişinin portalının adresini kendisi açar (belirteçsiz isteğin portalı adrestir; bir sekmede seçilen kişi başka sekmeyi etkilemez). Yerel belirteç 12 saat geçerlidir; süresi dolunca kapı ve taklit 401 döner, kabuk sayfasını yenilemek yeni belirteç verir.
http://127.0.0.1:8789/ (--port artı iki)Yerel çalışma ortamının iç adresi. Ad (Host) denetimi YOKTUR ve gelen X-One-* başlıkları doğrulanmadan uygulamaya ulaşır: bu adrese istek gönderebilen her sayfa, kimliği istediği gibi yazarak uygulamanın yerel verisini okuyup değiştirebilir. Yalnız iş işleyicisini komut satırından elle denemek içindir (Zamanlanmış görev ve kuyruk); tarayıcıda açmayın. one dev açılışta bunu konsola uyarı olarak yazar.

Kişiler ve örnek veri uygulamanın kökündeki one.dev.json dosyasındadır (yoksa iki örnek kişi: gelistirici ve yöneticisi mudur). Dosya sır taşımaz ve depoya eklenebilir; bozuk bir alan açık bir hatayla reddedilir.

AlanAnlamı
portals[{ "key": "yerel", "name": "Yerel Portal", "id": "<24 onaltılık, isteğe bağlı>" }]. Varsayılan tek portal yerel. Yerel veritabanı, dosya ve önbellek portallar arasında ORTAKTIR (platformda her kurulumun kendisinindir).
defaultPerson--as verilmezse oturumu açan kişi (varsayılan ilk kişi).
people.<anahtar>Kişi (anahtar küçük harf, rakam ve tire). name zorunlu; email, phone, portal, title (titleEn), department, position, workplace, userCode, costCenterCode (DEXA bağlam kodu, aşağıda), manager ve operationalManager (kişi anahtarı, aynı portalda), permissions (yoksa uygulamanın bütün izinleri; ad alanı dışındaki kod platformdaki gibi iletilmez), scopes (yoksa manifest'in bütün yetki alanları; süreli onay gerektirenleri çıkararak onay verilmemiş oturumu deneyin), admin (portal yöneticisi), globalAdmin, active (false = ayrılmış kişi), push (false = bildirim alamaz), sub ve orgUserId (24 onaltılık; verilmezse kişiden kararlı biçimde üretilir). İki kimlik platformdaki gibi AYRIDIR: sub hesap kimliği, orgUserId organizasyon şeması kimliğidir.
groups.<anahtar>Kullanıcı grubu: { "name": "Satış Onay", "members": ["ayse"], "portal": "yerel" }.
channels{ "sms": "open" | "closed" | "disabled", "push": "open" | "closed" }, Devoplus backend kodunun varsayılanlarıyla. SMS varsayılanı open: kanal açık ortamdaki gibi ileti yerelde posta kutusuna yazılır; disabled SMS kanalı ortamda kapalıymış gibi 500 ONE-APP-E-110 döner; closed kanal açık ama portalın ve sistemin SMS sağlayıcısı tanımlı değilmiş gibi 500 ONE-APP-E-021 döner. Mobil bildirim varsayılanı closed: backend kanal ortamda ya da portalda açık değilse kuyruğa almadan 500 ONE-APP-E-029 döner ve kanalın varsayılan ayarı kapalıdır; open iletiyi posta kutusuna yazar (açık kanalı denemek için).
dexa.queries.<anahtar>Sorgunun örnek yanıtı: parameters (sorgunun bildirdiği parametre adları; bildirilmeyen ad ONE-APP-E-026), columns (yoksa ilk satırın alanları), rows, cases ([{ "when": { "Yil": "2025" }, "rows": [] }], parametreye göre yanıt), unavailable (true = "şu anda getirilemiyor"), truncated, appContext (true = kurulumda "kodları uygulama belirler" onayı var; yoksa UserCode, CompanyCode, CostCenterCode gönderimi ONE-APP-E-026). Sorgu bir bağlam kodu bildiriyor ve uygulama onu göndermiyorsa değer oturumdaki kişiden gelir: UserCode kişinin userCode'u, CompanyCode portalın şirket kodu (organizasyon şemasındaki companyCode, büyük harfle portal anahtarı), CostCenterCode kişinin costCenterCode'u; değer yoksa ya da kişi pasifse 403 ONE-APP-E-027. Gönderilen kod backend kuralıyla denetlenir: kırpıldıktan sonra boş, 64 karakterden uzun ya da kontrol karakterli değer 400 ONE-APP-E-076. Kodun portalda tanımlı olup olmadığı (ONE-APP-E-077) yerelde denetlenmez.
dexa.commands.<anahtar>İşlemin örnek sonucu: outcome (succeeded, rejected, inDoubt, pending = 409 ERPCMD-APP-E-016 + Retry-After, notSent = 424 ERPCMD-APP-E-013) ve erp (headerId, documentNumber, exceptionMessage, lines, raw, shape, isSuccessful). Aynı Idempotency-Key aynı içerikle yeniden gönderimdir (replayed: true), farklı içerikle 409 ERPCMD-APP-E-015. ERP'ye hiçbir şey yazılmaz.

Taklit edilen Devoplus uçları ve kuralları (Devoplus backend sözleşmesiyle aynı; belirteç yoksa ya da tanınmıyorsa 401):

UçYerelde
GET Users/meOturumdaki kişi ve orgContext (org kimliği, kullanıcı kodu, departman, unvan).
GET OrganizationManagement/organization-schema, users[?hideInactiveDocuments=false], user-groups/{id}/users, users/{id}/user-groupsKişilerden ve gruplardan türetilir; yalnız oturumdaki kişinin portalı, şemada yalnız aktif kayıtlar. Yetki alanı orgschema.read yoksa 403 ONE-APP-E-022.
POST One/services/mail, sms, pushMobil bildirim varsayılan olarak kapalıdır (500 ONE-APP-E-029, channels satırı). Doğrulama (400 GEN-VAL-E-001: en az bir, en çok 50 alıcı; konu 200, e-posta metni 10.000, SMS 300, bildirim başlığı 100 ve metni 300 karakter; bildirim path'i Portal yol kuralı), yetki alanı (403 ONE-APP-E-017), Vekalet / Yerine Görüntüle oturumunda 403 IDN-APP-E-080, alıcı kuralları (invalid-subject, invalid-org-user, not-in-portal, inactive, no-address, no-phone, not-deliverable; aynı kişi bir kez). Gönderilmez: posta kutusuna yazılır.
POST One/services/auditDoğrulanır ve konsola yazılır ({ "recorded": true }).
POST One/services/dexa/queryÖrnek veriden; tanımsız anahtar 403 ONE-APP-E-024, LangCode ve onaysız bağlam kodu 400 ONE-APP-E-026, biçimce geçersiz bağlam kodu 400 ONE-APP-E-076, oturumdan çözülemeyen bağlam kodu 403 ONE-APP-E-027, yetki alanı dexa.read.
POST One/services/dexa/command, GET One/services/dexa/command/{anahtar}Örnek veriden; tanımsız anahtar 403 ONE-APP-E-030, Idempotency-Key eksik ya da bozuk 400, yetki alanı dexa.write, Vekalet / Yerine Görüntüle oturumunda 403 IDN-APP-E-080. Kayıtlar süreç kapanınca silinir.
POST One/services/aiYerelde yoktur: 503 ONE-APP-E-050.
Diğer uçlar404 ONE-DEV-E-001.

Hata gövdesi Devoplus backend'inin biçimindedir: type ve title HTTP durumundan gelir (ör. 400 için https://tools.ietf.org/html/rfc9110#section-15.5.1 ve Bad Request), ayrım code alanındadır; doğrulama hatasında errors dizisinin her satırı code taşır (kuralın kodu, yoksa doğrulayıcının adı: NotEmptyValidator, MaximumLengthValidator, PredicateValidator). traceId yerelde bu sürecin opak bir kimliğidir; biçimine güvenmeyin. Uygulamanın kapısının hataları (ONE-DSP-E-*) platform kapısının biçimindedir (type: "about:blank").

Posta kutusu: http://127.0.0.1:8787/__one-dev/posta-kutusu (beş saniyede bir yenilenir; JSON biçimi posta-kutusu.json). İletiler .one/mailbox/messages.jsonl dosyasındadır; e-postanın alıcıya giden gövdesi Devoplus'ın şablonuyla gösterilir. Komut satırından denemek için oturum belirteci GET http://127.0.0.1:8787/__one-dev/session?as=<kişi> yanıtının token alanındadır (&kind=delegation|impersonation&actor=<kişi> ile başkası adına oturum); uygulamaya Authorization: Bearer <token> ile gönderin. Yerel belirteç yalnız bu süreçte geçerlidir.

Yerelde taklit EDİLMEYENLER: gönderim hız sınırları, kullanıcının süreli onay ekranı, kurulum onayları, arka plan işinin ve ajan aracının Devoplus servis çağrıları (belirteçsiz servis çağrısı 401 alır), gönderilen DEXA bağlam kodunun portalda tanımlı olup olmadığı (ONE-APP-E-077), Devoplus yapay zekası, gerçek ERP verisi. Bunlar platformda (test ortamı) denenir. Idempotency-Key yerelde yalnız tireli UUID biçiminde ve sıfır olmayan değerle kabul edilir (SDK'nın newIdempotencyKey() ürettiği biçim); backend daha fazla yazımı kabul eder.

Erişim talebi: one login, one request, one requests

Uygulamanız bir kuruluma, DEXA sorgusuna, uygulama izni atamasına ya da yetki alanına ihtiyaç duyduğunda izni yöneticiden komut satırından isteyebilirsiniz. Talep izni kendiliğinden VERMEZ; yönetici Devoplus Console'da onaylar ve sonuç size e-postayla bildirilir.

KomutNe yapar
one login [--env test|uat|prod]Tarayıcıda kod onayıyla giriş (portal orada seçilir). İlk girişte komut bu bilgisayarı kimlik sağlayıcıya kendisi kaydeder (yalnız erişim talebi, yayın ve kullanım panosu yetkisiyle) ve kimliği ~/.config/devoplus-one/clients.json'da saklar; oturum ~/.config/devoplus-one/session.json'da, ikisi de yalnız sizin okuyabileceğiniz izinle durur. Sabit bir istemci kullanmak için --client-id / ONE_CLIENT_ID.
one request <tür> ...Talep gönderir; türler: registration, installation, dexa-query, dexa-command, app-permission, scope, new-query. one request yazınca türlerin alanlarını listeler. Kısa ad one.manifest.json'dan okunur.
one requests [kimlik]Taleplerinizi ve durumlarını listeler.
one context [--json]Oturumdaki portalın adını, kimliğini ve var olan kısa adını (portal anahtarı; yoksa yok der) gösterir. Kurulum talebinde portal anahtarı seçilmez; sunucu onu portalın adından belirler.
one dexa queries [--search <metin>] [--json]Oturumdaki portalda Devoplus One uygulamalarına açılmış DEXA sorgularını listeler: anahtar, yön (okuma ya da ERP'ye yazma), bugün kullanılıp kullanılamadığı ve nedeni, açıklama, giriş parametreleri (tip adı; platformun oturumdan doldurduğu bağlam kodları işaretli: okumada LangCode, UserCode, CompanyCode, CostCenterCode, yazmada LangCode, CompanyCode) ve dönen sütunlar. --search anahtar, açıklama, parametre ya da sütun adında arar. Liste izin vermez: sorgu dexa-query talebiyle istenir. SQL metni ve test değerleri gösterilmez.
one logoutOturumu kimlik sağlayıcıda iptal eder ve dosyayı siler.

Uygulama içi izinler kayıt talebinde istenir: one request registration --title "<ad>" --permission One.Hello.Manage="Yönetir" (tekrar edilebilir, en çok 16; İngilizce ad --permission-en <kod>="<ad>"). Kayıt talebini portalın şirket yöneticisi, yetki alanı talebini platform yöneticisi onaylar. Kayıt talebi dışındaki türleri yalnız uygulamanın sahibi gönderebilir. Vekalet ya da Yerine Görüntüle oturumuyla talep gönderilemez. Aynı istemci Devoplus One MCP sunucusunda da kullanılır (@devoplus/one-sdk/access-request). Kayıttan sonra izin eklemenin talep türü yoktur: izni uygulamanın ana portalının şirket yöneticisi Devoplus Console'dan ekler (ana portalı belirlenemeyen uygulamada platform yöneticisi).

Talep reddedildiğinde dönen hatalar:

DurumNe olur
Talebi gönderen uygulamanın sahibi değil (kayıt hariç)403 ONE-APP-E-039; talebi uygulamanın sahibi gönderir
Kısa ad zaten kayıtlı (kayıt talebi)409 ONE-APP-E-038; başka bir kısa ad seçin
Kısa ad pasife alınmış bir uygulamaya ait409 ONE-APP-E-107; bu kısa ad yeniden kaydedilemez, başka bir kısa ad seçin
Aynı talep zaten bekliyor409 ONE-APP-E-040; yeniden göndermeyin, one requests ile izleyin
Kişi başına saatte 20 talep aşıldı429 ONE-APP-E-041
İzin kodu uygulamanın ad alanında değil400 ONE-APP-E-047; kod One.<Uygulama>.<Eylem> biçiminde olmalı
Ek izin biçimsiz, ad alanı dışında, ana kodla aynı ya da yinelenen; 16'dan fazla400; listeyi düzeltip yeniden gönderin
app-permission kodu uygulamanın kaydında yok400; izni önce uygulama kaydına ekletin (ana portalın şirket yöneticisi, Devoplus Console)
Oturumda portal yok403 ONE-APP-E-037; yeniden giriş yapıp portal seçin
Vekalet ya da Yerine Görüntüle oturumuTalep gönderilmez; kendi oturumunuzla bağlanın

Yayın ve kullanım panosu: one publish, one pipeline, one promote, one rollback, one deployments, one status

Kod bu bilgisayardan yayımlanmaz: one publish kaynağı Devoplus API'si üzerinden uygulamanın deposuna (test dalı) yazar ve bir yayın isteği bırakır; Devoplus isteği kaydeder ve yayın hattını kendisi başlatır, hattın kapılarının hiçbiri atlanmaz. Komutlar one login oturumuyla çalışır; uygulamanın kısa adı one.manifest.json'dan okunur (--app ile değişir), --json ham yanıtı basar.

KomutNe yapar
one publish [--portal <anahtar>] [--message <açıklama>] [--no-upload]Uygulamanın kök dizininden projenin tam görüntüsünü yükler ve test ortamına yayınlar. Dizin bir git deposuysa dosyalar git'in kurallarıyla seçilir (izlenen ve yok sayılmayan dosyalar); değilse dizin gezilir ve kökteki .gitignore yalnız basit kalıplarla uygulanır (ad, ad/, /ad, başta ya da sonda tek *), desteklenmeyen kalıpta komut durur. Git dizini okuyamazsa (bozuk dizin, güvenilmeyen sahiplik), çalışma ağacı seyrekse (sparse checkout) ya da çözülmemiş birleştirme çakışması varsa komut durur ve yükleme yapılmaz: yok sayma kuralları uygulanmadan sır dosyaları görüntüye girebilir ya da dizinde olmayan izlenen dosyalar silinmiş sayılırdı. Bağımlılık ve yerel durum klasörleri, .gitlab-ci.yml ve sır dosyaları yüklenmez ve hangi dosyanın atlandığı yazılır. Tam görüntüde listede olmayan dosya depodan silinir. Sınırlar: 2.000 dosya, dosya başına 2 MB, toplam 10 MB, uygulama başına saatte 60 yükleme; yalnız test ortamında. --no-upload yüklemeden depodaki kodu yeniden yayınlar. Hat bitince uygulama test Portal'ında açılır; adresi (https://<kısa-ad>-<anahtar>.test-one.devoplus.dev) paylaşılabilir önizlemedir.
one pipeline [--portal <anahtar>] [--deployment <kimlik>] [--job <iş>]Yayın isteğinin hattını (işler, durumlar, süreler) ve bir işin günlüğünü gösterir. --deployment yoksa en son istek, --job yoksa son başarısız iş, o da yoksa en son iş. Günlük sunucuda süzülür (yalnız betik bölümü ve son kısmı, sırlar maskeli).
one promote [--portal <anahtar>]Sürümü bir sonraki ortama taşır: komut hedef ortamın oturumuyla çalışır (one login --env uat sonrası test'ten uat'a). Kod birleştirmesini ve v<sürüm> etiketini Devoplus yapar; yalnız kaynak ortamda başarıyla yayınlanmış kod taşınır. Canlıya terfi bu komutla yapılamaz (ONE-APP-E-099): uygulamanın sahibi Portal'daki "Devoplus One Uygulamalarım" ekranından, şirket yöneticisi Devoplus Console'dan başlatır.
one rollback [--portal <anahtar>] [--to <x.y.z>] --yesÖnceki sürüme (ya da verilen sürüme) döner (test ve uat; canlıda sahip Portal'daki "Devoplus One Uygulamalarım" ekranından, şirket yöneticisi Devoplus Console'dan). Kod v<x.y.z> etiketinden alınır; etiketi Devoplus her terfide yazar, hiç terfi etmemiş sürüme dönülemez. --to yoksa dalın ucundaki sürümden küçük en büyük etiket. Veritabanı da o sürümün yapısına dönmek zorundaysa dönülen noktadan sonra kaydedilen veriler kaybolur; komut --yes olmadan hiçbir istek göndermez.
one deployments [--portal <anahtar>]Yayın isteklerinizi ve durumlarını listeler.
one status [--portal <anahtar>] [--days 7]Kullanım ve hata özeti: istek ve hata sayısı, işlemci süresi, günlük dağılım, son hata kayıtları (logError), başarısız arka plan işleri. Kişisel veri ve ileti gövdesi döndürülmez.
  • Kodu yalnız uygulamanın sahibi yükleyebilir; yalnız sahip ya da o portalın şirket yöneticisi yayın isteyebilir ve panoyu görebilir; kararı sunucu verir.
  • --portal verilmezse istek portal anahtarı taşımaz ve kurulumu sunucu seçer. Kısa ad, portal anahtarı, sürüm ve gün sayısı istek gönderilmeden denetlenir; tanınmayan bir seçenek (--portl) açık hatadır.
  • Yanıt "Yayın hattı bu ortamda yapılandırılmamış" ya da "Kullanım ve hata panosu bu ortamda yapılandırılmamış" ise o ortamda bu özellik henüz kullanılamıyor demektir.
  • Aynı istemci Devoplus One MCP sunucusunun yayın ve kullanım panosu araçlarında da kullanılır (@devoplus/one-sdk/app-ops).

Bağımlılık taraması ve yama: one deps patch

Devoplus her gün uygulamanın bağımlılıklarını bilinen güvenlik açıklarına karşı tarar (test dalı; uat ve main varsa onlar da). Açık bulunursa ve düzeltilmiş sürüm varsa platform bir yama önerir (package.json ve pnpm-lock.yaml). Yama kendiliğinden uygulanmaz ve depoda birleştirme isteği açılmaz: kodun doğru kaynağı yerel kopyanızdır, depoya kendiliğinden yazılan bir yama bir sonraki one publish ile geri alınırdı. Yamayı yerelde uygular, testleri çalıştırır ve her zamanki gibi yüklersiniz.

KomutNe yapar
one deps patch [--ref test|uat|main] [--json]Son taramanın bulgularını (paket, önem, kayıt, düzeltildiği sürüm), sunucunun notlarını ve varsa önerilen yamayı gösterir. Hiçbir dosya yazılmaz. --ref verilmezse test. Yama yalnız test için önerilir; uat ve main yalnız taranır, oradaki açık test'te düzeltilip terfi edilince kapanır.
one deps patch --apply [--ref test|uat|main]Yamayı bu dizine uygular. Yalnız kök dizindeki package.json ve pnpm-lock.yaml değişir; commit atılmaz, hiçbir şey yüklenmez. Şu durumlarda hiçbir dosyaya dokunulmaz ve komut hata verir: yama başka bir dosyaya dokunuyor ya da dosya ekliyor, siliyor, adını değiştiriyor; git bulunamıyor ya da dizin bir git deposu değil; bu iki dosyada kaydedilmemiş değişiklik var; yama yerel koda uymuyor (git apply --check).
  • Uyguladıktan sonra bağımlılıkları kurun (pnpm install), pnpm test ve one build çalıştırın, sonra one publish ile yükleyin. Yükleme yayını başlatır ve hattın kapıları (bağımlılık kapısı dahil) yine çalışır.
  • Yama uymuyorsa ya da hiç yoksa bulgulardaki paketleri düzeltildiği sürüme elle yükseltin. Düzeltilmiş sürümü olmayan paket için yama önerilmez.
  • Sonuç yoksa (available: false) notlar nedeni söyler (ör. henüz tarama yapılmadı); bulgu ya da yama gösterilmez.
  • Yalnız uygulamanın sahibi ya da platform yöneticisi görebilir; başkası 403 alır. Tarama hattının adresi yalnız platform yöneticisine gösterilir.
  • Aynı istemci (getDependencyPatch, describeDependencyPatch) Devoplus One MCP sunucusunda da kullanılır.

Hızlı başlangıç

ts
import {
  BackendClient, OneFiles, problemResponse, readContext, requireScope, requireUser, sendMail,
  type OneApp, type OneDatabase, type OneFilesBinding,
} from '@devoplus/one-sdk'

interface Env { DB: OneDatabase; FILES: OneFilesBinding; API_BASE: string }

export default {
  async fetch(request, env): Promise<Response> {
    try {
      const ctx = readContext(request.headers)          // platform kapısının yazdığı X-One-* başlıkları
      const url = new URL(request.url)

      if (url.pathname === '/api/me') {
        requireUser(ctx)                                 // oturum yoksa 401
        const api = new BackendClient({ baseUrl: env.API_BASE, authorization: request.headers.get('authorization') })
        return Response.json(await api.get('Users/me'))
      }

      if (url.pathname === '/api/notify' && request.method === 'POST') {
        const user = requireScope(ctx, 'mail.execute')   // scope manifest'te ve belirteçte yoksa 403
        const api = new BackendClient({ baseUrl: env.API_BASE, authorization: request.headers.get('authorization') })
        return Response.json(await sendMail(api, { toSubjects: [user.sub], subject: 'Merhaba', text: 'Deneme' }))
      }

      if (url.pathname === '/api/files' && request.method === 'POST') {
        requireUser(ctx)
        const files = new OneFiles(env.FILES)
        const info = await files.put(`uploads/${crypto.randomUUID()}`, await request.arrayBuffer(), {
          contentType: request.headers.get('content-type') ?? 'application/octet-stream',
        })
        return Response.json(info, { status: 201 })
      }

      return new Response('Bulunamadı', { status: 404 })
    } catch (error) {
      return problemResponse(error)                      // OneError RFC 9457 yanıtına çevrilir; bilinmeyen hata 500
    }
  },
} satisfies OneApp<Env>

Kimlik bağlamı

ÜyeAçıklama
readContext(headers): OneContextX-One-App, X-One-Portal-Key, X-One-Portal, (X-One-Auth=user ya da kuyruk işinde job + X-One-Sub, X-One-Scope, X-One-Global-Admin, X-One-Permissions, X-One-Admin). Üç kurulum başlığından biri yoksa ONE-SDK-E-001 (500): istek dispatch üzerinden gelmemiştir.
OneContext{ app, portalKey, portalId, user: OneUser | null, actor?: OneActor | null, mode?, admin?, jobServices? }. portalId kiracıdır (24-hex); veri her zaman buna bağlıdır. mode: 'user' (kullanıcının isteği), 'job' (kuyruk / zamanlanmış iş), 'agent' (Devoplus ajanının araç çağrısı). admin = user.admin (oturumsuzda ve işte false). jobServices: işte manifest jobs.services beyanı, diğer yollarda boş.
OneActor, isActingSession(ctx)Vekalet / Yerine Görüntüle oturumu (X-One-Actor-Sub + X-One-Session-Kind): { sub, kind: 'delegation' | 'impersonation' }. user.sub adına çalışılan kişidir (veri onundur), actor.sub oturumu kullanan kişidir; "kim yaptı" kaydına bu yazılır. Kendi oturumunda null. ⛔ Bu oturumlar varsayılan salt-okunurdur: platform GET/HEAD dışındaki isteği uygulamaya ulaşmadan 403 ONE-DSP-E-009 ile reddeder; uygulama manifest'te "actingSessions": "write" beyan ederse yazma isteği geçer.
OneUser{ sub, scope: readonly string[], globalAdmin, permissions: readonly string[], admin }. sub devoplus-auth kimlik uzayıdır (UserAccount._id), Organization Schema User._id DEĞİLDİR. permissions yalnız bu uygulamanın ad alanındaki (One.<Uygulama>.*) izinlerdir; admin = şirket ya da küresel yönetici rolü.
requireUser(ctx): OneUseranonimse ONE-SDK-E-002 (401).
requireScope(ctx, scope): OneUserbelirteçte scope yoksa ONE-SDK-E-003 (403). Onaya bağlı scope'ta metin kullanıcıya izni nereden vereceğini de söyler.
requireServiceScope(ctx, scope)Devoplus servisinin yerel kapısı, üç yolu ayırır (bkz. "Devoplus servisleri arka plan işinde ve ajan aracında"): kullanıcıda requireScope; ajan aracında süreli onay gerektiren scope'lar hiç kullanılamaz; işte kullanıcı gerekmez, scope jobs.services'te beyanlı olmalı. İhlal ONE-SDK-E-003 (403); asıl karar sunucudadır.
CONSENT_SCOPES, awaitsUserConsent(ctx, scope): booleanKullanıcının ayrıca ve süreli onay vermesi gereken scope'lar: dexa.write, mail.execute, sms.execute ve push.execute. awaitsUserConsent scope onaya bağlıysa ve belirteçte yoksa true döner; ilgili düğmeyi kapatıp kullanıcıya "portaldaki İzinleri incele düğmesiyle izin verin" demek için. ⚠️ Manifest'te hiç beyan edilmemiş scope için de true döner (beyan uygulamanın bilgisidir).
hasPermission(ctx, code): booleanuygulama izni: kullanıcıda kod varsa ya da admin ise true (backend'in yönetici baypasıyla aynı); anonimde false. Kod manifest permission/permissions içinde beyan edilir, atamayı portal yöneticisi Yetki Yönetimi'nde kullanıcı gruplarına (org düğümlerine) yapar. ⚠️ Ad alanı süzgeci bir kolaylıktır, gizlilik sınırı değildir: Authorization belirteci kullanıcının bütün izinlerini taşır.
requirePermission(ctx, code): OneUseroturum yoksa 401, izin yoksa ONE-SDK-E-004 (403).
OneErrorstatus, code, message (kullanıcıya görünen Türkçe metin).

Uygulama kodu X-One-* başlıklarını asla kendisi yazmaz; dispatch istemciden gelenleri siler ve belirteçten yeniden kurar.

Kullanıcı onayı. ERP'ye veri yazma (dexa.write) ve kullanıcı adına e-posta/SMS/mobil bildirim (mail.execute, sms.execute, push.execute) manifest'te beyan edilmiş ve kullanıcının yetkisi olsa bile, kullanıcı bu uygulamaya o portalda ayrıca onay vermeden belirtece girmez. Onay bir kez verilir, 30 gün geçerlidir; portal uygulamanın üstünde bir uyarı ve "İzinleri incele" düğmesi gösterir, onay devoplus-auth ekranında verilir ve oradaki "Yetkili uygulamalarım" sayfasından kaldırılır. Uygulama onay beklerken de açılır ve onay gerektirmeyen işlevleri çalışır; bu yüzden bu işleri yapan rotalar requireScope ile korunmalı, arayüz awaitsUserConsent ile ilgili düğmeyi kapatıp yönlendirme yazabilir. Onay kaldırılınca sonraki her belirteç hassas scope'u taşımaz (devoplus-auth onayı güçlü tutarlı bir Durable Object'te tutar); daha önce verilmiş belirteç iptal edilmez, en çok ömrü kadar (10 dk) geçerli kalır.

Portal köprüsü ve görsel dil

Uygulama sayfası Portal'da iframe içinde açılır. Sayfa üç dosyayı kendi adresinden yükler; satır içi betik platformda kapalıdır (script-src 'self') ve one-ci validate satır içi ya da harici (https://...) betiği yayından önce durdurur:

html
<link rel="stylesheet" href="/one-ui.css">    <!-- görsel dil -->
...
<script src="/one-bridge.js"></script>          <!-- window.one -->
<script src="/shell.js"></script>               <!-- sayfanın kendi betiği -->
ts
import manifest from '../one.manifest.json' with { type: 'json' }
import { clientAssetResponse, navigationFromManifest, scriptResponse } from '@devoplus/one-sdk'

const NAVIGATION = navigationFromManifest(manifest)          // Portal'ın çizeceği sekmeler
// fetch içinde:
const asset = clientAssetResponse(url.pathname, request)      // /one-bridge.js ve /one-ui.css
if (asset) return asset
if (url.pathname === '/shell.js') return scriptResponse(renderShellScript(NAVIGATION))
Sunucu üyesiAçıklama
clientAssetResponse(pathname, request?): Response | null/one-bridge.js (ONE_BRIDGE_SCRIPT, text/javascript), /one-charts.js (CHARTS_SCRIPT_PATH; portaldaki grafiklerle aynı grafik kütüphanesi; tarayıcı saklar ama her açılışta yeniden doğrular, request verilirse değişmediğinde gövdesiz 304 döner, SDK güncellenince yeni kütüphane hemen iner), /one-components.js (ONE_COMPONENTS_SCRIPT, hazır bileşenler) ve /one-ui.css (ONE_UI_CSS, text/css); başka yolda null.
scriptResponse(body)Sayfa betiği için doğru içerik türüyle yanıt.
navigationFromManifest(manifest)Manifest navigation alanı: bozuk/yinelenen öğe atlanır, en çok NAVIGATION_MAX_ITEMS (12). Yol kuralı ROUTE_PATH_RE / isRoutePath (manifest şemasıyla aynı). Sekme izni (permission) korunur; biçimi bozuk izinli öğe atlanır.
visibleNavigation(items, access)Kişinin görebildiği sekmeler (access: { permissions, admin } ya da null = yalnız izinsiz sekmeler); dönen öğeler izin kodu taşımaz.

Tarayıcıdaki window.one (tip: OneBridge):

ÜyeİletiAçıklama
one.start({ navigation })one:ready + one:navigationSayfa hazır olunca bir kez; yükseklik bildirimini de başlatır (one:resize). Menü sözleşmeye uymuyorsa fırlatır. İzinli sekmeler (permission) erişim bildirilene kadar gönderilmez.
one.setNavigationAccess({ permissions, admin })one:navigationOturumdaki kişinin uygulama izinleri: görebildiği izinli sekmeler menüye eklenir (menü değiştiyse ileti yeniden gönderilir; Portal son iletiyi geçerli sayar). Yönetici her sekmeyi görür. Oturum bilgisini okuyunca çağırın (GET /api/session).
one.token, one.onToken(fn), one.fetch(yol, init)one:tokenBelirteç yalnız Portal'dan gelir; one.fetch Authorization: Bearer ... ekler.
one.context, one.onContext(fn), one.actorone:context{ theme, lang, autoHeight, actor: { kind } | null, design }. Köprü data-theme'i köke yazar, tasarım değerlerini (primary, primaryContrast, fontFamily, radius; biçimi tutmayan değer yazılmaz) --one-primary ve benzeri değişkenlere geçirir, autoHeight gelince sayfanın kendi dikey taşmasını gizler (yatay taşma kaydırılabilir kalır; yatay kaydırma çubuğu bildirilen yüksekliğe eklenir). one.actor yalnız gösterim içindir; yazma kapısı sunucudadır.
one.onNavigate(fn), one.pathone:navigatePortal sekmesine tıklandı; görünümü değiştirin. Adres geçmiş kaydı eklenmeden güncellenir; geri bildirim gönderilmez.
one.setRoute(yol)one:routeUygulama kendi içinde görünüm değiştirdi; Portal adresi güncellenir. Yol / ile başlar, sorgu/# yok.
one.setTitle(metin | null)one:titleAlt sayfa başlığı (en çok TITLE_MAX_LENGTH = 120, uzunu kesilir); null kaldırır.
one.toast({ severity, summary, detail? })one:toastsuccess, info, warn, error; Portal içindeyken Portal çizer. Düz metin. Portal dışında hazır bileşenler yüklüyse sayfanın köşesinde gösterilir, değilse konsola yazılır.
await one.confirm({ message, title?, severity?, acceptLabel?, rejectLabel? })one:confirm, yanıt one:confirm-resulttrue/false. severity: danger, warn, info. Portal origin'i yoksa false. ⚠️ Portal bu iletiyi tanımıyorsa söz çözülmez.
await one.remove(adres, { message, title?, acceptLabel?, rejectLabel?, headers? })one:confirm (önem danger), kabulde DELETEOnaylı silme: kabulde yanıtı (Response), vazgeçilince null döner ve istek GİTMEZ. one-ci onaysız DELETE isteği için yayında uyarı verir (yayın sürer).
await one.download({ name, mime, data })one:downloaddata ArrayBuffer (Uint8Array, Blob ve metin çevrilir); en çok DOWNLOAD_MAX_BYTES (25 MB). iframe'de indirme kapalıdır; Portal kaydeder.
one.openExternal(url)one:open-externalYalnız https://; Portal kullanıcıya sorup yeni sekmede açar.
one.setDirty(bool)one:dirtyKaydedilmemiş değişiklik; Portal ayrılmadan önce sorar. Yalnız değişince gönderilir.
one.viewport.current, one.viewport.onChange(fn)one:viewportiframe'in ekranda görünen kısmı { top, height } (iframe CSS pikseli). Kendi penceresini görünen alana yerleştirmek için kullanılır. Köprü aynı değeri --one-viewport-top / --one-viewport-height CSS değişkenlerine de yazar (<one-dialog> bunlarla yerleşir; betik konum yazmaz).
one.setModal(bool)one:modal { open }Uygulama içinde pencere açık mı? true Portal'a kendi ekranını, çerçevenin görünen kısmı DIŞINDA aynı renkle karartmasını söyler; false karartmayı kaldırır. Yalnız değişince gönderilir; one.start()'tan önce açılmış pencere one:ready'nin ardından yeniden bildirilir. Sayfa giderken (tam sayfa gezinme, yeniden yükleme) açık pencere Portal'a kapalı bildirilir; sayfa geri/ileri önbelleğinden dönerse ve pencere açık kalmışsa yeniden açık bildirilir. <one-dialog> bunu kendisi çağırır; elle yalnız kendi penceresini çizen uygulama kullanır.
(köprü kendisi)one:modal-escapePencere açıkken odak iframe'de olduğu için Escape Portal'a ulaşmaz. Uygulama tuşu işlemezse köprü onu Portal'a iletir ve Portal bunu vazgeçme sayar (one:modal-dismiss döner). İletilmediği durumlar: tuşa basıldığı anda sayfada kipli bir <dialog> vardı (tarayıcı Escape'i yalnız kipli <dialog>'da, showModal() ile açılmışsa, cancel olarak kendisi işler; <one-dialog> da öyle; pencereyi close olayında kapatmanız yeterlidir), Escape stopPropagation() ya da preventDefault() ile işlendi ya da olay bittiğinde pencere kapanmıştı. Escape'i zaman uyumsuz işliyorsanız preventDefault() çağırın; yoksa Portal da vazgeçer.
one.onModalDismiss(fn)one:modal-dismissKullanıcı Portal'ın karartılmış alanına tıkladı ya da Portal'da Escape'e bastı: Portal karartmayı beklemeden kendisi kaldırmıştır; açık pencereyi "vazgeçildi" diye kapatın. Uygulama Portal'ı kilitleyemez (karartma ayrıca uygulama/kimlik değişince, Portal gezintisinde, sayfa yeniden yüklenince kalkar; açılış hız sınırlıdır).

Bütün iletiler yalnız iframe src'sindeki parent origin'lerine gönderilir (joker yok); gelen ileti origin ve üst pencere doğrulanarak alınır. Portal dışında (yerel geliştirme) ileti gönderilmez: confirm tarayıcının kendi sorusuna, download tarayıcı indirmesine, openExternal yeni pencereye düşer; toast hazır bileşenler yüklüyse sayfada, değilse konsolda görünür.

Görsel dil (/one-ui.css): açık/koyu değişkenler (--one-bg, --one-fg, --one-muted, --one-surface, --one-border, --one-primary, --one-primary-contrast, --one-font-family, --one-radius, durum renkleri) ve sınıflar: one-btn (-primary, -danger, -text), one-field + one-label + one-input / one-select / one-textarea, one-table (+ one-table-wrap, one-num), one-card, one-message (-success, -info, -warn, -error), içerik sekmeleri one-tabs + one-tab (aria-selected), one-row, one-stack, one-muted, one-empty, one-loading, one-sr-only. ⚠️ one-tabs içerik içi gruplama içindir; uygulama menüsünü Portal çizer.

Ek sınıflar (renkler tema değişkenlerinden; hareket azaltma tercihine uyulur):

SınıfİşaretlemeNot
one-badge (+ -success, -info, -warn, -error, -primary)<span class="one-badge one-badge-success">Onaylandı</span>Durum rozeti. Durumu yalnız renkle anlatmayın; metin taşır.
one-switch<label class="one-check"><input type="checkbox" role="switch" class="one-switch"> Bildirimler</label>Açık/kapalı anahtarı; tarayıcının onay kutusudur (klavye ve ekran okuyucu hazır).
one-radio-group (+ -inline)<fieldset class="one-radio-group" role="radiogroup"><legend class="one-label">Öncelik</legend><label class="one-check"><input type="radio" name="p"> Düşük</label>...</fieldset>Ok tuşları tarayıcının kendi radyo davranışıdır.
one-progress<progress class="one-progress" max="100" value="60">%60</progress>value yoksa belirsiz (akan) kip. Etiketi <label for> ya da aria-label ile verin.
one-spinner (+ -lg), one-loading-inline<span class="one-loading-inline" role="status"><span class="one-spinner" aria-hidden="true"></span> Yükleniyor</span>Yalnız döner simge kullanılacaksa role="status" + aria-label verin.

Hazır arayüz bileşenleri

Liste, form, süzgeç, kişi seçimi, dosya yükleme ve grafik için hazır bileşenler /one-components.js'tedir; açılır liste, etiket girişi, pencere, ipucu, avatar, eylem menüsü, sekmeler ve akordeon da oradadır (aşağıda). Bileşenler bağımlılıksızdır, uygulamanın kendi adresinden yüklenir (satır içi betik gerekmez), /one-ui.css görsel dilini kullanır, Portal'ın tema ve dil değişimini (one:context) kendiliğinden izler ve bütün metinleri Türkçe + İngilizce taşır. Tarih gg.aa.yyyy, sayı ve para tr-TR biçimindedir. İstekler yalnız uygulamanın kendi adresine gider ve belirteci köprü ekler (one.fetch); başka adrese istek gönderilmez.

html
<link rel="stylesheet" href="/one-ui.css">
...
<one-filter for="talepler"></one-filter>
<one-table id="talepler" src="/api/requests" export="xlsx csv" selectable clickable caption="Talepler"></one-table>
...
<script src="/one-bridge.js"></script>
<script src="/one-components.js"></script>   <!-- köprüden SONRA -->
<script src="/shell.js"></script>
js
// /shell.js: kolon ve alan tanımları JavaScript özelliğidir (öznitelik değil).
var table = document.getElementById('talepler')
table.columns = [
  { key: 'title', label: { tr: 'Başlık', en: 'Title' } },
  { key: 'amount', label: { tr: 'Tutar', en: 'Amount' }, type: 'money' },
  { key: 'createdAt', label: { tr: 'Tarih', en: 'Date' }, type: 'date' },
  { key: 'status', label: { tr: 'Durum', en: 'Status' }, type: 'choice',
    options: [{ value: 'open', label: { tr: 'Açık', en: 'Open' } }, { value: 'closed', label: { tr: 'Kapalı', en: 'Closed' } }] },
]
document.querySelector('one-filter').fields = [
  { key: 'q', label: { tr: 'Ara', en: 'Search' }, type: 'text' },
  { key: 'status', label: { tr: 'Durum', en: 'Status' }, type: 'choice', options: table.columns[3].options },
  { key: 'createdAt', label: { tr: 'Oluşturma', en: 'Created' }, type: 'dateRange' },
]
table.addEventListener('rowclick', function (e) { openRequest(e.detail.row.id) })

Sunucu tarafında bir şey eklemeniz gerekmez: clientAssetResponse(pathname, request) /one-components.js'i ve grafik kütüphanesi /one-charts.js'i de döndürür. Sayfa sırası: köprü, /one-charts.js, /one-components.js, sayfa betiği. Kişi seçici için peopleSearchHandler ucunu sunun (aşağıda).

BileşenÖzellikler (JS)ÖzniteliklerOlaylar
<one-table>columns (OneTableColumn[]), rows (yerel veri), filters (ek sorgu parametreleri), emptyText, selection (salt okunur), reload(), applyFilter(detail), clearSelection()src (uzak liste), page-size (varsayılan 20, en çok 100), paging="page", selectable (single ya da çoklu), clickable, export="xlsx csv", export-name, caption, row-key (varsayılan id), no-search, langrowclick { row, index }; selectionchange { rows, keys }; load { items, nextCursor }
<one-form>fields (OneFormField[]), value (atamak "kaydedilmiş" hal), submitLabel, dirty, validate(), setErrors(errors, detail?), reset()action (aynı adres; JSON gönderir), method (POST/PUT/PATCH), no-cancel, acting-write, langsave { value } (iptal edilebilir); saved { value, response }; savefailed { status, detail }; cancel; dirtychange { dirty }
<one-filter>fields (OneFilterField[]), value, clear()for (tablo kimliği), langchange { values, params }
<one-person-picker>value (OnePerson / dizi / null), labelsrc (varsayılan /api/one/people), multiple, required, disabled, label, langchange { value }
<one-file-upload>value (OneUploadedFile / dizi / null), labelaction (varsayılan /api/files), accept, max-size (bayt, varsayılan 5 MB), multiple, required, disabled, name-header (varsayılan x-file-name), label, languploaded { file }; change { value }
<one-chart>data ({ labels, series: [{ name, values }] } ya da [{ label, value }]), labeltype (bar, line, area, pie, doughnut), stacked, horizontal, size (sm, lg), label, unit, langYok
<one-stat>label, value, caption, deltaLabellabel, value, unit, format (number, money, percent, text), currency, decimals, delta, delta-format (percent, number), delta-label, delta-polarity (neutral, up-good, down-good), tone (neutral, primary, success, warn, danger, info), caption, loading, clickable, langselect (clickable)

<one-form> pasife alma onayı: yöntem POST değilken active ya da is_active adlı boolean alan açıktan kapalıya çevrilince form kaydetmeden önce onay sorar (vazgeçilirse save olayı bile oluşmaz). Başka bir alan için confirmOff: { tr, en } yazın; confirmOff: false soruyu kapatır.

Alan türleri (OneFieldType, veri modeliyle aynı küme): text, longText, number, money, date, datetime, boolean, choice, person, file. Etiketler ve seçenek adları { tr, en } ya da düz metin olur; hepsi DÜZ METİNDİR (HTML yorumlanmaz). Kolonun format(value, row, lang) işlevi de düz metin döndürür.

Uzak liste sözleşmesi (<one-table src>): GET <src>?limit=&cursor=&sort=&order=asc|desc&q=&<alan>=... isteğine { items: [...], nextCursor: "..." | null } biçiminde yanıt verilir. Sonraki sayfa nextCursor'la istenir, önceki sayfaya bileşen kendi tuttuğu imleçle döner. paging="page" ile istek page= taşır ve yanıttaki total sayfa sayısını gösterir. Süzgeç değerleri <one-filter>'dan parametre olarak gelir: metin için q, seçim için alan adı, tarih aralığı için <alan>From / <alan>To (başka ad gerekiyorsa alanda fromParam / toParam). Dışa aktarma aynı adresi format=xlsx ya da format=csv ile, sayfalama parametresi OLMADAN çağırır ve gelen dosyayı köprünün one.download'ıyla indirir (ad Content-Disposition'dan, yoksa export-name).

Form: istemci doğrulaması (zorunlu, en az/en çok karakter, sayı aralığı, biçim, tarih, seçenek) Türkçe ve İngilizce iletilerle; hatalı alan aria-invalid alır ve odak ilk hataya gider. Sunucu 4xx yanıtındaki RFC 9457 errors alanı ({ "alan": "ileti" | ["ileti"] } ya da [{ "field" | "pointer": "#/alan", "detail": "..." }]) alanlara bağlanır; eşleşmeyen iletiler ve detail formun üstünde gösterilir. Kaydedilmemiş değişiklik köprüye bildirilir (one.setDirty; sayfada birden çok form varsa herhangi biri kirli olduğu sürece Portal sorar, sayfadan ayrılan form sayılmaz, yeniden eklenen kirli form yeniden sayılır); Vazgeç değişiklik varsa Portal'da onay sorar. Başkası adına açılmış oturumda (Vekalet / Yerine Görüntüle) Kaydet kapalıdır; manifest actingSessions: "write" beyan eden uygulama forma acting-write yazar.

Kişi araması: bileşen uygulamanın kendi ucuna gider; ucu SDK hazırlar:

ts
import { PEOPLE_SEARCH_PATH, peopleSearchHandler } from '@devoplus/one-sdk'
// fetch içinde (manifest scopes: "orgschema.read"):
if (pathname === PEOPLE_SEARCH_PATH && method === 'GET') return peopleSearchHandler(request, env, ctx)

GET /api/one/people?q=&lang= yanıtı { items: [{ id, name, title, department }] } biçimindedir: yalnız aktif kişiler, en az 2 en çok 100 karakterlik arama, en çok 20 sonuç, Türkçe harf duyarsız. Yanıt yalnız ad, unvan ve departman taşır; e-posta, telefon, kullanıcı kodu ve fotoğraf DÖNMEZ (veri en azaltma). ⚠️ id Organization Schema kimliğidir (sub DEĞİL); bildirim gönderirken toOrgUserIds alanına yazın. Şema her aramada okunur; options.loadSchema ile kendi önbelleğinizi (OneCache) bağlayabilirsiniz.

Dosya yükleme iskeletin dosya ucuyla uyumludur (POST /api/files, gövde dosyanın kendisi, ad x-file-name başlığında; başlık yalnız ASCII taşıdığı için Türkçe harfler karşılığına çevrilir). Tür ve boyut sınırı yüklemeden önce tarayıcıda denetlenir; sunucu yine kendi sınırını uygular. Kaldır düğmesi yalnız değeri değiştirir, dosyayı silmez.

Grafik sayfa /one-charts.js'i yüklediyse portaldaki grafiklerle aynı kütüphaneyle (Chart.js) tuvale çizilir, yüklemediyse SVG'dir (area çizgi, doughnut pasta olarak). Pasta ve halka iki çizimde de yalnız ilk seriyi gösterir (gizli tablo da); birden çok seri için bar ya da line kullanılır. İkisinde de role="img" + aria-label ve ekran okuyucu için gizli veri tablosu vardır; renkler --one-chart-1 ile --one-chart-6 arasındaki değişkenlerden (açık/koyu), yazı tipi ve ızgara --one-font-family, --one-muted, --one-border'dan okunur. Yeniden çizimde ve sayfadan ayrılınca eski grafik yok edilir.

Gösterge kartı (<one-stat>): sayı ve durum özetleri cümle değil karttır. Birden çok kart <div class="one-stat-grid"> içinde dizilir (dar ekranda alt alta). Değer tr-TR biçimindedir (money TRY varsayılanıyla); delta oklu SVG simge ve ekran okuyucu metniyle ("artış", "azalış") çizilir ve delta-polarity verilmedikçe nötr renktedir. loading yükleniyor göstergesi, değer yokken "-" çizilir. Metinler yalnız textContent ile yazılır.

Açılır liste, pencere ve gezinme bileşenleri

BileşenÖzellikler (JS)ÖzniteliklerOlaylar
<one-select>options ([{ value, label, description?, disabled? }]), value (tekli: değer ya da null; multiple: dizi), labellabel, multiple, required, disabled, placeholder, langchange { value }
<one-tag-input>value (string[]), suggestions (string[], isteğe bağlı), labellabel, max (adet, varsayılan 50), max-length (karakter, varsayılan 50), required, disabled, placeholder, langchange { value }
<one-dialog>label, body (gövde öğesi), open (salt okunur), show() (Promise<string> döner), close(değer?)label (başlık), size (sm, lg), accept-label / reject-label (onay penceresi), severity (danger, warn, info), alert (role="alertdialog"), open (eklenince açılır, kaldırılınca kapanır), keep-open (içteki form kaydedince açık kalır), langopen; cancel { reason } (iptal edilebilir; escape, close, backdrop, portal); close { returnValue }
<one-tooltip>text ({ tr, en } ya da metin)text, text-en, placement="bottom", langYok
<one-menu>items ([{ id, label, danger?, disabled? }], ayraç { separator: true }), labellabel, compact (yalnız ⋯ düğmesi; label erişilebilir ad olur), disabled, langselect { id }
<one-avatar>Yokname, src (kendi adresi, https:, data:image/, blob:), size (sm, lg), langYok
<one-tabs>selected (sekme anahtarı)label (sekme şeridinin erişilebilir adı), selected, langchange { tab }
<one-accordion>Yokmultiple, heading-level (2-6, varsayılan 3), langtoggle { index, open, key }

Açılır liste (<one-select>): APG "combobox + listbox" kalıbı. Yazınca Türkçe harf duyarsız süzer (ı/i, ş/s gibi), ok tuşları etkin seçeneği taşır (devre dışı seçenek atlanır), Enter seçer, Escape önce listeyi kapatır (olay dışarı, ör. pencereye, taşmaz), Alt+Aşağı listeyi açar. Çoklu seçimde liste açık kalır, seçilenler etiket olarak görünür ve boş alanda Geri son seçimi kaldırır. En çok 200 seçenek gösterilir (fazlası için "aramayı daraltın" yazar). Liste giriş alanının altında akış içindedir (Portal iframe'i içerik boyunda çizer; akış dışındaki liste kesilirdi). Değer seçeneğin value'sudur (sayı ve mantıksal değer korunur). Seçimi yalnız null (ya da undefined) atamak temizler. '' yalnız '' değerli bir seçenek varsa seçilir: önce options, sonra value atayın; seçenekler değişince artık seçenek olmayan '' seçimden düşer.

Etiket girişi (<one-tag-input>): Enter ya da virgül etiket ekler; yapıştırılan virgüllü/çok satırlı liste ayrı etiketlere bölünür; alandan ayrılınca yazılı kalan metin etikete dönüşür; aynı etiket (büyük/küçük harf ve Türkçe harf duyarsız) ikinci kez eklenmez. Uzunluk, adet ve yinelenme nedenini durum satırı söyler. suggestions verilirse kutu combobox olur ve öneriler listelenir.

Pencere (<one-dialog>): tarayıcının kipli penceresidir (<dialog>.showModal()): odak pencerede kalır, sayfanın geri kalanı etkisizdir. Alt öğeler gövdeye taşınır; data-one-dialog-footer taşıyan öğe alt bilgiye gider. data-one-close="değer" taşıyan her düğme pencereyi o değerle kapatır (<form method="dialog"> da çalışır; değersiz düğme vazgeçmedir). Pencerenin içindeki <one-form> kaydedilince pencere 'saved' ile, formda Vazgeç'e basılınca '' ile kapanır (keep-open özniteliği kapatmaz; kayıt sürerken yapılmış ve gönderilmemiş bir değişiklik varsa da kapanmaz); formda kaydedilmemiş değişiklik varken vazgeçme önce "değişiklikler kaybolacak" onayı sorar ve kabulde form son kaydedilen haline döner. İlk odak sırası: autofocus, onay penceresinde "vazgeç", gövdedeki ilk odaklanabilir öğe, kapat düğmesi; kapanınca odak açan öğeye döner. Escape, kapat düğmesi ve karartmaya tıklama iptal edilebilir cancel olayıdır (preventDefault pencereyi açık tutar). Kaydedilmemiş <one-form> varken cancel ancak kullanıcı değişiklikleri atmayı kabul ettikten sonra gelir; o olayda ikinci bir soru sormayın ve preventDefault çağırmayın. <form method="dialog"> gönderimini preventDefault ile durdurursanız pencere açık kalır.

  • Pencere iframe'in görünen dilimine yerleşir (uzun sayfada kullanıcının baktığı yerde açılır).
  • Açılınca köprü one.setModal(true) ile Portal'a bildirir; Portal kendi ekranını aynı renkle karartır ve iki karartma kenarda birleşir. Kullanıcı Portal'ın karartılmış alanına tıklarsa ya da Portal'da Escape'e basarsa Portal karartmayı kendisi kaldırır ve pencere cancel (reason: "portal") ile kapanır. cancel iptal edilirse pencere açık kalır ama Portal yeniden karartılmaz.
  • Onay penceresi (accept-label / reject-label): sonuç 'accept' ya da 'reject'. ⛔ Portal içindeyken pencere iframe'de ÇİZİLMEZ, Portal'ın onay penceresiyle (one.confirm) sorulur: başlık label, ileti gövdenin DÜZ METNİ (HTML ve biçim taşınmaz), düğmeler accept-label / reject-label, severity. Portal'ın onay penceresi uygulamadan kapatılamaz (close() etkisizdir). Portal dışında (yerel geliştirme) aynı pencere sayfada çizilir.
  • Zengin içerikli onay (biçimli metin, alan) gerekiyorsa onay öznitelikleri olmadan genel pencere kullanın ve düğmeleri data-one-close ile kendiniz koyun; o pencere Portal içinde de iframe'de çizilir.

Bildirim için ayrı bileşen yoktur: one.toast(...) Portal içindeyken bildirimi Portal'a verir (tek görünüm); Portal dışında hazır bileşenler yüklüyse sayfanın köşesinde gösterir (hata 8, diğerleri 5 saniye; kapatılabilir).

İpucu (<one-tooltip>): ilk alt öğe tetikleyicidir ve odaklanabilir olmalıdır (düğme, bağlantı). Fareyle üzerine gelince ve klavye odağıyla görünür, Escape ile kapanır, fare ipucunun üzerine geçebilir (WCAG 1.4.13). Tetikleyiciye aria-describedby bağlanır. İpucuna önemli bilgi koymayın; ipucu dokunmatik ekranda görünmeyebilir.

Eylem menüsü (<one-menu>): APG "menu button". Düğme aria-haspopup="menu"; açınca ilk öğe odaklanır (Yukarı ok ile son öğe), oklar/Home/End gezinir, harf yazınca o harfle başlayan öğeye atlar, Enter/Boşluk seçer, Escape kapatıp odağı düğmeye döndürür. Devre dışı öğe odaklanır ama seçilmez. Menü aşağıda yer yoksa yukarı açılır.

Avatar (<one-avatar>): baş harfler Türkçe büyük harfle (şule yıldız için ŞY), renk addan kararlı biçimde seçilir; erişilebilir ad kişinin adıdır. Görsel yüklenemezse baş harflere döner.

Sekmeler (<one-tabs>): alt öğeler paneldir: data-label (+ data-label-en), isteğe bağlı data-tab (anahtar) ve data-disabled. APG "tabs": oklar devre dışı sekmeyi atlayarak gezer ve seçer, Home/End uçlara gider. Uygulama menüsü DEĞİLDİR (menüyü Portal çizer); içerik içi gruplama içindir. Seçimin tek kaynağı selected özniteliğidir: selected özelliği başlangıçta da gösterilen sekmeyi döndürür, kullanıcının seçimi özniteliğe yazılır, uygulama özniteliği sonradan da değiştirebilir. change yalnız seçim gerçekten değişince yayılır. Panel sonradan eklenir ya da data-label, data-tab, data-disabled değişirse şerit yenilenir.

Akordeon (<one-accordion>): alt öğeler bölümdür: data-label (+ data-label-en), başlangıçta açık olan için data-open. Başlık düğmesi aria-expanded taşır, oklar başlıklar arasında gezer; multiple yoksa aynı anda tek bölüm açıktır. data-open iki yönlüdür: kullanıcı bölüm açınca öznitelik eklenir, kapatınca kalkar; uygulama özniteliği ekleyip kaldırarak bölümü açıp kapatabilir.

Sayfadan ayrılıp yeniden eklenen bileşen: ayrıyken değişen öznitelikler yeniden eklenince uygulanır (ör. src değişen tablo bir kez yeniden yüklenir, open alan pencere açılır, selected alan sekmeler o sekmeyi gösterir); ayrıyken değişip eski değerine dönen öznitelik yok sayılır (tablo bulunduğu sayfada kalır). Ayrılan açık pencere '' ile kapanır ve Portal karartması kalkar; yeniden eklenince kapalı görünür, show() ya da open ile yeniden açılır (Portal'ın onay penceresi hariç: o yanıt gelince sonuçlanır). Açık liste, menü ve ipucu kapanır.

Açılır öğeler ve yükseklik: menü ve ipucu kapsayıcılarına göre mutlak konumludur; açıkken alt kenarları otomatik yükseklik ölçütüne katılır (data-one-popup), sayfanın altındaki menü iframe'in alt kenarında kesilmez.

SabitDeğer
COMPONENTS_SCRIPT_PATH/one-components.js (ONE_COMPONENTS_SCRIPT)
ONE_COMPONENT_TAGSon beş etiket adı
ONE_COMPONENT_EVENTSetiket başına yayılan olay adları (OneComponentEvents ile aynı küme)
PEOPLE_SEARCH_PATH, PEOPLE_SEARCH_MIN_QUERY, PEOPLE_SEARCH_MAX_QUERY, PEOPLE_SEARCH_MAX_RESULTS/api/one/people, 2, 100, 20
FILE_UPLOAD_DEFAULT_ACTION, FILE_UPLOAD_DEFAULT_MAX_BYTES/api/files, 5 MB
TABLE_DEFAULT_PAGE_SIZE, TABLE_MAX_PAGE_SIZE20, 100

Tipler: OneTableColumn, OneTableElement, OneListResponse, OneFormField, OneFormElement, OneFormErrors, OneFilterField, OneFilterElement, OneFilterChange, OnePerson, OnePersonPickerElement, OneUploadedFile, OneFileUploadElement, OneChartData, OneChartSeries, OneChartElement, OneChoiceOption, OneLocalizedText, OneFieldType, OneSelectElement, OneSelectOption, OneSelectValue, OneTagInputElement, OneDialogElement, OneDialogCancelReason, OneTooltipElement, OneMenuElement, OneMenuItem, OneTabsElement. Olay yükleri bileşen başına OneComponentEvents[etiket][olay] (ör. OneComponentEvents['one-dialog']['cancel'] = { reason }) ya da OneComponentEvent<etiket, olay> (CustomEvent) ile yazılır; eski birleşik OneComponentEventMap yükü daraltamaz ve kullanımdan kalkacaktır. Sunucu: peopleSearchHandler, searchPeople (saf), foldPersonText, PeopleSearchItem, PeopleSearchEnv, PeopleSearchOptions.

Backend istemcisi

new BackendClient({ baseUrl, authorization }): baseUrl ortamın API tabanıdır (uygulama vars'ından gelir ve outbound allowlist'inde olmak zorundadır); authorization tarayıcıdan gelen Authorization: Bearer <app_token> başlığıdır ve olduğu gibi iletilir.

MetotDavranış
request<T>(path, init?)api/v1/<path>; accept: application/json; gövde varsa content-type: application/json; init.headers olduğu gibi eklenir (outbound yalnız X-One-* başlıklarını siler); 204 yanıtında undefined döner; !ok olursa OneError(status, problem.code ?? HTTP-<status>, problem.detail ?? title) fırlatır. Yanıtta Retry-After (saniye) ya da gövdede retryAfterSeconds varsa OneError.retryAfterSeconds olarak taşınır.
get<T>(path), post<T>(path, body)kısayollar.

Backend "kullanıcı yetkisi ∩ scope" kuralını kendisi uygular; SDK ek bir yetki vermez.

Dosya servisi

Manifest'te services.files: true ise platform kurulum başına bir dosya deposu açar ve env.FILES'a bağlar. İzolasyon depo sınırındadır: anahtara portalId eklenmez, başka kurulumun deposu görünmez.

ÜyeAçıklama
new OneFiles(env.FILES)
put(key, body, { contentType?, maxBytes?, metadata? })FileInfo; gövde ArrayBuffer | ArrayBufferView | string | Blob (akış KABUL EDİLMEZ); tavan FILE_MAX_BYTES (25 MiB), aşımda ONE-SDK-E-014 (413). maxBytes sayı değilse, NaN ya da negatifse depoya yazılmadan TypeError atılır.
get(key)OneStoredFileBody | null; dosyayı uygulamanın kendisi sunar (writeHttpMetadata(headers) + httpEtag).
head(key), delete(key), list({ prefix?, limit?, cursor? })limit 1-1000.
normalizeFileKey(raw)1-512 karakter ve UTF-8 ile en çok 1.024 bayt (depo sınırı, FILE_KEY_MAX_BYTES), baştaki / atılır; boş / . / .. parçası ve kontrol karakteri reddedilir (ONE-SDK-E-010...012).
sanitizeFileName(raw, maxLength = 120)kullanıcı dosya adını tek parçaya indirger (ONE-SDK-E-013).
readBoundedBody(request, maxBytes)HTTP isteğinin gövdesini sınırlı okur: Content-Length varsa tek bayt okunmadan sınanır, yoksa (parçalı aktarım) akış parça parça okunur ve sınır aşıldığı an kesilir (ONE-SDK-E-014, 413); bozuk Content-Length için hata ONE-SDK-E-016 (400) olur. maxBytes sayı değilse, NaN ya da negatifse gövdeden tek bayt okunmadan TypeError atılır (Infinity tavana kırpılır). request.arrayBuffer() gövdenin tamamını belleğe aldıktan SONRA sınar, bu da uygulamanın belleğini taşırabilir; yükleme ucunda bunu kullanın.

Önbellek servisi

Manifest'te services.cache: true ise platform kurulum başına bir önbellek açar ve env.CACHE'e bağlar. İzolasyon önbellek sınırındadır. Bu bir önbellektir: nihai tutarlıdır (yazma başka bölgelerde 60 sn'ye kadar gecikebilir). Sayaç, kilit ya da kalıcı kayıt için kullanılmaz; kalıcı veri veritabanındadır.

ÜyeAçıklama
new OneCache(env.CACHE)
get<T>(key)T | null; değer yazıldığı JSON şeklinde döner (bozuk kayıt fırlatmaz, null).
put(key, value, { ttlSeconds })TTL zorunlu, tam sayı, CACHE_MIN_TTL_SECONDS (60) ile CACHE_MAX_TTL_SECONDS (30 gün) arasında; sınır dışı değer ONE-SDK-E-022, sessizce kırpılmaz. Değer JSON.stringify ile yazılır; undefined, üst düzey null (okumada "anahtar yok" ile ayırt edilemezdi) ve çevrilemeyen değer ONE-SDK-E-023; CACHE_VALUE_MAX_BYTES (1 MiB) üstü ONE-SDK-E-024 (413).
delete(key)
normalizeCacheKey(raw)kırpılır; boş / 512 bayttan uzun / tam olarak . ya da .. (önbelleğin ayırdığı anahtarlar; ONE-SDK-E-020) ya da kontrol karakterli (ONE-SDK-E-021) anahtar reddedilir.

İletişim servisi (e-posta / SMS / mobil bildirim)

sendMail(api, { toSubjects, subject, text }) POST One/services/mail ucunu, sendSms(api, { toSubjects, text }) POST One/services/sms ucunu, sendPush(api, { toSubjects, title, text }) POST One/services/push ucunu çağırır (aşağıda). Alıcılar devoplus-auth sub değerleridir; adres/telefon sunucuda ve yalnız aynı portalın aktif üyeleri için çözülür; serbest adres/numara kabul edilmez. Scope mail.execute / sms.execute hem kimlik sağlayıcı kaydında hem platform kayıt defterinde tanımlı olmalıdır. Yanıt NotifyResult { queued, skipped[{ sub, reason }] }; reason değerleri: invalid-subject, not-in-portal, inactive, no-address, no-phone, preference, rate-limited, invalid-org-user, not-deliverable (yalnız push). Kurulum başına saatlik tavan vardır (varsayılan e-posta 50, SMS 20, push 100). Backend 429 yalnız hiçbir ileti kuyruğa girmez ve atlananların tamamı rate-limited ise döner; başka gerekçeyle hiç kuyruğa girmeyen istek 200 + queued: 0 alır. Sonucu skipped[].reason ile denetleyin (backend OneServiceHandlers, okundu 15.10.2026).

Org kimliğiyle alıcı: toOrgUserIds alanı org şemasındaki kullanıcı kimliğini (legacy User._id, 24-hex) kabul eder. Yönetici zinciri ve grup üyeleri bu uzayda geldiği için onlara sub bulmadan bildirim gönderilebilir. toSubjects ile birlikte verilebilir; toplam alıcı en çok 50'dir, aynı kişi iki kez sayılmaz. Biçimi bozuk org kimliği invalid-org-user, portalın aktif kullanıcısı olmayan kimlik not-in-portal ile atlanır. ⛔ İki kimlik uzayını karıştırmayın: ctx.user.sub org kimliği DEĞİLDİR (ikisi de 24-hex; karışırsa hata vermez, kişi not-in-portal ile sessizce atlanır).

Mobil bildirim

sendPush(api, { toSubjects, toOrgUserIds?, title, text, path? }), POST One/services/push ucunu çağırır; scope push.execute. Devoplus Portal mobil uygulamasına başlık + metin gider (başlık 1-100, metin 1-300 karakter; bu sınırı sunucu uygular, SDK sendPush istemci denetimi yapmaz, hazır senaryolar toPush ile kırpar). İsteğe bağlı path bildirime dokununca açılacak uygulama içi konumdur (/siparis/42, istenirse ?sekme=ozet); yoksa uygulamanın ana sayfası. Kural Portal'ınkiyle aynıdır: / ile başlar, en çok 512 karakter, yalnız harf, rakam ve - . _ ~ /; //, ./.. bölümü ve # yok. Sorgu ad=değer çiftleridir ve yalnız harf, rakam ile - . _ ~ = & + % içerir; diğer karakterler (Türkçe harfler, /, boşluk, ! ' ( ) *) yüzde kodlanır. Yolu pushPath('/siparis/42', { sekme: 'özet' }) ile kurun: encodeURIComponent ! ' ( ) * karakterlerini kodlamaz ve sunucu onları reddeder. Portal'a ayrılmış sorgu anahtarları (parent, lang, theme, delegate, isMobileApp), boş, değersiz, uzun ya da yinelenen anahtar ve 20'den fazla anahtar 400 alır. Bildirimin hangi uygulamaya ve portala ait olduğunu sunucu yazar. Alıcı kuralları e-posta/SMS ile aynıdır; yeni atlama gerekçesi not-deliverable = kişinin bildirim tercihi kapalı ya da mobil kimliği çözülemedi (sunucu ikisini ayırt edemez). Kanal ortamda ya da portalda kapalıysa uç kuyruğa almadan OneError ONE-APP-E-029 ile reddeder (kapalı kanal sessiz başarı gibi görünmesin); SMS kanalı ortamda kapalıysa sendSms ONE-APP-E-110 alır. Kodunuz bu hatalarda çalışmayı sürdürmeli; notify kanalları bağımsız gönderir.

Hazır bildirim senaryoları

approvalRequest, approvalResult, reminder, systemNotice, operationResult, verificationCode aynı girdiden e-posta konusu + gövdesi ve SMS metni üretir (ScenarioMessage { subject, text, smsText }; Türkçe resmi "siz" ya da lang: 'en'). Metinler backend sınırlarına göre kırpılır (konu ≤ 200, SMS ≤ 300; e-posta gövdesi 10.000'i aşarsa ONE-SDK-E-062). Bağlantı yalnız https olabilir ve kullanıcı adı/parola taşıyamaz (normalizeLink, ONE-SDK-E-061). notify(api, message, { toSubjects?, toOrgUserIds?, channels }) kanalları (mail, sms, push) bağımsız gönderir: SMS ya da push düşse de e-posta gider; sonuç kanal adıyla döner, düşen kanal { error } taşır. toPush(message, to) başlığı e-posta konusundan (≤ 100, PUSH_TITLE_MAX_LENGTH), metni SMS metninden (≤ 300, PUSH_TEXT_MAX_LENGTH) kurar; kırpma bir emojiyi ortasından bölmez.

ts
import { approvalChain, approvalRequest, getMyOrgContext, getOrgUsersWithManagers, notify } from '@devoplus/one-sdk'

const me = await getMyOrgContext(api)            // null: kişinin bu portalda org kaydı yok
if (me) {
  const users = await getOrgUsersWithManagers(api, { includeInactive: true })   // pasifler de gelsin ki atlanabilsin
  const [first] = approvalChain(users, me.orgUserId)   // işlevsel yönetici zinciri, pasifler atlanır
  if (first) {
    const message = approvalRequest({ appName: 'İzin Talepleri', item: '3 günlük yıllık izin', requester: me.fullName ?? '', link })
    await notify(api, message, { toOrgUserIds: [first], channels: ['mail'] })
  }
}

Doğrulama kodu

generateVerificationCode(digits = 6) (4-10 hane, yanlılıksız); generateSalt(); hashVerificationCode({ code, salt, purpose, sub }); verificationCodeMatches({ ..., hash }) (sabit süreli karşılaştırma). Özet amaca ve kişiye bağlıdır: başka bir işlem ya da başka bir kişi için üretilmiş kod kabul edilmez. Kodun kendisini değil salt + hash + son kullanma + deneme sayısını uygulamanın veritabanında saklayın (önbellek nihai tutarlıdır; deneme sayacı ve tek kullanım orada güvenilir değildir). Süreyi ve deneme tavanını (ör. 5) uygulama uygular; doğru koddan sonra kaydı silin.

Organizasyon şeması

Scope orgschema.read (salt okuma) + kullanıcının kendi Portal.Generic.Read izni. Veri aktif portalın aktif kayıtlarıdır.

İşlevUçNot
getOrgSchema(api)GET OrganizationManagement/organization-schemaşirket, iş yeri, departman, pozisyon, ünvan, çalışan türü, kullanıcı grubu, kullanıcı
getOrgUsersWithManagers(api, { includeInactive? })GET OrganizationManagement/users (includeInactive karşılığı ?hideInactiveDocuments=false)yönetici alanlarını (functionalManager / operationalManager) taşıyan TEK uç; boş metin = yönetici yok. Uç varsayılan olarak yalnız aktifleri döner; onay zinciri kurarken includeInactive: true verin, yoksa zincir pasif yöneticide kesilir. ⚠️ Yanıt iletişim bilgisi de taşır, saklamayın
getMyOrgContext(api)GET Users/meoturumdaki kişinin org kimliği (orgUserId); org kaydı yoksa null
getUserGroupMembers(api, groupId)GET OrganizationManagement/user-groups/{id}/usersüyelik SUNUCUDA çözülür (yedi boyutun VEYA'sı + iç içe gruplar); istemcide türetmeyin
getUserGroupsOfUser(api, orgUserId)GET OrganizationManagement/users/{id}/user-groups

Saf yardımcılar (ağ yok): managerOf(users, orgUserId, kind?); approvalChain(users, orgUserId, { kind?, maxDepth?, skipInactive? }) (döngüye ve kendine yöneticiye karşı güvenli; pasif yönetici varsayılan olarak atlanır ve zincir onun yöneticisinden sürer; derinlik varsayılan 5, en çok 20); approvalChainReport(...) (aynı zincir ve durma nedeni: top, depth, unknown ya da cycle; boş zinciri "onaycı yok" diye okumadan önce bakın); orgUsersMatching(schema, criteria) (verilen boyutların hepsine uyan aktif kullanıcılar; ölçütsüz çağrı ONE-SDK-E-051); localizedText(text, lang?). Şemada departman/pozisyon hiyerarşisi alanı yoktur; zincir yalnız kişi kayıtlarındaki yönetici alanlarından kurulur.

DEXA (ERP okuma) servisi

queryDexa(api, key, parameters?), POST One/services/dexa/query ucunu çağırır. Uygulama ERP verisini yalnız önceden tanımlı QueryStore anahtarlarıyla okur: serbest SQL, ERP'ye yazan anahtar ya da desteklenmeyen sorgu türü çalıştırılamaz. Hangi anahtarın çalışabileceğinin tek kaynağı platform kayıt defteridir: anahtarları Devoplus Console'da uygulamanın kaydına yönetici ekler; kayıtta olmayan anahtar 403 ONE-APP-E-024 alır. Manifest services.dexa.queryKeys yalnız beyandır (çalışma anında okunmaz) ve scopes içinde dexa.read ile birlikte yazılır; biri eksikse manifest doğrulaması düşer. Belirteçte dexa.read yoksa backend 403 ONE-APP-E-017 döner.

ts
import { BackendClient, queryDexa, requireScope } from '@devoplus/one-sdk'

requireScope(ctx, 'dexa.read')
const api = new BackendClient({ baseUrl: env.API_BASE, authorization: request.headers.get('authorization') })
// Anahtar ve parametre adları sorgu tanımından (QueryStore) gelir; buradakiler yalnız örnektir.
const result = await queryDexa(api, 'Ornek.Rapor', { StartDate: '2026-10-01' })

if (result.unavailable) {
  // Sorgu ÇALIŞAMADI (DEXA tüneli kurulmamış / ERP'ye ulaşılamıyor / sorgu düştü): boş liste "kayıt yok" DEĞİLDİR.
  return Response.json({ status: 'unavailable', message: 'Veriler şu anda getirilemiyor; lütfen daha sonra deneyin.' }, { status: 503 })
}
return Response.json({
  columns: result.columns,
  rows: result.rows,                           // boşsa gerçekten kayıt yoktur
  truncated: result.truncated,                 // true: sunucu sonucu 5.000 satırda kesti, süzgeci daraltın
})
AlanAnlam
columns: string[]Kolon adları (sıra korunur).
rows: Array<Record<string, unknown>>Satırlar (kolon adı: değer).
unavailable: booleantrue = sorgu çalışamadı; kullanıcıya "şu anda getirilemiyor" gösterin, "kayıt yok" değil. Sunucu 200 döner, SDK fırlatmaz. Yanıt gövdesi hiç nesne değilse (boş/bozuk) SDK da true sayar.
truncated: booleantrue = sunucu sonucu 5.000 satırda kesti; liste eksiktir.

Eksik dizi [], eksik bayrak false olarak okunur (normalizeDexaQueryResult). İstemci tarafı kurallar sunucuyla birebirdir ve istek gönderilmeden fırlatır (buildDexaQueryRequest; programlama hatası):

KuralSabitHata
Anahtar ^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$, harf duyarlıDEXA_KEY_REONE-SDK-E-040
Parametreler { ad: metin } nesnesi, en çok 32DEXA_MAX_PARAMETERSONE-SDK-E-041
Ad ^[A-Za-z_][A-Za-z0-9_]{0,63}$DEXA_PARAMETER_NAME_REONE-SDK-E-042
LangCode gönderilemez: platformun bağlam parametresidir, sunucu doldurur; denetim harf duyarsızdır (langcode da reddedilir)DEXA_RESERVED_PARAMETERSONE-SDK-E-043
UserCode, CompanyCode ve CostCenterCode gönderilebilir ama boş olamaz (boş dize ya da yalnız boşluk; ad harf duyarsız tanınır)DEXA_APP_CONTEXT_PARAMETERSONE-SDK-E-140
Değer metin, en çok 2.000 karakter (sayı/tarih String(...) ile)DEXA_PARAMETER_VALUE_MAX_LENGTHONE-SDK-E-044

Sunucu hataları OneError.code ile taşınır: 403 ONE-APP-E-011...017 (kapı; -017 = dexa.read yok); 403 ONE-APP-E-024 (anahtar kayıt defterinde yok); 400 ONE-APP-E-025 (anahtar çalıştırılamaz: katalogda yok / ERP'ye yazan anahtar / desteklenmeyen sorgu türü); 400 ONE-APP-E-026 (parametre izinli değil: sorgu tanımlamıyor, LangCode, ya da kurulumda onay yokken gönderilen bağlam kodu); 403 ONE-APP-E-027 (sorgu kişiye bağlı bir bağlam, UserCode ya da CompanyCode, istiyor ve oturumdaki kişiden çözülemedi; sorgu ÇALIŞTIRILMAZ, boş değerle "tüm kayıtlar"a dönmez; oturumdan çözülen bağlam değeri boş kaldığında da bu kod döner); 429 ONE-APP-E-018 (kurulum başına saatte 600 sorgu); 400 ONE-APP-E-076 (gönderilen bağlam kodu geçersiz: boş, 64 karakterden uzun ya da kontrol karakteri içeriyor); 403 ONE-APP-E-077 (gönderilen UserCode ya da CompanyCode kurulumun portalında tanımlı değil); 400 ONE-APP-E-078 (arka plan işinde sorgu bir bağlam kodu istiyor ve uygulama onu göndermedi).

Bağlam kodları: UserCode, CompanyCode, CostCenterCode

Sorgu bu kodlardan birini bildiriyorsa değer şu sırayla belirlenir:

DurumUygulama kodu gönderdiUygulama kodu göndermedi
Kurulumda yönetici "kodları uygulama belirler" onayını vermedi400 ONE-APP-E-026 ile reddedilirkişi / ajan kipinde oturumdaki kişiden çözülür; arka plan işinde reddedilir
Onay verildi, kişi (Portal) ya da ajan kipisunucu doğrular, gönderilen kullanılıroturumdaki kişiden çözülür
Onay verildi, arka plan işisunucu doğrular, gönderilen kullanılırçözülecek oturum olmadığı için reddedilir
  • Portal doğrulaması: UserCode kurulumun portalındaki aktif bir kişinin kodu, CompanyCode aynı portaldaki bir şirketin kodu olmalıdır; değilse sorgu ERP'ye gitmeden reddedilir (ONE-APP-E-077). CostCenterCode için portalda karşılaştırılacak bir kayıt yoktur: yalnız biçimi denetlenir (boş değil, en çok 64 karakter, kontrol karakteri yok). Uygulamanın gönderdiği kodla yapılan her çağrı denetim günlüğüne yazılır.
  • Boş değer ERP'ye gitmez: gönderilen ya da oturumdan çözülen değer boşsa sorgu çalışmaz. SDK boş dizeyi ve yalnız boşluğu istek göndermeden reddeder (ONE-SDK-E-140). Kodu bilmiyorsanız parametreyi hiç göndermeyin.
  • CostCenterCode artık açıktır: onu bildiren sorgular çalışır. Uygulama göndermezse değeri sunucu kişiden çözer; çözülemezse sorgu reddedilir.
  • LangCode platformundur: uygulama hiçbir kipte gönderemez (ONE-SDK-E-043).
ts
// Arka plan işinde (oturum yok) kişiye bağlı bir sorgu: kodu uygulama verir. Kurulumda onay yoksa sunucu reddeder.
const stock = await queryDexa(api, 'Ornek.MagazaStok', { CompanyCode: '01', StoreCode: storeCode })

DEXA işlemleri: ERP'ye veri yazma

runDexaCommand(api, key, parameters, { idempotencyKey }), POST One/services/dexa/command ucunu Idempotency-Key başlığıyla çağırır; getDexaCommandStatus(api, idempotencyKey) ise GET One/services/dexa/command/{anahtar} ucunu. Scope dexa.write. Okuma sorgularından (queryDexa) ayrıdır: bu uç ERP'ye VERİ YAZAR. Çalışması dört kapının kesişimidir:

  1. Belirteçte dexa.write (yoksa 403 ONE-APP-E-017).
  2. Anahtar uygulamanın kayıt defterindeki işlem izin listesinde (dexaCommandKeys, Devoplus Console; yoksa 403 ONE-APP-E-030).
  3. Anahtar koddaki ERP komut kataloğunda One uygulamalarına açık (yoksa 404 ERPCMD-APP-E-006; açmak backend kod değişikliğidir; ⚠️ bugün açık hiçbir işlem yoktur).
  4. Kullanıcının o işleme özgü izni (yoksa 403 ERPCMD-APP-E-0xx).

Manifest services.dexa.commandKeys yalnız beyandır ve scopes içinde dexa.write ile birlikte yazılır.

⛔ Idempotency (kod yazmadan önce okuyun):

DurumNe yapılır
Yeni kullanıcı eylemi ("Kaydet"e basış)newIdempotencyKey() ile anahtar üretin ve isteği göndermeden ÖNCE veritabanına yazın
Ağ hatası / zaman aşımıAYNI anahtarla tekrar deneyin ya da getDexaCommandStatus ile okuyun; ERP'ye ikinci kez yazılmaz (replayed: true)
409 ERPCMD-APP-E-016 (aynı işlem sürüyor)error.retryAfterSeconds kadar bekleyin, durumu okuyun ya da AYNI anahtarla deneyin. Hatayı problemResponse(error) ile tarayıcıya döndürürseniz yanıt bu süreyi Retry-After başlığında ve gövdedeki retryAfterSeconds alanında taşır (yalnız pozitif tam sayıysa)
424 ERPCMD-APP-E-013 (ERP bağlantısı kurulamadı)hiçbir şey gönderilmedi; AYNI anahtarla tekrar denenebilir
409 ERPCMD-APP-E-015 (anahtar farklı içerikle kullanılmış)programlama hatası: anahtar başka bir eylemle karışmış
200 outcome: 'succeeded'ERP kaydı oluştu (erp.headerId / erp.documentNumber)
200 outcome: 'rejected'ERP reddetti (erp.exceptionMessage); aynı içerikle tekrar yine reddedilir
200 outcome: 'inDoubt'ERP'ye gidip gitmediği BİLİNMİYOR: YENİ anahtarla ASLA yeniden göndermeyin (çift kayıt); durumu okuyun, kullanıcıya "sonuç doğrulanamadı" deyin
ts
import { BackendClient, getDexaCommandStatus, newIdempotencyKey, requireScope, runDexaCommand } from '@devoplus/one-sdk'

requireScope(ctx, 'dexa.write')
const api = new BackendClient({ baseUrl: env.API_BASE, authorization: request.headers.get('authorization') })
// Anahtar eylem başına BİR KEZ üretilir ve göndermeden önce saklanır (tablo ve sütun adları örnektir).
const idempotencyKey = newIdempotencyKey()
await env.DB.prepare('INSERT INTO erp_ops (id, sub, state) VALUES (?, ?, ?)').bind(idempotencyKey, user.sub, 'sent').run()
// İşlem anahtarı ve parametre adları QueryStore tanımından gelir; buradakiler yalnız örnektir.
const result = await runDexaCommand(api, 'Ornek.Islem', { Body: JSON.stringify(model) }, { idempotencyKey })
if (result.outcome === 'inDoubt') {
  // Yeni anahtarla TEKRAR GÖNDERMEYİN. Durum daha sonra getDexaCommandStatus(api, idempotencyKey) ile okunur.
}

DexaCommandResult alanları backend ErpCommandResponse ile birebirdir: queryStoreKey, idempotencyKey (sunucunun küçük harf biçimi), replayed, outcome, correlationId, erp { shape, isSuccessful, headerId, documentNumber, exceptionMessage, lines, raw }. SDK sözleşmeye uymayan her 200 yanıtını inDoubt sayar: nesne olmayan gövde, bilinmeyen outcome, backend kaydında nullable olmayan bir alanın eksik olması ya da herhangi bir alanın yanlış tipte olması, gönderilenden farklı idempotencyKey ya da farklı queryStoreKey (yalnız ASCII büyük/küçük harf farkı aynı anahtardır: backend katalogdaki yazımı döner). O durumda gövdeden hiçbir alan sonuca taşınmaz (anahtarlar gönderilenlerdir, correlationId: null, erp.shape: 'unrecognized'); doğru sonuç AYNI anahtarla durum sorgusundan okunur. getDexaCommandStatus backend ErpCommandStatusResponse'u döner (outcome ayrıca pending ya da notSent olabilir; attemptCount, createdAtUtc, completedAtUtc, erp, steps, kind, warnings) ya da kayıt yoksa (404 ERPCMD-APP-E-017) null döner; o anahtarla hiçbir şey kaydedilmemiştir ve AYNI anahtarla gönderilebilir. Durum yanıtı da aynı kuralla doğrulanır (komut anahtarı istekte olmadığı için queryStoreKey yalnız tipiyle); ayrıca succeeded/rejected erp görünümü olmadan kabul edilmez.

İstemci tarafı kurallar istek gönderilmeden fırlatır (buildDexaCommandRequest / normalizeIdempotencyKey):

KuralSabitHata
Anahtar ^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$, harf duyarlıDEXA_KEY_REONE-SDK-E-045
Parametreler { ad: metin } nesnesi, en çok 32DEXA_MAX_PARAMETERSONE-SDK-E-046
Ad ^[A-Za-z_][A-Za-z0-9_]{0,63}$DEXA_PARAMETER_NAME_REONE-SDK-E-047
Değer metin (null olamaz), en çok 65.536 karakter (Body model JSON'u taşır)DEXA_COMMAND_PARAMETER_VALUE_MAX_LENGTHONE-SDK-E-048
Idempotency-Key kanonik UUID (8-4-4-4-12), boş UUID değil; SDK küçük harfe çevirirIDEMPOTENCY_KEY_REONE-SDK-E-049

Sorgulardaki ayrılmış bağlam parametresi yasağı (LangCode vb.) işlemlerde yoktur, çünkü sunucu böyle bir kural uygulamıyor. 4xx yanıtların hepsi ERP'ye hiçbir şey gönderilmediği anlamına gelir; ayrıca 429 ONE-APP-E-018 (kurulum başına saatte 300 işlem).

Zamanlanmış görev ve kuyruk

Uygulama işi manifest jobs ile beyan eder ve işleyicisini yazar; işi platform tetikler. Ayrıntı ve örnek: belgelerdeki /docs/jobs sayfası; manifest kuralları /docs/manifest sayfasındaki jobs bölümünde.

ÜyeAçıklama
defineJobs({ schedules?, queues? }): OneJobsDefinitionİşleyicileri ada göre tanımlar; ad manifest beyanıyla aynı olmalı (JOB_NAME_RE; geçersiz ad hemen ONE-SDK-E-070). Son tanım handleJobRequest'in varsayılanıdır.
OneJobHandler(job: OneJob, env, ctx: OneContext, execution) => unknown; ctx platform bağlamıdır (readContext).
OneJob{ id, name, kind: 'schedule' | 'queue', attempt, body, scheduledTime?, enqueuedAt? }. id her denemede AYNIDIR (tekrar eleme anahtarı); attempt 1'den başlar; tarih alanları ISO 8601 (UTC).
handleJobRequest(request, env, execution, definition?): Promise<Response> | nullYol /__one/jobs/... değilse null (uygulama kendi yönlendirmesine geçer). Bu yolda yalnız platform işaretli (X-One-Auth: job) POST işlenir, başka her şey 404 ONE-SDK-E-075. İşleyici dönerse 200; 4xx OneError fırlatırsa o durum (kalıcı hata, yeniden denenmez); başka hata 500 (platform geri çekilmeyle yeniden dener).
enqueue(name, body, { delaySeconds? }): Promise<{ id }>Kuyruğa ileti bırakır. Ad manifest jobs.queues içinde beyanlı olmalı (yoksa platform 404 ONE-OUT-E-011); gövde JSON ≤ 64 KiB (JOB_BODY_MAX_BYTES), gecikme 0-86.400 sn (JOB_DELAY_MAX_SECONDS). Kimlik istekten damgalanır; Vekalet / Yerine Görüntüle oturumunda, uygulama yazmaya açık değilse 403 ONE-OUT-E-015.

⛔ Aynı iş birden çok kez gelebilir: kalıcı yazımı job.id ile tekrarsız yapın (birincil anahtar + INSERT OR IGNORE). Kuyruk işi iletiyi bırakanın sub'unu bilgi olarak taşır (ctx.user); uygulama izinleri ve yönetici bilgisi işe taşınmaz. Zamanlanmış iş kullanıcısızdır (ctx.user null). İkisinde de Devoplus servisleri uygulamanın kimliğiyle çağrılır (bkz. aşağıdaki bölüm). /__one/ yolu platforma ayrılmıştır ve internetten kapalıdır. Yerelde (one dev) zamanlama tetiklenmez ve enqueue 503 döner; uygulamanın yerel adresi (8788) /__one/ yolunu platform gibi reddeder. İşleyiciyi denemek için çalışma ortamının iç adresine (8789) X-One-Auth: job, X-One-App, X-One-Portal-Key ve X-One-Portal başlıklarıyla POST /__one/jobs/<schedule|queue>/<ad> gönderin; işin Devoplus servis çağrıları yerelde taklit edilmez. Bu iç adres ad denetimi yapmaz ve başlıkları doğrulamaz; yalnız komut satırından deneme içindir.

Başka uygulamalara veri açmak (dışa açım)

Uygulama, manifest exports ile beyan ettiği okuma uçlarını başka One uygulamalarına açabilir. Alan uygulama veriyi yalnız kendi imports beyanı ve portal yöneticisinin onayıyla, yalnız aynı portalda okur; çağrıyı platform kurar. ⚠️ Platformun onay ve servis tarafı (Devoplus Console onay ekranı, backend servis ucu) yazılıyor: o taraf yayına alınana kadar işleyici tanımlanabilir ve denenebilir, ama başka bir uygulama onu çağıramaz.

ts
import { defineExports, handleExportRequest, handleJobRequest } from '@devoplus/one-sdk'

const exportsDef = defineExports<Env>({
  'open-orders': {
    1: async (call, env, ctx) => {
      const rows = await call.db!.prepare('SELECT id, total FROM orders WHERE status = ? LIMIT 100').bind(String(call.params.status)).all()
      return { items: rows.results }
    },
  },
})

export default {
  fetch(request, env, execution) {
    return handleExportRequest(request, env, execution) ?? handleJobRequest(request, env, execution) ?? app(request, env, execution)
  },
}
ÜyeAçıklama
defineExports({ '<ad>': { <sürüm>: işleyici } })İşleyicileri ad ve sürüme göre tanımlar; ad ve sürüm manifest exports beyanıyla aynı olmalı (EXPORT_NAME_RE, sürüm 1-999999; biçimsiz değer hemen ONE-SDK-E-150). Aynı ad birden çok sürümle sunulabilir. Son tanım handleExportRequest'in varsayılanıdır.
OneExportHandler(call: OneExportCall, env, ctx: OneContext, execution) => nesne. Dönen değer JSON'a çevrilir; kökü nesne olmalıdır (yanıt şeması).
OneExportCall{ name, version, params, callerApp, db }. params alan uygulamanın gönderdiği JSON nesnesi; callerApp alan uygulamanın kısa adı (platform yazar); db salt-okunur veritabanı (yoksa null).
handleExportRequest(request, env, execution, definition?): Promise<Response> | nullYol /__one/exports/... değilse null. Bu yolda yalnız platform işaretli POST işlenir; biçimsiz yol, tanımsız ya da manifestte beyan edilmemiş ad/sürüm 404 ONE-SDK-E-151. Platformun yazdığı onaylı sözleşme özeti (X-One-Contract-Digest) bu yayının özetinden farklıysa işleyici çağrılmaz (503 ONE-SDK-E-159, yayın geçişi; yeniden denenir). Başarılı yanıt sözleşme özetini (X-One-Export-Digest) taşır.
readOnlyDatabase(db), isReadOnlySql(sql)Salt-okunur veritabanı sarmalayıcısı ve deyim denetimi (aşağıda).

Kurallar:

  • Yalnız okuma. İşleyici veritabanını yalnız salt-okunur görür: call.db ve işleyiciye verilen env.DB aynı sarmalayıcıdır. Tek bir SELECT, WITH ya da VALUES deyimi çalışır; yazan ya da şemayı değiştiren deyim çalıştırılmadan 403 ONE-SDK-E-155 alır. insert, update, delete, create, alter, drop, pragma gibi sözcükleri sütun adı olarak kullanıyorsanız adı çift tırnakla yazın ("update"). E-posta, SMS, mobil bildirim ve DEXA'ya yazma bu çağrıda kullanılamaz (403 ONE-SDK-E-158); kuyruğa iş bırakma ve başka bir uygulamanın verisini okuma da platformda reddedilir (403 ONE-OUT-E-003). Devoplus servislerinden yalnız okuma servisleri (DEXA okuma gibi) kullanılır; kişinin portal belirteci bu çağrıya gelmediği için organizasyon şeması uçları okunamaz.
  • İzin. Beyan permission taşıyorsa, çağıran kişide o izin yoksa işleyici hiç çağrılmaz (403 ONE-SDK-E-153). Portal yöneticisi her izne sahip sayılır (hasPermission ile aynı kural). Kayıt düzeyindeki ek kurallarınızı işleyicide ctx ile uygulayın.
  • Kimlik. ctx.user veriyi isteyen kişidir; Vekalet ya da Yerine Görüntüle oturumunda ctx.actor işlemi gerçekte yapan kişidir, denetim kaydına onu yazın. ctx.mode 'export'tır.
  • Boyut. İstek gövdesi en çok 16 KB (413 ONE-SDK-E-152), yanıt en çok 256 KB (500 ONE-SDK-E-157).
  • Sınır. Salt-okunurluk bir kaza önleyicidir: kodunuz ham veritabanı nesnesini başka bir yoldan saklayıp kullanırsa yazabilir. Platformun garanti ettiği şey, veriyi okuyan uygulamanın sizin verinize yazamamasıdır.
  • Sözleşmeyi değiştirmek (bu yayın denetimi platformun uygulamalar arası okuma güncellemesiyle gelir; o yayına alınana kadar yayın eskisi gibi hatta denetlenir). Yayın ve terfide aynı ad ve sürümün açıklaması, izni ya da şeması değiştirilemez: değişiklik ne kadar küçük olursa olsun yeni bir sürüm verin ve eskisini alan uygulamalar geçene kadar birlikte sunun (409 ONE-APP-E-163). Aynı portalda onaylı olarak okunan bir sürümü kaldıran yayın 409 ONE-APP-E-162 alır; geçersiz exports / imports beyanı 409 ONE-APP-E-164. Bu denetim geri almada yapılmaz: geri alma, sözleşmesi farklı ya da onaylı okunan bir sürümü kaldırmış bir sürüme de dönebilir; o zaman onay yeniden istenir ya da alan uygulama okuyamaz. Ayrıntı: belgelerdeki /docs/manifest sayfasının "Yayın kuralları" bölümü.

Başka bir uygulamanın verisini okumak (içe aktarım)

Başka bir uygulamanın açtığı veriyi okumak için önce manifestte beyan edin; şirket yöneticisi beyanı Devoplus'ın yönetim ekranında onaylar. Onaydan sonra:

ts
import { BackendClient, readAppExport } from '@devoplus/one-sdk'

// manifest: "imports": [{ "app": "siparis", "export": "open-orders", "version": 2, "reason": { "tr": "..." } }]
const api = new BackendClient({ baseUrl: env.API_BASE, authorization: request.headers.get('authorization') })
const { items } = await readAppExport<{ items: Array<{ id: string }> }>(api, env, 'siparis', 'open-orders', { branch: 'IST01' })
ÜyeAçıklama
readAppExport(api, env, app, exportName, params?)Onaylı bir dışa açımı okur ve veren uygulamanın JSON nesnesini döndürür. Sürüm sizin beyanınızdan gelir (platform yayında yazar); ayrıca vermezsiniz.
readImportVersion(env, app, exportName)Beyan ettiğiniz sürüm; beyan yoksa null.

Kurallar:

  • Beyan ve onay. Beyan edilmemiş bir çifti okumak istek gönderilmeden 400 ONE-SDK-E-161 alır. Beyanı ekleyip yeniden yayımlayın; şirket yöneticisi onaylayana kadar platform çağrıyı yapmaz. Beyanın gerekçesi değişirse onay yeniden istenir. Veren uygulama paylaştığı verinin sözleşmesini yalnız yeni bir sürümle değiştirebilir; o sürüme geçmek de yeni onay ister.
  • Sürüm geçişi. Yeni bir sürüme geçen yayınınız, o sürümün onayı gelene kadar okuyamaz: platform eski onayla yeni kodu çalıştırmaz.
  • Parametreler. params bir JSON nesnesidir, en çok 16 KB (400 ONE-SDK-E-162); şemaya uyduğunu platform denetler.
  • Yanıt. Veren uygulamanın onaylı şemaya uyan nesnesidir. Nesne olmayan yanıt 502 ONE-SDK-E-163 ile hata verir, boş sonuç diye gösterilmez.
  • Hatalar. Onay yok ya da geri çekilmiş, veren uygulama bu portalda kurulu değil ya da durdurulmuş, hız sınırı, geçici arıza ya da veren uygulamanın hatası OneError ile gelir; code platformun kodudur.
  • Sınır. Dışa açım işleyicisinin içinden başka bir uygulamanın verisi okunamaz (derinlik 1). Yerel geliştirmede (one dev) bu servis taklit edilmez; test ortamında deneyin.

Portal ana sayfasında veri widget'ı

Uygulama, manifest widgets ile beyan ettiği veri widget'larının (stat, list, chart, table) verisini üretir; Portal bu veriyi kendi bileşenleriyle çizer. Tarayıcı uygulamanızın adresine istek atmaz: Portal veriyi Devoplus'tan ister, Devoplus kişiyi, pano kaydını, izni ve ayarları doğrulayıp çağrıyı platform üzerinden yapar. ⚠️ Pano tasarımcısı ve Devoplus'ın widget çağrısı henüz yayında değildir: bugün işleyici tanımlanabilir ve denenebilir, Portal onu henüz çağırmaz. Sayfa widget'ı (page) bu yoldan çağrılmaz; o widget kendi sayfanızı açar.

ts
import { defineWidgets, handleExportRequest, handleWidgetRequest } from '@devoplus/one-sdk'

const widgetsDef = defineWidgets<Env>({
  'low-stock': {
    1: async (call, env) => {
      const limit = Number(call.params.limit ?? 10)
      const rows = await env.DB.prepare('SELECT sku, qty FROM stock WHERE qty < 5 ORDER BY qty LIMIT ?').bind(limit).all()
      return { columns: [{ key: 'sku', label: call.lang === 'en' ? 'Item' : 'Ürün' }, { key: 'qty', label: { tr: 'Adet', en: 'Qty' }, type: 'number' }], rows: rows.results }
    },
  },
})

export default {
  fetch(request, env, execution) {
    return handleWidgetRequest(request, env, execution) ?? handleExportRequest(request, env, execution) ?? app(request, env, execution)
  },
}
ÜyeAçıklama
defineWidgets({ '<ad>': { <sürüm>: işleyici } })İşleyicileri ad ve sürüme göre tanımlar; ad ve sürüm manifest widgets beyanıyla aynı olmalı (WIDGET_NAME_RE, sürüm 1-999999; biçimsiz değer hemen ONE-SDK-E-164). Aynı ad birden çok sürümle sunulabilir. Son tanım handleWidgetRequest'in varsayılanıdır.
OneWidgetHandler(call: OneWidgetCall, env, ctx: OneContext, execution) => nesne. Dönen değer JSON'a çevrilir; kökü nesne olmalıdır (türün yanıt şekli).
OneWidgetCall{ name, version, kind, params, lang, theme }. params pano tasarımcısında seçilen ayarlardır (beyan yoksa {}); lang 'tr' ya da 'en', theme 'light' ya da 'dark'.
handleWidgetRequest(request, env, execution, definition?): Promise<Response> | nullYol /__one/widgets/... değilse null. Bu yolda yalnız platform işaretli POST işlenir; biçimsiz yol, tanımsız ya da manifestte beyan edilmemiş veri widget'ı 404 ONE-SDK-E-165. Çağrıdaki widget sözleşme özeti (X-One-Widget-Digest) bu yayının özetinden farklıysa işleyici çağrılmaz (409 ONE-SDK-E-169, yayın geçişi; Portal widget'ı bir sonraki yenilemede yeniden ister). Başarılı yanıt aynı başlığı taşır.
readWidgetMeta(env, name, version)Platformun yayında yazdığı beyanı ({ digest, kind, permission }) okur; beyan yoksa null.

Yanıt şekli. İşleyicinin döndürdüğü nesne, türün Portal bileşeninin verisidir (one-stat, one-chart, one-table; liste için aşağıdaki şekil). Devoplus yanıtı Portal'a göndermeden önce denetler: bilinmeyen alan, yanlış tür ya da aşılan sınır yanıtı geçersiz kılar; widget yanıtın beklenen biçimde olmadığını yazar. Sorunun yolu ve açıklaması yalnız Devoplus kaydına yazılır, kullanıcıya gösterilmez. Metin alanları düz metin ya da { tr, en? } nesnesidir ve düz metin olarak çizilir.

TürŞekilSınırlar
stat{ value, label?, unit?, format?, currency?, decimals?, delta?, deltaFormat?, deltaLabel?, deltaPolarity?, tone?, caption? }. value zorunludur; sayı, düz metin ya da null olabilir (null "-" çizer). format, deltaFormat, deltaPolarity ve tone değerleri <one-stat> ile aynıdır.Metin en çok 200 karakter; decimals 0-4; currency üç büyük harf (TRY)
list{ items: [{ title, subtitle?, value?, tone? }] }; title boş olamaz, value düz metindir ({ tr, en } değil); tone: success, warn, danger, infoEn çok 50 öğe; title ve subtitle 200, value 50 karakter
chart{ type, data, stacked?, horizontal?, unit? }; type <one-chart> ile aynı; data { labels, series: [{ name, values }] } ya da [{ label, value }]En çok 6 seri ve 50 etiket; her serinin values sayısı etiket sayısına eşit
table{ columns: [{ key, label, type?, sortable?, align?, options?, currency? }], rows: [{ <key>: değer }] }. Sütun başlığı label'dır (title değil). Hücre düz metin, sayı, mantıksal ya da null'dır; satırda sütun olmayan anahtar bulunamaz. Sütun açıkça sortable: false değilse Portal'da başlığına tıklanarak sıralanır.1-12 sütun, en çok 100 satır, sütun başına en çok 100 seçenek; hücre metni 500 karakter; sütun anahtarı harf ya da alt çizgiyle başlar, en çok 64 karakter

Kurallar:

  • Ayarlar doğrulanmış gelir. params Devoplus'ta manifestteki ayar şemasına göre doğrulanır; SDK onu yeniden doğrulamaz. Özet karşılaştırması, işleyicinizin yalnız bu yayının şemasına göre doğrulanmış ayarı görmesini sağlar.
  • İzin. Beyan permission taşıyorsa, kişide o izin yoksa işleyici hiç çağrılmaz (403 ONE-SDK-E-167). Portal yöneticisi her izne sahip sayılır.
  • Kimlik. ctx.user widget'ı gören kişidir; ctx.mode 'widget'tır. Vekalet ya da Yerine Görüntüle oturumunda widget yalnız manifestte actingSessions: "write" beyan eden uygulamada çağrılır; ctx.actor işlemi gerçekte yapan kişidir.
  • Yalnız okuma servisleri. E-posta, SMS, mobil bildirim ve DEXA'ya yazma bu çağrıda kullanılamaz (403 ONE-SDK-E-158). Widget veriyi gösterir; kaydı değiştirmez.
  • Boyut. İstek gövdesi en çok 4 KB (413 ONE-SDK-E-166), yanıt en çok 64 KB (500 ONE-SDK-E-171).

Devoplus servisleri arka plan işinde ve ajan aracında

İşte ve ajan aracında tarayıcıdan gelen bir belirteç yoktur; servis istemcileri (BackendClient) yine de çağrı yapar: platform çıkış katmanı isteği doğru kimlikle Devoplus'a taşır (new BackendClient({ baseUrl, authorization: null }) ya da request.headers.get('authorization'), ikisi de olur). Uygulamanın yazdığı Authorization başlığı bu iki yolda Devoplus'a iletilmez.

YolKimin adınaKullanılabilen servisler
Kuyruk / zamanlanmış iş (ctx.mode === 'job')Uygulamanın kendi kimliği; kişi yokturYalnız manifest jobs.services ile beyan edilip kurulumda portal yöneticisinin onayladığı servisler: mail.execute, sms.execute, push.execute, dexa.read, ai.use (her biri manifest scopes'ta da olmalı). Onaysız servis 403 alır. ⛔ DEXA'ya yazma (dexa.write) bu yolla açılmaz. "Kendime gönder" yoktur: alıcıyı açıkça verin. Denetim kaydında aktör uygulamadır.
Dışa açım işleyicisi (ctx.mode === 'export')Veriyi isteyen kişi (başka bir uygulamanın kullanıcısı)Yalnız okuma servisleri. E-posta, SMS, mobil bildirim, DEXA'ya yazma, kuyruğa iş bırakma ve başka bir uygulamanın verisini okuma platformda reddedilir (403 ONE-OUT-E-003). ctx.callerApp: veriyi isteyen uygulama.
Ajan aracı (ctx.mode === 'agent')Aracı çağıran kişiKişinin bu uygulamada yapabildikleri; süreli onay gerektiren scope'lar (CONSENT_SCOPES: e-posta, SMS, mobil bildirim ve DEXA'ya yazma) bu yolda kullanılamaz (kişi onay ekranını göremez). ctx.admin: kişi kurulumun portalının şirket yöneticisi; ctx.actor: Vekalet / Yerine Görüntüle oturumunda işlemi yapan kişi.
jsonc
// one.manifest.json
"scopes": ["ai.use", "mail.execute"],
"jobs": { "schedules": [{ "name": "weekly-summary", "cron": "0 8 * * 1" }], "services": ["ai.use", "mail.execute"] }

Günlük

logEvent(ctx, level, message, data?) sistem/çalışma zamanı olayı; logError(ctx, message, error, data?) beklenmeyen hata (ad, ileti ve en çok 4.000 karakterlik yığın; OneError kodu ve durumu korunur); logAudit(ctx, action, target?, data?) kritik kullanıcı işlemi; aktör zorunludur (anonim bağlamda fırlatır). Hepsi tek satır JSON olarak yazılır ve platform günlüğünde saklanır: platform her kurulumda günlüğü açar, örnekleme manifest observability.headSamplingRate (varsayılan 1). Satırları platform ekibi kurulum (uygulama × portal) süzgeciyle görür; uygulamanın son hata kayıtları kısaltılmış olarak kullanım ve hata panosunda görünür (one status).

Koruma: data içinde adı parola, token, secret, authorization, cookie, API anahtarı, private key, credential ya da parola/şifre geçen alanların değeri "[gizli]" olur (her derinlikte, girdi değişmez; redact); JSON'u 8 KiB'ı aşan data yerine { truncated, bytes } yazılır (platform günlüğü çağrı başına en çok 256 KB tutar, aşan kısım düşer; safeData); ileti 1.000 karakterde kırpılır. Maskeleme bir emniyet ağıdır, izin değildir: kişisel veri ve sır yazmayın. problemResponse(error, ctx?) bilinmeyen hatayı bu kayda (ctx verilirse bağlamıyla) düşer; yanıtta yalnız sabit metin döner.

Denetim günlüğü (kalıcı)

recordAudit(api, ctx, action, target?, data?), logAudit izini yazar ve POST One/services/audit ile Devoplus denetim günlüğüne kaydeder (scope audit.write). Kayıt Devoplus Console Denetim Günlüğü'nde "Devoplus One uygulaması" türüyle, reason = {slug}:{action}, targetLabel = target, details = data olarak görünür; aktör ve portal istekle gönderilmez, backend doğrulanmış bağlamdan alır. Kurallar sunucuyla birebir: action ^[a-z0-9][a-z0-9._:-]*$ ≤ 64, target ≤ 200, data JSON nesnesi ≤ 4 KiB (buildAuditRequest; ihlal ONE-SDK-E-030...032 ile FIRLATIR, çünkü programlama hatasıdır). Uzak yazma best-effort: ağ/kapı hatası { recorded: false } + uyarı olayı döner, isteğiniz düşmez. ⛔ target/dataya kişisel veri ya da sır yazmayın (365 gün saklanır).

Yapay zeka servisi

completeAi(api, ctx, istek) (eşdeğeri ai.complete), POST One/services/ai ucunu çağırır (scope ai.use; kullanıcıdan ayrıca onay istenmez). Diğer One/services/* uçlarıyla aynı kapıdan geçer; kullanıcı, portal ve uygulama istekle GÖNDERİLMEZ.

ts
const { output } = await completeAi(api, ctx, { task: 'summarize', input: metin, instructions: 'üç madde', lang: 'tr' })
const { label } = await completeAi(api, ctx, { task: 'classify', input: talep, labels: ['acil', 'normal'] })
const { json } = await completeAi(api, ctx, { task: 'extract', input: fatura, schema: { type: 'object', properties: { tutar: { type: 'number' } } } })
AlanKural (istemcide de denetlenir; ihlal istek gönderilmeden FIRLATIR)
tasksummarize, classify, extract, generate, chat (ONE-SDK-E-080)
input1-24.000 karakter (ONE-SDK-E-081)
instructions, lang≤ 4.000 karakter; tr / en (ONE-SDK-E-082)
labelsyalnız classify; 2-50 tekil etiket, her biri ≤ 100 karakter (ONE-SDK-E-083)
schemayalnız extract; JSON nesnesi ≤ 8 KiB (ONE-SDK-E-084)
messagesyalnız chat; ≤ 20 ileti, role = user ya da assistant, content dolu metin (ONE-SDK-E-085)

Yanıt OneAiResult: output (metin), json? (alan çıkarma), label? (sınıflandırma), model, usage (inputTokens, outputTokens). Tanınmayan yanıt ONE-SDK-E-086 fırlatır. Model, kota ve portalda yapay zekanın açık olup olmadığı Devoplus'tadır: kapalı/yapılandırılmamış portalda 503, kota dolunca 429 (OneError). ⛔ Model sayı hesaplamaz ve kayıt yazmaz: tutar/toplam/tarih farkı kodda hesaplanıp input'a hazır verilir; çıktı bir öneridir, veritabanına yazmadan önce kod doğrular. input'a sır ya da işin gerektirmediği kişisel veri konmaz.

Devoplus yapay zekasına açılan araçlar

Devoplus yapay zekası, kullanıcı Devoplus'ta bir soru sorduğunda uygulamanın verisine ve işlemlerine yalnız manifest agent.tools[] ile beyan edilen araçlarla, kullanıcının kendi kimliğiyle ulaşır. Her araç üç yerde durur: manifest beyanı (ad, TR/EN başlık + açıklama, readOnly, permission?, input şeması) + işleyici + AGENT_GUIDE.md.

ts
import guide from '../AGENT_GUIDE.md' with { type: 'text' } // derlemede metin olarak gömülür
import manifest from '../one.manifest.json' with { type: 'json' }

const tools = defineAgentTools<Env>({
  'list-notes': { handler: async ({ input, ctx, env }) => ({ notes: await notlariGetir(env, ctx.user!.sub, input.limit) }) },
})

export default {
  async fetch(request, env) {
    const agent = (await handleAgentRequest(request, env, tools, manifest)) ?? agentManifestResponse(request, manifest, guide)
    if (agent) return agent
    // ... uygulamanın kendi yönlendirmesi
  },
} satisfies OneApp<Env>

handleAgentRequest yalnız POST /__one/agent/<ad> için yanıt üretir (başka yolda null). Kapılar sırasıyla: X-One-Invoked-By: devoplus-agent (yalnız platformun imzalı çağrısı yazar; tarayıcıdan gelen 404); araç manifest'te VE defineAgentTools'ta (beyansız 404 ONE-SDK-E-091, işleyicisiz 501 ONE-SDK-E-092); oturum; readOnly: false araç Vekalet / Yerine Görüntüle oturumunda 403 (ONE-SDK-E-095); permission varsa hasPermission (403 ONE-SDK-E-004); gövde JSON ve ≤ 256 KiB (ONE-SDK-E-093); girdi şemaya uygun (400 ONE-SDK-E-094 + errors: [{ path, message }]). İşleyici { input, ctx, env } alır, JSON'a çevrilebilir bir değer döndürür; fırlattığı OneError ProblemDetails olur. ctx.admin çağıran kişinin kurulumun portalının şirket yöneticisi olduğunu, ctx.actor Vekalet / Yerine Görüntüle oturumunu bildirir. İşleyici Devoplus servislerini kişi adına çağırabilir; süreli onay gerektiren scope'lar (e-posta, SMS, mobil bildirim ve DEXA'ya yazma) bu yolda kullanılamaz.

Girdi şeması JSON Schema'nın dar bir alt kümesidir: type (object, string, number, integer, boolean, array), properties, required, enum, minimum, maximum, minLength, maxLength, items, description (AGENT_INPUT_KEYWORDS). ⛔ Nesneler kapalıdır: şemada olmayan alan reddedilir. Doğrulayıcı validateAgentInput(şema, girdi) olarak da dışa açıktır.

agentManifestResponse(request, manifest, guide) oturum istemeden GET /.well-known/one-agent.json (yalnız app: { slug, name } + agent) ve GET /.well-known/one-agent.md (AGENT_GUIDE.md, ≤ 256 KiB) sunar; manifest'te agent yoksa 404. Rehbere kullanıcı verisi, sır ya da iç adres yazılmaz.

Veri modeli ve kayıt işlemleri

Kayıt tutan uygulamada tabloyu, tipleri ve uçları elle yazmak yerine one.data.json'da varlıkları ve alanları tanımlayın; gerisini one data generate ve createCrudHandler yapar.

json
{
  "version": 1,
  "entities": {
    "talepler": {
      "title": { "tr": "Talepler", "en": "Requests" },
      "fields": {
        "baslik": { "type": "text", "title": { "tr": "Başlık", "en": "Title" }, "required": true, "maxLength": 120 },
        "tutar":  { "type": "money", "title": { "tr": "Tutar", "en": "Amount" }, "min": 0 },
        "durum":  { "type": "choice", "title": { "tr": "Durum", "en": "Status" }, "required": true, "default": "acik", "index": true,
                    "choices": [ { "value": "acik", "title": { "tr": "Açık", "en": "Open" } },
                                 { "value": "kapali", "title": { "tr": "Kapalı", "en": "Closed" } } ] },
        "sorumlu": { "type": "person", "title": { "tr": "Sorumlu", "en": "Owner" } }
      }
    }
  }
}
TürSaklananAPI'deKural
text / longTextmetinmetinmaxLength (varsayılan 200 / 10.000; en çok 1.000 / 50.000); text tek satır
numberondalıklı sayısayımin / max
moneykuruş, tam sayı; kolon <ad>_minor"1234.50" (metin)en çok iki ondalık; yuvarlanmaz, reddedilir; binlik ayırıcı yok; min / max lira
dateYYYY-AA-GGaynıgerçek takvim günü
datetimeUTC ISO (...Z)aynıgönderilirken saat dilimi zorunlu (+03:00 ya da Z)
boolean0/1true/false
choicevalue kodu (kısıtsız; seçenek denetimi kayıt işlemlerinde)kodkod ^[a-z0-9][a-z0-9_-]{0,39}$; görünen ad title; yeni değer migration'sız eklenir
personkullanıcı kimliği (sub)aynı
filedosya anahtarı (OneFiles)aynı

Her alan: required, unique (silinmemiş kayıtlar arasında), index, default. Ad kuralı ^[a-z][a-z0-9_]{0,39}$ (çift alt çizgi, sondaki alt çizgi, _minor / _fold ekleri ve order, group, not gibi SQL anahtar sözcükleri reddedilir); otomatik kolonlar id (uuid), created_at, created_by, updated_at, updated_by, deleted_at alan adı olamaz. En çok 30 varlık, varlık başına 60 alan ve tabloda en çok 100 kolon (her metin alanının bir arama kolonu <alan>_fold vardır, aşağıya bakın). softDelete varsayılan true; false yalnız en az 10 karakterlik hardDeleteReason gerekçesiyle kabul edilir (karakterler harf harf sayılır, yalnız boşluk sayılmaz; gerekçe yoksa one data generate ve one-ci veri kapısı durur; model createCrudHandler'a koddan verildiyse kayıt uçları 500 ONE-SDK-E-100 döner).

one data generate şunları üretir: migrations/NNNN_one_data.sql (bir sonraki numara) + src/data/types.ts (<Varlık>Record, <Varlık>Input, dataModel) + migrations/.one-data-snapshot.json (bir sonraki farkın tabanı; elle düzenlenmez). --check hiçbir şey yazmaz, dosyalar güncel değilse 1 ile çıkar. Yalnız ekleyen değişiklik üretilir: yeni varlık, yeni alan (zorunluysa default şart), yeni dizin. Seçim alanına yeni değer eklemek migration gerektirmez (yalnız tipler ve anlık görüntü yenilenir); seçim değeri kaldırmak reddedilir (var olan kayıtlar o değeri taşıyabilir). Alan/varlık silme, ad değiştirme, tür değiştirme, zorunluluk değişikliği, tekillik değişikliği ve dizin kaldırma algılanırsa komut hiçbir dosya yazmadan nedenini söyleyip durur (görünen adlar serbesttir). Üretilmiş migration uygulandıktan sonra değiştirilmez. ⚠️ Bu kurallardan önceki bir SDK ile kurulmuş tablolarda seçim değerleri veritabanında sabitlenmiştir (anlık görüntüde choiceChecks); o alanlara değer eklemek yine reddedilir. Eski anlık görüntüyle ilk üretim metin alanlarına arama kolonlarını ekleyen yeni bir migration yazar.

ts
import { createCrudHandler, problemResponse, readContext } from '@devoplus/one-sdk'
import { dataModel } from './data/types.ts'

const talepler = createCrudHandler({ model: dataModel, entity: 'talepler', records: 'shared', canWrite: 'One.Talep.Write' })
// fetch içinde:  const res = await talepler.handle(request, env.DB); if (res) return res
YöntemYolİş
GET/api/<varlık>{ items, page, pageSize, total } döner; parametreler _page, _size (≤ 200), _sort=alan / -alan, alan=değer, alan.contains=metin (metin alanları), alan.from / alan.to (sayı, para, tarih; dahil); en çok 20 süzgeç
POST/api/<varlık>201 + kayıt
GET/api/<varlık>/<id>kayıt
PATCH/api/<varlık>/<id>gönderilen alanlar
DELETE/api/<varlık>/<id>204 (yumuşak silme)
GET/api/<varlık>/export.csv, export.xlsx, report.htmlaynı süzgeçlerle dışa aktarma / yazdırma sayfası (_lang=tr|en)

Hazır liste bileşeniyle doğrudan bağlanır (<one-table src="/api/<varlık>" paging="page" export="xlsx csv">): uçlar bileşenin sorgu lehçesini de kabul eder: limit (= _size), page (= _page), cursor (sayfa numarası; yanıttaki nextCursor, son sayfada null), sort + order=asc|desc (= _sort), q (metin alanlarının herhangi birinde içerir), <alan>From / <alan>To (= .from / .to; <one-filter> tarih aralığının varsayılan adları), lang (= _lang), format=xlsx|csv (listeyle aynı adreste dışa aktarma). Lehçe adı modeldeki bir alanın adıysa (ör. alan page) o parametre alan süzgecidir.

Seçenekler: records (zorunlu: shared ile herkes görür, own ile herkes yalnız kendi oluşturduğunu), canRead / canWrite (uygulama izni kodu ya da (ctx) => boolean), actingSessions (read varsayılan; write Vekalet / Yerine Görüntüle'de yazmayı açar; manifest'te de actingSessions: "write" gerekir), basePath, pageSize, exportLimit (varsayılan 10.000), timeZone. Okuma ve yazma oturum ister. Gövde yalnız application/json, ≤ 1 MB; sistem alanı gönderilemez (created_by oturumdan yazılır). Hata gövdesi RFC 9457 + errors: [{ field, message }] (Türkçe, kullanıcıya gösterilebilir). Değerler SQL'e her zaman bağlı parametre olarak gider; sıralama ve süzgeç adları yalnız modelden seçilir.

Arama Türkçe katlanmıştır: alan.contains ve q büyük/küçük harfe ve Türkçe harflere duyarsızdır ("istanbul" "İSTANBUL"u, "cay" "Çay"ı bulur; İ ı I i aynı sayılır). Her metin alanının yanında <alan>_fold arama kolonu durur; createCrudHandler onu yazar, API yanıtında görünmez. Kolonu boş olan kayıt (eski kayıt ya da elle yazılmış SQL) da bulunur. ⚠️ Bir metin alanını elle yazdığınız SQL ile güncelliyorsanız arama kolonunu da yazın ("<alan>_fold" = ? + foldSearchText(değer)) ya da NULL yapın; yoksa arama eski metne göre bulur.

Rapor ve dışa aktarma

toCsv(rows, columns, { lang, delimiter, timeZone }); toXlsx(rows, columns, { lang, sheetName, timeZone }); exportResponse(içerik, { fileName, format: 'csv' | 'xlsx' | 'html' }); renderPrintableReport({ title, subtitle, columns, rows, totals, lang }). Sütun: { key, title: { tr, en }, type?: 'text' | 'number' | 'money' | 'date' | 'datetime' | 'boolean', labels?, width? }.

  • CSV: UTF-8 + BOM (Excel Türkçe karakteri bozmasın), Türkçede ; ayırıcı ve 1.234,50 sayı (Türkçe bölge ayarlı Excel'in liste ayırıcısı ;'dir), İngilizcede ,. =, +, -, @ ile başlayan metin hücresine ' eklenir (formül enjeksiyonu).
  • XLSX: bağımlılıksız; sayı/para/tarih hücreleri sayı olarak yazılır ve biçim alır (Excel'de toplanabilir), metin hücresi formül olarak çalışmaz; ilk satır sabitlenir. En çok 50.000 satır, 100 sütun.
  • İndirme: arayüz dosyayı belirteçli istekle alır ve one.download({ name, mime, data }) ile Portal'a verir (en çok 25 MB). Dosya adı Content-Disposition'dadır (filename*, Türkçe karakterli; downloadFileName(header)).
  • PDF: ayrı motor yok. renderPrintableReport betiksiz bir yazdırma sayfası üretir; arayüz onu gizli bir çerçevede açıp print() çağırır, kullanıcı "PDF olarak kaydet"i seçer.

Hata kodları

KodDurumAnlam
ONE-SDK-E-001500Platform bağlamı eksik (dispatch dışından çağrı)
ONE-SDK-E-002401Oturum gerekli
ONE-SDK-E-003403Scope yok (onaya bağlı scope'ta kullanıcı henüz izin vermemiş olabilir)
ONE-SDK-E-004403Uygulama izni yok (requirePermission)
ONE-SDK-E-010...013400Dosya anahtarı / adı geçersiz
ONE-SDK-E-014413Dosya boyut tavanı (put ve readBoundedBody)
ONE-SDK-E-015500Dosya yazılamadı
ONE-SDK-E-016400Content-Length başlığı geçersiz
ONE-SDK-E-020...021400Önbellek anahtarı geçersiz
ONE-SDK-E-022400Önbellek TTL sınır dışı ya da eksik
ONE-SDK-E-023400Önbellek değeri JSON'a çevrilemiyor
ONE-SDK-E-024413Önbellek değeri 1 MiB tavanı
ONE-SDK-E-040400DEXA sorgu anahtarı geçersiz
ONE-SDK-E-041400DEXA parametre kümesi nesne değil ya da 32'den fazla
ONE-SDK-E-042400DEXA parametre adı geçersiz
ONE-SDK-E-043400DEXA LangCode gönderilemez; platformun bağlam parametresidir (harf duyarsız)
ONE-SDK-E-044400DEXA parametre değeri metin değil ya da 2.000 karakteri aşıyor
ONE-SDK-E-045400DEXA işlem anahtarı geçersiz
ONE-SDK-E-046400DEXA işlem parametre kümesi nesne değil ya da 32'den fazla
ONE-SDK-E-047400DEXA işlem parametre adı geçersiz
ONE-SDK-E-048400DEXA işlem parametre değeri metin değil ya da 65.536 karakteri aşıyor
ONE-SDK-E-049400Idempotency-Key kanonik UUID değil ya da boş UUID
ONE-SDK-E-050400Org kimliği 24 karakterlik onaltılık değil (istek gönderilmez)
ONE-SDK-E-051400orgUsersMatching ölçütsüz çağrıldı
ONE-SDK-E-060400Bildirim senaryosu alanı boş / tanınmayan sonuç / geçersiz doğrulama kodu ya da süresi
ONE-SDK-E-061400Bildirim bağlantısı https değil ya da kimlik bilgisi taşıyor
ONE-SDK-E-062400Bildirim metni backend sınırını aşıyor
ONE-SDK-E-063400Alıcı ya da kanal verilmedi
ONE-SDK-E-070400İş / kuyruk adı geçersiz
ONE-SDK-E-071400Kuyruk iletisi gövdesi JSON'a çevrilemiyor
ONE-SDK-E-072413Kuyruk iletisi gövdesi 64 KiB tavanı
ONE-SDK-E-073400delaySeconds 0-86.400 tam sayı değil
ONE-SDK-E-074503İleti kuyruğa bırakılamadı (ağ hatası ya da beklenmeyen yanıt)
ONE-SDK-E-075404İş bulunamadı (handleJobRequest: bilinmeyen tür/ad ya da platform dışı çağrı)
ONE-SDK-E-076400İş çağrısının gövdesi JSON değil
ONE-SDK-E-080...085400 / 413Yapay zeka isteği geçersiz (iş, metin, talimat/dil, etiketler, şema, sohbet)
ONE-SDK-E-086502Yapay zeka yanıtı tanınmadı
ONE-SDK-E-090500defineAgentTools: araç adı geçersiz ya da işleyici yok
ONE-SDK-E-091404Araç yok / bu yoldan çağrılamaz (beyansız araç, imzasız çağrı, yanlış yöntem)
ONE-SDK-E-092501Araç manifest'te beyanlı ama işleyicisi tanımlanmamış
ONE-SDK-E-093400 / 413Araç girdisi geçerli JSON değil ya da 256 KiB'ı aşıyor
ONE-SDK-E-094400Araç girdisi şemaya uymuyor (errors alan listesi)
ONE-SDK-E-095403Değiştiren araç Vekalet / Yerine Görüntüle oturumunda çağrılamaz
ONE-SDK-E-096500Ajan rehberi 256 KiB'ı aşıyor
ONE-SDK-E-100500Veri modeli ya da createCrudHandler yapılandırması geçersiz (one data generate ile düzeltin)
ONE-SDK-E-101400Kayıt doğrulanamadı (errors: [{ field, message }])
ONE-SDK-E-102404Kayıt bulunamadı (silinmiş ya da başkasının, records: 'own')
ONE-SDK-E-103409Tekil alan değeri başka bir kayıtta kullanılıyor
ONE-SDK-E-104400Liste sorgu parametresi geçersiz (bilinmeyen alan, uygunsuz süzgeç, sayfa boyu)
ONE-SDK-E-105405Bu adreste bu yöntem kullanılamaz
ONE-SDK-E-106403Vekalet / Yerine Görüntüle oturumunda kayıt değiştirilemez
ONE-SDK-E-107403canRead / canWrite izni yok
ONE-SDK-E-108413Kayıt gövdesi 1 MB'ı aşıyor
ONE-SDK-E-109415Kayıt gövdesi application/json değil
ONE-SDK-E-110400Dışa aktarılacak kayıt sayısı sınırı aşıyor (süzgeci daraltın)
ONE-SDK-E-120400Dışa aktarma sütunu / saat dilimi / toplam sütunu geçersiz
ONE-SDK-E-121400Dışa aktarma satır sınırı (50.000) ya da 25 MB indirme sınırı aşıldı
ONE-SDK-E-130400Kişi araması 2-100 karakter olmalı (peopleSearchHandler)
ONE-SDK-E-131405Kişi araması yalnız GET
ONE-SDK-E-140400DEXA bağlam kodu (UserCode, CompanyCode, CostCenterCode) boş ya da yalnız boşluk (istek gönderilmez)
ONE-SDK-E-150400Dışa açım adı ya da sürümü geçersiz, işleyici işlev değil (defineExports)
ONE-SDK-E-151404Dışa açım bulunamadı (handleExportRequest: biçimsiz yol, tanımsız ya da beyan edilmemiş ad/sürüm, platform dışı çağrı)
ONE-SDK-E-152413Dışa açım isteğinin gövdesi 16 KB sınırını aşıyor
ONE-SDK-E-153403Dışa açımın beyan ettiği izin çağıran kişide yok (işleyici çağrılmaz)
ONE-SDK-E-154400Dışa açım isteğinin gövdesi bir JSON nesnesi değil
ONE-SDK-E-155403Dışa açım işleyicisinde yazma ya da şema değiştiren deyim (salt-okunur veritabanı)
ONE-SDK-E-156500Dışa açım işleyicisi JSON nesnesi döndürmedi
ONE-SDK-E-157500Dışa açım yanıtı 256 KB sınırını aşıyor
ONE-SDK-E-158403Yazan servis (e-posta, SMS, bildirim, DEXA'ya yazma) dışa açım ya da widget işleyicisinde kullanılamaz
ONE-SDK-E-159503Platformun onayladığı sözleşme özeti bu yayınınkiyle aynı değil; yayın geçişi sürüyor (işleyici çağrılmaz, yeniden denenir)
ONE-SDK-E-160400readAppExport: uygulama kısa adı ya da dışa açım adı geçersiz (istek gönderilmez)
ONE-SDK-E-161400readAppExport: çift manifestte imports ile beyan edilmemiş (istek gönderilmez)
ONE-SDK-E-162400readAppExport: parametreler nesne değil, JSON'a çevrilemiyor ya da 16 KB'ı aşıyor (istek gönderilmez)
ONE-SDK-E-163502readAppExport: veren uygulamanın yanıtı JSON nesnesi değil
ONE-SDK-E-164400Widget adı ya da sürümü geçersiz, işleyici işlev değil (defineWidgets)
ONE-SDK-E-165404Widget bulunamadı (handleWidgetRequest: biçimsiz yol, tanımsız ya da beyan edilmemiş veri widget'ı, platform dışı çağrı)
ONE-SDK-E-166413Widget isteğinin gövdesi 4 KB sınırını aşıyor
ONE-SDK-E-167403Widget'ın beyan ettiği izin kişide yok (işleyici çağrılmaz)
ONE-SDK-E-168400Widget isteğinin gövdesi { lang, theme, params } biçiminde bir JSON nesnesi değil
ONE-SDK-E-169409Çağrıdaki widget sözleşme özeti bu yayınınkiyle aynı değil; yayın geçişi sürüyor (işleyici çağrılmaz)
ONE-SDK-E-170500Widget işleyicisi JSON nesnesi döndürmedi
ONE-SDK-E-171500Widget yanıtı 64 KB sınırını aşıyor
ONE-SDK-E-999500Beklenmeyen hata (problemResponse)
ONE-DEV-E-001404Yalnız yerelde (one dev): bu Devoplus ucu yerel taklitte yok
ONE-DEV-E-002404Yalnız yerelde: kullanıcı grubu örnek veride yok
ONE-DEV-E-003400Yalnız yerelde: taklit uca giden gövde geçerli bir JSON nesnesi değil ya da alan tipi yanlış
ONE-DEV-E-004404Yalnız yerelde: yol yerel çalışma ortamına ayrılmış
ONE-DEV-E-005404Yalnız yerelde: organizasyon kullanıcısı örnek veride yok

Backend'den dönen kodlar (ONE-APP-E-*, USRDIR-* vb.) OneError.code ile olduğu gibi taşınır.

Sürümleme

SemVer; 0.x boyunca küçük sürüm artışı kırıcı olabilir; CHANGELOG.md Kırıcı başlığına bakın. Paket iki kanaldan otomatik yayımlanır (Publish SDK (npm) iş akışı):

KanalNe zamanSürümnpm etiketi
Ön sürümPlatform deposunun test dalına birleşmede<package.json sürümü>-next.<n> (ör. 0.3.0-next.512)next
Kararlımain dalına birleşmedepackage.json sürümülatest

Sürüm npm'de zaten varsa hiçbir şey yapılmaz; etiketi geriye çekecek sürüm (npm'dekilerden düşük) reddedilir. Sürüm SemVer 2.0'dır; derleme üstverisi (+...) karşılaştırmada sayılmaz. Yayımlanan her zaman dalın ucundaki sürümdür. Ön sürüm numarası n dalın ucundaki commit sayısıdır, dolayısıyla her yeni ön sürüm bir öncekinden büyüktür. Bir sürüm kararlı olarak çıktıktan sonra test'teki package.json sürümü yükseltilir; aksi halde ön sürüm kanalı durur.

Yayın hattında SDK sürüm denetimi. one-ci validate (yayın hattının ilk adımı; plan ve deploy de geçer) uygulamanın kurduğu SDK sürümünü asgari sürümle karşılaştırır. Bugünkü asgari sürüm 0.2.0-next.158'dir. Sürüm sırasıyla node_modules/@devoplus/one-sdk, pnpm-lock.yaml, package-lock.json ve package.json bildiriminden okunur. Kurulu sürüm asgari sürümden eskiyse ya da okunamıyorsa hat çıktısına ⚠ SDK: ile başlayan bir uyarı yazılır ve yayın durmaz. Platform bu denetimi ileride zorlayıcı yapabilir (o zaman eski SDK yayını durdurur); uyarıyı kalıcı bırakmayın, SDK'yı güncelleyip kilit dosyasıyla birlikte yükleyin.

⚠️ ^0.3.0 aralığı ön sürümleri KAPSAMAZ (SemVer kuralı). Ön sürüm de kurulsun isteniyorsa aralık ^0.3.0-next.0 yazılır: bu aralık 0.3.0-next.* ve 0.3.x kararlı sürümlerini kapsar, 0.4.0'ı kapsamaz. ^0.2.0-next.0 aralığı 0.3.0'a hiç yükselmez: 0.3'e geçmek için pnpm add @devoplus/one-sdk@latest (ön sürüm kanalında kalmak için @next), ardından npx one upgrade.

The version of this topic written for AI tools (Markdown): /docs/sdk.md

Ready to turn your idea into an app?

Paste the setup text into your AI tool; it starts by asking what you want to build.