├── .github └── workflows │ └── firebase-hosting-merge.yml ├── .gitignore ├── .vscode └── pack.code-snippets ├── LICENSE ├── README.md ├── airnow ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.png │ ├── icon.png │ └── listing.json └── pack.ts ├── amazon ├── .coda-pack.json ├── assets │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── pack.ts └── schemas.ts ├── apps-script ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── cover_alt.png │ ├── example0.jpg │ ├── icon.png │ ├── listing.json │ ├── oauth_icon.png │ └── oauth_icon_small.png ├── constants.ts ├── documentation │ └── PRIVACY.md ├── helpers.ts ├── pack.ts └── schemas.ts ├── asciify ├── .coda-pack.json ├── assets │ ├── cover.png │ ├── cover.psd │ ├── example0.png │ ├── icon.png │ ├── icon.psd │ └── listing.json ├── fonts.ts ├── pack.ts └── utils │ └── generate-fonts.ts ├── bigquery ├── README.md ├── api.ts ├── helpers.ts ├── pack.ts └── schemas.ts ├── bin ├── backup.ts └── patch.sh ├── bullet-graph ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.png │ ├── icon.png │ └── listing.json └── pack.ts ├── cases ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── pack.ts └── pack_test.ts ├── cat-photos ├── .coda-pack.json ├── assets │ ├── cover.png │ ├── example0.png │ ├── icon.png │ └── listing.json └── pack.ts ├── check-host ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json └── pack.ts ├── cloudconvert ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── helpers.ts └── pack.ts ├── code-blocks ├── .coda-pack.json ├── assets │ ├── cover.png │ ├── example0.png │ ├── icon.png │ └── listing.json ├── embed │ ├── .firebaserc │ ├── .gitignore │ ├── firebase.json │ ├── functions │ │ ├── .gitignore │ │ ├── index.html │ │ ├── index.js │ │ ├── package-lock.json │ │ └── package.json │ └── public │ │ └── 404.html └── pack.ts ├── college-football ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── helpers.ts ├── pack.ts ├── schemas.ts └── types.ts ├── countries ├── .coda-pack.json ├── assets │ ├── icon.png │ └── listing.json └── pack.ts ├── date-icon ├── .coda-pack.json ├── assets │ ├── cover.png │ ├── example0.png │ ├── icon.png │ └── listing.json └── pack.ts ├── dicebear ├── .coda-pack.json ├── assets │ ├── example0.png │ ├── icon.png │ └── listing.json └── pack.ts ├── diff ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── pack.ts └── test │ ├── new.txt │ └── old.txt ├── docs ├── CNAME ├── bookmarklet.html ├── dlink.html ├── export │ └── index.html ├── files │ ├── sample1.pdf │ └── sample2.pdf ├── index.html ├── orgchart │ ├── index.html │ └── v2.html └── preview │ ├── index.html │ └── main.js ├── email-address ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── pack.ts └── pack_test.ts ├── export-docs ├── .coda-pack.json ├── assets │ ├── icon.png │ └── listing.json └── pack.ts ├── ezeep ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.jpg │ └── listing.json ├── helpers.ts ├── pack.ts ├── parameters.ts └── schemas.ts ├── favicon ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json └── pack.ts ├── fitbit ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json └── pack.ts ├── flight-codes ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── constants.ts ├── helpers.ts ├── pack.ts └── schemas.ts ├── formulas ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json └── pack.ts ├── glossary ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json └── pack.ts ├── handlebars ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── pack.ts └── pack_test.ts ├── holidays ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── pack.ts ├── pack_test.ts └── update.sh ├── httpbin ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json └── pack.ts ├── hue ├── .coda-pack.json ├── assets │ ├── cover.png │ ├── example0.png │ ├── icon.png │ └── listing.json ├── helpers.ts ├── pack.ts └── schemas.ts ├── ical ├── .coda-pack.json ├── assets │ ├── example0.jpg │ ├── icon.png │ └── listing.json └── pack.ts ├── imoji ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json └── pack.ts ├── integrately └── pack.ts ├── ipinfo ├── .coda-pack.json ├── assets │ ├── cover.png │ ├── example0.png │ ├── icon.png │ └── listing.json └── pack.ts ├── json5 ├── .coda-pack.json ├── assets │ ├── example0.png │ ├── icon.png │ ├── icon.svg │ └── listing.json ├── pack.ts └── tests │ └── pack_test.ts ├── jwt ├── .coda-pack.json ├── assets │ ├── example0.jpg │ ├── icon.svg │ └── listing.json └── pack.ts ├── keep └── pack.ts ├── mailto ├── .coda-pack.json ├── assets │ ├── cover.png │ ├── example0.png │ ├── icon.png │ └── listing.json ├── pack.ts └── pack_test.ts ├── medium ├── .coda-pack.json ├── assets │ ├── cover.png │ ├── example0.png │ ├── icon.jpg │ └── listing.json └── pack.ts ├── metadata ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── pack.ts └── server │ ├── .firebaserc │ ├── .gitignore │ ├── firebase.json │ ├── functions │ ├── .eslintrc.js │ ├── .gitignore │ ├── package-lock.json │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.dev.json │ └── tsconfig.json │ └── public │ └── 404.html ├── mockaroo ├── .coda-pack.json ├── assets │ ├── cover.png │ ├── example0.png │ ├── icon.png │ └── listing.json └── pack.ts ├── nomnoml ├── .coda-pack.json ├── assets │ ├── cover.png │ ├── example0.png │ ├── icon.png │ └── listing.json └── pack.ts ├── nps ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── pack.ts └── pack_test.ts ├── orgchart ├── .coda-pack.json ├── assets │ ├── example0.gif │ ├── icon.png │ └── listing.json └── pack.ts ├── package-lock.json ├── package.json ├── packs ├── .coda-pack.json ├── assets │ ├── cover.png │ ├── example0.png │ ├── icon.png │ └── listing.json ├── constants.ts ├── helpers.ts ├── pack.ts └── schemas.ts ├── patches ├── @dicebear+converter+7.0.4.patch ├── @universe+util+1.4.1.patch ├── ajv+8.12.0.patch ├── node-fetch+2.6.11.patch └── setimmediate+1.0.5.patch ├── phone-number ├── .coda-pack.json ├── assets │ ├── cover.png │ ├── example0.png │ ├── icon.png │ └── listing.json ├── pack.ts └── pack_test.ts ├── placeholder ├── .coda-pack.json ├── assets │ ├── cover.png │ ├── example0.png │ ├── icon.png │ └── listing.json ├── pack.ts └── pack_test.ts ├── preview ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json └── pack.ts ├── rebrickable ├── .coda-pack.json ├── api.ts ├── assets │ ├── icon.png │ └── listing.json ├── helpers.ts ├── images.json ├── pack.ts └── schemas.ts ├── regex ├── .coda-pack.json ├── assets │ ├── cover.png │ ├── example0.png │ ├── icon.png │ └── listing.json ├── pack.ts └── pack_test.ts ├── rich-text ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── pack.ts └── pack_test.ts ├── safe-browsing ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── pack.ts └── pack_test.ts ├── seinfeld ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json └── pack.ts ├── short-number ├── .coda-pack.json ├── assets │ ├── cover.png │ ├── example0.png │ ├── icon.png │ └── listing.json └── pack.ts ├── smartsheet ├── .coda-pack.json ├── api.ts ├── assets │ ├── example0.png │ ├── icon.jpg │ └── listing.json ├── convert.ts ├── pack.ts └── types.ts ├── smartsuite ├── .coda-pack.json ├── api.ts ├── assets │ ├── example0.png │ ├── icon.jpg │ └── listing.json ├── convert.ts ├── pack.ts ├── schemas.ts ├── types.ts └── types │ ├── coda.ts │ └── smartsuite.ts ├── socrata ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json └── pack.ts ├── spintax ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── pack.ts └── pack_test.ts ├── sql ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── constants.ts ├── helpers.ts ├── pack.ts ├── test │ └── helpers_test.ts └── types.ts ├── streaks ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json └── pack.ts ├── taylor-swift ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json └── pack.ts ├── temp-file ├── .coda-pack.json ├── assets │ ├── cover.png │ ├── example0.png │ ├── icon.png │ └── listing.json └── pack.ts ├── tmdb ├── .coda-pack.json ├── api.ts ├── assets │ ├── cover.png │ ├── example0.png │ ├── icon.png │ └── listing.json ├── constants.ts ├── helpers.ts ├── pack.ts └── schemas.ts ├── tokenize ├── .coda-pack.json ├── assets │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── pack.ts └── pack_test.ts ├── tsconfig.json ├── usps ├── .coda-pack.json ├── assets │ └── listing.json └── pack.ts ├── visualping ├── .coda-pack.json ├── assets │ ├── cover.jpg │ ├── example0.jpg │ ├── icon.png │ └── listing.json ├── helpers.ts ├── pack.ts └── schemas.ts ├── xml ├── .coda-pack.json ├── assets │ ├── example0.png │ ├── icon.png │ └── listing.json ├── pack.ts └── tests │ └── pack_test.ts └── yaml ├── .coda-pack.json ├── assets ├── example0.png ├── icon.png └── listing.json ├── pack.ts └── tests └── pack_test.ts /.github/workflows/firebase-hosting-merge.yml: -------------------------------------------------------------------------------- 1 | # This file was auto-generated by the Firebase CLI 2 | # https://github.com/firebase/firebase-tools 3 | 4 | name: Deploy to Firebase Hosting on merge 5 | 'on': 6 | push: 7 | branches: 8 | - main 9 | jobs: 10 | build_and_deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - uses: FirebaseExtended/action-hosting-deploy@v0 15 | with: 16 | entryPoint: './code-blocks/embed' 17 | repoToken: '${{ secrets.GITHUB_TOKEN }}' 18 | firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_CODE_SNIPPETS_EMBED }}' 19 | channelId: live 20 | projectId: code-snippets-embed 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .coda.json 2 | .coda-credentials.json 3 | scratch 4 | node_modules 5 | credentials.ts 6 | *.pem 7 | service-account.json 8 | *.bak 9 | bigquery/.coda-pack.json 10 | bigquery/assets 11 | _build 12 | _upload_build 13 | .clinic -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Eric Koleda 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 | # Eric's Packs 2 | 3 | Coda Packs I've created. 4 | -------------------------------------------------------------------------------- /airnow/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 22954 3 | } -------------------------------------------------------------------------------- /airnow/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/airnow/assets/cover.jpg -------------------------------------------------------------------------------- /airnow/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/airnow/assets/example0.png -------------------------------------------------------------------------------- /airnow/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/airnow/assets/icon.png -------------------------------------------------------------------------------- /amazon/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 35706 3 | } -------------------------------------------------------------------------------- /amazon/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/amazon/assets/example0.jpg -------------------------------------------------------------------------------- /amazon/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/amazon/assets/icon.png -------------------------------------------------------------------------------- /amazon/pack.ts: -------------------------------------------------------------------------------- 1 | import * as coda from '@codahq/packs-sdk'; 2 | import * as schemas from './schemas'; 3 | 4 | export const pack = coda.newPack(); 5 | 6 | const OneDaySecs = 24 * 60 * 60; 7 | 8 | // From: https://github.com/vaiden/amazon-asin/blob/master/index.js 9 | const AmazonProductUrlRegex = /^https?:\/\/(www\.)?(.*)amazon\.([a-z\.]{2,6})(\/d\/(.*)|\/(.*)\/?(?:dp|o|gp|-)\/)(aw\/d\/|product\/)?(B[0-9]{1}[0-9A-Z]{8}|[0-9]{9}(?:X|[0-9]))/i; 10 | const AmazonShortUrlRegex = /^https?:\/\/([a-zA-Z\d-]+\.){0,}(amzn|a)\.(to|eu|co)\//i; 11 | 12 | pack.addNetworkDomain("outscraper.com"); 13 | 14 | pack.setUserAuthentication({ 15 | type: coda.AuthenticationType.CustomHeaderToken, 16 | headerName: "X-API-KEY", 17 | instructionsUrl: "https://app.outscraper.com/profile", 18 | }); 19 | 20 | pack.addFormula({ 21 | name: "Product", 22 | description: "Gets information about an Amazon product given a link.", 23 | parameters: [ 24 | coda.makeParameter({ 25 | type: coda.ParameterType.String, 26 | name: "link", 27 | description: "A link to the Amazon product.", 28 | }), 29 | ], 30 | resultType: coda.ValueType.Object, 31 | schema: schemas.ProductSchema, 32 | cacheTtlSecs: OneDaySecs, 33 | execute: async function (args, context) { 34 | let [link] = args; 35 | let query = link; 36 | let match; 37 | if (match = link.match(AmazonProductUrlRegex)) { 38 | query = match.at(-1); 39 | } else if (link.match(AmazonShortUrlRegex)) { 40 | // All good. 41 | } else { 42 | throw new coda.UserVisibleError("Invalid link: " + link); 43 | } 44 | let url = coda.withQueryParams("https://api.app.outscraper.com/amazon/products", { 45 | query: link, 46 | async: false, 47 | }) 48 | let response = await context.fetcher.fetch({ 49 | method: "GET", 50 | url: url, 51 | cacheTtlSecs: OneDaySecs, 52 | }); 53 | let {data} = response.body; 54 | let product = data[0][0]; 55 | let result = { 56 | ...product, 57 | brand: product.overview?.brand, 58 | upc: product.details?.upc, 59 | photo: product.image_1, 60 | asOf: new Date(), 61 | } 62 | return result; 63 | }, 64 | }); 65 | 66 | pack.addColumnFormat({ 67 | name: "Product", 68 | instructions: "Paste an Amazon product URL to get information about it.", 69 | formulaName: "Product", 70 | matchers: [AmazonProductUrlRegex, AmazonShortUrlRegex], 71 | }); -------------------------------------------------------------------------------- /amazon/schemas.ts: -------------------------------------------------------------------------------- 1 | import * as coda from '@codahq/packs-sdk'; 2 | 3 | export const ProductSchema = coda.makeObjectSchema({ 4 | properties: { 5 | asin: { type: coda.ValueType.String }, 6 | upc: { type: coda.ValueType.String }, 7 | name: { type: coda.ValueType.String }, 8 | description: { type: coda.ValueType.String }, 9 | brand: { type: coda.ValueType.String }, 10 | categories: { 11 | type: coda.ValueType.Array, 12 | items: { type: coda.ValueType.String }, 13 | }, 14 | rating: { type: coda.ValueType.Number }, 15 | reviews: { type: coda.ValueType.Number }, 16 | price: { fromKey: "price_parsed", type: coda.ValueType.Number, codaType: coda.ValueHintType.Currency }, 17 | photo: { type: coda.ValueType.String, codaType: coda.ValueHintType.ImageReference }, 18 | link: { fromKey: "short_url", type: coda.ValueType.String, codaType: coda.ValueHintType.Url }, 19 | asOf: { type: coda.ValueType.String, codaType: coda.ValueHintType.Date }, 20 | }, 21 | idProperty: "asin", 22 | displayProperty: "name", 23 | featuredProperties: ["price", "photo"], 24 | imageProperty: "photo", 25 | snippetProperty: "description", 26 | subtitleProperties: ["price", "rating", "reviews", "asOf"], 27 | linkProperty: "link", 28 | }); -------------------------------------------------------------------------------- /apps-script/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 14470 3 | } -------------------------------------------------------------------------------- /apps-script/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/apps-script/assets/cover.jpg -------------------------------------------------------------------------------- /apps-script/assets/cover_alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/apps-script/assets/cover_alt.png -------------------------------------------------------------------------------- /apps-script/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/apps-script/assets/example0.jpg -------------------------------------------------------------------------------- /apps-script/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/apps-script/assets/icon.png -------------------------------------------------------------------------------- /apps-script/assets/oauth_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/apps-script/assets/oauth_icon.png -------------------------------------------------------------------------------- /apps-script/assets/oauth_icon_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/apps-script/assets/oauth_icon_small.png -------------------------------------------------------------------------------- /apps-script/constants.ts: -------------------------------------------------------------------------------- 1 | export const ScriptUrlRegexes = [ 2 | new RegExp("^https://script.google.com/home/projects/([^/?#]+)"), 3 | ]; -------------------------------------------------------------------------------- /apps-script/documentation/PRIVACY.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy 2 | 3 | Last updated: August 18, 2022 4 | 5 | This Pack doesn't collect or store any of your data. Anonymous, aggregated usage data is provided by Coda, but doesn't include any Personal Identifiable Information (PII). This Pack may log some data for the purpose of development and support, but it is both temporary and can only be accessed if you explicitly share your document with us. 6 | 7 | If you have any questions about this Privacy Policy, You can contact us at packs@erickoleda.com. -------------------------------------------------------------------------------- /apps-script/helpers.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | import { ScriptUrlRegexes } from "./constants"; 3 | import _string = require('lodash/string'); 4 | 5 | const PageSize = 20; 6 | const Extensions = { 7 | SERVER_JS: "gs", 8 | JSON: "json", 9 | HTML: "html", 10 | }; 11 | 12 | export function parseScriptId(scriptIdOrUrl: string): string { 13 | for (let regex of ScriptUrlRegexes) { 14 | let match = scriptIdOrUrl.match(regex); 15 | if (match) return match[1]; 16 | } 17 | return scriptIdOrUrl; 18 | } 19 | 20 | export async function getScript(scriptId: string, context: coda.ExecutionContext) { 21 | let url = `https://script.googleapis.com/v1/projects/${scriptId}`; 22 | let response = await context.fetcher.fetch({ 23 | method: "GET", 24 | url, 25 | }); 26 | let script = response.body; 27 | return script; 28 | } 29 | 30 | export async function getFiles(scriptId: string, context: coda.ExecutionContext) { 31 | let url = `https://script.googleapis.com/v1/projects/${scriptId}/content`; 32 | let response = await context.fetcher.fetch({ 33 | method: "GET", 34 | url, 35 | }); 36 | let files = response.body.files; 37 | for (let file of files) { 38 | let extension = Extensions[file.type]; 39 | if (extension) { 40 | file.name += `.${extension}`; 41 | } 42 | } 43 | return files; 44 | } 45 | 46 | export async function getMetrics(scriptId: string, context: coda.ExecutionContext) { 47 | let url = coda.withQueryParams(`https://script.googleapis.com/v1/projects/${scriptId}/metrics`, { 48 | metricsGranularity: "WEEKLY", 49 | }); 50 | let response = await context.fetcher.fetch({ 51 | method: "GET", 52 | url, 53 | }); 54 | let metrics = response.body; 55 | for (let [key, value] of Object.entries(metrics)) { 56 | metrics[key] = parseInt(value[0].value) || 0; 57 | } 58 | metrics.summary = "Last 7 days: " + Object.entries(metrics) 59 | .map(([key, value]) => `${value} ${_string.startCase(key)}`) 60 | .join(", "); 61 | return metrics; 62 | } 63 | -------------------------------------------------------------------------------- /apps-script/schemas.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | 3 | const UserSchema = coda.makeObjectSchema({ 4 | properties: { 5 | name: { type: coda.ValueType.String }, 6 | email: { type: coda.ValueType.String, codaType: coda.ValueHintType.Email }, 7 | photo: { type: coda.ValueType.String, codaType: coda.ValueHintType.ImageReference, fromKey: "photoUrl" }, 8 | }, 9 | displayProperty: "name", 10 | }); 11 | 12 | const FileSchema = coda.makeObjectSchema({ 13 | properties: { 14 | name: { type: coda.ValueType.String }, 15 | type: { type: coda.ValueType.String }, 16 | content: { type: coda.ValueType.String, fromKey: "source" }, 17 | createTime: { type: coda.ValueType.String, codaType: coda.ValueHintType.DateTime }, 18 | updateTime: { type: coda.ValueType.String, codaType: coda.ValueHintType.DateTime }, 19 | lastModifyUser: UserSchema, 20 | } 21 | }); 22 | 23 | const MetricsSchema = coda.makeObjectSchema({ 24 | properties: { 25 | summary: { type: coda.ValueType.String }, 26 | activeUsers: { type: coda.ValueType.Number }, 27 | totalExecutions: { type: coda.ValueType.Number }, 28 | failedExecutions: { type: coda.ValueType.Number }, 29 | }, 30 | displayProperty: "summary", 31 | }); 32 | 33 | export const ScriptSchema = coda.makeObjectSchema({ 34 | properties: { 35 | scriptId: { type: coda.ValueType.String }, 36 | title: { type: coda.ValueType.String }, 37 | files: { type: coda.ValueType.Array, items: FileSchema }, 38 | metrics: MetricsSchema, 39 | createTime: { type: coda.ValueType.String, codaType: coda.ValueHintType.DateTime }, 40 | updateTime: { type: coda.ValueType.String, codaType: coda.ValueHintType.DateTime }, 41 | creator: UserSchema, 42 | lastModifyUser: UserSchema, 43 | link: { type: coda.ValueType.String, codaType: coda.ValueHintType.Url }, 44 | }, 45 | displayProperty: "title", 46 | idProperty: "scriptId", 47 | featuredProperties: ["files", "metrics"], 48 | subtitleProperties: [ 49 | { property: "creator.name", label: `By ${coda.PropertyLabelValueTemplate}` }, 50 | ], 51 | linkProperty: "link", 52 | }); 53 | -------------------------------------------------------------------------------- /asciify/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 10744 3 | } -------------------------------------------------------------------------------- /asciify/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/asciify/assets/cover.png -------------------------------------------------------------------------------- /asciify/assets/cover.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/asciify/assets/cover.psd -------------------------------------------------------------------------------- /asciify/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/asciify/assets/example0.png -------------------------------------------------------------------------------- /asciify/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/asciify/assets/icon.png -------------------------------------------------------------------------------- /asciify/assets/icon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/asciify/assets/icon.psd -------------------------------------------------------------------------------- /asciify/utils/generate-fonts.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | 3 | let fontFiles = fs.readdirSync("node_modules/figlet/importable-fonts"); 4 | let imports = []; 5 | let lines = [ 6 | "export type Font = { name: string; definition: any; }", 7 | "export const fonts: Font[] = [];" 8 | ]; 9 | for (let fontFile of fontFiles) { 10 | let name = fontFile.replace(/\.js$/, ""); 11 | let module = "font_" + name.replace(/[^\w]/g, "_"); 12 | imports.push(`import ${module} from "figlet/importable-fonts/${fontFile}";`); 13 | lines.push(`fonts.push({name: "${name}", definition: ${module}});`); 14 | } 15 | let text = imports.concat([""], lines).join("\n"); 16 | 17 | fs.writeFileSync("asciify/fonts.ts", text); -------------------------------------------------------------------------------- /bigquery/README.md: -------------------------------------------------------------------------------- 1 | # BigQuery Pack 2 | 3 | This BigQuery Pack can't be published to the Coda gallery due to Google's policies around sensitive scopes. Although it's possible to request Google Cloud scopes during the OAuth flow, Google won't approve those scopes for a public project. Therefore to use this Pack you must deploy your own copy of it with your own OAuth credentials. 4 | 5 | ## Setup 6 | 7 | ### Pack 8 | 9 | 1. Clone this repository. 10 | 11 | ```sh 12 | git clone https://github.com/erickoledadevrel/packs.git 13 | ``` 14 | 15 | 1. Install the dependencies. 16 | 17 | ```sh 18 | npm install 19 | ``` 20 | 21 | 1. Register an API token. 22 | 23 | ```sh 24 | npx coda register 25 | ``` 26 | 27 | 1. Create the Pack. 28 | 29 | ```sh 30 | npx coda create bigquery/pack.ts --name "BigQuery" 31 | ``` 32 | 33 | 1. Upload the Pack. 34 | 35 | ``` 36 | npx coda upload bigquery/pack.ts 37 | ``` 38 | 39 | ### OAuth 40 | 41 | 1. Open the Pack in the Pack Studio and navigate to the **Settings** tab. 42 | 1. Click **Add OAuth credentials**. 43 | 1. Copy the value shown for **Redirect URL**. 44 | 1. In a new tab, open the [Google Cloud Console](https://console.cloud.google.com/). 45 | 1. Create or select a project to use with the Pack. 46 | 1. Navigate to **APIs & Services > OAuth consent screen**. 47 | 1. Select the **User Type** value **Internal**, and click **Create**. 48 | 49 | If you aren't part of a Google Workspace organization you must select **External**. You should keep the project in **Testing** mode, and add yourself as a test user. 50 | 51 | 1. Enter a value for all required fields and click **Save and continue**. 52 | 1. Click **Add or remove scopes**, and under **Manually add scopes** enter the following: 53 | 54 | ``` 55 | profile 56 | https://www.googleapis.com/auth/bigquery.readonly 57 | https://www.googleapis.com/auth/cloudplatformprojects.readonly 58 | ``` 59 | 60 | 1. Click **Add to table**, then **Update**, and finally **Save and continue**. 61 | 1. Navigate to the **Credentials** tab. 62 | 1. Click **+ Create Credentials > OAuth client ID**. 63 | 1. For **Application type** select **Web application**. 64 | 1. Under **Authorized redirect URIs** click **+ Add URI**. 65 | 1. Paste the URL you copied earlier from the Pack Studio. 66 | 1. Click the **Create** button. 67 | 1. Copy the **Client ID** and **Client secret**. 68 | 1. Back in the Pack Studio OAuth credentials dialog, paste the client ID and secret into the corresponding text boxes. 69 | 1. Click **Save**. 70 | -------------------------------------------------------------------------------- /bigquery/api.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | import { onError } from "./helpers"; 3 | 4 | const QueryPageSize = 100; 5 | const QueryTimeoutMs = 30 * 1000; 6 | 7 | export class BigQueryApi { 8 | context: coda.ExecutionContext; 9 | projectId: string; 10 | accessTokenPromise: Promise; 11 | 12 | constructor(context: coda.ExecutionContext, projectId: string) { 13 | this.context = context; 14 | this.projectId = projectId; 15 | } 16 | 17 | async runQuery(query: string, dryRun = false, parameters?) { 18 | let payload = { 19 | query, 20 | useLegacySql: false, 21 | maxResults: QueryPageSize, 22 | timeoutMs: QueryTimeoutMs, 23 | dryRun, 24 | connectionProperties: [ 25 | { key: "time_zone", value: this.context.timezone }, 26 | ], 27 | queryParameters: parameters, 28 | }; 29 | let response = await this.makeRequest({ 30 | method: "POST", 31 | url: `/queries`, 32 | headers: { 33 | "Content-Type": "application/json", 34 | }, 35 | body: JSON.stringify(payload), 36 | }); 37 | return response.body; 38 | } 39 | 40 | async getQueryResults(jobId: string, pageToken?: string) { 41 | let url = coda.withQueryParams(`/queries/${jobId}`, { 42 | maxResults: QueryPageSize, 43 | timeoutMs: QueryTimeoutMs, 44 | pageToken, 45 | }); 46 | let response = await this.makeRequest({ 47 | method: "GET", 48 | url: url, 49 | }); 50 | return response.body; 51 | } 52 | 53 | private async makeRequest(request: coda.FetchRequest) { 54 | if (request.url?.startsWith("/")) { 55 | request.url = coda.joinUrl("https://www.googleapis.com/bigquery/v2/projects", this.projectId, request.url); 56 | } 57 | try { 58 | return await this.context.fetcher.fetch(request); 59 | } catch (error) { 60 | onError(error); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /bigquery/schemas.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | 3 | export const RowIdKey = "__row__"; 4 | export const RowIndexKey = "__index__"; 5 | export const ObjectTypeKey = "__type__"; 6 | 7 | export const BaseQueryRowSchema = coda.makeObjectSchema({ 8 | properties: { 9 | rowId: { type: coda.ValueType.String, fromKey: RowIdKey }, 10 | rowIndex: { type: coda.ValueType.Number, fromKey: RowIndexKey }, 11 | }, 12 | displayProperty: "rowIndex", 13 | idProperty: "rowId", 14 | featuredProperties: [], 15 | }); 16 | 17 | export const BaseQueryObjectSchema = coda.makeObjectSchema({ 18 | properties: { 19 | objectType: { type: coda.ValueType.Number, fromKey: ObjectTypeKey }, 20 | }, 21 | displayProperty: "objectType", 22 | featuredProperties: [], 23 | }); 24 | -------------------------------------------------------------------------------- /bin/backup.ts: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | import { glob } from "glob"; 4 | import fetch from "node-fetch"; 5 | import * as mime from "mime-types"; 6 | 7 | const DefaultIcon = "default-pack-icon.png"; 8 | 9 | async function run(packName) { 10 | let apiKey = JSON.parse(fs.readFileSync(".coda.json").toString()).apiKey; 11 | let packs = await glob("**/.coda-pack.json"); 12 | packs.sort(); 13 | for (let pack of packs) { 14 | let name = path.basename(path.dirname(pack)); 15 | if (packName && packName !== name) continue; 16 | let packId = JSON.parse(fs.readFileSync(pack).toString()).packId; 17 | let url = `https://coda.io/apis/v1/packs/${packId}/listing`; 18 | let response = await fetch(url, { 19 | headers: { 20 | "Authorization": `Bearer ${apiKey}`, 21 | } 22 | }); 23 | let listing = await response.json(); 24 | if (response.status !== 200) { 25 | throw new Error(`Error backing up ${packId}: ${response.status}`); 26 | } 27 | 28 | let dir = path.join(path.dirname(pack), "assets"); 29 | if (!fs.existsSync(dir)){ 30 | fs.mkdirSync(dir); 31 | } 32 | let file = path.join(dir, "listing.json"); 33 | fs.writeFileSync(file, JSON.stringify(listing, null, 2)); 34 | 35 | if (listing.logoUrl && !listing.logoUrl.endsWith(DefaultIcon)) { 36 | backupImage(listing.logoUrl, dir, "icon"); 37 | } 38 | if (listing.exampleImages?.length) { 39 | for (let i = 0; i < listing.exampleImages.length; i++) { 40 | backupImage(listing.exampleImages[i].imageUrl, dir, "example" + i); 41 | } 42 | } 43 | 44 | console.log("Backed up listing " + file); 45 | await new Promise(resolve => setTimeout(resolve, 1000)); 46 | } 47 | } 48 | 49 | async function backupImage(url, dir, name) { 50 | let response = await fetch(url); 51 | let buffer = await response.buffer(); 52 | let extension = mime.extension(response.headers.get("content-type")); 53 | if (extension == "jpeg") extension = "jpg"; 54 | fs.writeFileSync(path.join(dir, `${name}.${extension}`), buffer); 55 | } 56 | 57 | let [n, f, packName] = process.argv; 58 | run(packName); 59 | -------------------------------------------------------------------------------- /bin/patch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Work around error when building the Holidays Pack. 4 | find ./node_modules/astronomia/lib -type f | xargs sed -i.bak 's/let /var /g' 5 | -------------------------------------------------------------------------------- /bullet-graph/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 20912 3 | } -------------------------------------------------------------------------------- /bullet-graph/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/bullet-graph/assets/cover.jpg -------------------------------------------------------------------------------- /bullet-graph/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/bullet-graph/assets/example0.png -------------------------------------------------------------------------------- /bullet-graph/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/bullet-graph/assets/icon.png -------------------------------------------------------------------------------- /bullet-graph/assets/listing.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 20912, 3 | "name": "Bullet Graph", 4 | "description": "If a picture is worth a thousand words, a good chart is worth at least a couple hundred. A bullet graph is a special kind of bar chart that includes not only the current value of a metric, but also the target value and qualitative ranges.\n\nThis Pack includes a BulletGraph() formula you can use to easily add these charts to your Coda docs.\n\nIcon created by Freepik - Flaticon\nCover photo by Photo by Kelly Sikkema on Unsplash", 5 | "shortDescription": "Generate bullet graphs from the data in your Coda doc.", 6 | "logoUrl": "https://codahosted.io/packs/20912/unversioned/assets/LOGO/80dda80a0961a6ebf6e93bbb324ef6ad03c76763acdf4686b6fc26bd69ef363cc21153c1c8ab158e023501d8f388ea875923ea55c200ea178a462e3baeb5b171b79d66d7f6aaffec9d2d8252c2c31be9be1e7a3b67a05e676ddf326ffd4292dd37f13949", 7 | "coverUrl": "https://codahosted.io/packs/20912/unversioned/assets/COVER/aa79febf5f559d8480a6ad724c40b74602692304f12ebd2dee1c160eb6c27e254bbce3b9e3b514b5c4bb56ee0877299d830339c66273d3729b2bc7e096d0c19f95c420e4afaaeec6566c6536e4d5d592eb03216858e745fddcb6445f7a3e40f7c4f58b10", 8 | "exampleImages": [ 9 | { 10 | "imageUrl": "https://codahosted.io/packs/20912/unversioned/assets/EXAMPLE/373485cb35cf4a2d44c65e4bfb4682e95c9e2dbe855dd0ce1ea38efa0ebe6a754176ff2c59a77d43ace9e45899f53e0ff1c4d6f8db7e750c4aa73e7bc31d78eac19a7db84a53c6c68b9c0afff15566b98188e4c23cf82a7ca2a176b4f91398a66f1190a4", 11 | "filename": "bullet.png", 12 | "mimeType": "image/png", 13 | "assetId": "373485cb35cf4a2d44c65e4bfb4682e95c9e2dbe855dd0ce1ea38efa0ebe6a754176ff2c59a77d43ace9e45899f53e0ff1c4d6f8db7e750c4aa73e7bc31d78eac19a7db84a53c6c68b9c0afff15566b98188e4c23cf82a7ca2a176b4f91398a66f1190a4" 14 | } 15 | ], 16 | "packVersion": "28", 17 | "releaseId": 1, 18 | "lastReleasedAt": "2023-03-24T18:40:07.834Z", 19 | "externalMetadataUrl": "https://codahosted.io/packs/20912/28/metadata/dd1d2e30cb023c55700cf4885dd46383fd6c574e3e0d6eabacd78b8aa0aeca7e2a08c304328b33b824ffda8b7caaa5e0685e459ece9b67ecc789c0de1a3c089a9f6921a98d9b9788bcc9a5f137bc9ab3ce8384d3ed935ca7f53bebd16a6cdab810ca86d4.json", 20 | "categories": [], 21 | "supportEmail": "packs@erickoleda.com", 22 | "sourceCodeVisibility": "private", 23 | "makers": [ 24 | { 25 | "name": "Eric Koleda", 26 | "pictureLink": "https://images-codaio.imgix.net/https%3A%2F%2Flh3.googleusercontent.com%2Fa%2FACg8ocJYa8nknu-CdeUwswBMRrDPKyLXdwzRPhMFuTodG063315NF-Xe%3Ds96-c?ixlib=js-3.8.0&auto=format%2Ccompress&fit=crop&w=360&h=360&crop=faces&s=3883cf143fc4db7e36979789fddc045e", 27 | "employer": "Coda", 28 | "jobTitle": "Developer Advocate", 29 | "slug": "eric-koleda" 30 | } 31 | ], 32 | "standardPackPlan": { 33 | "packPlanId": "f53a58ab-0e73-4f58-9f62-c94e329c3aea", 34 | "packId": 20912, 35 | "createdAt": "2023-03-17T20:22:51.385Z", 36 | "pricing": { 37 | "type": "Free" 38 | } 39 | }, 40 | "sdkVersion": "1.3.0", 41 | "discoverability": "public", 42 | "userAccess": { 43 | "canEdit": true, 44 | "canInstall": false, 45 | "canTest": true, 46 | "canView": true, 47 | "canPurchase": false, 48 | "requiresTrial": false, 49 | "canConnectAccount": true 50 | } 51 | } -------------------------------------------------------------------------------- /cases/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 20970 3 | } -------------------------------------------------------------------------------- /cases/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/cases/assets/cover.jpg -------------------------------------------------------------------------------- /cases/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/cases/assets/example0.jpg -------------------------------------------------------------------------------- /cases/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/cases/assets/icon.png -------------------------------------------------------------------------------- /cases/pack_test.ts: -------------------------------------------------------------------------------- 1 | import {executeFormulaFromPackDef} from '@codahq/packs-sdk/dist/development'; 2 | import {pack} from './pack'; 3 | import * as chai from "chai"; 4 | import {assert} from "chai"; 5 | import {describe} from "mocha"; 6 | import {it} from "mocha"; 7 | import chaiAsPromised from "chai-as-promised"; 8 | chai.use(chaiAsPromised); 9 | chai.should(); 10 | 11 | describe("Examples", () => { 12 | for (let formula of pack.formulas) { 13 | if (formula.examples) { 14 | describe(formula.name, () => { 15 | for (let [i, example] of formula.examples.entries()) { 16 | it(`Example ${i}`, async () => { 17 | const result = await executeFormulaFromPackDef(pack, formula.name, example.params as any); 18 | if (typeof example.result == "object") { 19 | assert.deepEqual(result, example.result); 20 | } else { 21 | assert.equal(result, example.result); 22 | } 23 | }); 24 | } 25 | }); 26 | } 27 | } 28 | }); 29 | -------------------------------------------------------------------------------- /cat-photos/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 11251 3 | } -------------------------------------------------------------------------------- /cat-photos/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/cat-photos/assets/cover.png -------------------------------------------------------------------------------- /cat-photos/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/cat-photos/assets/example0.png -------------------------------------------------------------------------------- /cat-photos/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/cat-photos/assets/icon.png -------------------------------------------------------------------------------- /check-host/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 21582 3 | } -------------------------------------------------------------------------------- /check-host/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/check-host/assets/cover.jpg -------------------------------------------------------------------------------- /check-host/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/check-host/assets/example0.jpg -------------------------------------------------------------------------------- /check-host/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/check-host/assets/icon.png -------------------------------------------------------------------------------- /cloudconvert/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 25653 3 | } -------------------------------------------------------------------------------- /cloudconvert/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/cloudconvert/assets/cover.jpg -------------------------------------------------------------------------------- /cloudconvert/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/cloudconvert/assets/example0.jpg -------------------------------------------------------------------------------- /cloudconvert/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/cloudconvert/assets/icon.png -------------------------------------------------------------------------------- /code-blocks/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 11298 3 | } -------------------------------------------------------------------------------- /code-blocks/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/code-blocks/assets/cover.png -------------------------------------------------------------------------------- /code-blocks/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/code-blocks/assets/example0.png -------------------------------------------------------------------------------- /code-blocks/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/code-blocks/assets/icon.png -------------------------------------------------------------------------------- /code-blocks/embed/.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "code-snippets-e6382" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /code-blocks/embed/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | firebase-debug.log* 8 | firebase-debug.*.log* 9 | 10 | # Firebase cache 11 | .firebase/ 12 | 13 | # Firebase config 14 | 15 | # Uncomment this if you'd like others to create their own Firebase project. 16 | # For a team working on the same Firebase project(s), it is recommended to leave 17 | # it commented so all members can deploy to the same project(s) in .firebaserc. 18 | # .firebaserc 19 | 20 | # Runtime data 21 | pids 22 | *.pid 23 | *.seed 24 | *.pid.lock 25 | 26 | # Directory for instrumented libs generated by jscoverage/JSCover 27 | lib-cov 28 | 29 | # Coverage directory used by tools like istanbul 30 | coverage 31 | 32 | # nyc test coverage 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 36 | .grunt 37 | 38 | # Bower dependency directory (https://bower.io/) 39 | bower_components 40 | 41 | # node-waf configuration 42 | .lock-wscript 43 | 44 | # Compiled binary addons (http://nodejs.org/api/addons.html) 45 | build/Release 46 | 47 | # Dependency directories 48 | node_modules/ 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | -------------------------------------------------------------------------------- /code-blocks/embed/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "public", 4 | "ignore": [ 5 | "firebase.json", 6 | "**/.*", 7 | "**/node_modules/**" 8 | ], 9 | "rewrites": [ 10 | { "source": "/", "function": "index" }, 11 | { "source": "/embed", "destination": "embed.html" }, 12 | { "source": "/oembed", "function": "oembed" } 13 | ] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /code-blocks/embed/functions/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /code-blocks/embed/functions/index.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require('path'); 3 | 4 | const functions = require("firebase-functions"); 5 | 6 | exports.index = functions.https.onRequest((request, response) => { 7 | let host = request.headers["x-forwarded-host"] || request.headers.host; 8 | let url = new URL(`${request.protocol}://${host}${request.path}`); 9 | for (let key of Object.keys(request.query)) { 10 | url.searchParams.append(key, request.query[key]); 11 | } 12 | let template = fs.readFileSync(path.resolve("index.html")).toString(); 13 | let page = template 14 | .replace("{protocol}", request.protocol) 15 | .replace("{host}", host) 16 | .replace("{url}", encodeURIComponent(url)); 17 | response.send(page); 18 | }); 19 | 20 | exports.oembed = functions.https.onRequest((request, response) => { 21 | response.send({ 22 | type: "rich", 23 | version: "1.0", 24 | html: ` 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/preview/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Markup Preview 5 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /docs/preview/main.js: -------------------------------------------------------------------------------- 1 | const PublicKeyPEM = ` 2 | -----BEGIN PUBLIC KEY----- 3 | MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAErxixU+jZ9XWp7U+yBb3sEsO2FoTZRctc 4 | /B/AD99G5ZdumRkqbz+52LMwHxybM7qXo70asmu4sm88rU39VlZR6g== 5 | -----END PUBLIC KEY----- 6 | `.trim(); 7 | 8 | async function run() { 9 | let urlParams = new URLSearchParams(window.location.search); 10 | 11 | let signature = urlParams.get("s"); 12 | let html = urlParams.get("h"); 13 | let markdown = urlParams.get("m"); 14 | 15 | if (!html && !markdown) { 16 | return; 17 | } 18 | 19 | if (!signature) { 20 | return embedHtml("Error: Signature missing."); 21 | } 22 | 23 | let valid = await verify(signature, html || markdown); 24 | if (!valid) { 25 | return embedHtml("Error: Signature invalid."); 26 | } 27 | 28 | if (html) { 29 | return embedHtml(html); 30 | } 31 | if (markdown) { 32 | return embedMarkdown(markdown); 33 | } 34 | } 35 | 36 | function verify(signature, content) { 37 | console.log(content); 38 | console.log(signature); 39 | let sig = new KJUR.crypto.Signature({alg: "SHA512withECDSA"}); 40 | sig.init(PublicKeyPEM); 41 | sig.updateString(content); 42 | return sig.verify(base64ToHex(signature)); 43 | } 44 | 45 | function base64ToHex(str) { 46 | const raw = atob(str); 47 | let result = ''; 48 | for (let i = 0; i < raw.length; i++) { 49 | const hex = raw.charCodeAt(i).toString(16); 50 | result += (hex.length === 2 ? hex : '0' + hex); 51 | } 52 | return result.toUpperCase(); 53 | } 54 | 55 | async function embedMarkdown(markdown) { 56 | let converter = new showdown.Converter({ 57 | strikethrough: true, 58 | tables: true, 59 | tasklists: true, 60 | disableForced4SpacesIndentedSublists: true, 61 | }); 62 | let html = converter.makeHtml(markdown); 63 | embedHtml(html); 64 | } 65 | 66 | async function embedHtml(html) { 67 | document.querySelector("#preview").srcdoc = html; 68 | } 69 | -------------------------------------------------------------------------------- /email-address/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 10827 3 | } -------------------------------------------------------------------------------- /email-address/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/email-address/assets/cover.jpg -------------------------------------------------------------------------------- /email-address/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/email-address/assets/example0.jpg -------------------------------------------------------------------------------- /email-address/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/email-address/assets/icon.png -------------------------------------------------------------------------------- /export-docs/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 29897 3 | } -------------------------------------------------------------------------------- /export-docs/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/export-docs/assets/icon.png -------------------------------------------------------------------------------- /ezeep/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 26008 3 | } -------------------------------------------------------------------------------- /ezeep/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/ezeep/assets/cover.jpg -------------------------------------------------------------------------------- /ezeep/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/ezeep/assets/example0.jpg -------------------------------------------------------------------------------- /ezeep/assets/icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/ezeep/assets/icon.jpg -------------------------------------------------------------------------------- /favicon/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 14289 3 | } -------------------------------------------------------------------------------- /favicon/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/favicon/assets/cover.jpg -------------------------------------------------------------------------------- /favicon/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/favicon/assets/example0.jpg -------------------------------------------------------------------------------- /favicon/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/favicon/assets/icon.png -------------------------------------------------------------------------------- /favicon/pack.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | 3 | export const pack = coda.newPack(); 4 | 5 | const DefaultSize = 16; 6 | 7 | pack.addFormula({ 8 | name: "Favicon", 9 | description: "Get the favicon (tab icon) for the provided website.", 10 | parameters: [ 11 | coda.makeParameter({ 12 | type: coda.ParameterType.String, 13 | name: "url", 14 | description: "The URL or domain of the website.", 15 | }), 16 | coda.makeParameter({ 17 | type: coda.ParameterType.Number, 18 | name: "size", 19 | description: [ 20 | "The size of the image to return, in pixels.", 21 | "Available sizes are 16, 32, 64, 128, 256.", 22 | "Not all sizes are supported for all URLs.", 23 | "When the URL doesn't support a given size it will fall back to a smaller one.", 24 | "Default: " + DefaultSize, 25 | ].join(" "), 26 | optional: true, 27 | }), 28 | ], 29 | resultType: coda.ValueType.String, 30 | codaType: coda.ValueHintType.ImageReference, 31 | examples: [ 32 | { params: ["pokemon.com"], result: "" }, 33 | { params: ["https://www.pokemon.com/us/"], result: "" }, 34 | { params: ["pokemon.com", 32], result: "" }, 35 | ], 36 | execute: async function (args, context) { 37 | let [url, size = DefaultSize] = args; 38 | if (!url) { 39 | url = "_"; // Placeholder to generate generic image. 40 | } 41 | return `https://www.google.com/s2/favicons?domain=${url}&sz=${size}`; 42 | } 43 | }); 44 | 45 | pack.addColumnFormat({ 46 | name: "Favicon", 47 | instructions: "Enter a domain or URL to get it's favicon image.", 48 | formulaName: "Favicon", 49 | }); -------------------------------------------------------------------------------- /fitbit/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 16718 3 | } -------------------------------------------------------------------------------- /fitbit/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/fitbit/assets/cover.jpg -------------------------------------------------------------------------------- /fitbit/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/fitbit/assets/example0.jpg -------------------------------------------------------------------------------- /fitbit/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/fitbit/assets/icon.png -------------------------------------------------------------------------------- /flight-codes/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 17566 3 | } -------------------------------------------------------------------------------- /flight-codes/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/flight-codes/assets/cover.jpg -------------------------------------------------------------------------------- /flight-codes/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/flight-codes/assets/example0.jpg -------------------------------------------------------------------------------- /flight-codes/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/flight-codes/assets/icon.png -------------------------------------------------------------------------------- /flight-codes/constants.ts: -------------------------------------------------------------------------------- 1 | export const OneDaySecs = 24 * 60 * 60; 2 | -------------------------------------------------------------------------------- /flight-codes/helpers.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | import { parse } from "but-csv"; 3 | import { OneDaySecs } from "./constants"; 4 | 5 | const AirportDataUrl = "https://raw.githubusercontent.com/jpatokal/openflights/master/data/airports.dat"; 6 | const AirportColumns = [ 7 | "airportID", // Not used. 8 | "name", 9 | "city", 10 | "country", 11 | "IATA", 12 | "ICAO", 13 | "latitude", 14 | "longitude", 15 | "altitude", 16 | "tz_offset", // Not used. 17 | "DST", // Not used. 18 | "timezone", 19 | "type", // Not used. 20 | "source", // Not used. 21 | ]; 22 | const AirportNumberColumns = ["latitude", "longitude", "altitude"]; 23 | 24 | const AirlineDataUrl = "https://raw.githubusercontent.com/jpatokal/openflights/master/data/airlines.dat"; 25 | const AirlineColumns = [ 26 | "airlineID", // Not used. 27 | "name", 28 | "alias", 29 | "IATA", 30 | "ICAO", 31 | "callsign", 32 | "country", 33 | "active", 34 | ]; 35 | const AirlineBooleanColumns = ["active"]; 36 | 37 | export async function getAirports(context: coda.ExecutionContext): Promise { 38 | let response = await context.fetcher.fetch({ 39 | method: "GET", 40 | url: AirportDataUrl, 41 | cacheTtlSecs: OneDaySecs, 42 | }); 43 | let csv = response.body.trim(); 44 | let rows = parse(csv); 45 | return rows.map(row => { 46 | let result = {}; 47 | for (let [i, column] of AirportColumns.entries()) { 48 | result[column] = row[i]; 49 | if (AirportNumberColumns.includes(column)) { 50 | result[column] = Number(result[column]); 51 | } 52 | } 53 | return result; 54 | }); 55 | } 56 | 57 | export async function getAirlines(context: coda.ExecutionContext): Promise { 58 | let response = await context.fetcher.fetch({ 59 | method: "GET", 60 | url: AirlineDataUrl, 61 | cacheTtlSecs: OneDaySecs, 62 | }); 63 | let csv = response.body; 64 | csv = csv.replace(/\\\N/g, "").trim(); 65 | let rows = parse(csv); 66 | return rows.map(row => { 67 | let result = {}; 68 | for (let [i, column] of AirlineColumns.entries()) { 69 | result[column] = row[i]; 70 | if (AirlineBooleanColumns.includes(column)) { 71 | result[column] = Boolean(result[column]); 72 | } 73 | } 74 | return result; 75 | }); 76 | } 77 | 78 | export async function getAirline(context: coda.ExecutionContext, code: string): Promise { 79 | code = code.toUpperCase(); 80 | let airlines = await getAirlines(context); 81 | let result; 82 | if (code.length == 2) { 83 | result = airlines.find(airline => airline.IATA == code); 84 | } else if (code.length == 3) { 85 | result = airlines.find(airline => airline.ICAO == code); 86 | } 87 | if (!result) { 88 | throw new coda.UserVisibleError(`Invalid airline code: ${code}`); 89 | } 90 | return result; 91 | } 92 | -------------------------------------------------------------------------------- /formulas/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 13219 3 | } -------------------------------------------------------------------------------- /formulas/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/formulas/assets/cover.jpg -------------------------------------------------------------------------------- /formulas/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/formulas/assets/example0.jpg -------------------------------------------------------------------------------- /formulas/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/formulas/assets/icon.png -------------------------------------------------------------------------------- /glossary/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 19440 3 | } -------------------------------------------------------------------------------- /glossary/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/glossary/assets/cover.jpg -------------------------------------------------------------------------------- /glossary/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/glossary/assets/example0.jpg -------------------------------------------------------------------------------- /glossary/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/glossary/assets/icon.png -------------------------------------------------------------------------------- /handlebars/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 25453 3 | } -------------------------------------------------------------------------------- /handlebars/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/handlebars/assets/cover.jpg -------------------------------------------------------------------------------- /handlebars/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/handlebars/assets/example0.jpg -------------------------------------------------------------------------------- /handlebars/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/handlebars/assets/icon.png -------------------------------------------------------------------------------- /handlebars/pack_test.ts: -------------------------------------------------------------------------------- 1 | import {executeFormulaFromPackDef} from '@codahq/packs-sdk/dist/development'; 2 | import {pack} from './pack'; 3 | import * as chai from "chai"; 4 | import {assert} from "chai"; 5 | import {describe} from "mocha"; 6 | import {it} from "mocha"; 7 | import chaiAsPromised from "chai-as-promised"; 8 | chai.use(chaiAsPromised); 9 | chai.should(); 10 | 11 | describe("Examples", () => { 12 | for (let formula of pack.formulas) { 13 | if (formula.examples) { 14 | describe(formula.name, () => { 15 | for (let [i, example] of formula.examples.entries()) { 16 | it(`Example ${i}`, async () => { 17 | const result = await executeFormulaFromPackDef(pack, formula.name, example.params as any); 18 | if (typeof example.result == "object") { 19 | assert.deepEqual(result, example.result); 20 | } else { 21 | assert.equal(result, example.result); 22 | } 23 | }); 24 | } 25 | }); 26 | } 27 | } 28 | }); 29 | -------------------------------------------------------------------------------- /holidays/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 22009 3 | } -------------------------------------------------------------------------------- /holidays/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/holidays/assets/cover.jpg -------------------------------------------------------------------------------- /holidays/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/holidays/assets/example0.jpg -------------------------------------------------------------------------------- /holidays/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/holidays/assets/icon.png -------------------------------------------------------------------------------- /holidays/pack_test.ts: -------------------------------------------------------------------------------- 1 | import {executeFormulaFromPackDef} from "@codahq/packs-sdk/dist/development"; 2 | import {pack} from "./pack"; 3 | import * as chai from "chai"; 4 | import {assert} from "chai"; 5 | import {describe} from "mocha"; 6 | import {it} from "mocha"; 7 | import chaiAsPromised from "chai-as-promised"; 8 | chai.use(chaiAsPromised); 9 | chai.should(); 10 | 11 | describe("Examples", () => { 12 | for (let formula of pack.formulas) { 13 | describe(formula.name, () => { 14 | for (let [i, example] of formula.examples?.entries() ?? []) { 15 | it(`Example ${i}`, async () => { 16 | const result = await executeFormulaFromPackDef(pack, formula.name, example.params as any); 17 | if (typeof example.result == "object") { 18 | assert.deepEqual(result, example.result); 19 | } else { 20 | assert.equal(result, example.result); 21 | } 22 | }); 23 | } 24 | }); 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /holidays/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | npm install date-holidays@"<4.0.0" && 3 | npx mocha --require ts-node/register holidays/pack_test.ts && 4 | npx coda upload holidays/pack.ts --notes="Update NPM package" 5 | -------------------------------------------------------------------------------- /httpbin/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 10766 3 | } -------------------------------------------------------------------------------- /httpbin/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/httpbin/assets/cover.jpg -------------------------------------------------------------------------------- /httpbin/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/httpbin/assets/example0.jpg -------------------------------------------------------------------------------- /httpbin/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/httpbin/assets/icon.png -------------------------------------------------------------------------------- /httpbin/pack.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | export const pack = coda.newPack(); 3 | 4 | pack.addNetworkDomain("httpbin.org"); 5 | 6 | const ValidFetchMethods = ['GET', 'PATCH', 'POST', 'PUT', 'DELETE'] as const; 7 | type FetchMethodType = typeof ValidFetchMethods[number]; 8 | 9 | pack.addFormula({ 10 | name: "Request", 11 | description: "Make a request to httpbin.org.", 12 | parameters: [ 13 | coda.makeParameter({ 14 | type: coda.ParameterType.String, 15 | name: "path", 16 | description: "The path to request.", 17 | }), 18 | coda.makeParameter({ 19 | type: coda.ParameterType.String, 20 | name: "method", 21 | description: "The HTTP method.", 22 | optional: true, 23 | }), 24 | coda.makeParameter({ 25 | type: coda.ParameterType.String, 26 | name: "contentType", 27 | description: "The Content-Type header.", 28 | optional: true, 29 | }), 30 | coda.makeParameter({ 31 | type: coda.ParameterType.String, 32 | name: "body", 33 | description: "The request body.", 34 | optional: true, 35 | }), 36 | coda.makeParameter({ 37 | type: coda.ParameterType.String, 38 | name: "formName", 39 | description: "The name of a form field.", 40 | optional: true, 41 | }), 42 | coda.makeParameter({ 43 | type: coda.ParameterType.String, 44 | name: "formValue", 45 | description: "The value of form field.", 46 | optional: true, 47 | }), 48 | coda.makeParameter({ 49 | type: coda.ParameterType.Boolean, 50 | name: "isBinary", 51 | description: "If the content to fetch is binary. Default: false.", 52 | optional: true, 53 | }), 54 | ], 55 | resultType: coda.ValueType.String, 56 | execute: async ([path, method = "GET", contentType, body, formName, formValue, isBinary = false], context) => { 57 | let headers = {}; 58 | if (contentType) { 59 | headers["Content-Type"] = contentType; 60 | } 61 | let form: any; 62 | if (formName && formValue) { 63 | form = { 64 | [formName]: formValue, 65 | }; 66 | } 67 | try { 68 | let response = await context.fetcher.fetch({ 69 | method: method as FetchMethodType, 70 | url: coda.joinUrl("https://httpbin.org/", path), 71 | headers: headers, 72 | body: body, 73 | form: form, 74 | isBinaryResponse: isBinary, 75 | }); 76 | return JSON.stringify(response.body, null, 2); 77 | } catch (e) { 78 | return JSON.stringify(e, null, 2); 79 | } 80 | }, 81 | }); 82 | -------------------------------------------------------------------------------- /hue/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 15849 3 | } -------------------------------------------------------------------------------- /hue/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/hue/assets/cover.png -------------------------------------------------------------------------------- /hue/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/hue/assets/example0.png -------------------------------------------------------------------------------- /hue/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/hue/assets/icon.png -------------------------------------------------------------------------------- /hue/schemas.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | 3 | export const LightSchema = coda.makeObjectSchema({ 4 | properties: { 5 | name: { type: coda.ValueType.String, required: true }, 6 | type: { type: coda.ValueType.String, fromKey: "archetype" }, 7 | manufacturer: { type: coda.ValueType.String, fromKey: "manufacturername" }, 8 | model: { type: coda.ValueType.String, fromKey: "modelid" }, 9 | lightId: { type: coda.ValueType.String, fromKey: "id", required: true }, 10 | }, 11 | displayProperty: "name", 12 | idProperty: "lightId", 13 | featuredProperties: ["type", "lightId"], 14 | }); 15 | 16 | export const LightStatusSchema = coda.makeObjectSchema({ 17 | properties: { 18 | name: { type: coda.ValueType.String }, 19 | lightId: { type: coda.ValueType.String }, 20 | on: { type: coda.ValueType.Boolean }, 21 | hue: { type: coda.ValueType.Number }, 22 | saturation: { type: coda.ValueType.Number, fromKey: "sat" }, 23 | brightness: { type: coda.ValueType.Number, fromKey: "bri" }, 24 | color: { type: coda.ValueType.String }, 25 | swatch: { type: coda.ValueType.String, codaType: coda.ValueHintType.ImageReference }, 26 | reachable: { type: coda.ValueType.Boolean }, 27 | }, 28 | displayProperty: "name", 29 | }); 30 | 31 | const LightReferenceSchema = coda.makeReferenceSchemaFromObjectSchema(LightSchema, "Light"); 32 | 33 | export const RoomSchema = coda.makeObjectSchema({ 34 | properties: { 35 | name: { type: coda.ValueType.String, required: true }, 36 | type: { type: coda.ValueType.String, fromKey: "class" }, 37 | lights: { 38 | type: coda.ValueType.Array, 39 | items: LightReferenceSchema, 40 | }, 41 | roomId: { type: coda.ValueType.String, fromKey: "id", required: true }, 42 | }, 43 | displayProperty: "name", 44 | idProperty: "roomId", 45 | featuredProperties: ["lights", "roomId"], 46 | }); 47 | 48 | const RoomReferenceSchema = coda.makeReferenceSchemaFromObjectSchema(RoomSchema, "Room"); 49 | 50 | export const SceneSchema = coda.makeObjectSchema({ 51 | properties: { 52 | name: { type: coda.ValueType.String }, 53 | type: { type: coda.ValueType.String }, 54 | lights: { 55 | type: coda.ValueType.Array, 56 | items: LightReferenceSchema, 57 | }, 58 | room: RoomReferenceSchema, 59 | sceneId: { type: coda.ValueType.String, fromKey: "id" }, 60 | }, 61 | displayProperty: "name", 62 | idProperty: "sceneId", 63 | featuredProperties: ["type", "lights", "room"], 64 | }); 65 | 66 | const TimePointSchema = coda.makeObjectSchema({ 67 | properties: { 68 | summary: { type: coda.ValueType.String }, 69 | type: { type: coda.ValueType.String }, 70 | time: { type: coda.ValueType.String }, 71 | offset: { type: coda.ValueType.Number }, 72 | }, 73 | displayProperty: "summary", 74 | }) 75 | 76 | export const AutomationSchema = coda.makeObjectSchema({ 77 | properties: { 78 | name: { type: coda.ValueType.String }, 79 | automationId: { type: coda.ValueType.String, fromKey: "id" }, 80 | enabled: { type: coda.ValueType.Boolean }, 81 | start: TimePointSchema, 82 | end: TimePointSchema, 83 | }, 84 | displayProperty: "name", 85 | idProperty: "automationId", 86 | featuredProperties: ["enabled", "start", "end"], 87 | }); 88 | -------------------------------------------------------------------------------- /ical/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 28876 3 | } -------------------------------------------------------------------------------- /ical/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/ical/assets/example0.jpg -------------------------------------------------------------------------------- /ical/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/ical/assets/icon.png -------------------------------------------------------------------------------- /imoji/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 10702 3 | } -------------------------------------------------------------------------------- /imoji/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/imoji/assets/cover.jpg -------------------------------------------------------------------------------- /imoji/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/imoji/assets/example0.jpg -------------------------------------------------------------------------------- /imoji/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/imoji/assets/icon.png -------------------------------------------------------------------------------- /imoji/pack.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | import * as emojidict from "emoji-dictionary"; 3 | 4 | export const pack = coda.newPack(); 5 | 6 | const DefaultImageSize = 200; 7 | 8 | pack.addFormula({ 9 | name: "Imoji", 10 | description: "Create an image from an emoji.", 11 | parameters: [ 12 | coda.makeParameter({ 13 | type: coda.ParameterType.String, 14 | name: "emoji", 15 | description: "The emoji to convert into an image. It can be the actual emoji characters, or the name of the emoji.", 16 | suggestedValue: "😃", 17 | autocomplete: async function(context, search) { 18 | let options = emojidict.names.map(name => { 19 | let emoji = emojidict.getUnicode(name); 20 | return { 21 | display: `${name} ${emoji}`, 22 | value: name, 23 | }; 24 | }); 25 | return coda.autocompleteSearchObjects(search, options, "display", "value"); 26 | }, 27 | }), 28 | coda.makeParameter({ 29 | type: coda.ParameterType.Number, 30 | name: "size", 31 | description: `The desired size of the resulting image, in pixels. Defaults to ${DefaultImageSize}.`, 32 | optional: true, 33 | }), 34 | ], 35 | resultType: coda.ValueType.String, 36 | codaType: coda.ValueHintType.ImageReference, 37 | examples: [ 38 | {params: ["😃"], result: ""}, 39 | {params: ["smiley"], result: ""}, 40 | {params: [":smiley:"], result: ""}, 41 | ], 42 | execute: async function ([emoji, size = DefaultImageSize], context) { 43 | emoji = emoji.trim(); 44 | if (!emoji?.length) { 45 | return ""; 46 | } 47 | // Remove leading and trailing colons, if present. 48 | emoji = emoji.replace(/^\:(.*)\:$/, "$1"); 49 | 50 | // Replace emoji name with character. 51 | if (emojidict.names.includes(emoji)) { 52 | emoji = emojidict.getUnicode(emoji); 53 | } 54 | 55 | let svg = ` 56 | 57 | ${emoji} 58 | 59 | `.trim(); 60 | let encoded = Buffer.from(svg).toString("base64"); 61 | //return coda.SvgConstants.DataUrlPrefix + encoded; 62 | return "data:image/svg+xml;base64," + encoded; 63 | }, 64 | }); 65 | 66 | pack.addColumnFormat({ 67 | name: "Imoji", 68 | instructions: "Displays the emoji in the column as an image.", 69 | formulaName: "Imoji", 70 | }); -------------------------------------------------------------------------------- /integrately/pack.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | 3 | export const pack = coda.newPack(); 4 | 5 | pack.addFormula({ 6 | name: "TriggerWebhook", 7 | description: "Trigger a webhook created in Integrately.", 8 | parameters: [ 9 | coda.makeParameter({ 10 | type: coda.ParameterType.String, 11 | name: "webhookUrl", 12 | description: "The URL of the webhook. Ex: https://webhooks.integrately.com/a/webhooks/...", 13 | }), 14 | ], 15 | resultType: coda.ValueType.String, 16 | isAction: true, 17 | execute: async function (args, context) { 18 | // TODO: Unpack the parameter values. 19 | let [] = args; 20 | // TODO: Do something. 21 | return "OK"; 22 | }, 23 | }); 24 | -------------------------------------------------------------------------------- /ipinfo/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 23291 3 | } -------------------------------------------------------------------------------- /ipinfo/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/ipinfo/assets/cover.png -------------------------------------------------------------------------------- /ipinfo/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/ipinfo/assets/example0.png -------------------------------------------------------------------------------- /ipinfo/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/ipinfo/assets/icon.png -------------------------------------------------------------------------------- /ipinfo/assets/listing.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 23291, 3 | "name": "IPinfo", 4 | "description": "Use the IPinfo.io service to get information about an IP address, including geolocation.\n\nThe logo, cover image, and brand are the property of IPinfo.", 5 | "shortDescription": "Get geolocation information about IP addresses.", 6 | "logoUrl": "https://codahosted.io/packs/23291/unversioned/assets/LOGO/134345988da315d67de6e9c0c09497b3c8fb65a044902e7a774e155667f8284e1bf2018922a141aa40f24294305fcec8cca76e23605defcb2d7fa83d744597cd8a13300477006cf444222cd63b784a95969079700c213cac9451be4146ca0a1ec708b5df", 7 | "coverUrl": "https://codahosted.io/packs/23291/unversioned/assets/COVER/43de7c9c9abc901b906e4ccc1eefd9b46aa9d92b456eef289746ffcfef4602fcd3c98e0e29a09f7984bf583d1973f48b840b5494f860a52ef4a505248fdb5a3f8bd29a9b48c36962c5d612db43e0c9add6e5f58d03a2dca839658705d1a2ca776e122cb5", 8 | "exampleImages": [ 9 | { 10 | "imageUrl": "https://codahosted.io/packs/23291/unversioned/assets/EXAMPLE/43de7c9c9abc901b906e4ccc1eefd9b46aa9d92b456eef289746ffcfef4602fcd3c98e0e29a09f7984bf583d1973f48b840b5494f860a52ef4a505248fdb5a3f8bd29a9b48c36962c5d612db43e0c9add6e5f58d03a2dca839658705d1a2ca776e122cb5", 11 | "filename": "Cover photo", 12 | "assetId": "43de7c9c9abc901b906e4ccc1eefd9b46aa9d92b456eef289746ffcfef4602fcd3c98e0e29a09f7984bf583d1973f48b840b5494f860a52ef4a505248fdb5a3f8bd29a9b48c36962c5d612db43e0c9add6e5f58d03a2dca839658705d1a2ca776e122cb5" 13 | } 14 | ], 15 | "packVersion": "6", 16 | "releaseId": 1, 17 | "lastReleasedAt": "2023-07-10T00:16:33.020Z", 18 | "externalMetadataUrl": "https://codahosted.io/packs/23291/6/metadata/14f5d527899fb59aa69dba890c693638914f25adf7f8dc43bd3013ac5ef73e8b752b2e7350938b5b087f1e730281eefee87dd6117740ee72a60293a55eba7f733acf2eb0c3bf0f4fef79e54d8506214d2708c4c549a31fe19b94fb22dce36a68aa2305b1.json", 19 | "categories": [ 20 | { 21 | "categoryId": "5b2f676a-1700-4feb-bc90-9a6a6715edfd", 22 | "categoryName": "IT admins", 23 | "categorySlug": "it-admins" 24 | } 25 | ], 26 | "supportEmail": "packs@erickoleda.com", 27 | "sourceCodeVisibility": "private", 28 | "makers": [ 29 | { 30 | "name": "Eric Koleda", 31 | "pictureLink": "https://images-codaio.imgix.net/https%3A%2F%2Flh3.googleusercontent.com%2Fa%2FACg8ocJYa8nknu-CdeUwswBMRrDPKyLXdwzRPhMFuTodG063315NF-Xe%3Ds96-c?ixlib=js-3.8.0&auto=format%2Ccompress&fit=crop&w=360&h=360&crop=faces&s=3883cf143fc4db7e36979789fddc045e", 32 | "employer": "Coda", 33 | "jobTitle": "Developer Advocate", 34 | "slug": "eric-koleda" 35 | } 36 | ], 37 | "standardPackPlan": { 38 | "packPlanId": "a5f02818-6b3b-4933-bd99-b8954b7efd8b", 39 | "packId": 23291, 40 | "createdAt": "2023-06-22T17:38:02.891Z", 41 | "pricing": { 42 | "type": "Free" 43 | } 44 | }, 45 | "sdkVersion": "1.4.0", 46 | "discoverability": "public", 47 | "userAccess": { 48 | "canEdit": true, 49 | "canInstall": false, 50 | "canTest": true, 51 | "canView": true, 52 | "canPurchase": false, 53 | "requiresTrial": false, 54 | "canConnectAccount": true 55 | } 56 | } -------------------------------------------------------------------------------- /ipinfo/pack.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | export const pack = coda.newPack(); 3 | 4 | const InfoSchema = coda.makeObjectSchema({ 5 | properties: { 6 | ip: { 7 | type: coda.ValueType.String, 8 | description: "The IP address.", 9 | }, 10 | city: { 11 | type: coda.ValueType.String, 12 | description: "The city where the IP is associated.", 13 | }, 14 | region: { 15 | type: coda.ValueType.String, 16 | description: "The region where the IP is associated.", 17 | }, 18 | country: { 19 | type: coda.ValueType.String, 20 | description: "The country where the IP is associated.", 21 | }, 22 | postal: { 23 | type: coda.ValueType.String, 24 | description: "The post code where the IP is associated.", 25 | }, 26 | timezone: { 27 | type: coda.ValueType.String, 28 | description: "The timezone where the IP is associated.", 29 | }, 30 | latitude: { 31 | type: coda.ValueType.Number, 32 | description: "The latitude where the IP is associated.", 33 | }, 34 | longitude: { 35 | type: coda.ValueType.Number, 36 | description: "The longitude where the IP is associated.", 37 | }, 38 | }, 39 | displayProperty: "ip", 40 | }); 41 | 42 | pack.addFormula({ 43 | name: "IPInfo", 44 | description: "Gets information about an IP address.", 45 | parameters: [ 46 | coda.makeParameter({ 47 | type: coda.ParameterType.String, 48 | name: "ipAddress", 49 | description: "The IP address to lookup.", 50 | }), 51 | ], 52 | resultType: coda.ValueType.Object, 53 | schema: InfoSchema, 54 | execute: async function (args, context) { 55 | let [ipAddress] = args; 56 | let response = await context.fetcher.fetch({ 57 | method: "GET", 58 | url: `https://ipinfo.io/${ipAddress}/json`, 59 | }); 60 | let data = response.body; 61 | if (data.loc) { 62 | let [lat, long] = data.loc.split(",").map(p => Number(p)); 63 | data.latitude = lat; 64 | data.longitude = long; 65 | } 66 | return data; 67 | }, 68 | }); 69 | 70 | pack.addNetworkDomain("ipinfo.io"); 71 | -------------------------------------------------------------------------------- /json5/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 34513 3 | } -------------------------------------------------------------------------------- /json5/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/json5/assets/example0.png -------------------------------------------------------------------------------- /json5/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/json5/assets/icon.png -------------------------------------------------------------------------------- /json5/assets/listing.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 34513, 3 | "name": "JSON5", 4 | "description": "JSON5 is an extension to the popular JSON file format that aims to be easier to write and maintain by hand (e.g. for config files). This Pack provides a number of utility formulas that make it easier to work with JSON5 content in Coda.\n\nIcon and logo are the property of json5.org.", 5 | "shortDescription": "JSON for Humans", 6 | "logoUrl": "https://codahosted.io/packs/34513/unversioned/assets/LOGO/e30ee67601594d142a4bede9a6a8cf74d99c61e867845fdc8cac5cc1bc9862b79e942872aa60bf2e681f22c1a700002cb4faab65a8a940a05668808122e6edb3c956ddf41c56f309bc79a25318f8d9e66a36bfd24ecb34ecbd163c243afa88a68f2ba9c2", 7 | "exampleImages": [ 8 | { 9 | "imageUrl": "https://codahosted.io/packs/34513/unversioned/assets/EXAMPLE/4a7e48bd619cecd19789e1d4af4ffa514e49ad63eb7469366efc1c278fb32d48161fbaa59d8da4790a85c7a036b9fc6c10e8f3eecb93a8372cb3f9229a5333776673b20567991e95532e03aadad4dad7d07460cdd127ff496667f4c28c4f78cc00c5bb0a", 10 | "filename": "json5.png", 11 | "mimeType": "image/png", 12 | "assetId": "4a7e48bd619cecd19789e1d4af4ffa514e49ad63eb7469366efc1c278fb32d48161fbaa59d8da4790a85c7a036b9fc6c10e8f3eecb93a8372cb3f9229a5333776673b20567991e95532e03aadad4dad7d07460cdd127ff496667f4c28c4f78cc00c5bb0a" 13 | } 14 | ], 15 | "packVersion": "2", 16 | "releaseId": 1, 17 | "lastReleasedAt": "2024-09-23T21:32:38.333Z", 18 | "externalMetadataUrl": "https://codahosted.io/packs/34513/2/metadata/18e7612db4f10a0dc6dcaded1887acb447355c3d55af73cca6e3cda8251635020ce73f3320a334451eb6b37a747c92fa7784e79e8f84122667715b9158edfa03aa68686fb697bb2a13480d8b8c0d4a372bacf88b2160f92d2977767a6d941957b49b60be.json", 19 | "categories": [ 20 | { 21 | "categoryId": "076f4d78-eac3-4d62-b87a-4f3964c96e65", 22 | "categoryName": "Engineering", 23 | "categorySlug": "engineering" 24 | } 25 | ], 26 | "supportEmail": "packs@erickoleda.com", 27 | "sourceCodeVisibility": "private", 28 | "makers": [ 29 | { 30 | "name": "Eric Koleda", 31 | "pictureLink": "https://images-codaio.imgix.net/https%3A%2F%2Flh3.googleusercontent.com%2Fa%2FACg8ocJYa8nknu-CdeUwswBMRrDPKyLXdwzRPhMFuTodG063315NF-Xe%3Ds96-c?ixlib=js-3.8.0&auto=format%2Ccompress&fit=crop&w=360&h=360&crop=faces&s=3883cf143fc4db7e36979789fddc045e", 32 | "employer": "Coda", 33 | "jobTitle": "Developer Advocate", 34 | "slug": "eric-koleda" 35 | } 36 | ], 37 | "standardPackPlan": { 38 | "packPlanId": "be92857f-93fe-4e73-abe3-9dece7ad37ac", 39 | "packId": 34513, 40 | "createdAt": "2024-09-23T21:17:09.359Z", 41 | "pricing": { 42 | "type": "Free" 43 | } 44 | }, 45 | "sdkVersion": "1.7.5", 46 | "discoverability": "public", 47 | "userAccess": { 48 | "canEdit": true, 49 | "canInstall": false, 50 | "canTest": true, 51 | "canView": true, 52 | "canPurchase": false, 53 | "requiresTrial": false, 54 | "canConnectAccount": true 55 | } 56 | } -------------------------------------------------------------------------------- /json5/tests/pack_test.ts: -------------------------------------------------------------------------------- 1 | import {executeFormulaFromPackDef} from '@codahq/packs-sdk/dist/development'; 2 | import {pack} from '../pack'; 3 | import * as chai from "chai"; 4 | import {assert} from "chai"; 5 | import {describe} from "mocha"; 6 | import {it} from "mocha"; 7 | import chaiAsPromised from "chai-as-promised"; 8 | chai.use(chaiAsPromised); 9 | chai.should(); 10 | 11 | describe("Examples", () => { 12 | for (let formula of pack.formulas) { 13 | describe(formula.name, () => { 14 | for (let [i, example] of formula.examples.entries()) { 15 | it(`Example ${i}`, async () => { 16 | const result = await executeFormulaFromPackDef(pack, formula.name, example.params as any); 17 | if (typeof example.result == "object") { 18 | assert.deepEqual(result, example.result); 19 | } else { 20 | assert.equal(result, example.result); 21 | } 22 | }); 23 | } 24 | }); 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /jwt/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 24109 3 | } -------------------------------------------------------------------------------- /jwt/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/jwt/assets/example0.jpg -------------------------------------------------------------------------------- /jwt/assets/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 5 | Created with Sketch. 6 | 7 | 8 | 27 | 28 | -------------------------------------------------------------------------------- /keep/pack.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Blocked, as this API currently only works with DwD. 3 | */ 4 | 5 | import * as coda from "@codahq/packs-sdk"; 6 | export const pack = coda.newPack(); 7 | 8 | pack.addNetworkDomain("googleapis.com"); 9 | 10 | pack.setUserAuthentication({ 11 | type: coda.AuthenticationType.OAuth2, 12 | authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth", 13 | tokenUrl: "https://oauth2.googleapis.com/token", 14 | scopes: [ 15 | "profile", 16 | "https://www.googleapis.com/auth/keep", 17 | ], 18 | additionalParams: { 19 | access_type: "offline", 20 | prompt: "consent", 21 | }, 22 | getConnectionName: async function (context) { 23 | let response = await context.fetcher.fetch({ 24 | method: "GET", 25 | url: "https://www.googleapis.com/oauth2/v1/userinfo", 26 | }); 27 | let user = response.body; 28 | return user.name; 29 | }, 30 | }); 31 | 32 | const NoteSchema = coda.makeObjectSchema({ 33 | properties: { 34 | id: { type: coda.ValueType.String, fromKey: "name" }, 35 | title: { type: coda.ValueType.String }, 36 | body: { type: coda.ValueType.String, codaType: coda.ValueHintType.Markdown }, 37 | images: { 38 | type: coda.ValueType.Array, 39 | items: { type: coda.ValueType.String, codaType: coda.ValueHintType.Attachment }, 40 | fromKey: "attachments", 41 | }, 42 | trashed: { type: coda.ValueType.String, codaType: coda.ValueHintType.DateTime, fromKey: "trashed" }, 43 | createdAt: { type: coda.ValueType.String, codaType: coda.ValueHintType.DateTime, fromKey: "createTime" }, 44 | updatedAt: { type: coda.ValueType.String, codaType: coda.ValueHintType.DateTime, fromKey: "updateTime" }, 45 | trashedAt: { type: coda.ValueType.String, codaType: coda.ValueHintType.DateTime, fromKey: "trashTime" }, 46 | }, 47 | displayProperty: "title", 48 | idProperty: "id", 49 | featuredProperties: ["body", "attachments"], 50 | }); 51 | 52 | pack.addSyncTable({ 53 | name: "Notes", 54 | description: "Lists the notes in your account.", 55 | identityName: "Note", 56 | schema: NoteSchema, 57 | formula: { 58 | name: "SyncNotes", 59 | description: "Syncs the data.", 60 | parameters: [ 61 | coda.makeParameter({ 62 | type: coda.ParameterType.Boolean, 63 | name: "includeTrashed", 64 | description: "If true, trashed notes are included.", 65 | }), 66 | ], 67 | execute: async function (args, context) { 68 | let [includeTrashed] = args; 69 | let pageToken = context.sync.continuation?.pageToken; 70 | let url = coda.withQueryParams("https://keep.googleapis.com/v1/notes", { 71 | pageToken, 72 | }); 73 | let response = await context.fetcher.fetch({ 74 | method: "GET", 75 | url: url, 76 | }); 77 | let page = response.body; 78 | let rows = page.notes.map(note => { 79 | return { 80 | ...note, 81 | }; 82 | }); 83 | let continuation; 84 | if (page.nextPageToken) { 85 | continuation = {pageToken: page.nextPageToken}; 86 | } 87 | return { 88 | result: rows, 89 | continuation, 90 | }; 91 | }, 92 | }, 93 | }); -------------------------------------------------------------------------------- /mailto/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 16431 3 | } -------------------------------------------------------------------------------- /mailto/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/mailto/assets/cover.png -------------------------------------------------------------------------------- /mailto/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/mailto/assets/example0.png -------------------------------------------------------------------------------- /mailto/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/mailto/assets/icon.png -------------------------------------------------------------------------------- /mailto/pack.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | export const pack = coda.newPack(); 3 | 4 | pack.addFormula({ 5 | name: "MailtoLink", 6 | description: "Generate a mailto link, which when clicked will start composing a new email in the user's email program.", 7 | parameters: [ 8 | makeParameter("To"), 9 | makeParameter("CC"), 10 | makeParameter("BCC"), 11 | makeParameter("Subject"), 12 | makeParameter("Body"), 13 | ], 14 | resultType: coda.ValueType.String, 15 | codaType: coda.ValueHintType.Url, 16 | examples: [ 17 | { 18 | params: ["alice@example.com"], 19 | result: "mailto:alice@example.com", 20 | }, 21 | { 22 | params: ["alice@example.com", "bob@example.com, carol@example.com"], 23 | result: "mailto:alice@example.com?cc=bob%40example.com%2C%20carol%40example.com", 24 | }, 25 | { 26 | params: ["alice@example.com", null, null, "Free cake!"], 27 | result: "mailto:alice@example.com?subject=Free%20cake!", 28 | }, 29 | { 30 | params: ["alice@example.com", null, null, "Free cake!", "Yum!"], 31 | result: "mailto:alice@example.com?subject=Free%20cake!&body=Yum!", 32 | }, 33 | { 34 | params: [null, null, null, "No more cake"], 35 | result: "mailto:?subject=No%20more%20cake", 36 | }, 37 | ], 38 | execute: async function (args, context) { 39 | let [to, cc, bcc, subject, body] = args; 40 | let params = compact({ cc, bcc, subject, body }); 41 | return coda.withQueryParams(`mailto:${to ?? ""}`, params); 42 | }, 43 | }); 44 | 45 | function makeParameter(field: string) { 46 | return coda.makeParameter({ 47 | type: coda.ParameterType.String, 48 | name: field.toLowerCase(), 49 | description: `The value to pre-fill in the "${field}" field of the email.`, 50 | optional: true, 51 | }); 52 | } 53 | 54 | function compact(obj: Record): Record { 55 | return Object.fromEntries( 56 | Object.entries(obj).filter(([key, value]) => 57 | value !== undefined && value !== null)); 58 | } -------------------------------------------------------------------------------- /mailto/pack_test.ts: -------------------------------------------------------------------------------- 1 | import {executeFormulaFromPackDef} from '@codahq/packs-sdk/dist/development'; 2 | import {pack} from './pack'; 3 | import * as chai from "chai"; 4 | import {assert} from "chai"; 5 | import {describe} from "mocha"; 6 | import {it} from "mocha"; 7 | import chaiAsPromised from "chai-as-promised"; 8 | chai.use(chaiAsPromised); 9 | chai.should(); 10 | 11 | describe("Examples", () => { 12 | for (let formula of pack.formulas) { 13 | describe(formula.name, () => { 14 | for (let [i, example] of formula.examples.entries()) { 15 | it(`Example ${i}`, async () => { 16 | const result = await executeFormulaFromPackDef(pack, formula.name, example.params as any); 17 | if (typeof example.result == "object") { 18 | assert.deepEqual(result, example.result); 19 | } else { 20 | assert.equal(result, example.result); 21 | } 22 | }); 23 | } 24 | }); 25 | } 26 | }); -------------------------------------------------------------------------------- /medium/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 20891 3 | } -------------------------------------------------------------------------------- /medium/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/medium/assets/cover.png -------------------------------------------------------------------------------- /medium/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/medium/assets/example0.png -------------------------------------------------------------------------------- /medium/assets/icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/medium/assets/icon.jpg -------------------------------------------------------------------------------- /metadata/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 17088 3 | } -------------------------------------------------------------------------------- /metadata/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/metadata/assets/cover.jpg -------------------------------------------------------------------------------- /metadata/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/metadata/assets/example0.jpg -------------------------------------------------------------------------------- /metadata/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/metadata/assets/icon.png -------------------------------------------------------------------------------- /metadata/server/.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "pack-redirector" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /metadata/server/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | firebase-debug.log* 8 | firebase-debug.*.log* 9 | 10 | # Firebase cache 11 | .firebase/ 12 | 13 | # Firebase config 14 | 15 | # Uncomment this if you'd like others to create their own Firebase project. 16 | # For a team working on the same Firebase project(s), it is recommended to leave 17 | # it commented so all members can deploy to the same project(s) in .firebaserc. 18 | # .firebaserc 19 | 20 | # Runtime data 21 | pids 22 | *.pid 23 | *.seed 24 | *.pid.lock 25 | 26 | # Directory for instrumented libs generated by jscoverage/JSCover 27 | lib-cov 28 | 29 | # Coverage directory used by tools like istanbul 30 | coverage 31 | 32 | # nyc test coverage 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 36 | .grunt 37 | 38 | # Bower dependency directory (https://bower.io/) 39 | bower_components 40 | 41 | # node-waf configuration 42 | .lock-wscript 43 | 44 | # Compiled binary addons (http://nodejs.org/api/addons.html) 45 | build/Release 46 | 47 | # Dependency directories 48 | node_modules/ 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | -------------------------------------------------------------------------------- /metadata/server/firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "functions": { 3 | "source": "functions", 4 | "predeploy": [ 5 | "npm --prefix \"$RESOURCE_DIR\" run lint", 6 | "npm --prefix \"$RESOURCE_DIR\" run build" 7 | ] 8 | }, 9 | "hosting": { 10 | "public": "public", 11 | "ignore": [ 12 | "firebase.json", 13 | "**/.*", 14 | "**/node_modules/**" 15 | ], 16 | "rewrites": [ 17 | { "source": "/", "function": "redirect" } 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /metadata/server/functions/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | es6: true, 5 | node: true, 6 | }, 7 | extends: [ 8 | "eslint:recommended", 9 | "plugin:import/errors", 10 | "plugin:import/warnings", 11 | "plugin:import/typescript", 12 | "google", 13 | "plugin:@typescript-eslint/recommended", 14 | ], 15 | parser: "@typescript-eslint/parser", 16 | parserOptions: { 17 | project: ["tsconfig.json", "tsconfig.dev.json"], 18 | sourceType: "module", 19 | }, 20 | ignorePatterns: [ 21 | "/lib/**/*", // Ignore built files. 22 | ], 23 | plugins: [ 24 | "@typescript-eslint", 25 | "import", 26 | ], 27 | rules: { 28 | "quotes": ["error", "double"], 29 | "import/no-unresolved": 0, 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /metadata/server/functions/.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled JavaScript files 2 | lib/**/*.js 3 | lib/**/*.js.map 4 | 5 | # TypeScript v1 declaration files 6 | typings/ 7 | 8 | # Node.js dependency directory 9 | node_modules/ 10 | -------------------------------------------------------------------------------- /metadata/server/functions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "functions", 3 | "scripts": { 4 | "lint": "eslint --ext .js,.ts .", 5 | "build": "tsc", 6 | "build:watch": "tsc --watch", 7 | "serve": "npm run build && firebase emulators:start --only functions", 8 | "shell": "npm run build && firebase functions:shell", 9 | "start": "npm run shell", 10 | "deploy": "firebase deploy --only functions", 11 | "logs": "firebase functions:log" 12 | }, 13 | "engines": { 14 | "node": "16" 15 | }, 16 | "main": "lib/index.js", 17 | "dependencies": { 18 | "firebase-admin": "^10.0.2", 19 | "firebase-functions": "^4.1.0" 20 | }, 21 | "devDependencies": { 22 | "@typescript-eslint/eslint-plugin": "^5.12.0", 23 | "@typescript-eslint/parser": "^5.12.0", 24 | "eslint": "^8.9.0", 25 | "eslint-config-google": "^0.14.0", 26 | "eslint-plugin-import": "^2.25.4", 27 | "firebase-functions-test": "^0.2.0", 28 | "typescript": "^4.5.4" 29 | }, 30 | "private": true 31 | } 32 | -------------------------------------------------------------------------------- /metadata/server/functions/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as functions from "firebase-functions"; 2 | 3 | export const redirect = functions.https.onRequest(async (request, response) => { 4 | const secret = request.headers.authorization?.split(" ")[1]; 5 | if (secret != process.env.SECRET_VALUE) { 6 | response.status(403).send("Invalid authorization."); 7 | return; 8 | } 9 | const url = request.query.url as string; 10 | if (!url) { 11 | response.status(400).send("Missing parameter: url"); 12 | return; 13 | } 14 | console.log("User-Agent: %s, IP Address: %s, URL: %s", 15 | request.headers["user-agent"], request.ip, url); 16 | response.redirect(url); 17 | }); 18 | -------------------------------------------------------------------------------- /metadata/server/functions/tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | ".eslintrc.js" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /metadata/server/functions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "noImplicitReturns": true, 5 | "noUnusedLocals": true, 6 | "outDir": "lib", 7 | "sourceMap": true, 8 | "strict": true, 9 | "target": "es2017" 10 | }, 11 | "compileOnSave": true, 12 | "include": [ 13 | "src" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /metadata/server/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Not found 5 | 6 | 7 | -------------------------------------------------------------------------------- /mockaroo/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 17106 3 | } -------------------------------------------------------------------------------- /mockaroo/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/mockaroo/assets/cover.png -------------------------------------------------------------------------------- /mockaroo/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/mockaroo/assets/example0.png -------------------------------------------------------------------------------- /mockaroo/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/mockaroo/assets/icon.png -------------------------------------------------------------------------------- /nomnoml/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 28266 3 | } -------------------------------------------------------------------------------- /nomnoml/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/nomnoml/assets/cover.png -------------------------------------------------------------------------------- /nomnoml/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/nomnoml/assets/example0.png -------------------------------------------------------------------------------- /nomnoml/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/nomnoml/assets/icon.png -------------------------------------------------------------------------------- /nomnoml/pack.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | import * as nomnoml from "nomnoml"; 3 | 4 | const OneDaySecs = 24 * 60 * 60; 5 | 6 | export const pack = coda.newPack(); 7 | 8 | pack.addFormula({ 9 | name: "Diagram", 10 | description: "Draws a diagram from nomnoml markup.", 11 | parameters: [ 12 | coda.makeParameter({ 13 | type: coda.ParameterType.String, 14 | name: "markup", 15 | description: "The nomnoml diagram markup (see the language reference at nomnoml.com).", 16 | }), 17 | ], 18 | resultType: coda.ValueType.String, 19 | codaType: coda.ValueHintType.ImageReference, 20 | cacheTtlSecs: OneDaySecs, 21 | execute: async function (args, context) { 22 | let [code] = args; 23 | let svg = nomnoml.renderSvg(code); 24 | let encoded = Buffer.from(svg).toString("base64"); 25 | return coda.SvgConstants.DataUrlPrefix + encoded; 26 | }, 27 | }); 28 | 29 | pack.addColumnFormat({ 30 | name: "Diagram", 31 | instructions: "Enter nomnoml markup and it will be rendered as a diagram.", 32 | formulaName: "Diagram", 33 | }); 34 | 35 | pack.addFormula({ 36 | name: "GenerateMarkup", 37 | description: "Generate nomnoml markup from a table that has a parent-child relationship.", 38 | parameters: [ 39 | coda.makeParameter({ 40 | type: coda.ParameterType.SparseStringArray, 41 | name: "labelColumn", 42 | description: "The entire column of the table that contains the label for each row.", 43 | }), 44 | coda.makeParameter({ 45 | type: coda.ParameterType.SparseStringArray, 46 | name: "parentColumn", 47 | description: "The entire column of the table that contains the parent for each row, if any.", 48 | }), 49 | ], 50 | resultType: coda.ValueType.String, 51 | cacheTtlSecs: OneDaySecs, 52 | execute: async function (args, context) { 53 | let [labels, parents] = args; 54 | [labels, parents].reduce((result, list) => { 55 | if (!result) return list.length; 56 | if (result != list.length) throw new coda.UserVisibleError("All lists must be the same length."); 57 | return result; 58 | }, 0); 59 | let lines = labels.map((label, i) => { 60 | let parent = parents[i]; 61 | if (!label) return ""; 62 | if (parent) { 63 | return `[${parent}] -> [${label}]`; 64 | } else { 65 | return `[${label}]`; 66 | } 67 | }); 68 | return lines.filter(Boolean).join("\n"); 69 | }, 70 | }); -------------------------------------------------------------------------------- /nps/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 14223 3 | } -------------------------------------------------------------------------------- /nps/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/nps/assets/cover.jpg -------------------------------------------------------------------------------- /nps/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/nps/assets/example0.jpg -------------------------------------------------------------------------------- /nps/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/nps/assets/icon.png -------------------------------------------------------------------------------- /nps/pack.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | 3 | export const pack = coda.newPack(); 4 | 5 | const PromoterValue = "Promoter"; 6 | const PassiveValue = "Passive"; 7 | const DetractorValue = "Detractor"; 8 | 9 | pack.addFormula({ 10 | name: "Category", 11 | description: "Calculates whether the given score is a Promoter, Detractor, or Passive.", 12 | parameters: [ 13 | coda.makeParameter({ 14 | type: coda.ParameterType.Number, 15 | name: "rating", 16 | description: "The rating (0-10) that the user gave.", 17 | }), 18 | ], 19 | resultType: coda.ValueType.String, 20 | examples: [ 21 | { params: [4], result: "Detractor" }, 22 | { params: [7], result: "Passive" }, 23 | { params: [9], result: "Promoter" }, 24 | ], 25 | execute: async function (args, context) { 26 | let [rating] = args; 27 | if (isInvalid(rating)) { 28 | throw new coda.UserVisibleError("Invalid rating: " + rating); 29 | } 30 | return getBucket(rating); 31 | } 32 | }); 33 | 34 | pack.addFormula({ 35 | name: "NPS", 36 | description: "Calculates the Net Promoter Score® for a set of ratings.", 37 | parameters: [ 38 | coda.makeParameter({ 39 | type: coda.ParameterType.SparseNumberArray, 40 | name: "ratings", 41 | description: "The ratings (0-10) that the users gave.", 42 | }), 43 | ], 44 | resultType: coda.ValueType.Number, 45 | examples: [ 46 | { params: [10, 4, 9, 8], result: 25 }, 47 | ], 48 | execute: async function (args, context) { 49 | let [ratings] = args; 50 | 51 | // Remove sparse rows. 52 | ratings = ratings.filter(rating => rating !== null && rating !== undefined); 53 | 54 | // Handle an empty list. 55 | if (!ratings.length) { 56 | return 0; 57 | } 58 | 59 | // Handle invalid ratings. 60 | let invalid = ratings.filter(rating => isInvalid(rating)); 61 | if (invalid.length) { 62 | throw new coda.UserVisibleError("Invalid ratings: " + invalid.join(", ")); 63 | } 64 | 65 | let buckets = ratings.map(rating => getBucket(rating)); 66 | let total = buckets.length; 67 | let promoters = buckets.filter(bucket => bucket == PromoterValue).length; 68 | let detractors = buckets.filter(bucket => bucket == DetractorValue).length; 69 | return Math.round((promoters - detractors) * 100 / total); 70 | } 71 | }); 72 | 73 | function isInvalid(rating) { 74 | return rating < 0 || rating > 10; 75 | } 76 | 77 | function getBucket(rating) { 78 | if (rating >= 9) return PromoterValue; 79 | if (rating <= 6) return DetractorValue; 80 | return PassiveValue; 81 | } -------------------------------------------------------------------------------- /nps/pack_test.ts: -------------------------------------------------------------------------------- 1 | import {executeFormulaFromPackDef} from '@codahq/packs-sdk/dist/development'; 2 | import {pack} from './pack'; 3 | import * as chai from "chai"; 4 | import {assert} from "chai"; 5 | import {describe} from "mocha"; 6 | import {it} from "mocha"; 7 | import chaiAsPromised from "chai-as-promised"; 8 | chai.use(chaiAsPromised); 9 | chai.should(); 10 | 11 | describe('NPS', () => { 12 | it(`All promoters`, async () => { 13 | const ratings = [9, 10]; 14 | const result = await executeFormulaFromPackDef(pack, 'NPS', [ratings]); 15 | assert.equal(result, 100); 16 | }); 17 | 18 | it(`All detractors`, async () => { 19 | const ratings = [0, 1, 2, 3, 4, 5, 6]; 20 | const result = await executeFormulaFromPackDef(pack, 'NPS', [ratings]); 21 | assert.equal(result, -100); 22 | }); 23 | 24 | it(`All passives`, async () => { 25 | const ratings = [7, 8]; 26 | const result = await executeFormulaFromPackDef(pack, 'NPS', [ratings]); 27 | assert.equal(result, 0); 28 | }); 29 | 30 | it(`All ratings`, async () => { 31 | const ratings = [4, 3, 0, 9, 5, 8, 10, 6, 1, 7, 2]; 32 | const result = await executeFormulaFromPackDef(pack, 'NPS', [ratings]); 33 | assert.equal(result, -45); 34 | }); 35 | 36 | it(`Balanced`, async () => { 37 | const ratings = [0, 1, 7, 8, 9, 10]; 38 | const result = await executeFormulaFromPackDef(pack, 'NPS', [ratings]); 39 | assert.equal(result, 0); 40 | }); 41 | 42 | it(`Empty`, async () => { 43 | const ratings = []; 44 | const result = await executeFormulaFromPackDef(pack, 'NPS', [ratings]); 45 | assert.equal(result, 0); 46 | }); 47 | 48 | it(`Sparse`, async () => { 49 | const ratings = [1, null, 10, 10]; 50 | const result = await executeFormulaFromPackDef(pack, 'NPS', [ratings]); 51 | assert.equal(result, 33); 52 | }); 53 | 54 | it(`Sparse empty`, async () => { 55 | const ratings = [null, null, null]; 56 | const result = await executeFormulaFromPackDef(pack, 'NPS', [ratings]); 57 | assert.equal(result, 0); 58 | }); 59 | 60 | it(`Invalid`, async () => { 61 | const ratings = [-1, 1, 10, 11]; 62 | const invocation = executeFormulaFromPackDef(pack, 'NPS', [ratings]); 63 | await invocation.should.be.rejected; 64 | }); 65 | 66 | }); -------------------------------------------------------------------------------- /orgchart/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 32027 3 | } -------------------------------------------------------------------------------- /orgchart/assets/example0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/orgchart/assets/example0.gif -------------------------------------------------------------------------------- /orgchart/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/orgchart/assets/icon.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@codahq/packs-sdk": "^1.9.5", 4 | "@dicebear/collection": "^7.0.4", 5 | "@dicebear/core": "^7.0.4", 6 | "@universe/address-parser": "^4.0.0", 7 | "@xmldom/xmldom": "^0.9.3", 8 | "airport-codes": "^1.0.2", 9 | "ajv": "^8.12.0", 10 | "awesome-phonenumber": "^3.1.0", 11 | "but-csv": "^2.0.13", 12 | "case": "^1.6.3", 13 | "cheerio": "^1.0.0-rc.12", 14 | "color-convert": "^2.0.1", 15 | "content-disposition": "^0.5.4", 16 | "countries-list": "^3.1.1", 17 | "css-named-colors": "^1.0.1", 18 | "date-holidays": "^3.23.8", 19 | "diff": "^5.1.0", 20 | "email-addresses": "^5.0.0", 21 | "emoji-dictionary": "^1.0.11", 22 | "escape-html": "^1.0.3", 23 | "figlet": "^1.5.2", 24 | "flight-designator": "^3.1.1", 25 | "form-data": "^4.0.0", 26 | "handlebars": "^4.7.8", 27 | "hsl-to-hex": "^1.0.0", 28 | "hue-colors": "^0.5.4", 29 | "ical-generator": "^6.0.1", 30 | "json-mask": "^2.0.0", 31 | "json5": "^2.2.3", 32 | "jsrsasign": "^10.8.6", 33 | "jszip": "^3.10.1", 34 | "lodash": "^4.17.21", 35 | "luxon": "^3.2.1", 36 | "lz-string": "^1.5.0", 37 | "metadata-scraper": "^0.2.61", 38 | "mime-types": "^2.1.35", 39 | "node-spintax": "^1.0.4", 40 | "nomnoml": "^1.6.2", 41 | "number-abbreviate": "^2.0.0", 42 | "parse-data-uri": "^0.2.0", 43 | "qs": "^6.12.3", 44 | "regex-escape": "^3.4.10", 45 | "sanitize-filename": "^1.6.3", 46 | "seinfeldapi": "github:wdifruscio/seinfeld-api", 47 | "sql.js": "^1.8.0", 48 | "stopword": "^3.1.1", 49 | "text-encoding": "^0.7.0", 50 | "tokenize-words": "^0.0.4-alpha.0", 51 | "url-parse": "^1.5.10", 52 | "url-polyfill": "^1.1.12", 53 | "xml2js": "^0.6.2", 54 | "xpath": "^0.0.34", 55 | "xslt-processor": "^3.2.0", 56 | "yaml": "^2.5.1" 57 | }, 58 | "devDependencies": { 59 | "@types/chai": "^4.3.0", 60 | "@types/chai-as-promised": "^7.1.3", 61 | "@types/lodash": "^4.14.182", 62 | "@types/luxon": "^3.2.0", 63 | "@types/mocha": "^9.0.0", 64 | "@types/node": "^17.0.10", 65 | "@types/sinon": "^10.0.6", 66 | "@typescript-eslint/eslint-plugin": "^5.10.2", 67 | "@typescript-eslint/experimental-utils": "^5.10.2", 68 | "@typescript-eslint/parser": "^5.10.2", 69 | "chai": "^4.3.4", 70 | "chai-as-promised": "^7.1.1", 71 | "eslint": "^8.8.0", 72 | "eslint-plugin-ban": "^1.6.0", 73 | "eslint-plugin-filenames": "^1.3.2", 74 | "eslint-plugin-local": "^1.0.0", 75 | "eslint-plugin-prefer-let": "^3.0.1", 76 | "firebase-tools": "^11.16.1", 77 | "glob": "^10.3.1", 78 | "json-schema": "^0.4.0", 79 | "mocha": "^9.1.4", 80 | "sinon": "^13.0.1", 81 | "ts-node": "^10.4.0", 82 | "typescript": "^4.5.4" 83 | }, 84 | "scripts": { 85 | "test": "mocha --require ts-node/register **/*_test.ts --ignore node_modules/**/*", 86 | "postinstall": "npx patch-package && sh bin/patch.sh", 87 | "generate-fonts": "node -r ts-node/register asciify/utils/generate-fonts.ts", 88 | "backup": "node -r ts-node/register bin/backup.ts", 89 | "server": "python -m http.server 8000" 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /packs/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 16745 3 | } -------------------------------------------------------------------------------- /packs/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/packs/assets/cover.png -------------------------------------------------------------------------------- /packs/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/packs/assets/example0.png -------------------------------------------------------------------------------- /packs/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/packs/assets/icon.png -------------------------------------------------------------------------------- /packs/constants.ts: -------------------------------------------------------------------------------- 1 | import { MetadataSettings, addBuildingBlocks, addPublished, addReleases, addFeaturedDocs, addManifest } from "./helpers"; 2 | import { BuildingBlockPoperties, FeaturedDocsProperties, PublishedProperties, ReleasesProperties } from "./schemas"; 3 | 4 | export const PackUrlRegexes = [ 5 | new RegExp("^(https://(?:[^.]+\\.)?coda.io)/p/(\\d+)"), 6 | new RegExp("^(https://(?:[^.]+\\.)?coda.io)/packs/(?:\\w+-)*(\\d+)"), 7 | ]; 8 | 9 | export const MetadataTypes: Record = { 10 | blocks: { 11 | name: "Building blocks", 12 | callback: addBuildingBlocks, 13 | properties: BuildingBlockPoperties, 14 | }, 15 | published: { 16 | name: "Published status", 17 | callback: addPublished, 18 | properties: PublishedProperties, 19 | }, 20 | releases: { 21 | name: "Releases", 22 | callback: addReleases, 23 | properties: ReleasesProperties, 24 | }, 25 | featuredDocs: { 26 | name: "Featured docs", 27 | callback: addFeaturedDocs, 28 | properties: FeaturedDocsProperties, 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /patches/@dicebear+converter+7.0.4.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/@dicebear/converter/package.json b/node_modules/@dicebear/converter/package.json 2 | index 7c707b0..7816e3b 100644 3 | --- a/node_modules/@dicebear/converter/package.json 4 | +++ b/node_modules/@dicebear/converter/package.json 5 | @@ -19,7 +19,6 @@ 6 | "main": "./lib/node/index.js", 7 | "browser": "./lib/index.js", 8 | "exports": { 9 | - "node": "./lib/node/index.js", 10 | "default": "./lib/index.js" 11 | }, 12 | "types": "./lib/index.d.ts", 13 | -------------------------------------------------------------------------------- /patches/@universe+util+1.4.1.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/@universe/util/dist/mjs/src/index.mjs b/node_modules/@universe/util/dist/mjs/src/index.mjs 2 | index 375cb25..ecd01ce 100644 3 | --- a/node_modules/@universe/util/dist/mjs/src/index.mjs 4 | +++ b/node_modules/@universe/util/dist/mjs/src/index.mjs 5 | @@ -1,6 +1,7 @@ 6 | /* eslint-disable max-len */ 7 | /* global MessageChannel */ 8 | import * as nodeCrypto from 'node:crypto'; 9 | +import {TextDecoder} from "text-encoding"; 10 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 11 | // @ts-ignore 12 | const IS_BROWSER = (typeof globalThis.window === 'object' && window.document) || (typeof globalThis.WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope); 13 | -------------------------------------------------------------------------------- /patches/ajv+8.12.0.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/ajv/dist/compile/index.js b/node_modules/ajv/dist/compile/index.js 2 | index b125ecd..74d89cd 100644 3 | --- a/node_modules/ajv/dist/compile/index.js 4 | +++ b/node_modules/ajv/dist/compile/index.js 5 | @@ -86,7 +86,8 @@ function compileSchema(sch) { 6 | if (this.opts.code.process) 7 | sourceCode = this.opts.code.process(sourceCode, sch); 8 | // console.log("\n\n\n *** \n", sourceCode) 9 | - const makeValidate = new Function(`${names_1.default.self}`, `${names_1.default.scope}`, sourceCode); 10 | + const F = () => Function 11 | + const makeValidate = new (F())(`${names_1.default.self}`, `${names_1.default.scope}`, sourceCode); 12 | const validate = makeValidate(this, this.scope.get()); 13 | this.scope.value(validateName, { ref: validate }); 14 | validate.errors = null; 15 | -------------------------------------------------------------------------------- /patches/node-fetch+2.6.11.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/node-fetch/lib/index.js b/node_modules/node-fetch/lib/index.js 2 | index 337d6e5..c0e767c 100644 3 | --- a/node_modules/node-fetch/lib/index.js 4 | +++ b/node_modules/node-fetch/lib/index.js 5 | @@ -160,7 +160,7 @@ FetchError.prototype.name = 'FetchError'; 6 | 7 | let convert; 8 | try { 9 | - convert = require('encoding').convert; 10 | + // convert = require('encoding').convert; 11 | } catch (e) {} 12 | 13 | const INTERNALS = Symbol('Body internals'); 14 | -------------------------------------------------------------------------------- /patches/setimmediate+1.0.5.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/setimmediate/setImmediate.js b/node_modules/setimmediate/setImmediate.js 2 | index 3c1b06e..309b99f 100644 3 | --- a/node_modules/setimmediate/setImmediate.js 4 | +++ b/node_modules/setimmediate/setImmediate.js 5 | @@ -14,7 +14,8 @@ 6 | function setImmediate(callback) { 7 | // Callback can either be a function or a string 8 | if (typeof callback !== "function") { 9 | - callback = new Function("" + callback); 10 | + // callback = new Function("" + callback); 11 | + throw new Error("Not supported."); 12 | } 13 | // Copy function arguments 14 | var args = new Array(arguments.length - 1); 15 | -------------------------------------------------------------------------------- /phone-number/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 12434 3 | } -------------------------------------------------------------------------------- /phone-number/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/phone-number/assets/cover.png -------------------------------------------------------------------------------- /phone-number/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/phone-number/assets/example0.png -------------------------------------------------------------------------------- /phone-number/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/phone-number/assets/icon.png -------------------------------------------------------------------------------- /placeholder/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 15514 3 | } -------------------------------------------------------------------------------- /placeholder/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/placeholder/assets/cover.png -------------------------------------------------------------------------------- /placeholder/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/placeholder/assets/example0.png -------------------------------------------------------------------------------- /placeholder/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/placeholder/assets/icon.png -------------------------------------------------------------------------------- /placeholder/pack_test.ts: -------------------------------------------------------------------------------- 1 | import {executeFormulaFromPackDef} from '@codahq/packs-sdk/dist/development'; 2 | import {pack} from './pack'; 3 | import * as chai from "chai"; 4 | import {assert} from "chai"; 5 | import {describe} from "mocha"; 6 | import {it} from "mocha"; 7 | import chaiAsPromised from "chai-as-promised"; 8 | chai.use(chaiAsPromised); 9 | chai.should(); 10 | 11 | describe("Examples", () => { 12 | for (let formula of pack.formulas) { 13 | describe(formula.name, () => { 14 | for (let [i, example] of formula.examples.entries()) { 15 | it(`Example ${i}`, async () => { 16 | const result = await executeFormulaFromPackDef(pack, formula.name, example.params as any); 17 | if (typeof example.result == "object") { 18 | assert.deepEqual(result, example.result); 19 | } else { 20 | assert.equal(result, example.result); 21 | } 22 | }); 23 | } 24 | }); 25 | } 26 | }); -------------------------------------------------------------------------------- /preview/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 26080 3 | } -------------------------------------------------------------------------------- /preview/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/preview/assets/cover.jpg -------------------------------------------------------------------------------- /preview/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/preview/assets/example0.jpg -------------------------------------------------------------------------------- /preview/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/preview/assets/icon.png -------------------------------------------------------------------------------- /preview/pack.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | import * as rs from "jsrsasign"; 3 | import { PrivateKeyPEM } from "./credentials"; 4 | 5 | const BaseUrl = "https://preview.erickoleda.com/docs/preview/"; 6 | const OneDaySecs = 24 * 60 * 60; 7 | const MaxUrlLength = Math.pow(2, 15); 8 | 9 | export const pack = coda.newPack(); 10 | 11 | pack.addFormula({ 12 | name: "PreviewHTML", 13 | description: "Generate a preview of HTML content.", 14 | parameters: [ 15 | coda.makeParameter({ 16 | type: coda.ParameterType.String, 17 | name: "html", 18 | description: "The HTML to preview.", 19 | }), 20 | ], 21 | resultType: coda.ValueType.String, 22 | schema: { 23 | type: coda.ValueType.String, 24 | codaType: coda.ValueHintType.Embed, 25 | force: true, 26 | }, 27 | cacheTtlSecs: OneDaySecs, 28 | execute: async function (args, context) { 29 | let [html] = args; 30 | // Minify JS. 31 | html = html 32 | .replace(/\s+/g, " ") 33 | .replace(/> <"); 34 | let url = coda.withQueryParams(BaseUrl, { 35 | h: html, 36 | s: sign(html), 37 | }); 38 | if (url.length > MaxUrlLength) { 39 | throw new coda.UserVisibleError("Too much content to preview."); 40 | } 41 | return url; 42 | }, 43 | }); 44 | 45 | pack.addFormula({ 46 | name: "PreviewMarkdown", 47 | description: "Generate a preview of markdown content.", 48 | parameters: [ 49 | coda.makeParameter({ 50 | type: coda.ParameterType.String, 51 | name: "markdown", 52 | description: "The markdown to preview.", 53 | }), 54 | ], 55 | resultType: coda.ValueType.String, 56 | schema: { 57 | type: coda.ValueType.String, 58 | codaType: coda.ValueHintType.Embed, 59 | force: true, 60 | }, 61 | cacheTtlSecs: OneDaySecs, 62 | execute: async function (args, context) { 63 | let [markdown] = args; 64 | let url = coda.withQueryParams(BaseUrl, { 65 | m: markdown, 66 | s: sign(markdown), 67 | }); 68 | if (url.length > MaxUrlLength) { 69 | throw new coda.UserVisibleError("Too much content to preview."); 70 | } 71 | return url; 72 | }, 73 | }); 74 | 75 | function sign(content) { 76 | let sig = new rs.crypto.Signature({alg: "SHA512withECDSA"}); 77 | sig.init(PrivateKeyPEM); 78 | sig.updateString(content); 79 | let hex = sig.sign(); 80 | return Buffer.from(hex, 'hex').toString('base64'); 81 | } 82 | -------------------------------------------------------------------------------- /rebrickable/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 35912 3 | } -------------------------------------------------------------------------------- /rebrickable/api.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | 3 | const OneDaySecs = 24 * 60 * 60; 4 | 5 | export async function getSet(context: coda.ExecutionContext, id: string) { 6 | let url = `https://rebrickable.com/api/v3/lego/sets/${id}`; 7 | let response = await context.fetcher.fetch({ 8 | method: "GET", 9 | url, 10 | cacheTtlSecs: OneDaySecs, 11 | }); 12 | return response.body; 13 | } 14 | 15 | export async function listSets(context: coda.ExecutionContext, themeId?: string, max = 500) { 16 | let url = coda.withQueryParams("https://rebrickable.com/api/v3/lego/sets/", { 17 | page_size: max, 18 | theme_id: themeId, 19 | }); 20 | let response = await context.fetcher.fetch({ 21 | method: "GET", 22 | url, 23 | cacheTtlSecs: OneDaySecs, 24 | }); 25 | let page = response.body; 26 | return page.results; 27 | } 28 | 29 | export async function listPartCategories(context: coda.ExecutionContext, max = 1000) { 30 | let url = coda.withQueryParams("https://rebrickable.com/api/v3/lego/part_categories/", { 31 | page_size: max, 32 | }); 33 | let response = await context.fetcher.fetch({ 34 | method: "GET", 35 | url, 36 | cacheTtlSecs: OneDaySecs, 37 | }); 38 | let page = response.body; 39 | return page.results; 40 | } 41 | 42 | export async function listParts(context: coda.ExecutionContext, categoryId?: string, max = 1000) { 43 | let url = coda.withQueryParams("https://rebrickable.com/api/v3/lego/parts", { 44 | page_size: max, 45 | part_cat_id: categoryId, 46 | }); 47 | let response = await context.fetcher.fetch({ 48 | method: "GET", 49 | url, 50 | cacheTtlSecs: OneDaySecs, 51 | }); 52 | let page = response.body; 53 | return page.results; 54 | } 55 | 56 | export async function listThemes(context: coda.ExecutionContext, max = 1000) { 57 | let url = coda.withQueryParams("https://rebrickable.com/api/v3/lego/themes/", { 58 | page_size: max, 59 | }); 60 | let response = await context.fetcher.fetch({ 61 | method: "GET", 62 | url, 63 | cacheTtlSecs: OneDaySecs, 64 | }); 65 | let page = response.body; 66 | return page.results; 67 | } -------------------------------------------------------------------------------- /rebrickable/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/rebrickable/assets/icon.png -------------------------------------------------------------------------------- /rebrickable/assets/listing.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 35912, 3 | "name": "Rebrickable", 4 | "description": "All data and photos courtesy of Rebrickable (rebrickable.com).", 5 | "shortDescription": "Get information about LEGO® sets and parts.", 6 | "logoUrl": "https://codahosted.io/packs/35912/unversioned/assets/LOGO/d0c15c067ceef8dca840f0e48b9e8622c025d0682cfbd805bdb82e1a783133ead48ba6add1d5a4e1fe73ef1e750f5b0e8e6f42250dea789758a1921159de2409f885e1a7ddd17830bc8209383b5d31a0b2fb52bb766d4da7e0fb6e6246e302ec1819626b", 7 | "exampleImages": [], 8 | "packVersion": "20", 9 | "releaseId": 1, 10 | "lastReleasedAt": "2025-01-10T22:19:06.273Z", 11 | "externalMetadataUrl": "https://codahosted.io/packs/35912/20/metadata/8e77bd904ce5f3149657e5599ec333d5fe1e0c4665160d9356407dc3b841fa053cb265ca6324cf085ad0156ccbad1d1a4dbec57b666ea7898273ca6ff42e33f482001fd0f3f2fbf13e000ff20072c0f2f371583bee3bd8d8b76e688d693def990ce921d4.json", 12 | "categories": [ 13 | { 14 | "categoryId": "bdc08c38-448c-4a8e-875d-b3b79689375b", 15 | "categoryName": "Personal", 16 | "categorySlug": "personal" 17 | } 18 | ], 19 | "supportEmail": "packs@erickoleda.com", 20 | "sourceCodeVisibility": "private", 21 | "makers": [ 22 | { 23 | "name": "Eric Koleda", 24 | "pictureLink": "https://images-codaio.imgix.net/https%3A%2F%2Flh3.googleusercontent.com%2Fa%2FACg8ocJYa8nknu-CdeUwswBMRrDPKyLXdwzRPhMFuTodG063315NF-Xe%3Ds96-c?ixlib=js-3.8.0&auto=format%2Ccompress&fit=crop&w=360&h=360&crop=faces&s=3883cf143fc4db7e36979789fddc045e", 25 | "employer": "Coda", 26 | "jobTitle": "Developer Advocate", 27 | "slug": "eric-koleda" 28 | } 29 | ], 30 | "standardPackPlan": { 31 | "packPlanId": "d6a2650f-0dac-42fb-b56e-354ce357d338", 32 | "packId": 35912, 33 | "createdAt": "2024-11-16T19:52:42.595Z", 34 | "pricing": { 35 | "type": "Free" 36 | } 37 | }, 38 | "sdkVersion": "1.8.5", 39 | "discoverability": "public", 40 | "userAccess": { 41 | "canEdit": true, 42 | "canInstall": false, 43 | "canTest": true, 44 | "canView": true, 45 | "canPurchase": false, 46 | "requiresTrial": false, 47 | "canConnectAccount": true 48 | } 49 | } -------------------------------------------------------------------------------- /rebrickable/helpers.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | 3 | export function getYear(context: coda.ExecutionContext, date: Date) { 4 | let formatter = new Intl.DateTimeFormat("en", { 5 | timeZone: context.timezone, 6 | year: "numeric", 7 | }); 8 | let parts = formatter.formatToParts(date); 9 | return parts.find(part => part.type === "year").value; 10 | } -------------------------------------------------------------------------------- /regex/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 20705 3 | } -------------------------------------------------------------------------------- /regex/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/regex/assets/cover.png -------------------------------------------------------------------------------- /regex/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/regex/assets/example0.png -------------------------------------------------------------------------------- /regex/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/regex/assets/icon.png -------------------------------------------------------------------------------- /regex/pack.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | const RegexEscape = require("regex-escape"); 3 | 4 | export const pack = coda.newPack(); 5 | 6 | pack.addFormula({ 7 | name: "RegexCapture", 8 | description: "Executes a regular expression on text and returns the values of the capture groups.", 9 | parameters: [ 10 | coda.makeParameter({ 11 | type: coda.ParameterType.String, 12 | name: "text", 13 | description: "The text to apply it to.", 14 | }), 15 | coda.makeParameter({ 16 | type: coda.ParameterType.String, 17 | name: "regex", 18 | description: "The regular expression to apply.", 19 | }), 20 | coda.makeParameter({ 21 | type: coda.ParameterType.String, 22 | name: "flags", 23 | description: `Any regular expression flags to use (ex: "g", "i", etc). Using the "g" flag will change the behavior of the formula, returning all of the full matches and ignoring capture groups.`, 24 | optional: true, 25 | }), 26 | ], 27 | resultType: coda.ValueType.Array, 28 | items: { type: coda.ValueType.String }, 29 | examples: [ 30 | {params: ["Hello Packs", "H.*o"], result: ["Hello"]}, 31 | {params: ["Hello Packs", "H(.*)o"], result: ["Hello", "ell"]}, 32 | {params: ["Hello Packs", "H(.*)o P(.*)s"], result: ["Hello Packs", "ell", "ack"]}, 33 | {params: ["Hello Packs", "H.*y"], result: []}, 34 | {params: ["Hello Packs", "h.*o", "i"], result: ["Hello"]}, 35 | {params: ["Hello Packs", "(H|P).", "g"], result: ["He", "Pa"]}, 36 | ], 37 | execute: async function (args, context) { 38 | let [text, regex, flags = ""] = args; 39 | let r = new RegExp(regex, flags); 40 | let match = text.match(r); 41 | if (!match) { 42 | return []; 43 | } 44 | return match; 45 | }, 46 | }); 47 | 48 | pack.addFormula({ 49 | name: "RegexEscape", 50 | description: "Escapes special characters in a string, so it's safe to use as a literal value in a regular expression.", 51 | parameters: [ 52 | coda.makeParameter({ 53 | type: coda.ParameterType.String, 54 | name: "text", 55 | description: "The text to escape.", 56 | }), 57 | ], 58 | resultType: coda.ValueType.String, 59 | examples: [ 60 | {params: ["*Hello* World."], result: "\\*Hello\\* World\\."}, 61 | ], 62 | execute: async function (args, context) { 63 | let [text] = args; 64 | return RegexEscape(text); 65 | }, 66 | }); 67 | -------------------------------------------------------------------------------- /regex/pack_test.ts: -------------------------------------------------------------------------------- 1 | import {executeFormulaFromPackDef} from '@codahq/packs-sdk/dist/development'; 2 | import {pack} from './pack'; 3 | import * as chai from "chai"; 4 | import {assert} from "chai"; 5 | import {describe} from "mocha"; 6 | import {it} from "mocha"; 7 | import chaiAsPromised from "chai-as-promised"; 8 | chai.use(chaiAsPromised); 9 | chai.should(); 10 | 11 | describe("Examples", () => { 12 | for (let formula of pack.formulas) { 13 | describe(formula.name, () => { 14 | for (let [i, example] of formula.examples.entries()) { 15 | it(`Example ${i}`, async () => { 16 | const result = await executeFormulaFromPackDef(pack, formula.name, example.params as any); 17 | if (typeof example.result == "object") { 18 | assert.deepEqual(result, example.result); 19 | } else { 20 | assert.equal(result, example.result); 21 | } 22 | }); 23 | } 24 | }); 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /rich-text/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 18653 3 | } -------------------------------------------------------------------------------- /rich-text/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/rich-text/assets/cover.jpg -------------------------------------------------------------------------------- /rich-text/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/rich-text/assets/example0.jpg -------------------------------------------------------------------------------- /rich-text/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/rich-text/assets/icon.png -------------------------------------------------------------------------------- /rich-text/assets/listing.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 18653, 3 | "name": "Rich Text Tools", 4 | "description": "A set of simple formulas for converting rich text to and from HTML and Markdown.\n\nIcon by OpenMoji – the open-source emoji and icon project. License: CC BY-SA 4.0.\nCover photo by cottonbro studio on Pexels.", 5 | "shortDescription": "Formulas for working with rich text as HTML and Markdown.", 6 | "logoUrl": "https://codahosted.io/packs/18653/unversioned/assets/LOGO/3a6abfc40704be469820a22eca6be237543ae98a0a1af64d38f1281d4448901aedd737474ac3017d1c3a3c404814231818700dc8415eacf25850f1fbe1062d2835659fbcdd345f5bf7eb713d6db9e923d5985227a725ab53fe79cf5501843810f24b3658", 7 | "coverUrl": "https://codahosted.io/packs/18653/unversioned/assets/COVER/b9fce1785a7711baa49d584f30f4057697b0a675ce3510082b72bea07a5952bf9df290889dca98f42b98aa8ed3df8151ade14ff7a0cef4ebb91ae24a8ab9e47f9969f3d7b4c77e21b8998d7fefaae327c276021811e7c034c8b3f0516c7fb3f74e5bc86c", 8 | "exampleImages": [ 9 | { 10 | "imageUrl": "https://codahosted.io/packs/18653/unversioned/assets/EXAMPLE/b9fce1785a7711baa49d584f30f4057697b0a675ce3510082b72bea07a5952bf9df290889dca98f42b98aa8ed3df8151ade14ff7a0cef4ebb91ae24a8ab9e47f9969f3d7b4c77e21b8998d7fefaae327c276021811e7c034c8b3f0516c7fb3f74e5bc86c", 11 | "filename": "Cover photo", 12 | "assetId": "b9fce1785a7711baa49d584f30f4057697b0a675ce3510082b72bea07a5952bf9df290889dca98f42b98aa8ed3df8151ade14ff7a0cef4ebb91ae24a8ab9e47f9969f3d7b4c77e21b8998d7fefaae327c276021811e7c034c8b3f0516c7fb3f74e5bc86c" 13 | } 14 | ], 15 | "packVersion": "3", 16 | "releaseId": 3, 17 | "lastReleasedAt": "2024-03-06T01:41:26.901Z", 18 | "externalMetadataUrl": "https://codahosted.io/packs/18653/3/metadata/625517636d8a527657857eaeaee4d29abc01f1768bdd56203b0e7a524ef8a9af8b254bacd1d2e906fa8c5da652616a0cf20c7241d03ecfdf95b936af23dc3b64d42e98b934dc246e10cb82dc261d63d21353b5c9450bfb7404959223d5c70244a13b2af5.json", 19 | "categories": [ 20 | { 21 | "categoryId": "bdc08c38-448c-4a8e-875d-b3b79689375b", 22 | "categoryName": "Personal", 23 | "categorySlug": "personal" 24 | } 25 | ], 26 | "supportEmail": "packs@erickoleda.com", 27 | "sourceCodeVisibility": "private", 28 | "makers": [ 29 | { 30 | "name": "Eric Koleda", 31 | "pictureLink": "https://images-codaio.imgix.net/https%3A%2F%2Flh3.googleusercontent.com%2Fa%2FACg8ocJYa8nknu-CdeUwswBMRrDPKyLXdwzRPhMFuTodG063315NF-Xe%3Ds96-c?ixlib=js-3.8.0&auto=format%2Ccompress&fit=crop&w=360&h=360&crop=faces&s=3883cf143fc4db7e36979789fddc045e", 32 | "employer": "Coda", 33 | "jobTitle": "Developer Advocate", 34 | "slug": "eric-koleda" 35 | } 36 | ], 37 | "standardPackPlan": { 38 | "packPlanId": "7c92eedb-93b2-4c0e-a385-0cf1122ce462", 39 | "packId": 18653, 40 | "createdAt": "2023-01-07T01:17:57.048Z", 41 | "pricing": { 42 | "type": "Free" 43 | } 44 | }, 45 | "sdkVersion": "1.7.5", 46 | "discoverability": "public", 47 | "userAccess": { 48 | "canEdit": true, 49 | "canInstall": false, 50 | "canTest": true, 51 | "canView": true, 52 | "canPurchase": false, 53 | "requiresTrial": false, 54 | "canConnectAccount": true 55 | } 56 | } -------------------------------------------------------------------------------- /rich-text/pack.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | import * as emojidict from "emoji-dictionary"; 3 | 4 | export const pack = coda.newPack(); 5 | 6 | pack.addFormula({ 7 | name: "ToHTML", 8 | description: "Convert rich text to HTML.", 9 | parameters: [ 10 | coda.makeParameter({ 11 | type: coda.ParameterType.Html, 12 | name: "text", 13 | description: "The rich text to convert.", 14 | }), 15 | ], 16 | resultType: coda.ValueType.String, 17 | execute: async function ([text], context) { 18 | return text; 19 | }, 20 | }); 21 | 22 | pack.addFormula({ 23 | name: "ToMarkdown", 24 | description: "Convert rich text to markdown.", 25 | parameters: [ 26 | coda.makeParameter({ 27 | type: coda.ParameterType.Markdown, 28 | name: "text", 29 | description: "The rich text to convert.", 30 | }), 31 | coda.makeParameter({ 32 | type: coda.ParameterType.Boolean, 33 | name: "preserveBlankLines", 34 | description: "Preserve blank lines in the text. Default: false.", 35 | optional: true, 36 | }), 37 | ], 38 | resultType: coda.ValueType.String, 39 | execute: async function ([text, preserveBlankLines], context) { 40 | let result = text; 41 | if (preserveBlankLines) { 42 | let previous; 43 | do { 44 | previous = result; 45 | result = result.replace(/\n\n/g, "\n \n"); 46 | } while (previous != result) 47 | } 48 | return result; 49 | }, 50 | }); 51 | 52 | pack.addFormula({ 53 | name: "RenderHTML", 54 | description: "Render HTML as rich text.", 55 | parameters: [ 56 | coda.makeParameter({ 57 | type: coda.ParameterType.String, 58 | name: "html", 59 | description: "The HTML markup to render.", 60 | }), 61 | ], 62 | resultType: coda.ValueType.String, 63 | codaType: coda.ValueHintType.Html, 64 | execute: async function ([html], context) { 65 | return html; 66 | }, 67 | }); 68 | 69 | pack.addFormula({ 70 | name: "RenderMarkdown", 71 | description: "Render markdown as rich text.", 72 | parameters: [ 73 | coda.makeParameter({ 74 | type: coda.ParameterType.String, 75 | name: "markdown", 76 | description: "The markdown to render.", 77 | }), 78 | coda.makeParameter({ 79 | type: coda.ParameterType.Boolean, 80 | name: "supportEmojiShortcodes", 81 | description: `Render emoji shortcodes as the equivalent emoji. For example, :heart_eyes: => 😍. Default: false.`, 82 | optional: true, 83 | }), 84 | ], 85 | resultType: coda.ValueType.String, 86 | codaType: coda.ValueHintType.Markdown, 87 | execute: async function ([markdown, supportEmojiShortcodes = false], context) { 88 | if (supportEmojiShortcodes) { 89 | for (let name of emojidict.names) { 90 | markdown = markdown.replaceAll(`:${name}:`, emojidict.getUnicode(name)) 91 | } 92 | } 93 | return markdown; 94 | }, 95 | }); 96 | -------------------------------------------------------------------------------- /rich-text/pack_test.ts: -------------------------------------------------------------------------------- 1 | import {executeFormulaFromPackDef} from '@codahq/packs-sdk/dist/development'; 2 | import {pack} from './pack'; 3 | import * as chai from "chai"; 4 | import {assert} from "chai"; 5 | import {describe} from "mocha"; 6 | import {it} from "mocha"; 7 | import chaiAsPromised from "chai-as-promised"; 8 | chai.use(chaiAsPromised); 9 | chai.should(); 10 | 11 | describe("ToMarkdown", () => { 12 | it("Preserves line breaks", async() => { 13 | const result = await executeFormulaFromPackDef(pack, "ToMarkdown", ["A\n\n\nB", true]); 14 | assert.equal(result, "A\n \n \nB"); 15 | }); 16 | }); 17 | 18 | describe("Examples", () => { 19 | for (let formula of pack.formulas) { 20 | if (formula.examples) { 21 | describe(formula.name, () => { 22 | for (let [i, example] of formula.examples.entries()) { 23 | it(`Example ${i}`, async () => { 24 | const result = await executeFormulaFromPackDef(pack, formula.name, example.params as any); 25 | if (typeof example.result == "object") { 26 | assert.deepEqual(result, example.result); 27 | } else { 28 | assert.equal(result, example.result); 29 | } 30 | }); 31 | } 32 | }); 33 | } 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /safe-browsing/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 23267 3 | } -------------------------------------------------------------------------------- /safe-browsing/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/safe-browsing/assets/cover.jpg -------------------------------------------------------------------------------- /safe-browsing/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/safe-browsing/assets/example0.jpg -------------------------------------------------------------------------------- /safe-browsing/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/safe-browsing/assets/icon.png -------------------------------------------------------------------------------- /safe-browsing/pack_test.ts: -------------------------------------------------------------------------------- 1 | import {executeFormulaFromPackDef} from "@codahq/packs-sdk/dist/development"; 2 | import {pack} from "./pack"; 3 | import * as chai from "chai"; 4 | import {assert} from "chai"; 5 | import {describe} from "mocha"; 6 | import {it} from "mocha"; 7 | import chaiAsPromised from "chai-as-promised"; 8 | chai.use(chaiAsPromised); 9 | chai.should(); 10 | 11 | describe("Examples", () => { 12 | for (let formula of pack.formulas) { 13 | if (formula.cacheTtlSecs == 0 || !formula.examples) continue; 14 | describe(formula.name, () => { 15 | for (let [i, example] of formula.examples.entries()) { 16 | it(`Example ${i}`, async () => { 17 | const result = await executeFormulaFromPackDef(pack, formula.name, example.params as any, undefined, undefined, { 18 | useRealFetcher: true, 19 | manifestPath: require.resolve("./pack"), 20 | }); 21 | if (typeof example.result == "object") { 22 | assert.deepEqual(result, example.result); 23 | } else { 24 | assert.equal(result, example.result); 25 | } 26 | }); 27 | } 28 | }); 29 | } 30 | }); 31 | -------------------------------------------------------------------------------- /seinfeld/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 19321 3 | } -------------------------------------------------------------------------------- /seinfeld/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/seinfeld/assets/cover.jpg -------------------------------------------------------------------------------- /seinfeld/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/seinfeld/assets/example0.jpg -------------------------------------------------------------------------------- /seinfeld/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/seinfeld/assets/icon.png -------------------------------------------------------------------------------- /short-number/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 13400 3 | } -------------------------------------------------------------------------------- /short-number/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/short-number/assets/cover.png -------------------------------------------------------------------------------- /short-number/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/short-number/assets/example0.png -------------------------------------------------------------------------------- /short-number/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/short-number/assets/icon.png -------------------------------------------------------------------------------- /short-number/assets/listing.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 13400, 3 | "name": "Shorten Number", 4 | "description": "The best dashboards use short, easy to read numbers like \"20k new subscribers\". However the best dashboards also use real, live data, which can be overly precise like \"20347 new subscribers\".\n\nUse this Pack to easily convert your precise numbers into short abbreviations.\n\nShortNumber(20347) ==> 20k\n\nCreated using the number-abbreviate NPM module.\nIcon by OpenMoji – the open-source emoji and icon project. License: CC BY-SA 4.0.\nCover photo by Diana Polekhina on Unsplash", 5 | "shortDescription": "Abbreviate numbers using letter suffixes (1.2k, 4m, etc)", 6 | "logoUrl": "https://codahosted.io/packs/13400/unversioned/assets/LOGO/1523eb8f46fa6a0bfdd845eedd2eb7f05ed91d49b5c2bc304c4b811cd101aabfcf571f9ba0818370fe6678e14c1083626f85eea1f5771ad43d22a22c87525340e784a15ec7107bb3dc84573d1182182303e7a46403751a71a95bc795ab869cc784665089", 7 | "coverUrl": "https://codahosted.io/packs/13400/unversioned/assets/COVER/145a04e1ac3197c57349cbd5a8cccb29e4895d992e96de435cbac380374423de58fccd7ad6a04441939f37bf3d7a5ffad115ffc426f65cc62545471ecd684ba8d47a516f5001e06cf0f81f4509be75265df4612275bca341ff5486ce56a25b541c1fdd60", 8 | "exampleImages": [ 9 | { 10 | "imageUrl": "https://codahosted.io/packs/13400/unversioned/assets/EXAMPLE/145a04e1ac3197c57349cbd5a8cccb29e4895d992e96de435cbac380374423de58fccd7ad6a04441939f37bf3d7a5ffad115ffc426f65cc62545471ecd684ba8d47a516f5001e06cf0f81f4509be75265df4612275bca341ff5486ce56a25b541c1fdd60", 11 | "filename": "Cover photo", 12 | "assetId": "145a04e1ac3197c57349cbd5a8cccb29e4895d992e96de435cbac380374423de58fccd7ad6a04441939f37bf3d7a5ffad115ffc426f65cc62545471ecd684ba8d47a516f5001e06cf0f81f4509be75265df4612275bca341ff5486ce56a25b541c1fdd60" 13 | } 14 | ], 15 | "packVersion": "3", 16 | "releaseId": 2, 17 | "lastReleasedAt": "2022-07-19T19:12:20.766Z", 18 | "externalMetadataUrl": "https://codahosted.io/packs/13400/3/metadata/60efc6dd4423d8a5b41507fc53a962e5b11c715cc666759725e175167d0b04ba0b5f788d9a4411eb33dcd4610e8bb27a26928abbf17fedc933eade5200ae7db951c4e3468765a22e20f655ebd5b40b957d1886ce72a6a5f0937748d4e4212c1b4f303b37.json", 19 | "categories": [], 20 | "supportEmail": "packs@erickoleda.com", 21 | "sourceCodeVisibility": "private", 22 | "makers": [ 23 | { 24 | "name": "Eric Koleda", 25 | "pictureLink": "https://images-codaio.imgix.net/https%3A%2F%2Flh3.googleusercontent.com%2Fa%2FACg8ocJYa8nknu-CdeUwswBMRrDPKyLXdwzRPhMFuTodG063315NF-Xe%3Ds96-c?ixlib=js-3.8.0&auto=format%2Ccompress&fit=crop&w=360&h=360&crop=faces&s=3883cf143fc4db7e36979789fddc045e", 26 | "employer": "Coda", 27 | "jobTitle": "Developer Advocate", 28 | "slug": "eric-koleda" 29 | } 30 | ], 31 | "standardPackPlan": { 32 | "packPlanId": "047eef83-d14b-47e5-8e25-8fe9031c7582", 33 | "packId": 13400, 34 | "createdAt": "2022-07-19T18:59:37.470Z", 35 | "pricing": { 36 | "type": "Free" 37 | } 38 | }, 39 | "sdkVersion": "1.0.1", 40 | "discoverability": "public", 41 | "userAccess": { 42 | "canEdit": true, 43 | "canInstall": false, 44 | "canTest": true, 45 | "canView": true, 46 | "canPurchase": false, 47 | "requiresTrial": false, 48 | "canConnectAccount": true 49 | } 50 | } -------------------------------------------------------------------------------- /short-number/pack.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | import abbreviate from 'number-abbreviate'; 3 | 4 | const DefaultDecimalPlaces = 1; 5 | 6 | export const pack = coda.newPack(); 7 | 8 | pack.addFormula({ 9 | name: "ShortNumber", 10 | description: "Abbreviate a number.", 11 | parameters: [ 12 | coda.makeParameter({ 13 | type: coda.ParameterType.Number, 14 | name: "number", 15 | description: "The number to shorten.", 16 | }), 17 | coda.makeParameter({ 18 | type: coda.ParameterType.Number, 19 | name: "decimalPlaces", 20 | description: `How many decimal places to include. Default: ${DefaultDecimalPlaces}`, 21 | optional: true, 22 | }), 23 | ], 24 | resultType: coda.ValueType.String, 25 | examples: [ 26 | {params: [1234], result: "1.2k"}, 27 | {params: [1234, 0], result: "1k"}, 28 | {params: [1234, 2], result: "1.23k"}, 29 | ], 30 | execute: async function ([number, decimalPlaces = DefaultDecimalPlaces], context) { 31 | return abbreviate(number, decimalPlaces); 32 | } 33 | }); 34 | 35 | pack.addColumnFormat({ 36 | name: "Short Number", 37 | formulaName: "ShortNumber", 38 | instructions: "Enter any number, and if it's over one thousand it will be abbreviated.", 39 | }) -------------------------------------------------------------------------------- /smartsheet/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 28295 3 | } -------------------------------------------------------------------------------- /smartsheet/api.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | import { Row, SheetResult, Sheet, SheetFormatSettings } from "./types"; 3 | import { PageSize } from "./pack"; 4 | 5 | export async function searchSheets(context: coda.ExecutionContext, query: string): Promise { 6 | if (query) { 7 | let url = coda.withQueryParams("https://api.smartsheet.com/2.0/search", { 8 | query, 9 | scopes: "sheetNames", 10 | }) 11 | let response = await context.fetcher.fetch({ 12 | method: "GET", 13 | url: url, 14 | }); 15 | let results = response.body.results ?? []; 16 | return results 17 | .filter(item => item.objectType == "sheet") 18 | .map(item => { 19 | let name = item.text; 20 | let parent = item.contextData?.[0]; 21 | let id = item.objectId; 22 | let url = `https://api.smartsheet.com/2.0/sheets/${id}`; 23 | return {name, parent, id, url}; 24 | }); 25 | } else { 26 | let url = coda.withQueryParams("https://api.smartsheet.com/2.0/sheets", { 27 | pageSize: 100, 28 | }); 29 | let response = await context.fetcher.fetch({ 30 | method: "GET", 31 | url: url, 32 | }); 33 | let sheets = response.body.data ?? []; 34 | return sheets 35 | .map(sheet => { 36 | return { 37 | ...sheet, 38 | url: `https://api.smartsheet.com/2.0/sheets/${sheets.id}` 39 | }; 40 | }); 41 | } 42 | } 43 | 44 | export async function getSheet(context: coda.ExecutionContext, sheetUrl: string): Promise { 45 | let url = coda.withQueryParams(sheetUrl, { 46 | include: 'filterDefinitions', 47 | pageSize: 0, 48 | }); 49 | let response = await context.fetcher.fetch({ 50 | method: "GET", 51 | url: url, 52 | }); 53 | return response.body; 54 | } 55 | 56 | export async function syncSheet(context: coda.ExecutionContext, sheetUrl: string, settings: SheetFormatSettings): Promise { 57 | let url = coda.withQueryParams(sheetUrl, { 58 | page: String(settings.page), 59 | pageSize: String(PageSize), 60 | filterId: settings.filterId, 61 | exclude: 'filteredOutRows', 62 | columnIds: settings.columnIds, 63 | include: "rowPermalink", 64 | }); 65 | let response = await context.fetcher.fetch({ 66 | method: "GET", 67 | url: url, 68 | }); 69 | return response.body as Sheet; 70 | } 71 | 72 | export async function updateRows(context: coda.ExecutionContext, sheetUrl: string, rows: Row[]) { 73 | let response = await context.fetcher.fetch({ 74 | method: "PUT", 75 | url: coda.withQueryParams(coda.joinUrl(sheetUrl, "/rows"), { 76 | allowPartialSuccess: true, 77 | }), 78 | headers: { 79 | "Content-Type": "application/json", 80 | }, 81 | body: JSON.stringify(rows), 82 | }); 83 | return response.body; 84 | } 85 | 86 | export async function getRows(context: coda.ExecutionContext, sheetUrl: string, rowIds: number[], cacheTtlSecs?: number) { 87 | let url = coda.withQueryParams(sheetUrl, { 88 | rowIds: rowIds.join(","), 89 | }); 90 | let response = await context.fetcher.fetch({ 91 | method: "GET", 92 | url: url, 93 | cacheTtlSecs, 94 | }); 95 | return response.body.rows; 96 | } 97 | -------------------------------------------------------------------------------- /smartsheet/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/smartsheet/assets/example0.png -------------------------------------------------------------------------------- /smartsheet/assets/icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/smartsheet/assets/icon.jpg -------------------------------------------------------------------------------- /smartsheet/types.ts: -------------------------------------------------------------------------------- 1 | export interface Cell { 2 | columnId: number; 3 | value: T; 4 | displayValue?: string; 5 | strict?: boolean; 6 | } 7 | 8 | export interface Row { 9 | id: number; 10 | rowNumber?: number; 11 | permalink?: string; 12 | cells: Cell[]; 13 | } 14 | 15 | export interface Column { 16 | id: number; 17 | title: string; 18 | type: "ABSTRACT_DATETIME" | "CHECKBOX" | "CONTACT_LIST" | "DATE" | "DATETIME" | "DURATION" | "MULTI_CONTACT_LIST" | "MULTI_PICKLIST" |"PICKLIST" | "PREDECESSOR" | "TEXT_NUMBER"; 19 | systemColumnType: string; 20 | hidden: boolean; 21 | description?: string; 22 | options?: string[]; 23 | primary: boolean; 24 | } 25 | 26 | interface Filter { 27 | name: string; 28 | id: number; 29 | } 30 | 31 | export interface Sheet { 32 | name: string; 33 | permalink: string; 34 | columns: Column[]; 35 | filters: Filter[]; 36 | rows: Row[]; 37 | totalRowCount: number; 38 | filteredRowCount: number; 39 | } 40 | 41 | export interface CodaRow extends Record { 42 | id: string; 43 | rowNumber: number; 44 | } 45 | 46 | export interface CodaPerson { 47 | name: string; 48 | email: string; 49 | } 50 | 51 | export interface SheetFormatSettings { 52 | filterId?: number; 53 | columnIds?: number[]; 54 | useColumnTypes?: boolean; 55 | page?: number; 56 | } 57 | 58 | export interface SheetResult { 59 | name: string; 60 | parent?: string; 61 | id: string; 62 | url: string; 63 | } -------------------------------------------------------------------------------- /smartsuite/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 30242 3 | } -------------------------------------------------------------------------------- /smartsuite/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/smartsuite/assets/example0.png -------------------------------------------------------------------------------- /smartsuite/assets/icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/smartsuite/assets/icon.jpg -------------------------------------------------------------------------------- /smartsuite/schemas.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | import type * as sst from "./types/smartsuite"; 3 | 4 | const PersonSchema = coda.makeObjectSchema({ 5 | codaType: coda.ValueHintType.Person, 6 | properties: { 7 | name: { type: coda.ValueType.String, required: true }, 8 | email: { type: coda.ValueType.String, required: true }, 9 | }, 10 | displayProperty: "name", 11 | idProperty: "email", 12 | }); 13 | 14 | export const MemberSchema = coda.makeObjectSchema({ 15 | properties: { 16 | name: { type: coda.ValueType.String, required: true }, 17 | id: { type: coda.ValueType.String, required: true }, 18 | email: { type: coda.ValueType.String, codaType: coda.ValueHintType.Email }, 19 | codaAccount: { ...PersonSchema }, 20 | timezone: { type: coda.ValueType.String }, 21 | }, 22 | displayProperty: "name", 23 | idProperty: "id", 24 | featuredProperties: ["email", "codaAccount"], 25 | }); 26 | 27 | export function getBaseRowSchema(table: sst.Table): coda.GenericObjectSchema { 28 | let primaryColumn = table.structure.find(column => column.slug == table.primary_field); 29 | return coda.makeObjectSchema({ 30 | properties: { 31 | recordId: { 32 | type: coda.ValueType.String, 33 | description: "The unique ID of the record.", 34 | fromKey: "id", 35 | required: true, 36 | }, 37 | [primaryColumn.label]: { 38 | type: coda.ValueType.String, 39 | fromKey: primaryColumn.slug, 40 | required: true, 41 | }, 42 | }, 43 | idProperty: "recordId", 44 | displayProperty: primaryColumn.label, 45 | featuredProperties: [], 46 | identity: { 47 | name: "Record", 48 | dynamicUrl: table.id, 49 | } 50 | }); 51 | } 52 | 53 | export function getReferenceSchema(table: sst.Table) { 54 | let schema = getBaseRowSchema(table); 55 | return coda.makeReferenceSchemaFromObjectSchema(schema); 56 | } -------------------------------------------------------------------------------- /smartsuite/types.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | import type * as sst from "./types/smartsuite"; 3 | 4 | export interface ConversionSettings { 5 | table: sst.Table; 6 | membersTable: sst.Table; 7 | relatedTables: Record; 8 | context: coda.ExecutionContext; 9 | } -------------------------------------------------------------------------------- /smartsuite/types/coda.ts: -------------------------------------------------------------------------------- 1 | export interface Row extends Record { 2 | id: string; 3 | } 4 | 5 | export interface Person { 6 | name: string; 7 | email: string; 8 | } 9 | 10 | export interface MemberReference { 11 | name: string; 12 | id: string; 13 | } 14 | 15 | export interface Option { 16 | label: string; 17 | value: string; 18 | } 19 | 20 | export interface DatePersonField { 21 | by: MemberReference, 22 | on: string, 23 | } 24 | 25 | export interface Vote { 26 | total_votes: number; 27 | votes: MemberReference[]; 28 | } 29 | 30 | export interface Checklist { 31 | summary: string; 32 | items: Array<{ 33 | preview: string; 34 | label: string; 35 | completed: boolean; 36 | assignee?: MemberReference; 37 | due_date?: string; 38 | completed_at?: string; 39 | }> 40 | } 41 | 42 | export interface TimeTrackingLog { 43 | total: string; 44 | entries: Array<{ 45 | user: MemberReference; 46 | created: string; 47 | duration: string; 48 | note?: string; 49 | }>; 50 | } -------------------------------------------------------------------------------- /socrata/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 21950 3 | } -------------------------------------------------------------------------------- /socrata/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/socrata/assets/cover.jpg -------------------------------------------------------------------------------- /socrata/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/socrata/assets/example0.jpg -------------------------------------------------------------------------------- /socrata/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/socrata/assets/icon.png -------------------------------------------------------------------------------- /spintax/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 23437 3 | } -------------------------------------------------------------------------------- /spintax/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/spintax/assets/cover.jpg -------------------------------------------------------------------------------- /spintax/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/spintax/assets/example0.jpg -------------------------------------------------------------------------------- /spintax/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/spintax/assets/icon.png -------------------------------------------------------------------------------- /spintax/pack_test.ts: -------------------------------------------------------------------------------- 1 | import {executeFormulaFromPackDef} from '@codahq/packs-sdk/dist/development'; 2 | import {pack} from './pack'; 3 | import * as chai from "chai"; 4 | import {assert} from "chai"; 5 | import {describe} from "mocha"; 6 | import {it} from "mocha"; 7 | import chaiAsPromised from "chai-as-promised"; 8 | chai.use(chaiAsPromised); 9 | chai.should(); 10 | 11 | describe("Examples", () => { 12 | for (let formula of pack.formulas) { 13 | if (formula.cacheTtlSecs == 0) continue; 14 | describe(formula.name, () => { 15 | for (let [i, example] of formula.examples.entries()) { 16 | it(`Example ${i}`, async () => { 17 | const result = await executeFormulaFromPackDef(pack, formula.name, example.params as any); 18 | if (typeof example.result == "object") { 19 | assert.deepEqual(result, example.result); 20 | } else { 21 | assert.equal(result, example.result); 22 | } 23 | }); 24 | } 25 | }); 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /sql/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 16509 3 | } -------------------------------------------------------------------------------- /sql/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/sql/assets/cover.jpg -------------------------------------------------------------------------------- /sql/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/sql/assets/example0.jpg -------------------------------------------------------------------------------- /sql/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/sql/assets/icon.png -------------------------------------------------------------------------------- /sql/constants.ts: -------------------------------------------------------------------------------- 1 | export const MaxRowCount = 10000; 2 | export const RowPageSize = 500; 3 | export const ShortCacheTimeSecs = 60; 4 | export const LongCacheTimeSecs = 24 * 60 * 60; 5 | export const DefaultUseRowIds = false; 6 | -------------------------------------------------------------------------------- /sql/test/helpers_test.ts: -------------------------------------------------------------------------------- 1 | import {executeFormulaFromPackDef} from '@codahq/packs-sdk/dist/development'; 2 | import * as chai from "chai"; 3 | import {assert} from "chai"; 4 | import {describe} from "mocha"; 5 | import {it} from "mocha"; 6 | import chaiAsPromised from "chai-as-promised"; 7 | import { parseSpec } from '../helpers'; 8 | chai.use(chaiAsPromised); 9 | chai.should(); 10 | 11 | describe('parseLoad', () => { 12 | it(`Table name`, async () => { 13 | const result = parseSpec("Foo"); 14 | assert.deepEqual(result, {table: "Foo", doc: undefined, destination: undefined}); 15 | }); 16 | it(`Spaces in table name`, async () => { 17 | const result = parseSpec("Foo Foo"); 18 | assert.deepEqual(result, {table: "Foo Foo", doc: undefined, destination: undefined}); 19 | }); 20 | it(`With doc ID`, async () => { 21 | const result = parseSpec("Foo@1234"); 22 | assert.deepEqual(result, {table: "Foo", doc: "1234", destination: undefined}); 23 | }); 24 | it(`With destination`, async () => { 25 | const result = parseSpec("Foo=>Bar"); 26 | assert.deepEqual(result, {table: "Foo", doc: undefined, destination: "Bar"}); 27 | }); 28 | it(`With doc ID and destination`, async () => { 29 | const result = parseSpec("Foo@1234=>Bar"); 30 | assert.deepEqual(result, {table: "Foo", doc: "1234", destination: "Bar"}); 31 | }); 32 | it(`With whitespace`, async () => { 33 | const result = parseSpec(" Foo @ 1234 => Bar "); 34 | assert.deepEqual(result, {table: "Foo", doc: "1234", destination: "Bar"}); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /sql/types.ts: -------------------------------------------------------------------------------- 1 | export interface QueryOptions { 2 | load: string[]; 3 | query: string; 4 | values?: string[]; 5 | useRowIds: boolean; 6 | asObject: boolean; 7 | } 8 | 9 | export interface LoadTableSpec { 10 | doc?: string; 11 | table: string; 12 | destination?: string; 13 | columns?: any[], 14 | } 15 | -------------------------------------------------------------------------------- /streaks/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 19066 3 | } -------------------------------------------------------------------------------- /streaks/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/streaks/assets/cover.jpg -------------------------------------------------------------------------------- /streaks/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/streaks/assets/example0.jpg -------------------------------------------------------------------------------- /streaks/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/streaks/assets/icon.png -------------------------------------------------------------------------------- /streaks/pack.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | import { Settings, DateTime } from "luxon"; 3 | 4 | export const pack = coda.newPack(); 5 | 6 | const DefaultAllowGaps = false; 7 | 8 | pack.addFormula({ 9 | name: "Streaks", 10 | description: "Calculate the streak for each row in a table.", 11 | parameters: [ 12 | coda.makeParameter({ 13 | type: coda.ParameterType.SparseDateArray, 14 | name: "dates", 15 | description: "The dates you have recorded.", 16 | }), 17 | coda.makeParameter({ 18 | type: coda.ParameterType.SparseBooleanArray, 19 | name: "goals", 20 | description: "If the goal was met for each of the dates.", 21 | }), 22 | coda.makeParameter({ 23 | type: coda.ParameterType.Boolean, 24 | name: "allowGaps", 25 | description: `Whether gaps in the record should be allowed, AKA not break the streak. Default: ${DefaultAllowGaps}`, 26 | optional: true, 27 | }), 28 | ], 29 | resultType: coda.ValueType.Array, 30 | items: { 31 | type: coda.ValueType.Number, 32 | }, 33 | execute: async function (args, context) { 34 | Settings.defaultZone = context.timezone ?? "America/Los_Angeles"; 35 | let [jsDates, goals, allowGaps = DefaultAllowGaps] = args; 36 | if (jsDates.length != goals.length) { 37 | throw new coda.UserVisibleError("You must pass in the same number of dates and goals."); 38 | } 39 | for (let [i, date] of jsDates.entries()) { 40 | if (!date) { 41 | delete jsDates[i]; 42 | delete goals[i]; 43 | } 44 | } 45 | 46 | let entries: Entry[] = jsDates.map((date, i) => { 47 | return { 48 | date: DateTime.fromJSDate(date).startOf("day"), 49 | success: goals[i], 50 | } 51 | }); 52 | let sorted = [...entries].sort((a, b) => a.date.toMillis() - b.date.toMillis()); 53 | let min = sorted[0].date; 54 | let max = sorted[sorted.length - 1].date; 55 | 56 | let map: Record = entries.reduce((result, entry) => { 57 | let key = getKey(entry.date); 58 | if (result[key]) { 59 | throw new coda.UserVisibleError(`Duplicate entries found for the date ${key}. Ensure each date is only included once.`); 60 | } 61 | result[key] = entry; 62 | return result; 63 | }, {}); 64 | 65 | let streak = 0; 66 | let current = min; 67 | while (current <= max) { 68 | let key = getKey(current); 69 | let entry = map[key]; 70 | if (entry) { 71 | streak = entry.success ? streak + 1 : 0; 72 | entry.streak = streak; 73 | } else if (!allowGaps) { 74 | streak = 0; 75 | } 76 | current = current.plus({days: 1}); 77 | } 78 | return entries.map(entry => entry.streak); 79 | }, 80 | }); 81 | 82 | function getKey(date: DateTime) { 83 | return date.toLocaleString(DateTime.DATE_FULL); 84 | } 85 | 86 | interface Entry { 87 | date: DateTime; 88 | success: boolean; 89 | streak?: number; 90 | } 91 | -------------------------------------------------------------------------------- /taylor-swift/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 24705 3 | } -------------------------------------------------------------------------------- /taylor-swift/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/taylor-swift/assets/cover.jpg -------------------------------------------------------------------------------- /taylor-swift/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/taylor-swift/assets/example0.jpg -------------------------------------------------------------------------------- /taylor-swift/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/taylor-swift/assets/icon.png -------------------------------------------------------------------------------- /temp-file/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 12946, 3 | "options": { 4 | "timerStrategy": "fake" 5 | } 6 | } -------------------------------------------------------------------------------- /temp-file/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/temp-file/assets/cover.png -------------------------------------------------------------------------------- /temp-file/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/temp-file/assets/example0.png -------------------------------------------------------------------------------- /temp-file/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/temp-file/assets/icon.png -------------------------------------------------------------------------------- /tmdb/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 27791 3 | } -------------------------------------------------------------------------------- /tmdb/api.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | import * as constants from "./constants"; 3 | 4 | export class Api { 5 | context: coda.ExecutionContext; 6 | 7 | constructor(context: coda.ExecutionContext) { 8 | this.context = context; 9 | } 10 | 11 | public async searchMovies(query: string) { 12 | let url = coda.withQueryParams("https://api.themoviedb.org/3/search/movie", { 13 | query, 14 | include_adult: false, 15 | language: "en-US", 16 | }); 17 | let response = await this.context.fetcher.fetch({ 18 | method: "GET", 19 | url: url, 20 | cacheTtlSecs: constants.OneDaySecs, 21 | }); 22 | let data = response.body; 23 | return data; 24 | } 25 | 26 | public async searchShows(query: string) { 27 | let url = coda.withQueryParams("https://api.themoviedb.org/3/search/tv", { 28 | query, 29 | include_adult: false, 30 | language: "en-US", 31 | }); 32 | let response = await this.context.fetcher.fetch({ 33 | method: "GET", 34 | url: url, 35 | cacheTtlSecs: constants.OneDaySecs, 36 | }); 37 | let data = response.body; 38 | return data; 39 | } 40 | 41 | public async getMovie(id: string) { 42 | let url = coda.withQueryParams(`https://api.themoviedb.org/3/movie/${id}`, { 43 | append_to_response: "release_dates", 44 | }); 45 | let response = await this.context.fetcher.fetch({ 46 | method: "GET", 47 | url: url, 48 | cacheTtlSecs: constants.OneDaySecs, 49 | }); 50 | let movie = response.body; 51 | return movie; 52 | } 53 | 54 | public async getShow(id: string) { 55 | let url = coda.withQueryParams(`https://api.themoviedb.org/3/tv/${id}`, { 56 | append_to_response: "content_ratings", 57 | }); 58 | let response = await this.context.fetcher.fetch({ 59 | method: "GET", 60 | url: url, 61 | cacheTtlSecs: constants.OneDaySecs, 62 | }); 63 | let movie = response.body; 64 | return movie; 65 | } 66 | 67 | public async getConfiguration() { 68 | let response = await this.context.fetcher.fetch({ 69 | method: "GET", 70 | url: "https://api.themoviedb.org/3/configuration", 71 | cacheTtlSecs: constants.OneDaySecs, 72 | }); 73 | let configuration = response.body; 74 | return configuration; 75 | } 76 | 77 | public async getCountries() { 78 | let response = await this.context.fetcher.fetch({ 79 | method: "GET", 80 | url: "https://api.themoviedb.org/3/configuration/countries", 81 | cacheTtlSecs: constants.OneDaySecs, 82 | }); 83 | let countries = response.body; 84 | return countries; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tmdb/assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/tmdb/assets/cover.png -------------------------------------------------------------------------------- /tmdb/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/tmdb/assets/example0.png -------------------------------------------------------------------------------- /tmdb/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/tmdb/assets/icon.png -------------------------------------------------------------------------------- /tmdb/assets/listing.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 27791, 3 | "name": "The Movie Database", 4 | "description": "The Movie Database (TMDB) is a community built movie and TV database. Using this Pack you can quickly load key information about any movie or TV show in the collection simply by entering it's name.\n\nLogo and cover photo are the property of The Movie Database.", 5 | "shortDescription": "Look up the details of your favorite movies and television shows.", 6 | "logoUrl": "https://codahosted.io/packs/27791/unversioned/assets/LOGO/02a07668f1246fabe052c9c66a28c118432dc4862e79549c7fb86c566304af3a6cfeb37656ee2011ecdd085ddd57ecd4ca5fe5516b20cb404aa4125b80a0de6b20bb2777927b4fb386dfa806be39ff552441c6bfeb937f7f9680c4e6fb1bfc5421a2b9da", 7 | "coverUrl": "https://codahosted.io/packs/27791/unversioned/assets/COVER/8640db39c2f512888516711580e552e84885c743508cb2b71bea271583918c4e38c424b20238107f1faf3873b4314e924b4dad033e0c64c5ca9862be67fb7412246a320743b058f78a0c570fd50f522c009c172c4c26180537fbb3661bbb13d96bc8707b", 8 | "exampleImages": [ 9 | { 10 | "imageUrl": "https://codahosted.io/packs/27791/unversioned/assets/EXAMPLE/8640db39c2f512888516711580e552e84885c743508cb2b71bea271583918c4e38c424b20238107f1faf3873b4314e924b4dad033e0c64c5ca9862be67fb7412246a320743b058f78a0c570fd50f522c009c172c4c26180537fbb3661bbb13d96bc8707b", 11 | "filename": "Cover photo", 12 | "assetId": "8640db39c2f512888516711580e552e84885c743508cb2b71bea271583918c4e38c424b20238107f1faf3873b4314e924b4dad033e0c64c5ca9862be67fb7412246a320743b058f78a0c570fd50f522c009c172c4c26180537fbb3661bbb13d96bc8707b" 13 | } 14 | ], 15 | "packVersion": "20", 16 | "releaseId": 2, 17 | "lastReleasedAt": "2024-01-17T14:22:34.278Z", 18 | "externalMetadataUrl": "https://codahosted.io/packs/27791/20/metadata/03a437b2de562e5a49c5da8707a282d185c6bf93e4282f3c05fcb42b5a84b3fde4b460f728d1a420062aaf9217524ac7d4e653de15a87782b4ccc472485c227a5102e5155e8a405a25ab641639b9e2df52297c155f9e6bdfabba628957ef90456a92c11f.json", 19 | "categories": [ 20 | { 21 | "categoryId": "5e87a243-6d99-4197-9d54-fba2c990dc7a", 22 | "categoryName": "Entertainment", 23 | "categorySlug": "entertainment" 24 | } 25 | ], 26 | "supportEmail": "packs@erickoleda.com", 27 | "sourceCodeVisibility": "private", 28 | "makers": [ 29 | { 30 | "name": "Eric Koleda", 31 | "pictureLink": "https://images-codaio.imgix.net/https%3A%2F%2Flh3.googleusercontent.com%2Fa%2FACg8ocJYa8nknu-CdeUwswBMRrDPKyLXdwzRPhMFuTodG063315NF-Xe%3Ds96-c?ixlib=js-3.8.0&auto=format%2Ccompress&fit=crop&w=360&h=360&crop=faces&s=3883cf143fc4db7e36979789fddc045e", 32 | "employer": "Coda", 33 | "jobTitle": "Developer Advocate", 34 | "slug": "eric-koleda" 35 | } 36 | ], 37 | "standardPackPlan": { 38 | "packPlanId": "6c224c94-f27b-42b6-ab96-017e70871ef0", 39 | "packId": 27791, 40 | "createdAt": "2024-01-09T18:50:39.696Z", 41 | "pricing": { 42 | "type": "Free" 43 | } 44 | }, 45 | "sdkVersion": "1.7.4", 46 | "discoverability": "public", 47 | "userAccess": { 48 | "canEdit": true, 49 | "canInstall": false, 50 | "canTest": true, 51 | "canView": true, 52 | "canPurchase": false, 53 | "requiresTrial": false, 54 | "canConnectAccount": true 55 | } 56 | } -------------------------------------------------------------------------------- /tmdb/constants.ts: -------------------------------------------------------------------------------- 1 | export const OneDaySecs = 25 * 60 * 60; 2 | export const DefaultCountryCode = "US"; 3 | export const MovieUrlRegex = new RegExp("^https://www.themoviedb.org/movie/(\\d+)"); 4 | export const ShowUrlRegex = new RegExp("^https://www.themoviedb.org/tv/(\\d+)"); 5 | -------------------------------------------------------------------------------- /tmdb/helpers.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | 3 | export function formatMovieForSchema(movie, country: string, configuration) { 4 | formatCommonForSchema(movie, configuration); 5 | movie.link = `https://www.themoviedb.org/movie/${movie.id}`; 6 | if (movie.runtime) { 7 | movie.runtime = movie.runtime + " mins"; 8 | } 9 | movie.rating = movie.release_dates?.results 10 | ?.find(releasesByCountry => releasesByCountry.iso_3166_1 == country) 11 | ?.release_dates 12 | .sort((a, b) => a.release_date.localeCompare(b.release_date)) 13 | [0].certification; 14 | return movie; 15 | } 16 | 17 | export function formatShowForSchema(show, country: string, configuration) { 18 | formatCommonForSchema(show, configuration); 19 | show.link = `https://www.themoviedb.org/tv/${show.id}`; 20 | show.rating = show.content_ratings?.results 21 | ?.find(ratingByCountry => ratingByCountry.iso_3166_1 == country) 22 | ?.rating; 23 | return show; 24 | } 25 | 26 | function formatCommonForSchema(common, configuration) { 27 | // TODO: HTML escape or use template. 28 | common.details = [ 29 | common.tagline ? `${common.tagline}` : '', 30 | common.overview, 31 | ].join(" "); 32 | common.genres = common.genres?.map(genre => genre.name); 33 | 34 | // URLs 35 | common.homepage = common.homepage?.replace("http://", "https://"); 36 | 37 | // Images 38 | let baseUrl = configuration.images.secure_base_url; 39 | if (common.poster_path) { 40 | common.poster_path = coda.joinUrl(baseUrl, getLargestStandardSize(configuration.images.poster_sizes), common.poster_path); 41 | } 42 | if (common.backdrop_path) { 43 | common.backdrop_path = coda.joinUrl(baseUrl, getLargestStandardSize(configuration.images.backdrop_sizes), common.backdrop_path); 44 | } 45 | return common; 46 | } 47 | 48 | function getLargestStandardSize(sizes: string[]): string { 49 | return sizes.filter(s => s != "original").pop(); 50 | } 51 | -------------------------------------------------------------------------------- /tokenize/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 33888 3 | } -------------------------------------------------------------------------------- /tokenize/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/tokenize/assets/example0.jpg -------------------------------------------------------------------------------- /tokenize/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/tokenize/assets/icon.png -------------------------------------------------------------------------------- /tokenize/pack_test.ts: -------------------------------------------------------------------------------- 1 | import {executeFormulaFromPackDef} from '@codahq/packs-sdk/dist/development'; 2 | import {pack} from './pack'; 3 | import * as chai from "chai"; 4 | import {assert} from "chai"; 5 | import {describe} from "mocha"; 6 | import {it} from "mocha"; 7 | import chaiAsPromised from "chai-as-promised"; 8 | chai.use(chaiAsPromised); 9 | chai.should(); 10 | 11 | describe("Examples", () => { 12 | for (let formula of pack.formulas) { 13 | if (formula.cacheTtlSecs == 0) continue; 14 | describe(formula.name, () => { 15 | for (let [i, example] of formula.examples.entries()) { 16 | it(`Example ${i}`, async () => { 17 | const result = await executeFormulaFromPackDef(pack, formula.name, example.params as any); 18 | if (typeof example.result == "object") { 19 | assert.deepEqual(result, example.result); 20 | } else { 21 | assert.equal(result, example.result); 22 | } 23 | }); 24 | } 25 | }); 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es2022"], 4 | "target": "ES2022", 5 | "moduleResolution": "node", 6 | "allowSyntheticDefaultImports": true, 7 | "esModuleInterop": true, 8 | "resolveJsonModule": true, 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /usps/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 37312 3 | } -------------------------------------------------------------------------------- /usps/assets/listing.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 37312, 3 | "name": "USPS", 4 | "description": "", 5 | "shortDescription": "", 6 | "logoUrl": "https://cdn.coda.io/assets/31678415ed10/img/packs/default-pack-icon.png", 7 | "exampleImages": [], 8 | "packVersion": "10", 9 | "externalMetadataUrl": "https://codahosted.io/packs/37312/10/metadata/e75829c9e07f0a0e92101e8cee3df784a713530c7cfed3e0ff4f0269feca91f91fd430ca65f71e4aaa3a11df8dc702e5cd8f12ae5820df321448d3c1c1c5014f3601f9b372613819d6be43c390f7e06c6313784a4bf4fd678f4eb81c474932662dda6f55.json", 10 | "categories": [], 11 | "supportEmail": "koleda.eric@gmail.com", 12 | "sourceCodeVisibility": "private", 13 | "makers": [ 14 | { 15 | "name": "Eric Koleda", 16 | "pictureLink": "https://images-codaio.imgix.net/https%3A%2F%2Flh3.googleusercontent.com%2Fa%2FACg8ocJYa8nknu-CdeUwswBMRrDPKyLXdwzRPhMFuTodG063315NF-Xe%3Ds96-c?ixlib=js-3.8.0&auto=format%2Ccompress&fit=crop&w=360&h=360&crop=faces&s=3883cf143fc4db7e36979789fddc045e", 17 | "employer": "Coda", 18 | "jobTitle": "Developer Advocate", 19 | "slug": "eric-koleda" 20 | } 21 | ], 22 | "standardPackPlan": { 23 | "packPlanId": "244ac5e8-de4a-4559-a31b-3538a0b040b9", 24 | "packId": 37312, 25 | "createdAt": "2025-01-16T17:28:04.806Z", 26 | "pricing": { 27 | "type": "Free" 28 | } 29 | }, 30 | "sdkVersion": "1.8.1", 31 | "discoverability": "private", 32 | "userAccess": { 33 | "canEdit": true, 34 | "canInstall": false, 35 | "canTest": true, 36 | "canView": true, 37 | "canPurchase": false, 38 | "requiresTrial": false, 39 | "canConnectAccount": true 40 | } 41 | } -------------------------------------------------------------------------------- /usps/pack.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | import {Address} from "@universe/address-parser"; 3 | 4 | export const pack = coda.newPack(); 5 | 6 | pack.addNetworkDomain("usps.com"); 7 | 8 | pack.setSystemAuthentication({ 9 | type: coda.AuthenticationType.OAuth2ClientCredentials, 10 | // The following URL will be found in the API's documentation. 11 | tokenUrl: "https://apis.usps.com/oauth2/v3/token", 12 | }); 13 | 14 | pack.addFormula({ 15 | name: "StandardizeAddress", 16 | description: "TODO", 17 | parameters: [ 18 | coda.makeParameter({ 19 | type: coda.ParameterType.String, 20 | name: "address", 21 | description: "The address to standardize.", 22 | }), 23 | ], 24 | resultType: coda.ValueType.String, 25 | execute: async function (args, context) { 26 | let [address] = args; 27 | if (!address) return ""; 28 | // TODO: Split by lines. 29 | let parsed = new Address(address); 30 | let found = await lookupAddress(context, parsed); 31 | return found.print(); 32 | }, 33 | }); 34 | 35 | async function lookupAddress(context: coda.ExecutionContext, parsed: Address): Promise
{ 36 | let formatted = parsedToUSPS(parsed); 37 | let url = coda.withQueryParams("https://apis.usps.com/addresses/v3/address", { 38 | ...formatted, 39 | }); 40 | let response = await context.fetcher.fetch({ 41 | method: "GET", 42 | url: url, 43 | }); 44 | let found: USPSAddress = { 45 | firm: response.body.firm, 46 | ...response.body.address, 47 | }; 48 | return uspsToParsed(found); 49 | } 50 | 51 | interface USPSAddress { 52 | firm: string; 53 | streetAddress: string; 54 | secondaryAddress: string; 55 | city: string, 56 | state: string; 57 | urbanization?: string; 58 | ZIPCode: string; 59 | ZIPPlus4: string; 60 | } 61 | 62 | function parsedToUSPS(parsed: Address): USPSAddress { 63 | let {care, line1, line2, city, state, urbanization, zip, zip4} = parsed.label(); 64 | return { 65 | firm: care, 66 | streetAddress: line1, 67 | secondaryAddress: line2, 68 | city: city, 69 | state: state, 70 | urbanization: urbanization, 71 | ZIPCode: zip || undefined, 72 | ZIPPlus4: zip4 || undefined, 73 | }; 74 | } 75 | 76 | function uspsToParsed(usps: USPSAddress): Address { 77 | let {firm, streetAddress, secondaryAddress, city, state, urbanization, ZIPCode, ZIPPlus4} = usps; 78 | return new Address(firm, streetAddress, secondaryAddress, city, state, urbanization, 79 | [ZIPCode, ZIPPlus4].filter(Boolean).join("-") 80 | ); 81 | } -------------------------------------------------------------------------------- /visualping/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 23109 3 | } -------------------------------------------------------------------------------- /visualping/assets/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/visualping/assets/cover.jpg -------------------------------------------------------------------------------- /visualping/assets/example0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/visualping/assets/example0.jpg -------------------------------------------------------------------------------- /visualping/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/visualping/assets/icon.png -------------------------------------------------------------------------------- /visualping/helpers.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | 3 | export async function getToken(context: coda.ExecutionContext) { 4 | let invocationToken = context.invocationToken; 5 | let payload = { 6 | method: "PASSWORD", 7 | email: `{{username-${invocationToken}}}`, 8 | password: `{{password-${invocationToken}}}`, 9 | }; 10 | let response = await context.fetcher.fetch({ 11 | method: "POST", 12 | url: "https://api.visualping.io/v2/token", 13 | headers: { 14 | "Content-Type": "application/json", 15 | }, 16 | body: JSON.stringify(payload), 17 | cacheTtlSecs: 24 * 60 * 60, // 24 hours. 18 | forceCache: true, // Required to cache POST requests. 19 | }); 20 | return response.body.id_token; 21 | } 22 | 23 | export async function getJob(context: coda.ExecutionContext, jobId: string) { 24 | let token = await getToken(context); 25 | let url = `https://job.api.visualping.io/v2/jobs/${jobId}`; 26 | let response = await context.fetcher.fetch({ 27 | method: "GET", 28 | url, 29 | headers: { 30 | "Authorization": `Bearer ${token}`, 31 | }, 32 | disableAuthentication: true, 33 | }); 34 | return response.body; 35 | } 36 | -------------------------------------------------------------------------------- /visualping/schemas.ts: -------------------------------------------------------------------------------- 1 | import * as coda from "@codahq/packs-sdk"; 2 | 3 | export const JobSchema = coda.makeObjectSchema({ 4 | properties: { 5 | jobId: { 6 | type: coda.ValueType.Number, 7 | description: "The ID of the job.", 8 | fromKey: "id", 9 | required: true, 10 | }, 11 | name: { 12 | type: coda.ValueType.String, 13 | description: "The name of the job.", 14 | fromKey: "description", 15 | required: true, 16 | }, 17 | active: { 18 | type: coda.ValueType.Boolean, 19 | description: "If the job is active.", 20 | fromKey: "isActive", 21 | }, 22 | url: { 23 | type: coda.ValueType.String, 24 | codaType: coda.ValueHintType.Url, 25 | display: coda.LinkDisplayType.Url, 26 | description: "The URL that the job is configured to monitor.", 27 | }, 28 | icon: { 29 | type: coda.ValueType.String, 30 | codaType: coda.ValueHintType.ImageReference, 31 | description: "The icon associated with the website being monitored.", 32 | fromKey: "faviconPath", 33 | }, 34 | mode: { 35 | type: coda.ValueType.String, 36 | description: "The mode of the comparison between changes.", 37 | }, 38 | link: { 39 | type: coda.ValueType.String, 40 | codaType: coda.ValueHintType.Url, 41 | description: "A link to the job in the Visualping app.", 42 | }, 43 | }, 44 | displayProperty: "name", 45 | idProperty: "jobId", 46 | featuredProperties: ["url", "icon"], 47 | }); 48 | 49 | const JobReferenceSchema = coda.makeReferenceSchemaFromObjectSchema(JobSchema, "Job"); 50 | 51 | export const ChangeSchema = coda.makeObjectSchema({ 52 | properties: { 53 | changeId: { 54 | type: coda.ValueType.String, 55 | description: "The ID of the change.", 56 | fromKey: "id", 57 | }, 58 | created: { 59 | type: coda.ValueType.String, 60 | codaType: coda.ValueHintType.DateTime, 61 | description: "When the change was detected.", 62 | }, 63 | difference: { 64 | type: coda.ValueType.Number, 65 | codaType: coda.ValueHintType.Percent, 66 | precision: 1, 67 | fromKey: "PercentDifference", 68 | description: "How much the URL has changed.", 69 | }, 70 | preview: { 71 | type: coda.ValueType.String, 72 | codaType: coda.ValueHintType.ImageReference, 73 | fromKey: "thumb_diff_full", 74 | description: "A visual preview of the change.", 75 | }, 76 | link: { 77 | type: coda.ValueType.String, 78 | codaType: coda.ValueHintType.Url, 79 | description: "A link to more information about the change.", 80 | }, 81 | summary: { 82 | type: coda.ValueType.String, 83 | description: "An AI-generated summary of the change.", 84 | fromKey: "englishSummary", 85 | }, 86 | job: JobReferenceSchema, 87 | }, 88 | displayProperty: "created", 89 | idProperty: "changeId", 90 | featuredProperties: ["difference", "preview", "link", "summary"], 91 | }); 92 | -------------------------------------------------------------------------------- /xml/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 34738 3 | } -------------------------------------------------------------------------------- /xml/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/xml/assets/example0.png -------------------------------------------------------------------------------- /xml/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/xml/assets/icon.png -------------------------------------------------------------------------------- /xml/tests/pack_test.ts: -------------------------------------------------------------------------------- 1 | import {executeFormulaFromPackDef} from '@codahq/packs-sdk/dist/development'; 2 | import {pack} from '../pack'; 3 | import * as chai from "chai"; 4 | import {assert} from "chai"; 5 | import {describe} from "mocha"; 6 | import {it} from "mocha"; 7 | import chaiAsPromised from "chai-as-promised"; 8 | chai.use(chaiAsPromised); 9 | chai.should(); 10 | 11 | describe("Examples", () => { 12 | for (let formula of pack.formulas) { 13 | describe(formula.name, () => { 14 | for (let [i, example] of formula.examples.entries()) { 15 | it(`Example ${i}`, async () => { 16 | const result = await executeFormulaFromPackDef(pack, formula.name, example.params as any); 17 | if (typeof example.result == "object") { 18 | assert.deepEqual(result, example.result); 19 | } else { 20 | assert.equal(result, example.result); 21 | } 22 | }); 23 | } 24 | }); 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /yaml/.coda-pack.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 34536 3 | } -------------------------------------------------------------------------------- /yaml/assets/example0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/yaml/assets/example0.png -------------------------------------------------------------------------------- /yaml/assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/erickoledadevrel/packs/763c24e9b5385455754069b6e2d9415d2e74a4ce/yaml/assets/icon.png -------------------------------------------------------------------------------- /yaml/assets/listing.json: -------------------------------------------------------------------------------- 1 | { 2 | "packId": 34536, 3 | "name": "YAML", 4 | "description": "A collection of utility functions for working with YAML in Coda.\n\nIcon and logo are the property of yaml.org.", 5 | "shortDescription": "Human-friendly data serialization for all programming languages.", 6 | "logoUrl": "https://codahosted.io/packs/34536/unversioned/assets/LOGO/2e81d22e4833dbca15f43a6eff028aa7163e9cd67dcc41adc61ab62e3c7bbf141fcbc58bbd79245b7727ad234afad4ab4f26ba548dbb80592b213aaab0c923edda556fba31d522260439511d382f3ee9b411c7172086883f81008f4a2ecec372781d57fe", 7 | "exampleImages": [ 8 | { 9 | "imageUrl": "https://codahosted.io/packs/34536/unversioned/assets/EXAMPLE/287fc0f4ef338f16ba6d4da6971f05089cd2936e3d5f58937baeab62924f9df5fd4945b5156d82e822293adfc1f1a2a5390575bf422cec7bccc592abc259bb7e0eb42434e329aedc6826babdbf3c2ed259bbf5064bfe51b21b08b6b25d980ce7cdf7249b", 10 | "filename": "yaml.png", 11 | "mimeType": "image/png", 12 | "assetId": "287fc0f4ef338f16ba6d4da6971f05089cd2936e3d5f58937baeab62924f9df5fd4945b5156d82e822293adfc1f1a2a5390575bf422cec7bccc592abc259bb7e0eb42434e329aedc6826babdbf3c2ed259bbf5064bfe51b21b08b6b25d980ce7cdf7249b" 13 | } 14 | ], 15 | "packVersion": "2", 16 | "releaseId": 2, 17 | "lastReleasedAt": "2024-09-24T16:19:47.166Z", 18 | "externalMetadataUrl": "https://codahosted.io/packs/34536/2/metadata/3d78a8e0055be7d62adf585e36565b0fcd9edd9813791f145cb61497fbd4c4ce927f9acf171836b4f0326f87236d4bc294b2d878decf1117cf4876f4011c40b7288db5520543ee52c0ff69b196e922402182713b4b32ace0549135a92e66a09fd10824fc.json", 19 | "categories": [ 20 | { 21 | "categoryId": "076f4d78-eac3-4d62-b87a-4f3964c96e65", 22 | "categoryName": "Engineering", 23 | "categorySlug": "engineering" 24 | } 25 | ], 26 | "supportEmail": "packs@erickoleda.com", 27 | "sourceCodeVisibility": "private", 28 | "makers": [ 29 | { 30 | "name": "Eric Koleda", 31 | "pictureLink": "https://images-codaio.imgix.net/https%3A%2F%2Flh3.googleusercontent.com%2Fa%2FACg8ocJYa8nknu-CdeUwswBMRrDPKyLXdwzRPhMFuTodG063315NF-Xe%3Ds96-c?ixlib=js-3.8.0&auto=format%2Ccompress&fit=crop&w=360&h=360&crop=faces&s=3883cf143fc4db7e36979789fddc045e", 32 | "employer": "Coda", 33 | "jobTitle": "Developer Advocate", 34 | "slug": "eric-koleda" 35 | } 36 | ], 37 | "standardPackPlan": { 38 | "packPlanId": "7dd9c80e-ddb5-4045-af91-8697131cb26d", 39 | "packId": 34536, 40 | "createdAt": "2024-09-24T14:52:53.214Z", 41 | "pricing": { 42 | "type": "Free" 43 | } 44 | }, 45 | "sdkVersion": "1.7.5", 46 | "discoverability": "public", 47 | "userAccess": { 48 | "canEdit": true, 49 | "canInstall": false, 50 | "canTest": true, 51 | "canView": true, 52 | "canPurchase": false, 53 | "requiresTrial": false, 54 | "canConnectAccount": true 55 | } 56 | } -------------------------------------------------------------------------------- /yaml/tests/pack_test.ts: -------------------------------------------------------------------------------- 1 | import {executeFormulaFromPackDef} from '@codahq/packs-sdk/dist/development'; 2 | import {pack} from '../pack'; 3 | import * as chai from "chai"; 4 | import {assert} from "chai"; 5 | import {describe} from "mocha"; 6 | import {it} from "mocha"; 7 | import chaiAsPromised from "chai-as-promised"; 8 | chai.use(chaiAsPromised); 9 | chai.should(); 10 | 11 | describe("Examples", () => { 12 | for (let formula of pack.formulas) { 13 | describe(formula.name, () => { 14 | for (let [i, example] of formula.examples.entries()) { 15 | it(`Example ${i}`, async () => { 16 | const result = await executeFormulaFromPackDef(pack, formula.name, example.params as any); 17 | if (typeof example.result == "object") { 18 | assert.deepEqual(result, example.result); 19 | } else { 20 | assert.equal(result, example.result); 21 | } 22 | }); 23 | } 24 | }); 25 | } 26 | }); 27 | --------------------------------------------------------------------------------