├── .npmignore ├── README.md ├── assets ├── getAllExtensions.png └── log.png ├── index.js └── package.json /.npmignore: -------------------------------------------------------------------------------- 1 | assets -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cep-spy 2 | 3 | Slim, no-dependency utility to dynamically reveal all information about current Adobe host application, active panel using it and even it's sibling CEP extensions, all without needing CSInterface. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | npm install cep-spy 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```js 14 | // Via import: 15 | import spy from "cep-spy"; 16 | 17 | // Via require: 18 | const spy = require("cep-spy").default; 19 | ``` 20 | 21 | ## Examples: 22 | 23 | ### CEP-Spy returns as an object with various pre-calculated values and helper functions: 24 | 25 | ```js 26 | import spy from "cep-spy"; 27 | console.log(spy); 28 | ``` 29 | 30 | ![](assets/log.png) 31 | 32 | Some are simple wrappers around CSInterface's functions but returning more useful formats (no need to always `JSON.parse`), some return values that CSInterface doesn't even offer but are useful (localhost port, extension version, environment, etc) 33 | 34 | --- 35 | 36 | ### Launch the current localhost of the panel without knowing the app, panel, or localhost: 37 | 38 | ```js 39 | import spy from "cep-spy"; 40 | 41 | spy.launchLocalhost(); // Launches in default browser 42 | 43 | console.log(spy.localhost); // Returns `http://localhost:####` 44 | ``` 45 | 46 | --- 47 | 48 | ### Easily get absolute values about your panel/environment regardless of OS: 49 | 50 | ```js 51 | const spy = require("cep-spy").default; 52 | 53 | console.log(spy.path.root); // Returns 'C:/Users/.../[your-panel-root]' 54 | console.log(spy.extVersion); // Returns '1.0.0', as defined in manifest.xml 55 | console.log(spy.isDev); // Returns BOOL true if bombino and while hot reloading 56 | console.log(spy.appName); // Returns 'ILST' or current param of host app 57 | ``` 58 | 59 | --- 60 | 61 | ### Spy on all your colleagues: 62 | 63 | ```js 64 | import spy from "cep-spy"; 65 | 66 | let siblings = spy.getAllExtensions(); 67 | console.log(siblings); 68 | ``` 69 | 70 | ![](assets/getAllExtensions.png) 71 | 72 | --- 73 | 74 | ## API 75 | 76 | ### Static values 77 | 78 | - `path.root` [STRING]: The absolute path (pre-treated) of the current extension 79 | - `path.userData` [STRING]: The absolute path (pre-treated) to CS's `userData` 80 | - `path.commonFiles` [STRING]: The absolute path (pre-treated) to CS's `commonFiles` 81 | - `path.myDocuments` [STRING]: The absolute path (pre-treated) to CS's `myDocuments` 82 | - `path.hostApplication` [STRING]: The absolute path (pre-treated) to the app's `hostApplication` executable 83 | - `package` [OBJECT]: The JSON-parsed contents of the panel's root `package.json` file 84 | - `extID` [STRING]: `id` of the current extension as seen in manifest.xml (eg `com.adobe.panel`) 85 | - `author` [STRING/OBJECT]: Returns the `author` parameter of `package.json`, either as a string or an Object with `name`, `email`, and `url` key-values. 86 | - `extName` [STRING]: Proper name as displayed in Menu of the current extension 87 | - `extVersion` [STRING]: The major, minor, micro versioning of the extension (eg `1.x.x`) in manifest.xml 88 | - `ext` [OBJECT]: Object containing CSInterface data for current extension (height, mainPath, width, windowType, etc.) 89 | - `localhost` [STRING]: The direct URL including `"https://localhost:"` of the current extension 90 | - `isDev` [BOOL]: True if extension"s `document.location` contains `"localhost"` (if bombino panel during hot reload for development) 91 | - `appName` [STRING]: The 4-letter app identifier from `` (eg `"ILST"`, `"AEFT"`) 92 | - `appLocale` [STRING]: The language/region locale identifier (eg `"en_US"`) 93 | - `appVersion` [STRING]: The major/minor/micro of the host app (eg `24.0.0`) 94 | - `userAgent` [STRING]: If OS is `Windows` or `Mac` 95 | - `cepVersion` [STRING]: The major/minor/micro of CEP engine 96 | - `hostCapabilities` [OBJECT]: The extended parameters for host under the same name in CSInterface 97 | - `userId` [STRING]: `CSInterface.getCurrentImsUserId()` wrapper (returns null often) 98 | - `exts` [ARRAY]: Array of all CEP extensions (as objects with base data) in current host app. 99 | 100 | ### Functions 101 | 102 | - `getAllExtensions()`: Returns array of all extension data including version from each manifest.xml 103 | - `getExtData(id)`: Returns extension data object of matching `id` 104 | - `openExtension(ext)`: Requests a given extension launch by extension data object 105 | - `getVersion(ext)`: Returns a major/minor/micro from given extension data object 106 | - `launchLocalhost()`: Launches any given localhost or the current extension's localhost if none 107 | - `launchHomepage()`: Launches the homepage given in `package.json`'s `homepage` attribute 108 | - `launchGitRepo()`: Launches the GitHub repo link given in `package.json`'s `repository` attribute 109 | -------------------------------------------------------------------------------- /assets/getAllExtensions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Inventsable/CEP-Spy/a16fbd4c25105316c522d3698ce3f4e425aa4ce6/assets/getAllExtensions.png -------------------------------------------------------------------------------- /assets/log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Inventsable/CEP-Spy/a16fbd4c25105316c522d3698ce3f4e425aa4ce6/assets/log.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const path = window.__adobe_cep__ ? require("path") : null; 2 | const fs = window.__adobe_cep__ ? require("fs") : null; 3 | 4 | /** 5 | * 6 | * @param {String} str - The path to attempt resolution to 7 | * @param {Boolean} asJSON - If data should return as parsed JSON 8 | */ 9 | function resolveString(str, asJSON = false) { 10 | let result; 11 | result = path.resolve( 12 | str.replace( 13 | /file\:\/{1,}/, 14 | navigator.platform.indexOf("Mac") > -1 ? "/" : "" 15 | ) 16 | ); 17 | if (navigator.platform.indexOf("Mac") > -1) 18 | result = result.replace(/\\{1}/gm, "/"); 19 | return asJSON ? JSON.parse(result) : result; 20 | } 21 | 22 | function getRoot() { 23 | let result = resolveString(window.__adobe_cep__.getSystemPath("extension")); 24 | if (fs.existsSync(result)) return result; 25 | else if (fs.existsSync(decodeURI(result))) return decodeURI(result); 26 | else if (fs.existsSync(result.replace(/%20/gm, " "))) 27 | return result.replace.replace(/%20/gm, " "); 28 | else if (fs.existsSync(path.resolve(result))) return path.resolve(result); 29 | else { 30 | console.log("ROOT RESOLUTION FAILED:", result); 31 | return result; 32 | } 33 | } 34 | 35 | /** 36 | * 37 | * @param {String} root - Base extension path 38 | * @param {String} file - Name of file to read 39 | * @param {String} key - When a parsed JSON, return as this key value 40 | * @param {Boolean} asJSON - If data should return as parsed JSON 41 | */ 42 | 43 | function tryReadingFile(root, file, key = "", asJSON = false) { 44 | root = root && root.length ? root : getRoot(); 45 | let target = resolveString(`${root}/${file}`); 46 | if (fs.existsSync(target)) { 47 | let data = fs.readFileSync(target, "utf-8"); 48 | return asJSON 49 | ? key || key.length 50 | ? JSON.parse(data)[key] 51 | ? JSON.parse(data)[key] 52 | : "" 53 | : JSON.parse(data) 54 | : data; 55 | } else if (fs.existsSync(target.replace(/%20/gm, " "))) { 56 | let data = fs.readFileSync(target.replace(/%20/gm, " "), "utf-8"); 57 | return asJSON 58 | ? key || key.length 59 | ? JSON.parse(data)[key] 60 | ? JSON.parse(data)[key] 61 | : "" 62 | : JSON.parse(data) 63 | : data; 64 | } else if (fs.existsSync(decodeURI(target))) { 65 | console.log("DecodeURI does work"); 66 | let data = fs.readFileSync(decodeURI(target), "utf-8"); 67 | return asJSON 68 | ? key || key.length 69 | ? JSON.parse(data)[key] 70 | ? JSON.parse(data)[key] 71 | : "" 72 | : JSON.parse(data) 73 | : data; 74 | } else { 75 | console.error("Target not found:", file, target); 76 | return null; 77 | } 78 | } 79 | 80 | /** 81 | * Should be a platform independent way to get localhost 82 | */ 83 | function getLocalHost() { 84 | let data = tryReadingFile("", "/.debug"); 85 | if (!data) return "http://localhost:8081"; 86 | return `http://localhost:${ 87 | data.match( 88 | new RegExp( 89 | `\\ { 120 | if (item == "root") spy.path[item] = getRoot(); 121 | else if (fs.existsSync(spy.path[item].replace(/\%20/gm, " "))) 122 | spy.path[item] = spy.path[item].replace(/\%20/gm, " "); 123 | else if (fs.existsSync(decodeURI(spy.path[item]))) 124 | spy.path[item] = decodeURI(spy.path[item]); 125 | else { 126 | console.log("NO MATCH:", item, spy.path[item]); 127 | } 128 | }); 129 | } 130 | return spy; 131 | } else if (fs.existsSync(target.replace(/%20/g, " "))) { 132 | Object.keys(spy.path).forEach((item) => { 133 | spy.path[item] = spy.path[item].replace(/%20/g, " "); 134 | }); 135 | } else { 136 | console.error("Double check failed."); 137 | return spy; 138 | } 139 | return spy; 140 | } 141 | 142 | // If this is browser, return a fake object with static values. Otherwise, be dynamic per host app 143 | let spy = !window.__adobe_cep__ 144 | ? { 145 | path: { 146 | root: 147 | "C:/Users/TRSch/AppData/Roaming/Adobe/CEP/extensions/panelify-demo", 148 | userData: "C:/Users/TRSch/AppData/Roaming", 149 | commonFiles: "C:/Program Files/Common Files", 150 | myDocuments: "C:/Users/TRSch/OneDrive/Documents", 151 | hostApplication: 152 | "C:/Program Files/Adobe/Adobe Illustrator 2020/Support Files/Contents/Windows/Illustrator.exe", 153 | }, 154 | package: { 155 | name: "panelify-demo", 156 | version: "1.0.0", 157 | description: "Bombino-quasar-panelify template", 158 | productName: "Quasar Panelify", 159 | cordovaId: "org.cordova.panelify", 160 | repository: "Inventsable/bombino-quasar-panelify", 161 | author: "Tom Scharstein II ", 162 | homepage: "https://github.com/Inventsable/panelify-demo", 163 | capacitorId: "", 164 | private: true, 165 | scripts: { 166 | serve: "quasar dev", 167 | build: "quasar build", 168 | sign: "bombino-cmd sign", 169 | switch: "bombino-cmd switch", 170 | update: "bombino-cmd update", 171 | register: "bombino-cmd register", 172 | help: "bombino-cmd help", 173 | convert: "node ./bin/convertToPanelify.js", 174 | }, 175 | dependencies: { 176 | "@quasar/extras": "^1.0.0", 177 | "cep-spy": "^1.1.1", 178 | cluecumber: "0.0.31", 179 | "lottie-web": "^5.5.9", 180 | quasar: "^1.0.0", 181 | starlette: "^0.4.5", 182 | }, 183 | devDependencies: { 184 | "@quasar/app": "^1.0.0", 185 | "@quasar/quasar-app-extension-dotenv": "^1.0.0", 186 | "bombino-commands": "^1.0.1", 187 | chalk: "^3.0.0", 188 | "fs-extra": "^8.1.0", 189 | inquirer: "^7.0.0", 190 | "types-for-adobe": "^1.5.0", 191 | }, 192 | engines: { 193 | node: ">= 8.9.0", 194 | npm: ">= 5.6.0", 195 | yarn: ">= 1.6.0", 196 | }, 197 | browserslist: ["last 1 version, not dead, ie >= 11"], 198 | }, 199 | author: "Tom Scharstein II ", 200 | repository: "Inventsable/bombino-quasar-panelify", 201 | homepage: "https://github.com/Inventsable/panelify-demo", 202 | localhost: "http://localhost:2266", 203 | isDev: true, 204 | extVersion: "1.0.0", 205 | appName: "ILST", 206 | appLocale: "en_US", 207 | appVersion: "24.0.0", 208 | userAgent: "Windows", 209 | cepVersion: "9.4.0", 210 | hostCapabilities: { 211 | DISABLE_FLASH_EXTENSIONS: false, 212 | EXTENDED_PANEL_ICONS: true, 213 | SUPPORT_HTML_EXTENSIONS: true, 214 | DELEGATE_APE_ENGINE: false, 215 | EXTENDED_PANEL_MENU: true, 216 | }, 217 | userId: "", 218 | extID: "com.panelify-demo.panel", 219 | exts: [], 220 | ext: { 221 | mainPath: 222 | "C:\\Users\\TRSch\\AppData\\Roaming\\Adobe\\CEP\\extensions\\panelify-demo\\src\\index-dev.html", 223 | maxHeight: 500, 224 | maxWidth: 598, 225 | specialExtensionDataXML: "", 226 | id: "com.panelify-demo.panel", 227 | name: "panelify-demo", 228 | width: 280, 229 | windowType: "Panel", 230 | isAutoVisible: true, 231 | basePath: 232 | "C:\\Users\\TRSch\\AppData\\Roaming\\Adobe\\CEP\\extensions\\panelify-demo", 233 | height: 400, 234 | minWidth: 260, 235 | requiredRuntimeList: [ 236 | { 237 | name: "CSXS", 238 | versionRange: { 239 | lowerBound: { 240 | version: { 241 | minor: 0, 242 | micro: 0, 243 | major: 8, 244 | }, 245 | inclusive: true, 246 | }, 247 | }, 248 | }, 249 | ], 250 | minHeight: 300, 251 | isPluginExtension: false, 252 | defaultExtensionDataXML: "", 253 | }, 254 | activeExt: { 255 | mainPath: 256 | "C:\\Users\\TRSch\\AppData\\Roaming\\Adobe\\CEP\\extensions\\panelify-demo\\src\\index-dev.html", 257 | maxHeight: 500, 258 | maxWidth: 598, 259 | specialExtensionDataXML: "", 260 | id: "com.panelify-demo.panel", 261 | name: "panelify-demo", 262 | width: 280, 263 | windowType: "Panel", 264 | isAutoVisible: true, 265 | basePath: 266 | "C:\\Users\\TRSch\\AppData\\Roaming\\Adobe\\CEP\\extensions\\panelify-demo", 267 | height: 400, 268 | minWidth: 260, 269 | requiredRuntimeList: [ 270 | { 271 | name: "CSXS", 272 | versionRange: { 273 | lowerBound: { 274 | version: { 275 | minor: 0, 276 | micro: 0, 277 | major: 8, 278 | }, 279 | inclusive: true, 280 | }, 281 | }, 282 | }, 283 | ], 284 | minHeight: 300, 285 | isPluginExtension: false, 286 | defaultExtensionDataXML: "", 287 | }, 288 | extName: "panelify-demo", 289 | getAllExtensions() { 290 | return null; 291 | }, 292 | getExtData(id) { 293 | return null; 294 | }, 295 | openExtension(ext) { 296 | return null; 297 | }, 298 | getVersion(ext) { 299 | return this.extVersion; 300 | }, 301 | launchLocalhost(url = null) { 302 | return null; 303 | }, 304 | launchInNewTab(url) { 305 | window.open(url); 306 | }, 307 | launchHomepage() { 308 | if (this.homepage) this.launchInNewTab(this.homepage || null); 309 | else console.log("Panel has no homepage defined in package.json"); 310 | }, 311 | launchGitRepo() { 312 | if (this.repository) 313 | this.launchInNewTab("https://github.com/" + this.repository); 314 | else console.log("Panel has no repo defined in package.json"); 315 | }, 316 | } 317 | : { 318 | path: { 319 | root: resolveString(window.__adobe_cep__.getSystemPath("extension")), 320 | userData: resolveString(window.__adobe_cep__.getSystemPath("userData")), 321 | commonFiles: resolveString( 322 | window.__adobe_cep__.getSystemPath("commonFiles") 323 | ), 324 | myDocuments: resolveString( 325 | window.__adobe_cep__.getSystemPath("myDocuments") 326 | ), 327 | hostApplication: resolveString( 328 | window.__adobe_cep__.getSystemPath("hostApplication") 329 | ), 330 | }, 331 | package: tryReadingFile( 332 | window.__adobe_cep__.getSystemPath("extension"), 333 | "/package.json", 334 | "", 335 | true 336 | ), 337 | author: tryReadingFile( 338 | window.__adobe_cep__.getSystemPath("extension"), 339 | "/package.json", 340 | "author", 341 | true 342 | ), 343 | repository: tryReadingFile( 344 | window.__adobe_cep__.getSystemPath("extension"), 345 | "/package.json", 346 | "repository", 347 | true 348 | ), 349 | homepage: tryReadingFile( 350 | window.__adobe_cep__.getSystemPath("extension"), 351 | "/package.json", 352 | "homepage", 353 | true 354 | ), 355 | localhost: getLocalHost(), 356 | isDev: /localhost/.test(document.location.href), 357 | extVersion: getManifestVersion(), 358 | appName: JSON.parse(window.__adobe_cep__.getHostEnvironment()).appName, 359 | appLocale: JSON.parse(window.__adobe_cep__.getHostEnvironment()) 360 | .appLocale, 361 | appVersion: JSON.parse(window.__adobe_cep__.getHostEnvironment()) 362 | .appVersion, 363 | userAgent: 364 | navigator.platform.indexOf("Win") > -1 365 | ? "Windows" 366 | : navigator.platform.indexOf("Mac") > -1 367 | ? "Mac" 368 | : "Unknown", 369 | cepVersion: `${ 370 | JSON.parse(window.__adobe_cep__.getCurrentApiVersion()).major 371 | }.${JSON.parse(window.__adobe_cep__.getCurrentApiVersion()).minor}.${ 372 | JSON.parse(window.__adobe_cep__.getCurrentApiVersion()).micro 373 | }`, 374 | hostCapabilities: JSON.parse(window.__adobe_cep__.getHostCapabilities()), 375 | userId: window.__adobe_cep__.getCurrentImsUserId(), 376 | extID: window.__adobe_cep__.getExtensionId(), 377 | exts: JSON.parse(window.__adobe_cep__.getExtensions()), 378 | ext: JSON.parse(window.__adobe_cep__.getExtensions()).find((ext) => { 379 | return ext.id == window.__adobe_cep__.getExtensionId(); 380 | }), 381 | activeExt: JSON.parse(window.__adobe_cep__.getExtensions()).find( 382 | (ext) => { 383 | return ext.id == window.__adobe_cep__.getExtensionId(); 384 | } 385 | ), 386 | extName: JSON.parse(window.__adobe_cep__.getExtensions()).find((ext) => { 387 | return ext.id == window.__adobe_cep__.getExtensionId(); 388 | }).name, 389 | getAllExtensions() { 390 | return JSON.parse(window.__adobe_cep__.getExtensions()).map((ext) => { 391 | let obj = {}; 392 | obj["version"] = this.getVersion(ext); 393 | return Object.assign(obj, ext); 394 | }); 395 | }, 396 | getExtData(id) { 397 | const target = JSON.parse(window.__adobe_cep__.getExtensions()).find( 398 | (ext) => { 399 | return ext.id == id; 400 | } 401 | ); 402 | target["version"] = this.getVersion(target); 403 | return target; 404 | }, 405 | openExtension(ext) { 406 | window.__adobe_cep__.requestOpenExtension(ext.id); 407 | }, 408 | getVersion(ext) { 409 | const xml = window.cep.fs.readFile(`${ext.basePath}/CSXS/manifest.xml`); 410 | const verID = /ExtensionBundleVersion\=\"(\d|\.)*(?=\")/; 411 | const match = xml.data.match(verID); 412 | return match && match.length 413 | ? match[0].replace(/\w*\=\"/, "") 414 | : "unknown"; 415 | }, 416 | launchLocalhost(url = null) { 417 | this.launchInNewTab(url || this.localhost); 418 | }, 419 | launchInNewTab(url) { 420 | if (url) cep.util.openURLInDefaultBrowser(url); 421 | }, 422 | launchHomepage() { 423 | if (this.homepage) this.launchInNewTab(this.homepage || null); 424 | else console.log(`Panel has no homepage defined in package.json`); 425 | }, 426 | launchGitRepo() { 427 | if (this.repository) 428 | this.launchInNewTab(`https://github.com/${this.repository}`); 429 | else console.log(`Panel has no repo defined in package.json.`); 430 | }, 431 | }; 432 | 433 | spy = doubleCheckPathIntegrity(spy); 434 | export default spy; 435 | // 436 | // 437 | // UNUSED 438 | // 439 | // 440 | /** 441 | * @param {String} string - The string to sanitize as Mac equivalent 442 | * @returns {String} - A correctly formatted path regardless of OS 443 | */ 444 | function getRealString(string) { 445 | return navigator.platform.indexOf("Mac") > -1 446 | ? `/${string}`.replace(/\s/g, "\\ ") 447 | : string; 448 | } 449 | 450 | /** 451 | * @param {Object} parent - The containing object to recurse through 452 | */ 453 | function recheckPathData(parent) { 454 | function fixPath(string) { 455 | return navigator.platform.indexOf("Mac") > -1 456 | ? `/${string}`.replace(/\s/g, "\\ ") 457 | : string; 458 | } 459 | Object.keys(parent).forEach((key) => { 460 | if (/object/i.test(typeof parent[key])) 461 | parent[key] = recheckPathData(parent[key]); 462 | else if ( 463 | /string/i.test(typeof parent[key]) && 464 | /(\\|\/)\w{1,}(\\|\/)/.test(parent[key]) 465 | ) 466 | parent[key] = fixPath(parent[key]); 467 | }); 468 | return parent; 469 | } 470 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cep-spy", 3 | "version": "1.3.4", 4 | "description": "No-dependency CEP utility for identifying current host app, current panel, sibling extensions and more", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "homepage": "https://github.com/Inventsable/CEP-Spy", 10 | "repository": "Inventsable/CEP-Spy", 11 | "author": { 12 | "name": "Tom Scharstein", 13 | "email": "tom@inventsable.cc", 14 | "url": "https://www.inventsable.cc" 15 | }, 16 | "keywords": [], 17 | "license": "ISC" 18 | } 19 | --------------------------------------------------------------------------------