Statement कैटलॉग

statements array का हर element एक statement है। यह दस्तावेज़ सभी 25 statement प्रकारों के field, व्यवहार और परिणाम को सूचीबद्ध करता है। हर मान वाली जगह मान अभिव्यक्ति के नियमों (reference, literal, JsonLogic, locale map) का पालन करती है (अपवाद दो हैं: Regex का pattern और Cache की keyRegex और Cache देखें)।

Statement सारांश

श्रेणीtypeएक-पंक्ति सारांश
संसाधन लेखनResourceCreateContent/Media बनाना (वैकल्पिक रूप से publish)
ResourceUpdateContent/Media के field का पूर्ण प्रतिस्थापन (न दिए गए field/locale हट जाते हैं)
ResourcePatchContent/Media के field का आंशिक मर्ज (केवल निर्दिष्ट field/locale; literal null हटाता है)
ResourceDeleteहटाना (केवल Draft/Archived; Published हो तो पहले unpublish)
ResourcePublish / ResourceUnpublishpublish / unpublish
ResourceArchive / ResourceUnarchivearchive / unarchive
संसाधन पठनResourceReadid से एकल आइटम पठन
ResourceFindfilter से पहला मेल खाता एकल आइटम (न हो तो null)
ResourceForEachfilter से मेल खाते संसाधनों को आंतरिक रूप से पुनरावृत्त करते हुए हर आइटम पर onEach चलाना
ResourceCountfilter से मेल खाती केवल गिनती करना (आइटम नहीं पढ़े जाते)
बाहरीHttpबाहरी HTTP कॉल ({ status, body })
EmailSendपंजीकृत EmailAccount से 1 मेल भेजना
वेरिएबलSetVarscript-scoped वेरिएबल घोषित/अपडेट करना
कैशCacheउसी Script के अपने, थोड़ी देर जीने वाले कैश को पढ़ना/लिखना/हटाना
मान पार्सिंगParseJsonJSON टेक्स्ट को मान (ऑब्जेक्ट·array·स्केलर) में पार्स करके बाइंड करना
हस्ताक्षर और टेक्स्टSignatureप्राप्त हस्ताक्षर कोड गुप्त key से बने कोड के समान है या नहीं, इसका सत्यापन (Boolean)
Hashबिना key वाले डाइजेस्ट की गणना (string)
Regexregex लागू करना। मिलान हुआ या नहीं (Boolean), या capture group (array)
नियंत्रण प्रवाहIfशर्तीय शाखा
Loopपुनरावृत्ति (foreach / while / counted)
Parallelशाखाओं को समवर्ती रूप से चलाना
Returnपरिणाम लौटाना और जल्दी समाप्त होना
Tryअपवाद प्रबंधन (catch/finally)

जो Content statement अपना लक्ष्य id से निर्दिष्ट नहीं करते, वे जिस Content Type को संभालते हैं उसे लिखना अनिवार्य है। ResourceFind·ResourceForEach·ResourceCount में resource "Content" हो, तो contentType अनिवार्य है। पूरे Space में आर-पार जाने वाली Content क्वेरी नहीं होती। ResourceCreate भी जिस Content Type को बनाना है उसे लिखता है। Media पूरे Space के लिए एक ही समुच्चय है, इसलिए वह कोई दायरा नहीं लेता, और जो statement अपना लक्ष्य id से निर्दिष्ट करते हैं (ResourceRead·ResourceUpdate·ResourcePatch·ResourceDelete तथा publish एवं archive statements) उनमें target होता है, इसलिए उन्हें दायरे की आवश्यकता नहीं होती।

चक्रीय कॉल अधिकतम 3 बार। ऊपर दिए गए संसाधन-राइट statement (ResourceCreate, ResourceUpdate, ResourcePublish आदि) में propagateEvents चालू करने पर (डिफ़ॉल्ट मान बंद है) वे परिवर्तन इवेंट उत्पन्न करते हैं, और वे इवेंट Webhook के माध्यम से फिर से किसी Script को चला सकते हैं। ऐसी श्रृंखला (Script → इवेंट → Webhook → Script → …) अधिकतम 3 बार तक ही चलती है, उसके बाद प्लेटफ़ॉर्म इसे अपने-आप रोक देता है ताकि अनंत लूप न बनें।

साझा field

{ "type": "<StatementType>", "name": "<वैकल्पिक, script में अद्वितीय>", /* ...type-विशिष्ट field... */ }
  • type: विभेदक (discriminator)। ऊपर दी गई तालिका के मानों में से एक (अनिवार्य)।
  • name: वैकल्पिक। सेट करने पर परिणाम /<name> पर context में bind हो जाता है, जिससे बाद के statements उसे { /<name>/... } के रूप में संदर्भित कर सकते हैं। यदि परिणाम का उपयोग नहीं करना है तो इसे छोड़ दें।
  • बाइंडिंग नाम नियम: name context रूट पर सीधे रखी जाने वाली key है, इसलिए इसे सहेजते समय सत्यापित किया जाता है। इसमें केवल अंग्रेज़ी अक्षर, अंक, _ और - ही आ सकते हैं (इसे JSON Pointer key के रूप में उपयोग किया जाना है, इसलिए इसके अलावा कोई भी वर्ण या खाली नाम अस्वीकृत होता है), यह किसी reserved रूट (payload·rawPayload·headers·vars·error·now) के समान नहीं हो सकती, और एक Script के भीतर अद्वितीय होनी चाहिए। प्रारूप का उल्लंघन, reserved शब्द का उपयोग, या डुप्लिकेट होने पर सहेजना अस्वीकृत हो जाता है।

एंटिटी reference आकार

contentType और target जैसे एंटिटी reference एक ही आकार { "sys": { "id": <मान अभिव्यक्ति> } } में एकीकृत होते हैं। केवल sys.id चाहिए, और लक्ष्य type resource से अनुमानित होता है (sys.type और sys.targetType छोड़ दिए जाते हैं)।

  • contentType.sys.id आमतौर पर एक literal होता है (उदाहरण: "ct_post")।
  • target.sys.id आमतौर पर एक { /ptr } मान अभिव्यक्ति होता है (runtime पर resolve; उदाहरण: { /payload/sys/id })।

resource

संसाधन-परिवार के statements लक्ष्य प्रकार को resource: "Content" | "ContentType" | "Media" | "ServiceUser" से निर्दिष्ट करते हैं।

Content Type को केवल ResourceCount स्वीकार करता है। इसे किसी अन्य statement में लिखने पर सहेजना अस्वीकृत हो जाता है। ढाँचे को स्वयं बनाने या बदलने का काम Script का नहीं, बल्कि CMA का है।

ServiceUser (उत्पाद में साइन अप किया हुआ सदस्य) केवल पठनीय है। पठन के तीन statements (ResourceRead·ResourceFind·ResourceForEach) ही यह मान लेते हैं, और किसी write statement में इसे लिखने पर सहेजना अस्वीकृत हो जाता है (त्रुटियाँ देखें)। नियम सदस्य डायरेक्टरी का पठन में दिए गए हैं।

संसाधन लेखन

हर write statement में propagateEvents (डिफ़ॉल्ट false) होता है। इसे true करने पर वह write एक परिवर्तन इवेंट उत्पन्न करता है, जिससे Webhook जैसी बाद की क्रियाएँ चलती हैं। डिफ़ॉल्ट रूप से यह उत्पन्न नहीं करता (एक शांत system write)।

ResourceCreate

Content या Media बनाता है। Content और Media fields मॉडल साझा करते हैं, और मान locale map होते हैं।

fieldदायराविवरण
resourceसाझा"Content" या "Media" (अनिवार्य)
contentTypeContentबनाया जाने वाला Content Type ({ sys: { id } })। Content होने पर अनिवार्य
fieldsसाझाfield map { "<field>": { "<locale>": मान } }। हर populate किए गए field के लिए डिफ़ॉल्ट locale bucket अनिवार्य। Content की keys Content Type की परिभाषा का पालन करती हैं, और Media की keys तय हैं (title·description·file)
localeसाझा(सुविधा) देने पर fields के हर मान को { <locale>: मान } के रूप में स्वतः wrap कर देता है
publishसाझाwrite के बाद publish (CDA/ACDA पर दिखता है)। डिफ़ॉल्ट true
  • Media file: fields.file.{locale} का मान एक ingest निर्देश { "source": <मान अभिव्यक्ति>, "encoding": "url"|"base64" } होता है (दोनों अनिवार्य)। फ़ाइल शामिल करने वाले write में ingest engine करता है (url हो तो डाउनलोड, और base64 हो तो डिकोड करने के बाद अपलोड और प्रोसेस)। यह ingest कोई समय घोषित नहीं करता, इसलिए यह 30 सेकंड के मूल बजट से जाता है (समय बजट), और बाहरी कॉल की सीमा में नहीं गिना जाता। फ़ाइल-रहित (fileless) Media भी बनाया जा सकता है। यदि publish:true है पर फ़ाइल नहीं है या processing अधूरी है, तो publish चरण में error आता है; और publish:false हो तो यह Draft ही रहता है।
  • परिणाम (name binding): बनाया गया संसाधन। { /<name>/sys/id }, { /<name>/fields/<field>/<locale> }
// Content
{ "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_post" } },
  "fields": { "title": { "en-US": "{ /payload/fields/title }" } }, "publish": true, "name": "post" }
 
