├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── npmpublish.yml │ └── pull_request.yml ├── .gitignore ├── .nvmrc ├── .prettierrc ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── dist └── main.js ├── package-lock.json ├── package.json ├── server-sdk.js ├── src ├── client-test.js ├── client.js ├── constants-test.js ├── constants.js ├── helpers │ ├── index.js │ ├── monday-api-helpers.js │ └── ui-helpers.js ├── index.js ├── monday-api-client │ ├── fetch.js │ ├── index.js │ ├── monday-api-client-test.js │ └── monday-api-client.js ├── server-test.js ├── server.js ├── services │ ├── background-tracking-service.js │ └── oauth-service.js └── tests │ └── helpers.js ├── ts-tests └── monday-sdk-js-module.test.ts ├── tsconfig.json ├── types ├── client-api.interface.ts ├── client-context.type.ts ├── client-data.interface.ts ├── client-execute.interface.ts ├── client-sdk.interface.ts ├── index.d.ts ├── server-sdk.interface.ts └── theme-config.type.ts └── webpack.config.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true, 7 | "amd": true, 8 | "mocha": true 9 | }, 10 | "extends": "eslint:recommended", 11 | "globals": { 12 | "Atomics": "readonly", 13 | "SharedArrayBuffer": "readonly", 14 | "__BUNDLE__": true 15 | }, 16 | "parserOptions": { 17 | "ecmaVersion": 2018 18 | }, 19 | "rules": {} 20 | } 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[Bug]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Steps to reproduce** 14 | 1. Embed the SDK in an app 15 | 2. Call `monday.api(...)` 16 | 3. See the request fails 17 | 4. ... 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Context and environment** 23 | What capabilities were you trying to use, on which browser/mobile device, what app was involved... 24 | 25 | **Additional information** 26 | Screenshots and any other pieces of information that may be relevant for us to try and reproduce the issue 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[Feature]" 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **What problem is this request going to solve?** 11 | If relevant, explain what issue this feature request is aimed at solving, or how will the SDK become better once this is implemented 12 | 13 | **What is the solution you'd like to see?** 14 | A clear and concise description of what you want to happen 15 | 16 | **Describe alternatives you've considered** 17 | Have you tried alternative methods to solve this issue? Did they fail completely, or were you able to achieve your goal in a suboptimal way? 18 | 19 | **Additional information** 20 | Add any other context or screenshots about the feature request here 21 | -------------------------------------------------------------------------------- /.github/workflows/npmpublish.yml: -------------------------------------------------------------------------------- 1 | name: Node.js Package 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: [created] 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v1 14 | with: 15 | node-version: 16 16 | #- run: npm ci 17 | #- run: npm tests 18 | 19 | publish-npm: 20 | needs: build 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v2 24 | - uses: actions/setup-node@v1 25 | with: 26 | node-version: 16 27 | registry-url: https://registry.npmjs.org/ 28 | - run: npm ci 29 | - run: npm publish 30 | env: 31 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 32 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | on: pull_request 2 | name: Pull Request 3 | jobs: 4 | test: 5 | name: Run tests 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@master 9 | - name: Use Node.js 10 | uses: actions/setup-node@v1 11 | with: 12 | node-version: "16.x" 13 | - run: npm install 14 | - run: npm run compile-types 15 | - run: npm run test 16 | - run: npm run lint 17 | - run: npm run style-check 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### IntelliJ ### 2 | .idea 3 | out/ 4 | logs 5 | *.log 6 | npm-debug.log* 7 | yarn-debug.log* 8 | yarn-error.log* 9 | coverage 10 | node_modules/ 11 | .npm 12 | .eslintcache 13 | *.tgz 14 | .env 15 | .env.test 16 | .cache 17 | tmp/ 18 | temp/ 19 | .vscode/* 20 | .history 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 16.13 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120 3 | } 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ### Write me 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 monday.com LTD 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # monday.com Apps framework SDK for JavaScript 5 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/mondaycom/monday-sdk-js/blob/master/LICENSE)   [![npm version](https://img.shields.io/npm/v/monday-sdk-js.svg?style=flat)](https://www.npmjs.com/package/monday-sdk-js)   [![npm](https://img.shields.io/npm/dm/monday-sdk-js)](https://www.npmjs.com/package/monday-sdk-js)   [![jsDelivr hits (npm)](https://img.shields.io/jsdelivr/npm/hm/monday-sdk-js)](https://www.jsdelivr.com/package/npm/monday-sdk-js) 6 | 7 | 8 | The monday.com SDK provides a toolset for application developers to build features and solutions on top of the monday.com Work OS platform. You'll find this SDK useful if you want to: 9 | 10 | - Access monday.com account data from your application, by utilizing the GraphQL client 11 | - Build Board Views & Dashboard Widgets that extend the monday.com UI 12 | - Build Integrations & Automations using your own external services and business logic 13 | 14 | The SDK contains methods for server-side and client-side application development. Client-side capabilities assume a valid user session is present (and can seamlessly act on behalf of that user), while server-side methods can be used to access monday.com features using explicit credentials but without any client-side code. 15 | 16 | ## Usage 17 | 18 | ### Using as an `npm` module 19 | Install the SDK as a module: 20 | 21 | `npm install monday-sdk-js --save` 22 | 23 | Then import into your project: 24 | ```js 25 | import mondaySdk from "monday-sdk-js"; 26 | const monday = mondaySdk(); 27 | ``` 28 | 29 | 30 | ### As a ` 35 | 36 | ``` 37 | and then initialize the SDK anywhere in the page by declaring: 38 | 39 | ```js 40 | const monday = window.mondaySdk() 41 | ``` 42 | 43 | ## Docs 44 | 45 | To get started, check out the [SDK Documentation](https://developer.monday.com/apps/docs/introduction-to-the-sdk) 46 | -------------------------------------------------------------------------------- /dist/main.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function n(i){if(t[i])return t[i].exports;var r=t[i]={i:i,l:!1,exports:{}};return e[i].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(i,r,function(t){return e[t]}.bind(null,r));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=4)}([function(e,t,n){e.exports=n(6)},function(e,t,n){(function(t){function n(){return void 0!==t&&!1}const i=(e,i)=>n()&&"undefined"!==t.env[e]&&void 0!==t.env[e]?t.env[e]:i,r=()=>i("MONDAY_COM_PROTOCOL","https"),s=()=>i("MONDAY_COM_DOMAIN","monday.com"),o=()=>i("MONDAY_OAUTH_SUBDOMAIN","auth."),a=()=>`${r()}://${i("MONDAY_SUBDOMAIN_API","api.")}${s()}/v2`;e.exports={get MONDAY_DOMAIN(){return s()},get MONDAY_PROTOCOL(){return r()},get MONDAY_API_URL(){return a()},get MONDAY_OAUTH_URL(){return`${r()}://${o()}${s()}/oauth2/authorize`},get MONDAY_OAUTH_TOKEN_URL(){return`${r()}://${o()}${s()}/oauth2/token`}}}).call(this,n(7))},function(e,t){const n="undefined"!=typeof window&&void 0!==window.document;e.exports={convertToArrayIfNeeded:e=>Array.isArray(e)?e:[e],isBrowser:n}},function(e,t){e.exports={logWarnings:e=>{const t=e&&e.extensions&&e.extensions.warnings;return t&&Array.isArray(t)?(t.forEach(e=>{if(e&&e.message)try{const t=e.locations&&e.locations.map(e=>`line ${e.line}, column ${e.column}`).join("; "),n=e.path&&e.path.join(" → ");let i=e.message;i=i.replace(/\.$/,""),i=i.charAt(0).toLowerCase()+i.slice(1);const r=["[monday API]",`${n}:`,i,t&&`@ ${t}`,e.extensions?["\n\nAdditional details:",e.extensions]:void 0].flat().filter(Boolean);console.warn(...r)}catch(t){e&&console.warn("[monday API] Warning:",e)}}),e):e}}},function(e,t,n){var i,r;const{isBrowser:s}=n(2),o=n(s?5:13);"undefined"!=typeof self&&self,void 0===(r="function"==typeof(i=function(){return window.mondaySdk=o,o})?i.call(t,n,t,e):i)||(e.exports=r)},function(e,t,n){const i=n(0),{MONDAY_OAUTH_URL:r}=n(1),{convertToArrayIfNeeded:s}=n(2),{initScrollHelperIfNeeded:o}=n(10),{initBackgroundTracking:a}=n(11),{logWarnings:c}=n(3),l=[],u="v2",d="instance";class h{constructor(e={}){this._clientId=e.clientId,this._apiToken=e.apiToken,this._apiVersion=e.apiVersion,this.listeners={},this.setClientId=this.setClientId.bind(this),this.setToken=this.setToken.bind(this),this.setApiVersion=this.setApiVersion.bind(this),this.api=this.api.bind(this),this.listen=this.listen.bind(this),this.get=this.get.bind(this),this.set=this.set.bind(this),this.execute=this.execute.bind(this),this.oauth=this.oauth.bind(this),this._receiveMessage=this._receiveMessage.bind(this),this.storage={setItem:this.setStorageItem.bind(this),getItem:this.getStorageItem.bind(this),deleteItem:this.deleteStorageItem.bind(this),instance:{setItem:this.setStorageInstanceItem.bind(this),getItem:this.getStorageInstanceItem.bind(this),deleteItem:this.deleteStorageInstanceItem.bind(this)}},window.addEventListener("message",this._receiveMessage,!1),e.withoutScrollHelper||o(),a(this)}setClientId(e){this._clientId=e}setToken(e){this._apiToken=e}setApiVersion(e){this._apiVersion=e}api(e,t={}){const n={query:e,variables:t.variables},r=t.token||this._apiToken,s=t.apiVersion||this._apiVersion;let o;return o=r?i.execute(n,r,{apiVersion:s}):this._localApi("api",{params:n,apiVersion:s}).then(e=>e.data),o.then(c)}listen(e,t,n){const i=s(e),r=[];return i.forEach(e=>{r.push(this._addListener(e,t)),this._localApi("listen",{type:e,params:n})}),()=>{r.forEach(e=>e())}}get(e,t){return this._localApi("get",{type:e,params:t})}set(e,t){return this._localApi("set",{type:e,params:t})}execute(e,t){return this._localApi("execute",{type:e,params:t})}track(e,t){return this.execute("track",{name:e,data:t})}oauth(e={}){const t=e.clientId||this._clientId;if(!t)throw new Error("clientId is required");const n=`${e.mondayOauthUrl||r}?client_id=${t}`;window.location=n}setStorageItem(e,t,n={}){return this._localApi("storage",{method:"set",key:e,value:t,options:n,segment:u})}getStorageItem(e,t={}){return this._localApi("storage",{method:"get",key:e,options:t,segment:u})}deleteStorageItem(e,t={}){return this._localApi("storage",{method:"delete",key:e,options:t,segment:u})}setStorageInstanceItem(e,t,n={}){return this._localApi("storage",{method:"set",key:e,value:t,options:n,segment:d})}getStorageInstanceItem(e,t={}){return this._localApi("storage",{method:"get",key:e,options:t,segment:d})}deleteStorageInstanceItem(e,t={}){return this._localApi("storage",{method:"delete",key:e,options:t,segment:d})}_localApi(e,t){return new Promise((i,r)=>{const s=this._generateRequestId(),o=this._clientId,a=n(12).version;window.parent.postMessage({method:e,args:t,requestId:s,clientId:o,version:a},"*");const c=this._addListener(s,e=>{if(c(),e.errorMessage){const t=new Error(e.errorMessage);t.data=e.data,r(t)}else i(e)})})}_receiveMessage(e){const{method:t,type:n,requestId:i}=e.data,r=this.listeners[t]||l,s=this.listeners[n]||l,o=this.listeners[i]||l;let a=new Set([...r,...s,...o]);a&&a.forEach(t=>{try{t(e.data)}catch(e){console.error("Message callback error: ",e)}})}_addListener(e,t){return this.listeners[e]=this.listeners[e]||new Set,this.listeners[e].add(t),()=>{this.listeners[e].delete(t),0===this.listeners[e].size&&delete this.listeners[e]}}_generateRequestId(){return Math.random().toString(36).substring(2,9)}_removeEventListener(){window.removeEventListener("message",this._receiveMessage,!1)}_clearListeners(){this.listeners=[]}}e.exports=function(e={}){return new h(e)}},function(e,t,n){const{MONDAY_API_URL:i,MONDAY_OAUTH_TOKEN_URL:r}=n(1),s=n(8);e.exports={execute:async function(e,t,n={}){if(!t&&n.url!==r)throw new Error("Token is required");const o=`${n.url||i}${n.path||""}`;let a=await function(e,t,n,i={}){return s.nodeFetch(e,{method:i.method||"POST",body:JSON.stringify(t||{}),headers:{Authorization:n,"Content-Type":"application/json",...i.apiVersion?{"API-Version":i.apiVersion}:{}}})}(o,e,t,n);const c=a.status,l=a.headers.get("content-type");if(!l||!l.includes("application/json")){if(504===c)throw new Error("Received timeout from monday.com's GraphQL API");const e=await a.text();throw new Error(e)}try{return await a.json()}catch(e){throw new Error("Could not parse JSON from monday.com's GraphQL API response")}},COULD_NOT_PARSE_JSON_RESPONSE_ERROR:"Could not parse JSON from monday.com's GraphQL API response",TOKEN_IS_REQUIRED_ERROR:"Token is required",API_TIMEOUT_ERROR:"Received timeout from monday.com's GraphQL API"}},function(e,t){var n,i,r=e.exports={};function s(){throw new Error("setTimeout has not been defined")}function o(){throw new Error("clearTimeout has not been defined")}function a(e){if(n===setTimeout)return setTimeout(e,0);if((n===s||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:s}catch(e){n=s}try{i="function"==typeof clearTimeout?clearTimeout:o}catch(e){i=o}}();var c,l=[],u=!1,d=-1;function h(){u&&c&&(u=!1,c.length?l=c.concat(l):d=-1,l.length&&p())}function p(){if(!u){var e=a(h);u=!0;for(var t=l.length;t;){for(c=l,l=[];++d1)for(var n=1;n{if(n)return;n=!0;const t=()=>{e.track("ping")};t(),setInterval(t,3e5)}}},function(e){e.exports=JSON.parse('{"name":"monday-sdk-js","version":"0.4.12","private":false,"repository":"https://github.com/mondaycom/monday-sdk-js","main":"src/index.js","types":"types/index.d.ts","author":"talharamati ","license":"MIT","files":["LICENSE","README.md","dist/","src/","types/","server-sdk.js"],"dependencies":{"node-fetch":"^2.6.0"},"devDependencies":{"@babel/cli":"^7.6.0","@babel/core":"^7.6.0","@babel/node":"^7.6.1","@babel/preset-env":"^7.6.0","@babel/preset-react":"^7.0.0","@babel/register":"^7.6.0","@types/source-map":"^0.5.2","babel-loader":"^8.0.6","chai":"^4.2.0","eslint":"^6.8.0","jsdom":"^16.2.0","mocha":"^7.1.0","prettier":"^1.19.1","sinon":"^9.0.0","sinon-chai":"^3.5.0","typescript":"^4.9.5","webpack":"^4.38.0","webpack-cli":"^3.3.6","webpack-dev-server":"^3.7.2"},"scripts":{"start":"webpack-dev-server","build":"webpack --mode=production --env.WEBPACK_BUILD=true","test":"mocha \'./src/**/*-test.js\'","test:watch":"mocha \'./src/**/*-test.js\' --watch","precommit":"yarn lint && yarn style-check","lint":"eslint \'./src/**/*.*\'","style-check":"prettier --check \'./src/**/*.js\'","style-fix":"prettier --write \'./src/**/*.js\'","compile-types":"tsc --noEmit"}}')},function(e,t,n){const{logWarnings:i}=n(3),r=n(0),{oauthToken:s}=n(14);class o{constructor(e={}){this._token=e.token,this._apiVersion=e.apiVersion,this.setToken=this.setToken.bind(this),this.setApiVersion=this.setApiVersion.bind(this),this.api=this.api.bind(this)}setToken(e){this._token=e}setApiVersion(e){this._apiVersion=e}api(e,t={}){const n={query:e,variables:t.variables},s=t.token||this._token,o=t.apiVersion||this._apiVersion;if(!s)throw new Error("Should send 'token' as an option or call mondaySdk.setToken(TOKEN)");return r.execute(n,s,{apiVersion:o}).then(i)}oauthToken(e,t,n){return s(e,t,n)}}e.exports=function(e={}){return new o(e)}},function(e,t,n){const{execute:i}=n(0),{MONDAY_OAUTH_TOKEN_URL:r}=n(1);e.exports={oauthToken:(e,t,n)=>i({code:e,client_id:t,client_secret:n},null,{url:r})}}]); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "monday-sdk-js", 3 | "version": "0.5.6", 4 | "private": false, 5 | "repository": "https://github.com/mondaycom/monday-sdk-js", 6 | "main": "src/index.js", 7 | "types": "types/index.d.ts", 8 | "author": "talharamati ", 9 | "license": "MIT", 10 | "files": [ 11 | "LICENSE", 12 | "README.md", 13 | "dist/", 14 | "src/", 15 | "types/", 16 | "server-sdk.js" 17 | ], 18 | "dependencies": { 19 | "node-fetch": "^2.6.0" 20 | }, 21 | "devDependencies": { 22 | "@babel/cli": "^7.6.0", 23 | "@babel/core": "^7.6.0", 24 | "@babel/node": "^7.6.1", 25 | "@babel/preset-env": "^7.6.0", 26 | "@babel/preset-react": "^7.0.0", 27 | "@babel/register": "^7.6.0", 28 | "@types/source-map": "^0.5.2", 29 | "babel-loader": "^8.0.6", 30 | "chai": "^4.2.0", 31 | "eslint": "^6.8.0", 32 | "jsdom": "^16.2.0", 33 | "mocha": "^7.1.0", 34 | "prettier": "^1.19.1", 35 | "sinon": "^9.0.0", 36 | "sinon-chai": "^3.5.0", 37 | "typescript": "^4.9.5", 38 | "webpack": "^4.38.0", 39 | "webpack-cli": "^3.3.6", 40 | "webpack-dev-server": "^3.7.2" 41 | }, 42 | "scripts": { 43 | "start": "webpack-dev-server", 44 | "build": "webpack --mode=production --env.WEBPACK_BUILD=true", 45 | "test": "mocha './src/**/*-test.js'", 46 | "test:watch": "mocha './src/**/*-test.js' --watch", 47 | "precommit": "yarn lint && yarn style-check", 48 | "lint": "eslint './src/**/*.*'", 49 | "style-check": "prettier --check './src/**/*.js'", 50 | "style-fix": "prettier --write './src/**/*.js'", 51 | "compile-types": "tsc --noEmit" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /server-sdk.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./src/server.js"); 2 | -------------------------------------------------------------------------------- /src/client-test.js: -------------------------------------------------------------------------------- 1 | const { sinon, expect } = require("./tests/helpers"); 2 | const initMondaySdk = require("./client"); 3 | 4 | describe("Monday Client Test", () => { 5 | const clientId = "clientId"; 6 | const apiToken = "123456789"; 7 | 8 | let mondayClient; 9 | let options = { 10 | clientId, 11 | apiToken 12 | }; 13 | let clock; 14 | beforeEach(() => { 15 | clock = sinon.useFakeTimers(); 16 | mondayClient = initMondaySdk(options); 17 | }); 18 | 19 | afterEach(() => { 20 | clock.restore(); 21 | mondayClient._removeEventListener(); 22 | mondayClient._clearListeners(); 23 | }); 24 | describe("init", () => { 25 | it("should set client id", () => { 26 | expect(mondayClient._clientId).to.equal(clientId); 27 | }); 28 | 29 | it("should set the token id", () => { 30 | expect(mondayClient._apiToken).to.equal(apiToken); 31 | }); 32 | }); 33 | 34 | describe("setters", () => { 35 | it("should set client id correctly", () => { 36 | const newId = "newId"; 37 | const { setClientId } = mondayClient; 38 | 39 | setClientId(newId); 40 | 41 | expect(mondayClient._clientId).to.equal(newId); 42 | }); 43 | 44 | it("should set the api token correctly", () => { 45 | const newToken = "new toekn"; 46 | const { setToken } = mondayClient; 47 | 48 | setToken(newToken); 49 | 50 | expect(mondayClient._apiToken).to.equal(newToken); 51 | }); 52 | 53 | it("should set the version correctly", () => { 54 | const newVersion = "2023-01"; 55 | const { setApiVersion } = mondayClient; 56 | 57 | setApiVersion(newVersion); 58 | 59 | expect(mondayClient._apiVersion).to.eq(newVersion); 60 | }); 61 | }); 62 | 63 | describe("post message", () => { 64 | let listenCallback; 65 | let methodCallback; 66 | 67 | const type = "fakeType"; 68 | 69 | beforeEach(() => { 70 | listenCallback = sinon.stub(); 71 | methodCallback = sinon.stub(); 72 | }); 73 | 74 | afterEach(() => { 75 | listenCallback.reset(); 76 | methodCallback.reset(); 77 | }); 78 | 79 | it("callback should be call - type", () => { 80 | const data = { 81 | method: "method", 82 | type, 83 | requestId: "requestId" 84 | }; 85 | mondayClient.listen(type, listenCallback); 86 | window.postMessage(data, "*"); 87 | clock.tick(5); 88 | expect(listenCallback).to.be.calledWithExactly(data); 89 | }); 90 | 91 | it("unsubscribe should prevent callback being called", () => { 92 | const data = { 93 | method: "method", 94 | type, 95 | requestId: "requestId" 96 | }; 97 | const unsubscribe = mondayClient.listen(type, listenCallback); 98 | window.postMessage(data, "*"); 99 | window.postMessage(data, "*"); 100 | window.postMessage(data, "*"); 101 | clock.tick(5); 102 | unsubscribe(); 103 | window.postMessage(data, "*"); 104 | expect(listenCallback).to.be.calledWithExactly(data).and.calledThrice; 105 | }); 106 | }); 107 | describe("api methods", () => { 108 | let postMessageStub; 109 | beforeEach(() => { 110 | mondayClient.setToken(null); 111 | postMessageStub = sinon.stub(); 112 | }); 113 | 114 | afterEach(() => { 115 | postMessageStub.reset(); 116 | }); 117 | it("should post message to window with requestId ", () => { 118 | window.addEventListener("message", postMessageStub, false); 119 | mondayClient.api("query"); 120 | clock.tick(5); 121 | expect(postMessageStub).to.be.called; 122 | window.removeEventListener("message", postMessageStub, false); 123 | }); 124 | 125 | it("get api post message", () => { 126 | window.addEventListener("message", postMessageStub, false); 127 | mondayClient.get({ type: "type", method: "method" }); 128 | clock.tick(5); 129 | expect(postMessageStub).to.be.called; 130 | window.removeEventListener("message", postMessageStub, false); 131 | }); 132 | 133 | it("get api post message", () => { 134 | window.addEventListener("message", postMessageStub, false); 135 | mondayClient.execute({ type: "type", method: "method" }); 136 | clock.tick(5); 137 | expect(postMessageStub).to.be.called; 138 | window.removeEventListener("message", postMessageStub, false); 139 | }); 140 | }); 141 | }); 142 | 143 | describe("Monday Client Test API - Returning data", () => { 144 | let clock; 145 | beforeEach(() => { 146 | //we are not initializing mondaySdk in beforeEach to have it's event listener being registered 147 | //AFTER the test's event listener to prevent SDK to react on it's messages before the test listener 148 | clock = sinon.useFakeTimers(); 149 | }); 150 | 151 | it("API should resolve the promise coming from the host app ", done => { 152 | const clientId = "clientId"; 153 | const responseData = { accountId: 123, boards: [] }; 154 | function onPostMessage(event) { 155 | const { requestId, method, type } = event.data; 156 | if (method === "api" && !event.data.data) { 157 | window.postMessage({ requestId, data: responseData, method, type }, "*"); 158 | //because in tests we don't have 2 different window object (parent and iframe) they are exchanging in the same 159 | //events space, so here we need to stop the initial SDK event propogation to not allow SDK to react to it's own event 160 | event.stopImmediatePropagation(); 161 | } 162 | } 163 | window.addEventListener("message", onPostMessage, false); 164 | 165 | const mondayClient = initMondaySdk({ clientId }); 166 | mondayClient.api("query").then(res => { 167 | expect(res).to.be.ok; 168 | expect(res).to.be.equal(responseData); 169 | done(); 170 | }); 171 | clock.tick(5); 172 | window.removeEventListener("message", onPostMessage, false); 173 | }); 174 | 175 | it("API should reject the promise, when host raises an event with errorMessage", done => { 176 | const clientId = "clientId"; 177 | const errorMessage = "My custom error"; 178 | const errorData = { errors: ["1", "2", "3"] }; 179 | function onPostMessage(event) { 180 | const { requestId, method, type } = event.data; 181 | if (method === "api" && !event.data.errorMessage) { 182 | window.postMessage({ requestId, data: errorData, method, type, errorMessage: errorMessage }, "*"); 183 | //because in tests we don't have 2 different window object (parent and iframe) they are exchanging in the same 184 | //events space, so here we need to stop the initial SDK event propogation to not allow SDK to react to it's own event 185 | event.stopImmediatePropagation(); 186 | } 187 | } 188 | window.addEventListener("message", onPostMessage, false); 189 | 190 | const mondayClient = initMondaySdk({ clientId }); 191 | mondayClient.api("query").catch(err => { 192 | expect(err).to.be.ok; 193 | expect(err.message).to.be.equal(errorMessage); 194 | expect(err.data).to.be.equal(errorData); 195 | done(); 196 | }); 197 | clock.tick(5); 198 | window.removeEventListener("message", onPostMessage, false); 199 | }); 200 | }); 201 | -------------------------------------------------------------------------------- /src/client.js: -------------------------------------------------------------------------------- 1 | const mondayApiClient = require("./monday-api-client"); 2 | const { MONDAY_OAUTH_URL } = require("./constants.js"); 3 | const { convertToArrayIfNeeded } = require("./helpers"); 4 | const { initScrollHelperIfNeeded } = require("./helpers/ui-helpers"); 5 | const { initBackgroundTracking } = require("./services/background-tracking-service"); 6 | const { logWarnings } = require("./helpers/monday-api-helpers"); 7 | 8 | const EMPTY_ARRAY = []; 9 | 10 | const STORAGE_SEGMENT_KINDS = { 11 | GLOBAL: "v2", 12 | INSTANCE: "instance" 13 | }; 14 | 15 | class MondayClientSdk { 16 | constructor(options = {}) { 17 | this._clientId = options.clientId; 18 | this._apiToken = options.apiToken; 19 | this._apiVersion = options.apiVersion; 20 | 21 | this.listeners = {}; 22 | 23 | this.setClientId = this.setClientId.bind(this); 24 | this.setToken = this.setToken.bind(this); 25 | this.setApiVersion = this.setApiVersion.bind(this); 26 | this.api = this.api.bind(this); 27 | this.listen = this.listen.bind(this); 28 | this.get = this.get.bind(this); 29 | this.set = this.set.bind(this); 30 | this.execute = this.execute.bind(this); 31 | this.oauth = this.oauth.bind(this); 32 | this._receiveMessage = this._receiveMessage.bind(this); 33 | 34 | this.storage = { 35 | setItem: this.setStorageItem.bind(this), 36 | getItem: this.getStorageItem.bind(this), 37 | deleteItem: this.deleteStorageItem.bind(this), 38 | instance: { 39 | setItem: this.setStorageInstanceItem.bind(this), 40 | getItem: this.getStorageInstanceItem.bind(this), 41 | deleteItem: this.deleteStorageInstanceItem.bind(this) 42 | } 43 | }; 44 | 45 | window.addEventListener("message", this._receiveMessage, false); 46 | 47 | if (!options.withoutScrollHelper) initScrollHelperIfNeeded(); 48 | 49 | initBackgroundTracking(this); 50 | } 51 | 52 | setClientId(clientId) { 53 | this._clientId = clientId; 54 | } 55 | 56 | setToken(token) { 57 | this._apiToken = token; 58 | } 59 | 60 | setApiVersion(apiVersion) { 61 | this._apiVersion = apiVersion; 62 | } 63 | 64 | api(query, options = {}) { 65 | const params = { query, variables: options.variables }; 66 | const token = options.token || this._apiToken; 67 | const apiVersion = options.apiVersion || this._apiVersion; 68 | 69 | let responsePromise; 70 | if (token) { 71 | responsePromise = mondayApiClient.execute(params, token, { apiVersion }); 72 | } else { 73 | responsePromise = this._localApi("api", { params, apiVersion }).then(result => result.data); 74 | } 75 | 76 | return responsePromise.then(logWarnings); 77 | } 78 | 79 | listen(typeOrTypes, callback, params) { 80 | const types = convertToArrayIfNeeded(typeOrTypes); 81 | const unsubscribes = []; 82 | 83 | types.forEach(type => { 84 | unsubscribes.push(this._addListener(type, callback)); 85 | this._localApi("listen", { type, params }); 86 | }); 87 | 88 | return () => { 89 | unsubscribes.forEach(unsubscribe => unsubscribe()); 90 | }; 91 | } 92 | 93 | get(type, params) { 94 | return this._localApi("get", { type, params }); 95 | } 96 | 97 | set(type, params) { 98 | return this._localApi("set", { type, params }); 99 | } 100 | 101 | execute(type, params) { 102 | return this._localApi("execute", { type, params }); 103 | } 104 | 105 | track(name, data) { 106 | return this.execute("track", { name, data }); 107 | } 108 | 109 | oauth(options = {}) { 110 | const clientId = options.clientId || this._clientId; 111 | if (!clientId) throw new Error("clientId is required"); 112 | 113 | const mondayOauthUrl = options.mondayOauthUrl || MONDAY_OAUTH_URL; 114 | 115 | const url = `${mondayOauthUrl}?client_id=${clientId}`; 116 | window.location = url; 117 | } 118 | 119 | setStorageItem(key, value, options = {}) { 120 | return this._localApi("storage", { method: "set", key, value, options, segment: STORAGE_SEGMENT_KINDS.GLOBAL }); 121 | } 122 | 123 | getStorageItem(key, options = {}) { 124 | return this._localApi("storage", { method: "get", key, options, segment: STORAGE_SEGMENT_KINDS.GLOBAL }); 125 | } 126 | 127 | deleteStorageItem(key, options = {}) { 128 | return this._localApi("storage", { method: "delete", key, options, segment: STORAGE_SEGMENT_KINDS.GLOBAL }); 129 | } 130 | 131 | setStorageInstanceItem(key, value, options = {}) { 132 | return this._localApi("storage", { method: "set", key, value, options, segment: STORAGE_SEGMENT_KINDS.INSTANCE }); 133 | } 134 | 135 | getStorageInstanceItem(key, options = {}) { 136 | return this._localApi("storage", { method: "get", key, options, segment: STORAGE_SEGMENT_KINDS.INSTANCE }); 137 | } 138 | 139 | deleteStorageInstanceItem(key, options = {}) { 140 | return this._localApi("storage", { method: "delete", key, options, segment: STORAGE_SEGMENT_KINDS.INSTANCE }); 141 | } 142 | 143 | _localApi(method, args) { 144 | return new Promise((resolve, reject) => { 145 | const requestId = this._generateRequestId(); 146 | const clientId = this._clientId; 147 | const pjson = require("../package.json"); 148 | const version = pjson.version; 149 | 150 | window.parent.postMessage({ method, args, requestId, clientId, version }, "*"); 151 | const removeListener = this._addListener(requestId, data => { 152 | removeListener(); 153 | if (data.errorMessage) { 154 | const error = new Error(data.errorMessage); 155 | error.data = data.data; 156 | reject(error); 157 | } else { 158 | resolve(data); 159 | } 160 | }); 161 | }); 162 | } 163 | 164 | _receiveMessage(event) { 165 | const { method, type, requestId } = event.data; 166 | const methodListeners = this.listeners[method] || EMPTY_ARRAY; 167 | const typeListeners = this.listeners[type] || EMPTY_ARRAY; 168 | const requestIdListeners = this.listeners[requestId] || EMPTY_ARRAY; 169 | let listeners = new Set([...methodListeners, ...typeListeners, ...requestIdListeners]); 170 | 171 | if (listeners) { 172 | listeners.forEach(listener => { 173 | try { 174 | listener(event.data); 175 | } catch (err) { 176 | console.error("Message callback error: ", err); 177 | } 178 | }); 179 | } 180 | } 181 | 182 | _addListener(key, callback) { 183 | this.listeners[key] = this.listeners[key] || new Set(); 184 | this.listeners[key].add(callback); 185 | 186 | return () => { 187 | this.listeners[key].delete(callback); 188 | if (this.listeners[key].size === 0) { 189 | delete this.listeners[key]; 190 | } 191 | }; 192 | } 193 | 194 | _generateRequestId() { 195 | return Math.random() 196 | .toString(36) 197 | .substring(2, 9); 198 | } 199 | 200 | _removeEventListener() { 201 | window.removeEventListener("message", this._receiveMessage, false); 202 | } 203 | _clearListeners() { 204 | this.listeners = []; 205 | } 206 | } 207 | 208 | function init(options = {}) { 209 | return new MondayClientSdk(options); 210 | } 211 | 212 | module.exports = init; 213 | -------------------------------------------------------------------------------- /src/constants-test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require("./tests/helpers"); 2 | const constants = require("./constants"); 3 | 4 | describe("constants", () => { 5 | //setup 6 | beforeEach(() => { 7 | process.env.NODE_ENV = "development"; 8 | process.env.MONDAY_DOMAIN = undefined; 9 | process.env.MONDAY_COM_PROTOCOL = undefined; 10 | process.env.MONDAY_COM_DOMAIN = undefined; 11 | process.env.MONDAY_SUBDOMAIN_API = undefined; 12 | process.env.MONDAY_OAUTH_SUBDOMAIN = undefined; 13 | }); 14 | 15 | it("should have at least 3 constants", () => { 16 | expect(Object.keys(constants).length).to.be.at.least(3); 17 | }); 18 | 19 | it("check that constants changed after setting env variables", () => { 20 | const MONDAY_COM_PROTOCOL = "http"; 21 | const MONDAY_COM_DOMAIN = "mondaystaging.com"; 22 | const MONDAY_SUBDOMAIN_API = ""; 23 | const MONDAY_OAUTH_SUBDOMAIN = "authZ."; 24 | const MONDAY_API_URL = `${MONDAY_COM_PROTOCOL}://${MONDAY_SUBDOMAIN_API}${MONDAY_COM_DOMAIN}/v2`; 25 | const MONDAY_OAUTH_URL = `${MONDAY_COM_PROTOCOL}://${MONDAY_OAUTH_SUBDOMAIN}${MONDAY_COM_DOMAIN}/oauth2/authorize`; 26 | const MONDAY_OAUTH_TOKEN_URL = `${MONDAY_COM_PROTOCOL}://${MONDAY_OAUTH_SUBDOMAIN}${MONDAY_COM_DOMAIN}/oauth2/token`; 27 | process.env.MONDAY_COM_PROTOCOL = MONDAY_COM_PROTOCOL; 28 | process.env.MONDAY_COM_DOMAIN = MONDAY_COM_DOMAIN; 29 | process.env.MONDAY_SUBDOMAIN_API = MONDAY_SUBDOMAIN_API; 30 | process.env.MONDAY_OAUTH_SUBDOMAIN = MONDAY_OAUTH_SUBDOMAIN; 31 | 32 | expect(constants.MONDAY_DOMAIN).to.eq(MONDAY_COM_DOMAIN); 33 | expect(constants.MONDAY_PROTOCOL).to.eq(MONDAY_COM_PROTOCOL); 34 | expect(constants.MONDAY_API_URL).to.eq(MONDAY_API_URL); 35 | expect(constants.MONDAY_OAUTH_URL).to.eq(MONDAY_OAUTH_URL); 36 | expect(constants.MONDAY_OAUTH_TOKEN_URL).to.eq(MONDAY_OAUTH_TOKEN_URL); 37 | }); 38 | 39 | describe("check that constants are correct when NODE_ENV is development", () => { 40 | it("MONDAY_DOMAIN should be correct", () => { 41 | expect(constants.MONDAY_DOMAIN).to.eq("monday.com"); 42 | }); 43 | 44 | it("MONDAY_PROTOCOL should be correct", () => { 45 | expect(constants.MONDAY_PROTOCOL).to.eq("https"); 46 | }); 47 | 48 | it("MONDAY_API_URL should be correct", () => { 49 | expect(constants.MONDAY_API_URL).to.eq("https://api.monday.com/v2"); 50 | }); 51 | 52 | it("MONDAY_OAUTH_URL should be correct", () => { 53 | expect(constants.MONDAY_OAUTH_URL).to.eq("https://auth.monday.com/oauth2/authorize"); 54 | }); 55 | 56 | it("MONDAY_OAUTH_TOKEN_URL should be correct", () => { 57 | expect(constants.MONDAY_OAUTH_TOKEN_URL).to.eq("https://auth.monday.com/oauth2/token"); 58 | }); 59 | }); 60 | 61 | describe("check that constants are correct when NODE_ENV is undefined", () => { 62 | beforeEach(() => { 63 | process.env.NODE_ENV = undefined; 64 | process.env.MONDAY_COM_DOMAIN = "should not be used"; 65 | }); 66 | 67 | it("MONDAY_DOMAIN should be correct", () => { 68 | expect(constants.MONDAY_DOMAIN).to.eq("monday.com"); 69 | }); 70 | 71 | it("MONDAY_PROTOCOL should be correct", () => { 72 | expect(constants.MONDAY_PROTOCOL).to.eq("https"); 73 | }); 74 | 75 | it("MONDAY_API_URL should be correct", () => { 76 | expect(constants.MONDAY_API_URL).to.eq("https://api.monday.com/v2"); 77 | }); 78 | 79 | it("MONDAY_OAUTH_URL should be correct", () => { 80 | expect(constants.MONDAY_OAUTH_URL).to.eq("https://auth.monday.com/oauth2/authorize"); 81 | }); 82 | 83 | it("MONDAY_OAUTH_TOKEN_URL should be correct", () => { 84 | expect(constants.MONDAY_OAUTH_TOKEN_URL).to.eq("https://auth.monday.com/oauth2/token"); 85 | }); 86 | }); 87 | 88 | describe("check that constants are correct when NODE_ENV is undefined", () => { 89 | beforeEach(() => { 90 | process.env.NODE_ENV = "production"; 91 | process.env.MONDAY_COM_DOMAIN = "should not be used"; 92 | }); 93 | 94 | it("MONDAY_DOMAIN should be correct", () => { 95 | expect(constants.MONDAY_DOMAIN).to.eq("monday.com"); 96 | }); 97 | 98 | it("MONDAY_PROTOCOL should be correct", () => { 99 | expect(constants.MONDAY_PROTOCOL).to.eq("https"); 100 | }); 101 | 102 | it("MONDAY_API_URL should be correct", () => { 103 | expect(constants.MONDAY_API_URL).to.eq("https://api.monday.com/v2"); 104 | }); 105 | 106 | it("MONDAY_OAUTH_URL should be correct", () => { 107 | expect(constants.MONDAY_OAUTH_URL).to.eq("https://auth.monday.com/oauth2/authorize"); 108 | }); 109 | 110 | it("MONDAY_OAUTH_TOKEN_URL should be correct", () => { 111 | expect(constants.MONDAY_OAUTH_TOKEN_URL).to.eq("https://auth.monday.com/oauth2/token"); 112 | }); 113 | }); 114 | }); 115 | -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | function isNodeEnv() { 2 | return typeof process !== "undefined"; 3 | } 4 | 5 | function isNodeDevStageEnv() { 6 | return isNodeEnv() && (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "staging"); 7 | } 8 | 9 | const getEnvOrDefault = (key, defaultVal) => { 10 | return isNodeDevStageEnv() && process.env[key] !== "undefined" && process.env[key] !== undefined 11 | ? process.env[key] 12 | : defaultVal; 13 | }; 14 | 15 | const MONDAY_PROTOCOL = () => getEnvOrDefault("MONDAY_COM_PROTOCOL", "https"); 16 | const MONDAY_DOMAIN = () => getEnvOrDefault("MONDAY_COM_DOMAIN", "monday.com"); 17 | const MONDAY_SUBDOMAIN_API = () => getEnvOrDefault("MONDAY_SUBDOMAIN_API", "api."); 18 | const MONDAY_OAUTH_SUBDOMAIN = () => getEnvOrDefault("MONDAY_OAUTH_SUBDOMAIN", "auth."); 19 | 20 | const MONDAY_API_URL = () => `${MONDAY_PROTOCOL()}://${MONDAY_SUBDOMAIN_API()}${MONDAY_DOMAIN()}/v2`; 21 | const MONDAY_OAUTH_URL = () => `${MONDAY_PROTOCOL()}://${MONDAY_OAUTH_SUBDOMAIN()}${MONDAY_DOMAIN()}/oauth2/authorize`; 22 | const MONDAY_OAUTH_TOKEN_URL = () => 23 | `${MONDAY_PROTOCOL()}://${MONDAY_OAUTH_SUBDOMAIN()}${MONDAY_DOMAIN()}/oauth2/token`; 24 | 25 | module.exports = { 26 | get MONDAY_DOMAIN() { 27 | return MONDAY_DOMAIN(); 28 | }, 29 | get MONDAY_PROTOCOL() { 30 | return MONDAY_PROTOCOL(); 31 | }, 32 | get MONDAY_API_URL() { 33 | return MONDAY_API_URL(); 34 | }, 35 | get MONDAY_OAUTH_URL() { 36 | return MONDAY_OAUTH_URL(); 37 | }, 38 | get MONDAY_OAUTH_TOKEN_URL() { 39 | return MONDAY_OAUTH_TOKEN_URL(); 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /src/helpers/index.js: -------------------------------------------------------------------------------- 1 | const convertToArrayIfNeeded = x => { 2 | return Array.isArray(x) ? x : [x]; 3 | }; 4 | 5 | const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined"; 6 | 7 | module.exports = { 8 | convertToArrayIfNeeded, 9 | isBrowser 10 | }; 11 | -------------------------------------------------------------------------------- /src/helpers/monday-api-helpers.js: -------------------------------------------------------------------------------- 1 | const logWarnings = res => { 2 | const warnings = res && res.extensions && res.extensions.warnings; 3 | if (!warnings || !Array.isArray(warnings)) return res; 4 | 5 | warnings.forEach(warning => { 6 | if (!warning || !warning.message) return; 7 | 8 | try { 9 | const locations = 10 | warning.locations && warning.locations.map(loc => `line ${loc.line}, column ${loc.column}`).join("; "); 11 | const path = warning.path && warning.path.join(" → "); 12 | 13 | let message = warning.message; 14 | 15 | // remove the dot at the end of the message 16 | message = message.replace(/\.$/, ""); 17 | // start the message with lower case letter 18 | message = message.charAt(0).toLowerCase() + message.slice(1); 19 | 20 | const messageParts = [ 21 | "[monday API]", 22 | `${path}:`, 23 | message, 24 | locations && `@ ${locations}`, 25 | warning.extensions ? ["\n\nAdditional details:", warning.extensions] : undefined 26 | ] 27 | .flat() 28 | .filter(Boolean); 29 | 30 | console.warn(...messageParts); 31 | } catch (e) { 32 | if (warning) { 33 | console.warn("[monday API] Warning:", warning); 34 | } 35 | } 36 | }); 37 | 38 | return res; 39 | }; 40 | 41 | module.exports = { 42 | logWarnings 43 | }; 44 | -------------------------------------------------------------------------------- /src/helpers/ui-helpers.js: -------------------------------------------------------------------------------- 1 | let scrollHelperInitialized = false; 2 | 3 | function initScrollHelperIfNeeded() { 4 | if (scrollHelperInitialized) return; 5 | // will prevent white flashes when scrolling using wheel events on laptops 6 | // with dual gpu when using the power saving gpu 7 | // it's reproduceable on macbook when not connected to an external display 8 | scrollHelperInitialized = true; 9 | 10 | // when an elemnt cover the scollable element, wheel events are synchronous on chromium based browsers 11 | const css = 12 | 'body::before { content: ""; position: fixed; top: 0; right: 0; bottom: 0; left: 0; pointer-events: none; z-index: 2147483647; /* mondaySdk css - can be disabled with: mondaySdk({withoutScrollHelper: true }) */ }'; 13 | const style = document.createElement("style"); 14 | style.appendChild(document.createTextNode(css)); 15 | 16 | const head = document.head || document.getElementsByTagName("head")[0]; 17 | head.appendChild(style); 18 | } 19 | 20 | module.exports = { 21 | initScrollHelperIfNeeded 22 | }; 23 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const { isBrowser } = require("./helpers"); 2 | const init = isBrowser ? require("./client") : require("./server"); 3 | 4 | (function(root, factory) { 5 | if (typeof define === "function" && define.amd) { 6 | define(factory); 7 | } else if (typeof exports === "object") { 8 | module.exports = factory(); 9 | } else if (root) { 10 | root = factory(); 11 | } 12 | })(typeof self !== "undefined" ? self : this, function() { 13 | if (typeof __BUNDLE__ !== "undefined" && __BUNDLE__) { 14 | window.mondaySdk = init; 15 | } 16 | return init; 17 | }); 18 | -------------------------------------------------------------------------------- /src/monday-api-client/fetch.js: -------------------------------------------------------------------------------- 1 | const fetch = require("node-fetch"); 2 | 3 | // for tests - to allow stubbing node-fetch with sinon 4 | function nodeFetch(url, options = {}) { 5 | return fetch(url, options); 6 | } 7 | 8 | module.exports = { 9 | nodeFetch 10 | }; 11 | -------------------------------------------------------------------------------- /src/monday-api-client/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./monday-api-client"); 2 | -------------------------------------------------------------------------------- /src/monday-api-client/monday-api-client-test.js: -------------------------------------------------------------------------------- 1 | const { expect, assert, sinon } = require("./../tests/helpers"); 2 | const fetch = require("./fetch"); 3 | const mondayApiClient = require("./monday-api-client"); 4 | 5 | describe("mondayApiClient", () => { 6 | let nodeFetchStub; 7 | let fetchMock; 8 | 9 | beforeEach(() => { 10 | fetchMock = sinon.stub(async () => { 11 | return { data: "some_data" }; 12 | }); 13 | nodeFetchStub = sinon 14 | .stub(fetch, "nodeFetch") 15 | .resolves({ json: fetchMock, headers: { get: () => "application/json" } }); 16 | }); 17 | 18 | afterEach(() => { 19 | nodeFetchStub.restore(); 20 | }); 21 | 22 | it("should be able to execute an api request", async () => { 23 | const result = await mondayApiClient.execute("query { boards { id, name }}", "api_token"); 24 | expect(result).to.deep.equal({ data: "some_data" }); 25 | }); 26 | 27 | it("should call node fetch with the correct args", async () => { 28 | await mondayApiClient.execute("query { boards { id, name }}", "api_token"); 29 | assert.calledOnce(nodeFetchStub); 30 | assert.calledWithExactly(nodeFetchStub, "https://api.monday.com/v2", { 31 | body: '"query { boards { id, name }}"', 32 | headers: { Authorization: "api_token", "Content-Type": "application/json" }, 33 | method: "POST" 34 | }); 35 | }); 36 | 37 | it("should call node fetch with the version header", async () => { 38 | const apiVersion = "2023-01"; 39 | await mondayApiClient.execute("query { boards { id, name }}", "api_token", { apiVersion }); 40 | assert.calledOnce(nodeFetchStub); 41 | assert.calledWithExactly(nodeFetchStub, "https://api.monday.com/v2", { 42 | body: '"query { boards { id, name }}"', 43 | headers: { Authorization: "api_token", "Content-Type": "application/json", "API-Version": apiVersion }, 44 | method: "POST" 45 | }); 46 | }); 47 | 48 | it(`should throw ${mondayApiClient.TOKEN_IS_REQUIRED_ERROR}`, async () => { 49 | let errorMessage; 50 | try { 51 | await mondayApiClient.execute("query { boards { id, name }}"); 52 | } catch (err) { 53 | errorMessage = err.message; 54 | } 55 | expect(errorMessage).to.eq(mondayApiClient.TOKEN_IS_REQUIRED_ERROR); 56 | }); 57 | 58 | it(`should throw ${mondayApiClient.COULD_NOT_PARSE_JSON_RESPONSE_ERROR}`, async () => { 59 | nodeFetchStub.returns({ json: "not json", headers: { get: () => "application/json" } }); 60 | let errorMessage; 61 | try { 62 | await mondayApiClient.execute("query { boards { id, name }}", "api_token"); 63 | } catch (err) { 64 | errorMessage = err.message; 65 | } 66 | expect(errorMessage).to.eq(mondayApiClient.COULD_NOT_PARSE_JSON_RESPONSE_ERROR); 67 | }); 68 | 69 | it(`should throw ${mondayApiClient.API_TIMEOUT_ERROR}`, async () => { 70 | nodeFetchStub.returns({ headers: { get: () => "text/plain" }, status: 504 }); 71 | let errorMessage; 72 | try { 73 | await mondayApiClient.execute("query { boards { id, name }}", "api_token"); 74 | } catch (err) { 75 | errorMessage = err.message; 76 | } 77 | expect(errorMessage).to.eq(mondayApiClient.API_TIMEOUT_ERROR); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /src/monday-api-client/monday-api-client.js: -------------------------------------------------------------------------------- 1 | const { MONDAY_API_URL, MONDAY_OAUTH_TOKEN_URL } = require("./../constants.js"); 2 | const fetch = require("./fetch"); 3 | 4 | const COULD_NOT_PARSE_JSON_RESPONSE_ERROR = "Could not parse JSON from monday.com's GraphQL API response"; 5 | const TOKEN_IS_REQUIRED_ERROR = "Token is required"; 6 | const API_TIMEOUT_ERROR = "Received timeout from monday.com's GraphQL API"; 7 | 8 | function apiRequest(url, data, token, options = {}) { 9 | return fetch.nodeFetch(url, { 10 | method: options.method || "POST", 11 | body: JSON.stringify(data || {}), 12 | headers: { 13 | Authorization: token, 14 | "Content-Type": "application/json", 15 | ...(options.apiVersion ? { "API-Version": options.apiVersion } : {}) 16 | } 17 | }); 18 | } 19 | 20 | async function execute(data, token, options = {}) { 21 | if (!token && options.url !== MONDAY_OAUTH_TOKEN_URL) throw new Error(TOKEN_IS_REQUIRED_ERROR); 22 | 23 | const url = options.url || MONDAY_API_URL; 24 | const path = options.path || ""; 25 | const fullUrl = `${url}${path}`; 26 | let response = await apiRequest(fullUrl, data, token, options); 27 | 28 | const responseStatusCode = response.status; 29 | const responseContentType = response.headers.get("content-type"); 30 | if (!responseContentType || !responseContentType.includes("application/json")) { 31 | if (responseStatusCode === 504) { 32 | throw new Error(API_TIMEOUT_ERROR); 33 | } 34 | 35 | const responseText = await response.text(); 36 | throw new Error(responseText); 37 | } 38 | 39 | try { 40 | return await response.json(); 41 | } catch (err) { 42 | throw new Error(COULD_NOT_PARSE_JSON_RESPONSE_ERROR); 43 | } 44 | } 45 | 46 | module.exports = { execute, COULD_NOT_PARSE_JSON_RESPONSE_ERROR, TOKEN_IS_REQUIRED_ERROR, API_TIMEOUT_ERROR }; 47 | -------------------------------------------------------------------------------- /src/server-test.js: -------------------------------------------------------------------------------- 1 | const { sinon, assert, expect } = require("./tests/helpers"); 2 | let initServerSdk = require("./server"); 3 | const mondayApiClient = require("./monday-api-client"); 4 | 5 | describe("server sdk", () => { 6 | describe("init", () => { 7 | it("should be able to init serverSdk", () => { 8 | initServerSdk(); 9 | }); 10 | 11 | it("should be able to init serverSdk without token", () => { 12 | const serverSdk = initServerSdk(); 13 | expect(serverSdk._token).to.eq(undefined); 14 | }); 15 | 16 | it("should be able to init serverSdk with token", () => { 17 | const serverSdk = initServerSdk({ token: "sometoken123" }); 18 | expect(serverSdk._token).to.eq("sometoken123"); 19 | }); 20 | 21 | it("should be able to init serverSdk with API version", () => { 22 | const serverSdk = initServerSdk({ apiVersion: "2023-01" }); 23 | expect(serverSdk._apiVersion).to.eq("2023-01"); 24 | }); 25 | }); 26 | 27 | describe("setToken", () => { 28 | it("should be able to set token", () => { 29 | const serverSdk = initServerSdk(); 30 | expect(serverSdk._token).to.eq(undefined); 31 | serverSdk.setToken("sometoken123"); 32 | expect(serverSdk._token).to.eq("sometoken123"); 33 | }); 34 | 35 | it("should be binded", () => { 36 | const serverSdk = initServerSdk(); 37 | expect(serverSdk._token).to.eq(undefined); 38 | const { setToken } = serverSdk; 39 | setToken("sometoken123"); 40 | expect(serverSdk._token).to.eq("sometoken123"); 41 | }); 42 | }); 43 | 44 | describe("setApiVersion", () => { 45 | it("should be able to set the version", () => { 46 | const serverSdk = initServerSdk(); 47 | expect(serverSdk._apiVersion).to.eq(undefined); 48 | 49 | const { setApiVersion } = serverSdk; 50 | setApiVersion("2023-01"); 51 | 52 | expect(serverSdk._apiVersion).to.eq("2023-01"); 53 | }); 54 | }); 55 | 56 | describe("api", () => { 57 | let mondayApiClientExecuteStub; 58 | 59 | beforeEach(() => { 60 | mondayApiClientExecuteStub = sinon.stub(mondayApiClient, "execute").resolves("api-response"); 61 | }); 62 | 63 | afterEach(() => { 64 | mondayApiClientExecuteStub.restore(); 65 | }); 66 | 67 | it("should call to mondayApi if sdk was initialized with token", async () => { 68 | const serverSdk = initServerSdk({ token: "api_token" }); 69 | const result = await serverSdk.api("query { boards { id, name }}"); 70 | expect(result).to.eq("api-response"); 71 | }); 72 | 73 | it("should call mondayApiClient with the current args", async () => { 74 | const serverSdk = initServerSdk({ token: "api_token" }); 75 | await serverSdk.api("query { boards { id, name }}"); 76 | assert.calledOnce(mondayApiClientExecuteStub); 77 | assert.calledWithExactly( 78 | mondayApiClientExecuteStub, 79 | { query: "query { boards { id, name }}", variables: undefined }, 80 | "api_token", 81 | { apiVersion: undefined } 82 | ); 83 | }); 84 | 85 | it("should be able to pass variables in options", async () => { 86 | const serverSdk = initServerSdk({ token: "api_token" }); 87 | 88 | const query = ` 89 | mutation create_pulse($pulseName: String!, $columnValues: JSON) { 90 | create_pulse( 91 | pulse_name: $pulseName, 92 | board_id: 123, 93 | group_id: my_group", 94 | column_values: $columnValues 95 | ) { 96 | id 97 | } 98 | } 99 | `; 100 | 101 | const variables = { pulseName: "new pulse", columnValues: { numbers: 3 } }; 102 | 103 | await serverSdk.api(query, { variables }); 104 | assert.calledOnce(mondayApiClientExecuteStub); 105 | assert.calledWithExactly( 106 | mondayApiClientExecuteStub, 107 | { 108 | query: ` 109 | mutation create_pulse($pulseName: String!, $columnValues: JSON) { 110 | create_pulse( 111 | pulse_name: $pulseName, 112 | board_id: 123, 113 | group_id: my_group", 114 | column_values: $columnValues 115 | ) { 116 | id 117 | } 118 | } 119 | `, 120 | variables: { columnValues: { numbers: 3 }, pulseName: "new pulse" } 121 | }, 122 | "api_token", 123 | { apiVersion: undefined } 124 | ); 125 | }); 126 | 127 | it("should call to mondayApi when token is passed from options", async () => { 128 | const serverSdk = initServerSdk(); 129 | serverSdk.setToken("api_token"); 130 | const result = await serverSdk.api("query { boards { id, name }}"); 131 | expect(result).to.eq("api-response"); 132 | }); 133 | 134 | it("should call to mondayApi prefer token from options", async () => { 135 | const serverSdk = initServerSdk({ token: "api_token" }); 136 | serverSdk.setToken("api_token_2"); 137 | await serverSdk.api("query { boards { id, name }}"); 138 | assert.calledOnce(mondayApiClientExecuteStub); 139 | assert.calledWithExactly( 140 | mondayApiClientExecuteStub, 141 | { query: "query { boards { id, name }}", variables: undefined }, 142 | "api_token_2", 143 | { apiVersion: undefined } 144 | ); 145 | }); 146 | 147 | it("should provide version to mondayApi", async () => { 148 | const serverSdk = initServerSdk({ token: "api_token", apiVersion: "2023-01" }); 149 | serverSdk.setApiVersion("2023-04"); 150 | await serverSdk.api("query { boards { id, name }}"); 151 | assert.calledOnce(mondayApiClientExecuteStub); 152 | assert.calledWithExactly( 153 | mondayApiClientExecuteStub, 154 | { query: "query { boards { id, name }}", variables: undefined }, 155 | "api_token", 156 | { apiVersion: "2023-04" } 157 | ); 158 | }); 159 | 160 | it("should prefer version from method options when calling mondayApi", async () => { 161 | const serverSdk = initServerSdk({ token: "api_token", apiVersion: "2023-01" }); 162 | serverSdk.setApiVersion("2023-04"); 163 | await serverSdk.api("query { boards { id, name }}", { apiVersion: "2023-07" }); 164 | assert.calledOnce(mondayApiClientExecuteStub); 165 | assert.calledWithExactly( 166 | mondayApiClientExecuteStub, 167 | { query: "query { boards { id, name }}", variables: undefined }, 168 | "api_token", 169 | { apiVersion: "2023-07" } 170 | ); 171 | }); 172 | 173 | it("should call to mondayApi prefer version from options", async () => { 174 | const serverSdk = initServerSdk({ token: "api_token", apiVersion: "2023-01" }); 175 | serverSdk.setApiVersion("2023-04"); 176 | await serverSdk.api("query { boards { id, name }}", { apiVersion: "2023-07" }); 177 | assert.calledOnce(mondayApiClientExecuteStub); 178 | assert.calledWithExactly( 179 | mondayApiClientExecuteStub, 180 | { query: "query { boards { id, name }}", variables: undefined }, 181 | "api_token", 182 | { apiVersion: "2023-07" } 183 | ); 184 | }); 185 | 186 | it("should throw error if token is missing", async () => { 187 | let errorMessage; 188 | const serverSdk = initServerSdk(); 189 | try { 190 | await serverSdk.api("query { boards { id, name }}"); 191 | } catch (err) { 192 | errorMessage = err.message; 193 | } 194 | 195 | expect(errorMessage).to.equal("Should send 'token' as an option or call mondaySdk.setToken(TOKEN)"); 196 | }); 197 | }); 198 | }); 199 | -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | const { logWarnings } = require("./helpers/monday-api-helpers"); 2 | const mondayApiClient = require("./monday-api-client"); 3 | const { oauthToken } = require("./services/oauth-service.js"); 4 | 5 | const TOKEN_MISSING_ERROR = "Should send 'token' as an option or call mondaySdk.setToken(TOKEN)"; 6 | 7 | class MondayServerSdk { 8 | constructor(options = {}) { 9 | this._token = options.token; 10 | this._apiVersion = options.apiVersion; 11 | 12 | this.setToken = this.setToken.bind(this); 13 | this.setApiVersion = this.setApiVersion.bind(this); 14 | this.api = this.api.bind(this); 15 | } 16 | 17 | setToken(token) { 18 | this._token = token; 19 | } 20 | 21 | setApiVersion(apiVersion) { 22 | this._apiVersion = apiVersion; 23 | } 24 | 25 | api(query, options = {}) { 26 | const params = { query, variables: options.variables }; 27 | const token = options.token || this._token; 28 | const apiVersion = options.apiVersion || this._apiVersion; 29 | 30 | if (!token) throw new Error(TOKEN_MISSING_ERROR); 31 | 32 | return mondayApiClient.execute(params, token, { apiVersion }).then(logWarnings); 33 | } 34 | 35 | oauthToken(code, clientId, clientSecret) { 36 | return oauthToken(code, clientId, clientSecret); 37 | } 38 | } 39 | 40 | function init(options = {}) { 41 | return new MondayServerSdk(options); 42 | } 43 | 44 | module.exports = init; 45 | -------------------------------------------------------------------------------- /src/services/background-tracking-service.js: -------------------------------------------------------------------------------- 1 | const _5_MINUTES_MS = 5 * 60 * 1000; 2 | 3 | let initialized = false; 4 | const initBackgroundTracking = sdk => { 5 | if (initialized) return; 6 | initialized = true; 7 | 8 | const ping = () => { 9 | sdk.track("ping"); 10 | }; 11 | ping(); 12 | setInterval(ping, _5_MINUTES_MS); 13 | }; 14 | 15 | module.exports = { 16 | initBackgroundTracking 17 | }; 18 | -------------------------------------------------------------------------------- /src/services/oauth-service.js: -------------------------------------------------------------------------------- 1 | const { execute } = require("../monday-api-client"); 2 | const { MONDAY_OAUTH_TOKEN_URL } = require("../constants.js"); 3 | 4 | const oauthToken = (code, clientId, clientSecret) => { 5 | const data = { code, client_id: clientId, client_secret: clientSecret }; 6 | return execute(data, null, { url: MONDAY_OAUTH_TOKEN_URL }); 7 | }; 8 | 9 | module.exports = { 10 | oauthToken 11 | }; 12 | -------------------------------------------------------------------------------- /src/tests/helpers.js: -------------------------------------------------------------------------------- 1 | const sinon = require("sinon"); 2 | const chai = require("chai"); 3 | const sinonChai = require("sinon-chai"); 4 | const jsdom = require("jsdom"); 5 | const { JSDOM } = jsdom; 6 | 7 | chai.use(sinonChai); 8 | 9 | const dom = new JSDOM("", { 10 | url: "http://localhost" 11 | }); 12 | global.window = dom.window; 13 | global.window.parent = dom.window; 14 | global.document = dom.window.document; 15 | 16 | module.exports = { 17 | chai, 18 | expect: chai.expect, 19 | assert: sinon.assert, 20 | sinon 21 | }; 22 | -------------------------------------------------------------------------------- /ts-tests/monday-sdk-js-module.test.ts: -------------------------------------------------------------------------------- 1 | import mondaySdk from "../types"; 2 | const monday = mondaySdk(); 3 | 4 | monday.api("test"); 5 | 6 | monday.setApiVersion("2023-10"); 7 | mondaySdk({ apiVersion: "2023-10" }); 8 | monday.api("test", { apiVersion: "2023-07" }); 9 | 10 | monday.setToken("test"); 11 | 12 | monday.get("context", { appFeatureType: "AppFeatureBoardView" }).then(res => { 13 | const { data }: { data: { app: { id: number }; theme: string; boardId: number; viewMode: string } } = res; 14 | }); 15 | 16 | monday.get("context").then(res => { 17 | const { 18 | data 19 | }: { 20 | data: { 21 | subscription?: { 22 | billing_period: string; 23 | days_left: number; 24 | is_trial: boolean; 25 | max_units: number | null; 26 | plan_id: string; 27 | pricing_version: number; 28 | renewal_date: string; 29 | }; 30 | }; 31 | } = res; 32 | }); 33 | 34 | monday.get<{ id: number; name: string }>("testString").then(res => { 35 | const { data }: { data: { id: number; name: string } } = res; 36 | }); 37 | 38 | monday.get<{ text: string; level: number }>("settings").then(res => { 39 | const { data }: { data: { text: string; level: number } } = res; 40 | }); 41 | 42 | monday.get("itemIds").then(res => { 43 | const { data }: { data: number[] } = res; 44 | }); 45 | 46 | monday.get("sessionToken").then(res => { 47 | const { data }: { data: string } = res; 48 | }); 49 | 50 | monday.get("location").then(res => { 51 | const { 52 | data 53 | }: { 54 | data: { 55 | href: string; 56 | search: string; 57 | }; 58 | } = res; 59 | }); 60 | 61 | monday.get("filter").then(res => { 62 | const { 63 | data 64 | }: { 65 | data: { 66 | term: string; 67 | rules: (Record & { 68 | column_id?: string; 69 | compare_value?: string[]; 70 | compare_attribute?: string; 71 | operator?: string; 72 | })[]; 73 | operator: string | null; 74 | }; 75 | } = res; 76 | }); 77 | 78 | monday.set("settings", { text: "this is a test", number: 23 }); 79 | 80 | monday.listen( 81 | "context", 82 | res => { 83 | const { data }: { data: { app: { id: number }; theme: string; itemId: number } } = res; 84 | }, 85 | { appFeatureType: "AppFeatureItemView" } 86 | ); 87 | 88 | monday.execute("openItemCard", { itemId: 123 }); 89 | monday.execute("confirm", { message: "Hello" }); 90 | monday.execute("notice", { message: "Hello" }); 91 | 92 | monday.execute("openFilesDialog", { 93 | boardId: 12345, 94 | itemId: 23456, 95 | columnId: "files", 96 | assetId: 34567 97 | }); 98 | 99 | monday.execute("triggerFilesUpload", { 100 | boardId: 12345, 101 | itemId: 23456, 102 | columnId: "files" 103 | }); 104 | monday.execute("openAppFeatureModal", { urlPath: "/path", urlParams: {}, width: "100px", height: "100px" }); 105 | monday.execute("closeAppFeatureModal"); 106 | monday.execute("valueCreatedForUser"); 107 | 108 | monday.execute("addDocBlock", { 109 | type: "normal text", 110 | content: { deltaFormat: [{ insert: "test" }] } 111 | }); 112 | 113 | monday.execute("updateDocBlock", { 114 | id: "1234-1234-23434dsf", 115 | content: { deltaFormat: [{ insert: "test" }] } 116 | }); 117 | 118 | monday.execute("addMultiBlocks", { 119 | afterBlockId: "1234-1234-23434dsf", 120 | blocks: [ 121 | { 122 | type: "normal text", 123 | content: { deltaFormat: [{ insert: "test" }] } 124 | } 125 | ] 126 | }); 127 | monday.execute("closeDocModal"); 128 | 129 | monday.oauth({ clientId: "clientId" }); 130 | 131 | monday.storage.instance.getItem("test").then(res => { 132 | const { data }: { data: { error?: string; success: boolean }; errorMessage?: string } = res; 133 | }); 134 | 135 | monday.storage.instance.setItem("test", "123").then(res => { 136 | const { data }: { data: { error?: string; success: boolean }; errorMessage?: string } = res; 137 | }); 138 | 139 | monday.storage.instance.deleteItem("test").then(res => { 140 | const { data }: { data: { error?: string; success: boolean }; errorMessage?: string } = res; 141 | }); 142 | 143 | monday.storage.getItem("test").then(res => { 144 | const { data }: { data: { error?: string; success: boolean }; errorMessage?: string } = res; 145 | }); 146 | 147 | monday.storage.setItem("test", "123").then(res => { 148 | const { data }: { data: { error?: string; success: boolean }; errorMessage?: string } = res; 149 | }); 150 | 151 | monday.storage.deleteItem("test").then(res => { 152 | const { data }: { data: { error?: string; success: boolean }; errorMessage?: string } = res; 153 | }); 154 | 155 | const mondayServer = mondaySdk({ token: "123", apiVersion: "2023-10" }); 156 | 157 | mondayServer.setToken("123"); 158 | mondayServer.setApiVersion("2023-10"); 159 | mondayServer.api("test"); 160 | mondayServer.api("test", { token: "test" }); 161 | mondayServer.api("test", { variables: { variable1: "test" } }); 162 | mondayServer.api("test", { token: "test", variables: { variable1: "test" } }); 163 | mondayServer.api("test", { token: "test", apiVersion: "2023-07" }); 164 | mondayServer.oauthToken("test", "test", "test"); 165 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "lib": [ 5 | "es6" 6 | ], 7 | "noImplicitAny": true, 8 | "noImplicitThis": true, 9 | "strictFunctionTypes": true, 10 | "strictNullChecks": true, 11 | "noEmit": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "esModuleInterop": true 14 | }, 15 | "files": [ 16 | "types/index.d.ts", 17 | "ts-tests/monday-sdk-js-module.test.ts" 18 | ] 19 | } -------------------------------------------------------------------------------- /types/client-api.interface.ts: -------------------------------------------------------------------------------- 1 | export interface APIOptions { 2 | /** 3 | * Access token for the API 4 | * If not set, will use the credentials of the current user (client only) 5 | */ 6 | token?: string | undefined; 7 | 8 | /** 9 | * An object containing GraphQL query variables 10 | */ 11 | variables?: object | undefined; 12 | 13 | /** 14 | * A string specifying which version of the API should be used 15 | * If not set, will use the current API version 16 | */ 17 | apiVersion?: string | undefined; 18 | } 19 | 20 | interface OAuthOptions { 21 | /** 22 | * The OAuth client ID of the requesting application 23 | * Defaults to your client ID 24 | */ 25 | clientId?: string | undefined; 26 | 27 | /** 28 | * The URL of the monday OAuth endpoint 29 | */ 30 | mondayOauthUrl?: string | undefined; 31 | } 32 | 33 | export interface ClientApi { 34 | /** 35 | * Used for querying the monday.com GraphQL API seamlessly on behalf of the connected user, or using a provided API token. 36 | * For more information about the GraphQL API and all queries and mutations possible, read the [API Documentation](https://monday.com/developers/v2) 37 | * @param query A [GraphQL](https://graphql.org/) query, can be either a query (retrieval operation) or a mutation (creation/update/deletion operation). 38 | * Placeholders may be used, which will be substituted by the variables object passed within the options. 39 | * @param options 40 | */ 41 | api(query: string, options?: APIOptions): Promise<{ data: T, account_id: number }>; 42 | 43 | /** 44 | * Instead of passing the API token to the `api()` method on each request, you can set the API token once using: 45 | * @param token Access token for the API 46 | */ 47 | setToken(token: string): void; 48 | 49 | /** 50 | * Allows to set the API version for future requests. 51 | * @param version A string specifying which version of the API should be used 52 | */ 53 | setApiVersion(version: string): void; 54 | 55 | /** 56 | * Performs a client-side redirection of the user to the monday OAuth screen with your client ID embedded in the URL, 57 | * sin order to get their approval to generate a temporary OAuth token based on your requested permission scopes. 58 | * @param object An object with options 59 | */ 60 | oauth(object?: OAuthOptions): void; 61 | } 62 | -------------------------------------------------------------------------------- /types/client-context.type.ts: -------------------------------------------------------------------------------- 1 | import { Theme } from "./theme-config.type"; 2 | type User = { 3 | id: string; 4 | isAdmin: boolean; 5 | isGuest: boolean; 6 | isViewOnly: boolean; 7 | countryCode: string; 8 | currentLanguage: string; 9 | timeFormat: string; 10 | timeZoneOffset: number; 11 | }; 12 | 13 | type Account = { 14 | id: string; 15 | }; 16 | 17 | type App = { 18 | id: number; 19 | clientId: string; 20 | }; 21 | 22 | type AppVersion = { 23 | id: number; 24 | name: string; 25 | status: string; 26 | type: string; 27 | versionData: { 28 | major: number; 29 | minor: number; 30 | patch: number; 31 | type: string; 32 | }; 33 | }; 34 | 35 | export type Permissions = { 36 | approvedScopes: string[]; 37 | requiredScopes: string[]; 38 | }; 39 | 40 | export type AppSubscription = { 41 | /** 42 | * The billing period frequency: monthly or yearly 43 | */ 44 | billing_period: "yearly" | "monthly"; 45 | /** 46 | * The number of days left until the subscription ends 47 | */ 48 | days_left: number; 49 | /** 50 | * Returns true if it is still a trial subscription 51 | */ 52 | is_trial: boolean; 53 | /** 54 | * The maximum number of seats allowed for seat-based plans. Returns null for feature-based plans 55 | */ 56 | max_units: number | null; 57 | /** 58 | * The subscription plan ID from the app's side 59 | */ 60 | plan_id: string; 61 | /** 62 | * The subscription's pricing version 63 | */ 64 | pricing_version: number; 65 | /** 66 | * The date when the subscription renews, in ISO 8601 format 67 | */ 68 | renewal_date: string; 69 | }; 70 | 71 | export type BaseContext = { 72 | themeConfig?: Theme; 73 | theme: string; 74 | account: Account; 75 | user: User; 76 | region: string; 77 | app: App; 78 | appVersion: AppVersion; 79 | permissions: Permissions; 80 | subscription?: AppSubscription; 81 | }; 82 | 83 | export type AppFeatureBoardViewContext = BaseContext & { 84 | boardId: number; 85 | boardIds: number[]; 86 | boardViewId: number; 87 | viewMode: string; 88 | instanceId: number; 89 | instanceType: string; 90 | workspaceId: number; 91 | }; 92 | 93 | export type AppFeatureAiBoardMainMenuHeaderContext = BaseContext & { 94 | location: string; 95 | locationContext: { 96 | boardId: number; 97 | workspaceId: number; 98 | }; 99 | appFeatureId: number; 100 | withExternalWidth: boolean; 101 | withHeaderPadding: boolean; 102 | boardId: number; 103 | workspaceId: number; 104 | }; 105 | 106 | export type AppFeatureDashboardWidgetContext = BaseContext & { 107 | boardIds: number[]; 108 | widgetId: number; 109 | viewMode: string; 110 | editMode: boolean; 111 | instanceId: number; 112 | instanceType: string; 113 | }; 114 | 115 | export type AppFeatureItemMenuActionContext = BaseContext & { 116 | boardId: number; 117 | pulseId: number; 118 | itemId: number; 119 | }; 120 | 121 | export type AppFeatureItemBatchActionContext = BaseContext & { 122 | boardId: number; 123 | itemId: number; 124 | selectedPulsesIds: number[]; 125 | }; 126 | 127 | export type AppFeatureGroupMenuActionContext = BaseContext & { 128 | groupId: string; 129 | boardId: number; 130 | groupColor: string; 131 | }; 132 | 133 | export type AppFeatureObjectContext = BaseContext & { 134 | boardId: number; 135 | boardIds: [number]; 136 | workspaceId: number; 137 | appFeatureId: number; 138 | instanceId: number; 139 | instanceType: string; 140 | isFullScreen: boolean; 141 | isPresentingMode: boolean; 142 | objectPermissions: string; 143 | isFirstLevelControlPinned: boolean; 144 | isSlidePanelOpen: boolean; 145 | boardLoadingState: number; 146 | }; 147 | 148 | export type AppFeatureWorkspaceViewContext = BaseContext & { 149 | workspaceId: number; 150 | }; 151 | 152 | export type AppFeatureItemViewContext = BaseContext & { 153 | workspaceId: number; 154 | boardId: number; 155 | boardIds: [number]; 156 | itemId: number; 157 | instanceId: number; 158 | instanceType: string; 159 | }; 160 | 161 | export type AppFeatureAiDocQuickStartType = BaseContext & { 162 | location: string; 163 | locationContext: { 164 | docId: number; 165 | objectId: number; 166 | workspaceId: number; 167 | additionalSdkMethodsList: string[]; 168 | }; 169 | appFeatureId: number; 170 | withExternalWidth: boolean; 171 | withHeaderPadding: boolean; 172 | docId: number; 173 | objectId: number; 174 | workspaceId: number; 175 | additionalSdkMethodsList: string[]; 176 | }; 177 | 178 | export type AppFeatureAiDocTopBarContext = BaseContext & { 179 | location: string; 180 | locationContext: { 181 | input: string; 182 | docId: number; 183 | objectId: number; 184 | workspaceId: number; 185 | additionalSdkMethodsList: string[]; 186 | }; 187 | appFeatureId: number; 188 | withExternalWidth: boolean; 189 | withHeaderPadding: boolean; 190 | input: string; 191 | docId: number; 192 | objectId: number; 193 | workspaceId: number; 194 | additionalSdkMethodsList: string[]; 195 | }; 196 | export type FocusedBlock = { 197 | id: string; 198 | createdUserId: number; 199 | accountId: number; 200 | docId: number; 201 | type: string; 202 | content: { 203 | alignment: string; 204 | direction: string; 205 | deltaFormat: Array<{ 206 | insert: string; 207 | }>; 208 | base64Encoded: string; 209 | }; 210 | position: number; 211 | parentBlockId: string | null; 212 | createdAt: string; 213 | updatedAt: string; 214 | }; 215 | 216 | export type AppFeatureAiDocSlashCommandContext = BaseContext & { 217 | location: string; 218 | locationContext: { 219 | focusedBlocks: FocusedBlock[]; 220 | canMoveToNextSelectedTextualBlock: boolean; 221 | canMoveToPrevSelectedTextualBlock: boolean; 222 | input: string; 223 | isTextSelectedInBlock: boolean; 224 | docId: number; 225 | objectId: number; 226 | workspaceId: number; 227 | additionalSdkMethodsList: string[]; 228 | }; 229 | appFeatureId: number; 230 | withExternalWidth: boolean; 231 | withHeaderPadding: boolean; 232 | focusedBlocks: FocusedBlock[]; 233 | canMoveToNextSelectedTextualBlock: boolean; 234 | canMoveToPrevSelectedTextualBlock: boolean; 235 | input: string; 236 | isTextSelectedInBlock: boolean; 237 | docId: number; 238 | objectId: number; 239 | workspaceId: number; 240 | additionalSdkMethodsList: string[]; 241 | }; 242 | 243 | export type AppFeatureDocActionsContext = BaseContext & { 244 | themeConfig: Theme; 245 | docId: number; 246 | objectId: number; 247 | workspaceId: number; 248 | focusedBlocks: FocusedBlock[]; 249 | placement: string; 250 | highlightedText: string; 251 | range: { 252 | index: number; 253 | length: number; 254 | }; 255 | blockId: string; 256 | }; 257 | 258 | export type AppFeatureContextMap = { 259 | Base: BaseContext; 260 | AppFeatureBoardView: AppFeatureBoardViewContext; 261 | AppFeatureAiBoardMainMenuHeader: AppFeatureAiBoardMainMenuHeaderContext; 262 | AppFeatureDashboardWidget: AppFeatureDashboardWidgetContext; 263 | AppFeatureItemMenuAction: AppFeatureItemMenuActionContext; 264 | AppFeatureItemBatchAction: AppFeatureItemBatchActionContext; 265 | AppFeatureGroupMenuAction: AppFeatureGroupMenuActionContext; 266 | AppFeatureObject: AppFeatureObjectContext; 267 | AppFeatureWorkspaceView: AppFeatureWorkspaceViewContext; 268 | AppFeatureItemView: AppFeatureItemViewContext; 269 | AppFeatureAiDocQuickStart: AppFeatureAiDocQuickStartType; 270 | AppFeatureAiDocTopBar: AppFeatureAiDocTopBarContext; 271 | AppFeatureAiDocSlashCommand: AppFeatureAiDocSlashCommandContext; 272 | AppFeatureDocActions: AppFeatureDocActionsContext; 273 | }; 274 | 275 | export type AppFeatureTypes = keyof AppFeatureContextMap; 276 | -------------------------------------------------------------------------------- /types/client-data.interface.ts: -------------------------------------------------------------------------------- 1 | import { AppFeatureContextMap, AppFeatureTypes } from "./client-context.type"; 2 | 3 | export type LocationResponse = Record & { 4 | href: string; 5 | search: string; 6 | }; 7 | 8 | export type FilterResponse = Record & { 9 | term: string; 10 | rules: (Record & { 11 | column_id?: string; 12 | compare_value?: string[]; 13 | compare_attribute?: string; 14 | operator?: string; 15 | })[]; 16 | operator: string | null; 17 | }; 18 | 19 | type SubscribableEventsResponse = { 20 | context: AppFeatureContextMap[AppFeatureType]; 21 | settings: Record; 22 | itemIds: number[]; 23 | events: Record; 24 | location: LocationResponse; 25 | filter: FilterResponse; 26 | }; 27 | 28 | type SubscribableEvents = keyof SubscribableEventsResponse; 29 | 30 | type SettableTypes = "settings"; 31 | 32 | type StorageResponse = { 33 | success: boolean; 34 | value: any; 35 | version?: any; 36 | }; 37 | 38 | type Response = { 39 | data: T; 40 | errorMessage?: string | undefined; 41 | method: string; 42 | requestId: string; 43 | type?: string | undefined; 44 | }; 45 | 46 | type DeleteResponse = { 47 | data: { 48 | success: boolean; 49 | value: any; 50 | }; 51 | errorMessage?: string | undefined; 52 | method: string; 53 | requestId: string; 54 | type?: string | undefined; 55 | }; 56 | 57 | interface SetResponse { 58 | data: { 59 | success: boolean; 60 | version: string; 61 | reason?: string | undefined; 62 | error?: string | undefined; 63 | }; 64 | errorMessage?: string | undefined; 65 | requestId: string; 66 | method: string; 67 | type?: string | undefined; 68 | } 69 | 70 | export type GetterResponse = { 71 | context: AppFeatureContextMap[AppFeatureType]; 72 | settings: Record; 73 | itemIds: number[]; 74 | sessionToken: string; 75 | location: LocationResponse; 76 | filter: FilterResponse; 77 | }; 78 | export interface ClientData { 79 | /** 80 | * Used for retrieving data from the parent monday.com application where your app is currently running. 81 | * This object can only be used when your app is running inside an `iframe`. This can only be used in client-side apps. 82 | * @param type The type of requested information (available values below) 83 | * - `'context'` Information about where this app is currently displayed, depending on the type of feature 84 | * - `'settings'` The application settings as configured by the user that installed the app 85 | * - `'itemIds'` The list of item IDs that are filtered in the current board (or all items if no filters are applied) 86 | * - `'sessionToken'` A JWT token which is decoded with your app's secret and can be used as a session token between your app's frontend & backend 87 | * @param params Reserved for future use 88 | */ 89 | get< 90 | CustomResponse, 91 | T extends keyof GetterResponse = keyof GetterResponse, 92 | AppFeatureType extends AppFeatureTypes = AppFeatureTypes 93 | >( 94 | type: T | string, 95 | params?: Record & { appFeatureType?: AppFeatureType } 96 | ): Promise[T] & CustomResponse>>; 97 | 98 | /** 99 | * Creates a listener which allows subscribing to certain types of client-side events. 100 | * @param typeOrTypes The type, or array of types, of events to subscribe to 101 | * @param callback A callback function that is fired when the listener is triggered by a client-side event 102 | * @param params Reserved for future use 103 | * @return Unsubscribe/unlisten from all added during this method call 104 | */ 105 | listen< 106 | CustomResponse, 107 | T extends SubscribableEvents = SubscribableEvents, 108 | AppFeatureType extends AppFeatureTypes = AppFeatureTypes 109 | >( 110 | typeOrTypes: (T | string) | ReadonlyArray, 111 | callback: (res: { data: SubscribableEventsResponse[T] & CustomResponse }) => void, 112 | params?: Record & { appFeatureType?: AppFeatureType } 113 | ): () => void; 114 | 115 | /** 116 | * Set data in your application, such as updating settings 117 | * @param type The type of data that can be set 118 | * @param params object containing the data you want to update 119 | */ 120 | set(type: SettableTypes, params: object): Promise; 121 | 122 | /** 123 | * The Storage API is in early beta stages, its API is likely to change 124 | * 125 | * The monday apps infrastructure includes a persistent, key-value database storage that developers 126 | * can leverage to store data without having to create their own backend and maintain their own database. 127 | * 128 | * The database currently offers instance-level storage only, meaning that each application instance (i.e. a single board view or a dashboard widget) maintains its own storage. 129 | * Apps cannot share storage across accounts or even across apps installed in the same location. 130 | */ 131 | storage: { 132 | /** 133 | * Returns a stored value from the database under `key` for the app (**without any reference to the instance**) 134 | * @param {string} key - Used to access to stored data 135 | */ 136 | getItem(key: string): Promise; 137 | 138 | /** 139 | * Deletes a stored value from the database under `key` for the app (**without any reference to the instance**) 140 | * @param {string} key - Used to delete the stored data 141 | */ 142 | deleteItem(key: string): Promise; 143 | 144 | /** 145 | * Stores `value` under `key` in the database for the app (**without any reference to the instance**) 146 | * @param {string} key - Used to delete the stored data 147 | * @param {any} value - The value to store 148 | * @param {object=} options 149 | * @param {string=} options.previous_version - Use the new version of the storage (instance-less) 150 | * @param {number=} options.ttl - The time to live of the item in seconds 151 | */ 152 | setItem(key: string, value: any, options?: { previous_version?: string, ttl?: number }): Promise; 153 | /*** 154 | * The instance storage is a key-value database that is scoped to a specific app instance. 155 | * **Does not work** for instance-less apps. 156 | */ 157 | instance: { 158 | /** 159 | * Returns a stored value from the database under `key` for a specific app instance 160 | * @param key 161 | */ 162 | getItem(key: string): Promise; 163 | 164 | /** 165 | * Deletes a stored value from the database under `key` for a specific app instance 166 | * @param key 167 | */ 168 | deleteItem(key: string): Promise; 169 | 170 | /** 171 | * Stores `value` under `key` in the database for a specific app instance 172 | * @param key 173 | * @param value 174 | * @param options 175 | */ 176 | setItem(key: string, value: any, options?: { previous_version?: string }): Promise; 177 | }; 178 | }; 179 | } 180 | -------------------------------------------------------------------------------- /types/client-execute.interface.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Blocks that are supported by our SDK 3 | */ 4 | type BlockTypes = 'normal text' | 'large title' | 'medium title' | 'small title' | 'bulleted list' | 'numbered list' | 'quote' | 'check list' | 'code'; 5 | 6 | /** 7 | * Block content in delta format 8 | */ 9 | interface BlockContent { deltaFormat: Array }; 10 | export interface ClientExecute { 11 | /** 12 | * Type fallback to account for new execute methods during the AI hackathon. 13 | * This will be removed when the 0.4.0 version goes out of beta. 14 | */ 15 | execute (type: any, params?: any): Promise; 16 | 17 | /** 18 | * Opens a popup card with information from the selected item 19 | * @param type Which action to perform 20 | * @param params Optional parameters for the action 21 | */ 22 | execute( 23 | type: 'openItemCard', 24 | params: { 25 | /** 26 | * The ID of the item to open 27 | */ 28 | itemId: number; 29 | 30 | /** 31 | * On which view to open the item card. 32 | * Can be "updates" / "columns" 33 | * Defaults to "columns" 34 | */ 35 | kind?: 'updates' | 'columns' | undefined; 36 | }, 37 | ): Promise; 38 | /** 39 | * Opens a confirmation dialog to the user **type** `'confirm'` 40 | * @param type Which action to perform 41 | * @param params Optional parameters for the action 42 | */ 43 | execute( 44 | type: 'confirm', 45 | params: { 46 | /** 47 | * The message to display in the dialog 48 | */ 49 | message: string; 50 | 51 | /** 52 | * The text for the confirmation button 53 | * Defaults to "OK" 54 | */ 55 | confirmButton?: string | undefined; 56 | 57 | /** 58 | * The text for the cancel button 59 | * Defaults to "Cancel" 60 | */ 61 | cancelButton?: string | undefined; 62 | 63 | /** 64 | * Either to exclude the cancel button 65 | * Defaults to `false` 66 | */ 67 | excludeCancelButton?: boolean | undefined; 68 | }, 69 | ): Promise<{ data: { confirm: boolean } }>; 70 | /** 71 | * Display a message at the top of the user's page. Useful for success, error & general messages. 72 | * @param type Which action to perform 73 | * @param params Optional parameters for the action 74 | */ 75 | execute( 76 | type: 'notice', 77 | params: { 78 | /** 79 | * The message to display 80 | */ 81 | message: string; 82 | 83 | /** 84 | * The type of message to display. Can be "success" (green), "error" (red) or "info" (blue) 85 | * Defaults to "info" 86 | */ 87 | type?: 'success' | 'error' | 'info' | undefined; 88 | 89 | /** 90 | * The number of milliseconds to show the message until it closes 91 | * Defaults to 5000 92 | */ 93 | timeout?: number | undefined; 94 | }, 95 | ): Promise; 96 | /** 97 | * Opens a modal with the preview of an asset 98 | * @param type Which action to perform 99 | * @param params Optional parameters for the action 100 | */ 101 | execute( 102 | type: 'openFilesDialog', 103 | params: { 104 | /** 105 | * The ID of the board 106 | */ 107 | boardId: number; 108 | 109 | /** 110 | * The ID of the item, which contains an asset 111 | */ 112 | itemId: number; 113 | 114 | /** 115 | * The ID of the column, which contains an asset 116 | */ 117 | columnId: string; 118 | 119 | /** 120 | * The ID of the asset to open 121 | */ 122 | assetId: number; 123 | }, 124 | ): Promise; 125 | /** 126 | * Opens a modal to let the current user upload a file to a specific file column. 127 | * 128 | * Returns a promise. In case of error, the promise is rejected 129 | * 130 | * After the file is successfully uploaded, the "change_column_value" event will be triggered. See the {@link listen} method to subscribe to these events. 131 | * 132 | * _Requires boards:write scope_ 133 | * @param type Which action to perform 134 | * @param params Optional parameters for the action 135 | */ 136 | execute( 137 | type: 'triggerFilesUpload', 138 | params: { 139 | /** 140 | * The ID of the board 141 | */ 142 | boardId: number; 143 | 144 | /** 145 | * The ID of the item, which contains an asset 146 | */ 147 | itemId: number; 148 | 149 | /** 150 | * The ID of the file column, where file should be uploaded 151 | */ 152 | columnId: string; 153 | }, 154 | ): Promise; 155 | /** 156 | * Opens a new modal window as an iFrame. 157 | * @param type Which action to perform 158 | * @param params Optional parameters for the action 159 | */ 160 | execute( 161 | type: 'openAppFeatureModal', 162 | params: { 163 | /** 164 | * The URL of the page displayed in the modal 165 | * Defaults to current iFrame's URL 166 | */ 167 | url?: string; 168 | 169 | /** 170 | * Subdirectory or path of the URL to open 171 | */ 172 | urlPath?: string; 173 | 174 | /** 175 | * Query parameters for the URL 176 | */ 177 | urlParams?: Record; 178 | 179 | /** 180 | * The width of the modal 181 | * Defaults to "0px" 182 | */ 183 | width?: string; 184 | 185 | /** 186 | * The height of the modal 187 | * Defaults to "0px" 188 | */ 189 | height?: string; 190 | }, 191 | ): Promise<{ data: any }>; 192 | /** 193 | * Opens the provided link in a new tab. 194 | * @param type Which action to perform 195 | * @param params Optional parameters for the action 196 | */ 197 | execute( 198 | type: 'openLinkInTab', 199 | params: { 200 | /** 201 | * The URL to open a new tab with 202 | */ 203 | url: string; 204 | }, 205 | ): Promise<{ data: Record }>; 206 | /** 207 | * Doc command, This method adds multiple blocks to the beginning of a workdoc using HTML.. 208 | * @param type Which action to perform 209 | */ 210 | execute(type: 'addMultiBlocksFromHtml'): Promise<{ html: string }>; /** 211 | /** 212 | * Doc command, This method opens the app modal on the first selected block when multiple are open. 213 | * @param type Which action to perform 214 | */ 215 | execute(type: 'openAppOnFirstTextualSelectedBlock'): Promise<{ data: any }>; /** 216 | /** 217 | * Doc command This method opens the app on the next block when multiple are selected. This is only available after calling the "openAppOnFirstTextualBlock" function. 218 | * @param type Which action to perform 219 | */ 220 | execute(type: 'moveToNextSelectedTextualBlock'): Promise<{ data: any }>; /** 221 | /** 222 | * Doc command This method opens the app on the next block when multiple are selected. This is only available after calling the "openAppOnFirstTextualBlock" function. 223 | * @param type Which action to perform 224 | */ 225 | execute(type: 'moveToPrevSelectedTextualBlock'): Promise<{ data: any }>; /** 226 | /** 227 | * Doc command, This method replaces the highlighted text with text of your choosing at the beginning of the block. 228 | * @param type Which action to perform 229 | */ 230 | execute(type: 'replaceHighlightText'): Promise<{ text: string }>; /** 231 | /** 232 | * Item Update section, This method creates or changes the content of an item's update. 233 | * @param type Which action to perform 234 | */ 235 | execute(type: 'updatePostContentAction'): Promise<{ suggestedRephrase: string }>; /** 236 | /** 237 | * This method closes the AI assistant's dialog. 238 | * @param type Which action to perform 239 | */ 240 | execute(type: 'closeDialog'): Promise<{ data: any }>; /** 241 | * Closes the modal window. 242 | * @param type Which action to perform 243 | */ 244 | execute(type: 'closeAppFeatureModal'): Promise<{ data: any }>; 245 | /** 246 | * Notifies the monday platform when a user gains a first value in your app. 247 | * @param type Which action to perform 248 | */ 249 | execute(type: 'valueCreatedForUser'): Promise; 250 | /** 251 | * Adds a new block to a workdoc. 252 | * @param type Which action to perform 253 | * @param params The new block's data 254 | */ 255 | execute( 256 | type: 'addDocBlock', 257 | params: { 258 | /** 259 | * The block type 260 | */ 261 | type: BlockTypes; 262 | /** 263 | * Used to specify where in the doc the new block should go. 264 | * Provide the block's ID that will be above the new block. 265 | * Without this parameter, the block will appear at the top of the doc. 266 | */ 267 | afterBlockId?: string | undefined; 268 | /** 269 | * The block's content in Delta format. 270 | */ 271 | content: BlockContent; 272 | }, 273 | ): Promise; 274 | /** 275 | * Updates a block's content 276 | * @param type Which action to perform 277 | * @param params The updated block's data 278 | */ 279 | execute ( 280 | type: 'updateDocBlock', 281 | params: { 282 | /** 283 | * The block's unique identifier. 284 | */ 285 | id: string; 286 | /** 287 | * The block's content you want to update in Delta format. 288 | */ 289 | content: BlockContent; 290 | } 291 | ): Promise; 292 | /** 293 | * Adds multiple blocks to a workdoc. 294 | * @param type Which action to perform 295 | * @param params Data for the new blocks you want to create 296 | */ 297 | execute ( 298 | type: 'addMultiBlocks', 299 | params: { 300 | /** 301 | * The block's unique identifier. 302 | */ 303 | afterBlockId?: string | undefined; 304 | /** 305 | * The block's content in Delta format. 306 | * We support the following block types 307 | */ 308 | blocks: Array<{ type: BlockTypes, content: BlockContent}>; 309 | } 310 | ): Promise; 311 | /** 312 | * Closes the document block modal. If you don't call this method, the modal will close when the user clicks outside of it. 313 | * @param type Which action to perform 314 | */ 315 | execute (type: 'closeDocModal'): Promise; 316 | } 317 | -------------------------------------------------------------------------------- /types/client-sdk.interface.ts: -------------------------------------------------------------------------------- 1 | import { ClientData } from './client-data.interface'; 2 | import { ClientExecute } from './client-execute.interface'; 3 | import { ClientApi } from './client-api.interface'; 4 | 5 | export type MondayClientSdk = ClientData & ClientExecute & ClientApi; 6 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | // Original Definitions were contributed by: Josh Parnham 2 | import { MondayClientSdk } from "./client-sdk.interface"; 3 | import { MondayServerSdk } from "./server-sdk.interface"; 4 | 5 | export as namespace mondaySdk; 6 | 7 | declare function init( 8 | config?: Partial<{ 9 | clientId: string; 10 | apiToken: string; 11 | apiVersion: string; 12 | }> 13 | ): MondayClientSdk; 14 | 15 | declare function init( 16 | config?: Partial<{ 17 | token: string; 18 | apiVersion: string; 19 | }> 20 | ): MondayServerSdk; 21 | 22 | export { MondayClientSdk, MondayServerSdk }; 23 | 24 | export default init; 25 | -------------------------------------------------------------------------------- /types/server-sdk.interface.ts: -------------------------------------------------------------------------------- 1 | import { APIOptions } from './client-api.interface'; 2 | 3 | export interface MondayServerSdk { 4 | setToken(token: string): void; 5 | 6 | setApiVersion(version: string): void; 7 | 8 | api(query: string, options?: APIOptions): Promise; 9 | 10 | oauthToken(code: string, clientId: string, clientSecret: string): Promise; 11 | } 12 | -------------------------------------------------------------------------------- /types/theme-config.type.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * System themes: dark, light, black, hacker 3 | */ 4 | export enum SystemTheme { 5 | LIGHT = "light", 6 | DARK = "dark", 7 | BLACK = "black", 8 | HACKER = "hacker" 9 | } 10 | 11 | /** 12 | * Colors which are eligible for theming 13 | */ 14 | export enum ThemeColor { 15 | primaryColor = "primary-color", 16 | primaryHoverColor = "primary-hover-color", 17 | primarySelectedColor = "primary-selected-color", 18 | primarySelectedHoverColor = "primary-selected-hover-color", 19 | primarySelectedOnSecondaryColor = "primary-selected-on-secondary-color", 20 | textColorOnPrimary = "text-color-on-primary", 21 | brandColor = "brand-color", 22 | brandHoverColor = "brand-hover-color", 23 | brandSelectedColor = "brand-selected-color", 24 | brandSelectedHoverColor = "brand-selected-hover-color", 25 | textColorOnBrand = "text-color-on-brand" 26 | } 27 | 28 | export type Theme = { 29 | /** 30 | * The name of the theme - name of css class that will be added to the children - should be unique 31 | */ 32 | name: string; 33 | colors: SystemThemeColorMap; 34 | }; 35 | 36 | type SystemThemeColorMap = { 37 | [key in SystemTheme]?: ThemeColorTokenValueMap; 38 | }; 39 | 40 | export type ThemeColorTokenValueMap = ThemeColorTokenValue | ThemeCustomClassValue; 41 | 42 | export type ThemeColorTokenValue = { 43 | [key in ThemeColor]?: string; 44 | }; 45 | 46 | type ThemeCustomClassValue = { 47 | [key: string]: ThemeColorTokenValue | ThemeCustomClassValue; 48 | }; 49 | 50 | export const SystemThemeClassMap: SystemThemeClassMapType = { 51 | [SystemTheme.LIGHT]: "light-app-theme", 52 | [SystemTheme.DARK]: "dark-app-theme", 53 | [SystemTheme.BLACK]: "black-app-theme", 54 | [SystemTheme.HACKER]: "hacker_theme-app-theme" 55 | }; 56 | 57 | type SystemThemeClassMapType = { 58 | [key in SystemTheme]: string; 59 | }; 60 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | // webpack.config.js 2 | const path = require("path"); 3 | const webpack = require("webpack"); 4 | 5 | module.exports = env => { 6 | return { 7 | plugins: [ 8 | new webpack.DefinePlugin({ 9 | __BUNDLE__: env.WEBPACK_BUILD === "true" 10 | }) 11 | ] 12 | }; 13 | }; 14 | --------------------------------------------------------------------------------