// Media. file एक ingest निर्देश है
{ "type": "ResourceCreate", "resource": "Media",
  "fields": {
    "title": { "en-US": "{ /payload/fields/prompt }" },
    "file":  { "en-US": { "source": "{ /gen/body/data/0/url }", "encoding": "url" } }
  }, "name": "img" }

ResourceUpdate

लक्ष्य Content या Media के field को पूरी तरह प्रतिस्थापित करता है (PUT)। fields में जो दिया जाता है वही नया field-सेट बन जाता है, और यहाँ मौजूद न होने वाला हर field और locale हटा दिया जाता है। केवल कुछ हिस्सा बदलने के लिए ResourcePatch का उपयोग करें।

fieldविवरण
resource"Content" या "Media"
targetलक्ष्य ({ sys: { id } }, अनिवार्य)। id आमतौर पर { /ptr } होता है
fieldsलिखे जाने वाले सभी field। मान locale map होते हैं। चूँकि यह पूर्ण प्रतिस्थापन है, यहाँ मौजूद न होने वाला हर field और locale हटा दिया जाता है। Media के लिए file एक ingest निर्देश है (ऊपर ResourceCreate देखें)। सूचीबद्ध फ़ाइलें हमेशा पुनः ingest होती हैं, और जिन locales की फ़ाइल नहीं दी गई उन्हें हटा दिया जाता है
locale(सुविधा) fields को स्वतः wrap करता है
version(वैकल्पिक) मान अभिव्यक्ति (Int)। optimistic locking। देने पर, यह update केवल तभी चलता है जब यह लक्ष्य के वर्तमान sys.version से मेल खाता हो; मेल न खाने पर यह version-conflict error के साथ abort कर देता है (Try से catch किया जा सकता है)। छोड़ने पर कोई जाँच नहीं होती (last-write-wins)
publishupdate के बाद republish। डिफ़ॉल्ट true

Media का केवल metadata बदलने के लिए Update का उपयोग करने पर file छूट जाता है और फ़ाइल पूरी तरह हट जाती है (क्योंकि यह पूर्ण प्रतिस्थापन है)। आंशिक बदलाव के लिए हमेशा ResourcePatch का उपयोग करें।

{ "type": "ResourceUpdate", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } },
  "fields": { "title": { "en-US": "Hello", "ko-KR": "안녕" }, "status": { "en-US": "published" } } }

ResourcePatch

लक्ष्य Content या Media के field को आंशिक रूप से मर्ज करता है (PATCH)। यह fields में दिए गए केवल उन्हीं field (और उनके भीतर के locale) को अधिलेखित करता है, और जिन field और locale का उल्लेख नहीं किया गया उन्हें ज्यों-का-त्यों रखता है। मान का आकार, locale, version और publish ResourceUpdate जैसे ही हैं।

fieldविवरण
resource"Content" या "Media"
targetलक्ष्य ({ sys: { id } }, अनिवार्य)। id आमतौर पर { /ptr } होता है
fieldsअधिलेखित किए जाने वाले field। मान locale map होते हैं। केवल निर्दिष्ट field और locale bucket अपडेट होते हैं (बाकी बने रहते हैं)। यदि कोई मान literal null है, तो वह (field, locale) हट जाता हैMedia के लिए file एक ingest निर्देश है (ऊपर ResourceCreate देखें)
locale(सुविधा) fields को स्वतः wrap करता है
version(वैकल्पिक) ResourceUpdate जैसा ही (optimistic locking)
publishupdate के बाद republish। डिफ़ॉल्ट true
  • किसी विशिष्ट locale या फ़ाइल को हटाना: मान के रूप में literal null दें। उदाहरण: "title": { "fr-FR": null } (fr-FR title हटाता है), "file": { "en-US": null } (en-US फ़ाइल हटाता है)। जो मान अभिव्यक्ति runtime पर null के रूप में मूल्यांकित होता है वह हटाना नहीं बल्कि एक error है (केवल literal null हटाता है)।
  • Media file को ingest निर्देश देने पर वह उस locale की फ़ाइल को प्रतिस्थापित कर देता है। यदि फ़ाइल नहीं दी जाती, तो वह बनी रहती है।
// केवल viewCount(en-US) को +1. title, अन्य locale आदि बाकी सब ज्यों-का-त्यों बने रहते हैं
{ "type": "ResourcePatch", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } },
  "fields": { "viewCount": { "en-US": { "$+": [ "{ /payload/fields/viewCount }", 1 ] } } } }

ResourceDelete

लक्ष्य को हटाता है। केवल Draft और Archived स्थिति को ही हटाया जा सकता है। Published या Changed होने पर delete अस्वीकृत होता है, इसलिए आपको पहले ResourceUnpublish करना होगा। Media की फ़ाइल की processing चल रही हो, तो delete अस्वीकृत हो जाता है। यह auto-unpublish नहीं करता (CMA/ACMA जैसा ही)।

fieldविवरण
resource"Content" या "Media"
targetलक्ष्य ({ sys: { id } }, अनिवार्य)
{ "type": "ResourceDelete", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } } }

ResourcePublish, ResourceUnpublish, ResourceArchive, ResourceUnarchive

लक्ष्य की publish और archive स्थिति को स्वतंत्र रूप से नियंत्रित करता है। चारों के field एक समान हैं। हर कार्य की status पूर्वशर्त CMA/ACMA जैसी ही है। ResourcePublish, Archived से नहीं किया जा सकता और उसके लिए फ़ाइल की processing पूरी हो चुकी होनी चाहिए। ResourceUnpublish केवल Published·Changed से, ResourceArchive केवल Draft से, और ResourceUnarchive केवल Archived से किया जा सकता है।

fieldविवरण
resource"Content" या "Media"
targetलक्ष्य ({ sys: { id } }, अनिवार्य)
version(वैकल्पिक) मान अभिव्यक्ति (Int)। optimistic locking। देने पर यह कार्य केवल तभी किया जाता है जब यह वर्तमान sys.version से मेल खाता हो
{ "type": "ResourcePublish",   "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } } }
{ "type": "ResourceUnpublish", "resource": "Content", "target": { "sys": { "id": "{ /payload/sys/id }" } } }
{ "type": "ResourceArchive",   "resource": "Media",   "target": { "sys": { "id": "{ /m/sys/id }" } } }

संसाधन पठन

ResourceRead और ResourceFind संसाधन पढ़कर मान में bind करते हैं, और ResourceCount केवल गिनती करता है। तीनों स्थिति को नहीं बदलते (propagateEvents नहीं होता)। ResourceForEach में भी क्वेरी स्वयं एक पठन है, पर यदि onEach में कोई संसाधन-राइट statement रखा जाए, तो हर आइटम पर वह write चलता है और स्थिति बदल जाती है।

चारों statements (ResourceRead·ResourceFind·ResourceForEach·ResourceCount) from (डिफ़ॉल्ट Current) से यह तय करते हैं कि कौन-सा संग्रहीत संस्करण पढ़ा जाए। Current वह नवीनतम draft है जिसे कंटेंट स्टूडियो देखता है (वह मान जो CMA/ACMA पढ़ता है), और Published वह publish स्नैपशॉट है (जो CDA/ACDA डिलीवर करता है, अर्थात अंतिम publish के समय का मान)। ServiceUser publish नहीं होता, इसलिए वह केवल Current लेता है (सदस्य डायरेक्टरी का पठन देखें)।

इसके अतिरिक्त, ResourceFind·ResourceForEach·ResourceCount advanced (डिफ़ॉल्ट true) से उन्नत खोज (Advanced Search) को चालू और बंद करते हैं। न लिखने पर यह चालू रहती है। यह केवल Content के लिए है, इसलिए Media और ServiceUser पठन में इसे अनदेखा कर दिया जाता है। चालू होने पर where में regex, near और within operators तथा फ़ुल-टेक्स्ट खोज का उपयोग किया जा सकता है (जिस LongText field में फ़ुल-टेक्स्ट खोज चालू है, वहाँ eq उस मान को समाहित करने वाले आइटम भी आंशिक एवं समरूप मिलान से ढूँढ लेता है), और order fields.* से sorting कर सकता है। बंद होने पर ये तीनों operators अस्वीकार कर दिए जाते हैं, text पर eq ठीक-ठीक मिलान होता है, और prefix तथा तुलना एवं सूची operators उन्नत खोज से निरपेक्ष रूप से काम करते हैं। अभी-अभी बनाया या बदला गया कोई आइटम उन्नत खोज में प्रतिबिंबित होने में थोड़ा समय (लगभग 1 सेकंड) लेता है, इसलिए ठीक उसके बाद चलने वाली उन्नत खोज query में वह छूट सकता है। डिफ़ॉल्ट रूप से यह चालू रहती है, इसलिए यह विलंब तब तक हर query पर लागू होता है जब तक advanced को false न रखा जाए। अभी-अभी लिखे गए किसी आइटम को तुरंत पढ़ने के लिए, id से ResourceRead (मुख्य संग्रह, प्रतिबिंबित होने में कोई विलंब नहीं) का उपयोग करें, या write द्वारा लौटाए गए sys.id से उसका पठन करें।

where का createdBy: ":self" का अर्थ है "केवल वही जो अभी कॉल करने वाले उपयोगकर्ता ने बनाया है"। पर जिस Script में अनाम कॉल की अनुमति है (anonymousCallEnabled), वहाँ इसका उपयोग नहीं किया जा सकता। उस स्थिति में :self कॉलर के बजाय लेखक पर हल होता है और चुपचाप लेखक के संसाधन खुल जाते हैं, इसलिए ऐसी परिभाषा सहेजने पर अस्वीकृत हो जाती है (अनाम कॉल देखें)।

where और order में कंटेंट field को fields.<field> के रूप में लिखा जाता है (अकेले नाम से यह पहचाना नहीं जाता)। fields.<field> पर Space की डिफ़ॉल्ट locale स्वतः लागू होती है, इसलिए locale सीधे नहीं जोड़ी जाती। नीचे दिए उदाहरणों के fields.status, fields.slug ज्यों-के-त्यों डिफ़ॉल्ट locale की क्वेरी हैं। किसी विशिष्ट (गैर-डिफ़ॉल्ट) locale की क्वेरी करने पर ही fields.<field>.<locale> (उदाहरण: fields.title.ko-KR) के रूप में स्पष्ट करें। sys.* (sys.createdAt आदि) और createdBy (:self) को fields. के बिना ज्यों-का-त्यों लिखें। विस्तृत नियम मान अभिव्यक्ति में where और order की locale में दिए गए हैं।

सदस्य डायरेक्टरी का पठन (ServiceUser)

ResourceRead·ResourceFind·ResourceForEach resource में "ServiceUser" लेकर उस Space की सदस्य डायरेक्टरी पढ़ते हैं (ResourceCount इसे नहीं लेता; नीचे ResourceCount देखें)। किसी ऑर्डर का स्वामी कौन है यह जाँचने, या ईमेल से सदस्य ढूँढकर उसकी sys.id अगले statement को देने वाले प्रवाह में इसका उपयोग करें। नीचे दिए नियम तीनों statements पर समान रूप से लागू होते हैं।

  • केवल पठन होता है। ResourceCreate·ResourceUpdate·ResourcePatch·ResourceDelete तथा publish एवं archive statements "ServiceUser" नहीं लेते, और ऐसी परिभाषा सहेजते समय अस्वीकृत हो जाती है। यह कोई ऐसी चीज़ नहीं है जिसे अनुमति जोड़कर खोला जा सके; Script से सदस्य को बदलने का रास्ता ही नहीं है, इसलिए यह अनुमति की त्रुटि नहीं बल्कि गलत लिखे गए statement के रूप में अस्वीकृत होता है।
  • लेखक के पास सदस्य डायरेक्टरी की अनुमति हो, तभी सहेजा जाता है। Content और Media की तरह अनुमति map से जाँच नहीं होती, बल्कि यह देखा जाता है कि लेखक की SpaceRole के settings में SETTING_SERVICE_LOGIN (या SETTING_ALL) है या नहीं। कारण यह है कि सदस्य डायरेक्टरी बाकी सभी रास्तों पर भी Space सेटिंग के अधीन आने वाला संसाधन है। न होने पर सहेजना अस्वीकृत हो जाता है (सुरक्षा मॉडल देखें)।
  • from केवल Current लेता है। सदस्य publish होने वाला संसाधन नहीं है, इसलिए Published देने पर निष्पादन विफल हो जाता है।
  • contentType और advanced अनदेखा कर दिए जाते हैं। सदस्य डायरेक्टरी Content Type से नहीं बँटती (पूरे Space के लिए एक ही है), और उन्नत खोज भी केवल Content के लिए है।
  • where का sys.email केवल ठीक-ठीक मिलान वाले operators लेता है (eq·ne·in·nin)। सदस्य का पता एन्क्रिप्ट होकर संग्रहीत होता है, इसलिए क्रम-तुलना या prefix का कोई अर्थ नहीं है। इनके अलावा कोई operator देने पर चुपचाप 0 परिणाम लौटाने के बजाय निष्पादन विफल हो जाता है।
  • परिणाम ServiceUser संसाधन स्वयं होता है। इसे { /<name>/sys/id } और { /<name>/nickname } की तरह संदर्भित करें। इसकी संरचना ServiceUser संदर्भ में दी गई है। मिले हुए सदस्य को मेल भेजते समय पता निकालने के बजाय EmailSend के toServiceUser में उसकी sys.id दें (engine भेजने से ठीक पहले पते को resolve करता है, इसलिए सदस्य का पता Script के वेरिएबल स्थान में नहीं आता)।
// ईमेल से एक सदस्य ढूँढता है। न मिले तो null
{ "type": "ResourceFind", "resource": "ServiceUser",
  "where": { "sys.email": { "eq": "{ /payload/fields/email }" } }, "name": "member" }

ResourceRead

यह id से एकल आइटम पठन है (get-by-id)। परिणाम पूरे संसाधन को नाम में bind करता है।

fieldविवरण
resource"Content"·"Media"·"ServiceUser"
targetलक्ष्य ({ sys: { id } })। id एक मान अभिव्यक्ति है
from(वैकल्पिक) Current (डिफ़ॉल्ट, नवीनतम draft) या Published (publish स्नैपशॉट)। ServiceUser के लिए केवल Current
  • परिणाम: bind संसाधन स्वयं होता है। इस statement को name दिया हो, तो उसे सीधे { /<name>/sys/id } और { /<name>/fields/<field>/<locale> } से संदर्भित करें (नीचे के उदाहरण में "name": "order" है, यानी { /order/sys/id })। यह list नहीं है, इसलिए किसी array index से नहीं गुज़रना पड़ता।
  • यदि लक्ष्य मौजूद न हो तो error आता है। इसे संभालने के लिए Try में लपेटा जा सकता है।
{ "type": "ResourceRead", "resource": "Content",
  "target": { "sys": { "id": "{ /payload/fields/orderId }" } }, "name": "order" }

ResourceFind

filter से पहला मेल खाता एकल आइटम पढ़ता है। कोई न हो तो null होता है। किसी अद्वितीय business key (slug, email, sku) से एक रिकॉर्ड ढूँढने के लिए इसका उपयोग करें।

fieldविवरण
resource"Content"·"Media"·"ServiceUser"
contentTypeखोज का दायरा Content Type ({ sys: { id } })। Content होने पर अनिवार्यMedia और ServiceUser में अनदेखा
wherefilter ({ "<field>": { "<op>": <मान> } })। उपलब्ध operators operator सूची के अनुसार हैं (regex/near/within के लिए advanced आवश्यक)। createdBy: ":self" समर्थित। ServiceUser का sys.email केवल eq·ne·in·nin लेता है (सदस्य डायरेक्टरी का पठन)
orderकई मैच होने पर "पहला" तय करने वाला sort (उदाहरण: "-sys.createdAt")
from(वैकल्पिक) Current (डिफ़ॉल्ट, नवीनतम draft) या Published (publish स्नैपशॉट)। ServiceUser के लिए केवल Current
advanced(वैकल्पिक) उन्नत खोज (Advanced Search) से चलाएँ। केवल Content (Media और ServiceUser अनदेखा)। डिफ़ॉल्ट true। ऊपर संसाधन पठन टिप्पणी देखें।
  • परिणाम: पहले मेल खाते संसाधन को इस statement के name में bind करता है। इसे सीधे { /<name>/fields/<field>/<locale> } के रूप में संदर्भित करें। कोई न होने पर यह null होता है, इसलिए { "==": [ "{ /<name> }", null ] } से अस्तित्व के आधार पर शाखा बनाएँ (find-then-upsert का आम pattern)।
{ "type": "ResourceFind", "resource": "Content", "contentType": { "sys": { "id": "ct_article" } },
  "where": { "fields.slug": { "eq": "{ /payload/fields/slug }" } }, "name": "found" }

ResourceForEach

filter से मेल खाते संसाधनों को आंतरिक रूप से पुनरावृत्त करते हुए हर आइटम पर onEach चलाता है। यह मान के रूप में उपयोग होने वाला कोई collection नहीं बनाता, बल्कि हर आइटम पर एक कार्य करने के लिए बना statement है। draft को थोक में publish करना, शर्त से मेल खाते Content को थोक में संशोधित करना, या हर आइटम को बाहर भेजना/सिंक करना जैसे दोहराए जाने वाले कार्यों में इसका उपयोग करें। केवल एक रिकॉर्ड पढ़ने के लिए ResourceRead (id) या ResourceFind (filter) का उपयोग करें।

fieldविवरण
resource"Content"·"Media"·"ServiceUser" (अनिवार्य)
contentTypeपुनरावृत्ति का दायरा Content Type ({ sys: { id } })। Content होने पर अनिवार्यMedia और ServiceUser में अनदेखा
wherefilter ({ "<field>": { "<op>": <मान> } })। अर्थ ResourceFind के where जैसा ही है (ServiceUser के sys.email की बाधा भी वही है)। उपलब्ध operators operator सूची के अनुसार हैं (regex/near/within के लिए advanced आवश्यक)। createdBy: ":self" समर्थित
ordersort (उदाहरण: "sys.createdAt,sys.id")। न देने पर प्लेटफ़ॉर्म का डिफ़ॉल्ट क्रम
fromCurrent (डिफ़ॉल्ट, नवीनतम draft) या Published (publish स्नैपशॉट)। ServiceUser के लिए केवल Current
advancedउन्नत खोज (Advanced Search) से पुनरावृत्ति। केवल Content (Media और ServiceUser अनदेखा)। डिफ़ॉल्ट true। ऊपर संसाधन पठन टिप्पणी देखें
limit(वैकल्पिक, 1 या अधिक) कुल संसाधित की जाने वाली संख्या की ऊपरी सीमा (यह page size नहीं है)। न देने पर प्लेटफ़ॉर्म सीमा (10,000 आइटम) तक पुनरावृत्ति
name(वैकल्पिक) वर्तमान आइटम को bind करने वाला नाम। हर पुनरावृत्ति पर यह नए सिरे से bind होता है और onEach के भीतर { /<name> } से संदर्भित होता है (Loop के name जैसा ही जीवनकाल; पुनरावृत्ति समाप्त होने के बाद भी अंतिम आइटम bound रहता है)। आइटम को संदर्भित न करने पर इसे छोड़ दें
onEachहर आइटम पर चलाने के लिए child statement array (अनिवार्य)
  • यह किसी collection को bind नहीं करता (map नहीं, बल्कि foreach)।{ items, next } होता है, न कोई cursor। पुनरावृत्ति का परिणाम मान के रूप में वापस नहीं मिलता, बल्कि हर आइटम पर onEach चलता है। यदि सूची चाहिए, तो SetVar से स्वयं इकट्ठा करें। यदि केवल गिनती चाहिए, तो ResourceCount का उपयोग करें।
  • limit न होने पर भी यह अनंत पुनरावृत्ति नहीं है। न देने पर यह प्लेटफ़ॉर्म सीमा (10,000 आइटम) तक चलता है, और मेल खाते आइटम शेष रहते हुए उस सीमा पर पहुँचने पर विफल हो जाता है (ताकि अछूते आइटम छोड़कर उसे सफलता के रूप में रिपोर्ट न किया जाए)। इसके विपरीत, घोषित limit तक पहुँचना एक इच्छित विराम है, इसलिए यह सामान्य समाप्ति है। सीमा से अधिक limit सहेजते समय अस्वीकृत कर दिया जाता है।
  • कोई cursor नहीं है। पूरा हो जाए तो सफलता, और बीच में रुक जाए (wall-clock/कोटा पार, onEach की अनसंभाली विफलता) तो विफलता, और error यह इंगित करता है कि किस आइटम पर और क्यों विफल हुआ। पुनरारंभ को लेखक अपने डेटा से व्यक्त करता है (where को "असंसाधित" पर रखकर और onEach के अंत में पूर्णता को चिह्नित करके, पुनः निष्पादन शेष आइटम से आगे बढ़ता है)।
  • समय बजट में यह गुणा के रूप में आता है। यह statement जो समय घोषित करता है वह onEach द्वारा घोषित समय को संसाधित आइटम की संख्या (limit, न हो तो 10,000) से गुणा किया हुआ मान है (समय बजट)। चूँकि यह child रखने वाला एक composite statement है, यह स्वयं बाहरी कॉल leaf बजट में नहीं गिना जाता, बल्कि onEach के भीतर के बाहरी कॉल statement बजट में गिने जाते हैं।
  • onEach में अन्य statement की तरह बाहरी कॉल (Http·EmailSend) या Media फ़ाइल ingest रखी जा सकती है (Loop के body जैसा ही)। संसाधन क्वेरी के परिणाम को हर आइटम पर एक बार संसाधित करना ही इस statement के होने का कारण है।
// draft स्थिति वाली सभी पोस्ट ढूँढकर हर आइटम को publish करना
{ "type": "ResourceForEach", "resource": "Content", "contentType": { "sys": { "id": "ct_post" } },
  "where": { "fields.status": { "eq": "draft" } }, "order": "sys.createdAt,sys.id",
  "from": "Current", "advanced": false, "name": "post",
  "onEach": [
    { "type": "ResourcePublish", "resource": "Content", "target": { "sys": { "id": "{ /post/sys/id }" } } }
  ] }

ResourceCount

filter से मेल खाती केवल गिनती करता है। यह आइटम पढ़कर नहीं लाता, इसलिए जब सूची नहीं बल्कि संख्या चाहिए तब इसका उपयोग करें। बचा हुआ स्टॉक जाँचने, वही मान पहले से मौजूद है या नहीं यह तय करने, या सीमा पार हुई या नहीं यह जाँचने की जगह यही है।

fieldविवरण
resource"Content" या "ContentType" (अनिवार्य)। Media और ServiceUser गिने नहीं जा सकते, और ऐसा लिखने पर सहेजना अस्वीकृत हो जाता है
contentTypeगिनती का दायरा Content Type ({ sys: { id } })। Content होने पर अनिवार्यContent Type गिनते समय अनदेखा (पूरे Space के लिए एक ही समुच्चय)
wherefilter। अर्थ ResourceFind के where जैसा ही है। मेल खाते सभी आइटम गिने जाते हैं
from(वैकल्पिक) Current (डिफ़ॉल्ट, नवीनतम draft) या Published (publish स्नैपशॉट)
advanced(वैकल्पिक) उन्नत खोज (Advanced Search) से चलाएँ। केवल Content (Content Type गिनते समय अनदेखा)। डिफ़ॉल्ट true। ऊपर संसाधन पठन टिप्पणी देखें
name(वैकल्पिक) गिनती को bind करने वाला नाम
  • परिणाम: मेल खाती गिनती को इस statement के name में bind करता है। इसे { /<name> } से संदर्भित करके तुलना और शाखा बनाने में उपयोग करें।
  • यह आइटम नहीं लौटाता। आइटम चाहिए तो ResourceFind (पहला मेल खाता एकल आइटम) या ResourceForEach (हर आइटम पर चलाना) का उपयोग करें।
  • गिनती पाने के लिए ResourceForEach से घूमते हुए मत गिनिए। पुनरावृत्ति समय बजट को आइटम की संख्या से गुणा करके लेती है (समय बजट), और मेल खाते आइटम शेष रहते हुए प्लेटफ़ॉर्म सीमा पर पहुँचने पर विफल हो जाती है। केवल गिनना हो, तो यह statement इसे एक ही बार में पूरा कर देता है।
  • order और limit नहीं हैं। गिनने के लिए क्रम की आवश्यकता नहीं होती, और मेल खाते सभी आइटम गिने जाते हैं।
// इस पोस्ट पर कितनी टिप्पणियाँ आई हैं, यह गिनता है
{ "type": "ResourceCount", "resource": "Content", "contentType": { "sys": { "id": "ct_comment" } },
  "where": { "fields.postId": { "eq": "{ /payload/sys/id }" } }, "name": "commentCount" }

बाहरी

Http

बाहरी HTTP कॉल करता है। यह एक बाहरी कॉल है, इसलिए यह प्लान-वार बाहरी कॉल सीमा में गिना जाता है, और समय बजट में इसे timeoutMs (न हो तो 30 सेकंड) × (1 + retry) के रूप में गिना जाता है।

fieldविवरण
method"GET", "POST", "PUT", "PATCH", "DELETE"
urlलक्ष्य URL (मान अभिव्यक्ति; { /ptr } डाला जा सकता है)
headers[{ "key", "value", "secret"? }]value एक मान अभिव्यक्ति है। secret:true header को केवल CMA (administrator) के लिए माना जाता है: यह अंतिम उपयोगकर्ता को उजागर नहीं होता, और अनुरोध भेजे जाने से ठीक पहले ही decrypt किया जाता है। यहाँ Content-Type डालने पर body उसी प्रारूप में serialize होती है (नीचे)
bodyअनुरोध body (मान अभिव्यक्ति या JSON)। वह किस प्रारूप में भेजी जाती है, यह Content-Type header तय करता है
timeoutMsइस कॉल का timeout (ms)
retryresponse status 400 या उससे अधिक होने पर retry की संख्या। डिफ़ॉल्ट 0, अधिकतम सीमा 2
ignoreStatusCode(retry पूरा होने के बाद का) अंतिम status 400 या उससे अधिक होने पर इस कॉल को विफल माना जाए या नहीं। डिफ़ॉल्ट false होने पर इसे विफलता माना जाता है और यह Try/catch का लक्ष्य बन जाता है। true होने पर इसे विफलता नहीं माना जाता और { status, body } को ज्यों-का-त्यों bind कर दिया जाता है (कॉलर स्वयं status के आधार पर शाखा बनाता है)
responseTyperesponse body को किस रूप में लेना है। "Json" (डिफ़ॉल्ट) उसे ऑब्जेक्ट या array में पार्स करता है, "Text" उसे string के रूप में लेता है
  • परिणाम: { status, body }। इस statement को name दिया हो, तो { /<name>/status }, { /<name>/body/... }body का रूप responseType तय करता है।
  • responseType केवल सफल response पर लागू होता है। status 400 या उससे अधिक वाले response की body, घोषित मान चाहे जो हो, निदान के लिए बाइंड होती है (JSON होने पर पार्स किया गया मान, अन्यथा string)।
  • "Json" घोषित है पर body JSON नहीं है, तो यह कॉल विफल होती है (Try/catch का लक्ष्य)। जो API JSON नहीं देती, उसकी response body को "Text" से लें, और मान के रूप में चाहिए तो ParseJson से पार्स करें।
  • "Text" को response के Content-Type के charset से डिकोड किया जाता है, और charset न होने पर UTF-8 माना जाता है। body खाली होने पर दोनों स्थितियों में body null होती है।
  • response आकार सीमा: response body अधिकतम 10MiB है। इससे अधिक होने पर यह कॉल अपवाद के साथ विफल होती है और इसे किसी भी अन्य रनटाइम विफलता की तरह Try/catch से संभाला जा सकता है (यह आकार-आधारित विफलता है, इसलिए इसे ignoreStatusCode से नज़रअंदाज़ नहीं किया जाता)।
{ "type": "Http", "method": "POST", "url": "https://api.llm.com/v1/gen",
  "headers": [ { "key": "Authorization", "value": "Bearer sk-...", "secret": true } ],
  "body": { "prompt": "{ /payload/fields/prompt }" }, "timeoutMs": 15000, "retry": 1,
  "responseType": "Json", "name": "resp" }

body किस प्रारूप में भेजी जाती है

headers में दिया गया Content-Type, body का serialize प्रारूप तय करता है। तुलना में अक्षरों का case और ;charset=… जैसे पैरामीटर नज़रअंदाज़ कर केवल शुरुआती भाग देखा जाता है। header न हो या उसका मान खाली हो, तो application/json के रूप में भेजा जाता है। यह header केवल body होने पर ही जोड़ा जाता है, इसलिए body न हो तो लिखा हुआ header ज्यों-का-त्यों जाता है। एक ही key को कई बार देने पर केवल पहला मान उपयोग होता है और वे एक में मिला दिए जाते हैं।

जिस body को घोषित प्रारूप में नहीं रखा जा सकता, उसे रखे जा सकने वाले प्रारूप में सुधारकर भेजा जाता है। ऐसा नहीं होता कि header वास्तविक body से भिन्न बात कहे।

ये वे संयोजन हैं जिनमें घोषित मान ज्यों-का-त्यों जाता है।

घोषित Content-Typebody का रूपजाने वाली body
application/jsonकुछ भीJSON
application/x-www-form-urlencodedऑब्जेक्ट·arrayorder[id]=A-2481&order[amount]=34000
text/plainस्केलरमान ज्यों-का-त्यों
अन्य (text/xml आदि)कुछ भीJSON

ये वे संयोजन हैं जिन्हें घोषित प्रारूप में नहीं रखा जा सकता, इसलिए इन्हें सुधार दिया जाता है।

घोषित Content-Typebody का रूपवास्तव में जाने वाला Content-Typeजाने वाली body
application/x-www-form-urlencodedस्केलरtext/plain;charset=UTF-8मान ज्यों-का-त्यों
text/plainऑब्जेक्ट·arrayapplication/jsonJSON

ये दो पंक्तियाँ यह स्पष्ट करती हैं कि जोड़ी बेमेल होने पर अनुरोध किस रूप में जाता है, और ये इच्छित प्रारूप पाने का तरीका नहीं हैं। body मान अभिव्यक्ति से बनती हो, तो निष्पादन के समय की payload के अनुसार वह स्केलर हो सकती है, और तब यह सुधार बिना किसी त्रुटि के हो जाता है। प्राप्त करने वाला पक्ष प्रारूप पर आपत्ति करे, तो body के रूप और Content-Type में से किसी एक को अपनी मंशा के अनुसार ठीक करें।

form-urlencoded ऑब्जेक्ट को bracket key के रूप में और array को index के रूप में फैलाता है।

bodyफैलने वाली key और मान
{ "order": { "id": "A-2481", "amount": 34000 } }order[id]=A-2481&order[amount]=34000
{ "tags": ["outerwear", "winter"] }tags[0]=outerwear&tags[1]=winter
{ "items": [{ "sku": "TUMBLER-500" }] }items[0][sku]=TUMBLER-500
{ "memo": null }memo=

key और मान UTF-8 में percent-encode होकर जाते हैं। ऊपर की तालिका key की संरचना दिखाने के लिए decode किया हुआ रूप है। मान में & या + हो, तब भी उन्हें जोड़े का विभाजक या रिक्त स्थान नहीं समझा जाता और वे ज्यों-के-त्यों पहुँचते हैं।

नेस्टिंग को bracket key के रूप में फैलाने वाली यह लेखन-शैली व्यापक रूप से प्रचलित परिपाटी है, न कि प्रारूप का स्वयं का मानक। जाँच लें कि प्राप्त करने वाला पक्ष order[id] को नेस्टेड ऑब्जेक्ट में पुनर्स्थापित करता है या नहीं, और न करता हो तो body को सपाट key से बनाएँ।

{ "type": "Http", "method": "POST", "url": "https://api.example.com/oauth/token",
  "headers": [ { "key": "Content-Type", "value": "application/x-www-form-urlencoded" } ],
  "body": { "grant_type": "client_credentials", "client_id": "{ /vars/clientId }" },
  "name": "token" }

EmailSend

पंजीकृत EmailAccount के माध्यम से 1 मेल भेजता है। इसके field केवल वही हैं जो सीधे SMTP/MIME पर मैप होते हैं। कोई template id, शेड्यूल्ड भेजना, या प्रदाता-विशिष्ट विस्तार नहीं है (ऐसी सुविधा चाहिए तो Http से संबंधित मेल सेवा की API को सीधे कॉल करें)। भेजने वाला (from address) यहाँ तय नहीं होता, बल्कि account जिस EmailAccount को इंगित करता है, उससे आता है।

fieldविवरण
accountभेजने वाला EmailAccount reference ({ sys: { id } }, अनिवार्य)। आमतौर पर एक literal id। मान अभिव्यक्ति के रूप में देने पर यह भेजने के समय resolve होता है, इसलिए सहेजते समय इसकी जाँच नहीं की जा सकती
toप्राप्तकर्ता का पता (मान अभिव्यक्ति)। to और toServiceUser में से ठीक एक ही का उपयोग करें
toServiceUserप्राप्तकर्ता को ServiceUser reference से निर्दिष्ट करना ({ sys: { id } }; उसका sys.id मान अभिव्यक्ति हो सकता है)। engine भेजने से ठीक पहले पते को resolve करता है, इसलिए सदस्य का पता Script के वेरिएबल स्थान में नहीं आता
cccc प्राप्तकर्ता पतों का array (मान अभिव्यक्ति)
bccगुप्त प्राप्तकर्ता पतों का array (मान अभिव्यक्ति)
subjectविषय (मान अभिव्यक्ति, अनिवार्य)
bodyमुख्य भाग (मान अभिव्यक्ति, अनिवार्य)। यह हमेशा text/html के रूप में भेजा जाता है, इसलिए सादे टेक्स्ट के बजाय markup लिखें (लाइन-ब्रेक space बन जाते हैं, < को tag के रूप में समझा जाता है)। जो मान अभिव्यक्ति परिणाम इसमें interpolate होते हैं वे HTML-escape किए जाते हैं
replyTo(वैकल्पिक) Reply-To header (मान अभिव्यक्ति)। यह भेजने वाले से भिन्न हो सकता है (उदाहरण: no-reply से भेजना पर उत्तर support पते पर)
timeoutMs(वैकल्पिक, 1 या अधिक) इस भेजने का timeout (ms)। न देने पर प्लेटफ़ॉर्म का डिफ़ॉल्ट मान; ऊपरी सीमा से अधिक मान सहेजते समय अस्वीकृत
  • प्राप्तकर्ताओं की कुल संख्या अधिकतम 50 है। to (1), cc और bcc सबको मिलाकर गिना जाता है (SMTP envelope में cc/bcc का भेद नहीं होता और सब प्राप्तकर्ता के रूप में जाते हैं, इसलिए इन्हें कुल में गिना जाता है)। इससे अधिक होने पर सहेजने और निष्पादन में अस्वीकृत हो जाता है। कई लोगों को भेजने के लिए ResourceForEach + EmailSend से प्रति आइटम 1 मेल भेजें।
  • यह परिणाम को bind नहीं करता। सफलता का अर्थ केवल इतना है कि "प्रदाता ने मेल स्वीकार कर लिया", इसलिए लौटाने के लिए कोई मान नहीं होता और यह name नहीं लेता। यह retry भी नहीं करता (ईमेल non-idempotent है, इसलिए अस्पष्ट रूप से विफल होने के बाद retry करने पर डुप्लिकेट भेजना हो जाता है; इसीलिए यह Http के retry का पालन नहीं करता)। विफलता throw होती है और Try के catch से संभाली जाती है।
  • यह एक बाहरी कॉल है। यह प्लान-वार बाहरी कॉल सीमा में गिना जाता है, और समय बजट में इसे timeoutMs (न हो तो 10 सेकंड) एक बार के रूप में गिना जाता है (यह retry नहीं करता, इसलिए Http की तरह संख्या से गुणा नहीं होता)। इसे ResourceForEach के onEach के भीतर उपयोग किया जा सकता है (बहु-मेल भेजने का मानक रूप)।
{ "type": "EmailSend", "account": { "sys": { "id": "eml_orders" } },
  "to": "{ /order/fields/email/en-US }",
  "subject": "आपका ऑर्डर प्राप्त हो गया (ऑर्डर नंबर { /order/sys/id })",
  "body": "<p>आपका ऑर्डर प्राप्त हो गया है। शिपिंग शुरू होने पर हम आपको फिर सूचित करेंगे।</p>",
  "replyTo": "support@my-shop.example" }

वेरिएबल

SetVar

एक script-scoped परिवर्तनीय वेरिएबल घोषित या अपडेट करता है। इसे { /vars/<var> } के रूप में संदर्भित करें (JsonLogic में वेरिएबल घोषणा नहीं है, इसलिए इसे एक statement के रूप में दिया गया है)।

fieldविवरण
varवेरिएबल का नाम। { /vars/<var> } के रूप में संदर्भित
valueमान अभिव्यक्ति। यह संचय करने के लिए स्वयं को संदर्भित कर सकता है
{ "type": "SetVar", "var": "total", "value": 0 }
{ "type": "SetVar", "var": "total", "value": { "$+": [ "{ /vars/total }", "{ /row/qty }" ] } }   // संचय
{ "type": "SetVar", "var": "ids",   "value": { "$merge": [ "{ /vars/ids }", [ "{ /row/sys/id }" ] ] } }  // array में इकट्ठा करना

कैश

Cache

उसी Script के अपने, थोड़ी देर जीने वाले कैश को पढ़ता और लिखता है। बाहरी कॉल के परिणाम जैसे वे मान, जिन्हें हर बार दोबारा लाना व्यर्थ है, उन्हें कुछ सेकंड तक रखकर अगली कॉल में दोबारा उपयोग करने की जगह यही है। यह बाहरी कॉल नहीं है, इसलिए यह प्रति परिभाषा बाहरी कॉल की संख्या में नहीं गिना जाता, और समय बजट में इसका कोई घोषित समय भी नहीं होता।

fieldविवरण
action"Set" (लिखना), "Get" (पढ़ना), "Delete" (हटाना) में से एक (अनिवार्य)
keyकैश कुंजी (Cache Key) (अनिवार्य)। यह मान अभिव्यक्ति नहीं, बल्कि literal है (नीचे देखें)। अधिकतम 128 अक्षर, और इससे अधिक होने पर सहेजना अस्वीकृत हो जाता है
valueसहेजा जाने वाला मान (केवल Set)
ttlकैश के जीवित रहने का समय (केवल Set, सेकंड में)। 1 से 30 के बीच, और छोड़ने पर 5
defaultValueकैश किया गया डेटा न होने पर Get जिसे bind करेगा वह मान (केवल Get)। छोड़ने पर null
nameपरिणाम रखने वाला नाम। Get के लिए यह अनिवार्य है (पढ़े गए मान के जाने की कोई जगह न हो, तो उसे पढ़ने का कोई कारण नहीं)। Set सहेजे गए मान को और Delete हटाने का कार्य हुआ या नहीं इसे bind करता है, और इन दोनों में यह वैकल्पिक है
  • केवल उसी कार्य से संबंधित field लिखे जाते हैं। Get में ttl लिखने या Set में defaultValue लिखने पर सहेजना अस्वीकृत हो जाता है।
  • जो नहीं है और जो समाप्त हो चुका है, इनमें कोई भेद नहीं होता। दोनों में defaultValue bind होता है। जहाँ null सहेजा गया हो, वहाँ भी यही होता है।
  • संग्रह का दायरा वही एक Script है। उसी Space की दूसरी Script वही कैश कुंजी लिखे, तब भी वे एक-दूसरे का डेटा नहीं देख पातीं। उस Script को संशोधित करने या हटाने पर उस Script का सारा डेटा मिट जाता है।
  • key एक literal है। अनुरोध से आई कैश कुंजी से डेटा चुनने देने पर कॉलर यह तय करने लगता है कि क्या पढ़ा जाएगा, और जिस Script ने हर सदस्य के लिए एक-एक डेटा रखा है वह एक सदस्य का मान दूसरे सदस्य को दे बैठती है। इसलिए key में { /pointer } हो, तो वह न मान में बदलता है और न अक्षर-दर-अक्षर उपयोग होता है। सहेजना ही अस्वीकृत हो जाता है।
  • इसे पुनरावृत्ति के भीतर नहीं रखा जा सकता। Loop या ResourceForEach के block के भीतर Cache होने पर सहेजना अस्वीकृत हो जाता है। कारण यह है कि नीचे दी गई संख्या की ऊपरी सीमा पुनरावृत्ति के भीतर कोई भी प्रतिबंध नहीं बन पाती। हर पुनरावृत्ति पर एक-एक डेटा लिखा जाता है, इसलिए परिभाषा में लिखे statement की संख्या और वास्तव में उपयोग होने वाली कैश कुंजियों की संख्या मेल नहीं खातीं।
  • प्रति परिभाषा 5 तक रखे जा सकते हैं (नेस्टिंग सहित, कार्य से निरपेक्ष रूप से जोड़कर)। इससे अधिक होने पर सहेजना अस्वीकृत हो जाता है।
  • सहेजा जाने वाला मान 10,240 बाइट (10KiB) तक हो सकता है। इससे अधिक होने पर वह statement विफल हो जाता है (status 422)। यह अन्य रनटाइम विफलताओं जैसी ही है, इसलिए इसे Try/catch से स्थानीय रूप से संभाला जा सकता है।
// विनिमय दर को 30 सेकंड तक दोबारा उपयोग करना।
{ "type": "Cache", "action": "Get", "name": "cached", "key": "rates" }
 
// रखा हुआ मान हो, तो बिना बाहरी कॉल के उसे ज्यों-का-त्यों लौटा देना
{ "type": "If", "condition": { "!!": [ "{ /cached }" ] },
  "then": [ { "type": "Return", "value": "{ /cached }" } ] }
 
{ "type": "Http", "name": "fetched", "method": "GET", "url": "https://api.example.com/rates" }
{ "type": "Cache", "action": "Set", "key": "rates", "value": "{ /fetched/body }", "ttl": 30 }
{ "type": "Return", "value": "{ /fetched/body }" }
 
// रखे हुए मान को समाप्ति से पहले हटा देना
{ "type": "Cache", "action": "Delete", "key": "rates" }

मान पार्सिंग

ParseJson

JSON टेक्स्ट को उस मान में पार्स करता है जिसे वह दर्शाता है, और उसे एक नाम से बाइंड करता है। responseType: "Text" के साथ Http से मिली response body, payload में आई JSON string, या किसी field में string के रूप में रखी JSON को संभालने के लिए इसका उपयोग करें। यह बाहरी कॉल नहीं है, इसलिए यह बाहरी कॉल सीमा में नहीं गिना जाता, और समय बजट में इसका कोई घोषित समय भी नहीं होता।

fieldविवरण
nameपार्स किए गए मान को रखने वाला नाम (अनिवार्य)। अन्य statements में यह वैकल्पिक है, पर यहाँ अनिवार्य है। यह statement अपने परिणाम को बाइंड करने के अलावा कुछ नहीं करता, इसलिए नाम के बिना यह बिना किसी प्रभाव वाला statement बन जाता है
valueपार्स किया जाने वाला JSON टेक्स्ट (मान अभिव्यक्ति, अनिवार्य)। { /resp/body } की तरह पिछले चरण के मान की ओर संकेत करें, या JSON टेक्स्ट को literal के रूप में ज्यों-का-त्यों लिखें (literal के भीतर का { { पॉइंटर } टेम्पलेट के रूप में नहीं पढ़ा जाता)
  • परिणाम: पार्स किया गया मान स्वयं। ऑब्जेक्ट ऑब्जेक्ट रहता है, array array रहता है, और 42 या "a" जैसा एकल मान भी पार्स हो जाता है। इसके बाद { /<name>/... } से उसके भीतर संकेत करें।
  • पहले से पार्स किया गया मान आने पर वह जैसा है वैसा बाइंड होता है। जब value किसी ऐसे मान में resolve होता है जो string नहीं है, तो पार्स करने के लिए कोई टेक्स्ट नहीं होता, इसलिए वह मान जैसा है वैसा बाइंड कर दिया जाता है।
  • पार्स किए गए टेक्स्ट के भीतर का { /pointer } दोबारा हल नहीं किया जाता। बाहर से मिली string में { /payload/... } जैसा एक्सप्रेशन हो, तो भी वह मान से नहीं बदला जाता और टेक्स्ट ही रहता है।
  • null के दो मामलों में फ़र्क है। पार्स किया जाने वाला टेक्स्ट केवल null शब्द हो तो यह सामान्य है और परिणाम भी null होता है। लेकिन value जिस जगह की ओर संकेत करता है वह खाली हो, यानी कोई मान ही न हो, तो पार्स करने के लिए कुछ नहीं होता और statement विफल हो जाता है।
  • विफलता: जब value बिना मान या केवल खाली स्थान में resolve होता है, और जब टेक्स्ट JSON नहीं होता। इसे अन्य रनटाइम विफलताओं की तरह Try/catch से संभालें; त्रुटि संदेश में वह टेक्स्ट भी आता है जिसे पार्स करने की कोशिश हुई थी।
  • यह प्रति परिभाषा statement संख्या में 1 गिना जाता है, पर बाहरी कॉल की सीमा या SetVar की ऊपरी सीमा से इसका कोई संबंध नहीं है।
// 1) JSON न देने वाली API: Text से लेकर पार्स करना
{ "type": "Http", "method": "GET", "url": "https://api.partner.example/v1/quote",
  "responseType": "Text", "name": "resp" },
{ "type": "ParseJson", "name": "quote", "value": "{ /resp/body }" },
 
// 2) payload में आई JSON string को पार्स करना
{ "type": "ParseJson", "name": "spec", "value": "{ /payload/fields/specJson }" }

हस्ताक्षर सत्यापन और टेक्स्ट प्रोसेसिंग

ये वे statements हैं जो भुगतान प्रदाता द्वारा webhook से भेजे गए हस्ताक्षर की पुष्टि करते हैं, और उस हस्ताक्षर को जिस string में लपेटकर भेजा गया है उसे खोलते हैं। तीनों बाहरी कॉल नहीं बल्कि गणना हैं, इसलिए ये बाहरी कॉल सीमा में नहीं गिने जाते और समय बजट में इनका कोई घोषित समय भी नहीं होता; तथा इनमें कोई डेटा वाली जगह न होने से ये $ उपसर्ग नियम से निरपेक्ष हैं। तीनों को संयोजित करने वाला पूर्ण उदाहरण कुकबुक में webhook हस्ताक्षर सत्यापन में है।

तीनों statements में resolve हुए मान की लंबाई की एक ऊपरी सीमा है। यह एक्सप्रेशन की लंबाई नहीं, बल्कि उस एक्सप्रेशन द्वारा इंगित मान की लंबाई है ({ /rawPayload } के सोलह अक्षर कई दसियों KB की ओर संकेत करते हैं), और सीमा पार होने पर निष्पादन विफल हो जाता है, जिसे Try से संभाला जा सकता है। ठोस मान मान की लंबाई की ऊपरी सीमा में एक जगह दिए गए हैं।

Signature

प्राप्त हस्ताक्षर कोड secret से बने कोड के समान है या नहीं, यह जाँचकर उस उत्तर को एक Boolean मान के रूप में bind करता है। भुगतान प्रदाता (PG·MoR) webhook से जो हस्ताक्षर भेजते हैं, उनका सत्यापन इसी statement से किया जाता है।

fieldविवरण
nameसत्यापन का परिणाम रखने वाला नाम (अनिवार्य)। { /<name> } true या false होता है। सत्यापन करके परिणाम का उपयोग न करना सत्यापन न करने के बराबर है, इसलिए इसे छोड़ा नहीं जा सकता
algorithmकोड बनाने वाला hash (अनिवार्य)। SHA1·SHA256·SHA384·SHA512
secretसामने वाले पक्ष के साथ साझा की गई गुप्त key (मान अभिव्यक्ति, अनिवार्य)
secretEncodingsecret किस रूप में लिखी गई है। Utf8 (डिफ़ॉल्ट, टेक्स्ट key)·Hex·Base64। hex या base64 में जारी हुई key को टेक्स्ट के रूप में रखने पर वह दूसरी key बन जाती है, जिससे देखने में विश्वसनीय लगने वाला कोड बनता है पर वह कभी मेल नहीं खाता
valueवह संदेश जिस पर कोड की गणना होगी (मान अभिव्यक्ति, अनिवार्य)। यह सामने वाले पक्ष द्वारा हस्ताक्षरित bytes से अक्षर-दर-अक्षर समान होना चाहिए, इसलिए आमतौर पर यह { /rawPayload } होता है, या प्रदाता द्वारा header में साथ भेजी गई timestamp को उसके आगे जोड़ा हुआ रूप
expectedकॉलर द्वारा भेजा गया कोड (मान अभिव्यक्ति, अनिवार्य)। उदाहरण: { /headers/x-signature }
  • परिणाम: Boolean। इसके बाद If की शर्त में { /<name> } को ज्यों-का-त्यों लिखें।
  • value में पार्स किया गया /payload नहीं, बल्कि /rawPayload लिखें। पार्स किए गए payload को दोबारा string बनाने पर space, अंकों का लेखन और escape सामान्यीकृत हो जाते हैं, जिससे वह सामने वाले पक्ष द्वारा हस्ताक्षरित bytes पर नहीं लौटता (Context रूट)।
  • आउटपुट का रूप निर्दिष्ट करने वाला कोई field नहीं है। algorithm कोड की byte लंबाई तय कर देता है और समान लंबाई के hex एवं base64 की string लंबाई कभी नहीं टकराती, इसलिए सामने वाले पक्ष ने किस रूप में भेजा है यह बताए बिना भी engine bytes को पुनःप्राप्त कर लेता है। hex के छोटे-बड़े अक्षर, तथा base64 और base64url (padding हो या न हो) में भी इसी कारण भेद नहीं किया जाता।
  • विफलता और false में यह भेद है कि वह मान कौन देता है।
    • expected न हो या कोड मेल न खाए, तो परिणाम केवल false होता है, यह विफलता नहीं है। header न होने और कोड न मिलने की सूचना अलग-अलग देने पर भेजने वाले पक्ष को यह पता चल जाएगा कि इनमें से क्या गलत था।
    • value खाली हो, तो खाली संदेश पर गणना होती है। खाली body भी हस्ताक्षर का विषय है।
    • secret न हो, या वह secretEncoding में घोषित रूप में न हो, तो यह विफलता है। तीनों में लेखक का अपना इनपुट केवल यही है। विफलता संदेश में secret और value शामिल नहीं होते।
  • value की ऊपरी सीमा 65,536 अक्षर है (resolve हुए मान के आधार पर)। यह वास्तविक प्रदाताओं द्वारा भेजी जाने वाली webhook body के आकार के अनुरूप रखा गया मान है।
  • तुलना यह constant-time में तय करती है कि मान समान हैं या नहीं। आगे के कुछ bytes मेल खाए थे या नहीं, यह प्रतिक्रिया के समय से बाहर नहीं छनता।
  • secret एन्क्रिप्ट होकर संग्रहीत नहीं होती। Http header के secret: true (एन्क्रिप्ट होकर संग्रहीत, भेजने से ठीक पहले डिक्रिप्ट) के विपरीत यह परिभाषा में लिखे रूप में ही बनी रहती है, इसलिए जो भूमिका उस Script को पढ़ सकती है उसे यह मान दिख जाता है। सदस्य (ServiceUser) Script की परिभाषा नहीं पढ़ सकते (रचना और पठन केवल CMA पर होते हैं)।
// पूरी body पर हस्ताक्षर करने वाला प्रदाता
{ "type": "Signature", "name": "verified", "algorithm": "SHA256",
  "secret": "whsec_9f2c1b7ae4", "value": "{ /rawPayload }",
  "expected": "{ /headers/x-webhook-signature }" }
 
// key को base64 में जारी करने वाला प्रदाता
{ "type": "Signature", "name": "verified", "algorithm": "SHA256",
  "secret": "aGVsbG8td2VlZ2xvbw==", "secretEncoding": "Base64",
  "value": "{ /rawPayload }", "expected": "{ /headers/webhook-signature }" }

Hash

value का डाइजेस्ट बनाकर उसे encoding द्वारा तय रूप की string के रूप में bind करता है। इसका उपयोग HMAC के बजाय "कुछ field और गुप्त key को जोड़कर SHA256 की गणना" करने वाली हस्ताक्षर स्कीम को पुनःनिर्मित करने के लिए किया जाता है।

fieldविवरण
nameडाइजेस्ट रखने वाला नाम (अनिवार्य)
algorithmMD5·SHA1·SHA256·SHA384·SHA512 (अनिवार्य)। MD5 उन पुरानी स्कीमों को पुनःनिर्मित करने के लिए है जो उसकी अपेक्षा करती हैं, यह नए बनाए जाने वाले हस्ताक्षर के लिए चुना जाने वाला मान नहीं है
valueडाइजेस्ट किया जाने वाला संदेश (मान अभिव्यक्ति, अनिवार्य)
encodingपरिणाम का रूप। Hex (डिफ़ॉल्ट)·HexUpper·Base64·Base64Url
  • इसमें secret field नहीं है। हर स्कीम में key आगे, पीछे या बीच में आ सकती है, इसलिए key को सीधे value के भीतर लिखना ही सभी जगहों को व्यक्त कर पाता है।
  • परिणाम: string। सामने वाले पक्ष द्वारा भेजे गए कोड से तुलना करते समय { "==": [ "{ /<name> }", "{ /headers/... }" ] } लिखें। यह तुलना Signature की constant-time तुलना से भिन्न, एक सामान्य समता तुलना है।
  • value बिना मान के resolve हो या उसमें केवल खाली स्थान हो, तो यह विफलता है (क्योंकि यह लेखक का अपना एक्सप्रेशन है)।
  • value की ऊपरी सीमा 128 अक्षर है। यह जुड़े हुए कुछ field रखने की जगह है, इसलिए Signature से बहुत संकरी है। पूरी webhook body पर गणना करनी हो, तो Signature का उपयोग करें।
// SHA256(ऑर्डर नंबर + राशि + merchantKey) को बड़े अक्षरों वाले hex में
{ "type": "Hash", "name": "expectedSign", "algorithm": "SHA256", "encoding": "HexUpper",
  "value": "{ /payload/orderId }{ /payload/amount }9f2c1b7ae4" }

Regex

pattern को value पर लागू करके mode द्वारा माँगी गई चीज़ bind करता है। मान अभिव्यक्ति में string को काटने का कोई साधन नहीं है (केवल जोड़ने वाला cat और समावेश देखने वाला in है), इसलिए t=…,v1=… की तरह एक ही header में कई मान लपेटकर आने पर उसे खोलने के लिए इस statement का उपयोग किया जाता है।

fieldविवरण
nameपरिणाम रखने वाला नाम (अनिवार्य)। Capture में element को { /<name>/1 } से इंगित किया जाता है
mode"Match" मिलान हुआ या नहीं यह Boolean के रूप में, और "Capture" पहले मिलान को array के रूप में bind करता है (अनिवार्य)
patternregex (अनिवार्य)। यह मान अभिव्यक्ति नहीं, बल्कि literal है (नीचे देखें)। flag को (?i) की तरह pattern के भीतर लिखा जाता है। अधिकतम 128 अक्षर, और इससे अधिक होने पर सहेजना अस्वीकृत हो जाता है
valueजिस टेक्स्ट पर pattern लागू होगा (मान अभिव्यक्ति, अनिवार्य)। resolve हुआ मान 10,240 अक्षर (10KiB) से अधिक होने पर निष्पादन विफल हो जाता है
  • परिणाम: Match में Boolean, और Capture में array या null। array में index 0 पूरा मिलान होता है और 1 से capture group शुरू होते हैं, तथा जो group भाग नहीं लेते वे null होते हैं (खाली string नहीं, क्योंकि वह मिलान हुआ माना जाएगा)। pattern कहीं भी न दिखे, तो Capture खाली array नहीं बल्कि null होता है।
  • दोनों mode यही पूछते हैं कि "pattern कहीं दिखता है या नहीं"। पूरा टेक्स्ट pattern के समान होना चाहिए, तो ^…$ से उसे बाँध दें। Match से जाँचकर Capture से निकालने वाले दोनों statements कभी अलग-अलग उत्तर न दें, इसलिए प्रश्न को समान रखा गया है।
  • इस engine में मान अभिव्यक्ति न होने वाले दो field में से एक pattern है (दूसरा Cache की key है)। अनुरोध से आए pattern को ज्यों-का-त्यों चलाने पर कॉलर यह चुनने लगता है कि कौन-सी अभिव्यक्ति चलेगी, और regex का backtracking उसे सेवा-अवरोध (denial of service) का साधन बना देता है। इसलिए pattern के भीतर का { /pointer } भी मान में नहीं बदलता, बल्कि अक्षर-दर-अक्षर pattern का हिस्सा बन जाता है।
  • pattern निष्पादन शुरू होते समय पूरी परिभाषा में एक बार compile होता है। वह Loop या ResourceForEach के भीतर हो, तो भी हर पुनरावृत्ति पर दोबारा compile नहीं होता, और अनुपयोगी pattern पहला statement कुछ भी करने से पहले ही विफल हो जाता है (Try से संभाला जा सकता है)।
// "t=1492774577,v1=<64 अक्षर hex>" को खोलकर { /sig/1 } = timestamp, { /sig/2 } = कोड
{ "type": "Regex", "name": "sig", "mode": "Capture",
  "pattern": "^t=(\\d+),v1=([0-9a-f]{64})$", "value": "{ /headers/x-provider-signature }" }
 
// केवल प्रारूप की जाँच
{ "type": "Regex", "name": "isOrderId", "mode": "Match",
  "pattern": "^ORD-\\d{8}-\\d{4}$", "value": "{ /payload/orderId }" }

नियंत्रण प्रवाह

If

एक शर्तीय शाखा। condition JsonLogic है, और सत्य/असत्य सत्य और असत्य का निर्धारण नियमों का पालन करते हैं।

fieldविवरण
conditionJsonLogic (boolean के रूप में मूल्यांकित)
thentrue होने पर चलाने के लिए Statement array
else(वैकल्पिक) false होने पर चलाने के लिए Statement array
{ "type": "If",
  "condition": { "<": [ "{ /wallet/fields/balance/en-US }", "{ /payload/fields/cost }" ] },
  "then": [ { "type": "Return", "value": { "ok": false, "reason": "insufficient credit" } } ],
  "else": [ /* ... */ ] }

Loop

पुनरावृत्ति। एक mode चुनें: over (foreach), while (शर्त), या for (counted)। किसी भी mode में engine पुनरावृत्ति की एक ऊपरी सीमा लागू करता है (अनंत loop से बचने के लिए)। यह ऊपरी सीमा maxIterations से घोषित की जाती है, और न लिखने पर प्लेटफ़ॉर्म की ऊपरी सीमा लागू होती है। body के भीतर बाहरी कॉल (Http·EmailSend) और Media फ़ाइल ingest भी रखी जा सकती है, और बाहरी कॉल statement निष्पादन के समय हर पुनरावृत्ति पर वास्तव में कॉल किए जाते हैं। प्रति परिभाषा बाहरी कॉल की अधिकतम संख्या की सीमा ज्यों-की-त्यों लागू रहती है।

समय बजट में यह गुणा के रूप में आता है। यह statement जो समय घोषित करता है वह body द्वारा घोषित समय को maxIterations (न हो तो 10,000) से गुणा किया हुआ मान है (समय बजट)। body में कोई बाहरी कॉल न हो, तो घोषित समय 0 होता है, इसलिए 30 सेकंड का मूल बजट ही वास्तविक सीमा है।

fieldविवरण
overforeach: एक मान अभिव्यक्ति जो array के रूप में resolve होता है
whileशर्त: JsonLogic (true रहने तक दोहराता है)
forcounted: { "from", "to", "step"? }from से to तक समावेशी; step डिफ़ॉल्ट 1
maxIterationsअधिकतम पुनरावृत्ति संख्या (वैकल्पिक)। न लिखने पर प्लेटफ़ॉर्म की ऊपरी सीमा 10,000 लागू होती है, और उससे बड़ा मान सहेजते समय अस्वीकृत हो जाता है
name(वैकल्पिक) वर्तमान आइटम (foreach) या index (while·for) को bind करने के लिए नाम ({ /<name> })
bodyloop body के लिए Statement array
// foreach
{ "type": "Loop", "over": "{ /payload/fields/items }", "name": "item", "maxIterations": 100,
  "body": [ { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_item" } },
             "fields": { "name": { "en-US": "{ /item/name }" } } } ] }
 
// while
{ "type": "Loop", "while": "{ /vars/hasMore }", "maxIterations": 1000, "body": [ /* ... */ ] }
 
// counted (1..10 step 2)
{ "type": "Loop", "for": { "from": 1, "to": 10, "step": 2 }, "name": "i", "maxIterations": 100, "body": [ /* ... */ ] }

Parallel

शाखाओं को समवर्ती रूप से चलाता है और उनके join होने के बाद आगे बढ़ता है। शाखाओं के बीच reference संभव नहीं है (यदि कोई निर्भरता हो, तो उन्हें क्रमिक रूप से रखें)।

fieldविवरण
branchesStatement[][]। हर element एक शाखा है (statements का array)
{ "type": "Parallel", "branches": [
  [ { "type": "Http", "method": "GET", "url": "https://api.a.com/x", "name": "a" } ],
  [ { "type": "Http", "method": "GET", "url": "https://api.b.com/y", "name": "b" } ]
] }

Return

यह सामान्य प्रोग्रामिंग वाला return है। यह Script का परिणाम कॉल करने वाले को लौटाता है और उस बिंदु पर सामान्य रूप से समाप्त हो जाता है।

fieldविवरण
value(वैकल्पिक) लौटाने के लिए मान अभिव्यक्ति
isErrorडिफ़ॉल्ट falsetrue होने पर value response के error के रूप में लौटता है (अन्यथा return के रूप में)
statusCoderesponse status code। डिफ़ॉल्ट 200
  • यदि Return तक कभी नहीं पहुँचा जाता, तो कोई return मान नहीं होता। परिणाम लौटाने के लिए, value को स्पष्ट रूप से निर्दिष्ट करें।
  • चूँकि यह किसी अपवाद या throw नहीं, बल्कि एक सामान्य समाप्ति है, यह catch का लक्ष्य नहीं है (Try के भीतर भी यह पूरे Script को समाप्त कर देता है, पर finally फिर भी चलता है)।
  • guard को भी इसी statement से व्यक्त किया जाता है। If के then में Return रखने पर, शर्त का उल्लंघन होने पर वह मान लौटाता है और उसके बाद के statements नहीं चलाता। यह Return के कई उपयोगों में से एक है।
{ "type": "Return", "value": { "orderId": "{ /order/sys/id }", "status": "paid" }, "statusCode": 201 }
{ "type": "Return", "value": { "reason": "payment failed" }, "isError": true, "statusCode": 402 }

Try

अपवाद प्रबंधन।

fieldविवरण
bodyप्रयास करने के लिए Statement array
catch(वैकल्पिक) body विफल होने पर चलता है। /error पर { message } उजागर करता है (किस statement में विफलता हुई यह इसमें नहीं आता)
finally(वैकल्पिक) सफलता या विफलता की परवाह किए बिना हमेशा चलता है
  • यदि catch इसे संभाल लेता है, तो Script बाधित नहीं होता। केवल ऐसी विफलता जिसका कोई catch न हो, Script को बाधित करती है (क्षतिपूर्ति प्रयास सहित)।
  • क्या "विफलता" मानी जाती है, और क्षतिपूर्ति (compensation) की सीमाएँ, इनका वर्णन निष्पादन सिमेंटिक्स, बाधाएँ, सुरक्षा में किया गया है।
{ "type": "Try",
  "body":    [ { "type": "Http", "method": "POST", "url": "https://primary.api/gen", "name": "resp" },
               { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_result" } },
                 "fields": { "text": { "en-US": "{ /resp/body/text }" } } } ],
  "catch":   [ { "type": "ResourceCreate", "resource": "Content", "contentType": { "sys": { "id": "ct_result" } },
                 "fields": { "text": { "en-US": "Generation failed" }, "error": { "en-US": "{ /error/message }" } } } ],
  "finally": [ /* हमेशा चलता है */ ] }