[
  {
    "path": ".github/CODEOWNERS",
    "content": "# Salesforce Open Source project configuration\n# Learn more: https://github.com/salesforce/oss-template\n#ECCN:Open Source\n#GUSINFO:Open Source,Open Source Workflow\n\n# @slackapi/denosaurs\n# are code reviewers for all changes in this repo.\n* @slackapi/denosaurs\n\n# @slackapi/developer-education\n# are code reviewers for changes in the `/docs` directory.\n/docs/ @slackapi/developer-education\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contributors Guide\n\nInterested in contributing? Awesome! Before you do though, please read our\n[Code of Conduct](https://slackhq.github.io/code-of-conduct). We take it very\nseriously, and expect that you will as well.\n\nThere are many ways you can contribute! :heart:\n\n## :bug: Bug Reports and Fixes\n\n- If you find a bug, please search for it in the\n  [Issues](https://github.com/slackapi/deno-slack-sdk/issues), and if it isn't\n  already tracked,\n  [create a new Bug Report Issue](https://github.com/slackapi/deno-slack-sdk/issues/new/choose).\n  Fill out the \"Bug Report\" section of the issue template. Even if an Issue is\n  closed, feel free to comment and add details, it will still be reviewed.\n- Issues that have already been identified as a bug (note: able to reproduce)\n  will be labelled `bug`.\n- If you'd like to submit a fix for a bug,\n  [send a Pull Request](#creating-a-pull-request) and mention the Issue number.\n- Include tests that isolate the bug and verifies that it was fixed.\n\n## :bulb: New Features\n\n- If you'd like to add new functionality to this project, describe the problem\n  you want to solve in a\n  [new Feature Request Issue](https://github.com/slackapi/deno-slack-sdk/issues/new/choose).\n- Issues that have been identified as a feature request will be labelled\n  `enhancement`.\n- If you'd like to implement the new feature, please wait for feedback from the\n  project maintainers before spending too much time writing the code. In some\n  cases, `enhancement`s may not align well with the project objectives at the\n  time.\n\n## :mag: Tests, :books: Documentation,:sparkles: Miscellaneous\n\n- If you'd like to improve the tests, you want to make the documentation\n  clearer, you have an alternative implementation of something that may have\n  advantages over the way its currently done, or you have any other change, we\n  would be happy to hear about it!\n- If its a trivial change, go ahead and\n  [send a Pull Request](#creating-a-pull-request) with the changes you have in\n  mind.\n- If not, [open an Issue](https://github.com/slackapi/deno-slack-sdk/issues/new)\n  to discuss the idea first.\n\nIf you're new to our project and looking for some way to make your first\ncontribution, look for Issues labelled `good first contribution`.\n\n## Requirements\n\nFor your contribution to be accepted:\n\n- [x] You must have signed the\n      [Contributor License Agreement (CLA)](https://cla.salesforce.com/sign-cla).\n- [x] The test suite must be complete and pass.\n- [x] The changes must be approved by code review.\n- [x] Commits should be atomic and messages must be descriptive. Related issues\n      should be mentioned by Issue number.\n\nIf the contribution doesn't meet the above criteria, you may fail our automated\nchecks or a maintainer will discuss it with you. You can continue to improve a\nPull Request by adding commits to the branch from which the PR was created.\n\n[Interested in knowing more about about pull requests at Slack?](https://slack.engineering/on-empathy-pull-requests-979e4257d158#.awxtvmb2z)\n\n## Creating a Pull Request\n\n1. :fork_and_knife: Fork the repository on GitHub.\n2. :runner: Clone/fetch your fork to your local development machine. It's a good\n   idea to run the tests just to make sure everything is in order.\n3. :herb: Create a new branch and check it out.\n4. :crystal_ball: Make your changes and commit them locally. Magic happens here!\n5. :arrow_heading_up: Push your new branch to your fork. (e.g.\n   `git push username fix-issue-16`).\n6. :inbox_tray: Open a Pull Request on github.com from your new branch on your\n   fork to `main` in this repository.\n\n## Maintainers\n\nThere are more details about processes and workflow in the\n[Maintainer's Guide](./maintainers_guide.md).\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug.md",
    "content": "---\nname: Bug Report\nabout: Report a bug encountered while using this project\ntitle: '[BUG] <title>'\n---\n\n<!-- If you find a bug, please search for it in the [Issues](https://github.com/slackapi/deno-slack-sdk/issues), and if it isn't already tracked then create a new issue -->\n\n**The `deno-slack` versions**\n\n<!-- Paste the output of `cat import_map.json | grep deno-slack` -->\n\n**Deno runtime version**\n\n<!-- Paste the output of `deno --version` -->\n\n**OS info**\n\n<!-- Paste the output of `sw_vers && uname -v` on macOS/Linux or `ver` on Windows OS -->\n\n**Describe the bug**\n\n<!-- A clear and concise description of what the bug is. -->\n\n**Steps to reproduce**\n\n<!-- Share the commands to run, source code, and project settings -->\n1. \n2. \n3. \n\n**Expected result**\n\n<!-- Tell what you expected to happen -->\n\n**Actual result**\n\n<!-- Tell what actually happened with logs, screenshots -->\n\n**Requirements**\n\nPlease read the [Contributing guidelines](https://github.com/slackapi/deno-slack-sdk/blob/main/.github/contributing.md) and [Code of Conduct](https://slackhq.github.io/code-of-conduct) before creating this issue or pull request. By submitting, you are agreeing to those rules.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature.md",
    "content": "---\nname: Feature request\nabout: Suggest a new feature for this project\ntitle: '[FEATURE] <title>'\n---\n\n<!-- If you have a feature request, please search for it in the [Issues](https://github.com/slackapi/deno-slack-sdk/issues), and if it isn't already tracked then create a new issue -->\n\n**Description of the problem being solved**\n\n<!-- Please describe the problem you want to solve -->\n\n**Alternative solutions**\n\n<!-- Please describe the solutions you've considered -->\n\n**Requirements**\n\nPlease read the [Contributing guidelines](https://github.com/slackapi/deno-slack-sdk/blob/main/.github/contributing.md) and [Code of Conduct](https://slackhq.github.io/code-of-conduct) before creating this issue or pull request. By submitting, you are agreeing to those rules.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question.md",
    "content": "---\nname: Question\nabout: Ask a question about this project\ntitle: '[QUERY] <title>'\nlabel: question\n---\n\n<!-- If you have a question, please search for it in the [Issues](https://github.com/slackapi/deno-slack-sdk/issues), and if it isn't already tracked then create a new issue -->\n\n**Question**\n\n<!-- A clear and concise question with steps to reproduce -->\n\n**Context**\n\n<!-- Any additional context to your question -->\n\n**Environment**\n\n<!-- Paste the output of `cat import_map.json | grep deno-slack` -->\n<!-- Paste the output of `deno --version` -->\n<!-- Paste the output of `sw_vers && uname -v` on macOS/Linux or `ver` on Windows OS -->\n\n**Requirements**\n\nPlease read the [Contributing guidelines](https://github.com/slackapi/deno-slack-sdk/blob/main/.github/contributing.md) and [Code of Conduct](https://slackhq.github.io/code-of-conduct) before creating this issue or pull request. By submitting, you are agreeing to those rules.\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\nThanks for sending a pull request!\n\nFriendly reminder: this project is open sourced, the internet can see it,\nmake sure all the info/links shared in this pull request are public information.\n-->\n\n### Summary\n\n<!-- A high level description of the change that will make it easier for your reviewer to make sense of the changes -->\n\n### Testing\n\n<!-- Describe what steps a reviewer should follow to test your changes. -->\n\n### Special notes\n\n<!-- Any special notes reviewers should be aware of. -->\n\n### Requirements <!-- place an `x` in each `[ ]` -->\n\n* [ ] I've read and understood the [Contributing Guidelines](https://github.com/slackapi/deno-slack-sdk/blob/main/.github/CONTRIBUTING.md) and have done my best effort to follow them.\n* [ ] I've read and agree to the [Code of Conduct](https://slackhq.github.io/code-of-conduct).\n* [ ] I've ran `deno task test` after making the changes.\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "# Please see the documentation for all configuration options:\n# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file\n\nversion: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/maintainers_guide.md",
    "content": "# Maintainers Guide\n\nThis document describes tools, tasks and workflow that one needs to be familiar\nwith in order to effectively maintain this project. If you use this package\nwithin your own software as is but don't plan on modifying it, this guide is\n**not** for you.\n\n## Tools\n\nAll you need to work on this project is a recent version of\n[Deno](https://deno.land/)\n\n<details>\n  <summary>Note</summary>\n\n- You can set up shell completion by following the\n  [Shell Completion](https://deno.land/manual/getting_started/setup_your_environment#shell-completions)\n  guidelines.\n\n</details>\n\n## Tasks\n\n### Testing with Deno\n\nIn-code tests can be run directly with Deno:\n\n```zsh\ndeno task test\n```\n\nYou can also run a test coverage report with:\n\n```zsh\ndeno task coverage\n```\n\n### Testing with a sample app\n\nSometimes you may need to test out changes in this SDK with a sample app or\nproject.\n\nA modified SDK version can be used by updating the `deno-slack-sdk` import url\nin the app's `import_map.json` file.\n\n> After making changes to your imports, you may need to\n> [reload your modules](https://deno.land/manual@v1.29.1/basics/modules/reloading_modules)\n> in case they've been cached.\n\n#### Using local changes\n\nTo use your own code as the SDK, change the import url to the `src/` directory\nof your local `deno-slack-sdk` repo:\n\n```json\n{\n  \"imports\": {\n    \"deno-slack-sdk/\": \"../../tools/deno-slack-sdk/src/\",\n    \"deno-slack-api/\": \"jsr:@slack/api@2.9.0/\"\n  }\n}\n```\n\n#### With remote changes\n\nTo test with unreleased changes on a remote repo, commit your intended history\nto a remote branch and note the full commit SHA. (e.g.\n`fc0a0a1f0722e28fecb7782513d045522d7c0d6f`).\n\nThen in your sample app's `import_map.json` file, replace the `deno-slack-sdk`\nimport url with:\n\n```json\n{\n  \"imports\": {\n    \"deno-slack-sdk/\": \"https://raw.githubusercontent.com/slackapi/deno-slack-sdk/<commit-SHA-goes-here>/src/\",\n    \"deno-slack-api/\": \"jsr:@slack/api@2.9.0/\"\n  }\n}\n```\n\n### Lint and format\n\nThe linting and formatting rules are defined in the `deno.jsonc` file, your IDE\ncan be set up to follow these rules:\n\n1. Refer to the\n   [Deno Set Up Your Environment](https://deno.land/manual/getting_started/setup_your_environment)\n   guidelines to set up your IDE with the proper plugin.\n2. Ensure that the `deno.jsonc` file is set as the configuration file for your\n   IDE plugin\n   - If you are using VS code\n     [this](https://deno.land/manual/references/vscode_deno#using-a-configuration-file)\n     is already configured in `.vscode/settings.json`\n\n#### Linting\n\nThe list of linting rules can be found in\n[the linting deno docs](https://lint.deno.land/). Currently we apply all\nrecommended rules.\n\n#### Format\n\nThe list of format options is defined in the `deno.jsonc` file. They closely\nresemble the default values.\n\n### Releasing\n\nReleases for this library are automatically generated off of git tags. Before\ncreating a new release, ensure that everything on the `main` branch since the\nlast tag is in a releasable state! At a minimum,\n[run the tests](#testing-with-deno) and validate that the package meets JSR\npublishing requirements by doing a dry run of the publish command:\n\n```zsh\ndeno task publish:dry-run\n```\n\nTo create a new release:\n\n1. Determine the new release version. You can start off by incrementing the\n   version to reflect a patch (i.e. 1.0.0 -> 1.0.1).\n   - Review the pull request labels of the changes since the last release (i.e.\n     `semver:minor`, `semver:patch`, `semver:major`). Tip: Your release version\n     should be based on the tag of the largest change, so if the changes include\n     a `semver:minor`, the release version should be upgraded to reflect a\n     minor.\n   - Ensure that this version adheres to [semantic versioning][semver]. See\n     [Versioning](#versioning-and-tags) for correct version format. Version tags\n     should match the following pattern: `1.0.1` (no `v` preceding the number).\n2. Create a new branch from `main` named after the release version (e.g.\n   `1.0.1`).\n3. Bump the `version` field in `deno.jsonc` to the new release version.\n4. Open a pull request from the version branch into `main` and get it\n   approved/merged.\n   1. `git commit -m 'chore(release): version 1.0.1'`\n   2. `git push -u origin 1.0.1`\n5. Create a new GitHub Release from the\n   [Releases page](https://github.com/slackapi/deno-slack-sdk/releases) by\n   clicking the \"Draft a new release\" button.\n6. Input a new version manually into the \"Choose a tag\" input.\n   - After you input the new version, click the \"Create a new tag: x.x.x on\n     publish\" button. This won't create your tag immediately.\n   - Auto-generate the release notes by clicking the \"Auto-generate release\n     notes\" button. This will pull in changes that will be included in your\n     release.\n   - Edit the resulting notes to ensure they have decent messaging that are\n     understandable by non-contributors, but each commit should still have it's\n     own line.\n7. Set the \"Target\" input to the \"main\" branch.\n8. Name the release title after the version tag.\n9. Make any adjustments to generated release notes to make sure they are\n   accessible and approachable and that an end-user with little context about\n   this project could still understand.\n10. Publish the release by clicking the \"Publish release\" button!\n11. After a few minutes, the corresponding version will be available on\n    <https://deno.land/x/deno_slack_sdk> and <https://jsr.io/@slack/sdk>.\n\n## Workflow\n\n### Versioning and Tags\n\nThis project is versioned using [Semantic Versioning][semver].\n\n### Branches\n\n> Describe any specific branching workflow. For example: `main` is where active\n> development occurs. Long running branches named feature branches are\n> occasionally created for collaboration on a feature that has a large scope\n> (because everyone cannot push commits to another person's open Pull Request)\n\n### Issue Management\n\nLabels are used to run issues through an organized workflow. Here are the basic\ndefinitions:\n\n- `bug`: A confirmed bug report. A bug is considered confirmed when reproduction\n  steps have been documented and the issue has been reproduced.\n- `enhancement`: A feature request for something this package might not already\n  do.\n- `docs`: An issue that is purely about documentation work.\n- `tests`: An issue that is purely about testing work.\n- `needs feedback`: An issue that may have claimed to be a bug but was not\n  reproducible, or was otherwise missing some information.\n- `discussion`: An issue that is purely meant to hold a discussion. Typically\n  the maintainers are looking for feedback in this issues.\n- `question`: An issue that is like a support request because the user's usage\n  was not correct.\n- `semver:major|minor|patch`: Metadata about how resolving this issue would\n  affect the version number.\n- `security`: An issue that has special consideration for security reasons.\n- `good first contribution`: An issue that has a well-defined relatively-small\n  scope, with clear expectations. It helps when the testing approach is also\n  known.\n- `duplicate`: An issue that is functionally the same as another issue. Apply\n  this only if you've linked the other issue by number.\n\n**Triage** is the process of taking new issues that aren't yet \"seen\" and\nmarking them with a basic level of information with labels. An issue should have\n**one** of the following labels applied: `bug`, `enhancement`, `question`,\n`needs feedback`, `docs`, `tests`, or `discussion`.\n\nIssues are closed when a resolution has been reached. If for any reason a closed\nissue seems relevant once again, reopening is great and better than creating a\nduplicate issue.\n\n## Dependency Graph\n\n<!-- https://mermaid.js.org/syntax/flowchart.html -->\n<!-- Link in mermaid are not supported on github https://github.com/mermaid-js/mermaid/issues/3077 -->\n\n```mermaid\nflowchart TD\n    samples --> deno-slack-sdk\n    samples --> deno-slack-api\n    samples -- start hook --> deno-slack-runtime\n    samples --> deno-slack-hooks\n    deno-slack-hooks -. start hook .-> deno-slack-runtime\n    deno-slack-sdk --> deno-slack-api\n    deno-slack-hooks --> deno-slack-protocols\n    deno-slack-runtime --> deno-slack-protocols\n```\n\n|                                  Links                                   |\n| :----------------------------------------------------------------------: |\n|       [samples](https://github.com/slack-samples/deno-hello-world)       |\n|       [deno-slack-sdk](https://github.com/slackapi/deno-slack-sdk)       |\n|       [deno-slack-api](https://github.com/slackapi/deno-slack-api)       |\n|   [deno-slack-runtime](https://github.com/slackapi/deno-slack-runtime)   |\n|     [deno-slack-hooks](https://github.com/slackapi/deno-slack-hooks)     |\n| [deno-slack-protocols](https://github.com/slackapi/deno-slack-protocols) |\n\n## Everything else\n\nWhen in doubt, find the other maintainers and ask.\n\n[semver]: http://semver.org/\n"
  },
  {
    "path": ".github/workflows/deno.yml",
    "content": "name: Deno\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\n\njobs:\n  deno:\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        deno-version:\n          - v1.x\n          - v2.x\n    permissions:\n      contents: read\n    steps:\n      - name: Setup repo\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - name: Setup Deno ${{ matrix.deno-version }}\n        uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4\n        with:\n          deno-version: ${{ matrix.deno-version }}\n\n      - name: Run tests\n        run: deno task test\n\n      - name: Generate CodeCov-friendly coverage report\n        run: deno task coverage\n\n      - name: Upload coverage to CodeCov\n        uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0\n        if: matrix.deno-version == 'v2.x'\n        with:\n          files: ./lcov.info\n          token: ${{ secrets.CODECOV_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/dependencies.yml",
    "content": "name: Merge updates to dependencies\non:\n  pull_request:\njobs:\n  dependabot:\n    name: \"@dependabot\"\n    if: github.event.pull_request.user.login == 'dependabot[bot]'\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n      pull-requests: write\n    steps:\n      - name: Collect metadata\n        id: metadata\n        uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0\n        with:\n          github-token: \"${{ secrets.GITHUB_TOKEN }}\"\n      - name: Approve\n        if: steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor'\n        run: gh pr review --approve \"$PR_URL\"\n        env:\n          PR_URL: ${{github.event.pull_request.html_url}}\n          GH_TOKEN: ${{secrets.GITHUB_TOKEN}}\n      - name: Automerge\n        if: steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor'\n        run: gh pr merge --auto --squash \"$PR_URL\"\n        env:\n          PR_URL: ${{ github.event.pull_request.html_url }}\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/e2e.yml",
    "content": "# This workflow invokes and waits for the result of Slack's private E2E CI system\nname: Internal E2E CI\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n\njobs:\n  e2e:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n    steps:\n      - name: Checkout the sdk\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n      - name: Set environment variables\n        run: |\n          # Short name for current branch. For PRs, use source branch (GITHUB_HEAD_REF)\n          GIT_BRANCH=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}\n          echo \"Identified deno-slack-sdk branch name: ${GIT_BRANCH}; will invoke CI system to test this branch.\";\n          echo \"GIT_BRANCH=$GIT_BRANCH\" >> $GITHUB_ENV\n      - name: Kick off platform-devpx-test pipeline\n        env:\n          CCI_PAT: ${{ secrets.CCHEN_CIRCLECI_PERSONAL_TOKEN }}\n        run: |\n          IMPORT_URL=\"https://raw.githubusercontent.com/slackapi/deno-slack-sdk/refs/heads/${GIT_BRANCH}/src/\";\n          echo \"Import URL: ${IMPORT_URL}\";\n          # https://app.circleci.com/settings/organization/github/slackapi/contexts\n          TEST_PAYLOAD=$(curl --location --request POST 'https://circleci.com/api/v2/project/gh/slackapi/platform-devxp-test/pipeline' \\\n          --header 'Content-Type: application/json' \\\n          -u \"${CCI_PAT}:\" \\\n          --data \"{\\\"branch\\\":\\\"main\\\",\\\"parameters\\\":{\\\"deno_sdk_import_url\\\":\\\"${IMPORT_URL}\\\"}}\")\n          echo $TEST_PAYLOAD;\n          TEST_JOB_WORKFLOW_ID=$(echo $TEST_PAYLOAD | jq --raw-output '.id');\n          if [ $TEST_JOB_WORKFLOW_ID = \"null\" ]; then\n            echo \"Problem extracting job ID from invocation API call! Aborting!\";\n            exit 1;\n          fi\n          echo \"e2e test workflow started with id: $TEST_JOB_WORKFLOW_ID\"\n          echo \"TEST_JOB_WORKFLOW_ID=${TEST_JOB_WORKFLOW_ID}\" >> $GITHUB_ENV\n      - name: Wait for platform-devxp-test E2E run to complete\n        env:\n          CCI_PAT: ${{ secrets.CCHEN_CIRCLECI_PERSONAL_TOKEN }}\n        run: |\n          E2E_RESULT=\"{}\"\n          E2E_STATUS=\"running\"\n          # possible status values: success, running, not_run, failed, error, failing, on_hold, canceled, unauthorized\n          while [[ $E2E_STATUS != \"failed\" && $E2E_STATUS != \"canceled\" && $E2E_STATUS != \"success\" && $E2E_STATUS != \"not_run\" && $E2E_STATUS != \"error\" && $E2E_STATUS != \"unauthorized\" ]]\n          do\n            sleep 30s\n            echo \"Polling test job ${TEST_JOB_WORKFLOW_ID}...\"\n            E2E_RESULT=$(curl --location -sS --request GET \"https://circleci.com/api/v2/pipeline/${TEST_JOB_WORKFLOW_ID}/workflow\" --header \"Circle-Token: ${CCI_PAT}\")\n            echo $E2E_RESULT;\n            E2E_STATUS=$(echo $E2E_RESULT | jq --raw-output '.items[0].status')\n            if [ $E2E_STATUS = \"null\" ]; then\n              echo \"Problem extracting status from workflow API! Aborting!\";\n              exit 1\n            fi\n            echo \"Status is now: $E2E_STATUS\"\n          done\n          if [ $E2E_STATUS = \"failed\" ] || [ $E2E_STATUS = \"error\" ]; then\n            E2E_PIPE_NUM=$(echo $E2E_RESULT | jq '.items[0].pipeline_number')\n            E2E_WORKFLOW_ID=$(echo $E2E_RESULT | jq -r '.items[0].id')\n            CIRCLE_FAIL_LINK=\"https://app.circleci.com/pipelines/github/slackapi/platform-devxp-test/${E2E_PIPE_NUM}/workflows/${E2E_WORKFLOW_ID}\"\n            echo \"Tests failed! Visit $CIRCLE_FAIL_LINK for more info.\"\n            exit 1\n          elif [ \"$E2E_STATUS\" = \"canceled\" ] || [ \"$E2E_STATUS\" = \"unauthorized\" ] || [ $E2E_STATUS = \"not_run\" ]; then\n            echo \"Tests have been ${E2E_STATUS} and did not finish!\"\n            exit 1\n          else\n            echo \"Tests passed woot 🎉\"\n          fi\n"
  },
  {
    "path": ".github/workflows/npm-publish.yml",
    "content": "# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created\n# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages\n\nname: Build & Deploy to NPM\n\non:\n  push:\n    tags:\n      - \"*.*.*\"\n\njobs:\n  build:\n    runs-on: macos-latest\n    permissions:\n      contents: read\n    steps:\n      - name: Actions checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - name: Setup node\n        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0\n        with:\n          node-version: latest\n          registry-url: https://registry.npmjs.org/\n\n      - name: Setup Deno\n        uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4\n        with:\n          deno-version: v2.x\n\n      - name: Get the tag name\n        id: get_tag_name\n        run: echo \"TAG=${GITHUB_REF#refs/*/}\" >> $GITHUB_OUTPUT\n\n      - name: Run build_npm.ts\n        run: deno run -A scripts/build_npm.ts \"$TAG\"\n        env:\n          TAG: ${{steps.get_tag_name.outputs.TAG}}\n\n      - name: Publish to NPM\n        run: cd npm && npm publish --access=public\n        env:\n          NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}\n"
  },
  {
    "path": ".github/workflows/npm.yml",
    "content": "# This workflow runs a test build for npm against changes on main or PRs\n\nname: Npm Build\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\n\njobs:\n  build:\n    runs-on: macos-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        deno-version:\n          - v1.x\n          - v2.x\n    permissions:\n      contents: read\n    steps:\n      - name: Actions checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - name: Setup node\n        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0\n        with:\n          node-version: latest\n          registry-url: https://registry.npmjs.org/\n\n      - name: Setup Deno ${{ matrix.deno-version }}\n        uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4\n        with:\n          deno-version: ${{ matrix.deno-version }}\n\n      - name: Run build_npm.ts\n        run: deno run -A scripts/build_npm.ts\n"
  },
  {
    "path": ".github/workflows/publish.yml",
    "content": "name: Publish\n\non:\n  push:\n    tags:\n      - '[0-9]+.[0-9]+.[0-9]+'\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      id-token: write # The OIDC ID token is used for authentication with JSR.\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n      - run: npx jsr publish --allow-slow-types\n"
  },
  {
    "path": ".github/workflows/samples.yml",
    "content": "# This workflow runs a `deno check` against slack sample apps\nname: Samples Integration Type-checking\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\n\njobs:\n  samples:\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        sample:\n          - slack-samples/deno-issue-submission\n          - slack-samples/deno-starter-template\n          - slack-samples/deno-blank-template\n          - slack-samples/deno-message-translator\n          - slack-samples/deno-request-time-off\n          - slack-samples/deno-simple-survey\n        deno-version:\n          - v1.x\n          - v2.x\n    permissions:\n      contents: read\n    steps:\n      - name: Setup Deno ${{ matrix.deno-version }}\n        uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4\n        with:\n          deno-version: ${{ matrix.deno-version }}\n\n      - name: Checkout the sdk\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          path: ./deno-slack-sdk\n          persist-credentials: false\n\n      - name: Checkout the ${{ matrix.sample }} sample\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          repository: ${{ matrix.sample }}\n          path: ./sample\n          persist-credentials: false\n\n      - name: Set imports.deno-slack-sdk/ to ../deno-slack-sdk/src/ in imports\n        run: >\n          deno run\n          --allow-read --allow-write \n          deno-slack-sdk/scripts/imports/update.ts \n          --import-file \"./sample/deno.jsonc\"\n          --sdk \"./deno-slack-sdk/\"\n\n      - name: Deno check **/*.ts\n        working-directory: ./sample\n        run: find . -type f -regex \".*\\.ts\" | xargs deno check -r\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n.coverage\n.vim\nlcov.info\nnpm/\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"deno.enable\": true,\n  \"deno.lint\": true,\n  \"deno.config\": \"./deno.jsonc\",\n  \"[typescript]\": {\n    \"editor.formatOnSave\": true,\n    \"editor.defaultFormatter\": \"denoland.vscode-deno\"\n  }\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) Slack Technologies, Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<h1 align=\"center\">\n  Deno Slack SDK\n  <br>\n</h1>\n\n<p align=\"center\">\n    <img alt=\"deno.land version\" src=\"https://img.shields.io/endpoint?url=https%3A%2F%2Fdeno-visualizer.danopia.net%2Fshields%2Flatest-version%2Fx%2Fdeno_slack_sdk%2Fmod.ts\">\n    <img alt=\"deno dependencies\" src=\"https://img.shields.io/endpoint?url=https%3A%2F%2Fdeno-visualizer.danopia.net%2Fshields%2Fupdates%2Fx%2Fdeno_slack_sdk%2Fmod.ts\">\n    <img alt=\"Samples Integration Type-checking\" src=\"https://github.com/slackapi/deno-slack-sdk/workflows/Samples%20Integration%20Type-checking/badge.svg\">\n  </a>\n</p>\n\nA Deno SDK to build Slack apps with the latest platform features. Read the\n[quickstart guide](https://api.slack.com/automation/quickstart) and look at our\n[code samples](https://api.slack.com/automation/samples) to learn how to build\napps.\n\n## Versioning\n\nReleases for this repository follow the [SemVer](https://semver.org/) versioning\nscheme. The SDK's contract is determined by the top-level exports from\n`src/mod.ts` and `src/types.ts`. Exports not included in these files are deemed\ninternal and any modifications will not be treated as breaking changes. As such,\ninternal exports should be treated as unstable and used at your own risk.\n\n## Setup\n\nMake sure you have a development workspace where you have permission to install\napps. **Please note that the features in this project require that the workspace\nbe part of [a Slack paid plan](https://slack.com/pricing).**\n\n### Install the Slack CLI\n\nYou need to install and configure the Slack CLI. Step-by-step instructions can\nbe found on our\n[install & authorize page](https://api.slack.com/automation/cli/install).\n\n## Creating an app\n\nCreate a blank project by executing the following command:\n\n```zsh\nslack create my-app --template slack-samples/deno-blank-template\n\ncd my-app/\n```\n\nThe `manifest.ts` file contains the app's configuration. This file defines\nattributes like app name, description and functions.\n\n### Create a [function](https://api.slack.com/automation/functions/custom)\n\n```zsh\nmkdir ./functions && touch ./functions/hello_world.ts\n```\n\n```ts\n// Contents of ./functions/hello_world.ts\nimport { DefineFunction, Schema, SlackFunction } from \"deno-slack-sdk/mod.ts\";\n\nexport const HelloWorldFunctionDef = DefineFunction({\n  callback_id: \"hello_world_function\",\n  title: \"Hello World\",\n  source_file: \"functions/hello_world.ts\",\n  input_parameters: {\n    properties: {},\n    required: [],\n  },\n  output_parameters: {\n    properties: {\n      message: {\n        type: Schema.types.string,\n        description: \"Hello world message\",\n      },\n    },\n    required: [\"message\"],\n  },\n});\n\nexport default SlackFunction(\n  HelloWorldFunctionDef,\n  () => {\n    return {\n      outputs: { message: \"Hello World!\" },\n    };\n  },\n);\n```\n\n`DefineFunction` is used to define a custom function and provide Slack with the\ninformation required to use it.\n\n`SlackFunction` uses the definition returned by `DefineFunction` and your custom\nexecutable code to export a Slack-usable custom function.\n\n### Create a [workflow](https://api.slack.com/automation/workflows)\n\n```zsh\nmkdir ./workflows && touch ./workflows/hello_world.ts\n```\n\n```ts\n// Contents of ./workflows/hello_world.ts\nimport { DefineWorkflow, Schema } from \"deno-slack-sdk/mod.ts\";\nimport { HelloWorldFunctionDef } from \"../functions/hello_world.ts\";\n\nconst HelloWorldWorkflowDef = DefineWorkflow({\n  callback_id: \"hello_world_workflow\",\n  title: \"Hello World Workflow\",\n  input_parameters: {\n    properties: {\n      channel: {\n        type: Schema.slack.types.channel_id,\n      },\n    },\n    required: [\"channel\"],\n  },\n});\n\nconst helloWorldStep = HelloWorldWorkflowDef.addStep(HelloWorldFunctionDef, {});\n\nHelloWorldWorkflowDef.addStep(Schema.slack.functions.SendMessage, {\n  channel_id: HelloWorldWorkflowDef.inputs.channel,\n  message: helloWorldStep.outputs.message,\n});\n\nexport default HelloWorldWorkflowDef;\n```\n\n`DefineWorkflow` is used to define a workflow and provide Slack with the\ninformation required to use it.\n\n`HelloWorldWorkflow.addStep` is used to add a step to the workflow; here we add\nthe `HelloWorldFunction` and then the `SendMessage` Slack Function that will\npost the `message` to a Slack channel.\n\n### Update the [manifest](https://api.slack.com/automation/manifest)\n\n```ts\n// Contents of manifest.ts\nimport { Manifest } from \"deno-slack-sdk/mod.ts\";\nimport HelloWorldWorkflow from \"./workflows/hello_world.ts\";\n\nexport default Manifest({\n  name: \"my-app\",\n  description: \"A Hello World app\",\n  icon: \"assets/default_new_app_icon.png\",\n  workflows: [HelloWorldWorkflow],\n  outgoingDomains: [],\n  botScopes: [\"chat:write\", \"chat:write.public\"],\n});\n```\n\n`Manifest` is used to define your apps\n[manifest](https://api.slack.com/automation/manifest) and provides Slack with\nthe information required to manage it.\n\n### Create a [trigger](https://api.slack.com/automation/triggers)\n\n```zsh\nmkdir ./triggers && touch ./triggers/hello_world.ts\n```\n\n```ts\n// Contents of ./triggers/hello_world.ts\nimport { Trigger } from \"deno-slack-sdk/types.ts\";\nimport { TriggerContextData, TriggerTypes } from \"deno-slack-api/mod.ts\";\nimport HelloWorldWorkflow from \"../workflows/hello_world.ts\";\n\nconst trigger: Trigger<typeof HelloWorldWorkflow.definition> = {\n  type: TriggerTypes.Shortcut,\n  name: \"Reverse a string\",\n  description: \"Starts the workflow to reverse a string\",\n  workflow: `#/workflows/${HelloWorldWorkflow.definition.callback_id}`,\n  inputs: {\n    channel: {\n      value: TriggerContextData.Shortcut.channel_id,\n    },\n  },\n};\n\nexport default trigger;\n```\n\nThe `Trigger` object is used to define a trigger that will invoke the\n`HelloWorldWorkflow`. The Slack CLI will detect this file and prompt you for its\ncreation.\n\n## Running an app\n\n```zsh\nslack run\n```\n\nWhen prompted, create the `triggers/hello_world.ts` trigger. This will send your\ntrigger configuration to Slack.\n\nPost the `Hello world shortcut trigger` in a slack message and **use it**\n\n## Getting Help\n\n[This documentation](https://api.slack.com/automation) has more information on\nbasic and advanced concepts of the SDK.\n\nInformation on how to get started developing with Deno can be found in\n[this documentation](https://api.slack.com/automation/deno/develop).\n\nIf you get stuck, we're here to help. The following are the best ways to get\nassistance working through your issue:\n\n- [Issue Tracker](https://github.com/slackapi/deno-slack-sdk/issues?q=is%3Aissue)\n  for questions, bug reports, feature requests, and general discussion. **Try\n  searching for an existing issue before creating a new one.**\n- Email our developer support team: `support@slack.com`\n\n## Contributing\n\nContributions are more than welcome. Please look at the\n[contributing guidelines](https://github.com/slackapi/deno-slack-sdk/blob/main/.github/CONTRIBUTING.md)\nfor more info!\n"
  },
  {
    "path": "deno.jsonc",
    "content": "{\n  \"$schema\": \"https://deno.land/x/deno/cli/schemas/config-file.v1.json\",\n  \"name\": \"@slack/sdk\",\n  \"version\": \"2.15.2\",\n  \"exports\": {\n    \".\": \"./src/mod.ts\",\n    \"./mod.ts\": \"./src/mod.ts\",\n    \"./types.ts\": \"./src/types.ts\"\n  },\n  \"publish\": {\n    \"exclude\": [\"**/*_test.ts\", \"**/fixtures\"],\n    \"include\": [\n      \"README.md\",\n      \"LICENSE\",\n      \"deno.jsonc\",\n      \"src/**\"\n    ]\n  },\n  \"fmt\": {\n    \"include\": [\"src\", \"tests\", \"docs\", \"README.md\", \"scripts\", \".github/maintainers_guide.md\", \".github/CONTRIBUTING.md\"],\n    \"exclude\": [\"src/schema/slack/functions/_scripts/functions.json\"],\n    \"semiColons\": true,\n    \"indentWidth\": 2,\n    \"lineWidth\": 80,\n    \"proseWrap\": \"always\",\n    \"singleQuote\": false,\n    \"useTabs\": false\n  },\n  \"lint\": {\n    \"include\": [\"src\", \"tests\", \"scripts\"],\n    \"exclude\": [\"src/schema/slack/functions/_scripts/functions.json\", \"**/*.md\"],\n    \"rules\": { \"exclude\": [\"no-import-prefix\", \"no-slow-types\"] }\n  },\n  \"tasks\": {\n    \"test\": \"deno fmt --check && deno lint && deno run --allow-run --allow-env --allow-read scripts/bundle.ts && deno test --allow-read --allow-run --parallel src/ tests/\",\n    \"coverage\": \"rm -rf .coverage && deno test --reporter=dot --parallel --allow-read --coverage=.coverage src/ && deno coverage --exclude=fixtures --exclude=_test --lcov --output=lcov.info .coverage\",\n    \"publish:dry-run\": \"deno publish --allow-slow-types --dry-run\"\n  },\n  \"lock\": false\n}\n"
  },
  {
    "path": "docs/datastores.md",
    "content": "## Datastores\n\n### Defining a Datastore\n\nDatastores can be defined with the top level `DefineDatastore` export. Below is\nan example of setting up a Datastore:\n\n```ts\nimport { DefineDatastore, Schema } from \"deno-slack-sdk/mod.ts\";\n\nexport const ReversalsDatastore = DefineDatastore({\n  name: \"reversals\",\n  attributes: {\n    id: {\n      type: Schema.types.string,\n    },\n    original: {\n      type: Schema.types.string,\n    },\n    reversed: {\n      type: Schema.types.string,\n    },\n  },\n  primary_key: \"id\",\n});\n```\n\n### Registering a Datastore to the App\n\nTo register the newly defined Datastore, add it to the array assigned to the\n`datastores` parameter while defining the [`Manifest`][manifest].\n\n```ts\nexport default Manifest({\n  name: \"admiring-ox-50\",\n  description: \"Reverse a string\",\n  icon: \"assets/icon.png\",\n  functions: [ReverseFunction],\n  outgoingDomains: [],\n  datastores: [ReversalsDatastore],\n  botScopes: [\n    \"commands\",\n    \"chat:write\",\n    \"chat:write.public\",\n    \"datastore:read\",\n    \"datastore:write\",\n  ],\n});\n```\n\nNote: Registering a Datastore will automatically add `datastore:read` and\n`datastore:write` to the App's defined `botScopes`.\n\n### Using a Datastore in your custom function code\n\nNow that you have a Datastore all set up, you can use it in your\n[`functions`][functions]! Import the\n[deno-slack-api](https://github.com/slackapi/deno-slack-api) library,\ninstantiate your client, and make an API call to one of the Datastore endpoints!\n\n```ts\nimport { SlackFunction } from \"deno_slack_api/mod.ts\";\n\nexport default SlackFunction(ReverseFunction, async ({ client, inputs }) => {\n  const original = inputs.stringToReverse;\n  const recordId = crypto.randomUUID();\n  const reversed = inputs.stringToReverse.split(\"\").reverse().join(\"\");\n\n  const putResp = await client.apps.datastore.put({\n    datastore: \"reversals\",\n    item: {\n      id: recordId,\n      original,\n      reversed,\n    },\n  });\n  if (!putResp.ok) {\n    return {\n      error: putResp.error,\n    };\n  }\n  // ...\n});\n```\n\n[functions]: ./functions.md\n[manifest]: ./manifest.md\n"
  },
  {
    "path": "docs/events.md",
    "content": "## Events\n\nCustom events provide a way for Apps to validate\n[message metadata](https://api.slack.com/metadata) against a pre-defined schema.\n\n### Defining an event\n\nEvents can be defined with the top level `DefineEvent` export. Events must be\nset up as an `object` type or a [`custom Type`][types] of an `object` type.\nBelow is an example of setting up a custom Event that can be used during an\nincident.\n\n```ts\nconst IncidentEvent = DefineEvent({\n  name: \"incident\",\n  title: \"Incident\",\n  type: Schema.types.object,\n  properties: {\n    id: { type: Schema.types.string },\n    title: { type: Schema.types.string },\n    summary: { type: Schema.types.string },\n    severity: { type: Schema.types.string },\n    date_created: { type: Schema.types.number },\n  },\n  required: [\"id\", \"title\", \"summary\", \"severity\"],\n  additionalProperties: false, // Setting this to false forces the validation to catch any additional properties\n});\n```\n\n### Registering an event with the app\n\nTo register the newly defined event, add it to the array assigned to the\n`events` parameter while defining the [`Manifest`][manifest].\n\nNote: All custom events **must** be registered to the [Manifest][manifest] in\norder for them to be used. There is no automated registration for events.\n\n```ts\nManifest({\n  ...\n  events: [IncidentEvent],\n});\n```\n\n### Referencing events\n\nThere are two places where you can reference your events:\n\n1. Posting a message to Slack\n2. Creating a message metadata trigger\n\n#### Posting a message to Slack\n\nEvent validation happens against the App's manifest when an App posts a message\nto Slack using the\n[`metadata` parameter](https://api.slack.com/methods/chat.postMessage#arg_metadata).\nIf the `event_type` matches the `name` of a custom Event specified in the App's\nmanifest, it will validate that all required parameters are provided. If it\ndoesn't meet the validation standards, a warning will be returned in the\nresponse and the message will still be posted, but the metadata will be dropped\nfrom the message.\n\n```ts\n// At workflow authoring time\n// This example assumes all required values are passed to the workflow's inputs\nMyWorkflow.addStep(Schema.slack.functions.SendMessage, {\n  channel_id: MyWorkflow.inputs.channel_id,\n  message: \"We have an incident!\",\n  metadata: {\n    event_type: IncidentEvent,\n    event_payload: {\n      id: MyWorkflow.inputs.incident_id,\n      title: MyWorkflow.inputs.incident_title,\n      summary: MyWorkflow.inputs.incident_summary,\n      severity: MyWorkflow.inputs.incident_severity,\n      date_created: MyWorkflow.inputs.incident_date, // Since this isn't required, it doesn't need to exist to pass validation\n    },\n  },\n});\n```\n\n```ts\n// At function runtime\n// This example assumes all required values are passed to the function's inputs\nawait client.chat.postMessage({\n  channel_id: inputs.channel_id,\n  message: \"We have an incident!\",\n  metadata: {\n    event_type: IncidentEvent,\n    event_payload: {\n      id: inputs.incident_id,\n      title: inputs.incident_title,\n      summary: inputs.incident_summary,\n      severity: inputs.incident_severity,\n      date_created: inputs.incident_date, // Since this isn't required, it doesn't need to exist to pass validation\n    },\n  },\n});\n```\n\n#### Creating a message metadata trigger\n\nNow that the app has a defined schema for the event, a trigger can be created to\nwatch for any message posted with the expected metadata. When the schema is met,\nthe trigger will execute a workflow\n\n```ts\n// A trigger Definition file for the CLI\nimport { IncidentEvent } from \"./manifest.ts\";\n\nconst trigger: Trigger = {\n  type: \"event\",\n  name: \"Incident Metadata Posted\",\n  inputs: {\n    id: \"{{data.metadata.event_payload.incident_id}}\",\n    title: \"{{data.metadata.event_payload.incident_title}}\",\n    summary: \"{{data.metadata.event_payload.incident_summary}}\",\n    severity: \"{{data.metadata.event_payload.incident_severity}}\",\n    date_created: \"{{data.metadata.event_payload.incident_date}}\",\n  },\n  workflow: \"#/workflows/start_incident\",\n  event: {\n    event_type: \"slack#/events/message_metadata_posted\",\n    metadata_event_type: IncidentEvent,\n    channel_ids: [\"C012354\"], // The channel that needs to be watched for message metadata being posted\n  },\n};\n\nexport default trigger;\n```\n\n[manifest]: ./manifest.md\n[types]: ./types.md\n"
  },
  {
    "path": "docs/functions-action-handlers.md",
    "content": "## Block kit action handlers\n\nYour application's [functions][functions] can do a wide variety of interesting\nthings: post messages, create channels, or anything available to developers via\nthe [Slack API][api]. One of the more compelling features available to app\ndevelopers is the ability to use [Block Kit][block-kit] to add richness and\ndepth to messages in Slack. Even better, [Block Kit][block-kit] supports a\nvariety of [interactive components][interactivity]! This document explores the\nAPIs available to app developers building Run-On-Slack applications to leverage\nthese [interactive components][interactivity] and how applications can respond\nto user interactions with these [interactive components][interactivity].\n\nIf you're already familiar with the main concepts underpinning Block Kit Action\nHandlers, then you may want to skip ahead to the\n[`addBlockActionsHandler()` method API Reference](#api-reference).\n\n- [Block kit action handlers](#block-kit-action-handlers)\n  - [Requirements](#requirements)\n  - [Posting a message with block kit elements](#posting-a-message-with-block-kit-elements)\n  - [Adding block action handlers](#adding-block-action-handlers)\n  - [API reference](#api-reference)\n    - [`addBlockActionsHandler(constraint, handler)`](#addblockactionshandlerconstraint-handler)\n      - [`BlockActionConstraintField`](#blockactionconstraintfield)\n        - [`BlockActionConstraintObject`](#blockactionconstraintobject)\n\n### Requirements\n\nYour app needs to have an existing [function][functions] defined, implemented\nand working before you can add interactivity handlers like Block Kit Action\nHandlers to them. Make sure you have followed our\n[functions documentation][functions] and have a function in your app ready that\nwe can expand with a Block Kit Action Handler.\n\nAs part of exploring how Block Kit Action Handlers work, we'll walk through an\napproval flow example. A user would trigger our app's function, which would post\na message with two buttons: Approve and Deny. Once someone clicks either button,\nour app will handle these button interactions - these Block Kit Actions - and\nupdate the original message with either an \"Approved!\" or \"Denied!\" text.\n\nFor the purposes of walking through this approval flow example, let us assume\nthe following [function][functions] definition (that we will store in a file\ncalled `definition.ts` under the `functions/approval/` subdirectory inside your\napp):\n\n```typescript\nimport { DefineFunction, Schema } from \"deno-slack-sdk/mod.ts\";\n\nexport const ApprovalFunction = DefineFunction({\n  callback_id: \"review_approval\",\n  title: \"Approval\",\n  description: \"Get approval for a request\",\n  source_file: \"functions/approval/mod.ts\", // <-- important! Make sure this is where the logic for your function - which we will write in the next section - exists.\n  input_parameters: {\n    properties: {\n      requester_id: {\n        type: Schema.slack.types.user_id,\n        description: \"Requester\",\n      },\n      approval_channel_id: {\n        type: Schema.slack.types.channel_id,\n        description: \"Approval channel\",\n      },\n    },\n    required: [\n      \"requester_id\",\n      \"approval_channel_id\",\n    ],\n  },\n  output_parameters: {\n    properties: {\n      approved: {\n        type: Schema.types.boolean,\n        description: \"Approved\",\n      },\n      reviewer: {\n        type: Schema.slack.types.user_id,\n        description: \"Reviewer\",\n      },\n      message_ts: {\n        type: Schema.types.string,\n        description: \"Request Message TS\",\n      },\n    },\n    required: [\"approved\", \"reviewer\", \"message_ts\"],\n  },\n});\n```\n\n### Posting a message with block kit elements\n\nFirst, we need a message that has some [interactive components][interactivity]\nfrom [Block Kit][block-kit] included! We can modify one of our app's\n[functions][functions] to post a message that includes some interactive\ncomponents. Here's an example function (which we will assume exists in a\n`mod.ts` file under the `functions/approval/` subdirectory in your app) that\nposts a message with two buttons: an approval button, and a deny button:\n\n```typescript\nimport { SlackFunction } from \"deno-slack-sdk/mod.ts\";\n// ApprovalFunction is the function we defined in the previous section\nimport { ApprovalFunction } from \"./definition.ts\";\n\nexport default SlackFunction(ApprovalFunction, async ({ inputs, client }) => {\n  console.log(\"Incoming approval!\");\n\n  await client.chat.postMessage({\n    channel: inputs.approval_channel_id,\n    blocks: [{\n      \"type\": \"actions\",\n      \"block_id\": \"mah-buttons\",\n      \"elements\": [{\n        type: \"button\",\n        text: {\n          type: \"plain_text\",\n          text: \"Approve\",\n        },\n        action_id: \"approve_request\",\n        style: \"primary\",\n      }, {\n        type: \"button\",\n        text: {\n          type: \"plain_text\",\n          text: \"Deny\",\n        },\n        action_id: \"deny_request\",\n        style: \"danger\",\n      }],\n    }],\n  });\n  // Important to set completed: false! We will set the function's complete\n  // status later - in our action handler\n  return {\n    completed: false,\n  };\n});\n```\n\nThe key bit of information we need to remember before moving on to adding an\naction handler are the `action_id` and `block_id` properties defined in the\n`blocks` payload. Using these IDs, we will be able to differentiate between the\ndifferent button components that users interacted with in this message.\n\n### Adding block action handlers\n\nThe [Deno Slack SDK][sdk] - which comes bundled in your generated Run-on-Slack\napplication - provides a means for defining a handler to execute every time a\nuser interacts with an interactive Block Kit element created by your function.\n\nContinuing with our above example, we can now define a handler that will listen\nfor actions on one of the interactive components we attached to the message our\nmain function posted: either the approve button being clicked or the deny button\nbeing clicked. The code to add a Block Kit action handler is \"chained\" off of\nyour top-level function, and would look like this:\n\n```typescript\nexport default SlackFunction(ApprovalFunction, async ({ inputs, client }) => {\n  // ... the rest of your ApprovalFunction logic here ...\n}).addBlockActionsHandler(\n  [\"approve_request\", \"deny_request\"], // The first argument to addBlockActionsHandler can accept an array of action_id strings, among many other formats!\n  // Check the API reference at the end of this document for the full list of supported options\n  async ({ action, body, client }) => { // The second argument is the handler function itself\n    console.log(\"Incoming action handler invocation\", action);\n\n    const outputs = {\n      reviewer: body.user.id,\n      // Based on which button was pressed - determined via action_id - we can\n      // determine whether the request was approved or not.\n      approved: action.action_id === \"approve_request\",\n      message_ts: body.message.ts,\n    };\n\n    // Remove the button from the original message using the chat.update API\n    // and replace its contents with the result of the approval.\n    await client.chat.update({\n      channel: body.function_data.inputs.approval_channel_id,\n      ts: outputs.message_ts,\n      blocks: [{\n        type: \"context\",\n        elements: [\n          {\n            type: \"mrkdwn\",\n            text: `${\n              outputs.approved ? \" :white_check_mark: Approved\" : \":x: Denied\"\n            } by <@${outputs.reviewer}>`,\n          },\n        ],\n      }],\n    });\n\n    // And now we can mark the function as 'completed' - which is required as\n    // we explicitly marked it as incomplete in the main function handler.\n    await client.functions.completeSuccess({\n      function_execution_id: body.function_data.execution_id,\n      outputs,\n    });\n  },\n);\n```\n\nNow when you run your app and trigger your function, you have the basics in\nplace to provide interactivity between your application and users in Slack!\n\n### API reference\n\n#### `addBlockActionsHandler(constraint, handler)`\n\n```typescript\nSlackFunction({ ... }).addBlockActionsHandler({ block_id: \"mah-buttons\", action_id: \"approve_request\"}, async (ctx) => { ... });\n```\n\n`addHandler` registers a block action handler based on a `constraint` argument.\nIf any incoming actions match the `constraint`, then the specified `handler`\nwill be invoked with the action. This allows for authoring focussed,\nsingle-purpose action handlers and provides a concise but flexible API for\nregistering handlers to specific actions.\n\n`constraint` is of type [`BlockActionConstraint`][constraint], which itself can\nbe either a [`BlockActionConstraintField`](#blockactionconstraintfield) or a\n[`BlockActionConstraintObject`](#blockactionconstraintobject).\n\nIf a [`BlockActionConstraintField`](#blockactionconstraintfield) is used as the\nvalue for `constraint`, then this will be matched against the incoming action's\n`action_id` property.\n\n[`BlockActionConstraintObject`](#blockactionconstraintobject) is a more complex\nobject used to match against actions. It contains nested `block_id` and\n`action_id` properties - both optional - that are used to match against the\nincoming action.\n\n##### `BlockActionConstraintField`\n\n```typescript\ntype BlockActionConstraintField = string | string[] | RegExp;\n```\n\n- when provided as a `string`, it must match the field exactly.\n- when provided as an array of `string`s, it must match one of the array values\n  exactly.\n- when provided as a `RegExp`, the regular expression must match.\n\n###### `BlockActionConstraintObject`\n\n```typescript\ntype BlockActionConstraintObject = {\n  block_id?: BlockActionConstraintField;\n  action_id?: BlockActionConstraintField;\n};\n```\n\nThis object can contain two properties, both optional: `action_id` and/or\n`block_id`. The type of each property is\n[`BlockActionConstraintField`](#blockactionconstraintfield).\n\nIf both `action_id` and `block_id` properties exist on the `constraint`, then\nboth `action_id` and `block_id` properties _must match_ any incoming action. If\nonly one of these properties is provided, then only the provided property must\nmatch.\n\n[functions]: ./functions.md\n[api]: https://api.slack.com/methods\n[block-kit]: https://api.slack.com/block-kit\n[interactivity]: https://api.slack.com/block-kit/interactivity\n[sdk]: https://github.com/slackapi/deno-slack-sdk\n[constraint]: ../src/functions/routers/types.ts#L53-L62\n"
  },
  {
    "path": "docs/functions-suggestion-handlers.md",
    "content": "## Block Kit suggestion handlers\n\nYour application's [functions][functions] can do a wide variety of interesting\nthings: post messages, create channels, or anything available to developers via\nthe [Slack API][api]. One of the more compelling features available to app\ndevelopers is the ability to use [Block Kit][block-kit] to add richness and\ndepth to messages in Slack. Even better, [Block Kit][block-kit] supports a\nvariety of [interactive components][interactivity]! This document explores how\nto provide dynamic menu options for\n[external-data-sourced Block Kit drop-down menus](https://api.slack.com/reference/block-kit/block-elements#external_select).\n\nIf you're already familiar with the main concepts underpinning Block Kit\nSuggestion Handlers, then you may want to skip ahead to the\n[`addBlockSuggestionHandler()` method API Reference](#api-reference).\n\nWorthwhile noting that while this document covers how to render custom menu\noptions for external-data-sourced Block Kit drop-down menus, it does _not_ cover\nhow to respond to a user selecting one of the custom menu options. Do not fear,\nthough! The same approach discussed in our\n[Block Kit Action Handlers][action-handlers] document can be used to register an\naction handler to respond to drop-down menu selections.\n\n- [Block Kit suggestion handlers](#block-kit-suggestion-handlers)\n  - [Requirements](#requirements)\n  - [Posting a message with block kit elements](#posting-a-message-with-block-kit-elements)\n  - [Adding Block Suggestion Handlers](#adding-block-suggestion-handlers)\n  - [API Reference](#api-reference)\n    - [`addBlockSuggestionHandler(constraint, handler)`](#addblocksuggestionhandlerconstraint-handler)\n      - [`BlockActionConstraintField`](#blockactionconstraintfield)\n        - [`BlockActionConstraintObject`](#blockactionconstraintobject)\n\n### Requirements\n\nYour app needs to have an existing [function][functions] defined, implemented\nand working before you can add interactivity handlers like Block Kit Suggestion\nHandlers to them. Make sure you have followed our\n[functions documentation][functions] and have a function in your app ready that\nwe can expand with interactivity. Familiarity with the\n[Block Kit Actions Handlers][action-handlers] would be a huge plus as the\nhandling Block Kit Actions and handling Block Kit Suggestions is practically\nidentical.\n\nAs part of exploring how Block Kit Suggestion Handlers work, we'll walk through\nan example that posts an inspirational quote. A user would trigger our app's\nfunction, which would post a message with a drop down select menu and a button.\nThe options rendered in the select menu will be dynamically loaded from an\nexternal API. Finally, when someone has selected a drop-down menu option and\nclicked the button, our app can post the selection to the channel (note: for the\npurposes of describing how to respond to the select menu interactions, we won't\ncover handling the button click or posting the selection in this document).\n\nFor the purposes of walking through this example, let us assume the following\n[function][functions] definition (that we will store in a file called\n`definition.ts` under the `functions/quote/` subdirectory inside your app):\n\n```typescript\nimport { DefineFunction, Schema } from \"deno-slack-sdk/mod.ts\";\n\nexport const QuoteFunction = DefineFunction({\n  callback_id: \"quote\",\n  title: \"Inspire Me\",\n  description: \"Get an inspirational quote\",\n  source_file: \"functions/quote/mod.ts\", // <-- important! Make sure this is where the logic for your function - which we will write in the next section - exists.\n  input_parameters: {\n    properties: {\n      requester_id: {\n        type: Schema.slack.types.user_id,\n        description: \"Requester\",\n      },\n      channel_id: {\n        type: Schema.slack.types.channel_id,\n        description: \"Channel\",\n      },\n    },\n    required: [\n      \"requester_id\",\n      \"channel_id\",\n    ],\n  },\n  output_parameters: {\n    properties: {\n      quote: {\n        type: Schema.types.string,\n        description: \"Quote\",\n      },\n    },\n    required: [\"quote\"],\n  },\n});\n```\n\n### Posting a message with block kit elements\n\nFirst, we need a message that has some [interactive components][interactivity]\nfrom [Block Kit][block-kit] included! We can modify one of our app's\n[functions][functions] to post a message that includes some interactive\ncomponents - including our external select drop down menu. Here's an example\nfunction (which we will assume exists in a `mod.ts` file under the\n`functions/quote/` subdirectory in your app) that posts a message with a\nexternal data select drop down menu:\n\n```typescript\nimport { SlackFunction } from \"deno-slack-sdk/mod.ts\";\n// QuoteFunction is the function we defined in the previous section\nimport { QuoteFunction } from \"./definition.ts\";\n\nexport default SlackFunction(QuoteFunction, async ({ inputs, client }) => {\n  console.log(\"Incoming quote request!\");\n\n  await client.chat.postMessage({\n    channel: inputs.channel_id,\n    blocks: [{\n      \"type\": \"actions\",\n      \"block_id\": \"so-inspired\",\n      \"elements\": [{\n        type: \"external_select\",\n        placeholder: {\n          type: \"plain_text\",\n          text: \"Inspire\",\n        },\n        action_id: \"ext_select_input\",\n      }, {\n        type: \"button\",\n        text: {\n          type: \"plain_text\",\n          text: \"Post\",\n        },\n        action_id: \"post_quote\",\n      }],\n    }],\n  });\n  // Important to set completed: false! We should set the function's complete\n  // status later - in the action handler responding to the button click\n  return {\n    completed: false,\n  };\n});\n```\n\nThe key bit of information we need to remember before moving on to adding a\nsuggestion handler are the `action_id` and `block_id` properties defined in the\n`blocks` payload. Using these IDs, we will be able to differentiate between the\ndifferent Block Kit components that users interacted with in this message.\n\n### Adding Block Suggestion Handlers\n\nThe [Deno Slack SDK][sdk] - which comes bundled in your generated Run-on-Slack\napplication - provides a means for defining a handler to execute every time a\nuser interacts with an interactive Block Kit element created by your function.\n\nContinuing with our above example, we can now define a handler that will listen\nfor interactions with the external data drop down menu. The code to add a Block\nKit suggestion handler is \"chained\" off of your top-level function, and would\nlook like this:\n\n```typescript\nexport default SlackFunction(QuoteFunction, async ({ inputs, client }) => {\n  // ... the rest of your QuoteFunction logic here ...\n}).addBlockSuggestionHandler(\n  \"ext_select_input\", // The first argument to addBlockActionsHandler can accept an action_id string, among many other formats!\n  // Check the API reference at the end of this document for the full list of supported options\n  async ({ body, client }) => { // The second argument is the handler function itself\n    console.log(\"Incoming suggestion handler invocation\", body);\n    // Fetch some inspirational quotes\n    const apiResp = await fetch(\n      \"https://motivational-quote-api.herokuapp.com/quotes\",\n    );\n    const quotes = await apiResp.json();\n    console.log(\"Returning\", quotes.length, \"quotes\");\n    const opts = {\n      \"options\": quotes.map((q) => ({\n        value: `${q.id}`,\n        text: { type: \"plain_text\", text: q.quote.slice(0, 70) },\n      })),\n    };\n    return opts;\n  },\n);\n```\n\n### API Reference\n\n#### `addBlockSuggestionHandler(constraint, handler)`\n\n```typescript\nSlackFunction({ ... }).addBlockSuggestionHandler({ block_id: \"mah-buttons\", action_id: \"approve_request\"}, async (ctx) => { ... });\n```\n\n`addBlockSuggestionHandler` registers a block suggestion handler based on a\n`constraint` argument. If any incoming suggestion events match the `constraint`,\nthen the specified `handler` will be invoked with the suggestion payload. This\nallows for authoring focussed, single-purpose suggestion handlers and provides a\nconcise but flexible API for registering handlers to specific\nexternal-data-sourced drop down menu.\n\n`constraint` is of type [`BlockActionConstraint`][constraint], which itself can\nbe either a [`BlockActionConstraintField`](#blockactionconstraintfield) or a\n[`BlockActionConstraintObject`](#blockactionconstraintobject).\n\nIf a [`BlockActionConstraintField`](#blockactionconstraintfield) is used as the\nvalue for `constraint`, then this will be matched against the incoming action's\n`action_id` property.\n\n[`BlockActionConstraintObject`](#blockactionconstraintobject) is a more complex\nobject used to match against actions. It contains nested `block_id` and\n`action_id` properties - both optional - that are used to match against the\nincoming suggestion.\n\n##### `BlockActionConstraintField`\n\n```typescript\ntype BlockActionConstraintField = string | string[] | RegExp;\n```\n\n- when provided as a `string`, it must match the field exactly.\n- when provided as an array of `string`s, it must match one of the array values\n  exactly.\n- when provided as a `RegExp`, the regular expression must match.\n\n###### `BlockActionConstraintObject`\n\n```typescript\ntype BlockActionConstraintObject = {\n  block_id?: BlockActionConstraintField;\n  action_id?: BlockActionConstraintField;\n};\n```\n\nThis object can contain two properties, both optional: `action_id` and/or\n`block_id`. The type of each property is\n[`BlockActionConstraintField`](#blockactionconstraintfield).\n\nIf both `action_id` and `block_id` properties exist on the `constraint`, then\nboth `action_id` and `block_id` properties _must match_ any incoming suggestion.\nIf only one of these properties is provided, then only the provided property\nmust match.\n\n[functions]: ./functions.md\n[action-handlers]: ./functions-action-handlers.md\n[api]: https://api.slack.com/methods\n[block-kit]: https://api.slack.com/block-kit\n[interactivity]: https://api.slack.com/block-kit/interactivity\n[sdk]: https://github.com/slackapi/deno-slack-sdk\n[constraint]: ../src/functions/routers/types.ts#L53-L62\n"
  },
  {
    "path": "docs/functions-view-handlers.md",
    "content": "## View Handlers\n\nYour application's [functions][functions] can do a wide variety of interesting\nthings: post messages, create channels, or anything available to developers via\nthe [Slack API][api]. They can even include\n[interactive components][interactivity] or pop up a [Modal][modals].\n[Modals][modals] are composed of up to three [Views][views]. These\n[Views][views] can contain form inputs or\n[interactive components][interactivity]. [Views][views] themselves may also\n[trigger events][view-events]. This document explores the APIs available to app\ndevelopers building Run-On-Slack applications to create [modals][modals]\ncomposed of [views][views] and how applications can respond to the\n[view submission and closed events][view-events] they can trigger.\n\nIf you're already familiar with the main concepts underpinning View Handlers,\nthen you may want to skip ahead to the [API Reference](#api-reference).\n\n- [View Handlers](#view-handlers)\n  - [Requirements](#requirements)\n  - [Opening a view](#opening-a-view)\n    - [Opening a view from a custom function](#opening-a-view-from-a-custom-function)\n    - [Opening a view from a block action handler](#opening-a-view-from-a-block-action-handler)\n  - [Adding view handlers](#adding-view-handlers)\n  - [API reference](#api-reference)\n    - [`addViewSubmissionHandler(constraint, handler)`](#addviewsubmissionhandlerconstraint-handler)\n      - [`addViewClosedHandler(constraint, handler)`](#addviewclosedhandlerconstraint-handler)\n\n### Requirements\n\nThis functionality requires at least version 0.2.0 of the\n[`deno-slack-sdk`][sdk].\n\nYour app needs to have an existing [function][functions] defined, implemented\nand working before you can add interactivity handlers like View Handlers or\n[Block Kit Action Handlers][action-handlers] to them. Make sure you have\nfollowed our [functions documentation][functions] and have a function in your\napp ready that we can expand with a View Handler.\n\nAs part of exploring how View Handlers work, we'll walk through a simple diary\nflow example. It is nothing more than a contrived example aimed at showing off\nthe APIs. A user would trigger our app's function, which would open a view with\na single text input. If the view is submitted with content, the application will\nsend the user a DM with their inputted content. If the view is closed, the\napplication will send the user a DM encouraging them not to give up on their\ndiarying habit.\n\nFor the purposes of walking through this approval flow example, let us assume\nthe following [function][functions] definition (that we will store in a file\ncalled `definition.ts` under the `functions/diary/` subdirectory inside your\napp):\n\n```typescript\nimport { DefineFunction, Schema } from \"deno-slack-sdk/mod.ts\";\n\nexport const DiaryFunction = DefineFunction({\n  callback_id: \"diary\",\n  title: \"Diary\",\n  description: \"Write a diary entry\",\n  source_file: \"functions/diary/mod.ts\", // <-- important! Make sure this is where the logic for your function - which we will write in the next section - exists.\n  input_parameters: {\n    properties: {\n      interactivity: { // <-- important! This gives Slack a hint that your function will create interactive elements like views\n        type: Schema.slack.types.interactivity,\n      },\n      channel_id: {\n        type: Schema.slack.types.channel_id,\n      },\n    },\n    required: [\"interactivity\"],\n  },\n  output_parameters: {\n    properties: {},\n    required: [],\n  },\n});\n```\n\n### Opening a view\n\n[Opening a view via the `views.open` API][views-open] and\n[pushing a new view onto the view stack via the `views.push` API][views-push]\nboth require the use of a [`trigger_id`][trigger-ids]. These are identifiers\nrepresenting specific user interactions. Slack uses these to prevent\napplications from haphazardly opening modals in users' faces willy-nilly.\nWithout a `trigger_id`, your application can't create a modal and open a view.\nFYI `trigger_id`s are also known as `interactivity_pointer`s.\n\nAs such, there are two ways to open a view from inside a Run-On-Slack\napplication: doing so\n[from a function directly](#opening-a-view-from-a-function) vs. doing so\n[from a Block Action Handler](#opening-a-view-from-a-block-action-handler). The\nsections covering each approach below discuss how to retrieve the `trigger_id`\nin each scenario.\n\nWe will explore implementing our contrived example above by opening a view from\na function. In a section further below, we will also cover\n[opening a view from a Block Action Handler](#opening-a-view-from-a-block-action-handler).\n\n#### Opening a view from a custom function\n\nAs mentioned in the previous section, we need to have a `trigger_id` handy in\norder to open a view. This is why we defined an `interactivity` input in our\nfunction definition earlier: this input will magically provide us with a\n`trigger_id`. The property to use as a `trigger_id` exists on inputs with the\ntype `Schema.slack.types.interactivity` under the `interactivity_pointer`\nproperty. Check out the code below for an example:\n\n```typescript\nimport { SlackFunction } from \"deno-slack-sdk/mod.ts\";\n// DiaryFunction is the function we defined in the previous section\nimport { DiaryFunction } from \"./definition.ts\";\n\nexport default SlackFunction(DiaryFunction, async ({ inputs, client }) => {\n  console.log('Someone might want to write a diary entry...');\n\n  await client.views.open({\n    trigger_id: inputs.interactivity.interactivity_pointer,\n    view: {\n      \"type\": \"modal\",\n      \"title\": {\n        \"type\": \"plain_text\",\n        \"text\": \"Modal title\",\n      },\n      \"blocks\": [\n        {\n          \"type\": \"input\",\n          \"block_id\": \"section1\",\n          \"element\": {\n            \"type\": \"plain_text_input\",\n            \"action_id\": \"diary_input\",\n            \"multiline\": true,\n            \"placeholder\": {\n              \"type\": \"plain_text\",\n              \"text\": \"What is on your mind today?\",\n            },\n          },\n          \"label\": {\n            \"type\": \"plain_text\",\n            \"text\": \"Diary Entry\",\n          },\n          \"hint\": {\n            \"type\": \"plain_text\",\n            \"text\": \"Don't worry, no one but you will see this.\",\n          },\n        },\n      ],\n      \"close\": {\n        \"type\": \"plain_text\",\n        \"text\": \"Cancel\",\n      },\n      \"submit\": {\n        \"type\": \"plain_text\",\n        \"text\": \"Save\",\n      },\n      \"callback_id\": \"view_identifier_12\", // <-- remember this ID, we will use it to route events to handlers!\n      \"notify_on_close\": true, // <-- this must be defined in order to trigger `view_closed` events!\n    },\n  });\n  // Important to set completed: false! We will set the function's complete\n  // status later - in our view submission handler\n  return {\n    completed: false,\n  };\n};\n```\n\n#### Opening a view from a block action handler\n\nIf [Block Kit Action Handlers][action-handlers] is a foreign concept to you, we\nrecommend first checking out [its documentation][action-handlers] before\nventuring deeper into this section.\n\nSimilarly to opening a view from a function, doing so from a\n[Block Action Handler][action-handlers] is straightforward though slightly\ndifferent. It is important to remember that `trigger_id`s represent a unique\nuser interaction with a particular interactive component within Slack's UI. As\nsuch, when responding to a Block Kit Action interactive component, we don't want\nto use your function's `inputs` to retrieve the `interactivity_pointer`, as we\ndid in the previous section, but rather, we want to retrieve a `trigger_id` that\nis unique to the Block Kit interactive component.\n\nLuckily for us, this is provided as a parameter to Block Kit Action Handlers!\nYou can use the value of `body.interactivity.interactivity_pointer` within an\naction handler to open a view, like so:\n\n```typescript\nexport default SlackFunction(DiaryFunction, async ({ inputs, client }) => {\n  // ... the rest of your DiaryFunction logic here ...\n}).addBlockActionsHandler(\n  \"deny_request\",\n  async ({ action, body, client }) => {\n    await client.views.open({\n      trigger_id: body.interactivity.interactivity_pointer,\n      view: {/* your view object goes here */},\n    });\n  },\n);\n```\n\n### Adding view handlers\n\nThe [Deno Slack SDK][sdk] - which comes bundled in your generated Run-on-Slack\napplication - provides a means for defining handlers to execute every time a\nuser interacts with a view. In this way you can route view-related events to\nspecific handlers inside your application. The key identifier that we'll need to\nkeep handy is the `callback_id` we assigned to any views we created. This ID\nwill be the property that determines which view event handler will respond to\nincoming view events.\n\nContinuing with our above example, we can now define handlers that will listen\nfor view submission and closed events and respond accordingly. The code to add\nview handlers is \"chained\" off of your top-level function, and would look like\nthis:\n\n```typescript\nexport default SlackFunction(DiaryFunction, async ({ inputs, client }) => {\n  // ... the rest of your DiaryFunction logic here ...\n}).addViewSubmissionHandler(\n  /view/, // The first argument to any of the addView*Handler methods can accept a string, array of strings, or RegExp.\n  // This first argument will be used to match the view's `callback_id`\n  // Check the API reference at the end of this document for the full list of supported options\n  async ({ view, body, token }) => { // The second argument is the handler function itself\n    console.log(\"Incoming view submission handler invocation\", body);\n  },\n)\n  .addViewClosedHandler(\n    /view/,\n    async ({ view, body, token }) => {\n      console.log(\"Incoming view closed handler invocation\", body);\n    },\n  );\n```\n\nImportantly, more complex applications will likely be modifying views as users\ninteract with them: updating the view contents (to e.g. add new form fields),\nperhaps pushing a new view onto the view stack to introduce a new UI to the\nuser, maybe reporting errors to the user for some manner of faulty interaction,\nor even clearing the entire view stack altogether. All of these modal\ninteraction responses are\n[covered in depth on our API documentation site][modifying] - make sure to spend\nthe time to understand the concepts presented there.\n\nIn particular, modal interactions can be responded to by using the API, or by\nreturning particularly-crafted responses directly from inside the view handlers.\nOn our [API site detailing view modification][modifying], these returned view\nhandler responses are called `response_action`s.\n\nAs an example, consider the following two code snippets. They yield identical\nbehavior!\n\n```typescript\nexport default SlackFunction(DiaryFunction, async ({ inputs, client }) => {\n  // ... the rest of your DiaryFunction logic here ...\n}).addViewSubmissionHandler(/view/, async ({ client, body }) => {\n  // A view submission handler that pushes a new view using the API\n  await client.views.push({\n    trigger_id: body.trigger_id,\n    view: {/* your view object goes here */},\n  });\n}).addSubmissionHandler(/view/, async () => {\n  // A view submission handler that pushes a new view using the `response_action`\n  return {\n    response_action: \"push\",\n    view: {/* your view object goes here */},\n  };\n});\n```\n\n### API reference\n\n#### `addViewSubmissionHandler(constraint, handler)`\n\n```typescript\nSlackFunction({ ... }).addViewSubmissionHandler(\"my_view_callback_id\", async (ctx) => { ... });\n```\n\n`addViewSubmissionHandler` registers a view handler based on a `constraint`\nargument. If any incoming [`view_submission` event][view-events] matches the\n`constraint`, then the specified `handler` will be invoked with the event\npayload. This allows for authoring focussed, single-purpose view handlers and\nprovides a concise but flexible API for registering handlers to specific view\ninteractions.\n\n`constraint` can be either a string, an array of strings, or a regular\nexpression.\n\n- A simple string `constraint` must match a view's `callback_id` exactly.\n- An array of strings `constraint` must match a view's `callback_id` to any of\n  the strings in the array.\n- A regular expression `constraint` must match a view's `callback_id`.\n\n##### `addViewClosedHandler(constraint, handler)`\n\n```typescript\nSlackFunction({ ... }).addViewClosedHandler(\"my_view_callback_id\", async (ctx) => { ... });\n```\n\n⚠️ IMPORTANT: you must set a view's `notify_on_close` property to `true` for the\n`view_closed` event to trigger; by default this property is `false`. See the\n[View reference documentation - in particular the Fields section][view-ref] for\nmore information.\n\n`addViewClosedHandler` registers a view handler based on a `constraint`\nargument. If any incoming [`view_closed` event][view-events] matches the\n`constraint`, then the specified `handler` will be invoked with the event\npayload. This allows for authoring focussed, single-purpose view handlers and\nprovides a concise but flexible API for registering handlers to specific view\ninteractions.\n\n`constraint` can be either a string, an array of strings, or a regular\nexpression.\n\n- A simple string `constraint` must match a view's `callback_id` exactly.\n- An array of strings `constraint` must match a view's `callback_id` to any of\n  the strings in the array.\n- A regular expression `constraint` must match a view's `callback_id`.\n\n[functions]: ./functions.md\n[action-handlers]: ./functions-action-handlers.md\n[api]: https://api.slack.com/methods\n[block-kit]: https://api.slack.com/block-kit\n[interactivity]: https://api.slack.com/block-kit/interactivity\n[sdk]: https://github.com/slackapi/deno-slack-sdk\n[modals]: https://api.slack.com/surfaces/modals\n[views]: https://api.slack.com/surfaces/modals/using\n[modifying]: https://api.slack.com/surfaces/modals/using#modifying\n[trigger-ids]: https://api.slack.com/interactivity/handling#modal_responses\n[view-events]: https://api.slack.com/reference/interaction-payloads/views\n[views-methods]: https://api.slack.com/methods?filter=views\n[views-open]: https://api.slack.com/methods/views.open\n[views-update]: https://api.slack.com/methods/views.update\n[views-push]: https://api.slack.com/methods/views.push\n[view-ref]: https://api.slack.com/reference/surfaces/views\n"
  },
  {
    "path": "docs/functions.md",
    "content": "## Custom functions\n\nFunctions are the core of your Slack app: they accept one or more input\nparameters, execute some logic and return one or more output parameters.\n\nFunctions can optionally define different kinds of Interactivity Handlers. If\nyour function creates messages or opens views, then it may need to define one or\nmore interactivity handlers to respond to user interactions with these\ninteractive components. Run-on-Slack applications support the following\ninteractivity handlers, follow the links to get more information about how each\nof them work:\n\n- [Block Kit Action Handlers][action-handlers]: Handle events from interactive\n  [Block Kit][block-kit] components that you can use in messages like\n  [Buttons, Menus and Date/Time Pickers](https://api.slack.com/block-kit/interactivity)\n- [View Handlers][view-handlers]: Handle events triggered from [Modals][modals],\n  which are composed of [Views][views].\n- [Block Kit Suggestion Handlers][suggest-handlers]: Handle events from\n  [external-data-sourced Block Kit select menus](https://api.slack.com/reference/block-kit/block-elements#external_select)\n\n### Defining a custom function\n\nFunctions can be defined with the top level `DefineFunction` export. Below is an\nexample function that turns a `name` input parameter into a dinosaur name:\n\n```ts\nimport { DefineFunction, Schema } from \"slack-cloud-sdk/mod.ts\";\n\nexport const DinoFunction = DefineFunction({\n  callback_id: \"dino\",\n  title: \"Dino\",\n  description: \"Turns a name into a dinosaur name\",\n  source_file: \"functions/dino.ts\",\n  input_parameters: {\n    name: {\n      type: Schema.types.string,\n      description: \"The provided name\",\n    },\n  },\n  output_parameters: {\n    dinoname: {\n      type: Schema.types.string,\n      description: \"The new dinosaur name\",\n    },\n  },\n});\n```\n\nLet's go over each of the arguments that must be provided to `DefineFunction`.\n\n#### Function definition\n\nThe passed argument is the `definition` of the function, an object with a few\nproperties that help to describe and define the function in more detail. In\nparticular, the required properties of the object are:\n\n- `callback_id`: A unique string identifier representing the function (`\"dino\"`\n  in the above example). It must be unique in your application; no other\n  functions may be named identically. Changing a function's `callback_id` is not\n  recommended as it means that the function will be removed from the app and\n  created under the new `callback_id`, which will break any workflows\n  referencing the old function.\n- `title`: A pretty string to nicely identify the function.\n- `description`: A short-and-sweet string description of your function\n  succinctly summarizing what your function does.\n- `source_file`: The relative path from the project root to the function\n  `handler` file.\n- `input_parameters`: Itself an object which describes one or more input\n  parameters that will be available to your function. Each top-level property of\n  this object defines the name of one input parameter which will become\n  available to your function. The value for this property needs to be an object\n  with further sub-properties:\n  - `type`: The type of the input parameter. The supported types are `string`,\n    `integer`, `boolean`, `number`, `object` and `array`.\n  - `description`: A string description of the input parameter.\n- `output_parameters`: Itself an object which describes one or more output\n  parameters that will be returned by your function. This object follows the\n  exact same pattern as `input_parameters`: top-level properties of the object\n  define output parameter names, with the property values being an object that\n  further describes the `type` and `description` of individual output\n  parameters.\n\n### Adding runtime logic to your custom function\n\nNow that you have defined your function's input and output parameters, it's time\nto define the body of your function.\n\nFirst, create a new file at the location set on the `source_file` parameter of\nyour function definition. Next, let's add code for your function! You will want\nto `export default` an instance of `SlackFunction`, like so:\n\n```typescript\nimport { SlackFunction } from \"deno-slack-sdk/mod.ts\";\n\nexport default SlackFunction(\n  // Pass along the function definition you created earlier using `DefineFunction`\n  DinoFunction,\n  ({ inputs }) => { // Provide any context properties, like `inputs`, `env`, or `token`\n    // Implement your function\n    const { name } = inputs;\n    const dinoname = `${name}asaurus`;\n\n    // Don't forget any required output parameters\n    return { outputs: { dinoname } };\n  },\n);\n```\n\nKey points:\n\n- The function takes a single argument, referred to as the\n  [function \"context\"](#function-handler-context).\n- The function [returns an object](#function-return-object).\n\n#### Custom function handler context\n\nThe single argument to your function is an object composed of several properties\nthat may be useful to leverage during your function's execution:\n\n- `env`: represents environment variables available to your function's execution\n  context.\n- `inputs`: an object containing the input parameters you defined as part of\n  your function definition. In the example above, the `name` input parameter is\n  available on the `inputs` property of our function handler context.\n- `client`: An API client ready for use in your function. An instance of the\n  `deno-slack-api` library.\n- `token`: your application's access token.\n- `team_id`: the encoded team (a.k.a. Slack workspace) ID, i.e. T12345.\n- `enterprise_id`: the encoded enterprise ID, i.e. E12345. If the Slack\n  workspace the function executes in is not a part of an enterprise grid, then\n  this value will be the empty string (`\"\"`).\n- `event`: an object containing the full incoming event details.\n\n##### Custom function return object\n\nThe object returned by your function that supports the following properties:\n\n- `error`: a string indicating the error that was encountered. If present, the\n  function will return an error regardless of what is passed to `outputs`.\n- `outputs`: an object that exactly matches the structure of your function\n  definition's `output_parameters`. This is required unless an `error` is\n  returned.\n- `completed`: a boolean indicating whether or not the function is completed.\n  This defaults to `true`.\n\n### Adding custom functions to the manifest\n\nOnce you have defined a function, don't forget to include it in your\n[`Manifest`][manifest] definition!\n\n```ts\nimport { ReverseString } from \"./functions/reverse_definition.ts\";\n\nManifest({\n  name: \"heuristic-tortoise\",\n  description: \"A demo showing how to use custom functions\",\n  icon: \"assets/icon.png\",\n  botScopes: [\"commands\", \"chat:write\", \"chat:write.public\"],\n  functions: [ReverseString], // <-- don't forget this!\n});\n```\n\n[manifest]: ./manifest.md\n[action-handlers]: ./functions-action-handlers.md\n[view-handlers]: ./functions-view-handlers.md\n[suggest-handlers]: ./functions-suggestion-handlers.md\n[block-kit]: https://api.slack.com/block-kit\n[modals]: https://api.slack.com/surfaces/modals\n[views]: https://api.slack.com/surfaces/modals/using\n"
  },
  {
    "path": "docs/manifest.md",
    "content": "## Manifest\n\nA Manifest defines your entire Slack application, from its core properties like\nits name and description to its behavioural aspects like what\n[functions][functions] it contains.\n\n### Defining a manifest\n\nA Manifest can be defined with the top level `Manifest` export. Below is an\nexample, taken from the template application:\n\n```ts\nimport { Manifest } from \"slack-cloud-sdk/mod.ts\";\nimport { ReverseString } from \"./functions/reverse_definition.ts\";\n\nexport default Manifest({\n  name: \"heuristic-tortoise-312\",\n  description: \"A demo showing how to use Slack functions\",\n  icon: \"assets/icon.png\",\n  botScopes: [\"commands\", \"chat:write\", \"chat:write.public\"],\n  functions: [ReverseString],\n  datastores: [],\n  outgoing_domains: [],\n});\n```\n\nThe object passed into the `Manifest` method is the type\n[`SlackManifestType`][manifest-type]. Check out [its definition][manifest-type]\nfor the full list of attributes it supports, but the minimum required properties\nare listed in the table below:\n\n| Property      | Type          | Description                                                               |\n| ------------- | ------------- | ------------------------------------------------------------------------- |\n| `name`        | string        | Your Slack application name.                                              |\n| `description` | string        | A short sentence describing your application.                             |\n| `icon`        | string        | A relative path to an image asset to use for the application's icon.      |\n| `botScopes`   | Array<string> | A list of [scopes][scopes], or permissions, the bot requires to function. |\n\nFurthermore, to set up how your application works, you would create\n[functions][functions], and register them in the Manifest using the `functions`\nproperty of [`SlackManifestType`][manifest-type] argument used when creating a\nnew `Manifest`.\n\n[functions]: ./functions.md\n[manifest-type]: ../src/types.ts#L13\n[scopes]: https://api.slack.com/scopes\n"
  },
  {
    "path": "docs/types.md",
    "content": "## Types\n\nCustom Types provide a way to introduce reusable, sharable types to Apps.\n\n### Defining a type\n\nTypes can be defined with the top level `DefineType` export. Below is an example\nof setting up a custom Type used for incident management.\n\n```ts\nconst IncidentType = DefineType({\n  name: \"incident\",\n  title: \"Incident Ticket\",\n  description: \"Use this to enter an Incident Ticket\",\n  type: Schema.types.object,\n  properties: {\n    id: {\n      type: Schema.types.string,\n      minLength: 3,\n    },\n    title: {\n      type: Schema.types.string,\n    },\n    summary: {\n      type: Schema.types.string,\n    },\n    severity: {\n      type: Schema.types.string,\n    },\n    date_created: {\n      type: Schema.types.number,\n    },\n  },\n  required: [],\n});\n```\n\n### Registering a type with the App\n\nTo register the newly defined type, add it to the array assigned to the `types`\nparameter while defining the [`Manifest`][manifest].\n\nNote: All Custom Types **must** be registered to the [Manifest][manifest] in\norder for them to be used, but any types referenced by existing\n[`functions`][functions], [`workflows`][workflows], [`datastores`][datastores],\nor other types will be registered automatically.\n\n```ts\nManifest({\n  ...\n  types: [IncidentType],\n});\n```\n\n### Referencing types\n\nTo use a type as a [function][functions] parameter, set the parameter's `type`\nproperty to the Type it should reference.\n\n```js\ninput_parameters: {\n  incident: : {\n    title: 'A Special Incident',\n    type: IncidentType\n  }\n   ...\n}\n```\n\n_In the provided example the title from the Custom Type is being overridden_\n\n[functions]: ./functions.md\n[manifest]: ./manifest.md\n[datastores]: ./datastores.md\n[workflows]: ./workflows.md\n"
  },
  {
    "path": "docs/workflows.md",
    "content": "## Workflows\n\nWorkflows can be defined and included in your [manifest][manifest]. A workflow\nitself has several pieces of metadata, such as a unique `callback_id`, a `title`\nand a `description`. It can also include `input_parameters` just like a\n[function][function]. Key to a workflow is a series of steps, each of which are\na function that can be passed dynamic data to their inputs through referencing\nworkflow inputs, or outputs from previous steps. Let's take a look at an\nexample.\n\n```ts\nimport { DefineWorkflow, Manifest, Schema } from \"slack-cloud-sdk/mod.ts\";\n\nconst workflow = DefineWorkflow({\n  callback_id: \"my_workflow\",\n  title: \"My Workflow\",\n  description: \"A sample workflow\",\n  input_parameters: {\n    properties: {\n      a_string: {\n        type: Schema.types.string,\n      },\n      a_channel: {\n        type: Schema.slack.types.channel_id,\n      }\n    },\n    required: [\"a_string\", \"a_channel\"],\n  },\n});\n\n// register your workflow in your manifest\nexport default Manifest({\n  ...,\n  workflows: [\n    workflow,\n  ]\n});\n```\n\nA workflow by itself isn't of much use, and isn't valid, until you add some\nsteps. Let's use the `DinoFunction` we've defined over in the\n[functions][function] example as one of our steps. The `DinoFunction` has a\nsingle `input_parameter` of `name` that we'll need to pass it. We'll use our\n`a_string` workflow `input_parameter` as the value for this, but you could just\nas easily pass a hard-coded value to any step input parameter as well.\n\n```ts\nimport { DefineWorkflow } from \"slack-cloud-sdk/mod.ts\";\nimport { DinoFunction } from '../functions/dino.ts';\n\nconst workflow = DefineWorkflow({...});\n\nconst step1 = workflow.addStep(DinoFunction, {\n  name: workflow.inputs.a_string,\n});\n```\n\nGreat, we've got a single step workflow that takes a string, and turns it into a\ndinosaur name via our `DinoFunction`. It would be nice to see what that looks\nlike, so lets add another step that sends that value as a message somewhere. For\nthis, we can use one of Slack's functions. Notice how we can also use our\nreference to `step1` to access an output called `dinoname` that the\n`DinoFunction` produces.\n\n```ts\nconst step1 = workflow.addStep(...);\n\nworkflow.addStep(\"slack#/functions/send_message\", {\n  channel: workflow.inputs.a_channel,\n  message: `A dinosaur name: ${step1.outputs.dinoname}`,\n});\n```\n\nYou'll notice the first parameter to `addStep()` here is a string, instead of\nsomething like our `DinoFunction`. This is because we're referencing a step\nproduced outside of our app, in this case by `slack`. We're using the string\nreference of `\"slack#/functions/send_message\"` to identify the function we're\nadding as a step. In fact, you can do the same thing with your own functions by\ncreating what's called a local reference string to your own app's function. This\nuses your `callback_id`, and would look like `\"#/functions/my_workflow\"`. If we\nadded our function as a step that way, it would look like this:\n\n```ts\nconst step1 = workflow.addStep(\"#/functions/my_workflow\", {\n  name: workflow.inputs.a_string,\n});\n```\n\nThe big difference here is you won't get some of the automatic typing of\n`inputs` and `outputs` for that step, but you can still reference them as long\nas you follow the definition of that function.\n\n### Auto-Registration of workflow dependencies\n\nWhen a workflow is registered on your `Manifest()` any `functions` it uses as\nsteps, or custom `types` used as `input_parameters` to the workflow or functions\nit references are automatically registered in your manifest. This can save you\nfrom having to register each function and type that a workflow might use, and\njust register the workflow.\n\n[manifest]: ./manifest.md\n[function]: ./functions.md\n"
  },
  {
    "path": "scripts/build_npm.ts",
    "content": "// ex. scripts/build_npm.ts\nimport { build, emptyDir } from \"jsr:@deno/dnt@0.42.1\";\n\nawait emptyDir(\"./npm\");\n\nawait build({\n  typeCheck: false,\n  test: false,\n  entryPoints: [\"./src/mod.ts\"],\n  outDir: \"./npm\",\n  // ensures that the emitted package is compatible with node v14 later\n  compilerOptions: {\n    lib: [\"ES2022.Error\"], // fix ErrorOptions not exported in ES2020\n    target: \"ES2020\",\n  },\n  shims: {\n    // see JS docs for overview and more options\n    deno: true,\n    // Shim fetch, File, FormData, Headers, Request, and Response\n    undici: true,\n  },\n  package: {\n    // package.json properties\n    name: \"@slack/deno-slack-sdk\",\n    version: Deno.args[0],\n    description: \"Official library for using Deno Slack SDK in node Slack apps\",\n    license: \"MIT\",\n    repository: {\n      type: \"git\",\n      url: \"git+https://github.com/slackapi/deno-slack-sdk.git\",\n    },\n    bugs: {\n      url: \"https://github.com/slackapi/deno-slack-sdk/issues\",\n    },\n    // sets the minimum engine to node v14\n    // as of writing, dnt transpilation-generated code\n    // seems to only be able to successfully compile as far back ES2020\n    engines: {\n      \"node\": \">=14.20.1\",\n      \"npm\": \">=6.14.15\",\n    },\n  },\n});\n\n// post build steps\nDeno.copyFileSync(\"README.md\", \"npm/README.md\");\n"
  },
  {
    "path": "scripts/bundle.ts",
    "content": "import * as esbuild from \"npm:esbuild@0.25.5\";\nimport { denoPlugins } from \"jsr:@luca/esbuild-deno-loader@0.11.1\";\nimport { join } from \"jsr:@std/path@1.1.0\";\n\nasync function bundle(options: {\n  target: string;\n}): Promise<Uint8Array> {\n  const cwd = Deno.cwd();\n  try {\n    // esbuild configuration options https://esbuild.github.io/api/#overview\n    const result = await esbuild.build({\n      entryPoints: [join(cwd, \"src/mod.ts\")],\n      platform: \"browser\",\n      target: options.target,\n      format: \"esm\", // esm format stands for \"ECMAScript module\"\n      bundle: true, // inline any imported dependencies into the file itself\n      absWorkingDir: cwd, // root of this project\n      write: false, // Favor returning the contents\n      outdir: \"out\", // Nothing is being written to file here\n      plugins: [\n        ...denoPlugins({ configPath: join(cwd, \"deno.jsonc\") }),\n      ],\n    });\n    return result.outputFiles[0].contents;\n  } finally {\n    esbuild.stop();\n  }\n}\n\nconsole.log(\"Building bundle for target: deno1\");\nconst deno1Bundle = await bundle({ target: \"deno1\" });\nconst deno1Content = new TextDecoder().decode(deno1Bundle);\nconsole.log(`Bundle for deno1 built successfully!`);\nconsole.log(deno1Content);\n\nconsole.log(\"Building bundle for target: deno2\");\nconst deno2Bundle = await bundle({ target: \"deno2\" });\nconst deno2Content = new TextDecoder().decode(deno2Bundle);\nconsole.log(`Bundle for deno2 built successfully!`);\nconsole.log(deno2Content);\n"
  },
  {
    "path": "scripts/imports/update.ts",
    "content": "import { parseArgs } from \"jsr:@std/cli@1.0.17/parse-args\";\nimport { dirname, join, relative } from \"jsr:@std/path@1.1.0\";\n\nconst flags = parseArgs(Deno.args, {\n  string: [\"import-file\", \"sdk\"],\n  default: {\n    \"import-file\": `${Deno.cwd()}/deno.jsonc`,\n    \"sdk\": \"./deno-slack-sdk/\",\n  },\n});\n\nconst importFilePath = await Deno.realPath(flags[\"import-file\"]);\nconst importFileDir = dirname(importFilePath);\nconst sdkDir = await Deno.realPath(flags.sdk);\n\nconst importFileJsonIn = await Deno.readTextFile(importFilePath);\nconsole.log(`content in ${importFilePath}:`, importFileJsonIn);\n\nconst sdkPackageSpecifier = join(\n  relative(importFileDir, sdkDir),\n  \"/src/\",\n);\n\nconst importMap = JSON.parse(importFileJsonIn);\nimportMap[\"imports\"][\"deno-slack-sdk/\"] = sdkPackageSpecifier;\n\nconst importMapJsonOut = JSON.stringify(importMap);\nconsole.log(\"`import file` out content:\", importMapJsonOut);\nawait Deno.writeTextFile(importFilePath, importMapJsonOut);\n"
  },
  {
    "path": "src/README.md",
    "content": "<h1 align=\"center\">\n  Deno Slack SDK\n  <br>\n</h1>\n\n<p align=\"center\">\n    <img alt=\"deno.land version\" src=\"https://img.shields.io/endpoint?url=https%3A%2F%2Fdeno-visualizer.danopia.net%2Fshields%2Flatest-version%2Fx%2Fdeno_slack_sdk%2Fmod.ts\">\n    <img alt=\"deno dependencies\" src=\"https://img.shields.io/endpoint?url=https%3A%2F%2Fdeno-visualizer.danopia.net%2Fshields%2Fupdates%2Fx%2Fdeno_slack_sdk%2Fmod.ts\">\n    <img alt=\"Samples Integration Type-checking\" src=\"https://github.com/slackapi/deno-slack-sdk/workflows/Samples%20Integration%20Type-checking/badge.svg\">\n  </a>\n</p>\n\nA Deno SDK to build Slack apps with the latest platform features. Read the\n[quickstart guide](https://api.slack.com/automation/quickstart) and look at our\n[code samples](https://api.slack.com/automation/samples) to learn how to build\napps.\n\n## 📢 Important\n\nWe recommend importing this package from\n[JSR](https://jsr.io/@slack/sdk/versions), but new releases are currently still\npublished to `deno.land/x` as well.\n\n## Versioning\n\nReleases for this repository follow the [SemVer](https://semver.org/) versioning\nscheme. The SDK's contract is determined by the top-level exports from\n`src/mod.ts` and `src/types.ts`. Exports not included in these files are deemed\ninternal and any modifications will not be treated as breaking changes. As such,\ninternal exports should be treated as unstable and used at your own risk.\n\n## Setup\n\nMake sure you have a development workspace where you have permission to install\napps. **Please note that the features in this project require that the workspace\nbe part of [a Slack paid plan](https://slack.com/pricing).**\n\n### Install the Slack CLI\n\nYou need to install and configure the Slack CLI. Step-by-step instructions can\nbe found on our\n[install & authorize page](https://api.slack.com/automation/cli/install).\n\n## Creating an app\n\nCreate a blank project by executing the following command:\n\n```zsh\nslack create my-app --template slack-samples/deno-blank-template\n\ncd my-app/\n```\n\nThe `manifest.ts` file contains the app's configuration. This file defines\nattributes like app name, description and functions.\n\n### Create a [function](https://api.slack.com/automation/functions/custom)\n\n```zsh\nmkdir ./functions && touch ./functions/hello_world.ts\n```\n\n```ts\n// Contents of ./functions/hello_world.ts\nimport { DefineFunction, Schema, SlackFunction } from \"deno-slack-sdk/mod.ts\";\n\nexport const HelloWorldFunctionDef = DefineFunction({\n  callback_id: \"hello_world_function\",\n  title: \"Hello World\",\n  source_file: \"functions/hello_world.ts\",\n  input_parameters: {\n    properties: {},\n    required: [],\n  },\n  output_parameters: {\n    properties: {\n      message: {\n        type: Schema.types.string,\n        description: \"Hello world message\",\n      },\n    },\n    required: [\"message\"],\n  },\n});\n\nexport default SlackFunction(\n  HelloWorldFunctionDef,\n  () => {\n    return {\n      outputs: { message: \"Hello World!\" },\n    };\n  },\n);\n```\n\n`DefineFunction` is used to define a custom function and provide Slack with the\ninformation required to use it.\n\n`SlackFunction` uses the definition returned by `DefineFunction` and your custom\nexecutable code to export a Slack-usable custom function.\n\n### Create a [workflow](https://api.slack.com/automation/workflows)\n\n```zsh\nmkdir ./workflows && touch ./workflows/hello_world.ts\n```\n\n```ts\n// Contents of ./workflows/hello_world.ts\nimport { DefineWorkflow, Schema } from \"deno-slack-sdk/mod.ts\";\nimport { HelloWorldFunctionDef } from \"../functions/hello_world.ts\";\n\nconst HelloWorldWorkflowDef = DefineWorkflow({\n  callback_id: \"hello_world_workflow\",\n  title: \"Hello World Workflow\",\n  input_parameters: {\n    properties: {\n      channel: {\n        type: Schema.slack.types.channel_id,\n      },\n    },\n    required: [\"channel\"],\n  },\n});\n\nconst helloWorldStep = HelloWorldWorkflowDef.addStep(HelloWorldFunctionDef, {});\n\nHelloWorldWorkflowDef.addStep(Schema.slack.functions.SendMessage, {\n  channel_id: HelloWorldWorkflowDef.inputs.channel,\n  message: helloWorldStep.outputs.message,\n});\n\nexport default HelloWorldWorkflowDef;\n```\n\n`DefineWorkflow` is used to define a workflow and provide Slack with the\ninformation required to use it.\n\n`HelloWorldWorkflow.addStep` is used to add a step to the workflow; here we add\nthe `HelloWorldFunction` and then the `SendMessage` Slack Function that will\npost the `message` to a Slack channel.\n\n### Update the [manifest](https://api.slack.com/automation/manifest)\n\n```ts\n// Contents of manifest.ts\nimport { Manifest } from \"deno-slack-sdk/mod.ts\";\nimport HelloWorldWorkflow from \"./workflows/hello_world.ts\";\n\nexport default Manifest({\n  name: \"my-app\",\n  description: \"A Hello World app\",\n  icon: \"assets/default_new_app_icon.png\",\n  workflows: [HelloWorldWorkflow],\n  outgoingDomains: [],\n  botScopes: [\"chat:write\", \"chat:write.public\"],\n});\n```\n\n`Manifest` is used to define your apps\n[manifest](https://api.slack.com/automation/manifest) and provides Slack with\nthe information required to manage it.\n\n### Create a [trigger](https://api.slack.com/automation/triggers)\n\n```zsh\nmkdir ./triggers && touch ./triggers/hello_world.ts\n```\n\n```ts\n// Contents of ./triggers/hello_world.ts\nimport { Trigger } from \"deno-slack-sdk/types.ts\";\nimport { TriggerContextData, TriggerTypes } from \"deno-slack-api/mod.ts\";\nimport HelloWorldWorkflow from \"../workflows/hello_world.ts\";\n\nconst trigger: Trigger<typeof HelloWorldWorkflow.definition> = {\n  type: TriggerTypes.Shortcut,\n  name: \"Reverse a string\",\n  description: \"Starts the workflow to reverse a string\",\n  workflow: `#/workflows/${HelloWorldWorkflow.definition.callback_id}`,\n  inputs: {\n    channel: {\n      value: TriggerContextData.Shortcut.channel_id,\n    },\n  },\n};\n\nexport default trigger;\n```\n\nThe `Trigger` object is used to define a trigger that will invoke the\n`HelloWorldWorkflow`. The Slack CLI will detect this file and prompt you for its\ncreation.\n\n## Running an app\n\n```zsh\nslack run\n```\n\nWhen prompted, create the `triggers/hello_world.ts` trigger. This will send your\ntrigger configuration to Slack.\n\nPost the `Hello world shortcut trigger` in a slack message and **use it**\n\n## Getting Help\n\n[This documentation](https://api.slack.com/automation) has more information on\nbasic and advanced concepts of the SDK.\n\nInformation on how to get started developing with Deno can be found in\n[this documentation](https://api.slack.com/automation/deno/develop).\n\nIf you get stuck, we're here to help. The following are the best ways to get\nassistance working through your issue:\n\n- [Issue Tracker](https://github.com/slackapi/deno-slack-sdk/issues?q=is%3Aissue)\n  for questions, bug reports, feature requests, and general discussion. **Try\n  searching for an existing issue before creating a new one.**\n- Email our developer support team: `support@slack.com`\n\n## Contributing\n\nContributions are more than welcome. Please look at the\n[contributing guidelines](https://github.com/slackapi/deno-slack-sdk/blob/main/.github/CONTRIBUTING.md)\nfor more info!\n"
  },
  {
    "path": "src/datastore/datastore_test.ts",
    "content": "import { assertStrictEquals } from \"../dev_deps.ts\";\nimport { DefineDatastore } from \"./mod.ts\";\nimport SchemaTypes from \"../schema/schema_types.ts\";\nimport { DefineType } from \"../types/mod.ts\";\n\nconst customType = DefineType({\n  name: \"custom_type\",\n  type: SchemaTypes.boolean,\n});\n\nDeno.test(\"Datastore sets appropriate defaults\", () => {\n  const datastore = DefineDatastore({\n    name: \"dinos\",\n    primary_key: \"attr1\",\n    attributes: {\n      attr1: {\n        type: SchemaTypes.string,\n      },\n      attr2: {\n        type: SchemaTypes.boolean,\n      },\n      attr3: {\n        type: customType,\n      },\n      attr4: {\n        type: SchemaTypes.object,\n        properties: {\n          anObjectString: { type: SchemaTypes.string },\n        },\n      },\n    },\n  });\n  assertStrictEquals(datastore.definition.name, \"dinos\");\n  assertStrictEquals(datastore.definition.primary_key, \"attr1\");\n\n  const exported = datastore.export();\n  assertStrictEquals(exported.primary_key, \"attr1\");\n  assertStrictEquals(exported.attributes.attr1.type, SchemaTypes.string);\n  assertStrictEquals(exported.attributes.attr2.type, SchemaTypes.boolean);\n  assertStrictEquals(exported.attributes.attr3.type, customType);\n  assertStrictEquals(exported.attributes.attr4.type, SchemaTypes.object);\n  assertStrictEquals(\n    exported.attributes.attr4.properties?.anObjectString?.type,\n    SchemaTypes.string,\n  );\n});\n"
  },
  {
    "path": "src/datastore/mod.ts",
    "content": "import type { SlackManifest } from \"../manifest/mod.ts\";\nimport type { ManifestDatastoreSchema } from \"../manifest/manifest_schema.ts\";\nimport type {\n  ISlackDatastore,\n  SlackDatastoreAttributes,\n  SlackDatastoreDefinition,\n} from \"./types.ts\";\nimport { isCustomType } from \"../types/mod.ts\";\n\n/**\n * Define a datastore and primary key and attributes for use in a Slack application.\n * @param {SlackDatastoreDefinition<string, SlackDatastoreAttributes, string>} definition Defines information about your datastore.\n * @returns {SlackDatastore}\n */\nexport const DefineDatastore = <\n  Name extends string,\n  Attributes extends SlackDatastoreAttributes,\n  PrimaryKey extends keyof Attributes,\n  TimeToLiveAttribute extends keyof Attributes,\n>(\n  definition: SlackDatastoreDefinition<\n    Name,\n    Attributes,\n    PrimaryKey,\n    TimeToLiveAttribute\n  >,\n) => {\n  return new SlackDatastore(definition);\n};\n\nexport class SlackDatastore<\n  Name extends string,\n  Attributes extends SlackDatastoreAttributes,\n  PrimaryKey extends keyof Attributes,\n  TimeToLiveAttribute extends keyof Attributes,\n> implements ISlackDatastore {\n  public name: Name;\n\n  constructor(\n    public definition: SlackDatastoreDefinition<\n      Name,\n      Attributes,\n      PrimaryKey,\n      TimeToLiveAttribute\n    >,\n  ) {\n    this.name = definition.name;\n  }\n\n  registerAttributeTypes(manifest: SlackManifest) {\n    Object.values(this.definition.attributes).forEach((attribute) => {\n      if (isCustomType(attribute.type)) {\n        manifest.registerType(attribute.type);\n      }\n    });\n  }\n\n  export(): ManifestDatastoreSchema {\n    return {\n      primary_key: this.definition.primary_key as string,\n      time_to_live_attribute: this.definition.time_to_live_attribute as string,\n      attributes: this.definition.attributes,\n    };\n  }\n}\n"
  },
  {
    "path": "src/datastore/types.ts",
    "content": "import type { ICustomType } from \"../types/types.ts\";\nimport type { ManifestDatastoreSchema } from \"../manifest/manifest_schema.ts\";\nimport type { SlackManifest } from \"../manifest/mod.ts\";\nimport type { SlackPrimitiveTypes } from \"../schema/slack/types/mod.ts\";\nimport type { ValidSchemaTypes } from \"../schema/schema_types.ts\";\nimport type { ValidSlackPrimitiveTypes } from \"../schema/slack/types/mod.ts\";\nimport type { LooseStringAutocomplete } from \"../type_utils.ts\";\n\ntype InvalidDatastoreTypes =\n  | typeof SlackPrimitiveTypes.blocks\n  | typeof SlackPrimitiveTypes.oauth2;\n\ntype ValidDatastoreTypes = Exclude<\n  | ValidSchemaTypes\n  | ValidSlackPrimitiveTypes,\n  InvalidDatastoreTypes\n>;\n\nexport type SlackDatastoreAttribute = {\n  // supports custom types, primitive types, inline objects and lists\n  type: LooseStringAutocomplete<ValidDatastoreTypes> | ICustomType;\n};\n\nexport type SlackDatastoreAttributes = Record<string, SlackDatastoreAttribute>;\n\nexport type SlackDatastoreDefinition<\n  Name extends string,\n  Attributes extends SlackDatastoreAttributes,\n  PrimaryKey extends keyof Attributes,\n  TimeToLiveAttribute extends keyof Attributes,\n> = {\n  name: Name;\n  \"primary_key\": PrimaryKey;\n  \"time_to_live_attribute\"?: TimeToLiveAttribute;\n  attributes: Attributes;\n};\n\nexport interface ISlackDatastore {\n  name: string;\n  export: () => ManifestDatastoreSchema;\n  registerAttributeTypes: (manifest: SlackManifest) => void;\n}\n\nexport type SlackDatastoreItem<Attributes extends SlackDatastoreAttributes> = {\n  // TODO: In the future, see if we can map the attribute.type to\n  // the TS type map like functions do w/ parameters\n  // deno-lint-ignore no-explicit-any\n  [k in keyof Attributes]: any;\n};\n\nexport type PartialSlackDatastoreItem<\n  Attributes extends SlackDatastoreAttributes,\n> = OptionalPartial<Attributes>;\n\n// deno-lint-ignore no-explicit-any\ntype OptionalPartial<T extends any> = {\n  // deno-lint-ignore no-explicit-any\n  [P in keyof T]?: any;\n};\n"
  },
  {
    "path": "src/deps.ts",
    "content": "export { SlackAPI } from \"jsr:@slack/api@2.9.0\";\nexport type { SlackAPIClient, Trigger } from \"jsr:@slack/api@2.9.0/types\";\n"
  },
  {
    "path": "src/dev_deps.ts",
    "content": "export {\n  assertEquals,\n  assertExists,\n  assertInstanceOf,\n  AssertionError,\n  assertMatch,\n  assertNotStrictEquals,\n  assertRejects,\n  assertStrictEquals,\n  assertStringIncludes,\n  fail,\n} from \"jsr:@std/assert@^1.0.0\";\nexport * as mock from \"jsr:@std/testing@^1.0.0/mock\";\n\nexport { assertType as assert } from \"jsr:@std/testing@^1.0.0/types\";\nexport type { IsAny, IsExact } from \"jsr:@std/testing@^1.0.0/types\";\n\nexport { toPascalCase } from \"jsr:@std/text@^1.0.0\";\n"
  },
  {
    "path": "src/events/events_test.ts",
    "content": "import { DefineEvent } from \"./mod.ts\";\nimport { assertEquals } from \"../dev_deps.ts\";\nimport { DefineType, Schema } from \"../mod.ts\";\nimport { isCustomType } from \"../types/mod.ts\";\n\nDeno.test(\"DefineEvent accepts object types\", () => {\n  const TestEvent = DefineEvent({\n    name: \"test\",\n    type: Schema.types.object,\n    properties: {},\n  });\n\n  assertEquals(TestEvent.id, TestEvent.definition.name);\n  assertEquals(typeof TestEvent.definition.type, \"string\");\n});\n\nDeno.test(\"DefineEvent accepts custom types\", () => {\n  const TestType = DefineType({\n    name: \"test\",\n    type: Schema.types.object,\n    properties: {},\n  });\n\n  const TestEvent = DefineEvent({\n    title: \"Title\",\n    description: \"Description\",\n    name: \"test\",\n    type: TestType,\n  });\n\n  assertEquals(isCustomType(TestEvent.definition.type), true);\n});\n\nDeno.test(\"DefineEvent is properly stringified\", () => {\n  const TestEvent = DefineEvent({\n    name: \"test\",\n    type: Schema.types.object,\n    properties: {},\n  });\n\n  assertEquals(`${TestEvent}`, TestEvent.id);\n  assertEquals(TestEvent.toJSON(), TestEvent.id);\n  assertEquals(TestEvent.toString(), TestEvent.id);\n});\n"
  },
  {
    "path": "src/events/mod.ts",
    "content": "import type { SlackManifest } from \"../manifest/mod.ts\";\nimport type { ManifestCustomEventSchema } from \"../manifest/manifest_schema.ts\";\nimport type {\n  CustomEventDefinition,\n  DefineEventSignature,\n  ICustomEvent,\n} from \"./types.ts\";\nimport { isCustomType } from \"../types/mod.ts\";\nimport { isTypedObject } from \"../parameters/mod.ts\";\n\nexport const DefineEvent: DefineEventSignature = <\n  Def extends CustomEventDefinition,\n>(\n  definition: Def,\n) => {\n  return new CustomEvent(definition);\n};\n\nexport class CustomEvent<Def extends CustomEventDefinition>\n  implements ICustomEvent {\n  public id: string;\n  public title: string | undefined;\n  public description: string | undefined;\n\n  constructor(\n    public definition: Def,\n  ) {\n    this.id = definition.name;\n    this.definition = definition;\n    this.description = definition.description;\n    this.title = definition.title;\n  }\n\n  private generateReferenceString() {\n    return this.id;\n  }\n\n  toString() {\n    return this.generateReferenceString();\n  }\n\n  toJSON() {\n    return this.generateReferenceString();\n  }\n\n  registerParameterTypes(manifest: SlackManifest) {\n    if (isCustomType(this.definition.type)) {\n      manifest.registerType(this.definition.type);\n    } else if (isTypedObject(this.definition)) {\n      Object.values(this.definition.properties)?.forEach((property) => {\n        if (isCustomType(property.type)) {\n          manifest.registerType(property.type);\n        }\n      });\n    }\n  }\n  export(): ManifestCustomEventSchema {\n    // remove name from the definition we pass to the manifest\n    const { name: _n, ...definition } = this.definition;\n    // Using JSON.stringify to force any custom types into their string reference\n    return JSON.parse(JSON.stringify(definition));\n  }\n}\n"
  },
  {
    "path": "src/events/types.ts",
    "content": "import type {\n  CustomTypeParameterDefinition,\n  TypedObjectParameter,\n} from \"../parameters/definition_types.ts\";\nimport type { ManifestCustomEventSchema } from \"../manifest/manifest_schema.ts\";\nimport type { CustomEvent } from \"./mod.ts\";\nimport type { SlackManifest } from \"../manifest/mod.ts\";\n\ntype AcceptedEventTypes =\n  | TypedObjectParameter\n  | CustomTypeParameterDefinition;\n\nexport type CustomEventDefinition =\n  & {\n    /**\n     * The name of your event\n     * @example my_custom_event\n     */\n    name: string;\n  }\n  & AcceptedEventTypes;\n\nexport type DefineEventSignature = {\n  <Def extends CustomEventDefinition>(definition: Def): CustomEvent<Def>;\n};\n\nexport interface ICustomEvent {\n  id: string;\n  definition: CustomEventDefinition;\n  description?: string;\n  registerParameterTypes: (manifest: SlackManifest) => void;\n  export(): ManifestCustomEventSchema;\n}\n"
  },
  {
    "path": "src/functions/definitions/connector-function.ts",
    "content": "import type { ManifestFunctionType } from \"../../manifest/manifest_schema.ts\";\nimport type {\n  ParameterSetDefinition,\n  PossibleParameterKeys,\n} from \"../../parameters/types.ts\";\nimport type {\n  FunctionDefinitionArgs,\n  ISlackFunctionDefinition,\n} from \"../types.ts\";\n\n/**\n * Define a connector and its input and output parameters for use in a Slack application.\n * @param {FunctionDefinitionArgs<InputParameters, OutputParameters, RequiredInput, RequiredOutput>} definition Defines information about your function (title, description) as well as formalizes the input and output parameters of a connector\n * @returns {ConnectorFunctionDefinition}\n */\nexport const DefineConnector = <\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n>(\n  definition: FunctionDefinitionArgs<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutput\n  >,\n) => {\n  return new ConnectorFunctionDefinition(definition);\n};\n\nexport class ConnectorFunctionDefinition<\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n> implements\n  ISlackFunctionDefinition<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutput\n  > {\n  type: ManifestFunctionType = \"API\";\n  id: string;\n\n  constructor(\n    public definition: FunctionDefinitionArgs<\n      InputParameters,\n      OutputParameters,\n      RequiredInput,\n      RequiredOutput\n    >,\n  ) {\n    this.id = definition.callback_id;\n    this.definition = definition;\n  }\n}\n"
  },
  {
    "path": "src/functions/definitions/connector-function_test.ts",
    "content": "import {\n  assertEquals,\n  assertInstanceOf,\n  assertStrictEquals,\n} from \"../../dev_deps.ts\";\nimport type { PossibleParameterKeys } from \"../../parameters/types.ts\";\nimport Schema from \"../../schema/mod.ts\";\nimport {\n  ConnectorFunctionDefinition,\n  DefineConnector,\n} from \"./connector-function.ts\";\n\ntype emptyParameterType = Record<string, never>;\n\nDeno.test(\"DefineConnector returns an instance of `ConnectorFunctionDefinition`\", () => {\n  const connector = DefineConnector({\n    callback_id: \"my_connector\",\n    title: \"My Connector\",\n  });\n\n  assertInstanceOf(\n    connector,\n    ConnectorFunctionDefinition<\n      emptyParameterType,\n      emptyParameterType,\n      PossibleParameterKeys<emptyParameterType>,\n      PossibleParameterKeys<emptyParameterType>\n    >,\n  );\n});\n\nDeno.test(\"DefineConnector sets appropriate defaults\", () => {\n  const Func = DefineConnector({\n    callback_id: \"my_connector\",\n    title: \"My Connector\",\n  });\n\n  assertEquals(Func.id, Func.definition.callback_id);\n  assertStrictEquals(Func.type, \"API\");\n  assertEquals(Func.definition.title, \"My Connector\");\n  assertEquals(Func.definition.input_parameters, undefined);\n  assertEquals(Func.definition.output_parameters, undefined);\n});\n\nDeno.test(\"DefineConnector with input parameters but no output parameters\", () => {\n  const inputParameters = {\n    properties: {\n      aString: { type: Schema.types.string },\n    },\n    required: [],\n  };\n  const NoOutputParamFunction = DefineConnector({\n    callback_id: \"input_params_only\",\n    title: \"No Parameter Function\",\n    input_parameters: inputParameters,\n  });\n\n  assertStrictEquals(\n    NoOutputParamFunction.definition.input_parameters,\n    inputParameters,\n  );\n  assertEquals(\n    NoOutputParamFunction.definition.output_parameters,\n    undefined,\n  );\n});\n\nDeno.test(\"DefineConnector with output parameters but no input parameters\", () => {\n  const outputParameters = {\n    properties: {\n      aString: { type: Schema.types.string },\n    },\n    required: [],\n  };\n  const NoInputParamFunction = DefineConnector({\n    callback_id: \"output_params_only\",\n    title: \"No Parameter Function\",\n    output_parameters: outputParameters,\n  });\n\n  assertEquals(\n    NoInputParamFunction.definition.input_parameters,\n    undefined,\n  );\n  assertStrictEquals(\n    NoInputParamFunction.definition.output_parameters,\n    outputParameters,\n  );\n});\n"
  },
  {
    "path": "src/functions/definitions/mod.ts",
    "content": "export { DefineFunction, SlackFunctionDefinition } from \"./slack-function.ts\";\nexport {\n  ConnectorFunctionDefinition,\n  DefineConnector,\n} from \"./connector-function.ts\";\n"
  },
  {
    "path": "src/functions/definitions/slack-function.ts",
    "content": "import type {\n  ManifestFunctionSchema,\n  ManifestFunctionType,\n} from \"../../manifest/manifest_schema.ts\";\nimport type { SlackManifest } from \"../../mod.ts\";\nimport type {\n  ParameterSetDefinition,\n  PossibleParameterKeys,\n} from \"../../parameters/types.ts\";\nimport type {\n  ISlackFunctionDefinition,\n  SlackFunctionDefinitionArgs,\n} from \"../types.ts\";\n\n/**\n * Define a function and its input and output parameters for use in a Slack application.\n * @param {SlackFunctionDefinitionArgs<InputParameters, OutputParameters, RequiredInput, RequiredOutput>} definition Defines information about your function (title, description) as well as formalizes the input and output parameters of your function\n * @returns {SlackFunctionDefinition}\n */\nexport const DefineFunction = <\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n>(\n  definition: SlackFunctionDefinitionArgs<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutput\n  >,\n) => {\n  return new SlackFunctionDefinition(definition);\n};\n\nexport class SlackFunctionDefinition<\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n> implements\n  ISlackFunctionDefinition<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutput\n  > {\n  type: ManifestFunctionType = \"app\";\n  id: string;\n\n  constructor(\n    public definition: SlackFunctionDefinitionArgs<\n      InputParameters,\n      OutputParameters,\n      RequiredInput,\n      RequiredOutput\n    >,\n  ) {\n    this.id = definition.callback_id;\n    this.definition = definition;\n  }\n\n  registerParameterTypes(manifest: SlackManifest) {\n    const { input_parameters: inputParams, output_parameters: outputParams } =\n      this.definition;\n    manifest.registerTypes(inputParams?.properties ?? {});\n    manifest.registerTypes(outputParams?.properties ?? {});\n  }\n\n  export(): ManifestFunctionSchema {\n    return {\n      title: this.definition.title,\n      description: this.definition.description,\n      source_file: this.definition.source_file,\n      input_parameters: this.definition.input_parameters ??\n        { properties: {}, required: [] },\n      output_parameters: this.definition.output_parameters ??\n        { properties: {}, required: [] },\n    };\n  }\n}\n\nexport function isCustomFunctionDefinition<\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutputs extends PossibleParameterKeys<OutputParameters>,\n>(\n  functionDefinition: ISlackFunctionDefinition<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutputs\n  >,\n): functionDefinition is SlackFunctionDefinition<\n  InputParameters,\n  OutputParameters,\n  RequiredInput,\n  RequiredOutputs\n> {\n  if (\n    functionDefinition.type === \"app\" &&\n    functionDefinition.export &&\n    functionDefinition.registerParameterTypes\n  ) {\n    return true;\n  }\n  return false;\n}\n"
  },
  {
    "path": "src/functions/definitions/slack-function_test.ts",
    "content": "import {\n  assertEquals,\n  assertInstanceOf,\n  assertStrictEquals,\n} from \"../../dev_deps.ts\";\nimport Schema from \"../../schema/mod.ts\";\nimport type { PossibleParameterKeys } from \"../../parameters/types.ts\";\nimport {\n  DefineFunction,\n  isCustomFunctionDefinition,\n  SlackFunctionDefinition,\n} from \"./slack-function.ts\";\nimport type { ISlackFunctionDefinition } from \"../types.ts\";\n\n// TODO: Re-add tests to validate function execution when we've determined how to execute functions locally\n\nconst emptyParameterObject = Object.freeze({ required: [], properties: {} });\ntype emptyParameterType = Record<string, never>;\n\nDeno.test(\"DefineFunction returns an instance of `SlackFunctionDefinition`\", () => {\n  const func = DefineFunction({\n    callback_id: \"my_function\",\n    title: \"My function\",\n    source_file: \"functions/dino.ts\",\n  });\n\n  assertInstanceOf(\n    func,\n    SlackFunctionDefinition<\n      emptyParameterType,\n      emptyParameterType,\n      PossibleParameterKeys<emptyParameterType>,\n      PossibleParameterKeys<emptyParameterType>\n    >,\n  );\n});\n\nDeno.test(\"DefineFunction sets appropriate defaults\", () => {\n  const Func = DefineFunction({\n    callback_id: \"my_function\",\n    title: \"My function\",\n    source_file: \"functions/dino.ts\",\n  });\n\n  assertEquals(Func.id, Func.definition.callback_id);\n  assertEquals(Func.definition.title, \"My function\");\n  assertEquals(Func.definition.source_file, \"functions/dino.ts\");\n\n  const exportedFunc = Func.export();\n  assertStrictEquals(exportedFunc.source_file, \"functions/dino.ts\");\n  assertEquals(exportedFunc.input_parameters, emptyParameterObject);\n  assertEquals(exportedFunc.output_parameters, emptyParameterObject);\n});\n\nDeno.test(\"DefineFunction with required params\", () => {\n  const AllTypesFunction = DefineFunction({\n    callback_id: \"my_function\",\n    title: \"All Types Function\",\n    source_file: \"functions/example.ts\",\n    input_parameters: {\n      properties: {\n        myString: {\n          type: Schema.types.string,\n          title: \"My string\",\n          description: \"a really neat value\",\n          hint: \"Ex. my neat value\",\n        },\n        myBoolean: {\n          type: Schema.types.boolean,\n          title: \"My boolean\",\n          hint: \"Ex: true/false\",\n        },\n        myInteger: {\n          type: Schema.types.integer,\n          description: \"integer\",\n          hint: \"0-100\",\n        },\n        myNumber: {\n          type: Schema.types.number,\n          description: \"number\",\n        },\n      },\n      required: [\"myString\", \"myNumber\"],\n    },\n    output_parameters: {\n      properties: {\n        out: {\n          type: Schema.types.string,\n        },\n      },\n      required: [\"out\"],\n    },\n  });\n\n  assertEquals(AllTypesFunction.definition.input_parameters?.required, [\n    \"myString\",\n    \"myNumber\",\n  ]);\n  assertEquals(AllTypesFunction.definition.output_parameters?.required, [\n    \"out\",\n  ]);\n  assertEquals(\n    AllTypesFunction.definition.input_parameters?.properties.myString.hint,\n    \"Ex. my neat value\",\n  );\n  assertEquals(\n    AllTypesFunction.definition.input_parameters?.properties.myBoolean.hint,\n    \"Ex: true/false\",\n  );\n});\n\nDeno.test(\"DefineFunction without input and output parameters\", () => {\n  const NoParamFunction = DefineFunction({\n    callback_id: \"no_params\",\n    title: \"No Parameter Function\",\n    source_file: \"functions/no_params.ts\",\n  });\n\n  assertEquals(emptyParameterObject, NoParamFunction.export().input_parameters);\n  assertEquals(\n    emptyParameterObject,\n    NoParamFunction.export().output_parameters,\n  );\n});\n\nDeno.test(\"DefineFunction with input parameters but no output parameters\", () => {\n  const inputParameters = {\n    properties: {\n      aString: { type: Schema.types.string },\n    },\n    required: [],\n  };\n  const NoOutputParamFunction = DefineFunction({\n    callback_id: \"input_params_only\",\n    title: \"No Parameter Function\",\n    source_file: \"functions/input_params_only.ts\",\n    input_parameters: inputParameters,\n  });\n\n  NoOutputParamFunction.export();\n\n  assertStrictEquals(\n    inputParameters,\n    NoOutputParamFunction.definition.input_parameters,\n  );\n  assertEquals(\n    emptyParameterObject,\n    NoOutputParamFunction.export().output_parameters,\n  );\n});\n\nDeno.test(\"DefineFunction with output parameters but no input parameters\", () => {\n  const outputParameters = {\n    properties: {\n      aString: { type: Schema.types.string },\n    },\n    required: [],\n  };\n  const NoInputParamFunction = DefineFunction({\n    callback_id: \"output_params_only\",\n    title: \"No Parameter Function\",\n    source_file: \"functions/output_params_only.ts\",\n    output_parameters: outputParameters,\n  });\n\n  assertEquals(\n    emptyParameterObject,\n    NoInputParamFunction.export().input_parameters,\n  );\n  assertStrictEquals(\n    outputParameters,\n    NoInputParamFunction.definition.output_parameters,\n  );\n});\n\nDeno.test(\"DefineFunction using an OAuth2 property requests a provider key\", () => {\n  /**\n   * The `oauth2_provider_key` is not currently required because `type` supports any string\n   * But eventually we'd like to support static errors for OAuth2 properties without the provider key\n   */\n\n  const OAuth2Function = DefineFunction({\n    callback_id: \"oauth\",\n    title: \"OAuth Function\",\n    source_file: \"functions/oauth.ts\",\n    input_parameters: {\n      properties: {\n        googleAccessTokenId: {\n          type: Schema.slack.types.oauth2,\n          oauth2_provider_key: \"test\",\n        },\n      },\n      required: [],\n    },\n  });\n\n  /**\n   * TODO: Support the following test for static error\n    // ts-expect-error `oauth2_provider_key` must be set\n    const _IncompleteOAuth2Function = DefineFunction({\n      callback_id: \"oauth\",\n      title: \"OAuth Function\",\n      source_file: \"functions/oauth.ts\",\n      input_parameters: {\n        properties: {\n          googleAccessTokenId: {\n            type: Schema.slack.types.oauth2,\n          },\n        },\n        required: [],\n      },\n    });\n   */\n\n  assertEquals(\n    {\n      googleAccessTokenId: {\n        oauth2_provider_key: \"test\",\n        type: Schema.slack.types.oauth2,\n      },\n    },\n    OAuth2Function.export().input_parameters.properties,\n  );\n});\n\nDeno.test(\"DefineFunction using an OAuth2 property allows require_end_user_auth\", () => {\n  const OAuth2Function = DefineFunction({\n    callback_id: \"oauth\",\n    title: \"OAuth Function\",\n    source_file: \"functions/oauth.ts\",\n    input_parameters: {\n      properties: {\n        googleAccessTokenId: {\n          type: Schema.slack.types.oauth2,\n          oauth2_provider_key: \"test\",\n          require_end_user_auth: true,\n        },\n      },\n      required: [],\n    },\n  });\n\n  assertEquals(\n    {\n      googleAccessTokenId: {\n        oauth2_provider_key: \"test\",\n        type: Schema.slack.types.oauth2,\n        require_end_user_auth: true,\n      },\n    },\n    OAuth2Function.export().input_parameters.properties,\n  );\n});\n\nDeno.test(\"isCustomFunctionDefinition should return true when SlackFunctionDefinition is passed\", () => {\n  const NoParamFunction = DefineFunction({\n    callback_id: \"no_params\",\n    title: \"No Parameter Function\",\n    source_file: \"functions/no_params.ts\",\n  });\n\n  assertInstanceOf(NoParamFunction, SlackFunctionDefinition);\n  assertEquals(true, isCustomFunctionDefinition(NoParamFunction));\n});\n\nDeno.test(\"isCustomFunctionDefinition should return false when a non custom function is passed\", () => {\n  const notCustomFunction: ISlackFunctionDefinition<\n    emptyParameterType,\n    emptyParameterType,\n    PossibleParameterKeys<emptyParameterType>,\n    PossibleParameterKeys<emptyParameterType>\n  > = {\n    type: \"API\",\n    id: \"not_custom\",\n    definition: {\n      callback_id: \"not_custom\",\n      title: \"Not a custom Function\",\n      description: undefined,\n      input_parameters: undefined,\n      output_parameters: undefined,\n    },\n  };\n  assertEquals(false, isCustomFunctionDefinition(notCustomFunction));\n});\n"
  },
  {
    "path": "src/functions/enrich-context.ts",
    "content": "import { SlackAPI } from \"../deps.ts\";\nimport type {\n  BaseRuntimeFunctionContext,\n  FunctionContextEnrichment,\n} from \"./types.ts\";\n\nexport const enrichContext = (\n  // deno-lint-ignore no-explicit-any\n  context: BaseRuntimeFunctionContext<any>,\n): typeof context & FunctionContextEnrichment => {\n  const token = context.token;\n  const slackApiUrl = (context.env || {})[\"SLACK_API_URL\"];\n\n  const client = SlackAPI(token, {\n    slackApiUrl: slackApiUrl ? slackApiUrl : undefined,\n  });\n\n  return {\n    ...context,\n    client,\n  };\n};\n"
  },
  {
    "path": "src/functions/enrich-context_test.ts",
    "content": "import { assertExists } from \"../dev_deps.ts\";\nimport { enrichContext } from \"./enrich-context.ts\";\nimport type { BaseRuntimeFunctionContext } from \"./types.ts\";\n\nDeno.test(\"enrichContext with no env.SLACK_API_URL\", () => {\n  // deno-lint-ignore no-explicit-any\n  const ctx: BaseRuntimeFunctionContext<any> = {\n    env: {},\n    inputs: {},\n    team_id: \"team\",\n    enterprise_id: \"\",\n    token: \"token\",\n  };\n\n  const newContext = enrichContext(ctx);\n\n  assertExists(newContext.client);\n});\n\nDeno.test(\"enrichContext with env.SLACK_API_URL\", () => {\n  // deno-lint-ignore no-explicit-any\n  const ctx: BaseRuntimeFunctionContext<any> = {\n    env: {\n      \"SLACK_API_URL\": \"https://something.slack.com/api\",\n    },\n    inputs: {},\n    team_id: \"team\",\n    enterprise_id: \"\",\n    token: \"token\",\n  };\n\n  const newContext = enrichContext(ctx);\n\n  assertExists(newContext.client);\n});\n"
  },
  {
    "path": "src/functions/interactivity/block_actions_router.ts",
    "content": "import type {\n  ParameterSetDefinition,\n  PossibleParameterKeys,\n} from \"../../parameters/types.ts\";\nimport type { SlackFunctionDefinition } from \"../definitions/mod.ts\";\nimport { UnhandledEventError } from \"../unhandled-event-error.ts\";\nimport { enrichContext } from \"../enrich-context.ts\";\nimport type {\n  FunctionDefinitionArgs,\n  FunctionRuntimeParameters,\n} from \"../types.ts\";\nimport type {\n  ActionContext,\n  BlockActionConstraint,\n  BlockActionHandler,\n  RuntimeActionContext,\n} from \"./types.ts\";\nimport type { BlockAction } from \"./block_kit_types.ts\";\nimport {\n  matchBasicConstraintField,\n  normalizeConstraintToArray,\n} from \"./matchers.ts\";\n\n/**\n * Define an actions \"router\" and its input and output parameters for use in a Slack application. The ActionsRouter will route incoming action events to action-specific handlers.\n * @param {SlackFunctionDefinition<InputParameters, OutputParameters, RequiredInput, RequiredOutput>} func Reference to your previously-created SlackFunction, defined via DefineFunction\n * @returns {ActionsRouter}\n */\nexport const BlockActionsRouter = <\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n>(\n  func: SlackFunctionDefinition<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutput\n  >,\n) => {\n  const router = new ActionsRouter(func);\n\n  // deno-lint-ignore no-explicit-any\n  const exportedHandler: any = router.export();\n\n  // deno-lint-ignore no-explicit-any\n  exportedHandler.addHandler = ((...args: any) => {\n    router.addHandler.apply(router, args);\n\n    return exportedHandler;\n  }) as typeof router.addHandler;\n\n  return exportedHandler as\n    & ReturnType<typeof router.export>\n    & Pick<typeof router, \"addHandler\">;\n};\n\nexport class ActionsRouter<\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n> {\n  private routes: Array<\n    [BlockActionConstraint, BlockActionHandler<typeof this.func.definition>]\n  >;\n\n  constructor(\n    private func: SlackFunctionDefinition<\n      InputParameters,\n      OutputParameters,\n      RequiredInput,\n      RequiredOutput\n    >,\n  ) {\n    this.func = func;\n    this.routes = [];\n  }\n\n  /**\n   * Add an action handler for something that can match an action event.\n   * @param {BlockActionConstraint} actionConstraint A BlockActionConstraintField(i.e. a string, array of strings or regular expression) or more complex BlockActionConstraintObject to match incoming block action events. A BlockActionConstraintField parameter are matched with a block action event's `action_id` property. A BlockActionConstraintObject parameter allows to match with other block action event properties like `block_id` as well as `action_id`. If multiple properties are specified using BlockActionConstraintObject, then the event must match ALL provided BlockActionConstraintObject properties.\n   * @returns {ActionsRouter}\n   */\n  addHandler(\n    actionConstraint: BlockActionConstraint,\n    handler: BlockActionHandler<\n      FunctionDefinitionArgs<\n        InputParameters,\n        OutputParameters,\n        RequiredInput,\n        RequiredOutput\n      >\n    >,\n  ): ActionsRouter<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutput\n  > {\n    this.routes.push([actionConstraint, handler]);\n    return this;\n  }\n\n  /**\n   * Returns a method handling routing of action payloads to the appropriate action handler.\n   * The output of export() should be attached to the `blockActions` export of your function.\n   */\n  export() {\n    return async (\n      context: RuntimeActionContext<\n        FunctionRuntimeParameters<InputParameters, RequiredInput>\n      >,\n    ) => {\n      const action: BlockAction = context.action;\n      const handler = this.matchHandler(action);\n      if (handler === null) {\n        throw new UnhandledEventError(\n          `Received block action payload with action=${\n            JSON.stringify(action)\n          } but this app has no action handler defined to handle it!`,\n        );\n      }\n\n      const enrichedContext = enrichContext(context) as ActionContext<\n        FunctionRuntimeParameters<InputParameters, RequiredInput>\n      >;\n\n      return await handler(enrichedContext);\n    };\n  }\n\n  /**\n   * Return the first registered ActionHandler that matches the action ID string provided.\n   */\n  matchHandler(\n    action: BlockAction,\n  ):\n    | BlockActionHandler<\n      FunctionDefinitionArgs<\n        InputParameters,\n        OutputParameters,\n        RequiredInput,\n        RequiredOutput\n      >\n    >\n    | null {\n    for (let i = 0; i < this.routes.length; i++) {\n      const route = this.routes[i];\n      let [constraint, handler] = route;\n      // Handle different constraint types below\n      if (\n        constraint instanceof RegExp || constraint instanceof Array ||\n        typeof constraint === \"string\"\n      ) {\n        // Normalize simple string constraints to be an array of strings for consistency in handling inside this method.\n        constraint = normalizeConstraintToArray(constraint);\n        // Handle the case where the constraint is either a regex or an array of strings to match against action_id\n        if (matchBasicConstraintField(constraint, \"action_id\", action)) {\n          return handler;\n        }\n      } else {\n        // Assumes an object as a constraint (type BlockActionConstraintObject)\n        // Return first match *within* any of the defined fields on the constaint object, but ensure there is a match on *all* defined fields\n        // Effectively a logical AND across the action_id and block_id field(s)\n        // If either of the constraint fields are not defined, pre-set them to have matched so we can effectively\n        // ignore them when determining if we have a match by &&'ing them\n        let actionIDMatched = constraint.action_id ? false : true;\n        let blockIDMatched = constraint.block_id ? false : true;\n        if (constraint.action_id) {\n          actionIDMatched = matchBasicConstraintField(\n            normalizeConstraintToArray(constraint.action_id),\n            \"action_id\",\n            action,\n          );\n        }\n        if (constraint.block_id) {\n          blockIDMatched = matchBasicConstraintField(\n            normalizeConstraintToArray(constraint.block_id),\n            \"block_id\",\n            action,\n          );\n        }\n        if (blockIDMatched && actionIDMatched) {\n          return handler;\n        }\n      }\n    }\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/functions/interactivity/block_actions_router_test.ts",
    "content": "import { SlackAPI } from \"../../deps.ts\";\nimport {\n  assertEquals,\n  assertExists,\n  assertRejects,\n  mock,\n} from \"../../dev_deps.ts\";\nimport { BlockActionsRouter } from \"./block_actions_router.ts\";\nimport type { ActionContext } from \"./types.ts\";\nimport type { BlockAction } from \"./block_kit_types.ts\";\nimport type {\n  FunctionParameters,\n  FunctionRuntimeParameters,\n} from \"../types.ts\";\nimport type {\n  ParameterSetDefinition,\n  PossibleParameterKeys,\n} from \"../../parameters/types.ts\";\nimport type { SlackFunctionDefinition } from \"../definitions/mod.ts\";\nimport { DefineFunction, Schema } from \"../../mod.ts\";\n\n// Helper test types\n// TODO: maybe we want to export this for userland usage at some point?\n// Very much a direct copy from the existing main function tester types and utilties in src/functions/tester\ntype SlackActionHandlerTesterArgs<InputParameters extends FunctionParameters> =\n  & Partial<\n    ActionContext<InputParameters>\n  >\n  & {\n    inputs: InputParameters;\n  };\n\ntype CreateActionHandlerContext<\n  InputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n> = {\n  (\n    args: SlackActionHandlerTesterArgs<\n      FunctionRuntimeParameters<InputParameters, RequiredInput>\n    >,\n  ): ActionContext<\n    FunctionRuntimeParameters<InputParameters, RequiredInput>\n  >;\n};\n\ntype SlackActionHandlerTesterResponse<\n  InputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n> = {\n  createContext: CreateActionHandlerContext<InputParameters, RequiredInput>;\n};\n\ntype SlackActionHandlerTesterSignature = {\n  <\n    InputParameters extends ParameterSetDefinition,\n    OutputParameters extends ParameterSetDefinition,\n    RequiredInput extends PossibleParameterKeys<InputParameters>,\n    RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n  >(\n    func: SlackFunctionDefinition<\n      InputParameters,\n      OutputParameters,\n      RequiredInput,\n      RequiredOutput\n    >,\n  ): SlackActionHandlerTesterResponse<\n    InputParameters,\n    RequiredInput\n  >;\n};\n\n// Helper test fixtures and utilities\nconst DEFAULT_ACTION: BlockAction = {\n  type: \"button\",\n  block_id: \"block_id\",\n  action_ts: `${new Date().getTime()}`,\n  action_id: \"action_id\",\n  text: { type: \"plain_text\", text: \"duncare\", emoji: false },\n  style: \"danger\",\n};\nconst SlackActionHandlerTester: SlackActionHandlerTesterSignature = <\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n>(\n  _func: SlackFunctionDefinition<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutput\n  >,\n) => {\n  const createContext: CreateActionHandlerContext<\n    InputParameters,\n    RequiredInput\n  > = (\n    args,\n  ) => {\n    const inputs = (args.inputs || {}) as FunctionRuntimeParameters<\n      InputParameters,\n      RequiredInput\n    >;\n    const DEFAULT_BODY = {\n      type: \"block_actions\",\n      actions: [DEFAULT_ACTION],\n      function_data: {\n        execution_id: \"123\",\n        function: { callback_id: \"456\" },\n        inputs,\n      },\n      interactivity: {\n        interactor: {\n          secret: \"shhhh\",\n          id: \"123\",\n        },\n        interactivity_pointer: \"123.asdf\",\n      },\n      user: {\n        id: \"123\",\n        name: \"asdf\",\n        team_id: \"123\",\n      },\n      team: {\n        id: \"123\",\n        domain: \"asdf\",\n      },\n      enterprise: null,\n      is_enterprise_install: false,\n      api_app_id: \"123\",\n      token: \"123\",\n      trigger_id: \"123\",\n      response_url: \"asdf\",\n    };\n    const token = args.token || \"slack-function-test-token\";\n\n    return {\n      inputs,\n      env: args.env || {},\n      token,\n      client: SlackAPI(token),\n      team_id: args.team_id || \"test-team-id\",\n      enterprise_id: \"\",\n      action: args.action || DEFAULT_ACTION,\n      body: args.body || DEFAULT_BODY,\n    };\n  };\n\n  return { createContext };\n};\n\n// a basic function definition and associated block action router to test\nconst func = DefineFunction({\n  callback_id: \"id\",\n  title: \"test\",\n  source_file: \"whatever\",\n  input_parameters: {\n    properties: {\n      garbage: { type: Schema.types.string },\n    },\n    required: [\"garbage\"],\n  },\n});\nconst { createContext } = SlackActionHandlerTester(func);\nconst inputs = { garbage: \"in, garbage out\" };\n\nconst getRouter = () => {\n  return BlockActionsRouter(func);\n};\n\nDeno.test(\"ActionsRouter\", async (t) => {\n  await t.step(\n    \"export method returns result of handler when matching action comes in and baseline handler context parameters are present and exist\",\n    async () => {\n      const router = getRouter();\n      let handlerCalled = false;\n      router.addHandler(DEFAULT_ACTION.action_id, (ctx) => {\n        assertExists(ctx.inputs);\n        assertEquals<string>(ctx.inputs.garbage, inputs.garbage);\n        assertExists(ctx.token);\n        assertExists(ctx.action);\n        assertExists(ctx.env);\n        assertExists(ctx.client);\n        handlerCalled = true;\n      });\n      await router(createContext({ inputs }));\n      assertEquals(handlerCalled, true, \"action handler not called!\");\n    },\n  );\n});\n\nDeno.test(\"ActionsRouter action matching happy path\", async (t) => {\n  await t.step(\"simple string matching to action_id\", async () => {\n    const router = getRouter();\n    let handlerCalled = false;\n    router.addHandler(DEFAULT_ACTION.action_id, (ctx) => {\n      assertExists(ctx.inputs);\n      assertExists(ctx.token);\n      assertExists(ctx.action);\n      assertExists(ctx.env);\n      assertExists(ctx.client);\n      handlerCalled = true;\n    });\n    await router(createContext({ inputs }));\n    assertEquals(handlerCalled, true, \"action handler not called!\");\n  });\n  await t.step(\"array of strings matching to action_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy();\n    router.addHandler([\"nope\", DEFAULT_ACTION.action_id], handler);\n    await router(createContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n  await t.step(\"regex matching to action_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy();\n    router.addHandler(/action/, handler);\n    await router(createContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n  await t.step(\"{action_id:string} matching to action_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy();\n    router.addHandler({ action_id: DEFAULT_ACTION.action_id }, handler);\n    await router(createContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n  await t.step(\"{action_id:[string]} matching to action_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy();\n    router.addHandler(\n      { action_id: [\"hahtrickedyou\", DEFAULT_ACTION.action_id] },\n      handler,\n    );\n    await router(createContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n  await t.step(\"{action_id:regex} matching to action_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy();\n    router.addHandler({ action_id: /action/ }, handler);\n    await router(createContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n  await t.step(\"{block_id:string} matching to block_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy();\n    router.addHandler({ block_id: DEFAULT_ACTION.block_id }, handler);\n    await router(createContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n  await t.step(\"{block_id:[string]} matching to block_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy();\n    router.addHandler(\n      { block_id: [\"lol\", DEFAULT_ACTION.block_id] },\n      handler,\n    );\n    await router(createContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n  await t.step(\"{block_id:regex} matching to block_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy();\n    router.addHandler({ block_id: /block/ }, handler);\n    await router(createContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n  await t.step(\n    \"{block_id:string, action_id:string} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        block_id: DEFAULT_ACTION.block_id,\n        action_id: DEFAULT_ACTION.action_id,\n      }, handler);\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n  await t.step(\n    \"{block_id:string, action_id:[string]} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        block_id: DEFAULT_ACTION.block_id,\n        action_id: [\"notthisoneeither\", DEFAULT_ACTION.action_id],\n      }, handler);\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n  await t.step(\n    \"{block_id:string, action_id:regex} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler(\n        { block_id: DEFAULT_ACTION.block_id, action_id: /action/ },\n        handler,\n      );\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n  await t.step(\n    \"{block_id:[string], action_id:string} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        block_id: [\"notthistime\", DEFAULT_ACTION.block_id],\n        action_id: DEFAULT_ACTION.action_id,\n      }, handler);\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n  await t.step(\n    \"{block_id:[string], action_id:[string]} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        block_id: [\"notthistime\", DEFAULT_ACTION.block_id],\n        action_id: [\"gotyougood\", DEFAULT_ACTION.action_id],\n      }, handler);\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n  await t.step(\n    \"{block_id:[string], action_id:regex} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        block_id: [\"notthistime\", DEFAULT_ACTION.block_id],\n        action_id: /action/,\n      }, handler);\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n  await t.step(\n    \"{block_id:regex, action_id:string} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler(\n        { block_id: /block/, action_id: DEFAULT_ACTION.action_id },\n        handler,\n      );\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n  await t.step(\n    \"{block_id:regex, action_id:[string]} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        block_id: /block/,\n        action_id: [\"hahanope\", DEFAULT_ACTION.action_id],\n      }, handler);\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n  await t.step(\n    \"{block_id:regex, action_id:regex} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({ block_id: /block/, action_id: /action/ }, handler);\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n});\n\nDeno.test(\"ActionsRouter action matching sad path\", async (t) => {\n  await t.step(\"unhandled action should throw\", async () => {\n    const router = getRouter();\n    await assertRejects(() => router(createContext({ inputs })));\n  });\n\n  await t.step(\"no false positives\", async (t) => {\n    await t.step(\n      \"not matching action_id: string\",\n      async () => {\n        const router = getRouter();\n        const handler = mock.spy();\n        router.addHandler(\"nope\", handler);\n\n        await assertRejects(() => router(createContext({ inputs })));\n        mock.assertSpyCalls(handler, 0);\n      },\n    );\n  });\n\n  await t.step(\n    \"not matching action_id: string[]\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler([\"nope\", \"nuh uh\"], handler);\n\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n\n  await t.step(\n    \"not matching action_id: regex\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler(/regex/, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n\n  await t.step(\n    \"not matching {action_id: string}\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({ action_id: \"nope\" }, () => handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string[]}\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({ action_id: [\"nope\", \"nuh uh\"] }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: regex}\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({ action_id: /regex/ }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {block_id: string}\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({ block_id: \"nope\" }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {block_id: string[]}\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({ block_id: [\"nope\", \"nuh uh\"] }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {block_id: regex}\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({ block_id: /regex/ }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string, block_id: regex}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: \"not good enough\",\n        block_id: /block/,\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string, block_id: regex}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: \"action_id\",\n        block_id: /noway/,\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string, block_id: string[]}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: \"not good enough\",\n        block_id: [\"notthisonebut\", \"block_id\"],\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string, block_id: string}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: \"action_id\",\n        block_id: [\"this\", \"wont\", \"work\"],\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string, block_id: string}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: \"not good enough\",\n        block_id: \"block_id\",\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string, block_id: string}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: \"action_id\",\n        block_id: \"nicetry\",\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string[], block_id: regex}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: [\"not\", \"good\", \"enough\"],\n        block_id: /block/,\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string[], block_id: regex}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: [\"decoy\", \"action_id\"],\n        block_id: /noway/,\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string[], block_id: string[]}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: [\"not\", \"good\", \"enough\"],\n        block_id: [\"notthisonebut\", \"block_id\"],\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string[], block_id: string}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: [\"decoy\", \"action_id\"],\n        block_id: [\"this\", \"wont\", \"work\"],\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string[], block_id: string}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: [\"not\", \"good\", \"enough\"],\n        block_id: \"block_id\",\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string[], block_id: string}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: [\"decoy\", \"action_id\"],\n        block_id: \"nicetry\",\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: regex, block_id: regex}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: /heh/,\n        block_id: /block/,\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: regex, block_id: regex}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: /action/,\n        block_id: /noway/,\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: regex, block_id: string[]}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: /hah/,\n        block_id: [\"notthisonebut\", \"block_id\"],\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: regex, block_id: string}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: /action/,\n        block_id: [\"this\", \"wont\", \"work\"],\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: regex, block_id: string}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: /huh/,\n        block_id: \"block_id\",\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: regex, block_id: string}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy();\n      router.addHandler({\n        action_id: /action/,\n        block_id: \"nicetry\",\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n});\n"
  },
  {
    "path": "src/functions/interactivity/block_actions_types.ts",
    "content": "/*\nThis is currently a pretty light type of a block_actions payload that we can fill out\nonce we have a way to share some of these payload types across our different libraries better\nSome other references that may be useful to fill this out:\n- https://api.slack.com/changelog/2020-09-full-state-on-view-submisson-and-block-actions\n- https://api.slack.com/reference/interaction-payloads/block-actions#fields\n- https://github.com/slackapi/bolt-js/blob/main/src/types/actions/block-action.ts\n- https://api.slack.com/changelog/2018-05-file-threads-soon-tread\n*/\n\nimport type { BlockAction, BlockElement } from \"./block_kit_types.ts\";\n\n/**\n * @description Block Actions-specific type for the `body` property of a `block_actions` event\n */\nexport type BlockActionsBody = {\n  actions: BlockAction[];\n  /**\n   * @description The encoded application ID the event was dispatched to, e.g. A123456.\n   */\n  api_app_id: string;\n  /**\n   * @description If applicable, the channel the Block Action interaction originated from.\n   */\n  channel?: {\n    /**\n     * @description Encoded channel ID, e.g. C123456.\n     */\n    id: string;\n    /**\n     * @description Channel name, without the \"#\" prefix.\n     */\n    name: string;\n  };\n  /**\n   * @description If applicable, the enterprise associated with the workspace the Block Action interaction originated from. If not applicable, will be null.\n   */\n  enterprise: {\n    /**\n     * @description Encoded enterprise ID, e.g. E123456.\n     */\n    id: string;\n    /**\n     * @description Enterprise name.\n     */\n    name: string;\n  } | null;\n  /**\n   * @description Whether this event originated from a workspace that is part of an enterprise installation.\n   */\n  is_enterprise_install: boolean;\n  /**\n   * @description Information about the source message this Block Action interaction originated from.\n   * This may be optional in the case that the Block Action interaction originated from a view rather than a message.\n   */\n  message?: {\n    /**\n     * @description The encoded application ID the event was dispatched to, e.g. A123456.\n     */\n    app_id: string;\n    /**\n     * @description The {@link https://api.slack.com/block-kit Block Kit} elements included in the message.\n     */\n    blocks: BlockElement[];\n    /**\n     * @description Whether the {@link https://api.slack.com/messaging/managing#threading thread} has been locked.\n     */\n    is_locked: boolean;\n    /**\n     * @description If the {@link https://api.slack.com/messaging/managing#threading thread} has at least one reply, points to the most recent reply's `ts` value.\n     */\n    latest_reply?: string;\n    /**\n     * @description {@link https://api.slack.com/metadata Message metadata}, if any was attached to the message.\n     */\n    metadata?: {\n      event_type: string;\n      event_payload: {\n        // deno-lint-ignore no-explicit-any\n        [key: string]: any;\n      };\n    };\n    /**\n     * @description Total number of replies in the {@link https://api.slack.com/messaging/managing#threading thread}.\n     */\n    reply_count: number;\n    /**\n     * @description Array of up to 5 encoded user IDs (i.e. U12345) that replied in the {@link https://api.slack.com/messaging/managing#threading thread}.\n     */\n    reply_users: string[];\n    /**\n     * @description Total number of users that replied in the {@link https://api.slack.com/messaging/managing#threading thread}.\n     */\n    reply_users_count: number;\n    /**\n     * @description Encoded team ID, e.g. T123456.\n     */\n    team: string;\n    /**\n     * @description The text in the message. If the message was composed of Block Kit elements, this property would\n     * contain the fallback text to display in constrained UIs (like notifications) or in screenreaders. See\n     * {@link https://api.slack.com/methods/chat.postMessage#blocks_and_attachments the API documentation for use of text, blocks and attachments in messages}.\n     */\n    text: string;\n    /**\n     * @description Timestamp of the parent message. If the message is already a parent message, then this value will equal the `ts` value. Use this value if you want to post a message as a {@link https://api.slack.com/messaging/managing#threading threaded reply} to a particular message.\n     */\n    thread_ts: string;\n    /**\n     * @description Timestamp of the message.\n     */\n    ts: string;\n    /**\n     * @description The type of message. This is always \"message.\"\n     */\n    type: \"message\";\n    /**\n     * @description Encoded user ID of the user that posted the message, e.g. U123456.\n     */\n    user: string;\n  };\n  /**\n   * @description The workspace, or team, details the Block Kit interaction originated from.\n   */\n  team: {\n    /**\n     * @description The subdomain of the team, e.g. domain.slack.com\n     */\n    domain: string;\n    /**\n     * @description Encoded team ID, e.g. T123456.\n     */\n    id: string;\n  };\n  token: string;\n  /**\n   * @description A one-time use ID for opening modals or triggering other UI changes based on user interactions.\n   */\n  trigger_id: string;\n  /**\n   * @description Details for the user that initiated the Block Kit action.\n   */\n  user: {\n    /**\n     * @description Encoded user ID, e.g. U123456.\n     */\n    id: string;\n    /**\n     * @description User's handle as seen in the Slack client when e.g. at-notifying the user.\n     */\n    name: string;\n    /**\n     * @description The encoded team ID for the workspace, or team, where the Block Kit action originated from.\n     */\n    team_id: string;\n  };\n  // deno-lint-ignore no-explicit-any\n  [key: string]: any;\n  /* TODO: Other properties seen on this type that should be added at some point:\n   * container: {}; // should be a reference to message or view, depending on the container for the block kit interactive componenet\n   * message container looks like:\n   *  \"container\": {\n        \"type\": \"message\",\n        \"message_ts\": \"1663103912.870299\",\n        \"channel_id\": \"C03DS3P5ED6\",\n        \"is_ephemeral\": false\n      },\n      view container looks like:\n      container: { type: \"view\", view_id: \"V041UDW806B\" }\n   * view: {}; // if the block kit interactive component was part of a view, details for the view are here\n   * state: { values: {}}; // seen but usually empty?\n   */\n};\n"
  },
  {
    "path": "src/functions/interactivity/block_kit_types.ts",
    "content": "/**\n * @description A single Block Kit interactive component interaction.\n */\nexport type BlockAction =\n  & {\n    /**\n     * @description Identifies the Block Kit interactive component that was interacted with.\n     */\n    action_id: string;\n  }\n  & BlockElement;\n\n/**\n * @description A single Block element\n */\nexport type BlockElement = {\n  /**\n   * @description Identifies the block within a surface.\n   */\n  block_id: string;\n  type: string;\n  // deno-lint-ignore no-explicit-any\n  [key: string]: any;\n};\n"
  },
  {
    "path": "src/functions/interactivity/block_suggestion_router.ts",
    "content": "import type {\n  ParameterSetDefinition,\n  PossibleParameterKeys,\n} from \"../../parameters/types.ts\";\nimport type { SlackFunctionDefinition } from \"../definitions/mod.ts\";\nimport { UnhandledEventError } from \"../unhandled-event-error.ts\";\nimport { enrichContext } from \"../enrich-context.ts\";\nimport type {\n  FunctionDefinitionArgs,\n  FunctionRuntimeParameters,\n} from \"../types.ts\";\nimport type {\n  BlockActionConstraint,\n  BlockSuggestionHandler,\n  RuntimeSuggestionContext,\n  SuggestionContext,\n} from \"./types.ts\";\nimport type { BlockAction } from \"./block_kit_types.ts\";\nimport {\n  matchBasicConstraintField,\n  normalizeConstraintToArray,\n} from \"./matchers.ts\";\n\n/**\n * Define a suggestion \"router\" and its input and output parameters for use in a Slack application. The SuggestionRouter will route incoming action events to action-specific handlers.\n * @param {SlackFunctionDefinition<InputParameters, OutputParameters, RequiredInput, RequiredOutput>} func Reference to your previously-created SlackFunction, defined via DefineFunction\n * @returns {SuggestionRouter}\n */\nexport const BlockSuggestionRouter = <\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n>(\n  func: SlackFunctionDefinition<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutput\n  >,\n) => {\n  const router = new SuggestionRouter(func);\n\n  // deno-lint-ignore no-explicit-any\n  const exportedHandler: any = router.export();\n\n  // deno-lint-ignore no-explicit-any\n  exportedHandler.addHandler = ((...args: any) => {\n    router.addHandler.apply(router, args);\n\n    return exportedHandler;\n  }) as typeof router.addHandler;\n\n  return exportedHandler as\n    & ReturnType<typeof router.export>\n    & Pick<typeof router, \"addHandler\">;\n};\n\nexport class SuggestionRouter<\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n> {\n  private routes: Array<\n    [BlockActionConstraint, BlockSuggestionHandler<typeof this.func.definition>]\n  >;\n\n  constructor(\n    private func: SlackFunctionDefinition<\n      InputParameters,\n      OutputParameters,\n      RequiredInput,\n      RequiredOutput\n    >,\n  ) {\n    this.func = func;\n    this.routes = [];\n  }\n\n  /**\n   * Add a suggestion handler for something that can match an action event.\n   * @param {BlockActionConstraint} actionConstraint A BlockActionConstraintField(i.e. a string, array of strings or regular expression) or more complex BlockActionConstraintObject to match incoming block suggestion events. A BlockActionConstraintField parameter are matched with a block suggestion event's `action_id` property. A BlockActionConstraintObject parameter allows to match with other block suggestion event properties like `block_id` as well as `action_id`. If multiple properties are specified using BlockActionConstraintObject, then the event must match ALL provided BlockActionConstraintObject properties.\n   * @returns {SuggestionRouter}\n   */\n  addHandler(\n    actionConstraint: BlockActionConstraint,\n    handler: BlockSuggestionHandler<\n      FunctionDefinitionArgs<\n        InputParameters,\n        OutputParameters,\n        RequiredInput,\n        RequiredOutput\n      >\n    >,\n  ): SuggestionRouter<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutput\n  > {\n    this.routes.push([actionConstraint, handler]);\n    return this;\n  }\n\n  /**\n   * Returns a method handling routing of suggestion payloads to the appropriate suggestion handler.\n   * The output of export() should be attached to the `blockSuggestion` export of your function.\n   */\n  export() {\n    return async (\n      context: RuntimeSuggestionContext<\n        FunctionRuntimeParameters<InputParameters, RequiredInput>\n      >,\n    ) => {\n      const suggestion = context.body;\n      const handler = this.matchHandler(suggestion);\n      if (handler === null) {\n        throw new UnhandledEventError(\n          `Received block suggestion payload with suggestion=${\n            JSON.stringify(suggestion)\n          } but this app has no suggestion handler defined to handle it!`,\n        );\n      }\n\n      const enrichedContext = enrichContext(context) as SuggestionContext<\n        FunctionRuntimeParameters<InputParameters, RequiredInput>\n      >;\n\n      return await handler(enrichedContext);\n    };\n  }\n\n  /**\n   * Return the first registered SuggestionHandler that matches the action and/or block ID string(s) provided.\n   */\n  matchHandler(\n    action: BlockAction, // TODO: this type name is a bit misleading; BlockAction just has both action_id and block_id props on it, so it applies to both block_suggestion and block_action payloads\n  ):\n    | BlockSuggestionHandler<\n      FunctionDefinitionArgs<\n        InputParameters,\n        OutputParameters,\n        RequiredInput,\n        RequiredOutput\n      >\n    >\n    | null {\n    for (let i = 0; i < this.routes.length; i++) {\n      const route = this.routes[i];\n      let [constraint, handler] = route;\n      // Handle different constraint types below\n      if (\n        constraint instanceof RegExp || constraint instanceof Array ||\n        typeof constraint === \"string\"\n      ) {\n        // Normalize simple string constraints to be an array of strings for consistency in handling inside this method.\n        constraint = normalizeConstraintToArray(constraint);\n        // Handle the case where the constraint is either a regex or an array of strings to match against action_id\n        if (matchBasicConstraintField(constraint, \"action_id\", action)) {\n          return handler;\n        }\n      } else {\n        // Assumes an object as a constraint (type BlockActionConstraintObject)\n        // Return first match *within* any of the defined fields on the constaint object, but ensure there is a match on *all* defined fields\n        // Effectively a logical AND across the action_id and block_id field(s)\n        // If either of the constraint fields are not defined, pre-set them to have matched so we can effectively\n        // ignore them when determining if we have a match by &&'ing them\n        let actionIDMatched = constraint.action_id ? false : true;\n        let blockIDMatched = constraint.block_id ? false : true;\n        if (constraint.action_id) {\n          actionIDMatched = matchBasicConstraintField(\n            normalizeConstraintToArray(constraint.action_id),\n            \"action_id\",\n            action,\n          );\n        }\n        if (constraint.block_id) {\n          blockIDMatched = matchBasicConstraintField(\n            normalizeConstraintToArray(constraint.block_id),\n            \"block_id\",\n            action,\n          );\n        }\n        if (blockIDMatched && actionIDMatched) {\n          return handler;\n        }\n      }\n    }\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/functions/interactivity/block_suggestion_router_test.ts",
    "content": "import { SlackAPI } from \"../../deps.ts\";\nimport {\n  assertEquals,\n  assertExists,\n  assertRejects,\n  mock,\n} from \"../../dev_deps.ts\";\nimport { BlockSuggestionRouter } from \"./block_suggestion_router.ts\";\nimport type { SuggestionContext } from \"./types.ts\";\nimport type {\n  FunctionParameters,\n  FunctionRuntimeParameters,\n} from \"../types.ts\";\nimport type {\n  ParameterSetDefinition,\n  PossibleParameterKeys,\n} from \"../../parameters/types.ts\";\nimport type { SlackFunctionDefinition } from \"../definitions/mod.ts\";\nimport { DefineFunction, Schema } from \"../../mod.ts\";\n\n// Helper test types\n// TODO: maybe we want to export this for userland usage at some point?\n// Very much a direct copy from the existing main function tester types and utilties in src/functions/tester\ntype SlackSuggestionHandlerTesterArgs<\n  InputParameters extends FunctionParameters,\n> =\n  & Partial<\n    SuggestionContext<InputParameters>\n  >\n  & {\n    inputs: InputParameters;\n  };\n\ntype CreateSuggestionHandlerContext<\n  InputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n> = {\n  (\n    args: SlackSuggestionHandlerTesterArgs<\n      FunctionRuntimeParameters<InputParameters, RequiredInput>\n    >,\n  ): SuggestionContext<\n    FunctionRuntimeParameters<InputParameters, RequiredInput>\n  >;\n};\n\ntype SlackSuggestionHandlerTesterResponse<\n  InputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n> = {\n  createContext: CreateSuggestionHandlerContext<InputParameters, RequiredInput>;\n};\n\ntype SlackSuggestionHandlerTesterSignature = {\n  <\n    InputParameters extends ParameterSetDefinition,\n    OutputParameters extends ParameterSetDefinition,\n    RequiredInput extends PossibleParameterKeys<InputParameters>,\n    RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n  >(\n    func: SlackFunctionDefinition<\n      InputParameters,\n      OutputParameters,\n      RequiredInput,\n      RequiredOutput\n    >,\n  ): SlackSuggestionHandlerTesterResponse<\n    InputParameters,\n    RequiredInput\n  >;\n};\n\nconst DEFAULT_ACTION_ID = \"action_id\";\nconst DEFAULT_BLOCK_ID = \"block_id\";\n// deno-lint-ignore no-explicit-any\nconst generateSuggestion = (inputs: any) => ({\n  block_id: DEFAULT_BLOCK_ID,\n  action_id: DEFAULT_ACTION_ID,\n  value: \"ohai\",\n  type: \"block_suggestion\",\n  function_data: {\n    execution_id: \"123\",\n    function: { callback_id: \"456\" },\n    inputs,\n  },\n  interactivity: {\n    interactor: {\n      secret: \"shhhh\",\n      id: \"123\",\n    },\n    interactivity_pointer: \"123.asdf\",\n  },\n  user: {\n    id: \"123\",\n    name: \"asdf\",\n    team_id: \"123\",\n  },\n  team: {\n    id: \"123\",\n    domain: \"asdf\",\n  },\n  enterprise: null,\n  api_app_id: \"123\",\n});\n\nconst SlackSuggestionHandlerTester: SlackSuggestionHandlerTesterSignature = <\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n>(\n  _func: SlackFunctionDefinition<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutput\n  >,\n) => {\n  const createContext: CreateSuggestionHandlerContext<\n    InputParameters,\n    RequiredInput\n  > = (\n    args,\n  ) => {\n    const inputs = (args.inputs || {}) as FunctionRuntimeParameters<\n      InputParameters,\n      RequiredInput\n    >;\n    const token = args.token || \"slack-function-test-token\";\n\n    return {\n      inputs,\n      env: args.env || {},\n      token,\n      client: SlackAPI(token),\n      team_id: args.team_id || \"test-team-id\",\n      enterprise_id: \"\",\n      body: args.body || generateSuggestion(inputs),\n    };\n  };\n\n  return { createContext };\n};\n\n// a basic function definition and associated block action router to test\nconst func = DefineFunction({\n  callback_id: \"id\",\n  title: \"test\",\n  source_file: \"whatever\",\n  input_parameters: {\n    properties: {\n      garbage: { type: Schema.types.string },\n    },\n    required: [\"garbage\"],\n  },\n});\nconst { createContext } = SlackSuggestionHandlerTester(func);\nconst inputs = { garbage: \"in, garbage out\" };\n\nconst getRouter = () => {\n  return BlockSuggestionRouter(func);\n};\n\nDeno.test(\"SuggestionRouter\", async (t) => {\n  await t.step(\n    \"export method returns result of handler when matching suggestion comes in and baseline handler context parameters are present and exist\",\n    async () => {\n      const router = getRouter();\n      let handlerCalled = false;\n      router.addHandler(DEFAULT_ACTION_ID, (ctx) => {\n        assertExists(ctx.inputs);\n        assertEquals<string>(ctx.inputs.garbage, inputs.garbage);\n        assertExists(ctx.token);\n        assertExists(ctx.body);\n        assertExists(ctx.env);\n        assertExists(ctx.client);\n        handlerCalled = true;\n        return { options: [] };\n      });\n      await router(createContext({ inputs }));\n      assertEquals(handlerCalled, true, \"suggestion handler not called!\");\n    },\n  );\n});\n\nDeno.test(\"SuggestionRouter matching happy path\", async (t) => {\n  await t.step(\"simple string matching to action_id\", async () => {\n    const router = getRouter();\n    let handlerCalled = false;\n    router.addHandler(DEFAULT_ACTION_ID, (ctx) => {\n      assertExists(ctx.inputs);\n      assertExists(ctx.token);\n      assertExists(ctx.body);\n      assertExists(ctx.env);\n      assertExists(ctx.client);\n      handlerCalled = true;\n      return { options: [] };\n    });\n    await router(createContext({ inputs }));\n    assertEquals(handlerCalled, true, \"suggestion handler not called!\");\n  });\n  await t.step(\"array of strings matching to action_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy(() => ({ options: [] }));\n    router.addHandler([\"nope\", DEFAULT_ACTION_ID], handler);\n    await router(createContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n  await t.step(\"regex matching to action_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy(() => ({ options: [] }));\n    router.addHandler(/action/, handler);\n    await router(createContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n  await t.step(\"{action_id:string} matching to action_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy(() => ({ options: [] }));\n    router.addHandler({ action_id: DEFAULT_ACTION_ID }, handler);\n    await router(createContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n  await t.step(\"{action_id:[string]} matching to action_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy(() => ({ options: [] }));\n    router.addHandler(\n      { action_id: [\"hahtrickedyou\", DEFAULT_ACTION_ID] },\n      handler,\n    );\n    await router(createContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n  await t.step(\"{action_id:regex} matching to action_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy(() => ({ options: [] }));\n    router.addHandler({ action_id: /action/ }, handler);\n    await router(createContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n  await t.step(\"{block_id:string} matching to block_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy(() => ({ options: [] }));\n    router.addHandler({ block_id: DEFAULT_BLOCK_ID }, handler);\n    await router(createContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n  await t.step(\"{block_id:[string]} matching to block_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy(() => ({ options: [] }));\n    router.addHandler(\n      { block_id: [\"lol\", DEFAULT_BLOCK_ID] },\n      handler,\n    );\n    await router(createContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n  await t.step(\"{block_id:regex} matching to block_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy(() => ({ options: [] }));\n    router.addHandler({ block_id: /block/ }, handler);\n    await router(createContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n  await t.step(\n    \"{block_id:string, action_id:string} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        block_id: DEFAULT_BLOCK_ID,\n        action_id: DEFAULT_ACTION_ID,\n      }, handler);\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n  await t.step(\n    \"{block_id:string, action_id:[string]} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        block_id: DEFAULT_BLOCK_ID,\n        action_id: [\"notthisoneeither\", DEFAULT_ACTION_ID],\n      }, handler);\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n  await t.step(\n    \"{block_id:string, action_id:regex} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler(\n        { block_id: DEFAULT_BLOCK_ID, action_id: /action/ },\n        handler,\n      );\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n  await t.step(\n    \"{block_id:[string], action_id:string} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        block_id: [\"notthistime\", DEFAULT_BLOCK_ID],\n        action_id: DEFAULT_ACTION_ID,\n      }, handler);\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n  await t.step(\n    \"{block_id:[string], action_id:[string]} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        block_id: [\"notthistime\", DEFAULT_BLOCK_ID],\n        action_id: [\"gotyougood\", DEFAULT_ACTION_ID],\n      }, handler);\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n  await t.step(\n    \"{block_id:[string], action_id:regex} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        block_id: [\"notthistime\", DEFAULT_BLOCK_ID],\n        action_id: /action/,\n      }, handler);\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n  await t.step(\n    \"{block_id:regex, action_id:string} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler(\n        { block_id: /block/, action_id: DEFAULT_ACTION_ID },\n        handler,\n      );\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n  await t.step(\n    \"{block_id:regex, action_id:[string]} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        block_id: /block/,\n        action_id: [\"hahanope\", DEFAULT_ACTION_ID],\n      }, handler);\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n  await t.step(\n    \"{block_id:regex, action_id:regex} matching to both block_id and action_id\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({ block_id: /block/, action_id: /action/ }, handler);\n      await router(createContext({ inputs }));\n      mock.assertSpyCalls(handler, 1);\n    },\n  );\n});\n\nDeno.test(\"SuggestionRouter matching sad path\", async (t) => {\n  await t.step(\"unhandled suggestion should throw\", async () => {\n    const router = getRouter();\n    await assertRejects(() => router(createContext({ inputs })));\n  });\n\n  await t.step(\"no false positives\", async (t) => {\n    await t.step(\n      \"not matching action_id: string\",\n      async () => {\n        const router = getRouter();\n        const handler = mock.spy(() => ({ options: [] }));\n        router.addHandler(\"nope\", handler);\n\n        await assertRejects(() => router(createContext({ inputs })));\n        mock.assertSpyCalls(handler, 0);\n      },\n    );\n  });\n\n  await t.step(\n    \"not matching action_id: string[]\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler([\"nope\", \"nuh uh\"], handler);\n\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n\n  await t.step(\n    \"not matching action_id: regex\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler(/regex/, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n\n  await t.step(\n    \"not matching {action_id: string}\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({ action_id: \"nope\" }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string[]}\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({ action_id: [\"nope\", \"nuh uh\"] }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: regex}\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({ action_id: /regex/ }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {block_id: string}\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({ block_id: \"nope\" }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {block_id: string[]}\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({ block_id: [\"nope\", \"nuh uh\"] }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {block_id: regex}\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({ block_id: /regex/ }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string, block_id: regex}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: \"not good enough\",\n        block_id: /block/,\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string, block_id: regex}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: DEFAULT_ACTION_ID,\n        block_id: /noway/,\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string, block_id: string[]}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: \"not good enough\",\n        block_id: [\"notthisonebut\", DEFAULT_BLOCK_ID],\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string, block_id: string}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: DEFAULT_ACTION_ID,\n        block_id: [\"this\", \"wont\", \"work\"],\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string, block_id: string}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: \"not good enough\",\n        block_id: DEFAULT_BLOCK_ID,\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string, block_id: string}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: DEFAULT_ACTION_ID,\n        block_id: \"nicetry\",\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string[], block_id: regex}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: [\"not\", \"good\", \"enough\"],\n        block_id: /block/,\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string[], block_id: regex}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: [\"decoy\", DEFAULT_ACTION_ID],\n        block_id: /noway/,\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string[], block_id: string[]}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: [\"not\", \"good\", \"enough\"],\n        block_id: [\"notthisonebut\", DEFAULT_BLOCK_ID],\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string[], block_id: string}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: [\"decoy\", DEFAULT_ACTION_ID],\n        block_id: [\"this\", \"wont\", \"work\"],\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string[], block_id: string}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: [\"not\", \"good\", \"enough\"],\n        block_id: DEFAULT_BLOCK_ID,\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: string[], block_id: string}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: [\"decoy\", DEFAULT_ACTION_ID],\n        block_id: \"nicetry\",\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: regex, block_id: regex}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: /heh/,\n        block_id: /block/,\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: regex, block_id: regex}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: /action/,\n        block_id: /noway/,\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: regex, block_id: string[]}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: /hah/,\n        block_id: [\"notthisonebut\", DEFAULT_BLOCK_ID],\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: regex, block_id: string}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: /action/,\n        block_id: [\"this\", \"wont\", \"work\"],\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: regex, block_id: string}, action_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: /huh/,\n        block_id: DEFAULT_BLOCK_ID,\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n  await t.step(\n    \"not matching {action_id: regex, block_id: string}, block_id does not match\",\n    async () => {\n      const router = getRouter();\n      const handler = mock.spy(() => ({ options: [] }));\n      router.addHandler({\n        action_id: /action/,\n        block_id: \"nicetry\",\n      }, handler);\n      await assertRejects(() => router(createContext({ inputs })));\n      mock.assertSpyCalls(handler, 0);\n    },\n  );\n});\n"
  },
  {
    "path": "src/functions/interactivity/block_suggestion_types.ts",
    "content": "import type { BlockAction, BlockElement } from \"./block_kit_types.ts\";\n\n// TODO: lots of duplication here with block_actions_types.ts\n/**\n * @description Block Suggestion-specific type for the `body` property of a `block_suggestion` event\n */\nexport type BlockSuggestionBody =\n  & BlockAction // adds block_id and action_id properties\n  & {\n    /**\n     * @description The encoded application ID the event was dispatched to, e.g. A123456.\n     */\n    api_app_id: string;\n    /**\n     * @description If applicable, the channel the Block Suggestion interaction originated from.\n     */\n    channel?: {\n      /**\n       * @description Encoded channel ID, e.g. C123456.\n       */\n      id: string;\n      /**\n       * @description Channel name, without the \"#\" prefix.\n       */\n      name: string;\n    };\n    /**\n     * @description If applicable, the enterprise associated with the workspace the Block Suggestion interaction originated from. If not applicable, will be null.\n     */\n    enterprise: {\n      /**\n       * @description Encoded enterprise ID, e.g. E123456.\n       */\n      id: string;\n      /**\n       * @description Enterprise name.\n       */\n      name: string;\n    } | null;\n    /**\n     * @description Information about the source message this Block Suggestion interaction originated from.\n     * This may be optional in the case that the Block Suggestion interaction originated from a view rather than a message.\n     */\n    message?: {\n      /**\n       * @description The encoded application ID the event was dispatched to, e.g. A123456.\n       */\n      app_id: string;\n      /**\n       * @description The {@link https://api.slack.com/block-kit Block Kit} elements included in the message.\n       */\n      blocks: BlockElement[];\n      /**\n       * @description Whether the {@link https://api.slack.com/messaging/managing#threading thread} has been locked.\n       */\n      is_locked: boolean;\n      /**\n       * @description If the {@link https://api.slack.com/messaging/managing#threading thread} has at least one reply, points to the most recent reply's `ts` value.\n       */\n      latest_reply?: string;\n      /**\n       * @description {@link https://api.slack.com/metadata Message metadata}, if any was attached to the message.\n       */\n      metadata?: {\n        event_type: string;\n        event_payload: {\n          // deno-lint-ignore no-explicit-any\n          [key: string]: any;\n        };\n      };\n      /**\n       * @description Total number of replies in the {@link https://api.slack.com/messaging/managing#threading thread}.\n       */\n      reply_count: number;\n      /**\n       * @description Array of up to 5 encoded user IDs (i.e. U12345) that replied in the {@link https://api.slack.com/messaging/managing#threading thread}.\n       */\n      reply_users: string[];\n      /**\n       * @description Total number of users that replied in the {@link https://api.slack.com/messaging/managing#threading thread}.\n       */\n      reply_users_count: number;\n      /**\n       * @description Encoded team ID, e.g. T123456.\n       */\n      team: string;\n      /**\n       * @description The text in the message. If the message was composed of Block Kit elements, this property would\n       * contain the fallback text to display in constrained UIs (like notifications) or in screenreaders. See\n       * {@link https://api.slack.com/methods/chat.postMessage#blocks_and_attachments the API documentation for use of text, blocks and attachments in messages}.\n       */\n      text: string;\n      /**\n       * @description Timestamp of the parent message. If the message is already a parent message, then this value will equal the `ts` value. Use this value if you want to post a message as a {@link https://api.slack.com/messaging/managing#threading threaded reply} to a particular message.\n       */\n      thread_ts: string;\n      /**\n       * @description Timestamp of the message.\n       */\n      ts: string;\n      /**\n       * @description The type of message. This is always \"message.\"\n       */\n      type: \"message\";\n      /**\n       * @description Encoded user ID of the user that posted the message, e.g. U123456.\n       */\n      user: string;\n    };\n    /**\n     * @description The workspace, or team, details the Block Kit interaction originated from.\n     */\n    team: {\n      /**\n       * @description The subdomain of the team, e.g. domain.slack.com\n       */\n      domain: string;\n      /**\n       * @description Encoded team ID, e.g. T123456.\n       */\n      id: string;\n    };\n    /**\n     * @description Details for the user that initiated the Block Suggestion interaction.\n     */\n    user: {\n      /**\n       * @description Encoded user ID, e.g. U123456.\n       */\n      id: string;\n      /**\n       * @description User's handle as seen in the Slack client when e.g. at-notifying the user.\n       */\n      name: string;\n      /**\n       * @description The encoded team ID for the workspace, or team, where the Block Suggestion originated from.\n       */\n      team_id: string;\n    };\n    /**\n     * @description The value the user entered into the select menu.\n     */\n    value: string;\n    // deno-lint-ignore no-explicit-any\n    [key: string]: any;\n    /* TODO: Other properties seen on this type that should be added at some point:\n   * container: {}; // should be a reference to message or view, depending on the container for the block kit interactive componenet\n   * message container looks like:\n   *  \"container\": {\n        \"type\": \"message\",\n        \"message_ts\": \"1663103912.870299\",\n        \"channel_id\": \"C03DS3P5ED6\",\n        \"is_ephemeral\": false\n      },\n      view container looks like:\n      container: { type: \"view\", view_id: \"V041UDW806B\" }\n   * view: {}; // if the block kit interactive component was part of a view, details for the view are here\n   */\n  };\n"
  },
  {
    "path": "src/functions/interactivity/matchers.ts",
    "content": "import type {\n  BasicConstraintField,\n  BlockActionConstraintObject,\n} from \"./types.ts\";\nimport type { BlockAction } from \"./block_kit_types.ts\";\nimport type { View } from \"./view_types.ts\";\n\nexport function normalizeConstraintToArray(constraint: BasicConstraintField) {\n  if (typeof constraint === \"string\") {\n    constraint = [constraint];\n  }\n  return constraint;\n}\n\nexport function matchBasicConstraintField(\n  constraint: BasicConstraintField,\n  field: keyof BlockActionConstraintObject | \"callback_id\",\n  payload: BlockAction | View,\n) {\n  if (constraint instanceof RegExp) {\n    if (payload[field].match(constraint)) {\n      return true;\n    }\n  } else if (constraint instanceof Array) {\n    for (let j = 0; j < constraint.length; j++) {\n      const c = constraint[j];\n      if (payload[field] === c) {\n        return true;\n      }\n    }\n  }\n  return false;\n}\n"
  },
  {
    "path": "src/functions/interactivity/mod.ts",
    "content": "export { BlockActionsRouter } from \"./block_actions_router.ts\";\nexport { ViewsRouter } from \"./view_router.ts\";\nexport { BlockSuggestionRouter } from \"./block_suggestion_router.ts\";\n"
  },
  {
    "path": "src/functions/interactivity/types.ts",
    "content": "import type {\n  BaseRuntimeFunctionContext,\n  FunctionContextEnrichment,\n  FunctionDefinitionArgs,\n  FunctionParameters,\n  FunctionRuntimeParameters,\n} from \"../types.ts\";\nimport type { BlockActionsBody } from \"./block_actions_types.ts\";\nimport type { BlockSuggestionBody } from \"./block_suggestion_types.ts\";\nimport type { BlockAction } from \"./block_kit_types.ts\";\nimport type {\n  View,\n  ViewClosedBody,\n  ViewEvents,\n  ViewSubmissionBody,\n} from \"./view_types.ts\";\n\nexport type BlockActionHandler<Definition> = Definition extends\n  FunctionDefinitionArgs<infer I, infer O, infer RI, infer RO> ? {\n    (\n      context: ActionContext<FunctionRuntimeParameters<I, RI>>,\n      // deno-lint-ignore no-explicit-any\n    ): Promise<any> | any;\n  }\n  : never;\n\nexport type BlockSuggestionHandler<Definition> = Definition extends\n  FunctionDefinitionArgs<infer I, infer O, infer RI, infer RO> ? {\n    (\n      context: SuggestionContext<FunctionRuntimeParameters<I, RI>>,\n    ): Promise<BlockSuggestionHandlerResponse> | BlockSuggestionHandlerResponse;\n  }\n  : never;\n\ntype BlockSuggestionHandlerResponse =\n  | BlockSuggestionHandlerOptionsResponse\n  | BlockSuggestionHandlerOptionGroupsResponse;\n\ntype BlockSuggestionHandlerOptionsResponse = {\n  options: MenuOption[];\n};\n\ntype BlockSuggestionHandlerOptionGroupsResponse = {\n  option_groups: MenuOptionGroup[];\n};\n\ntype MenuOptionGroup = {\n  /**\n   * @description A {@link PlainTextObject} that defines the label shown above this group of options. Maximum length for the `text` property inside this field is 75 characters.\n   */\n  label: PlainTextObject;\n  /**\n   * @description An array of {@link MenuOption} objects that belong to this specific group. Maximum of 100 items.\n   */\n  options: MenuOption[];\n};\n\ntype MenuOption = {\n  /**\n   * @description A {@link PlainTextObject} that defines the text shown in the option on the menu. Maximum length for the `text` property inside this field is 75 characters.\n   */\n  text: PlainTextObject;\n  /**\n   * @description A unique string value that will be passed to your app when this option is chosen. Maximum length for this filed is 75 characters.\n   */\n  value: string;\n};\n\ntype PlainTextObject = {\n  type: \"plain_text\";\n  /**\n   * @description The text for the object.\n   */\n  text: string;\n  /**\n   * @description Indicates whether emojis in the text field shoul be escaped into a colon emoji format.\n   */\n  emoji?: boolean;\n};\n\nexport type ViewSubmissionHandler<Definition> = Definition extends\n  FunctionDefinitionArgs<infer I, infer O, infer RI, infer RO> ? {\n    (\n      context: ViewSubmissionContext<FunctionRuntimeParameters<I, RI>>,\n      // deno-lint-ignore no-explicit-any\n    ): Promise<any> | any;\n  }\n  : never;\n\nexport type ViewClosedHandler<Definition> = Definition extends\n  FunctionDefinitionArgs<infer I, infer O, infer RI, infer RO> ? {\n    (\n      context: ViewClosedContext<FunctionRuntimeParameters<I, RI>>,\n      // deno-lint-ignore no-explicit-any\n    ): Promise<any> | any;\n  }\n  : never;\n\nexport type UnhandledEventHandler<Definition> = Definition extends\n  FunctionDefinitionArgs<infer I, infer O, infer RI, infer RO> ? {\n    (\n      context: UnhandledEventContext<FunctionRuntimeParameters<I, RI>>,\n      // deno-lint-ignore no-explicit-any\n    ): Promise<any> | any;\n  }\n  : never;\n\nexport type BaseInteractivityContext<\n  InputParameters extends FunctionParameters,\n> =\n  & BaseRuntimeFunctionContext<InputParameters>\n  & FunctionContextEnrichment;\n\nexport type ActionContext<InputParameters extends FunctionParameters> =\n  & BaseInteractivityContext<InputParameters>\n  & ActionSpecificContext<InputParameters>;\n\nexport type SuggestionContext<InputParameters extends FunctionParameters> =\n  & BaseInteractivityContext<InputParameters>\n  & SuggestionSpecificContext<InputParameters>;\n\nexport type ViewSubmissionContext<InputParameters extends FunctionParameters> =\n  & BaseInteractivityContext<InputParameters>\n  & ViewSubmissionSpecificContext<InputParameters>;\n\nexport type ViewClosedContext<InputParameters extends FunctionParameters> =\n  & BaseInteractivityContext<InputParameters>\n  & ViewClosedSpecificContext<InputParameters>;\n\nexport type UnhandledEventContext<InputParameters extends FunctionParameters> =\n  & BaseInteractivityContext<\n    InputParameters\n  >\n  & UnhandledEventSpecificContext<InputParameters>;\n\ntype ActionSpecificContext<InputParameters extends FunctionParameters> = {\n  body: BlockActionInvocationBody<InputParameters>;\n  action: BlockAction;\n};\n\ntype SuggestionSpecificContext<InputParameters extends FunctionParameters> = {\n  body: BlockSuggestionInvocationBody<InputParameters>;\n};\n\ntype ViewSubmissionSpecificContext<InputParameters extends FunctionParameters> =\n  {\n    body: ViewSubmissionInvocationBody<InputParameters>;\n    view: View;\n  };\n\ntype ViewClosedSpecificContext<InputParameters extends FunctionParameters> = {\n  body: ViewClosedInvocationBody<InputParameters>;\n  view: View;\n};\n\ntype UnhandledEventSpecificContext<InputParameters extends FunctionParameters> =\n  {\n    // unhandled events will contain at least function_data, but the rest is unknown\n    body:\n      & Pick<FunctionInteractivity<InputParameters>, \"function_data\">\n      & {\n        // deno-lint-ignore no-explicit-any\n        [key: string]: any;\n      };\n  };\n\nexport type BlockActionInvocationBody<\n  InputParameters extends FunctionParameters,\n> =\n  & BlockActionsBody\n  & FunctionInteractivity<InputParameters>;\n\nexport type BlockSuggestionInvocationBody<\n  InputParameters extends FunctionParameters,\n> =\n  & BlockSuggestionBody\n  & FunctionInteractivity<InputParameters>;\n\nexport type ViewSubmissionInvocationBody<\n  InputParameters extends FunctionParameters,\n> =\n  & ViewSubmissionBody\n  & FunctionInteractivity<InputParameters>;\n\nexport type ViewClosedInvocationBody<\n  InputParameters extends FunctionParameters,\n> =\n  & ViewClosedBody\n  & FunctionInteractivity<InputParameters>;\n\ntype FunctionInteractivity<InputParameters extends FunctionParameters> = {\n  function_data: FunctionData<InputParameters>;\n  interactivity: Interactivity;\n};\n\ntype FunctionData<InputParameters extends FunctionParameters> = {\n  function: {\n    callback_id: string;\n  };\n  execution_id: string;\n  inputs: InputParameters;\n};\n\ntype Interactivity = {\n  interactor: UserContext;\n  interactivity_pointer: string;\n};\n\ntype UserContext = {\n  secret: string;\n  id: string;\n};\n\n// TODO: with the arrival of block_suggestion payloads, the naming here is not\n// fully accurate: these constraint fields can apply to both block_actions and block_suggestion\n// payloads. Perhaps worth renaming to BlockConstraint?\n/**\n * @description An {@link BlockActionConstraintObject} or {@link BasicConstraintField} constraining which Block Kit interaction payloads get handled by particular interactivity handlers.\n */\nexport type BlockActionConstraint =\n  | BasicConstraintField\n  | BlockActionConstraintObject;\n\n/**\n * @description An object constraining which Block Kit interaction payloads get handled by particular interactivity handlers.\n * If both `block_id` and `action_id` properties are specified, then both properties must have their constraint satisfied in order for there to be a match.\n */\nexport type BlockActionConstraintObject = {\n  /**\n   * @description A {@link BasicConstraintField} to match against the `block_id` property of a Block Kit interactivity event.\n   */\n  block_id?: BasicConstraintField;\n  /**\n   * @description A {@link BasicConstraintField} to match against the `action_id` property of a Block Kit interactivity event.\n   */\n  action_id?: BasicConstraintField;\n};\n\nexport type ViewConstraintObject = {\n  type: ViewEvents;\n  callback_id: BasicConstraintField;\n};\n\nexport type BasicConstraintField = string | string[] | RegExp;\n\n// -- These types represent the deno-slack-runtime function handler interfaces\nexport type RuntimeSuggestionContext<\n  InputParameters extends FunctionParameters,\n> =\n  & BaseRuntimeFunctionContext<InputParameters>\n  & SuggestionSpecificContext<InputParameters>;\n\nexport type RuntimeActionContext<InputParameters extends FunctionParameters> =\n  & BaseRuntimeFunctionContext<InputParameters>\n  & ActionSpecificContext<InputParameters>;\n\nexport type RuntimeViewSubmissionContext<\n  InputParameters extends FunctionParameters,\n> =\n  & BaseRuntimeFunctionContext<InputParameters>\n  & ViewSubmissionSpecificContext<InputParameters>;\n\nexport type RuntimeViewClosedContext<\n  InputParameters extends FunctionParameters,\n> =\n  & BaseRuntimeFunctionContext<InputParameters>\n  & ViewClosedSpecificContext<InputParameters>;\n"
  },
  {
    "path": "src/functions/interactivity/view_router.ts",
    "content": "import type {\n  ParameterSetDefinition,\n  PossibleParameterKeys,\n} from \"../../parameters/types.ts\";\nimport type { SlackFunctionDefinition } from \"../definitions/mod.ts\";\nimport { UnhandledEventError } from \"../unhandled-event-error.ts\";\nimport { enrichContext } from \"../enrich-context.ts\";\nimport type {\n  FunctionDefinitionArgs,\n  FunctionRuntimeParameters,\n} from \"../types.ts\";\nimport {\n  matchBasicConstraintField,\n  normalizeConstraintToArray,\n} from \"./matchers.ts\";\nimport type {\n  BasicConstraintField,\n  RuntimeViewClosedContext,\n  RuntimeViewSubmissionContext,\n  ViewClosedHandler,\n  ViewConstraintObject,\n  ViewSubmissionHandler,\n} from \"./types.ts\";\nimport type { View, ViewEvents } from \"./view_types.ts\";\n\nexport const ViewsRouter = <\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n>(\n  func: SlackFunctionDefinition<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutput\n  >,\n) => {\n  return new ViewRouter(func);\n};\n\nclass ViewRouter<\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n> {\n  private closedRoutes: Array<\n    [\n      ViewConstraintObject,\n      ViewClosedHandler<typeof this.func.definition>,\n    ]\n  >;\n  private submissionRoutes: Array<\n    [\n      ViewConstraintObject,\n      ViewSubmissionHandler<typeof this.func.definition>,\n    ]\n  >;\n\n  constructor(\n    private func: SlackFunctionDefinition<\n      InputParameters,\n      OutputParameters,\n      RequiredInput,\n      RequiredOutput\n    >,\n  ) {\n    this.func = func;\n    this.submissionRoutes = [];\n    this.closedRoutes = [];\n\n    // Bind these two handler functions as they're meant to exported directly by the user-defined function module\n    this.viewClosed = this.viewClosed.bind(this);\n    this.viewSubmission = this.viewSubmission.bind(this);\n  }\n\n  /**\n   * Add a handler for view_closed events\n   * @param {BasicConstraintField} viewConstraint A view constraing (i.e. a string, array of strings or regular expression) that matches against view_closed event's `callback_id` property.\n   * @param handler A handler function for the matched view_closed event\n   * @returns {ViewRouter}\n   */\n  addClosedHandler(\n    viewConstraint: BasicConstraintField,\n    handler: ViewClosedHandler<\n      FunctionDefinitionArgs<\n        InputParameters,\n        OutputParameters,\n        RequiredInput,\n        RequiredOutput\n      >\n    >,\n  ): ViewRouter<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutput\n  > {\n    const constraint: ViewConstraintObject = {\n      type: \"view_closed\",\n      callback_id: viewConstraint,\n    };\n    this.closedRoutes.push([constraint, handler]);\n    return this;\n  }\n\n  /**\n   * Add a handler for view_submission events\n   * @param {BasicConstraintField} viewConstraint A view constraing (i.e. a string, array of strings or regular expression) that matches against view_submission event's `callback_id` property.\n   * @param handler A handler function for the matched view_submission event\n   * @returns {ViewRouter}\n   */\n  addSubmissionHandler(\n    viewConstraint: BasicConstraintField,\n    handler: ViewSubmissionHandler<\n      FunctionDefinitionArgs<\n        InputParameters,\n        OutputParameters,\n        RequiredInput,\n        RequiredOutput\n      >\n    >,\n  ): ViewRouter<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutput\n  > {\n    const constraint: ViewConstraintObject = {\n      type: \"view_submission\",\n      callback_id: viewConstraint,\n    };\n    this.submissionRoutes.push([constraint, handler]);\n    return this;\n  }\n\n  /**\n   * Method for handling view_closed events. This should be the `viewClosed` export of your function module.\n   */\n  async viewClosed(\n    context: RuntimeViewClosedContext<\n      FunctionRuntimeParameters<InputParameters, RequiredInput>\n    >,\n  ) {\n    const handler = this.matchHandler(context.body.type, context.view);\n    if (handler === null) {\n      throw new UnhandledEventError(\n        `Received ${context.body.type} payload ${\n          JSON.stringify(context.view)\n        } but this app has no view handler defined to handle it!`,\n      );\n    }\n    const enrichedContext = enrichContext(context);\n\n    return await handler(enrichedContext);\n  }\n\n  /**\n   * Method for handling view_submission events. This should be the `viewSubmission` export of your function module.\n   */\n  async viewSubmission(\n    context: RuntimeViewSubmissionContext<\n      FunctionRuntimeParameters<InputParameters, RequiredInput>\n    >,\n  ) {\n    const handler = this.matchHandler(context.body.type, context.view);\n    if (handler === null) {\n      throw new UnhandledEventError(\n        `Received ${context.body.type} payload ${\n          JSON.stringify(context.view)\n        } but this app has no view handler defined to handle it!`,\n      );\n    }\n    const enrichedContext = enrichContext(context);\n\n    return await handler(enrichedContext);\n  }\n\n  private matchHandler(\n    type: ViewEvents,\n    view: View,\n    // deno-lint-ignore no-explicit-any\n  ): any {\n    let routes;\n    let _handler: typeof type extends \"view_closed\" ? ViewClosedHandler<\n        FunctionDefinitionArgs<\n          InputParameters,\n          OutputParameters,\n          RequiredInput,\n          RequiredOutput\n        >\n      >\n      : ViewSubmissionHandler<\n        FunctionDefinitionArgs<\n          InputParameters,\n          OutputParameters,\n          RequiredInput,\n          RequiredOutput\n        >\n      >;\n    if (type === \"view_closed\") {\n      routes = this.closedRoutes;\n    } else {\n      routes = this.submissionRoutes;\n    }\n    for (let i = 0; i < routes.length; i++) {\n      const route = routes[i];\n      const [constraint, _handler] = route;\n      // Check that the view event type (submission vs. closed) matches\n      if (constraint.type !== type) continue;\n      // Normalize simple string constraints to be an array of strings for consistency in handling inside this method.\n      const constraintArray = normalizeConstraintToArray(\n        constraint.callback_id,\n      );\n      // Handle the case where the constraint is either a regex or an array of strings to match against action_id\n      if (matchBasicConstraintField(constraintArray, \"callback_id\", view)) {\n        return _handler;\n      }\n    }\n    return null;\n  }\n}\n"
  },
  {
    "path": "src/functions/interactivity/view_router_test.ts",
    "content": "import { SlackAPI } from \"../../deps.ts\";\nimport {\n  assertEquals,\n  assertExists,\n  assertRejects,\n  mock,\n} from \"../../dev_deps.ts\";\nimport { ViewsRouter } from \"./view_router.ts\";\nimport type {\n  ViewClosedContext,\n  ViewClosedInvocationBody,\n  ViewSubmissionContext,\n  ViewSubmissionInvocationBody,\n} from \"./types.ts\";\nimport type { View } from \"./view_types.ts\";\nimport type {\n  FunctionDefinitionArgs,\n  FunctionParameters,\n  FunctionRuntimeParameters,\n} from \"../types.ts\";\nimport type {\n  ParameterSetDefinition,\n  PossibleParameterKeys,\n} from \"../../parameters/types.ts\";\nimport type { SlackFunctionDefinition } from \"../definitions/mod.ts\";\nimport { DefineFunction, Schema } from \"../../mod.ts\";\n\n// Helper test types\n// TODO: maybe we want to export this for userland usage at some point?\n// Very much a direct copy from the existing main function tester types and utilties in src/functions/tester\ntype SlackViewSubmissionHandlerTesterArgs<\n  InputParameters extends FunctionParameters,\n> =\n  & Partial<\n    ViewSubmissionContext<InputParameters>\n  >\n  & {\n    inputs: InputParameters;\n  };\n\ntype SlackViewClosedHandlerTesterArgs<\n  InputParameters extends FunctionParameters,\n> =\n  & Partial<\n    ViewClosedContext<InputParameters>\n  >\n  & {\n    inputs: InputParameters;\n  };\n\ntype CreateViewSubmissionHandlerContext<\n  InputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n> = {\n  (\n    args: SlackViewSubmissionHandlerTesterArgs<\n      FunctionRuntimeParameters<InputParameters, RequiredInput>\n    >,\n  ): ViewSubmissionContext<\n    FunctionRuntimeParameters<InputParameters, RequiredInput>\n  >;\n};\n\ntype CreateViewClosedHandlerContext<\n  InputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n> = {\n  (\n    args: SlackViewClosedHandlerTesterArgs<\n      FunctionRuntimeParameters<InputParameters, RequiredInput>\n    >,\n  ): ViewClosedContext<\n    FunctionRuntimeParameters<InputParameters, RequiredInput>\n  >;\n};\n\ntype SlackViewSubmissionHandlerTesterResponse<\n  InputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n> = {\n  createContext: CreateViewSubmissionHandlerContext<\n    InputParameters,\n    RequiredInput\n  >;\n};\n\ntype SlackViewClosedHandlerTesterResponse<\n  InputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n> = {\n  createContext: CreateViewClosedHandlerContext<\n    InputParameters,\n    RequiredInput\n  >;\n};\n\ntype SlackViewSubmissionHandlerTesterSignature = {\n  <\n    InputParameters extends ParameterSetDefinition,\n    OutputParameters extends ParameterSetDefinition,\n    RequiredInput extends PossibleParameterKeys<InputParameters>,\n    RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n  >(\n    func: SlackFunctionDefinition<\n      InputParameters,\n      OutputParameters,\n      RequiredInput,\n      RequiredOutput\n    >,\n  ): SlackViewSubmissionHandlerTesterResponse<\n    InputParameters,\n    RequiredInput\n  >;\n};\n\ntype SlackViewClosedHandlerTesterSignature = {\n  <\n    InputParameters extends ParameterSetDefinition,\n    OutputParameters extends ParameterSetDefinition,\n    RequiredInput extends PossibleParameterKeys<InputParameters>,\n    RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n  >(\n    func: SlackFunctionDefinition<\n      InputParameters,\n      OutputParameters,\n      RequiredInput,\n      RequiredOutput\n    >,\n  ): SlackViewClosedHandlerTesterResponse<\n    InputParameters,\n    RequiredInput\n  >;\n};\n// Helper test fixtures and utilities\nconst DEFAULT_VIEW: View = {\n  type: \"modal\",\n  team_id: \"T123456\",\n  state: { values: {} },\n  notify_on_close: false,\n  hash: \"pipe\",\n  clear_on_close: false,\n  callback_id: \"123\",\n  blocks: [],\n  app_installed_team_id: \"T123456\",\n  app_id: \"A123456\",\n};\n\nconst SlackViewSubmissionHandlerTester:\n  SlackViewSubmissionHandlerTesterSignature = <\n    InputParameters extends ParameterSetDefinition,\n    OutputParameters extends ParameterSetDefinition,\n    RequiredInput extends PossibleParameterKeys<InputParameters>,\n    RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n  >(\n    _func: SlackFunctionDefinition<\n      InputParameters,\n      OutputParameters,\n      RequiredInput,\n      RequiredOutput\n    >,\n  ) => {\n    const createContext: CreateViewSubmissionHandlerContext<\n      InputParameters,\n      RequiredInput\n    > = (\n      args,\n    ) => {\n      const inputs = (args.inputs || {}) as FunctionRuntimeParameters<\n        InputParameters,\n        RequiredInput\n      >;\n      const DEFAULT_BODY = {\n        type: \"view_submission\" as const,\n        view: DEFAULT_VIEW,\n        function_data: {\n          execution_id: \"123\",\n          function: { callback_id: \"456\" },\n          inputs,\n        },\n        interactivity: {\n          interactor: {\n            secret: \"shhhh\",\n            id: \"123\",\n          },\n          interactivity_pointer: \"123.asdf\",\n        },\n        user: {\n          id: \"123\",\n          name: \"asdf\",\n          team_id: DEFAULT_VIEW.team_id,\n        },\n        team: {\n          id: DEFAULT_VIEW.team_id,\n          domain: \"asdf\",\n        },\n        enterprise: null,\n        is_enterprise_install: false,\n        api_app_id: DEFAULT_VIEW.app_id,\n        app_id: DEFAULT_VIEW.app_id,\n        token: \"123\",\n        response_urls: [],\n        trigger_id: \"12345\",\n      };\n      const token = args.token || \"slack-function-test-token\";\n\n      return {\n        inputs,\n        env: args.env || {},\n        token,\n        client: SlackAPI(token),\n        view: args.view || DEFAULT_VIEW,\n        body: args.body || DEFAULT_BODY,\n        team_id: DEFAULT_VIEW.team_id,\n        enterprise_id: \"\",\n      };\n    };\n\n    return { createContext };\n  };\n\nconst SlackViewClosedHandlerTester: SlackViewClosedHandlerTesterSignature = <\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n>(\n  _func: SlackFunctionDefinition<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutput\n  >,\n) => {\n  const createContext: CreateViewClosedHandlerContext<\n    InputParameters,\n    RequiredInput\n  > = (\n    args,\n  ) => {\n    const inputs = (args.inputs || {}) as FunctionRuntimeParameters<\n      InputParameters,\n      RequiredInput\n    >;\n    const DEFAULT_BODY = {\n      type: \"view_closed\" as const,\n      view: DEFAULT_VIEW,\n      function_data: {\n        execution_id: \"123\",\n        function: { callback_id: \"456\" },\n        inputs,\n      },\n      interactivity: {\n        interactor: {\n          secret: \"shhhh\",\n          id: \"123\",\n        },\n        interactivity_pointer: \"123.asdf\",\n      },\n      user: {\n        id: \"123\",\n        name: \"asdf\",\n        team_id: DEFAULT_VIEW.team_id,\n      },\n      team: {\n        id: DEFAULT_VIEW.team_id,\n        domain: \"asdf\",\n      },\n      enterprise: null,\n      is_enterprise_install: false,\n      api_app_id: DEFAULT_VIEW.app_id,\n      app_id: DEFAULT_VIEW.app_id,\n      token: \"123\",\n      is_cleared: false,\n    };\n    const token = args.token || \"slack-function-test-token\";\n\n    return {\n      inputs,\n      env: args.env || {},\n      token,\n      client: SlackAPI(token),\n      view: args.view || DEFAULT_VIEW,\n      body: args.body || DEFAULT_BODY,\n      team_id: DEFAULT_VIEW.team_id,\n      enterprise_id: \"\",\n    };\n  };\n\n  return { createContext };\n};\n// a basic function definition and associated block action router to test\nconst func = DefineFunction({\n  callback_id: \"id\",\n  title: \"test\",\n  source_file: \"whatever\",\n  input_parameters: {\n    properties: {\n      garbage: { type: Schema.types.string },\n    },\n    required: [\"garbage\"],\n  },\n});\nconst { createContext: createSubmissionContext } =\n  SlackViewSubmissionHandlerTester(func);\nconst { createContext: createClosedContext } = SlackViewClosedHandlerTester(\n  func,\n);\n// Dummy object to be able to programmatically reference the identifiers\nconst inputs = { garbage: \"in, garbage out\" };\n\nconst getRouter = () => ViewsRouter(func);\n\nDeno.test(\"ViewRouter viewSubmission\", async (t) => {\n  await t.step(\n    \"export method returns result of submissionHandler when matching view comes in and baseline handler context parameters are present and exist\",\n    async () => {\n      const router = getRouter();\n      let handlerCalled = false;\n      router.addSubmissionHandler(DEFAULT_VIEW.callback_id, (ctx) => {\n        assertExists(ctx.inputs);\n        assertEquals<string>(ctx.inputs.garbage, inputs.garbage);\n        assertExists(ctx.token);\n        assertExists(ctx.client);\n        assertExists<View>(ctx.view);\n        assertExists<\n          ViewSubmissionInvocationBody<\n            typeof func.definition extends\n              FunctionDefinitionArgs<infer I, infer O, infer RI, infer RO>\n              ? FunctionRuntimeParameters<I, RI>\n              : never\n          >\n        >(ctx.body);\n        assertExists(ctx.env);\n        handlerCalled = true;\n      });\n      await router.viewSubmission(createSubmissionContext({ inputs }));\n      assertEquals(handlerCalled, true, \"view handler not called!\");\n    },\n  );\n});\n\nDeno.test(\"ViewRouter viewSubmission happy path\", async (t) => {\n  await t.step(\"simple string matching to callback_id\", async () => {\n    const router = getRouter();\n    let handlerCalled = false;\n    router.addSubmissionHandler(DEFAULT_VIEW.callback_id, (ctx) => {\n      assertExists(ctx.inputs);\n      assertExists<string>(ctx.token);\n      assertExists<View>(ctx.view);\n      assertExists(ctx.env);\n      assertExists(ctx.client);\n      handlerCalled = true;\n    });\n    await router.viewSubmission(createSubmissionContext({ inputs }));\n    assertEquals(handlerCalled, true, \"view handler not called!\");\n  });\n\n  await t.step(\"array of strings matching to callback_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy();\n    router.addSubmissionHandler(\n      [\"nope\", DEFAULT_VIEW.callback_id],\n      handler,\n    );\n    await router.viewSubmission(createSubmissionContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n\n  await t.step(\"regex matching to callback_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy();\n    router.addSubmissionHandler(/12/, handler);\n    await router.viewSubmission(createSubmissionContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n});\n\nDeno.test(\"ViewRouter viewSubmission sad path\", async (t) => {\n  await t.step(\n    \"unhandled view_submission throw\",\n    async () => {\n      const router = getRouter();\n      await assertRejects(\n        () => router.viewSubmission(createSubmissionContext({ inputs })),\n        \"no view handler defined\",\n      );\n    },\n  );\n\n  await t.step(\"no false positives\", async (t) => {\n    await t.step(\n      \"not matching callback_id: string\",\n      async () => {\n        const router = getRouter();\n        const handler = mock.spy();\n        router.addSubmissionHandler(\"nope\", handler);\n        await assertRejects(() =>\n          router.viewSubmission(createSubmissionContext({ inputs }))\n        );\n        mock.assertSpyCalls(handler, 0);\n      },\n    );\n\n    await t.step(\n      \"not matching callback_id: string[]\",\n      async () => {\n        const router = getRouter();\n        const handler = mock.spy();\n        router.addSubmissionHandler([\"nope\", \"nuh uh\"], handler);\n        await assertRejects(() =>\n          router.viewSubmission(createSubmissionContext({ inputs }))\n        );\n        mock.assertSpyCalls(handler, 0);\n      },\n    );\n\n    await t.step(\n      \"not matching callback_id: regex\",\n      async () => {\n        const router = getRouter();\n        const handler = mock.spy();\n        router.addSubmissionHandler(/regex/, handler);\n        await assertRejects(() =>\n          router.viewSubmission(createSubmissionContext({ inputs }))\n        );\n        mock.assertSpyCalls(handler, 0);\n      },\n    );\n  });\n});\n\nDeno.test(\"ViewRouter viewClosed\", async (t) => {\n  await t.step(\n    \"export method returns result of closedHandler when matching view comes in and baseline handler context parameters are present and exist\",\n    async () => {\n      const router = getRouter();\n      let handlerCalled = false;\n      router.addClosedHandler(DEFAULT_VIEW.callback_id, (ctx) => {\n        assertExists(ctx.inputs);\n        assertEquals<string>(ctx.inputs.garbage, inputs.garbage);\n        assertExists(ctx.token);\n        assertExists(ctx.client);\n        assertExists<View>(ctx.view);\n        assertExists<\n          ViewClosedInvocationBody<\n            typeof func.definition extends\n              FunctionDefinitionArgs<infer I, infer O, infer RI, infer RO>\n              ? FunctionRuntimeParameters<I, RI>\n              : never\n          >\n        >(ctx.body);\n        assertExists(ctx.env);\n        handlerCalled = true;\n      });\n      await router.viewClosed(createClosedContext({ inputs }));\n      assertEquals(handlerCalled, true, \"view handler not called!\");\n    },\n  );\n});\n\nDeno.test(\"ViewRouter viewClosed happy path\", async (t) => {\n  await t.step(\"simple string matching to callback_id\", async () => {\n    const router = getRouter();\n    let handlerCalled = false;\n    router.addClosedHandler(DEFAULT_VIEW.callback_id, (ctx) => {\n      assertExists(ctx.inputs);\n      assertExists<string>(ctx.token);\n      assertExists(ctx.client);\n      assertExists<View>(ctx.view);\n      assertExists(ctx.env);\n      handlerCalled = true;\n    });\n    await router.viewClosed(createClosedContext({ inputs }));\n    assertEquals(handlerCalled, true, \"view handler not called!\");\n  });\n\n  await t.step(\"array of strings matching to callback_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy();\n    router.addClosedHandler(\n      [\"nope\", DEFAULT_VIEW.callback_id],\n      handler,\n    );\n    await router.viewClosed(createClosedContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n\n  await t.step(\"regex matching to callback_id\", async () => {\n    const router = getRouter();\n    const handler = mock.spy();\n    router.addClosedHandler(/12/, handler);\n    await router.viewClosed(createClosedContext({ inputs }));\n    mock.assertSpyCalls(handler, 1);\n  });\n});\n\nDeno.test(\"ViewRouter viewClosed sad path\", async (t) => {\n  await t.step(\n    \"unhandled view_submission should throw\",\n    async () => {\n      const router = getRouter();\n      await assertRejects(\n        () => router.viewClosed(createClosedContext({ inputs })),\n        \"no view handler defined\",\n      );\n    },\n  );\n\n  await t.step(\"no false positives\", async (t) => {\n    await t.step(\n      \"not matching callback_id: string\",\n      async () => {\n        const router = getRouter();\n        const handler = mock.spy();\n        router.addClosedHandler(\"nope\", handler);\n        await assertRejects(() =>\n          router.viewClosed(createClosedContext({ inputs }))\n        );\n        mock.assertSpyCalls(handler, 0);\n      },\n    );\n\n    await t.step(\n      \"not matching callback_id: string[]\",\n      async () => {\n        const router = getRouter();\n        const handler = mock.spy();\n        router.addClosedHandler([\"nope\", \"nuh uh\"], handler);\n        await assertRejects(() =>\n          router.viewClosed(createClosedContext({ inputs }))\n        );\n        mock.assertSpyCalls(handler, 0);\n      },\n    );\n\n    await t.step(\n      \"not matching callback_id: regex\",\n      async () => {\n        const router = getRouter();\n        const handler = mock.spy();\n        router.addClosedHandler(/regex/, handler);\n        await assertRejects(() =>\n          router.viewClosed(createClosedContext({ inputs }))\n        );\n        mock.assertSpyCalls(handler, 0);\n      },\n    );\n  });\n});\n"
  },
  {
    "path": "src/functions/interactivity/view_types.ts",
    "content": "/*\nPossibly helpful links:\n- https://github.com/slackapi/bolt-js/blob/main/src/types/view/index.ts\n- https://api.slack.com/reference/interaction-payloads/views\n- https://api.slack.com/reference/surfaces/views\n*/\n\nimport type { BlockElement } from \"./block_kit_types.ts\";\n\nexport type ViewEvents = \"view_submission\" | \"view_closed\";\n\n/**\n * @description Common `body` type for both `view_submission` and `view_closed` events.\n */\nexport type BaseViewBody = {\n  /**\n   * @description The encoded application ID the view event was dispatched to, e.g. A123456.\n   */\n  api_app_id: string;\n  /**\n   * @description If applicable, the enterprise associated with the workspace the Block Action interaction originated from. If not applicable, will be null.\n   */\n  enterprise: {\n    /**\n     * @description Encoded enterprise ID, e.g. E123456.\n     */\n    id: string;\n    /**\n     * @description Enterprise name.\n     */\n    name: string;\n  } | null;\n  /**\n   * @description Whether this event originated from a workspace that is part of an enterprise installation.\n   */\n  is_enterprise_install: boolean;\n  /**\n   * @description The workspace, or team, details the Block Kit interaction originated from.\n   */\n  team: {\n    /**\n     * @description The subdomain of the team, e.g. domain.slack.com\n     */\n    domain: string;\n    /**\n     * @description Encoded team ID, e.g. T123456.\n     */\n    id: string;\n  };\n  token: string;\n  type: string;\n  /**\n   * @description Details for the user that initiated the Block Kit action.\n   */\n  user: {\n    /**\n     * @description Encoded user ID, e.g. U123456.\n     */\n    id: string;\n    /**\n     * @description User's handle as seen in the Slack client when e.g. at-notifying the user.\n    name: string;\n    /**\n     * @description The encoded team ID for the workspace, or team, where the Block Kit action originated from.\n     */\n    team_id: string;\n  };\n  /**\n   * @description The source view of the modal interacted with.\n   */\n  view: View;\n  // deno-lint-ignore no-explicit-any\n  [key: string]: any;\n};\n\n/**\n * @description `body` type for `view_submission` event.\n */\nexport type ViewSubmissionBody =\n  & BaseViewBody\n  & {\n    /**\n     * @description Used to open a modal by passing it to e.g. `view.open` or `view.push` APIs. Represents a particular user interaction with an interactive component. Short-lived token (expires fast!) that may only be used once.\n     */\n    trigger_id: string;\n    type: \"view_submission\";\n  };\n\n/**\n * @description `body` type for `view_closed` event.\n */\nexport type ViewClosedBody =\n  & BaseViewBody\n  & {\n    /**\n     * @description Whether or not an entire view stack was cleared.\n     */\n    is_cleared: boolean;\n    type: \"view_closed\";\n  };\n\n/**\n * @description The source view of the modal interacted with.\n */\nexport type View = {\n  /**\n   * @description The encoded application ID the view event was dispatched to, e.g. A123456.\n   */\n  app_id: string;\n  /**\n   * @description The encoded team ID for the workspace, or team, of the View.\n   */\n  app_installed_team_id: string;\n  /**\n   * @description The Block elements composing the view.\n   */\n  blocks: BlockElement[];\n  // bot_id: string;\n  /**\n   * @description An identifier to recognize interactions and submissions of this particular view. Don't use this to store sensitive information (use `private_metadata` instead). Max length of 255 characters.\n   */\n  callback_id: string;\n  /**\n   * @description Whether clicking on the close button clears all views in this modal and closes it..\n   */\n  clear_on_close: boolean;\n  // close: any;\n  // external_id: string;\n  /**\n   * @description A unique value which is optionally accepted in `views.update` and `views.publish` API calls. When provided to those APIs, the `hash` is validated such that only the most recent view can be updated. This should be used to ensure the correct view is being updated when updates are happening asynchronously.\n   */\n  hash: string;\n  // id: string;\n  /**\n   * @description Indicates whether Slack sends your function a `view_closed` event when a user clicks the close button in this view.\n   */\n  notify_on_close: boolean;\n  // previous_view_id: any;\n  /**\n   * @description An optional string that will be sent to your app in `view_submission` and `view_closed` events. Max length of 3000 characters.\n   */\n  private_metadata?: string;\n  // root_view_id: string;\n  /**\n   * @description Object representing view state.\n   */\n  state: {\n    /**\n     * @description A dictionary of objects. Each object represents a block in the view that contained stateful, interactive components. Objects are keyed by the `block_id` of those blocks. These objects each contain a child object. The child object is keyed by the `action_id` of the interactive element in the block. This final child object will contain the type and submitted value of the input block element.\n     */\n    values: {\n      // deno-lint-ignore no-explicit-any\n      [key: string]: any;\n    };\n  };\n  // submit?: any;\n  /**\n   * @description The encoded team ID for the workspace, or team, of the View.\n   */\n  team_id: string;\n  // title: any;\n  type: \"modal\";\n  // deno-lint-ignore no-explicit-any\n  [key: string]: any;\n};\n"
  },
  {
    "path": "src/functions/mod.ts",
    "content": "export { DefineConnector, DefineFunction } from \"./definitions/mod.ts\";\n"
  },
  {
    "path": "src/functions/slack-function.ts",
    "content": "import type {\n  ParameterSetDefinition,\n  PossibleParameterKeys,\n} from \"../parameters/types.ts\";\nimport type {\n  EnrichedSlackFunctionHandler,\n  RuntimeFunctionContext,\n  RuntimeUnhandledEventContext,\n  SlackFunctionType,\n} from \"./types.ts\";\nimport type { SlackFunctionDefinition } from \"./definitions/mod.ts\";\nimport { enrichContext } from \"./enrich-context.ts\";\nimport { BlockActionsRouter } from \"./interactivity/block_actions_router.ts\";\nimport { BlockSuggestionRouter } from \"./interactivity/block_suggestion_router.ts\";\nimport { ViewsRouter } from \"./interactivity/view_router.ts\";\n\nexport const SlackFunction = <\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n>(\n  func: SlackFunctionDefinition<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutput\n  >,\n  functionHandler: EnrichedSlackFunctionHandler<typeof func.definition>,\n) => {\n  // Start with the provided fn handler, and we'll wrap it up so we can append some additional functions to it\n\n  // Wrap the provided handler's call so we can add additional context\n  // deno-lint-ignore no-explicit-any\n  const handlerModule: any = (\n    ctx: RuntimeFunctionContext<InputParameters>,\n    // deno-lint-ignore no-explicit-any\n    ...args: any\n  ) => {\n    // enrich the context w/ additional properties\n    const newContext = enrichContext(ctx);\n\n    //@ts-expect-error - intentionally specifying the provided functionHandler as the `this` arg for the handler's call\n    return functionHandler.apply(functionHandler, [newContext, ...args]);\n  };\n  // Unhandled events are sent to a single handler, which is not set by default\n  handlerModule.unhandledEvent = undefined;\n\n  // Create routers for block/view actions\n  // TODO: we could probably lazily create these when corresponding add* functions are called\n  const blockActionsRouter = BlockActionsRouter(func);\n  const blockSuggestionRouter = BlockSuggestionRouter(func);\n  const viewsRouter = ViewsRouter(func);\n\n  // Add fns for additional function handlers\n\n  // deno-lint-ignore no-explicit-any\n  handlerModule.addBlockActionsHandler = (...args: any) => {\n    blockActionsRouter.addHandler.apply(blockActionsRouter, args);\n\n    return handlerModule;\n  };\n\n  // deno-lint-ignore no-explicit-any\n  handlerModule.addBlockSuggestionHandler = (...args: any) => {\n    blockSuggestionRouter.addHandler.apply(blockSuggestionRouter, args);\n\n    return handlerModule;\n  };\n\n  // deno-lint-ignore no-explicit-any\n  handlerModule.addViewClosedHandler = (...args: any) => {\n    viewsRouter.addClosedHandler.apply(viewsRouter, args);\n\n    return handlerModule;\n  };\n\n  // deno-lint-ignore no-explicit-any\n  handlerModule.addViewSubmissionHandler = (...args: any) => {\n    viewsRouter.addSubmissionHandler.apply(viewsRouter, args);\n\n    return handlerModule;\n  };\n\n  // deno-lint-ignore no-explicit-any\n  handlerModule.addUnhandledEventHandler = (handler: any) => {\n    // Set the unhandledEvent property directly\n    handlerModule.unhandledEvent = (\n      ctx: RuntimeUnhandledEventContext<InputParameters>,\n      // deno-lint-ignore no-explicit-any\n      ...args: any\n    ) => {\n      const newContext = enrichContext(ctx);\n\n      return handler.apply(handler, [newContext, ...args]);\n    };\n\n    return handlerModule;\n  };\n\n  // Expose named handlers that the deno-slack-runtime will invoke\n  handlerModule.blockActions = blockActionsRouter;\n  handlerModule.blockSuggestion = blockSuggestionRouter;\n  handlerModule.viewClosed = viewsRouter.viewClosed;\n  handlerModule.viewSubmission = viewsRouter.viewSubmission;\n\n  return handlerModule as SlackFunctionType<typeof func.definition>;\n};\n"
  },
  {
    "path": "src/functions/slack-function_test.ts",
    "content": "import { assertEquals, assertExists, mock } from \"../dev_deps.ts\";\nimport { DefineFunction, SlackFunction } from \"../mod.ts\";\n\nconst TestFunction = DefineFunction({\n  title: \"Test\",\n  callback_id: \"test\",\n  source_file: \"test.js\",\n});\n\nDeno.test(\"SlackFunction returns the expected interface\", () => {\n  const mainFnHandler = mock.spy(() => ({ outputs: {} }));\n\n  const handlers = SlackFunction(TestFunction, mainFnHandler);\n\n  assertEquals(typeof handlers.addBlockActionsHandler, \"function\");\n  assertEquals(typeof handlers.addViewClosedHandler, \"function\");\n  assertEquals(typeof handlers.addViewSubmissionHandler, \"function\");\n  assertEquals(typeof handlers.addUnhandledEventHandler, \"function\");\n});\n\nDeno.test(\"SlackFunction returns a proper function module definition\", () => {\n  const mainFnHandler = mock.spy(() => ({ outputs: {} }));\n\n  const handlers = SlackFunction(TestFunction, mainFnHandler);\n  const typedHandlers = typeHandlersForTesting(handlers);\n\n  assertEquals(typeof handlers, \"function\");\n  assertEquals(typeof typedHandlers.blockActions, \"function\");\n  assertEquals(typeof typedHandlers.viewSubmission, \"function\");\n  assertEquals(typeof typedHandlers.viewClosed, \"function\");\n});\n\nDeno.test(\"SlackFunction unhandledEvent is undefined by default\", () => {\n  const mainFnHandler = mock.spy(() => ({ outputs: {} }));\n\n  const handlers = SlackFunction(TestFunction, mainFnHandler);\n  const typedHandlers = typeHandlersForTesting(handlers);\n\n  assertEquals(typedHandlers.unhandledEvent, undefined);\n});\n\nDeno.test(\"SlackFunction unhandledEvent is defined after calling addUnhandledEventHandler\", () => {\n  const mainFnHandler = mock.spy(() => ({ outputs: {} }));\n\n  const handlers = SlackFunction(TestFunction, mainFnHandler);\n  const typedHandlers = typeHandlersForTesting(handlers);\n\n  const handlerSpy = mock.spy();\n  typedHandlers.addUnhandledEventHandler(handlerSpy);\n\n  assertEquals(typeof typedHandlers.unhandledEvent, \"function\");\n});\n\nDeno.test(\"Main handler should pass arguments through\", () => {\n  const args = { test: \"arguments\" };\n\n  // deno-lint-ignore no-explicit-any\n  const mainFnHandler = mock.spy((ctx: any) => {\n    assertEquals(ctx.test, args.test);\n    assertExists(ctx.client);\n\n    return { outputs: {} };\n  });\n\n  const handlers = SlackFunction(TestFunction, mainFnHandler);\n  const typedHandlers = typeHandlersForTesting(handlers);\n\n  typedHandlers(args);\n\n  mock.assertSpyCalls(mainFnHandler, 1);\n});\n\nDeno.test(\"Main handler should have a client instance\", () => {\n  const args = { test: \"arguments\" };\n\n  // deno-lint-ignore no-explicit-any\n  const mainFnHandler = mock.spy((ctx: any) => {\n    assertExists(ctx.client);\n\n    return { outputs: {} };\n  });\n\n  const handlers = SlackFunction(TestFunction, mainFnHandler);\n  const typedHandlers = typeHandlersForTesting(handlers);\n\n  typedHandlers(args);\n\n  mock.assertSpyCalls(mainFnHandler, 1);\n});\n\nDeno.test(\"addBlockActionsHandler\", async () => {\n  const mainFnHandler = mock.spy(() => ({ outputs: {} }));\n\n  const handlers = SlackFunction(TestFunction, mainFnHandler);\n  const typedHandlers = typeHandlersForTesting(handlers);\n\n  const actionId = \"whatever\";\n  const actionSpy = mock.spy();\n  typedHandlers.addBlockActionsHandler(actionId, actionSpy);\n\n  await typedHandlers.blockActions({ action: { action_id: actionId } });\n  mock.assertSpyCalls(actionSpy, 1);\n});\n\nDeno.test(\"addBlockSuggestionHandler\", async () => {\n  const mainFnHandler = mock.spy(() => ({ outputs: {} }));\n\n  const handlers = SlackFunction(TestFunction, mainFnHandler);\n  const typedHandlers = typeHandlersForTesting(handlers);\n\n  const actionId = \"whatever\";\n  const suggestionSpy = mock.spy(() => ({ options: [] }));\n  typedHandlers.addBlockSuggestionHandler(actionId, suggestionSpy);\n\n  await typedHandlers.blockSuggestion({ body: { action_id: actionId } });\n  mock.assertSpyCalls(suggestionSpy, 1);\n});\n\nDeno.test(\"addViewClosedHandler\", async () => {\n  const mainFnHandler = mock.spy(() => ({ outputs: {} }));\n\n  const handlers = SlackFunction(TestFunction, mainFnHandler);\n  const typedHandlers = typeHandlersForTesting(handlers);\n\n  const callbackId = \"lolwutwut\";\n  const closedHandler = mock.spy();\n  typedHandlers.addViewClosedHandler(callbackId, closedHandler);\n\n  await typedHandlers.viewClosed({\n    body: { type: \"view_closed\" },\n    view: { callback_id: callbackId },\n  });\n  mock.assertSpyCalls(closedHandler, 1);\n});\n\nDeno.test(\"addViewSubmissionHandler\", async () => {\n  const mainFnHandler = mock.spy(() => ({ outputs: {} }));\n\n  const handlers = SlackFunction(TestFunction, mainFnHandler);\n  const typedHandlers = typeHandlersForTesting(handlers);\n\n  const callbackId = \"lolwutwut\";\n  const submissionSpy = mock.spy();\n  typedHandlers.addViewSubmissionHandler(callbackId, submissionSpy);\n\n  await typedHandlers.viewSubmission({\n    body: { type: \"view_submission\" },\n    view: { callback_id: callbackId },\n  });\n  mock.assertSpyCalls(submissionSpy, 1);\n});\n\nDeno.test(\"addUnhandledEventHandler\", async () => {\n  const mainFnHandler = mock.spy(() => ({ outputs: {} }));\n\n  const handlers = SlackFunction(TestFunction, mainFnHandler);\n  const typedHandlers = typeHandlersForTesting(handlers);\n\n  // deno-lint-ignore no-explicit-any\n  const handlerSpy = mock.spy((ctx: any) => {\n    assertEquals(ctx.some, args.some);\n    assertExists(ctx.client);\n\n    return { outputs: {} };\n  });\n  typedHandlers.addUnhandledEventHandler(handlerSpy);\n\n  const args = { some: \"arguments\" };\n  await typedHandlers.unhandledEvent(args);\n});\n\nconst typeHandlersForTesting = <HandlerType>(handlers: HandlerType) => {\n  return handlers as HandlerType & {\n    (...args: unknown[]): unknown;\n    blockActions: (...args: unknown[]) => void;\n    blockSuggestion: (...args: unknown[]) => void;\n    viewSubmission: (...args: unknown[]) => void;\n    viewClosed: (...args: unknown[]) => void;\n    unhandledEvent: (...args: unknown[]) => void;\n  };\n};\n"
  },
  {
    "path": "src/functions/tester/function_tester_test.ts",
    "content": "import { assertEquals, mock } from \"../../dev_deps.ts\";\nimport { DEFAULT_FUNCTION_TESTER_TITLE, SlackFunctionTester } from \"./mod.ts\";\nimport { DefineFunction } from \"../mod.ts\";\nimport { Schema, SlackFunction } from \"../../mod.ts\";\n\nDeno.test(\"SlackFunctionTester.createContext using a string for callback_id\", () => {\n  const callbackId = \"my_callback_id\";\n  const { createContext } = SlackFunctionTester(callbackId);\n\n  const inputs = {\n    myValue: \"some value\",\n  };\n\n  const ctxWithInputs = createContext({\n    inputs,\n  });\n\n  const ctxWithoutInputs = createContext({ inputs: {} });\n\n  assertEquals(ctxWithInputs.inputs, inputs);\n  assertEquals(ctxWithInputs.env, {});\n  assertEquals(typeof ctxWithInputs.token, \"string\");\n  assertEquals(ctxWithInputs.event.type, \"function_executed\");\n  assertEquals(ctxWithInputs.event.function.callback_id, callbackId);\n  assertEquals(\n    ctxWithInputs.event.function.title,\n    DEFAULT_FUNCTION_TESTER_TITLE,\n  );\n\n  assertEquals(ctxWithoutInputs.inputs, {});\n  assertEquals(ctxWithoutInputs.event.function.callback_id, callbackId);\n});\n\nDeno.test(\"SlackFunctionTester.createContext using function definitions\", () => {\n  const callbackId = \"my_callback_id\";\n  const TestFunction = DefineFunction({\n    callback_id: callbackId,\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        myValue: { type: Schema.types.string },\n        myOptionalValue: { type: Schema.types.boolean },\n      },\n      required: [\"myValue\"],\n    },\n  });\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n  const requiredCtx = createContext({\n    inputs: { myValue: \"some value\" },\n  });\n\n  const optionalCtx = createContext({\n    inputs: { myValue: \"some value\", myOptionalValue: true },\n  });\n\n  assertEquals(requiredCtx.inputs, { myValue: \"some value\" });\n  assertEquals(requiredCtx.env, {});\n  assertEquals(typeof requiredCtx.token, \"string\");\n  assertEquals(requiredCtx.event.type, \"function_executed\");\n  assertEquals(requiredCtx.event.function.callback_id, callbackId);\n  assertEquals(requiredCtx.event.function.title, TestFunction.definition.title);\n\n  assertEquals(optionalCtx.inputs, {\n    myValue: \"some value\",\n    myOptionalValue: true,\n  });\n});\n\nDeno.test(\"SlackFunctionTester.createContext with empty inputs\", () => {\n  const callbackId = \"my_callback_id\";\n  const { createContext } = SlackFunctionTester(callbackId);\n\n  const ctx = createContext({ inputs: {} });\n\n  assertEquals(ctx.inputs, {});\n  assertEquals(ctx.env, {});\n  assertEquals(typeof ctx.token, \"string\");\n  assertEquals(ctx.event.type, \"function_executed\");\n  assertEquals(ctx.event.function.callback_id, callbackId);\n});\n\nDeno.test(\"SlackFunctionTester with a SlackFunction()\", async () => {\n  const callbackId = \"my_callback_id\";\n  const outputValue = \"an-output-value\";\n  const { createContext } = SlackFunctionTester(callbackId);\n\n  const TestFunction = DefineFunction({\n    callback_id: callbackId,\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        myValue: { type: Schema.types.string },\n        myOptionalValue: { type: Schema.types.boolean },\n      },\n      required: [\"myValue\"],\n    },\n    output_parameters: {\n      properties: {\n        myOutput: { type: Schema.types.string },\n      },\n      required: [\"myOutput\"],\n    },\n  });\n\n  const handlerSpy = mock.spy(() => {\n    return { outputs: { myOutput: outputValue } };\n  });\n\n  const handler = SlackFunction(TestFunction, handlerSpy);\n  const ctx = createContext({ inputs: { myValue: \"test\" } });\n  const resp = await handler(ctx);\n  assertEquals(resp.outputs?.myOutput, outputValue);\n});\n"
  },
  {
    "path": "src/functions/tester/mod.ts",
    "content": "import { SlackAPI } from \"../../deps.ts\";\nimport type {\n  ParameterSetDefinition,\n  PossibleParameterKeys,\n} from \"../../parameters/types.ts\";\nimport type { SlackFunctionDefinition } from \"../definitions/mod.ts\";\nimport type { FunctionRuntimeParameters } from \"../types.ts\";\nimport type {\n  CreateFunctionContext,\n  SlackFunctionTesterSignature,\n} from \"./types.ts\";\nexport const DEFAULT_FUNCTION_TESTER_TITLE = \"Function Test Title\";\n\nexport const SlackFunctionTester: SlackFunctionTesterSignature = <\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n>(\n  funcOrCallbackId:\n    | string\n    | SlackFunctionDefinition<\n      InputParameters,\n      OutputParameters,\n      RequiredInput,\n      RequiredOutput\n    >,\n) => {\n  const now = new Date();\n  const testFnID = `fn${now.getTime()}`;\n  let testFnCallbackID: string;\n  let testFnTitle: string;\n\n  if (typeof funcOrCallbackId === \"string\") {\n    testFnCallbackID = funcOrCallbackId;\n    testFnTitle = DEFAULT_FUNCTION_TESTER_TITLE;\n  } else {\n    testFnCallbackID = funcOrCallbackId.definition.callback_id;\n    testFnTitle = funcOrCallbackId.definition.title;\n  }\n\n  const createContext: CreateFunctionContext<InputParameters, RequiredInput> = (\n    args,\n  ) => {\n    const ts = new Date();\n    const token = args.token || \"slack-function-test-token\";\n\n    // TODO: can we reuse some of our existing types for modeling payloads to ensure this structure doesnt become out of date?\n    return {\n      inputs: (args.inputs || {}) as FunctionRuntimeParameters<\n        InputParameters,\n        RequiredInput\n      >,\n      env: args.env || {},\n      token,\n      client: SlackAPI(token),\n      team_id: args.team_id || \"test-team-id\",\n      enterprise_id: args.enterprise_id || \"test-enterprise-id\",\n      event: args.event || {\n        type: \"function_executed\",\n        event_ts: `${ts.getTime()}`,\n        function_execution_id: `fx${ts.getTime()}`,\n        inputs: args.inputs as Record<string, unknown>,\n        function: {\n          id: testFnID,\n          callback_id: testFnCallbackID,\n          title: testFnTitle,\n        },\n      },\n    };\n  };\n\n  return { createContext };\n};\n"
  },
  {
    "path": "src/functions/tester/types.ts",
    "content": "import type {\n  ParameterSetDefinition,\n  PossibleParameterKeys,\n} from \"../../parameters/types.ts\";\nimport type { SlackFunctionDefinition } from \"../definitions/mod.ts\";\nimport type {\n  FunctionContext,\n  FunctionParameters,\n  FunctionRuntimeParameters,\n} from \"../types.ts\";\n\nexport type SlackFunctionTesterArgs<\n  InputParameters extends FunctionParameters,\n> =\n  & Partial<\n    FunctionContext<InputParameters>\n  >\n  & {\n    inputs: InputParameters;\n  };\n\nexport type CreateFunctionContext<\n  InputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n> = {\n  (\n    args: SlackFunctionTesterArgs<\n      FunctionRuntimeParameters<InputParameters, RequiredInput> | undefined\n    >,\n  ): FunctionContext<\n    FunctionRuntimeParameters<InputParameters, RequiredInput>\n  >;\n};\n\nexport type SlackFunctionTesterResponse<\n  InputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n> = {\n  createContext: CreateFunctionContext<InputParameters, RequiredInput>;\n};\n\n// This type is overloaded to accept either a string or a SlackFunction\nexport type SlackFunctionTesterSignature = {\n  <\n    InputParameters extends ParameterSetDefinition,\n    OutputParameters extends ParameterSetDefinition,\n    RequiredInput extends PossibleParameterKeys<InputParameters>,\n    RequiredOutput extends PossibleParameterKeys<OutputParameters>,\n  >(\n    funcOrCallbackId: SlackFunctionDefinition<\n      InputParameters,\n      OutputParameters,\n      RequiredInput,\n      RequiredOutput\n    >,\n  ): SlackFunctionTesterResponse<\n    InputParameters,\n    RequiredInput\n  >;\n\n  // Accept a string\n  (funcOrCallbackId: string): {\n    createContext: {\n      <I extends FunctionParameters>(\n        args: SlackFunctionTesterArgs<I>,\n      ): FunctionContext<I>;\n    };\n  };\n};\n"
  },
  {
    "path": "src/functions/types.ts",
    "content": "import type { SlackAPIClient } from \"../deps.ts\";\nimport type { Env } from \"../types.ts\";\nimport type {\n  ManifestFunctionSchema,\n  ManifestFunctionType,\n} from \"../manifest/manifest_schema.ts\";\nimport type {\n  ParameterPropertiesDefinition,\n  ParameterSetDefinition,\n  PossibleParameterKeys,\n} from \"../parameters/types.ts\";\nimport type {\n  CustomTypeParameterDefinition,\n  ParameterDefinition,\n  TypedArrayParameterDefinition,\n  TypedObjectParameterDefinition,\n  TypedObjectProperties,\n  TypedObjectRequiredProperties,\n} from \"../parameters/definition_types.ts\";\nimport type SchemaTypes from \"../schema/schema_types.ts\";\nimport type SlackSchemaTypes from \"../schema/slack/schema_types.ts\";\nimport type { SlackManifest } from \"../manifest/mod.ts\";\nimport type {\n  BasicConstraintField,\n  BlockActionConstraint,\n  BlockActionHandler,\n  BlockSuggestionHandler,\n  UnhandledEventHandler,\n  ViewClosedHandler,\n  ViewSubmissionHandler,\n} from \"./interactivity/types.ts\";\nimport type { ICustomType } from \"../types/types.ts\";\nimport type {\n  IncreaseDepth,\n  MaxRecursionDepth,\n  RecursionDepthLevel,\n} from \"../type_utils.ts\";\n\nexport type { BlockActionHandler } from \"./interactivity/types.ts\";\n\nexport type FunctionInvocationBody = {\n  \"team_id\": string;\n  \"api_app_id\": string;\n  type: \"event_callback\";\n  \"event_id\": string;\n  event: {\n    type: \"function_executed\";\n    function: {\n      id: string;\n      \"callback_id\": string;\n      title: string;\n      description?: string;\n    };\n    \"function_execution_id\": string;\n    inputs?: Record<string, unknown>;\n    \"event_ts\": string;\n  };\n};\n\n/**\n * @description Maps a ParameterDefinition into a runtime type, i.e. \"string\" === string.\n */\ntype FunctionInputRuntimeType<\n  Param extends ParameterDefinition,\n  CurrentDepth extends RecursionDepthLevel = 0,\n> =\n  // Recurse through Custom Types, stop when we hit our max depth\n  CurrentDepth extends MaxRecursionDepth ? UnknownRuntimeType\n    : Param[\"type\"] extends ICustomType\n      ? Param extends CustomTypeParameterDefinition ? FunctionInputRuntimeType<\n          Param[\"type\"][\"definition\"],\n          IncreaseDepth<CurrentDepth>\n        >\n      : UnknownRuntimeType\n    : Param[\"type\"] extends\n      | typeof SchemaTypes.string\n      | typeof SlackSchemaTypes.user_id\n      | typeof SlackSchemaTypes.usergroup_id\n      | typeof SlackSchemaTypes.channel_id\n      | typeof SlackSchemaTypes.canvas_id\n      | typeof SlackSchemaTypes.canvas_template_id\n      | typeof SlackSchemaTypes.date\n      | typeof SlackSchemaTypes.salesforce_record_id\n      | typeof SlackSchemaTypes.message_ts ? string\n    : Param[\"type\"] extends\n      | typeof SchemaTypes.integer\n      | typeof SchemaTypes.number\n      | typeof SlackSchemaTypes.timestamp ? number\n    : Param[\"type\"] extends typeof SchemaTypes.boolean ? boolean\n    : Param[\"type\"] extends typeof SchemaTypes.array\n      ? Param extends TypedArrayParameterDefinition\n        ? TypedArrayFunctionInputRuntimeType<Param>\n      : UnknownRuntimeType[]\n    : Param[\"type\"] extends typeof SchemaTypes.object\n      ? Param extends TypedObjectParameterDefinition<\n        infer P,\n        infer RP\n      > ? TypedObjectFunctionInputRuntimeType<P, RP, Param>\n      : UnknownRuntimeType\n    : Param[\"type\"] extends\n      | typeof SlackSchemaTypes.rich_text\n      | typeof SlackSchemaTypes.expanded_rich_text ? UnknownRuntimeType\n    : UnknownRuntimeType;\n\n// deno-lint-ignore no-explicit-any\ntype UnknownRuntimeType = any;\n\ntype TypedObjectFunctionInputRuntimeType<\n  Props extends TypedObjectProperties,\n  RequiredProps extends TypedObjectRequiredProperties<Props>,\n  Param extends TypedObjectParameterDefinition<Props, RequiredProps>,\n> =\n  & {\n    [prop in keyof Props]?: FunctionInputRuntimeType<\n      Props[prop]\n    >;\n  }\n  & (RequiredProps extends Array<keyof Props> ? {\n      [prop in RequiredProps[number]]: FunctionInputRuntimeType<\n        Props[prop]\n      >;\n    }\n    : Record<never, never>)\n  & (Param[\"additionalProperties\"] extends false ? Record<never, never> : {\n    [key: string]: UnknownRuntimeType;\n  });\n\ntype TypedArrayFunctionInputRuntimeType<\n  Param extends TypedArrayParameterDefinition,\n> = FunctionInputRuntimeType<Param[\"items\"]>[];\n\n/**\n * @description Converts a ParameterSetDefinition, and list of required params into an object type used for runtime inputs and outputs\n */\nexport type FunctionRuntimeParameters<\n  Params extends ParameterSetDefinition,\n  RequiredParams extends PossibleParameterKeys<Params>,\n> =\n  & {\n    [k in RequiredParams[number]]: FunctionInputRuntimeType<\n      Params[k]\n    >;\n  }\n  & {\n    [k in keyof Params]?: FunctionInputRuntimeType<\n      Params[k]\n    >;\n  };\n\ntype AsyncFunctionHandler<\n  InputParameters extends FunctionParameters,\n  OutputParameters extends FunctionParameters,\n  Context extends BaseRuntimeFunctionContext<InputParameters>,\n> = {\n  (\n    context: Context,\n  ): Promise<FunctionHandlerReturnArgs<OutputParameters>>;\n};\n\ntype SyncFunctionHandler<\n  InputParameters extends FunctionParameters,\n  OutputParameters extends FunctionParameters,\n  Context extends BaseRuntimeFunctionContext<InputParameters>,\n> = {\n  (\n    context: Context,\n  ): FunctionHandlerReturnArgs<OutputParameters>;\n};\n\n/**\n * @description Custom function handler from a function definition\n */\nexport type RuntimeSlackFunctionHandler<Definition> = Definition extends\n  FunctionDefinitionArgs<infer I, infer O, infer RI, infer RO>\n  ? BaseRuntimeSlackFunctionHandler<\n    FunctionRuntimeParameters<I, RI>,\n    FunctionRuntimeParameters<O, RO>\n  >\n  : never;\n\n/**\n * @description Custom function handler from input and output types directly\n */\nexport type BaseRuntimeSlackFunctionHandler<\n  InputParameters extends FunctionParameters,\n  OutputParameters extends FunctionParameters,\n> =\n  | AsyncFunctionHandler<\n    InputParameters,\n    OutputParameters,\n    RuntimeFunctionContext<InputParameters>\n  >\n  | SyncFunctionHandler<\n    InputParameters,\n    OutputParameters,\n    RuntimeFunctionContext<InputParameters>\n  >;\n\n/**\n * @description Custom function handler from a function definition\n */\nexport type EnrichedSlackFunctionHandler<Definition> = Definition extends\n  FunctionDefinitionArgs<infer I, infer O, infer RI, infer RO>\n  ? (BaseEnrichedSlackFunctionHandler<\n    FunctionRuntimeParameters<I, RI>,\n    FunctionRuntimeParameters<O, RO>\n  >)\n  : never;\n\n/**\n * @description Custom function handler from input and output types directly\n */\ntype BaseEnrichedSlackFunctionHandler<\n  InputParameters extends FunctionParameters,\n  OutputParameters extends FunctionParameters,\n> =\n  | AsyncFunctionHandler<\n    InputParameters,\n    OutputParameters,\n    FunctionContext<InputParameters>\n  >\n  | SyncFunctionHandler<\n    InputParameters,\n    OutputParameters,\n    FunctionContext<InputParameters>\n  >;\n\n// export type SlackFunctionHandler<Definition> = EnrichedSlackFunctionHandler<\n//   Definition\n// >;\n\ntype SuccessfulFunctionReturnArgs<\n  OutputParameters extends FunctionParameters,\n> = {\n  completed?: true;\n  // Allow function to return an empty object if no outputs are defined\n  outputs: OutputParameters extends undefined ? (Record<never, never>)\n    : OutputParameters;\n  error?: string;\n};\n\ntype ErroredFunctionReturnArgs<OutputParameters extends FunctionParameters> =\n  & Partial<SuccessfulFunctionReturnArgs<OutputParameters>>\n  & Required<Pick<SuccessfulFunctionReturnArgs<OutputParameters>, \"error\">>;\n\ntype PendingFunctionReturnArgs = {\n  completed: false;\n  outputs?: never;\n  error?: never;\n};\n\nexport type FunctionHandlerReturnArgs<\n  OutputParameters extends FunctionParameters,\n> =\n  | SuccessfulFunctionReturnArgs<OutputParameters>\n  | ErroredFunctionReturnArgs<OutputParameters>\n  | PendingFunctionReturnArgs;\n\n// This describes the base-version of context objects deno-slack-runtime passes into different function handlers (i.e. main fn handler, blockActions, etc).\n// Each function handler type extends this with it's own specific additions.\nexport type BaseRuntimeFunctionContext<\n  InputParameters extends FunctionParameters,\n> = {\n  /**\n   * @description A map of string keys to string values containing any environment variables available and provided to your function handler's execution context.\n   */\n  env: Env;\n  /**\n   * @description The inputs to the function as defined by your function definition. If no inputs are specified, an empty object is provided at runtime.\n   */\n  inputs: InputParameters;\n  /**\n   * @description API token that can be used with the deno-slack-api API client.\n   */\n  token: string;\n  /**\n   * @description A unique encoded ID representing the Slack team associated with the workspace where the function execution takes place.\n   */\n  team_id: string;\n  /**\n   * @description A unique encoded ID representing the Slack enterprise associated with the workspace where the function execution takes place. In a non-enterprise workspace, this value will be the empty string.\n   */\n  enterprise_id: string;\n};\n\n// SDK function handlers receive these additional properties on the function context object\nexport type FunctionContextEnrichment = {\n  client: SlackAPIClient;\n};\n\n// This is the context deno-slack-runtime passes to the main function handler\nexport type RuntimeFunctionContext<InputParameters extends FunctionParameters> =\n  & BaseRuntimeFunctionContext<InputParameters>\n  & {\n    event: FunctionInvocationBody[\"event\"];\n  };\n\n// This is the enriched context object passed into the main function handler setup with SlackFunction()\nexport type FunctionContext<\n  InputParameters extends FunctionParameters,\n> = RuntimeFunctionContext<InputParameters> & FunctionContextEnrichment;\n\n// Allow undefined here for functions that have no inputs and/or outputs\nexport type FunctionParameters = {\n  // deno-lint-ignore no-explicit-any\n  [key: string]: any;\n} | undefined;\n\nexport interface ISlackFunctionDefinition<\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInput extends PossibleParameterKeys<InputParameters>,\n  RequiredOutputs extends PossibleParameterKeys<OutputParameters>,\n> {\n  type: ManifestFunctionType;\n  id: string;\n  definition: FunctionDefinitionArgs<\n    InputParameters,\n    OutputParameters,\n    RequiredInput,\n    RequiredOutputs\n  >;\n  export?: (() => ManifestFunctionSchema) | undefined;\n  registerParameterTypes?: ((manifest: SlackManifest) => void) | undefined;\n}\n\nexport type SlackFunctionDefinitionArgs<\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInputs extends PossibleParameterKeys<InputParameters>,\n  RequiredOutputs extends PossibleParameterKeys<OutputParameters>,\n> =\n  & FunctionDefinitionArgs<\n    InputParameters,\n    OutputParameters,\n    RequiredInputs,\n    RequiredOutputs\n  >\n  & { source_file: string };\n\nexport type FunctionDefinitionArgs<\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInputs extends PossibleParameterKeys<InputParameters>,\n  RequiredOutputs extends PossibleParameterKeys<OutputParameters>,\n> = {\n  callback_id: string;\n  /** A title for your function. */\n  title: string;\n  /** An optional description for your function. */\n  description?: string;\n  /** An optional map of input parameter names containing information about their type, title, description, required and (additional) properties. */\n  \"input_parameters\"?: ParameterPropertiesDefinition<\n    InputParameters,\n    RequiredInputs\n  >;\n  /** An optional map of output parameter names containing information about their type, title, description, required and (additional) properties. */\n  \"output_parameters\"?: ParameterPropertiesDefinition<\n    OutputParameters,\n    RequiredOutputs\n  >;\n};\n\nexport type SlackFunctionType<Definition> = Definition extends\n  FunctionDefinitionArgs<infer I, infer O, infer RI, infer RO> ? (\n    & EnrichedSlackFunctionHandler<Definition>\n    & {\n      /**\n       * @description Add an interactivity handler responding to specific {@link https://api.slack.com/reference/interaction-payloads/block-actions `block_actions` events}.\n       * @param {BlockActionConstraint} actionConstraint - A {@link BlockActionConstraint} filter; only `block_actions` payloads that satisfy the constraints provided in this parameter will be routed to the provided handler.\n       * @param {BlockActionHandler} handler - A {@link BlockActionHandler} function handler that will be invoked when a matching {@link https://api.slack.com/reference/interaction-payloads/block-actions `block_actions` payload} is dispatched to your application.\n       */\n      addBlockActionsHandler(\n        actionConstraint: BlockActionConstraint,\n        handler: BlockActionHandler<\n          FunctionDefinitionArgs<I, O, RI, RO>\n        >,\n      ): SlackFunctionType<Definition>;\n      /**\n       * @description Add an interactivity handler responding to specific `block_suggestion` events.\n       * @param {BlockActionConstraint} actionConstraint - A {@link BlockActionConstraint} filter; only `block_suggestion` payloads that satisfy the constraints provided in this parameter will be routed to the provided handler.\n       * @param {BlockSuggestionHandler} handler - A {@link BlockSuggestionHandler} function handler that will be invoked when a matching `block_suggestion` payload is dispatched to your application.\n       */\n      addBlockSuggestionHandler(\n        actionConstraint: BlockActionConstraint,\n        handler: BlockSuggestionHandler<\n          FunctionDefinitionArgs<I, O, RI, RO>\n        >,\n      ): SlackFunctionType<Definition>;\n      addViewClosedHandler(\n        viewConstraint: BasicConstraintField,\n        handler: ViewClosedHandler<\n          FunctionDefinitionArgs<I, O, RI, RO>\n        >,\n      ): SlackFunctionType<Definition>;\n      addViewSubmissionHandler(\n        viewConstraint: BasicConstraintField,\n        handler: ViewSubmissionHandler<\n          FunctionDefinitionArgs<I, O, RI, RO>\n        >,\n      ): SlackFunctionType<Definition>;\n      addUnhandledEventHandler(\n        handler: UnhandledEventHandler<\n          FunctionDefinitionArgs<I, O, RI, RO>\n        >,\n      ): SlackFunctionType<Definition>;\n    }\n  )\n  : never;\n\n// This is the context deno-slack-runtime passes to the unhandledEvent handler\nexport type RuntimeUnhandledEventContext<\n  InputParameters extends FunctionParameters,\n> =\n  & BaseRuntimeFunctionContext<InputParameters>\n  & {\n    // deno-lint-ignore no-explicit-any\n    body: any;\n  };\n"
  },
  {
    "path": "src/functions/types_base_runtime_function_handler_test.ts",
    "content": "import { assertEquals } from \"../dev_deps.ts\";\nimport { SlackFunctionTester } from \"./tester/mod.ts\";\nimport type { BaseRuntimeSlackFunctionHandler } from \"./types.ts\";\n\n// These tests are to ensure our Function Handler types are supporting the use cases we want to\n// Any \"failures\" here will most likely be reflected in Type errors\n\nDeno.test(\"BaseRuntimeSlackFunctionHandler types\", () => {\n  type Inputs = {\n    in: string;\n  };\n  type Outputs = {\n    out: string;\n  };\n  const handler: BaseRuntimeSlackFunctionHandler<Inputs, Outputs> = (\n    { inputs },\n  ) => {\n    return {\n      outputs: {\n        out: inputs.in,\n      },\n    };\n  };\n  const { createContext } = SlackFunctionTester(\"test\");\n  const inputs = { in: \"test\" };\n  const result = handler(createContext({ inputs }));\n  assertEquals(result.outputs?.out, inputs.in);\n});\n\nDeno.test(\"BaseRuntimeSlackFunctionHandler with empty inputs and empty outputs\", () => {\n  type Inputs = Record<never, never>;\n  type Outputs = Record<never, never>;\n  const handler: BaseRuntimeSlackFunctionHandler<Inputs, Outputs> = () => {\n    return {\n      outputs: {},\n    };\n  };\n  const { createContext } = SlackFunctionTester(\"test\");\n  const result = handler(createContext({ inputs: {} }));\n  assertEquals(result.outputs, {});\n});\n\nDeno.test(\"BaseRuntimeSlackFunctionHandler with undefined inputs and outputs\", () => {\n  type Inputs = undefined;\n  type Outputs = undefined;\n  const handler: BaseRuntimeSlackFunctionHandler<Inputs, Outputs> = () => {\n    return {\n      outputs: {},\n    };\n  };\n  const { createContext } = SlackFunctionTester(\"test\");\n  const result = handler(createContext({ inputs: undefined }));\n  assertEquals(result.outputs, {});\n});\n\nDeno.test(\"BaseRuntimeSlackFunctionHandler with inputs and empty outputs\", () => {\n  type Inputs = {\n    in: string;\n  };\n  type Outputs = Record<never, never>;\n  const handler: BaseRuntimeSlackFunctionHandler<Inputs, Outputs> = (\n    { inputs },\n  ) => {\n    const _test = inputs.in;\n\n    return {\n      outputs: {},\n    };\n  };\n  const { createContext } = SlackFunctionTester(\"test\");\n  const inputs = { in: \"test\" };\n  const result = handler(createContext({ inputs }));\n  assertEquals(result.outputs, {});\n});\n\nDeno.test(\"BaseRuntimeSlackFunctionHandler with empty inputs and outputs\", () => {\n  type Inputs = Record<never, never>;\n  type Outputs = {\n    out: string;\n  };\n  const handler: BaseRuntimeSlackFunctionHandler<Inputs, Outputs> = () => {\n    return {\n      outputs: {\n        out: \"test\",\n      },\n    };\n  };\n  const { createContext } = SlackFunctionTester(\"test\");\n  const result = handler(createContext({ inputs: {} }));\n  assertEquals(result.outputs?.out, \"test\");\n});\n\nDeno.test(\"BaseRuntimeSlackFunctionHandler with any inputs and any outputs\", () => {\n  // deno-lint-ignore no-explicit-any\n  const handler: BaseRuntimeSlackFunctionHandler<any, any> = ({ inputs }) => {\n    return {\n      outputs: {\n        out: inputs.in,\n      },\n    };\n  };\n  const { createContext } = SlackFunctionTester(\"test\");\n  const inputs = { in: \"test\" };\n  const result = handler(createContext({ inputs }));\n  assertEquals(result.outputs?.out, inputs.in);\n});\n\nDeno.test(\"BaseRuntimeSlackFunctionHandler with no inputs and error output\", () => {\n  // deno-lint-ignore no-explicit-any\n  const handler: BaseRuntimeSlackFunctionHandler<any, { example: string }> =\n    () => {\n      return {\n        error: \"error\",\n      };\n    };\n  const { createContext } = SlackFunctionTester(\"test\");\n  const result = handler(createContext({ inputs: {} }));\n  assertEquals(result.error, \"error\");\n});\n\nDeno.test(\"BaseRuntimeSlackFunctionHandler with no inputs and completed false output\", () => {\n  // deno-lint-ignore no-explicit-any\n  const handler: BaseRuntimeSlackFunctionHandler<any, { example: boolean }> =\n    () => {\n      return {\n        completed: false,\n      };\n    };\n  const { createContext } = SlackFunctionTester(\"test\");\n  const result = handler(createContext({ inputs: {} }));\n  assertEquals(result.completed, false);\n});\n\nDeno.test(\"BaseRuntimeSlackFunctionHandler with set inputs and any outputs\", () => {\n  type Inputs = {\n    in: string;\n  };\n  // deno-lint-ignore no-explicit-any\n  const handler: BaseRuntimeSlackFunctionHandler<Inputs, any> = (\n    { inputs },\n  ) => {\n    return {\n      outputs: {\n        out: inputs.in,\n      },\n    };\n  };\n  const { createContext } = SlackFunctionTester(\"test\");\n  const inputs = { in: \"test\" };\n  const result = handler(createContext({ inputs }));\n  assertEquals(result.outputs?.out, inputs.in);\n});\n\nDeno.test(\"BaseRuntimeSlackFunctionHandler with input and output objects\", () => {\n  type Inputs = {\n    anObject: {\n      in: string;\n    };\n  };\n  type Outputs = {\n    anObject: {\n      out: string;\n    };\n  };\n  const handler: BaseRuntimeSlackFunctionHandler<Inputs, Outputs> = (\n    { inputs },\n  ) => {\n    return {\n      outputs: {\n        anObject: { out: inputs.anObject.in },\n      },\n    };\n  };\n  const { createContext } = SlackFunctionTester(\"test\");\n  const inputs = { anObject: { in: \"test\" } };\n  const result = handler(createContext({ inputs }));\n  assertEquals(result.outputs?.anObject?.out, inputs.anObject.in);\n});\n"
  },
  {
    "path": "src/functions/types_runtime_slack_function_handler_test.ts",
    "content": "import { assertExists } from \"../dev_deps.ts\";\nimport { assertEqualsTypedValues } from \"../test_utils.ts\";\nimport { SlackFunctionTester } from \"./tester/mod.ts\";\nimport { DefineFunction } from \"./mod.ts\";\nimport type { RuntimeSlackFunctionHandler } from \"./types.ts\";\n\nDeno.test(\"RuntimeSlackFunctionHandler type should not include a client property\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    output_parameters: {\n      properties: {\n        example: {\n          type: \"boolean\",\n        },\n      },\n      required: [\"example\"],\n    },\n  });\n  const handler: RuntimeSlackFunctionHandler<typeof TestFn.definition> = (\n    ctx,\n  ) => {\n    // @ts-expect-error ctx.client shouldn't be typed by RuntimeSlackFunctionHandler type - but SlackFunctionTester should inject it at runtime\n    assertExists(ctx.client);\n\n    return {\n      completed: false,\n    };\n  };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const result = handler(createContext({ inputs: {} }));\n  assertEqualsTypedValues(result.completed, false);\n});\n"
  },
  {
    "path": "src/functions/unhandled-event-error.ts",
    "content": "// This error conforms with what the deno-slack-runtime expects for an unhandled events\nexport class UnhandledEventError extends Error {\n  constructor(message: string) {\n    super(message);\n    // The name here is important, as it's what deno-slack-runtime keys off of\n    this.name = \"UnhandledEventError\";\n  }\n}\n"
  },
  {
    "path": "src/manifest/errors.ts",
    "content": "export class DuplicateCallbackIdError extends Error {\n  constructor(callbackId: string, readableType: \"Function\" | \"Workflow\") {\n    super(`Duplicate callback_id: \"${callbackId}\" found in ${readableType}.`);\n  }\n}\n\nexport class DuplicateNameError extends Error {\n  constructor(\n    name: string,\n    readableType: \"CustomType\" | \"Datastore\" | \"CustomEvent\",\n  ) {\n    super(`Duplicate name: \"${name}\" found in ${readableType}.`);\n  }\n}\n\nexport class DuplicateProviderKeyError extends Error {\n  constructor(provider_key: string, readableType: \"OAuth2Provider\") {\n    super(\n      `Duplicate provider_key: \"${provider_key}\" found in ${readableType}.`,\n    );\n  }\n}\n"
  },
  {
    "path": "src/manifest/errors_test.ts",
    "content": "import {\n  DuplicateCallbackIdError,\n  DuplicateNameError,\n  DuplicateProviderKeyError,\n} from \"./errors.ts\";\nimport { assertStringIncludes } from \"../dev_deps.ts\";\n\nDeno.test(`${DuplicateCallbackIdError.name} returns proper error message`, () => {\n  const actual = new DuplicateCallbackIdError(\"test\", \"Function\");\n\n  assertStringIncludes(actual.message, `callback_id: \"test`);\n  assertStringIncludes(actual.message, \"Function\");\n});\n\nDeno.test(`${DuplicateNameError.name} returns proper error message`, () => {\n  const actual = new DuplicateNameError(\"test\", \"CustomType\");\n\n  assertStringIncludes(actual.message, `name: \"test`);\n  assertStringIncludes(actual.message, \"CustomType\");\n});\n\nDeno.test(`${DuplicateProviderKeyError.name} returns proper error message`, () => {\n  const actual = new DuplicateProviderKeyError(\"test\", \"OAuth2Provider\");\n\n  assertStringIncludes(actual.message, `provider_key: \"test`);\n  assertStringIncludes(actual.message, \"OAuth2Provider\");\n});\n"
  },
  {
    "path": "src/manifest/manifest_schema.ts",
    "content": "import type { ISlackDatastore } from \"../datastore/types.ts\";\nimport type { ISlackFunctionDefinition } from \"../functions/types.ts\";\nimport type { ParameterSetDefinition } from \"../parameters/types.ts\";\nimport type { ParameterDefinition } from \"../parameters/definition_types.ts\";\nimport type { OAuth2ProviderTypeValues } from \"../schema/providers/oauth2/types.ts\";\nimport type { ICustomType } from \"../types/types.ts\";\nimport type { ISlackWorkflow } from \"../workflows/types.ts\";\nimport type { OAuth2ProviderOptions } from \"../providers/oauth2/types.ts\";\n\n// ----------------------------------------------------------------------------\n// Manifest Schema Types\n// These types should map directly to internal types, basically a pass through\n// ----------------------------------------------------------------------------\nexport type ManifestSchema = {\n  _metadata?: ManifestMetadataSchema;\n  settings: ManifestSettingsSchema;\n  app_directory?: ManifestAppDirectorySchema;\n  display_information: ManifestDisplayInformationSchema;\n  icon: string;\n  oauth_config: ManifestOauthConfigSchema;\n  features: ManifestFeaturesSchema;\n  functions?: ManifestFunctionsSchema;\n  workflows?: ManifestWorkflowsSchema;\n  outgoing_domains?: string[];\n  types?: ManifestCustomTypesSchema;\n  datastores?: ManifestDataStoresSchema;\n  events?: ManifestCustomEventsSchema;\n  external_auth_providers?: ManifestExternalAuthProviders;\n};\n\n// ---------------------------------------------------------------------------\n// Manifest: _metadata\n// ---------------------------------------------------------------------------\nexport type ManifestMetadataSchema = {\n  major_version?: number;\n  minor_version?: number;\n};\n\n// ---------------------------------------------------------------------------\n// Manifest: settings\n// ---------------------------------------------------------------------------\nexport type ManifestSettingsSchema = {\n  allowed_ip_address_ranges?: [string, ...string[]];\n  event_subscriptions?: ManifestEventSubscriptionsSchema;\n  incoming_webhooks?: ManifestIncomingWebhooks;\n  interactivity?: ManifestInteractivitySchema;\n  org_deploy_enabled?: boolean;\n  socket_mode_enabled?: boolean;\n  token_rotation_enabled?: boolean;\n  siws_links?: ManifestSiwsLinksSchema;\n  function_runtime?: ManifestFunctionRuntime;\n};\n\n// Settings: event subscriptions\nexport type ManifestEventSubscriptionsSchema = {\n  request_url?: string;\n  user_events?: string[];\n  bot_events?: string[];\n  metadata_subscriptions?: [\n    {\n      app_id: string;\n      event_type: string;\n    },\n    ...{\n      app_id: string;\n      event_type: string;\n    }[],\n  ];\n};\n\n// Settings: incoming webhooks\nexport type ManifestIncomingWebhooks = {\n  incoming_webhooks_enabled?: boolean;\n};\n\n// Settings interactivity\nexport type ManifestInteractivitySchema = {\n  is_enabled: boolean;\n  request_url?: string;\n  message_menu_options_url?: string;\n};\n\n// Settings: SIWS\nexport type ManifestSiwsLinksSchema = {\n  initiate_uri?: string;\n};\n\n// Settings: function runtime\nexport type ManifestFunctionRuntime = \"slack\" | \"remote\" | \"local\";\n\n// ---------------------------------------------------------------------------\n// Manifest: app directory\n// ---------------------------------------------------------------------------\nexport type ManifestAppDirectorySchema = {\n  app_directory_categories?: string[];\n  use_direct_install?: boolean;\n  direct_install_url?: string;\n  installation_landing_page: string;\n  privacy_policy_url: string;\n  support_url: string;\n  support_email: string;\n  supported_languages: [string, ...string[]];\n  pricing: string;\n};\n\n// ---------------------------------------------------------------------------\n// Manifest: display information\n// ---------------------------------------------------------------------------\nexport type ManifestDisplayInformationSchema = {\n  name: string;\n  description?: string;\n  background_color?: string;\n  long_description?: string;\n};\n\n// ---------------------------------------------------------------------------\n// Manifest: OAuth config\n// ---------------------------------------------------------------------------\nexport type ManifestOauthConfigSchema = {\n  scopes: {\n    bot?: string[];\n    user?: string[];\n  };\n  redirect_urls?: string[];\n  token_management_enabled?: boolean;\n};\n\n// ---------------------------------------------------------------------------\n// Manifest: features\n// ---------------------------------------------------------------------------\nexport interface ManifestFeaturesSchema {\n  bot_user?: ManifestBotUserSchema;\n  app_home?: ManifestAppHomeSchema;\n  shortcuts?: ManifestShortcutsSchema;\n  slash_commands?: ManifestSlashCommandsSchema;\n  unfurl_domains?: ManifestUnfurlDomainsSchema;\n  workflow_steps?: ManifestWorkflowStepsSchemaLegacy;\n}\n\n// Features: bot user\nexport type ManifestBotUserSchema = {\n  display_name: string;\n  always_online?: boolean;\n};\n\n// Features: app home\nexport type ManifestAppHomeSchema = ManifestAppHomeMessagesTabSchema & {\n  home_tab_enabled?: boolean;\n};\n\nexport type ManifestAppHomeMessagesTabSchema = {\n  /** @default true */\n  messages_tab_enabled?: true;\n  /** @default true */\n  messages_tab_read_only_enabled?: boolean;\n} | {\n  /** @default true */\n  messages_tab_enabled: false;\n  /** @default true */\n  messages_tab_read_only_enabled: false;\n};\n\n// Features: shortcuts\nexport type ManifestShortcutSchema = {\n  name: string;\n  type: \"message\" | \"global\";\n  callback_id: string;\n  description: string;\n};\n\nexport type ManifestShortcutsSchema = PopulatedArray<ManifestShortcutSchema>;\n\n// Features: slash commands\nexport type ManifestSlashCommandsSchema = PopulatedArray<\n  ManifestSlashCommandSchema\n>;\n\nexport type ManifestSlashCommandSchema = {\n  command: string;\n  url?: string;\n  description: string;\n  usage_hint?: string;\n  should_escape?: boolean;\n};\n\n// Features: legacy workflow step (To be deprecated)\n// Not to be confused with next generation ManifestWorkflowStepSchema\nexport type ManifestWorkflowStepLegacy = {\n  name: string;\n  callback_id: string;\n};\n\nexport type ManifestWorkflowStepsSchemaLegacy = PopulatedArray<\n  ManifestWorkflowStepLegacy\n>;\n\n// Features: unfurl domains\nexport type ManifestUnfurlDomainsSchema = [string, ...string[]];\n\n// ---------------------------------------------------------------------------\n// Manifest: custom functions\n// ---------------------------------------------------------------------------\n\n// This is typed liberally at this level but more specifically down further\n// This is to work around an issue TS has with resolving the generics across the hierarchy\n// deno-lint-ignore no-explicit-any\nexport type ManifestFunction = ISlackFunctionDefinition<any, any, any, any>;\n\nexport type ManifestFunctionsSchema = { [key: string]: ManifestFunctionSchema };\n\nexport type ManifestFunctionType = \"API\" | \"app\" | undefined;\n\nexport type ManifestFunctionSchema = {\n  type?: ManifestFunctionType;\n  title?: string;\n  description?: string;\n  source_file: string;\n  input_parameters: ManifestFunctionParameters;\n  output_parameters: ManifestFunctionParameters;\n};\n\nexport type ManifestFunctionParameters = {\n  required?: RequiredParameters;\n  properties: ParameterSetDefinition;\n};\n\nexport type RequiredParameters = {\n  [index: number]: string | number | symbol;\n};\n\n// ---------------------------------------------------------------------------\n// Manifest: workflows\n// Not to be confused with ManifestWorkflowStepsSchemaLegacy\n// ---------------------------------------------------------------------------\nexport type ManifestWorkflow = ISlackWorkflow;\n\nexport type ManifestWorkflowsSchema = { [key: string]: ManifestWorkflowSchema };\nexport type ManifestWorkflowSchema = {\n  title?: string;\n  description?: string;\n  input_parameters?: ManifestFunctionParameters;\n  steps: ManifestWorkflowStepSchema[];\n};\nexport type ManifestWorkflowStepSchema = {\n  id: string;\n  function_id: string;\n  inputs: {\n    [name: string]: unknown;\n  };\n};\n\n// ---------------------------------------------------------------------------\n// Manifest: custom events\n// ---------------------------------------------------------------------------\n\nexport type ManifestCustomEventSchema = ParameterDefinition;\n\nexport type ManifestCustomEventsSchema = {\n  [key: string]: ManifestCustomEventSchema;\n};\n// ---------------------------------------------------------------------------\n// Manifest: custom types\n// ---------------------------------------------------------------------------\n\nexport type ManifestCustomTypeSchema = ParameterDefinition;\nexport type ManifestCustomTypesSchema = {\n  [key: string]: ManifestCustomTypeSchema;\n};\n\n// ---------------------------------------------------------------------------\n// Manifest: datastores\n// ---------------------------------------------------------------------------\nexport type ManifestDatastore = ISlackDatastore;\nexport type ManifestDatastoreSchema = {\n  primary_key: string;\n  time_to_live_attribute?: string;\n  attributes: {\n    [key: string]: {\n      type: string | ICustomType;\n      items?: ManifestCustomTypeSchema;\n      properties?: {\n        [key: string]: ManifestCustomTypeSchema;\n      };\n    };\n  };\n};\n\nexport type ManifestDataStoresSchema = {\n  [key: string]: ManifestDatastoreSchema;\n};\n\n// -------------------------------------------------------------------------\n// Manifest: OAuth2 provider\n// -------------------------------------------------------------------------\nexport type ManifestOAuth2Schema = {\n  [key: string]: ManifestOAuth2ProviderSchema;\n};\n\nexport type ManifestOAuth2ProviderSchema = {\n  provider_type: OAuth2ProviderTypeValues;\n  options: OAuth2ProviderOptions;\n};\n\nexport interface ManifestExternalAuthProviders {\n  oauth2?: ManifestOAuth2Schema;\n}\n\n// -------------------------------------------------------------------------\n// Utilities\n// -------------------------------------------------------------------------\n\n// Utility type for the array types which requires minumum one subtype in it.\nexport type PopulatedArray<T> = [T, ...T[]];\n"
  },
  {
    "path": "src/manifest/manifest_test.ts",
    "content": "import type {\n  ISlackManifestRemote,\n  ISlackManifestRunOnSlack,\n  SlackManifestType,\n} from \"./types.ts\";\nimport { Manifest, SlackManifest } from \"./mod.ts\";\nimport {\n  DefineDatastore,\n  DefineEvent,\n  DefineFunction,\n  DefineType,\n  DefineWorkflow,\n  Schema,\n} from \"../mod.ts\";\nimport {\n  assert,\n  assertEquals,\n  assertInstanceOf,\n  AssertionError,\n  assertStrictEquals,\n  assertStringIncludes,\n  fail,\n  type IsExact,\n  mock,\n} from \"../dev_deps.ts\";\nimport { DefineConnector } from \"../functions/mod.ts\";\nimport { InternalSlackTypes } from \"../schema/slack/types/custom/mod.ts\";\nimport { DuplicateCallbackIdError, DuplicateNameError } from \"./errors.ts\";\n\nDeno.test(\"SlackManifestType correctly resolves to a Hosted App when runOnSlack = true\", () => {\n  const definition: SlackManifestType = {\n    runOnSlack: true,\n    name: \"test\",\n    description: \"description\",\n    backgroundColor: \"#FFF\",\n    longDescription:\n      \"The book is a roman à clef, rooted in autobiographical incidents. The story follows its protagonist, Raoul Duke, and his attorney, Dr. Gonzo, as they descend on Las Vegas to chase the American Dream...\",\n    displayName: \"displayName\",\n    icon: \"icon.png\",\n    botScopes: [\"channels:history\", \"chat:write\", \"commands\"],\n  };\n\n  assert<IsExact<typeof definition, ISlackManifestRunOnSlack>>(true);\n  assert<IsExact<typeof definition, ISlackManifestRemote>>(false);\n});\n\nDeno.test(\"SlackManifestType correctly resolves to a Remote App when runOnSlack = false\", () => {\n  const definition: SlackManifestType = {\n    runOnSlack: false,\n    name: \"test\",\n    description: \"description\",\n    backgroundColor: \"#FFF\",\n    longDescription:\n      \"The book is a roman à clef, rooted in autobiographical incidents. The story follows its protagonist, Raoul Duke, and his attorney, Dr. Gonzo, as they descend on Las Vegas to chase the American Dream...\",\n    displayName: \"displayName\",\n    icon: \"icon.png\",\n    botScopes: [\"channels:history\", \"chat:write\", \"commands\"],\n  };\n  assert<IsExact<typeof definition, ISlackManifestRunOnSlack>>(false);\n  assert<IsExact<typeof definition, ISlackManifestRemote>>(true);\n});\n\nDeno.test(\"Manifest() sets function_runtime = slack when runOnSlack = true\", () => {\n  const definition: SlackManifestType = {\n    runOnSlack: true,\n    name: \"test\",\n    description: \"description\",\n    backgroundColor: \"#FFF\",\n    longDescription:\n      \"The book is a roman à clef, rooted in autobiographical incidents. The story follows its protagonist, Raoul Duke, and his attorney, Dr. Gonzo, as they descend on Las Vegas to chase the American Dream...\",\n    displayName: \"displayName\",\n    icon: \"icon.png\",\n    botScopes: [\"channels:history\", \"chat:write\", \"commands\"],\n  };\n  const manifest = Manifest(definition);\n  assertEquals(manifest.settings.function_runtime, \"slack\");\n});\n\nDeno.test(\"Manifest() sets function_runtime = remote when runOnSlack = false\", () => {\n  const definition: SlackManifestType = {\n    runOnSlack: false,\n    name: \"test\",\n    description: \"description\",\n    backgroundColor: \"#FFF\",\n    longDescription:\n      \"The book is a roman à clef, rooted in autobiographical incidents. The story follows its protagonist, Raoul Duke, and his attorney, Dr. Gonzo, as they descend on Las Vegas to chase the American Dream...\",\n    displayName: \"displayName\",\n    icon: \"icon.png\",\n    botScopes: [\"channels:history\", \"chat:write\", \"commands\"],\n  };\n  const manifest = Manifest(definition);\n  assertEquals(manifest.settings.function_runtime, \"remote\");\n});\n\nDeno.test(\"Manifest() property mappings\", () => {\n  const definition: SlackManifestType = {\n    runOnSlack: true,\n    name: \"fear and loathing in las vegas\",\n    description:\n      \"fear and loathing in las vegas: a savage journey to the heart of the american dream\",\n    backgroundColor: \"#FFF\",\n    longDescription:\n      \"The book is a roman à clef, rooted in autobiographical incidents. The story follows its protagonist, Raoul Duke, and his attorney, Dr. Gonzo, as they descend on Las Vegas to chase the American Dream...\",\n    displayName: \"fear and loathing\",\n    icon: \"icon.png\",\n    botScopes: [],\n  };\n  let manifest = Manifest(definition);\n\n  assertEquals(manifest.display_information, {\n    name: definition.name,\n    background_color: definition.backgroundColor,\n    long_description: definition.longDescription,\n    description: definition.description,\n  });\n  assertStrictEquals(manifest.icon, definition.icon);\n  assertStrictEquals(\n    manifest.features.bot_user?.display_name,\n    definition.displayName,\n  );\n  assertEquals(manifest.settings.function_runtime, \"slack\");\n\n  // If display_name is not defined on definition, should fall back to name\n  delete definition.displayName;\n  manifest = Manifest(definition);\n  assertStrictEquals(\n    manifest.features.bot_user?.display_name,\n    definition.name,\n  );\n});\n// TODO: Re-add test to catch dup datastore names\n// TODO: Re-add test for datastore columns\n\nDeno.test(\"Manifest() automatically registers types used by function input and output parameters\", () => {\n  const inputTypeId = \"test_input_type\";\n  const outputTypeId = \"test_output_type\";\n  const stringTypeId = \"test_string_type\";\n\n  const CustomStringType = DefineType({\n    name: stringTypeId,\n    type: Schema.types.string,\n  });\n\n  const CustomInputType = DefineType({\n    name: inputTypeId,\n    type: CustomStringType,\n  });\n\n  const CustomOutputType = DefineType({\n    name: outputTypeId,\n    type: Schema.types.boolean,\n  });\n\n  const Function = DefineFunction(\n    {\n      callback_id: \"test_function\",\n      title: \"Function title\",\n      source_file: \"functions/test_function.ts\",\n      input_parameters: {\n        properties: {\n          aType: { type: CustomInputType },\n        },\n        required: [],\n      },\n      output_parameters: {\n        properties: {\n          aType: { type: CustomOutputType },\n        },\n        required: [],\n      },\n    },\n  );\n\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    longDescription: \"LongDescription\",\n    botScopes: [],\n    functions: [Function],\n  };\n  const manifest = Manifest(definition);\n  assertEquals(definition.types, [\n    CustomInputType,\n    CustomOutputType,\n    CustomStringType,\n  ]);\n  assertEquals(manifest.types, {\n    [inputTypeId]: CustomInputType.export(),\n    [stringTypeId]: CustomStringType.export(),\n    [outputTypeId]: CustomOutputType.export(),\n  });\n});\n\nDeno.test(\"Manifest() automatically registers functions used by workflows\", () => {\n  const Function = DefineFunction(\n    {\n      callback_id: \"test_function\",\n      title: \"Function title\",\n      source_file: \"functions/test_function.ts\",\n      input_parameters: {\n        properties: { aString: { type: Schema.types.string } },\n        required: [],\n      },\n      output_parameters: {\n        properties: { aType: { type: Schema.types.string } },\n        required: [],\n      },\n    },\n  );\n\n  const Workflow = DefineWorkflow({\n    title: \"test workflow\",\n    callback_id: \"test_workflow\",\n  });\n\n  Workflow.addStep(Function, {\n    aString: \"test\",\n  });\n\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    longDescription: \"LongDescription\",\n    botScopes: [],\n    workflows: [Workflow],\n  };\n  const manifest = Manifest(definition);\n\n  assertEquals(manifest.workflows, {\n    [Workflow.id]: Workflow.export(),\n  });\n  assertEquals(manifest.functions, {\n    [Function.id]: Function.export(),\n  });\n});\n\nDeno.test(\"Manifest() properly converts name to proper key\", () => {\n  const UsingName = DefineType({\n    name: \"Using Name\",\n    type: Schema.types.boolean,\n  });\n\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    longDescription: \"LongDescription\",\n    botScopes: [],\n    types: [UsingName],\n  };\n  const manifest = Manifest(definition);\n  assertEquals(manifest.types, { \"Using Name\": { type: \"boolean\" } });\n});\n\nDeno.test(\"Manifest() always sets token_management_enabled to false for runOnSlack: true apps\", () => {\n  // When runOnSlack is explicitly specified as true, token_management_enabled must be set to false\n  const definition: SlackManifestType = {\n    runOnSlack: true,\n    name: \"\",\n    description: \"\",\n    backgroundColor: \"#FFF\",\n    longDescription: \"\",\n    displayName: \"\",\n    icon: \"\",\n    botScopes: [],\n  };\n  const manifest = Manifest(definition);\n  assertEquals(manifest.oauth_config.token_management_enabled, false);\n});\n\nDeno.test(\"Manifest() always sets token_management_enabled to false for function_runtime: slack apps\", () => {\n  // SlackManifestType will default to function_runtime == slack when runOnSlack property omitted\n  // AND when no remote-only features are specified\n  const definition: SlackManifestType = {\n    name: \"\",\n    description: \"\",\n    backgroundColor: \"#FFF\",\n    longDescription: \"\",\n    displayName: \"\",\n    icon: \"\",\n    botScopes: [],\n  };\n  const manifest = Manifest(definition);\n  assertEquals(manifest.oauth_config.token_management_enabled, false);\n});\n\nDeno.test(\"Manifest() sets token_management_enabled to true by default for runOnSlack: false apps\", () => {\n  const definition: SlackManifestType = {\n    runOnSlack: false,\n    name: \"\",\n    description: \"\",\n    backgroundColor: \"\",\n    longDescription: \"\",\n    displayName: \"\",\n    icon: \"\",\n    botScopes: [],\n  };\n  const manifest = Manifest(definition);\n  assertEquals(manifest.oauth_config.token_management_enabled, true);\n});\n\nDeno.test(\"Manifest() automatically registers types referenced by datastores\", () => {\n  const stringTypeId = \"test_string_type\";\n  const objectTypeId = \"test_object_type\";\n  const StringType = DefineType({\n    name: stringTypeId,\n    type: Schema.types.string,\n  });\n\n  const ObjectType = DefineType({\n    name: objectTypeId,\n    type: Schema.types.object,\n    properties: {\n      aString: { type: StringType },\n    },\n  });\n\n  const Store = DefineDatastore({\n    name: \"Test store\",\n    attributes: {\n      aString: { type: \"string\" },\n      aType: { type: ObjectType },\n    },\n    primary_key: \"aString\",\n  });\n\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    botScopes: [],\n    datastores: [Store],\n  };\n  const manifest = Manifest(definition);\n  assertEquals(definition.types, [ObjectType, StringType]);\n  assertEquals(manifest.types, {\n    [stringTypeId]: StringType.export(),\n    [objectTypeId]: ObjectType.export(),\n  });\n});\n\nDeno.test(\"Manifest() automatically registers types referenced by events\", () => {\n  const objectEventTypeId = \"test_object_event_type\";\n  const objectTypeId = \"test_object_type\";\n  const objectEventId = \"test_object_event\";\n  const stringTypeId = \"test_string_type\";\n  const booleanTypeId = \"test_boolean_type\";\n  const arrayTypeId = \"test_array_type\";\n\n  const BooleanType = DefineType({\n    name: booleanTypeId,\n    type: Schema.types.boolean,\n  });\n\n  const StringType = DefineType({\n    name: stringTypeId,\n    type: Schema.types.string,\n  });\n\n  const ArrayType = DefineType({\n    name: arrayTypeId,\n    type: Schema.types.array,\n    items: {\n      type: StringType,\n    },\n  });\n\n  const ObjectType = DefineType({\n    name: objectTypeId,\n    type: Schema.types.object,\n    properties: {\n      aBoolean: { type: BooleanType },\n    },\n  });\n\n  const ObjectEvent = DefineEvent({\n    name: objectEventId,\n    type: Schema.types.object,\n    properties: {\n      aBoolean: { type: BooleanType },\n      anArray: { type: ArrayType },\n    },\n  });\n\n  const ObjectTypeEvent = DefineEvent({\n    name: objectEventTypeId,\n    type: ObjectType,\n  });\n\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    longDescription: \"LongDescription\",\n    botScopes: [],\n    events: [ObjectTypeEvent, ObjectEvent],\n  };\n  const manifest = Manifest(definition);\n\n  assertEquals(definition.events, [ObjectTypeEvent, ObjectEvent]);\n  assertEquals(manifest.events, {\n    [objectEventTypeId]: ObjectTypeEvent.export(),\n    [objectEventId]: ObjectEvent.export(),\n  });\n  assertEquals(definition.types, [\n    ObjectType,\n    BooleanType,\n    ArrayType,\n    StringType,\n  ]);\n  assertEquals(manifest.types, {\n    [objectTypeId]: ObjectType.export(),\n    [booleanTypeId]: BooleanType.export(),\n    [arrayTypeId]: ArrayType.export(),\n    [stringTypeId]: StringType.export(),\n  });\n});\n\nDeno.test(\"Manifest() automatically registers types referenced by other types\", () => {\n  const objectTypeId = \"test_object_type\";\n  const stringTypeId = \"test_string_type\";\n  const booleanTypeId = \"test_boolean_type\";\n  const arrayTypeId = \"test_array_type\";\n\n  const BooleanType = DefineType({\n    name: booleanTypeId,\n    type: Schema.types.boolean,\n  });\n\n  const StringType = DefineType({\n    name: stringTypeId,\n    type: Schema.types.string,\n  });\n\n  const ObjectType = DefineType({\n    name: objectTypeId,\n    type: Schema.types.object,\n    properties: {\n      aBoolean: {\n        type: BooleanType,\n      },\n    },\n  });\n\n  const ArrayType = DefineType({\n    name: arrayTypeId,\n    type: Schema.types.array,\n    items: {\n      type: StringType,\n    },\n  });\n\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    longDescription: \"LongDescription\",\n    botScopes: [],\n    types: [ArrayType, ObjectType],\n  };\n  const manifest = Manifest(definition);\n\n  assertEquals(definition.types, [\n    ArrayType,\n    ObjectType,\n    StringType,\n    BooleanType,\n  ]);\n  assertEquals(manifest.types, {\n    [arrayTypeId]: ArrayType.export(),\n    [objectTypeId]: ObjectType.export(),\n    [stringTypeId]: StringType.export(),\n    [booleanTypeId]: BooleanType.export(),\n  });\n});\n\nDeno.test(\"Manifest() correctly assigns display_information properties \", () => {\n  const definition: SlackManifestType = {\n    runOnSlack: false,\n    name: \"fear and loathing in las vegas\",\n    description:\n      \"fear and loathing in las vegas: a savage journey to the heart of the american dream\",\n    backgroundColor: \"#FFF\",\n    longDescription:\n      \"The book is a roman à clef, rooted in autobiographical incidents. The story follows its protagonist, Raoul Duke, and his attorney, Dr. Gonzo, as they descend on Las Vegas to chase the American Dream...\",\n    displayName: \"fear and loathing\",\n    icon: \"icon.png\",\n    botScopes: [\"channels:history\", \"chat:write\", \"commands\"],\n  };\n\n  const manifest = Manifest(definition);\n\n  assertEquals(manifest.display_information, {\n    name: definition.name,\n    background_color: definition.backgroundColor,\n    long_description: definition.longDescription,\n    description: definition.description,\n  });\n  assertStrictEquals(manifest.icon, definition.icon);\n  assertStrictEquals(\n    manifest.features.bot_user?.display_name,\n    definition.displayName,\n  );\n});\n\nDeno.test(\"Manifest() correctly assigns app_directory properties\", () => {\n  const definition: SlackManifestType = {\n    runOnSlack: false,\n    name: \"fear and loathing in las vegas\",\n    description:\n      \"fear and loathing in las vegas: a savage journey to the heart of the american dream\",\n    backgroundColor: \"#FFF\",\n    longDescription:\n      \"The book is a roman à clef, rooted in autobiographical incidents. The story follows its protagonist, Raoul Duke, and his attorney, Dr. Gonzo, as they descend on Las Vegas to chase the American Dream...\",\n    displayName: \"fear and loathing\",\n    icon: \"icon.png\",\n    botScopes: [\"channels:history\", \"chat:write\", \"commands\"],\n    appDirectory: {\n      app_directory_categories: [\"app-directory-test\"],\n      use_direct_install: true,\n      direct_install_url: \"https://api.slack.com/\",\n      installation_landing_page: \"https://api.slack.com/\",\n      privacy_policy_url: \"https://api.slack.com/\",\n      support_url: \"https://api.slack.com/\",\n      support_email: \"example@salesfroce.com\",\n      supported_languages: [\"eng\", \"fr\"],\n      pricing: \"free\",\n    },\n  };\n  const manifest = Manifest(definition);\n  // app directory\n  assertStrictEquals(\n    manifest.app_directory,\n    definition.appDirectory,\n  );\n});\n\nDeno.test(\"Manifest() correctly assigns remote app settings properties\", () => {\n  const definition: SlackManifestType = {\n    runOnSlack: false,\n    name: \"fear and loathing in las vegas\",\n    description:\n      \"fear and loathing in las vegas: a savage journey to the heart of the american dream\",\n    backgroundColor: \"#FFF\",\n    longDescription:\n      \"The book is a roman à clef, rooted in autobiographical incidents. The story follows its protagonist, Raoul Duke, and his attorney, Dr. Gonzo, as they descend on Las Vegas to chase the American Dream...\",\n    displayName: \"fear and loathing\",\n    icon: \"icon.png\",\n    botScopes: [\"channels:history\", \"chat:write\", \"commands\"],\n    settings: {\n      \"allowed_ip_address_ranges\": [\"123.89.34.56\"],\n      \"incoming_webhooks\": { \"incoming_webhooks_enabled\": true },\n      interactivity: {\n        is_enabled: true,\n        request_url: \"https://app.slack.com/test\",\n        message_menu_options_url: \"https://app.slack.com\",\n      },\n      \"org_deploy_enabled\": true,\n      \"siws_links\": { initiate_uri: \"https://app.slack.com\" },\n    },\n    eventSubscriptions: {\n      request_url: \"string\",\n      user_events: [\"app_home_opened\"],\n      bot_events: [\"app_home_opened\"],\n      metadata_subscriptions: [\n        {\n          app_id: \"metadata-test\",\n          event_type: \"customer_created\",\n        },\n      ],\n    },\n    socketModeEnabled: true,\n    tokenRotationEnabled: false,\n  };\n  const manifest = Manifest(definition);\n  assertStrictEquals(\n    manifest.settings.socket_mode_enabled,\n    definition.socketModeEnabled,\n  );\n  assertStrictEquals(\n    manifest.settings.token_rotation_enabled,\n    definition.tokenRotationEnabled,\n  );\n  assertStrictEquals(\n    manifest.settings.event_subscriptions,\n    definition.eventSubscriptions,\n  );\n  assertStrictEquals(\n    manifest.settings.allowed_ip_address_ranges,\n    definition.settings?.allowed_ip_address_ranges,\n  );\n  assertStrictEquals(\n    manifest.settings.incoming_webhooks,\n    definition.settings?.incoming_webhooks,\n  );\n  assertStrictEquals(\n    manifest.settings.org_deploy_enabled,\n    definition.settings?.org_deploy_enabled,\n  );\n  assertStrictEquals(\n    manifest.settings.siws_links,\n    definition.settings?.siws_links,\n  );\n  assertStrictEquals(manifest.settings.function_runtime, \"remote\");\n\n  // When org_deploy_enabled not supplied, remote app settings default org deploy to true\n  const definition2: SlackManifestType = {\n    runOnSlack: false,\n    name: \"\",\n    description: \"\",\n    backgroundColor: \"\",\n    longDescription: \"\",\n    displayName: \"\",\n    icon: \"\",\n    botScopes: [],\n    settings: {},\n  };\n  const manifest2 = Manifest(definition2);\n  assertEquals(manifest2.settings.org_deploy_enabled, true);\n});\n\nDeno.test(\"Manifest() correctly assigns run on slack app settings properties\", () => {\n  const definition: SlackManifestType = {\n    name: \"\",\n    description: \"\",\n    backgroundColor: \"\",\n    longDescription: \"\",\n    displayName: \"\",\n    icon: \"\",\n    botScopes: [],\n  };\n  const manifest = Manifest(definition);\n  assertEquals(manifest.settings.org_deploy_enabled, true);\n});\n\nDeno.test(\"Manifest() correctly assigns oauth properties\", () => {\n  const definition: SlackManifestType = {\n    runOnSlack: false,\n    name: \"fear and loathing in las vegas\",\n    description:\n      \"fear and loathing in las vegas: a savage journey to the heart of the american dream\",\n    backgroundColor: \"#FFF\",\n    longDescription:\n      \"The book is a roman à clef, rooted in autobiographical incidents. The story follows its protagonist, Raoul Duke, and his attorney, Dr. Gonzo, as they descend on Las Vegas to chase the American Dream...\",\n    displayName: \"fear and loathing\",\n    icon: \"icon.png\",\n    botScopes: [\"channels:history\", \"chat:write\", \"commands\"],\n    userScopes: [\"admin\", \"calls:read\"],\n    redirectUrls: [\"https://api.slack.com/\", \"https://app.slack.com/\"],\n  };\n  const manifest = Manifest(definition);\n  //oauth\n  assertStrictEquals(\n    manifest.oauth_config.scopes.user,\n    definition.userScopes,\n  );\n  assertStrictEquals(\n    manifest.oauth_config.redirect_urls,\n    definition.redirectUrls,\n  );\n  assertStrictEquals(\n    manifest.oauth_config.token_management_enabled,\n    true,\n  );\n});\n\nDeno.test(\"Manifest() correctly assigns other app features\", () => {\n  const definition: SlackManifestType = {\n    runOnSlack: false,\n    name: \"fear and loathing in las vegas\",\n    description:\n      \"fear and loathing in las vegas: a savage journey to the heart of the american dream\",\n    backgroundColor: \"#FFF\",\n    longDescription:\n      \"The book is a roman à clef, rooted in autobiographical incidents. The story follows its protagonist, Raoul Duke, and his attorney, Dr. Gonzo, as they descend on Las Vegas to chase the American Dream...\",\n    displayName: \"fear and loathing\",\n    icon: \"icon.png\",\n    botScopes: [\"channels:history\", \"chat:write\", \"commands\"],\n    features: {\n      botUser: { always_online: false },\n      shortcuts: [{\n        name: \"test-shortcut\",\n        type: \"message\",\n        callback_id: \"callback_id\",\n        description: \"shortcut\",\n      }],\n      appHome: {\n        homeTabEnabled: true,\n        messagesTabEnabled: false,\n        messagesTabReadOnlyEnabled: false,\n      },\n      slashCommands: [{\n        command: \"sample-command\",\n        url: \"https://app.slack.com\",\n        description: \"test command\",\n        usage_hint: \"testing\",\n        should_escape: true,\n      }, {\n        command: \"sample-command2\",\n        url: \"https://app.slack.com\",\n        description: \"test command 2\",\n        usage_hint: \"testing 2\",\n        should_escape: true,\n      }],\n      unfurlDomains: [\"https://app.slack.com\"],\n      workflowSteps: [{\n        name: \"workflow step test\",\n        callback_id: \"workflow-step-test\",\n      }],\n    },\n  };\n  const manifest = Manifest(definition);\n\n  //features\n  assertStrictEquals(\n    manifest.features.bot_user?.always_online,\n    definition.features?.botUser?.always_online,\n  );\n  assertStrictEquals(\n    manifest.features.shortcuts,\n    definition.features?.shortcuts,\n  );\n  assertStrictEquals(\n    manifest.features.slash_commands,\n    definition.features?.slashCommands,\n  );\n  assertEquals(\n    manifest.features.app_home?.home_tab_enabled,\n    true,\n  );\n  assertEquals(\n    manifest.features.app_home?.messages_tab_enabled,\n    false,\n  );\n  assertEquals(\n    manifest.features.app_home?.messages_tab_read_only_enabled,\n    false,\n  );\n  assertStrictEquals(\n    manifest.features.unfurl_domains,\n    definition.features?.unfurlDomains,\n  );\n  assertStrictEquals(\n    manifest.features.workflow_steps,\n    definition.features?.workflowSteps,\n  );\n});\n\nDeno.test(\"SlackManifest() registration functions don't allow duplicates\", () => {\n  const functionId = \"test_function\";\n  const arrayTypeId = \"test_array_type\";\n  const objectTypeId = \"test_object_type\";\n  const stringTypeId = \"test_string_type\";\n\n  const CustomStringType = DefineType({\n    name: stringTypeId,\n    type: Schema.types.string,\n  });\n\n  const CustomObjectType = DefineType({\n    name: objectTypeId,\n    type: Schema.types.object,\n    properties: {\n      aString: {\n        type: CustomStringType,\n      },\n    },\n    required: [\"aString\"],\n  });\n\n  const CustomArrayType = DefineType({\n    name: arrayTypeId,\n    type: Schema.types.array,\n    items: {\n      type: CustomStringType,\n    },\n  });\n\n  const Func = DefineFunction({\n    callback_id: functionId,\n    title: \"Function title\",\n    source_file: `functions/${functionId}.ts`,\n  });\n\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    longDescription: \"LongDescription\",\n    botScopes: [],\n    functions: [Func],\n    types: [CustomArrayType, CustomObjectType],\n  };\n\n  const Manifest = new SlackManifest(definition);\n\n  Manifest.registerFunction(Func);\n  Manifest.registerFunction(Func);\n  Manifest.registerType(CustomObjectType);\n  Manifest.registerType(CustomObjectType);\n  Manifest.registerType(CustomArrayType);\n  Manifest.registerType(CustomStringType);\n  Manifest.registerType(InternalSlackTypes.form_input_object);\n\n  const exportedManifest = Manifest.export();\n\n  assertEquals(definition.functions, [Func]);\n  assertEquals(exportedManifest.functions, { [functionId]: Func.export() });\n  assertEquals(definition.types, [\n    CustomArrayType,\n    CustomObjectType,\n    CustomStringType,\n  ]);\n  assertEquals(exportedManifest.types, {\n    [arrayTypeId]: CustomArrayType.export(),\n    [objectTypeId]: CustomObjectType.export(),\n    [stringTypeId]: CustomStringType.export(),\n  });\n});\n\nDeno.test(\"SlackManifest.export() warns of missing datastore scopes if they are not present and app includes a datastore\", () => {\n  const Store = DefineDatastore({\n    name: \"test store\",\n    attributes: {\n      attr: {\n        type: Schema.types.string,\n      },\n    },\n    primary_key: \"attr\",\n  });\n\n  const definition: SlackManifestType = {\n    runOnSlack: true,\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    longDescription: \"LongDescription\",\n    botScopes: [],\n    datastores: [Store],\n  };\n\n  const Manifest = new SlackManifest(definition);\n  const warnStub = mock.stub(console, \"warn\");\n  Manifest.export();\n  assertStringIncludes(\n    warnStub.calls[0].args[0],\n    \"does not specify the following datastore-related scopes\",\n  );\n  assertStringIncludes(warnStub.calls[0].args[0], \"datastore:read\");\n  assertStringIncludes(warnStub.calls[0].args[0], \"datastore:write\");\n  warnStub.restore();\n});\n\nDeno.test(\"SlackManifest.export() does not warn of missing datastore scopes if they're already present and app includes at least one datastore\", () => {\n  const Store = DefineDatastore({\n    name: \"test store\",\n    attributes: {\n      attr: {\n        type: Schema.types.string,\n      },\n    },\n    primary_key: \"attr\",\n  });\n\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    longDescription: \"LongDescription\",\n    botScopes: [\"datastore:read\", \"datastore:write\"],\n    datastores: [Store],\n  };\n\n  const Manifest = new SlackManifest(definition);\n  const warnStub = mock.stub(console, \"warn\");\n  const exportedManifest = Manifest.export();\n  const botScopes = exportedManifest.oauth_config.scopes.bot;\n  assertStrictEquals(\n    botScopes?.filter((scope: string) => scope === \"datastore:read\").length,\n    1,\n  );\n  assertStrictEquals(\n    botScopes?.filter((scope: string) => scope === \"datastore:write\").length,\n    1,\n  );\n  mock.assertSpyCalls(warnStub, 0);\n  warnStub.restore();\n});\n\nDeno.test(\"SlackManifest.export() defaults to enabling the read only messages tab\", () => {\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    botScopes: [],\n  };\n\n  const Manifest = new SlackManifest(definition);\n  const exportedManifest = Manifest.export();\n  exportedManifest.features.app_home?.messages_tab_enabled;\n  exportedManifest.features.app_home?.messages_tab_read_only_enabled;\n  assertStrictEquals(\n    exportedManifest.features.app_home?.messages_tab_enabled,\n    true,\n  );\n  assertStrictEquals(\n    exportedManifest.features.app_home?.messages_tab_read_only_enabled,\n    true,\n  );\n});\n\nDeno.test(\"SlackManifest.export() allows overriding app home features\", () => {\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    botScopes: [],\n    features: {\n      appHome: {\n        messagesTabEnabled: false,\n        messagesTabReadOnlyEnabled: false,\n      },\n    },\n  };\n\n  const Manifest = new SlackManifest(definition);\n  const exportedManifest = Manifest.export();\n  exportedManifest.features.app_home?.messages_tab_enabled;\n  exportedManifest.features.app_home?.messages_tab_read_only_enabled;\n  assertStrictEquals(\n    exportedManifest.features.app_home?.messages_tab_enabled,\n    false,\n  );\n  assertStrictEquals(\n    exportedManifest.features.app_home?.messages_tab_read_only_enabled,\n    false,\n  );\n});\n\nDeno.test(\"Manifest supports multiple workflows with parameters\", () => {\n  const workflow1 = DefineWorkflow({\n    callback_id: \"test\",\n    title: \"test\",\n    input_parameters: {\n      properties: {\n        one: {\n          type: Schema.types.string,\n        },\n      },\n      required: [\"one\"],\n    },\n  });\n\n  const workflow2 = DefineWorkflow({\n    callback_id: \"test2\",\n    title: \"test\",\n    input_parameters: {\n      properties: {\n        one: {\n          type: Schema.types.string,\n        },\n      },\n      required: [\"one\"],\n    },\n  });\n\n  const manifest = Manifest({\n    name: \"Name\",\n    description: \"Description\",\n    botScopes: [],\n    icon: \"icon.png\",\n    workflows: [workflow1, workflow2],\n  });\n\n  assertEquals(Object.keys(manifest.workflows || {}).length, 2);\n});\n\nDeno.test(\"Manifest() does not register connectors used by workflows\", () => {\n  const Function = DefineConnector(\n    {\n      callback_id: \"test_connector\",\n      title: \"Connector title\",\n      input_parameters: {\n        properties: { aString: { type: Schema.types.string } },\n        required: [],\n      },\n      output_parameters: {\n        properties: { aType: { type: Schema.types.string } },\n        required: [],\n      },\n    },\n  );\n\n  const Workflow = DefineWorkflow({\n    title: \"test workflow\",\n    callback_id: \"test_workflow\",\n  });\n\n  Workflow.addStep(Function, {\n    aString: \"test\",\n  });\n\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    longDescription: \"LongDescription\",\n    botScopes: [],\n    workflows: [Workflow],\n  };\n  const manifest = Manifest(definition);\n\n  assertEquals(manifest.workflows, {\n    [Workflow.id]: Workflow.export(),\n  });\n  assertEquals(manifest.functions, {});\n});\n\nDeno.test(\"Manifest throws error when workflows with duplicate callback_id are added\", () => {\n  const workflow1 = DefineWorkflow({\n    callback_id: \"test\",\n    title: \"workflow1\",\n    input_parameters: {\n      properties: {},\n      required: [],\n    },\n  });\n\n  const workflow2 = DefineWorkflow({\n    callback_id: \"test\",\n    title: \"workflow2\",\n    input_parameters: {\n      properties: {},\n      required: [],\n    },\n  });\n\n  try {\n    Manifest({\n      name: \"Name\",\n      description: \"Description\",\n      botScopes: [],\n      icon: \"icon.png\",\n      workflows: [workflow1, workflow2],\n    });\n    fail(\"Manifest() should have thrown an error\");\n  } catch (error) {\n    if (error instanceof AssertionError) throw error;\n    assertInstanceOf(error, DuplicateCallbackIdError);\n    assertStringIncludes(error.message, \"Workflow\");\n  }\n});\n\nDeno.test(\"Manifest throws error when functions with duplicate callback_id are added\", () => {\n  const function1 = DefineFunction({\n    callback_id: \"test\",\n    title: \"function1\",\n    source_file: `functions/test.ts`,\n  });\n\n  const function2 = DefineFunction({\n    callback_id: \"test\",\n    title: \"function2\",\n    source_file: `functions/test.ts`,\n  });\n\n  try {\n    Manifest({\n      name: \"Name\",\n      description: \"Description\",\n      botScopes: [],\n      icon: \"icon.png\",\n      functions: [function1, function2],\n    });\n    fail(\"Manifest() should have thrown an error\");\n  } catch (error) {\n    if (error instanceof AssertionError) throw error;\n    assertInstanceOf(error, DuplicateCallbackIdError);\n    assertStringIncludes(error.message, \"Function\");\n  }\n});\n\nDeno.test(\"Manifest throws error when customType with duplicate name are added\", () => {\n  const customType1 = DefineType({\n    name: \"customType\",\n    type: Schema.types.string,\n  });\n\n  const customType2 = DefineType({\n    name: \"customType\",\n    type: Schema.types.string,\n  });\n\n  try {\n    Manifest({\n      name: \"Name\",\n      description: \"Description\",\n      botScopes: [],\n      icon: \"icon.png\",\n      types: [customType1, customType2],\n    });\n    fail(\"Manifest() should have thrown an error\");\n  } catch (error) {\n    if (error instanceof AssertionError) throw error;\n    assertInstanceOf(error, DuplicateNameError);\n    assertStringIncludes(error.message, \"CustomType\");\n  }\n});\n\nDeno.test(\"Manifest throws error when Datastores with duplicate name are added\", () => {\n  const datastore1 = DefineDatastore({\n    name: \"Test store\",\n    attributes: {\n      datastore1: { type: \"string\" },\n    },\n    primary_key: \"datastore1\",\n  });\n\n  const datastore2 = DefineDatastore({\n    name: \"Test store\",\n    attributes: {\n      datastore2: { type: \"string\" },\n    },\n    primary_key: \"datastore2\",\n  });\n\n  try {\n    Manifest({\n      name: \"Name\",\n      description: \"Description\",\n      botScopes: [],\n      icon: \"icon.png\",\n      datastores: [datastore1, datastore2],\n    });\n    fail(\"Manifest() should have thrown an error\");\n  } catch (error) {\n    if (error instanceof AssertionError) throw error;\n    assertInstanceOf(error, DuplicateNameError);\n    assertStringIncludes(error.message, \"Datastore\");\n  }\n});\n\nDeno.test(\"Manifest throws error when CustomEvents with duplicate name are added\", () => {\n  const customEvent1 = DefineEvent({\n    name: \"test\",\n    title: \"customEvent1\",\n    type: Schema.types.object,\n    properties: {},\n  });\n\n  const customEvent2 = DefineEvent({\n    name: \"test\",\n    title: \"customEvent2\",\n    type: Schema.types.object,\n    properties: {},\n  });\n\n  try {\n    Manifest({\n      name: \"Name\",\n      description: \"Description\",\n      botScopes: [],\n      icon: \"icon.png\",\n      events: [customEvent1, customEvent2],\n    });\n    fail(\"Manifest() should have thrown an error\");\n  } catch (error) {\n    if (error instanceof AssertionError) throw error;\n    assertInstanceOf(error, DuplicateNameError);\n    assertStringIncludes(error.message, \"CustomEvent\");\n  }\n});\n"
  },
  {
    "path": "src/manifest/mod.ts",
    "content": "import type {\n  ISlackManifestRemote,\n  ISlackManifestRunOnSlack,\n  SlackManifestType,\n} from \"./types.ts\";\nimport type { ICustomType } from \"../types/types.ts\";\nimport type { ParameterSetDefinition } from \"../parameters/types.ts\";\nimport type {\n  ManifestAppHomeMessagesTabSchema,\n  ManifestAppHomeSchema,\n  ManifestCustomEventsSchema,\n  ManifestCustomTypesSchema,\n  ManifestDataStoresSchema,\n  ManifestFunction,\n  ManifestFunctionRuntime,\n  ManifestFunctionsSchema,\n  ManifestSchema,\n  ManifestWorkflowsSchema,\n} from \"./manifest_schema.ts\";\nimport { isCustomType } from \"../types/mod.ts\";\nimport { isCustomFunctionDefinition } from \"../functions/definitions/slack-function.ts\";\nimport {\n  DuplicateCallbackIdError,\n  DuplicateNameError,\n  DuplicateProviderKeyError,\n} from \"./errors.ts\";\n\nexport const Manifest = (\n  definition: Omit<ISlackManifestRunOnSlack, \"runOnSlack\">,\n) => {\n  const manifest = new SlackManifest(definition);\n  return manifest.export();\n};\n\nexport class SlackManifest {\n  constructor(private definition: SlackManifestType) {\n    this.registerFeatures();\n  }\n\n  export() {\n    const def = this.definition;\n    const manifest: ManifestSchema = {\n      _metadata: {\n        // todo: is there a more idiomatic way of defining this? constant file?\n        major_version: 2,\n      },\n      display_information: {\n        background_color: def.backgroundColor,\n        name: def.name,\n        long_description: def.longDescription,\n        description: def.description,\n      },\n      icon: def.icon,\n      oauth_config: {\n        scopes: {\n          bot: this.ensureBotScopes(),\n        },\n      },\n      features: {\n        bot_user: {\n          display_name: def.displayName || def.name,\n        },\n      },\n      settings: { function_runtime: this.getFunctionRuntime() },\n    };\n\n    // Assign other shared properties\n    if (def.functions) {\n      manifest.functions = def.functions.reduce<ManifestFunctionsSchema>(\n        (acc = {}, fn) => {\n          if (isCustomFunctionDefinition(fn)) {\n            if (fn.id in acc) {\n              throw new DuplicateCallbackIdError(fn.id, \"Function\");\n            }\n            acc[fn.id] = fn.export();\n          }\n          return acc;\n        },\n        {},\n      );\n    }\n\n    if (def.workflows) {\n      manifest.workflows = def.workflows.reduce<ManifestWorkflowsSchema>(\n        (acc = {}, workflow) => {\n          if (workflow.id in acc) {\n            throw new DuplicateCallbackIdError(workflow.id, \"Workflow\");\n          }\n          acc[workflow.id] = workflow.export();\n          return acc;\n        },\n        {},\n      );\n    }\n\n    if (def.types) {\n      manifest.types = def.types.reduce<ManifestCustomTypesSchema>(\n        (acc = {}, customType) => {\n          if (customType.id in acc) {\n            throw new DuplicateNameError(customType.id, \"CustomType\");\n          }\n          acc[customType.id] = customType.export();\n          return acc;\n        },\n        {},\n      );\n    }\n\n    if (def.datastores) {\n      manifest.datastores = def.datastores.reduce<ManifestDataStoresSchema>(\n        (acc = {}, datastore) => {\n          if (datastore.name in acc) {\n            throw new DuplicateNameError(datastore.name, \"Datastore\");\n          }\n          acc[datastore.name] = datastore.export();\n          return acc;\n        },\n        {},\n      );\n    }\n\n    if (def.events) {\n      manifest.events = def.events.reduce<ManifestCustomEventsSchema>(\n        (acc = {}, event) => {\n          if (event.id in acc) {\n            throw new DuplicateNameError(event.id, \"CustomEvent\");\n          }\n          acc[event.id] = event.export();\n          return acc;\n        },\n        {},\n      );\n    }\n\n    manifest.outgoing_domains = def.outgoingDomains || [];\n\n    // Assign remote hosted app properties\n    if (manifest.settings.function_runtime === \"slack\") {\n      this.assignRunOnSlackManifestProperties(manifest);\n    } else if (manifest.settings.function_runtime === \"remote\") {\n      this.assignRemoteSlackManifestProperties(manifest);\n    }\n\n    return manifest;\n  }\n\n  private registerFeatures() {\n    this.definition.workflows?.forEach((workflow) => {\n      workflow.registerStepFunctions(this);\n      workflow.registerParameterTypes(this);\n    });\n    // Loop through functions to automatically register any referenced types\n    this.definition.functions?.forEach((func) => {\n      if (isCustomFunctionDefinition(func)) {\n        func.registerParameterTypes(this);\n      }\n    });\n\n    // Loop through datastores to automatically register any referenced types\n    this.definition.datastores?.forEach((datastore) => {\n      datastore.registerAttributeTypes(this);\n    });\n\n    // Loop through events to automatically register any referenced types\n    this.definition.events?.forEach((event) => {\n      event.registerParameterTypes(this);\n    });\n\n    // Loop through types to automatically register any referenced sub-types\n    const registeredTypes = this.definition.types || [];\n    for (let i = 0; i < registeredTypes.length; i++) {\n      this.definition.types?.[i].registerParameterTypes(this);\n    }\n  }\n\n  registerFunction(func: ManifestFunction) {\n    if (!this.definition.functions) this.definition.functions = [];\n    // Check to make sure function doesn't already exist on manifest\n    else if (this.definition.functions.some((f) => func.id === f.id)) return;\n    // Add function to manifest\n    this.definition.functions.push(func);\n  }\n\n  // Loop through a ParameterSetDefinition to register each individual type\n  registerTypes(parameterSet: ParameterSetDefinition) {\n    Object.values(parameterSet).forEach((param) => {\n      if (isCustomType(param.type)) {\n        this.registerType(param.type);\n      }\n    });\n  }\n\n  registerType(customType: ICustomType) {\n    if (!this.definition.types) this.definition.types = [];\n    // Don't register Slack types\n    if (customType.id.startsWith(\"slack#/\")) {\n      return;\n    }\n    // Check to make sure type doesn't already exist on manifest\n    if (this.definition.types.some((type) => type.id === customType.id)) {\n      return;\n    }\n    // Add type to manifest\n    this.definition.types.push(customType);\n  }\n\n  /**\n   * Verifies scopes defined in the app passes baseline validation.\n   * @returns {string[]} The user-defined manifest scopes from `definition.botScopes`\n   */\n  private ensureBotScopes(): string[] {\n    // Warn about missing datastore scopes if app includes datastores\n    if (Object.keys(this.definition.datastores ?? {}).length > 0) {\n      const missingScopes: string[] = [];\n      const datastoreScopes = [\"datastore:read\", \"datastore:write\"];\n      datastoreScopes.forEach((scope) => {\n        if (!this.definition.botScopes.includes(scope)) {\n          missingScopes.push(scope);\n        }\n      });\n      if (missingScopes.length > 0) {\n        console.warn(\n          `Warning! Application manifest includes at least one datastore, but does not specify the following datastore-related scopes in its 'botScopes': ${\n            missingScopes.join(\", \")\n          }`,\n        );\n      }\n    }\n\n    return this.definition.botScopes;\n  }\n\n  // Maps the top level runOnSlack boolean property to corresponding underlying ManifestSchema function_runtime property required by Slack API.\n  // If no runOnSlack property supplied, then functionRuntime defaults to \"slack\".\n  private getFunctionRuntime(): ManifestFunctionRuntime {\n    return this.definition.runOnSlack === false ? \"remote\" : \"slack\";\n  }\n\n  // Assigns the remote app properties\n  private assignRemoteSlackManifestProperties(manifest: ManifestSchema) {\n    const def = this.definition as ISlackManifestRemote;\n\n    //Settings\n    manifest.settings = {\n      ...manifest.settings,\n      ...def.settings,\n    };\n    manifest.settings.event_subscriptions = def.eventSubscriptions;\n    manifest.settings.socket_mode_enabled = def.socketModeEnabled;\n    manifest.settings.token_rotation_enabled = def.tokenRotationEnabled;\n\n    // Set app home features\n    if (def.features?.appHome) {\n      const {\n        homeTabEnabled,\n        messagesTabEnabled,\n        messagesTabReadOnlyEnabled,\n      } = def.features.appHome;\n\n      manifest.features.app_home = {\n        home_tab_enabled: homeTabEnabled,\n        messages_tab_enabled: messagesTabEnabled,\n        messages_tab_read_only_enabled: messagesTabReadOnlyEnabled,\n      } as ManifestAppHomeSchema;\n    }\n\n    // Set org deploy enabled to true unless specified by dev\n    // Org deploy enabled is required to use remote functions\n    manifest.settings.org_deploy_enabled =\n      (def.settings?.org_deploy_enabled !== undefined)\n        ? def.settings?.org_deploy_enabled\n        : true;\n\n    //AppDirectory\n    manifest.app_directory = def.appDirectory;\n\n    //OauthConfig\n    manifest.oauth_config.scopes.user = def.userScopes;\n    manifest.oauth_config.redirect_urls = def.redirectUrls;\n\n    // Remote-hosted Slack apps manage their own tokens\n    manifest.oauth_config.token_management_enabled = true;\n\n    // Remote Features\n    manifest.features.bot_user!.always_online = def.features?.botUser\n      ?.always_online;\n    manifest.features.shortcuts = def.features?.shortcuts;\n    manifest.features.slash_commands = def.features?.slashCommands;\n    manifest.features.unfurl_domains = def.features?.unfurlDomains;\n    manifest.features.workflow_steps = def.features?.workflowSteps;\n  }\n\n  private assignRunOnSlackManifestProperties(manifest: ManifestSchema) {\n    const def = this.definition as ISlackManifestRunOnSlack;\n\n    // Run on Slack Apps do not manage access tokens\n    // This is set by default as false\n    manifest.oauth_config.token_management_enabled = false;\n\n    // Required App Settings for run on slack apps\n    manifest.settings.org_deploy_enabled = true;\n\n    // App Home\n    // Default to messages enabled\n    manifest.features.app_home = {\n      messages_tab_enabled: true,\n      messages_tab_read_only_enabled: true,\n    } as ManifestAppHomeMessagesTabSchema;\n\n    // Allow App Home override values if provided in apphome\n    if (def.features?.appHome) {\n      const {\n        messagesTabEnabled,\n        messagesTabReadOnlyEnabled,\n      } = def.features.appHome;\n\n      manifest.features.app_home.messages_tab_enabled = messagesTabEnabled;\n      manifest.features.app_home.messages_tab_read_only_enabled =\n        messagesTabReadOnlyEnabled;\n    }\n\n    // External Auth providers\n    if (def.externalAuthProviders?.length) {\n      manifest.external_auth_providers = def.externalAuthProviders.reduce(\n        (acc, provider) => {\n          acc[\"oauth2\"] = acc[\"oauth2\"] ?? {};\n          if (provider.id in acc[\"oauth2\"]) {\n            throw new DuplicateProviderKeyError(provider.id, \"OAuth2Provider\");\n          }\n          acc[\"oauth2\"][provider.id] = provider.export();\n          return acc;\n        },\n        {} as NonNullable<ManifestSchema[\"external_auth_providers\"]>,\n      );\n    }\n  }\n}\n"
  },
  {
    "path": "src/manifest/types.ts",
    "content": "import type {\n  ManifestAppDirectorySchema,\n  ManifestAppHomeMessagesTabSchema,\n  ManifestAppHomeSchema,\n  ManifestBotUserSchema,\n  ManifestDatastore,\n  ManifestEventSubscriptionsSchema,\n  ManifestFunction,\n  ManifestSettingsSchema,\n  ManifestShortcutsSchema,\n  ManifestSlashCommandsSchema,\n  ManifestUnfurlDomainsSchema,\n  ManifestWorkflow,\n  ManifestWorkflowStepsSchemaLegacy,\n} from \"./manifest_schema.ts\";\nimport type { OAuth2Provider } from \"../providers/oauth2/mod.ts\";\nimport type { ICustomType } from \"../types/types.ts\";\nimport type { CamelCasedPropertiesDeep } from \"./types_util.ts\";\nimport type { ICustomEvent } from \"../events/types.ts\";\n\n/** Manifest definition.\n *\n * SlackManifestType contains affordances for better user experience (e.g runOnSlack property)\n * The lower level ManifestSchema aligns with Slack API\n *\n * A discriminated union where the discriminant property runOnSlack\n * maps to function_runtime in the underlying ManifestSchema.\n */\nexport type SlackManifestType =\n  | ISlackManifestRunOnSlack\n  | ISlackManifestRemote;\n\n/** Slack-hosted app manifest\n *\n * When runOnSlack = true\n * Corresponds to function_runtime = slack in ManifestSchema.\n */\nexport interface ISlackManifestRunOnSlack extends ISlackManifestShared {\n  runOnSlack?: true; // maps to function_runtime = \"slack\" in ManifestSchema, optional since the apps are slack hosted by default\n  features?: ISlackManifestRunOnSlackFeaturesSchema;\n  externalAuthProviders?: (OAuth2Provider /*|OAuth1Provider*/)[];\n}\n\n/** Non-Slack hosted app manifest\n *\n * When runOnSlack = false.\n * Corresponds to function_runtime = remote in ManifestSchema.\n */\nexport interface ISlackManifestRemote extends ISlackManifestShared {\n  runOnSlack: false; // maps to function_runtime = \"remote\" in ManifestSchema\n\n  settings?: Omit<\n    ManifestSettingsSchema,\n    | \"event_subscriptions\"\n    | \"socket_mode_enabled\"\n    | \"token_rotation_enabled\"\n    | \"function_runtime\"\n  >; // lifting omitted properties to top level\n  eventSubscriptions?: ManifestEventSubscriptionsSchema;\n  socketModeEnabled?: boolean;\n  tokenRotationEnabled?: boolean;\n  appDirectory?: ManifestAppDirectorySchema;\n  userScopes?: Array<string>;\n  redirectUrls?: Array<string>;\n  features?: ISlackManifestRemoteFeaturesSchema;\n}\n\n/* Shared app manifest properties */\ninterface ISlackManifestShared {\n  name: string;\n  backgroundColor?: string;\n  description: string;\n  displayName?: string;\n  icon: string;\n  longDescription?: string;\n  botScopes: Array<string>;\n  functions?: ManifestFunction[];\n  workflows?: ManifestWorkflow[];\n  outgoingDomains?: Array<string>;\n  events?: ICustomEvent[];\n  types?: ICustomType[];\n  datastores?: ManifestDatastore[];\n}\n\ninterface ISlackManifestRunOnSlackFeaturesSchema {\n  // currently home_tab_enabled is not supported for RunOnSlack apps\n  appHome?: CamelCasedPropertiesDeep<ManifestAppHomeMessagesTabSchema>;\n}\ninterface ISlackManifestRemoteFeaturesSchema {\n  appHome?: CamelCasedPropertiesDeep<ManifestAppHomeSchema>;\n  botUser?: Omit<ManifestBotUserSchema, \"display_name\">;\n  shortcuts?: ManifestShortcutsSchema;\n  slashCommands?: ManifestSlashCommandsSchema;\n  unfurlDomains?: ManifestUnfurlDomainsSchema;\n  workflowSteps?: ManifestWorkflowStepsSchemaLegacy;\n}\n"
  },
  {
    "path": "src/manifest/types_util.ts",
    "content": "/** Utility types to enable conversion of type properties to camelCase\n *\n * This is a shameless lift from sindresorhus's awesome type-fest project\n *\n * https://github.com/sindresorhus/type-fest\n *\n * Imported simply because it hasn't been distributed to deno.land yet\n * Please support this project!\n */\n\ntype Split<\n  S extends string,\n  Delimiter extends string,\n> = S extends `${infer Head}${Delimiter}${infer Tail}`\n  ? [Head, ...Split<Tail, Delimiter>]\n  : S extends Delimiter ? []\n  : [S];\n\n//deno-fmt-ignore\ntype CamelCase<K> = K extends string ? CamelCaseStringArray<\n  Split<K extends Uppercase<K> ? Lowercase<K> : K, \"-\" | \"_\" | \" \">\n>\n  : K;\n//deno-fmt-ignore\ntype CamelCaseStringArray<Parts extends readonly string[]> = Parts extends\n  [`${infer FirstPart}`, ...infer RemainingParts] ? Uncapitalize<\n  `${FirstPart}${InnerCamelCaseStringArray<RemainingParts, FirstPart>}`\n>\n  : never;\n\n// deno-lint-ignore no-explicit-any\ntype InnerCamelCaseStringArray<Parts extends readonly any[], PreviousPart> =\n  Parts extends [`${infer FirstPart}`, ...infer RemainingParts]\n    ? FirstPart extends undefined ? \"\"\n    : FirstPart extends \"\"\n      ? InnerCamelCaseStringArray<RemainingParts, PreviousPart>\n    : `${PreviousPart extends \"\" ? FirstPart\n      : Capitalize<FirstPart>}${InnerCamelCaseStringArray<\n      RemainingParts,\n      FirstPart\n    >}`\n    : \"\";\n\n// deno-lint-ignore ban-types\nexport type CamelCasedPropertiesDeep<Value> = Value extends Function ? Value\n  : Value extends Array<infer U> ? Array<CamelCasedPropertiesDeep<U>>\n  : Value extends Set<infer U> ? Set<CamelCasedPropertiesDeep<U>>\n  : {\n    [K in keyof Value as CamelCase<K>]: CamelCasedPropertiesDeep<Value[K]>;\n  };\n"
  },
  {
    "path": "src/mod.ts",
    "content": "export { Manifest, SlackManifest } from \"./manifest/mod.ts\";\nexport type {\n  ISlackManifestRemote,\n  ISlackManifestRunOnSlack,\n  SlackManifestType,\n} from \"./manifest/types.ts\";\nexport type { ManifestSchema } from \"./manifest/manifest_schema.ts\";\nexport { DefineFunction } from \"./functions/mod.ts\";\nexport { SlackFunction } from \"./functions/slack-function.ts\";\nexport {\n  BlockActionsRouter,\n  ViewsRouter,\n} from \"./functions/interactivity/mod.ts\";\nexport type { ViewEvents } from \"./functions/interactivity/view_types.ts\";\nexport { DefineWorkflow } from \"./workflows/mod.ts\";\nexport { DefineEvent } from \"./events/mod.ts\";\nexport { DefineType } from \"./types/mod.ts\";\nexport { DefineOAuth2Provider } from \"./providers/oauth2/mod.ts\";\nexport { default as Schema } from \"./schema/mod.ts\";\nexport { DefineDatastore } from \"./datastore/mod.ts\";\nexport { SlackFunctionTester } from \"./functions/tester/mod.ts\";\nexport { SlackAPI } from \"./deps.ts\";\nexport { DefineProperty } from \"./parameters/define_property.ts\";\n"
  },
  {
    "path": "src/mod_test.ts",
    "content": "import { assertExists } from \"./dev_deps.ts\";\nimport * as mod from \"./mod.ts\";\n\nDeno.test(\"Include all content of mod.ts in code coverage\", () => {\n  assertExists(mod);\n});\n"
  },
  {
    "path": "src/parameters/define_property.ts",
    "content": "import type {\n  TypedObjectParameterDefinition,\n  TypedObjectProperties,\n  TypedObjectRequiredProperties,\n} from \"../parameters/definition_types.ts\";\n\nexport const DefineProperty = <\n  Props extends TypedObjectProperties,\n  RequiredProps extends TypedObjectRequiredProperties<Props>,\n  Def extends TypedObjectParameterDefinition<Props, RequiredProps>,\n>(\n  definition: Def,\n) => {\n  return definition;\n};\n"
  },
  {
    "path": "src/parameters/define_property_test.ts",
    "content": "import { DefineProperty } from \"./define_property.ts\";\nimport SchemaTypes from \"../schema/schema_types.ts\";\nimport { assert, type IsExact } from \"../dev_deps.ts\";\n\nDeno.test(\"DefineProperty should allow for object property names to be specified in the required field\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      aString: {\n        type: SchemaTypes.string,\n      },\n      anOptionalString: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"aString\"],\n    additionalProperties: true,\n  });\n  assert<IsExact<typeof obj.required, \"aString\"[]>>(true);\n});\n\n/*\nTODO: DefineProperty fails to constrain the required field to property names :(\nDeno.test(\"DefineProperty should prevent non-object property names to be specified in the required field\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      aString: {\n        type: SchemaTypes.string,\n      },\n      anOptionalString: {\n        type: SchemaTypes.string,\n      },\n    },\n    // @ts-expect-error should not allow for bogus property names\n    required: [\"bogus\"],\n    additionalProperties: true,\n  });\n});\n*/\n"
  },
  {
    "path": "src/parameters/definition_types.ts",
    "content": "import type SchemaTypes from \"../schema/schema_types.ts\";\nimport type { ValidSchemaTypes } from \"../schema/schema_types.ts\";\nimport type {\n  SlackPrimitiveTypes,\n  ValidSlackPrimitiveTypes,\n} from \"../schema/slack/types/mod.ts\";\nimport type { LooseStringAutocomplete } from \"../type_utils.ts\";\nimport type { ICustomType } from \"../types/types.ts\";\n\nexport type ParameterDefinition = TypedParameterDefinition;\n\nexport type PrimitiveParameterDefinition =\n  | BooleanParameterDefinition\n  | StringParameterDefinition\n  | NumberParameterDefinition\n  | IntegerParameterDefinition\n  | BaseParameterDefinition<AllValues>\n  // | UntypedArrayParameterDefinition\n  | TypedArrayParameterDefinition;\n\nexport type TypedParameterDefinition =\n  | CustomTypeParameterDefinition\n  | TypedObjectParameter\n  | UntypedObjectParameterDefinition\n  | PrimitiveParameterDefinition\n  | OAuth2ParameterDefinition;\n\nexport interface CustomTypeParameterDefinition\n  extends Omit<BaseParameterDefinition<AllValues>, \"type\"> {\n  type: ICustomType;\n}\n\ninterface BaseParameterDefinition<T> {\n  /** Defines the parameter type. */\n  type: LooseStringAutocomplete<ValidSchemaTypes | ValidSlackPrimitiveTypes>;\n  /** An optional parameter title. */\n  title?: string;\n  /** An optional parameter description. */\n  description?: string;\n  /** An optional parameter hint. */\n  hint?: string;\n  /** An optional parameter default value. */\n  default?: T;\n  /** An option list of examples; intended for future use in a possible app type schemas page. */\n  examples?: T[];\n}\n\n/**\n * Only used for defining Custom Types via `DefineType`\n * The below type is explicitly different from the above ParameterDefinition type in that:\n * - It replaces the generic-less ComplexParameterDefinition so that...\n * - It can lift the generic-ful TypeddObjectParamaterDefinition's generics to ParameterDefinitionWithgenerics so that...\n * - The props/required props generic pair, which rely on each other, can be exposed in DefineType so that...\n * - .. the dependency between props/required props can be raised to the dev when authoring function runtime logic, and e.g. not returning a required property in a function output\n */\nexport type ParameterDefinitionWithGenerics<\n  Props extends TypedObjectProperties,\n  RequiredProps extends TypedObjectRequiredProperties<Props>,\n> =\n  | Exclude<ParameterDefinition, TypedObjectParameter>\n  | TypedObjectParameterDefinition<Props, RequiredProps>;\n\nexport interface UntypedObjectParameterDefinition\n  extends BaseParameterDefinition<ObjectValue> {\n  type: typeof SchemaTypes.object;\n}\n\nexport type TypedObjectProperties = {\n  [key: string]:\n    | PrimitiveParameterDefinition\n    | CustomTypeParameterDefinition;\n};\n\nexport type TypedObjectRequiredProperties<Props extends TypedObjectProperties> =\n  | (Exclude<keyof Props, symbol>)[]\n  | undefined;\n\n/**\n * Models the shape of a Typed Object parameter, and using the two generics,\n * models the dependent relationship between the properties of an object and which of the properties are required vs. optional\n */\nexport interface TypedObjectParameterDefinition<\n  Props extends TypedObjectProperties,\n  RequiredProps extends TypedObjectRequiredProperties<Props>,\n> extends UntypedObjectParameterDefinition // TODO: the second type parameter would not accurately reflect what typed objects would look like - would limit to flat objects only.\n{\n  /**\n   * Whether the parameter can accept objects with additional keys beyond those defined via `properties`\n   * @default \"true\"\n   */\n  additionalProperties?: boolean;\n  /** Object defining what properties are allowed on the parameter. */\n  properties: Props;\n  /** A list of required property names (must reference names defined on the `properties` property). Only for use with Object types. */\n  required?: RequiredProps;\n}\n\n/**\n * Models _only_ the shape of a Typed Object parameter\n * Unlike TypedObjectParameterDefinition above, does _not_ constrain the elements\n * of the `required` array to the keys of the `properties` object.\n */\nexport type TypedObjectParameter = TypedObjectParameterDefinition<\n  TypedObjectProperties,\n  TypedObjectRequiredProperties<TypedObjectProperties>\n>;\n\ninterface BooleanParameterDefinition extends BaseParameterDefinition<boolean> {\n  type: typeof SchemaTypes.boolean;\n}\n\ninterface StringParameterDefinition extends BaseParameterDefinition<string> {\n  type: typeof SchemaTypes.string;\n  /** Minimum number of characters comprising the string */\n  minLength?: number;\n  /** Maximum number of characters comprising the string */\n  maxLength?: number;\n  /** Constrain the available string options to just the list of strings denoted in the `enum` property. Usage of `enum` also instructs any UI that collects a value for this parameter to render a dropdown select input rather than a free-form text input. */\n  enum?: string[];\n  /** Defines labels that correspond to the `enum` values. */\n  choices?: EnumChoice<string>[];\n  /** Define accepted format of the string */\n  format?: \"url\" | \"email\";\n}\n\ninterface IntegerParameterDefinition extends BaseParameterDefinition<number> {\n  type: typeof SchemaTypes.integer;\n  /** Absolute minimum acceptable value for the integer */\n  minimum?: number;\n  /** Absolute maximum acceptable value for the integer */\n  maximum?: number;\n  /** Constrain the available integer options to just the list of integers denoted in the `enum` property. Usage of `enum` also instructs any UI that collects a value for this parameter to render a dropdown select input rather than a free-form text input. */\n  enum?: number[];\n  /** Defines labels that correspond to the `enum` values. */\n  choices?: EnumChoice<number>[];\n}\n\ninterface NumberParameterDefinition extends BaseParameterDefinition<number> {\n  type: typeof SchemaTypes.number;\n  /** Absolute minimum acceptable value for the number */\n  minimum?: number;\n  /** Absolute maximum acceptable value for the number */\n  maximum?: number;\n  /** Constrain the available number options to just the list of numbers denoted in the `enum` property. Usage of `enum` also instructs any UI that collects a value for this parameter to render a dropdown select input rather than a free-form text input. */\n  enum?: number[];\n  /** Defines labels that correspond to the `enum` values. */\n  choices?: EnumChoice<number>[];\n}\n\ninterface OAuth2ParameterDefinition extends BaseParameterDefinition<string> {\n  type: typeof SlackPrimitiveTypes.oauth2;\n  /** Specifies the oauth2 provider this input is associated to */\n  oauth2_provider_key: string;\n  /** Dictates whether only the auth of the user running the workflow should be used. Defaults to `false`. */\n  require_end_user_auth?: boolean;\n}\n\ntype EnumChoice<T> = {\n  /** The `enum` value this {@link EnumChoice} corresponds to. */\n  value: T;\n  /** The label to display for this {@link EnumChoice}. */\n  title: string;\n  /** An optional description for this {@link EnumChoice}. Intended for potential future use in a possible app type schemas page. */\n  description?: string;\n};\n\ninterface UntypedArrayParameterDefinition\n  extends BaseParameterDefinition<ArrayValue> {\n  type: typeof SchemaTypes.array;\n  /** Minimum number of items comprising the array */\n  minItems?: number;\n  /** Maximum number of items comprising the array */\n  maxItems?: number;\n}\n\nexport interface TypedArrayParameterDefinition\n  extends UntypedArrayParameterDefinition {\n  /** Defines the type of the items contained within the array parameter. */\n  items: ParameterDefinition;\n}\n\ntype AllValues = AllPrimitiveValues | ObjectValue | ArrayValue;\n\ntype AllPrimitiveValues = string | number | boolean;\n\ntype ObjectValue = {\n  [key: string]: AllPrimitiveValues | AllPrimitiveValues[];\n};\n\ntype ArrayValue = AllPrimitiveValues[];\n"
  },
  {
    "path": "src/parameters/mod.ts",
    "content": "// import SchemaTypes from \"../schema/schema_types.ts\";\nimport type {\n  ObjectParameterVariableType,\n  ParameterVariableType,\n  SingleParameterVariable,\n  UntypedObjectParameterVariableType,\n} from \"./types.ts\";\nimport type {\n  ParameterDefinition,\n  TypedArrayParameterDefinition,\n  TypedObjectParameter,\n  TypedObjectParameterDefinition,\n  TypedObjectProperties,\n  TypedObjectRequiredProperties,\n} from \"./definition_types.ts\";\nimport { ParamReference } from \"./param.ts\";\nimport { WithUntypedObjectProxy } from \"./with-untyped-object-proxy.ts\";\nimport SchemaTypes from \"../schema/schema_types.ts\";\nimport { isCustomType } from \"../types/mod.ts\";\n\n// Helpers that use type predicate for narrowing down to a Typed Object or Array\nexport const isTypedObject = (\n  def: ParameterDefinition,\n): def is TypedObjectParameter => (\"properties\" in def);\nexport const isTypedArray = (\n  def: ParameterDefinition,\n): def is TypedArrayParameterDefinition => (\"items\" in def);\n\nexport const ParameterVariable = <P extends ParameterDefinition>(\n  namespace: string,\n  paramName: string,\n  definition: P,\n): ParameterVariableType<P> => {\n  let param: ParameterVariableType<P> | null = null;\n\n  if (isCustomType(definition.type)) {\n    return ParameterVariable(\n      namespace,\n      paramName,\n      definition.type.definition,\n    );\n  } else if (definition.type === SchemaTypes.object) {\n    if (isTypedObject(definition)) {\n      param = CreateTypedObjectParameterVariable(\n        namespace,\n        paramName,\n        definition,\n      ) as ParameterVariableType<P>;\n    } else {\n      param = CreateUntypedObjectParameterVariable(namespace, paramName);\n    }\n  } else {\n    param = CreateSingleParameterVariable(\n      namespace,\n      paramName,\n    ) as ParameterVariableType<P>;\n  }\n  return param as ParameterVariableType<P>;\n};\n\nconst CreateTypedObjectParameterVariable = <\n  Props extends TypedObjectProperties,\n  RequiredProps extends TypedObjectRequiredProperties<Props>,\n  P extends TypedObjectParameterDefinition<\n    Props,\n    RequiredProps\n  >,\n>(\n  namespace: string,\n  paramName: string,\n  definition: P,\n): ObjectParameterVariableType<P> => {\n  const ns = namespace ? `${namespace}.` : \"\";\n  const pathReference = `${ns}${paramName}`;\n  const param = ParamReference(pathReference);\n\n  for (\n    const [propName, propDefinition] of Object.entries(\n      definition.properties || {},\n    )\n  ) {\n    param[propName as string] = ParameterVariable(\n      pathReference,\n      propName,\n      propDefinition,\n    );\n  }\n\n  // We wrap the typed object parameter w/ an untyped proxy to allow indexing into additional properties\n  return WithUntypedObjectProxy(\n    param,\n    namespace,\n    paramName,\n  ) as ObjectParameterVariableType<P>;\n};\n\nexport const CreateUntypedObjectParameterVariable = (\n  namespace: string,\n  paramName: string,\n): UntypedObjectParameterVariableType => {\n  return WithUntypedObjectProxy(\n    {},\n    namespace,\n    paramName,\n  ) as UntypedObjectParameterVariableType;\n};\n\nconst CreateSingleParameterVariable = (\n  namespace: string,\n  paramName: string,\n): SingleParameterVariable => {\n  return ParamReference(namespace, paramName) as SingleParameterVariable;\n};\n"
  },
  {
    "path": "src/parameters/param.ts",
    "content": "// deno-lint-ignore no-explicit-any\nexport const ParamReference = (...path: (string | undefined)[]): any => {\n  const fullPath = path.filter(Boolean).join(\".\");\n\n  return {\n    toString: () => `{{${fullPath}}}`,\n    toJSON: () => `{{${fullPath}}}`,\n  };\n};\n"
  },
  {
    "path": "src/parameters/param_test.ts",
    "content": "import { assertEquals } from \"../dev_deps.ts\";\nimport { ParamReference } from \"./param.ts\";\n\nDeno.test(ParamReference.name, async (t) => {\n  await t.step(\"should return a . separated string reference value\", () => {\n    const actual = ParamReference(\"hello\", \"world\");\n\n    assertEquals(actual.toString(), \"{{hello.world}}\");\n    assertEquals(actual.toJSON(), \"{{hello.world}}\");\n  });\n\n  await t.step(\n    \"should filter undefined values from string reference\",\n    () => {\n      const actual = ParamReference(\"hello\", undefined, \"world\");\n\n      assertEquals(actual.toString(), \"{{hello.world}}\");\n      assertEquals(actual.toJSON(), \"{{hello.world}}\");\n    },\n  );\n});\n"
  },
  {
    "path": "src/parameters/parameter-variable_test.ts",
    "content": "import SchemaTypes from \"../schema/schema_types.ts\";\nimport { ParameterVariable } from \"./mod.ts\";\nimport type { SingleParameterVariable } from \"./types.ts\";\nimport {\n  assert,\n  assertStrictEquals,\n  type IsAny,\n  type IsExact,\n} from \"../dev_deps.ts\";\nimport type { CannotBeUndefined } from \"../test_utils.ts\";\n/**\n * ParameterVariable-wrapped parameters should yield particular types\n */\nDeno.test(\"ParameterVariable of type string yields a SingleParameterVariable type that coerces into a string containing the provided parameter name\", () => {\n  const param = ParameterVariable(\"\", \"incident_name\", {\n    type: SchemaTypes.string,\n  });\n  assertStrictEquals(`${param}`, \"{{incident_name}}\");\n});\n\nDeno.test(\"ParameterVariable untyped object should yield a parameter of type any\", () => {\n  const param = ParameterVariable(\"\", \"incident\", {\n    type: SchemaTypes.object,\n  });\n\n  assert<IsAny<typeof param>>(true);\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  assertStrictEquals(`${param.name.foo.bar}`, \"{{incident.name.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable array should yield SingleParameterVariable type\", () => {\n  const param = ParameterVariable(\"\", \"myArray\", {\n    type: SchemaTypes.array,\n    items: {\n      type: SchemaTypes.string,\n    },\n  });\n  assert<IsExact<typeof param, SingleParameterVariable>>(true);\n\n  assertStrictEquals(`${param}`, \"{{myArray}}\");\n  assertStrictEquals(`${param}`, \"{{myArray}}\");\n});\n\nDeno.test(\"ParameterVariable unwrapped typed object with all optional properties should never yield object with potentially undefined properties\", () => {\n  const obj = {\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [],\n  };\n  const param = ParameterVariable(\"\", \"incident\", obj);\n\n  assert<CannotBeUndefined<typeof param.id>>(true);\n  assert<CannotBeUndefined<typeof param.name>>(true);\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n});\n\nDeno.test(\"ParameterVariable unwrapped typed object with all required properties should yield object with properties that cannot be undefined\", () => {\n  const obj = {\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"id\", \"name\"],\n  };\n\n  const param = ParameterVariable(\"\", \"incident\", obj);\n  assert<CannotBeUndefined<typeof param.id>>(true);\n  assert<CannotBeUndefined<typeof param.name>>(true);\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n});\n\nDeno.test(\"ParameterVariable unwrapped typed object with mix of optional and required properties should yield object with no undefined properties\", () => {\n  const obj = {\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"id\"],\n  };\n  const param = ParameterVariable(\"\", \"incident\", obj);\n\n  assert<CannotBeUndefined<typeof param.id>>(true);\n  assert<CannotBeUndefined<typeof param.name>>(true);\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n});\n\nDeno.test(\"ParameterVariable unwrapped typed object with all optional properties and undefined additionalProperties allows access to additional properties\", () => {\n  const param = ParameterVariable(\"\", \"incident\", {\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [],\n  });\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable unwrapped typed object with all required properties and undefined additionalProperties allows access to additional properties\", () => {\n  const param = ParameterVariable(\"\", \"incident\", {\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"id\", \"name\"],\n  });\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable unwrapped typed object with mix of required and optional properties and undefined additionalProperties allows access to additional properties\", () => {\n  const param = ParameterVariable(\"\", \"incident\", {\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"id\"],\n  });\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable unwrapped typed object with all optional properties and additionalProperties=true allows access to additional properties\", () => {\n  const param = ParameterVariable(\"\", \"incident\", {\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [],\n    additionalProperties: true,\n  });\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable unwrapped typed object with all required properties and additionalProperties=true allows access to additional properties\", () => {\n  const param = ParameterVariable(\"\", \"incident\", {\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"id\", \"name\"],\n    additionalProperties: true,\n  });\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable unwrapped typed object with mix of required and optional properties and additionalProperties=true allows access to additional properties\", () => {\n  const param = ParameterVariable(\"\", \"incident\", {\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"id\"],\n    additionalProperties: true,\n  });\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable unwrapped typed object with all optional properties and additionalProperties=false prevents access to additional properties\", () => {\n  const param = ParameterVariable(\"\", \"incident\", {\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [],\n    additionalProperties: false,\n  });\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  //@ts-expect-error foo doesn't exist\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable unwrapped typed object with all required properties and additionalProperties=false prevents access to additional properties\", () => {\n  const param = ParameterVariable(\"\", \"incident\", {\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"id\", \"name\"],\n    additionalProperties: false,\n  });\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  //@ts-expect-error foo doesn't exist\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable unwrapped typed object with mix of required and optional properties and additionalProperties=false prevents access to additional properties\", () => {\n  const param = ParameterVariable(\"\", \"incident\", {\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"id\"],\n    additionalProperties: false,\n  });\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  //@ts-expect-error foo doesn't exist\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n"
  },
  {
    "path": "src/parameters/types.ts",
    "content": "import type {\n  IncreaseDepth,\n  MaxRecursionDepth,\n  RecursionDepthLevel,\n} from \"../type_utils.ts\";\nimport type {\n  CustomTypeParameterDefinition,\n  ParameterDefinition,\n  TypedObjectParameter,\n  TypedObjectProperties,\n  UntypedObjectParameterDefinition,\n} from \"./definition_types.ts\";\n\n// Used for defining a set of input or output parameters\nexport type ParameterSetDefinition = {\n  [key: string]: ParameterDefinition;\n};\n\nexport type PossibleParameterKeys<\n  ParameterSetInternal extends ParameterSetDefinition,\n> = (keyof ParameterSetInternal)[];\n\nexport type ParameterPropertiesDefinition<\n  Params extends ParameterSetDefinition,\n  Required extends PossibleParameterKeys<Params>,\n> = {\n  properties: Params;\n  required: Required;\n};\n\nexport type ParameterVariableType<\n  Def extends ParameterDefinition,\n  CurrentDepth extends RecursionDepthLevel = 0,\n> = CurrentDepth extends MaxRecursionDepth ? UntypedObjectParameterVariableType // i.e. any\n  : Def extends CustomTypeParameterDefinition // If the ParameterVariable is a Custom type, use it's definition instead\n    ? ParameterVariableType<\n      Def[\"type\"][\"definition\"],\n      IncreaseDepth<CurrentDepth>\n    >\n  // If the ParameterVariable is of type object, allow access to the object's properties\n  : Def extends TypedObjectParameter ? ObjectParameterVariableType<Def>\n  : Def extends UntypedObjectParameterDefinition\n    ? UntypedObjectParameterVariableType\n  : SingleParameterVariable;\n\n// deno-lint-ignore ban-types\nexport type SingleParameterVariable = {};\n\n// deno-lint-ignore no-explicit-any\nexport type UntypedObjectParameterVariableType = any;\n\nexport type ObjectParameterPropertyTypes<\n  Props extends TypedObjectProperties,\n> = {\n  [name in keyof Props]: ParameterVariableType<\n    Props[name]\n  >;\n};\n\n// If additionalProperties is set to true, allow access to any key.\n// Otherwise, only allow keys provided through use of properties\nexport type ObjectParameterVariableType<\n  Def extends TypedObjectParameter,\n> =\n  & ObjectParameterPropertyTypes<Def[\"properties\"]>\n  & (Def[\"additionalProperties\"] extends false ? Record<never, never>\n    : {\n      // deno-lint-ignore no-explicit-any\n      [key: string]: any;\n    });\n"
  },
  {
    "path": "src/parameters/with-untyped-object-proxy.ts",
    "content": "import { ParamReference } from \"./param.ts\";\n\nexport const WithUntypedObjectProxy = (\n  // deno-lint-ignore no-explicit-any\n  rootObject: Record<string, any>,\n  ...path: (string | undefined)[]\n  // deno-lint-ignore no-explicit-any\n): any => {\n  const parameterizedObject = {\n    ...rootObject,\n    ...ParamReference(...path),\n  };\n\n  const proxy = new Proxy(parameterizedObject, {\n    get: function (obj, prop) {\n      // If it's a property that exists, just access it directly\n      if (prop in obj) {\n        // deno-lint-ignore no-explicit-any\n        return Reflect.get.apply(obj, arguments as any);\n      }\n\n      // We're attempting to access a property that doesn't exist, so create a new nested proxy\n      if (typeof prop === \"string\") {\n        return WithUntypedObjectProxy(obj, ...path, prop);\n      }\n\n      // Fallback to trying to access it directly even if it's not in this objects props\n      // deno-lint-ignore no-explicit-any\n      return Reflect.get.apply(obj, arguments as any);\n    },\n  });\n\n  return proxy;\n};\n"
  },
  {
    "path": "src/parameters/with-untyped-object-proxy_test.ts",
    "content": "import { WithUntypedObjectProxy } from \"./with-untyped-object-proxy.ts\";\nimport { assertStrictEquals } from \"../dev_deps.ts\";\n\nDeno.test(\"WithUntypedObjectProxy\", () => {\n  const ctx = WithUntypedObjectProxy({});\n\n  assertStrictEquals(`${ctx.foo}`, \"{{foo}}\");\n  assertStrictEquals(`${ctx.foo.baz}`, \"{{foo.baz}}\");\n  assertStrictEquals(\n    `${ctx.foo.baz.biz.buzz.wut.wut.hi.bye}`,\n    \"{{foo.baz.biz.buzz.wut.wut.hi.bye}}\",\n  );\n  assertStrictEquals(`Some text ${ctx.variable}`, \"Some text {{variable}}\");\n});\n\nDeno.test(\"WithUntypedObjectProxy with namespace\", () => {\n  const ctx = WithUntypedObjectProxy({}, \"metadata\");\n\n  assertStrictEquals(`${ctx.foo}`, \"{{metadata.foo}}\");\n  assertStrictEquals(`${ctx.foo.baz}`, \"{{metadata.foo.baz}}\");\n  assertStrictEquals(\n    `${ctx.foo.baz.biz.buzz.wut.wut.hi.bye}`,\n    \"{{metadata.foo.baz.biz.buzz.wut.wut.hi.bye}}\",\n  );\n  assertStrictEquals(\n    `Some text ${ctx.variable}`,\n    \"Some text {{metadata.variable}}\",\n  );\n});\n"
  },
  {
    "path": "src/providers/oauth2/mod.ts",
    "content": "import type {\n  OAuth2ProviderDefinitionArgs,\n  OAuth2ProviderOptions,\n} from \"./types.ts\";\n\nimport type { OAuth2ProviderTypeValues } from \"../../schema/providers/oauth2/types.ts\";\nimport type { ManifestOAuth2ProviderSchema } from \"../../manifest/manifest_schema.ts\";\n\nexport const DefineOAuth2Provider = (\n  definition: OAuth2ProviderDefinitionArgs,\n): OAuth2Provider => {\n  return new OAuth2Provider(definition);\n};\n\nexport class OAuth2Provider {\n  public id: string;\n  private provider_type: OAuth2ProviderTypeValues;\n  private options: OAuth2ProviderOptions;\n\n  constructor(\n    public definition: OAuth2ProviderDefinitionArgs,\n  ) {\n    this.id = definition.provider_key;\n    this.provider_type = definition.provider_type;\n    this.options = definition.options;\n  }\n\n  export(): ManifestOAuth2ProviderSchema {\n    return {\n      provider_type: this.provider_type,\n      options: this.options,\n    };\n  }\n}\n"
  },
  {
    "path": "src/providers/oauth2/oauth2_test.ts",
    "content": "import type { SlackManifestType } from \"../../manifest/types.ts\";\nimport { Manifest, SlackManifest } from \"../../manifest/mod.ts\";\nimport { DefineOAuth2Provider, Schema } from \"../../mod.ts\";\nimport {\n  assertEquals,\n  assertInstanceOf,\n  AssertionError,\n  assertStrictEquals,\n  assertStringIncludes,\n  fail,\n} from \"../../dev_deps.ts\";\nimport { DuplicateProviderKeyError } from \"../../manifest/errors.ts\";\n\nDeno.test(\"SlackManifest() oauth2 throws error when Providers with duplicate provider_keys are added\", () => {\n  const provider1 = DefineOAuth2Provider({\n    provider_key: \"test\",\n    provider_type: Schema.providers.oauth2.CUSTOM,\n    options: {\n      \"client_id\": \"123.456\",\n      \"scope\": [\"scope_a\"],\n    },\n  });\n\n  const provider2 = DefineOAuth2Provider({\n    provider_key: \"test\",\n    provider_type: Schema.providers.oauth2.CUSTOM,\n    options: {\n      \"client_id\": \"123.456\",\n      \"scope\": [\"scope_a\"],\n    },\n  });\n\n  try {\n    Manifest({\n      name: \"Name\",\n      description: \"Description\",\n      botScopes: [],\n      icon: \"icon.png\",\n      externalAuthProviders: [provider1, provider2],\n    });\n    fail(\"Manifest() should have thrown an error\");\n  } catch (error) {\n    if (error instanceof AssertionError) throw error;\n    assertInstanceOf(error, DuplicateProviderKeyError);\n    assertStringIncludes(error.message, \"OAuth2Provider\");\n  }\n});\n\nDeno.test(\"SlackManifest() oauth2 providers get set properly\", () => {\n  const providerKey = \"test_provider\";\n\n  const Provider = DefineOAuth2Provider({\n    provider_key: providerKey,\n    provider_type: Schema.providers.oauth2.CUSTOM,\n    options: {\n      \"client_id\": \"123.456\",\n      \"scope\": [\"scope_a\", \"scope_b\"],\n    },\n  });\n\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    botScopes: [],\n    externalAuthProviders: [Provider],\n  };\n\n  const Manifest = new SlackManifest(definition);\n  const exportedManifest = Manifest.export();\n  assertEquals(definition.externalAuthProviders, [Provider]);\n  assertEquals(exportedManifest.external_auth_providers, {\n    \"oauth2\": { \"test_provider\": Provider.export() },\n  });\n});\n\nDeno.test(\"SlackManifest() oauth2 providers get set properly with use_pkce\", () => {\n  const providerKey1 = \"test_provider_with_with_pkce_true\";\n  const providerKey2 = \"test_provider_with_with_pkce_false\";\n  const providerKey3 = \"test_provider_with_with_pkce_unset\";\n\n  const Provider1 = DefineOAuth2Provider({\n    provider_key: providerKey1,\n    provider_type: Schema.providers.oauth2.CUSTOM,\n    options: {\n      \"client_id\": \"123.456\",\n      \"scope\": [\"scope_a\", \"scope_b\"],\n      \"use_pkce\": true,\n    },\n  });\n\n  const Provider2 = DefineOAuth2Provider({\n    provider_key: providerKey2,\n    provider_type: Schema.providers.oauth2.CUSTOM,\n    options: {\n      \"client_id\": \"123.456\",\n      \"scope\": [\"scope_a\", \"scope_b\"],\n      \"use_pkce\": false,\n    },\n  });\n\n  const Provider3 = DefineOAuth2Provider({\n    provider_key: providerKey3,\n    provider_type: Schema.providers.oauth2.CUSTOM,\n    options: {\n      \"client_id\": \"123.456\",\n      \"scope\": [\"scope_a\", \"scope_b\"],\n    },\n  });\n\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    botScopes: [],\n    externalAuthProviders: [Provider1, Provider2, Provider3],\n  };\n  assertEquals(definition.externalAuthProviders, [\n    Provider1,\n    Provider2,\n    Provider3,\n  ]);\n  const Manifest = new SlackManifest(definition);\n  const exportedManifest = Manifest.export();\n\n  assertEquals(exportedManifest.external_auth_providers, {\n    \"oauth2\": {\n      \"test_provider_with_with_pkce_true\": Provider1.export(),\n      \"test_provider_with_with_pkce_false\": Provider2.export(),\n      \"test_provider_with_with_pkce_unset\": Provider3.export(),\n    },\n  });\n  assertStrictEquals(\n    exportedManifest.external_auth_providers?.oauth2\n      ?.test_provider_with_with_pkce_true?.options?.use_pkce,\n    true,\n  );\n  assertStrictEquals(\n    exportedManifest.external_auth_providers?.oauth2\n      ?.test_provider_with_with_pkce_false?.options?.use_pkce,\n    false,\n  );\n  assertStrictEquals(\n    exportedManifest.external_auth_providers?.oauth2\n      ?.test_provider_with_with_pkce_unset?.options?.use_pkce,\n    undefined,\n  );\n});\n\nDeno.test(\"SlackManifest() oauth2 providers are undefined when not configured\", () => {\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    botScopes: [],\n  };\n\n  const Manifest = new SlackManifest(definition);\n\n  const exportedManifest = Manifest.export();\n\n  assertEquals(definition.externalAuthProviders, undefined);\n  assertEquals(exportedManifest.external_auth_providers, undefined);\n});\n\nDeno.test(\"SlackManifest() oauth2 providers are undefined when set to the empty array\", () => {\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    botScopes: [],\n    externalAuthProviders: [],\n  };\n\n  const Manifest = new SlackManifest(definition);\n\n  const exportedManifest = Manifest.export();\n\n  assertEquals(definition.externalAuthProviders, []);\n  assertEquals(exportedManifest.external_auth_providers, undefined);\n});\n\nDeno.test(\"SlackManifest() oauth2 providers get set properly with token_url_config\", () => {\n  // test with token_url_config unset\n  const providerKey1 = \"test_provider_with_token_url_config_unset\";\n  const Provider1 = DefineOAuth2Provider({\n    provider_key: providerKey1,\n    provider_type: Schema.providers.oauth2.CUSTOM,\n    options: {\n      \"client_id\": \"123.456\",\n      \"scope\": [\"scope_a\", \"scope_b\"],\n      \"token_url_config\": {},\n    },\n  });\n  // test with use_basic_auth_scheme false\n  const providerKey2 = \"test_provider_with_use_basic_auth_scheme_false\";\n  const Provider2 = DefineOAuth2Provider({\n    provider_key: providerKey2,\n    provider_type: Schema.providers.oauth2.CUSTOM,\n    options: {\n      \"client_id\": \"123.456\",\n      \"scope\": [\"scope_a\", \"scope_b\"],\n      \"token_url_config\": {\n        \"use_basic_auth_scheme\": false,\n      },\n    },\n  });\n  // test with use_basic_auth_scheme true\n  const providerKey3 = \"test_provider_with_use_basic_auth_scheme_true\";\n  const Provider3 = DefineOAuth2Provider({\n    provider_key: providerKey3,\n    provider_type: Schema.providers.oauth2.CUSTOM,\n    options: {\n      \"client_id\": \"123.456\",\n      \"scope\": [\"scope_a\", \"scope_b\"],\n      \"token_url_config\": {\n        \"use_basic_auth_scheme\": true,\n      },\n    },\n  });\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    botScopes: [],\n    externalAuthProviders: [Provider1, Provider2, Provider3],\n  };\n  assertEquals(definition.externalAuthProviders, [\n    Provider1,\n    Provider2,\n    Provider3,\n  ]);\n  const Manifest = new SlackManifest(definition);\n  const exportedManifest = Manifest.export();\n\n  assertEquals(exportedManifest.external_auth_providers, {\n    \"oauth2\": {\n      \"test_provider_with_token_url_config_unset\": Provider1.export(),\n      \"test_provider_with_use_basic_auth_scheme_false\": Provider2\n        .export(),\n      \"test_provider_with_use_basic_auth_scheme_true\": Provider3\n        .export(),\n    },\n  });\n  // test with use_basic_auth_scheme unset\n  assertStrictEquals(\n    exportedManifest.external_auth_providers?.oauth2\n      ?.test_provider_with_token_url_config_unset?.options\n      ?.token_url_config?.use_basic_auth_scheme,\n    undefined,\n  );\n  // test with use_basic_auth_scheme false\n  assertStrictEquals(\n    exportedManifest.external_auth_providers?.oauth2\n      ?.test_provider_with_use_basic_auth_scheme_false?.options\n      ?.token_url_config?.use_basic_auth_scheme,\n    false,\n  );\n  // test with use_basic_auth_scheme true\n  assertStrictEquals(\n    exportedManifest.external_auth_providers?.oauth2\n      ?.test_provider_with_use_basic_auth_scheme_true?.options\n      ?.token_url_config?.use_basic_auth_scheme,\n    true,\n  );\n});\n\nDeno.test(\"SlackManifest() oauth2 providers get set properly with identity_config\", () => {\n  //test with identity_config containing required fields\n  const providerKey1 =\n    \"test_provider_with_identity_config_required_fields_set1\";\n  const Provider1 = DefineOAuth2Provider({\n    provider_key: providerKey1,\n    provider_type: Schema.providers.oauth2.CUSTOM,\n    options: {\n      \"client_id\": \"123.456\",\n      \"scope\": [\"scope_a\", \"scope_b\"],\n      \"identity_config\": {\n        \"url\": \"https://example.com\",\n        \"account_identifier\": \"account_identifier_string\",\n      },\n    },\n  });\n\n  //test with identity_config containing all fields with POST method\n  const providerKey2 =\n    \"test_provider_with_identity_config_with_all_fields_set2\";\n  const Provider2 = DefineOAuth2Provider({\n    provider_key: providerKey2,\n    provider_type: Schema.providers.oauth2.CUSTOM,\n    options: {\n      \"client_id\": \"123.456\",\n      \"scope\": [\"scope_a\", \"scope_b\"],\n      \"identity_config\": {\n        \"url\": \"https://example.com\",\n        \"account_identifier\": \"account_identifier_string\",\n        \"headers\": { \"key1\": \"header_1\", \"key2\": \"header_2\" },\n        \"body\": { \"param1\": \"body_1\", \"param2\": \"body_2\" },\n        \"http_method_type\": \"POST\",\n      },\n    },\n  });\n\n  // test with identity_config containing all fields with GET method\n  const providerKey3 =\n    \"test_provider_with_identity_config_with_all_fields_set3\";\n  const Provider3 = DefineOAuth2Provider({\n    provider_key: providerKey3,\n    provider_type: Schema.providers.oauth2.CUSTOM,\n    options: {\n      \"client_id\": \"123.456\",\n      \"scope\": [\"scope_a\", \"scope_b\"],\n      \"identity_config\": {\n        \"url\": \"https://example.com\",\n        \"account_identifier\": \"account_identifier_string\",\n        \"headers\": { \"key1\": \"header_1\", \"key2\": \"header_2\" },\n        \"body\": { \"param1\": \"body_1\", \"param2\": \"body_2\" },\n        \"http_method_type\": \"GET\",\n      },\n    },\n  });\n  const definition: SlackManifestType = {\n    name: \"Name\",\n    description: \"Description\",\n    icon: \"icon.png\",\n    botScopes: [],\n    externalAuthProviders: [Provider1, Provider2, Provider3],\n  };\n  assertEquals(definition.externalAuthProviders, [\n    Provider1,\n    Provider2,\n    Provider3,\n  ]);\n  const Manifest = new SlackManifest(definition);\n  const exportedManifest = Manifest.export();\n\n  assertEquals(exportedManifest.external_auth_providers, {\n    \"oauth2\": {\n      \"test_provider_with_identity_config_required_fields_set1\": Provider1\n        .export(),\n      \"test_provider_with_identity_config_with_all_fields_set2\": Provider2\n        .export(),\n      \"test_provider_with_identity_config_with_all_fields_set3\": Provider3\n        .export(),\n    },\n  });\n  //test with identity_config containing required fields\n  assertEquals(\n    exportedManifest.external_auth_providers?.oauth2\n      ?.test_provider_with_identity_config_required_fields_set1?.options\n      ?.identity_config,\n    {\n      \"url\": \"https://example.com\",\n      \"account_identifier\": \"account_identifier_string\",\n    },\n  );\n  //test with identity_config containing all fields with POST method\n  assertEquals(\n    exportedManifest.external_auth_providers?.oauth2\n      ?.test_provider_with_identity_config_with_all_fields_set2?.options\n      ?.identity_config,\n    {\n      \"url\": \"https://example.com\",\n      \"account_identifier\": \"account_identifier_string\",\n      \"headers\": { \"key1\": \"header_1\", \"key2\": \"header_2\" },\n      \"body\": { \"param1\": \"body_1\", \"param2\": \"body_2\" },\n      \"http_method_type\": \"POST\",\n    },\n  );\n  //test with identity_config containing all fields with GET metthod\n  assertEquals(\n    exportedManifest.external_auth_providers?.oauth2\n      ?.test_provider_with_identity_config_with_all_fields_set3?.options\n      ?.identity_config,\n    {\n      \"url\": \"https://example.com\",\n      \"account_identifier\": \"account_identifier_string\",\n      \"headers\": { \"key1\": \"header_1\", \"key2\": \"header_2\" },\n      \"body\": { \"param1\": \"body_1\", \"param2\": \"body_2\" },\n      \"http_method_type\": \"GET\",\n    },\n  );\n});\n"
  },
  {
    "path": "src/providers/oauth2/types.ts",
    "content": "import type {\n  OAuth2ProviderTypeValues,\n} from \"../../schema/providers/oauth2/types.ts\";\n\n/** Http Method types that are currently supported by identity config */\nexport type IdentityUrlHttpMethodTypes = \"GET\" | \"POST\";\nexport type OAuth2ProviderIdentitySchema = {\n  /** url that is used to identify the authed user */\n  \"url\": string;\n  /** A field name that is returned in response to invoking the identity config url that\n   * can be used as the account identifier of the authed user */\n  \"account_identifier\": string;\n  /** Extra headers that the identity url might expect.\n   * Note: It adds `Authorization` header automatically so no need to specify this. */\n  \"headers\"?: {\n    [key: string]: string;\n  };\n  /** static body parameters that the identity url expects. This is nullable since only POST methods use this */\n  \"body\"?: {\n    [key: string]: string;\n  };\n  /** method type of the identity url configured above. By default it is considered GET. */\n  \"http_method_type\"?: IdentityUrlHttpMethodTypes;\n};\n\nexport type tokenUrlConfigSchema = {\n  /** Default value is false */\n  \"use_basic_auth_scheme\"?: boolean;\n};\n\nexport type OAuth2ProviderOptions = {\n  /** Client id for your provider */\n  \"client_id\": string;\n  /** Scopes for your provider */\n  \"scope\": string[];\n  /** Display name for your provider. Required for CUSTOM provider types. */\n  \"provider_name\"?: string;\n  /** Authorization url for your provider. Required for CUSTOM provider types. */\n  \"authorization_url\"?: string;\n  /** Token url for your provider. Required for CUSTOM provider types. */\n  \"token_url\"?: string;\n  /** Optional configs for token url. Required for CUSTOM provider types. */\n  \"token_url_config\"?: tokenUrlConfigSchema;\n  /** Identity configuration for your provider. Required for CUSTOM provider types.\n   * If token_url_config is not present, use_basic_auth_scheme value is false by default. */\n  \"identity_config\"?: OAuth2ProviderIdentitySchema;\n  /** Optional extras dict for authorization url for your provider. Required for CUSTOM provider types. */\n  \"authorization_url_extras\"?: { [key: string]: string };\n  /** Optional boolean flag to specify if the provider uses PKCE. by default it is considered false. Required for CUSTOM provider types. */\n  \"use_pkce\"?: boolean;\n};\n\nexport type OAuth2ProviderDefinitionArgs = {\n  /** A unique name for your provider */\n  provider_key: string;\n  /** Type of your provider */\n  provider_type: OAuth2ProviderTypeValues;\n  /** OAuth2 Configuration options for your provider */\n  options: OAuth2ProviderOptions;\n};\n"
  },
  {
    "path": "src/schema/mod.ts",
    "content": "import SchemaTypes from \"./schema_types.ts\";\nimport SlackSchema from \"./slack/mod.ts\";\nimport Providers from \"./providers/mod.ts\";\n\nconst Schema = {\n  // Contains primitive types\n  types: SchemaTypes,\n  // Contains slack-specific schema types\n  slack: SlackSchema,\n  providers: Providers,\n} as const;\n\nexport default Schema;\n"
  },
  {
    "path": "src/schema/providers/mod.ts",
    "content": "import OAuth2Types from \"./oauth2/mod.ts\";\n\nconst Schema = {\n  oauth2: OAuth2Types,\n} as const;\n\nexport default Schema;\n"
  },
  {
    "path": "src/schema/providers/oauth2/mod.ts",
    "content": "const ProviderTypes = {\n  CUSTOM: \"CUSTOM\",\n} as const;\n\nexport default ProviderTypes;\n"
  },
  {
    "path": "src/schema/providers/oauth2/types.ts",
    "content": "import type OAuth2ProviderTypes from \"./mod.ts\";\n\nexport type OAuth2ProviderTypeValues =\n  typeof OAuth2ProviderTypes[keyof typeof OAuth2ProviderTypes];\n"
  },
  {
    "path": "src/schema/schema_types.ts",
    "content": "const SchemaTypes = {\n  string: \"string\",\n  boolean: \"boolean\",\n  integer: \"integer\",\n  number: \"number\",\n  object: \"object\",\n  array: \"array\",\n} as const;\n\nexport type ValidSchemaTypes = typeof SchemaTypes[keyof typeof SchemaTypes];\n\nexport default SchemaTypes;\n"
  },
  {
    "path": "src/schema/slack/functions/_scripts/.gitignore",
    "content": "functions.json"
  },
  {
    "path": "src/schema/slack/functions/_scripts/README.md",
    "content": "# Generating Slack function source files\n\nThis script will generate the necessary function TypeScript files along with\ntheir tests in the `schema/slack/functions` directory, i.e.\n`schema/slack/functions/send_message.ts` and\n`schema/slack/functions/send_message_test.ts`. It will also update the\n`schema/slack/functions/mod.ts` file to import/export all of the defined\nfunctions. It will also remove outdated function TypeScript files but not their\ncorresponding test, the tests must be removed manually.\n\n## Instructions\n\n1. First, you'll need to grab the response from `functions.list` API method\n   tester:\n\n- Choose a session token from a public production enterprise grid workspace that\n  is NOT enrolled in any beta toggles. Recommend using the Slack DevRel\n  production enterprise grid token.\n- Use `builtins` as the value for the `function_type` parameter to this API.\n- Copy the response into a `functions.json` file in this directory.\n\n2. With this `_scripts` directory as your working directory, run the generate\n   script:\n\n   ```sh\n   > ./generate\n   ```\n\nIf it completes without any linter errors, you should be good to go, with new,\nformatted and linted TypeScript files for all of the Slack functions included in\nyour `functions.json` payload. If there are any unexpected linting issues, you\nmay need to go into those files and manually resolve any problems.\n"
  },
  {
    "path": "src/schema/slack/functions/_scripts/generate",
    "content": "#!/bin/bash\nset -euo pipefail\ncd \"$(dirname \"$0\")\"\n\n# Clean parent directory of all files ending in .ts\necho \"Cleaning folder directory\"\nls -1 -d $PWD/../* | { grep \"\\.ts$\" || :; } | while read -r filename ; do\n    rm \"$filename\"\ndone\n\n# Temporaraly generate a mod.ts file to prevent circular imports\ncat > $PWD/../mod.ts <<EOL\nconst SlackFunctions = {};\nexport default SlackFunctions;\nEOL\n\n# Writes the function & test files based on a functions.json file existing alongside this script\ndeno run --quiet --allow-read --allow-write ./src/write_function_files.ts\n\necho \"Formatting Slack function files...\"\ndeno fmt --quiet ../*.ts\necho \"Linting Slack function files...\"\ndeno lint --quiet ../*.ts\necho \"Type-checking Slack function files...\"\ndeno check --quiet ../*.ts\n"
  },
  {
    "path": "src/schema/slack/functions/_scripts/src/templates/mod.ts",
    "content": "export { SlackFunctionTemplate } from \"./template_function.ts\";\nexport { SlackTestFunctionTemplate } from \"./test_template.ts\";\nexport { SlackFunctionModTemplate } from \"./template_mod.ts\";\n"
  },
  {
    "path": "src/schema/slack/functions/_scripts/src/templates/template_function.ts",
    "content": "import { isCustomType } from \"../../../../../../types/mod.ts\";\nimport SchemaTypes from \"../../../../../schema_types.ts\";\nimport SlackSchemaTypes from \"../../../../schema_types.ts\";\nimport { InternalSlackTypes } from \"../../../../types/custom/mod.ts\";\nimport type {\n  FunctionParameter,\n  FunctionProperties,\n  FunctionProperty,\n  FunctionRecord,\n} from \"../types.ts\";\nimport { isArrayFunctionProperty, isObjectFunctionProperty } from \"../utils.ts\";\nimport {\n  autogeneratedComment,\n  getSlackCallbackId,\n  renderTypeImports,\n  sanitize,\n} from \"./utils.ts\";\nimport type { AllowedTypeValue, AllowedTypeValueObject } from \"./types.ts\";\n\ntype AllowedHiddenParamsMap = Record<\n  string,\n  Record<\"input\" | \"output\", string[]>\n>;\n// Oops we accidentally exposed hidden parameters. That's ok, we'll keep them public for now.\nexport const allowedHiddenParams: AllowedHiddenParamsMap = {\n  \"open_form\": {\n    input: [],\n    output: [\"interactivity\"],\n  },\n  \"reply_in_thread\": {\n    input: [\"files\"],\n    output: [\"action\", \"interactivity\"],\n  },\n  \"send_dm\": {\n    input: [\"files\"],\n    output: [\n      \"action\",\n      \"interactivity\",\n      \"timestamp_started\",\n      \"timestamp_completed\",\n    ],\n  },\n  \"send_message\": {\n    input: [\"files\"],\n    output: [\n      \"action\",\n      \"interactivity\",\n      \"timestamp_started\",\n      \"timestamp_completed\",\n    ],\n  },\n};\n\nconst typeMap: Record<string, AllowedTypeValueObject> = {\n  SchemaTypes,\n  SlackTypes: SlackSchemaTypes,\n  InternalSlackTypes,\n};\n\nconst schemaTypeMap = Object.entries(typeMap).reduce<Record<string, string>>(\n  (acc, [schemaKey, schemaTypes]) => {\n    for (const schemaType in schemaTypes) {\n      const value: AllowedTypeValue = schemaTypes[schemaType];\n      const type: string = isCustomType(value) ? value.id : value;\n      acc[type] = `${schemaKey}.${schemaType}`;\n    }\n    return acc;\n  },\n  {},\n);\n\nconst propertyToTypeScript = (\n  property: FunctionProperty,\n): string => {\n  const typescript = [];\n  const sdkType = schemaTypeMap[property.type];\n  if (!sdkType) {\n    throw new Error(\n      `Unrecognized type \"${property.type}\"! Maybe a new automation platform type was recently introduced? If so, add it to one of the type files under src/schema.`,\n    );\n  }\n  typescript.push(\n    `type: ${sdkType}`,\n  );\n  if (property.description) {\n    typescript.push(`description: ${sanitize(property.description)}`);\n  }\n  if (property.title) {\n    typescript.push(`title: ${sanitize(property.title)}`);\n  }\n  if (isArrayFunctionProperty(property)) {\n    typescript.push(`items: ${propertyToTypeScript(property.items)}`);\n  }\n  if (isObjectFunctionProperty(property)) {\n    typescript.push(\n      `properties: ${propertiesToTypeScript(property.properties)}`,\n    );\n    typescript.push(\n      `additionalProperties: ${property.additionalProperties ?? true}`,\n    );\n    typescript.push(`required: ${JSON.stringify(property.required ?? [])}`);\n  }\n  return `{${typescript.join(\",\\n\")}}`;\n};\n\nconst propertiesToTypeScript = (\n  properties: FunctionProperties,\n) => {\n  const typescript: string[] = [];\n  Object.entries(properties).forEach(([propertyKey, property]) => {\n    typescript.push(\n      `${propertyKey}: ${propertyToTypeScript(property)}`,\n    );\n  });\n  return `{${typescript.join(\",\\n\")}}`;\n};\n\nconst manifestParametersToTypeScript = (\n  allowedHiddenParams: string[],\n  functionParameters: FunctionParameter[],\n) => {\n  const typescript: string[] = [];\n  typescript.push(\n    `properties: {${\n      functionParameters.filter((p) =>\n        allowedHiddenParams.includes(p.name) || !p.is_hidden\n      ).map((parameter) =>\n        `${parameter.name}: ${propertyToTypeScript(parameter)}`\n      ).join(\",\\n\")\n    }}`,\n  );\n  typescript.push(`required: ${\n    JSON.stringify(\n      functionParameters.filter((p) => p.is_required).map(\n        (p) => p.name,\n      ),\n    )\n  }`);\n  return `{${typescript.join(\",\\n\")}}`;\n};\n\nexport function manifestFunctionFieldsToTypeScript(\n  allowedParamsMap: AllowedHiddenParamsMap,\n  functionRecord: FunctionRecord,\n) {\n  const typescript: string[] = [];\n  typescript.push(`source_file: \"\"`);\n  if (functionRecord.title) {\n    typescript.push(\n      `title: ${sanitize(functionRecord.title)}`,\n    );\n  }\n  if (functionRecord.description) {\n    typescript.push(\n      `description: ${sanitize(functionRecord.description)}`,\n    );\n  }\n  const allowedHiddenParams = allowedParamsMap[functionRecord.callback_id] ||\n    { input: [], output: [] };\n  typescript.push(\n    `input_parameters: ${\n      manifestParametersToTypeScript(\n        allowedHiddenParams.input,\n        functionRecord.input_parameters,\n      )\n    }`,\n  );\n  typescript.push(\n    `output_parameters: ${\n      manifestParametersToTypeScript(\n        allowedHiddenParams.output,\n        functionRecord.output_parameters,\n      )\n    }`,\n  );\n  return typescript.join(\",\\n\");\n}\n\nconst defineFunctionInputToTypeScript = (\n  functionRecord: FunctionRecord,\n) => {\n  const typescript: string[] = [];\n  typescript.push(\n    `callback_id: ${sanitize(getSlackCallbackId(functionRecord))}`,\n  );\n  typescript.push(\n    manifestFunctionFieldsToTypeScript(allowedHiddenParams, functionRecord),\n  );\n  return `{${typescript.join(\",\\n\")}}`;\n};\n\nexport function SlackFunctionTemplate(\n  functionRecord: FunctionRecord,\n): string {\n  const typescript: string[] = [];\n  typescript.push(autogeneratedComment());\n  typescript.push(\n    `import { DefineFunction } from \"../../../functions/mod.ts\";`,\n  );\n  typescript.push(renderTypeImports(functionRecord));\n  typescript.push(\"\");\n  typescript.push(\n    `export default DefineFunction(${\n      defineFunctionInputToTypeScript(functionRecord)\n    });`,\n  );\n\n  return typescript.join(\"\\n\");\n}\n\nexport default SlackFunctionTemplate;\n"
  },
  {
    "path": "src/schema/slack/functions/_scripts/src/templates/template_mod.ts",
    "content": "import {\n  autogeneratedComment,\n  getFunctionName,\n  renderFunctionImport,\n} from \"./utils.ts\";\nimport type { FunctionRecord } from \"../types.ts\";\n\nexport const SlackFunctionModTemplate = (\n  functionRecords: FunctionRecord[],\n) => {\n  const callbackIds = functionRecords.map((dfi) => dfi.callback_id);\n  const typescript: string[] = [];\n  typescript.push(autogeneratedComment(true));\n  callbackIds.forEach((callbackId) => {\n    typescript.push(renderFunctionImport(callbackId));\n  });\n\n  typescript.push(\"\");\n  typescript.push(\n    `const SlackFunctions = {${\n      functionRecords.map((dfi) => `${getFunctionName(dfi.callback_id)}`)\n        .join(\",\")\n    }} as const;`,\n  );\n  typescript.push(\"\");\n  typescript.push(`export default SlackFunctions;`);\n\n  return typescript.join(\"\\n\");\n};\n\nexport default SlackFunctionModTemplate;\n"
  },
  {
    "path": "src/schema/slack/functions/_scripts/src/templates/test_template.ts",
    "content": "import {\n  autogeneratedComment,\n  getFunctionName,\n  getSlackCallbackId,\n  renderFunctionImport,\n  renderTypeImports,\n} from \"./utils.ts\";\nimport type { FunctionParameter, FunctionRecord } from \"../types.ts\";\nimport {\n  allowedHiddenParams,\n  manifestFunctionFieldsToTypeScript,\n} from \"./template_function.ts\";\n\nexport const manifestFunctionToTypeScript = (\n  functionRecord: FunctionRecord,\n) => {\n  return `{${\n    manifestFunctionFieldsToTypeScript(allowedHiddenParams, functionRecord)\n  }}`;\n};\n\nconst renderFunctionManifestTest = (\n  functionRecord: FunctionRecord,\n) => {\n  const functionName = getFunctionName(functionRecord.callback_id);\n  const typescript: string[] = [];\n  typescript.push(\n    `assertEquals(${functionName}.definition.callback_id, \"${\n      getSlackCallbackId(functionRecord)\n    }\");`,\n  );\n  typescript.push(\n    `const expected: ManifestFunctionSchema = ${\n      manifestFunctionToTypeScript(\n        functionRecord,\n      )\n    };`,\n  );\n  typescript.push(`const actual = ${functionName}.export();`);\n  typescript.push(\"\");\n  typescript.push(`assertNotStrictEquals(actual, expected);`);\n  return `() => {${typescript.join(\"\\n\")}}`;\n};\n\nconst workflowToTypeScript = (functionName: string) => {\n  const typescript: string[] = [];\n  typescript.push(`callback_id: \"test_${functionName}_slack_function\"`);\n  typescript.push(`title: \"Test ${functionName}\"`);\n  typescript.push(\n    `description: \"This is a generated test to test ${functionName}\"`,\n  );\n  return `{${typescript.join(\", \\n\")}}`;\n};\n\nconst requiredParametersToTypeScript = (\n  parameters: FunctionParameter[],\n) => {\n  const typescript: string[] = [];\n  parameters.forEach((parameter: FunctionParameter) => {\n    if (parameter.is_required) {\n      typescript.push(`${parameter.name}: \"test\"`);\n    }\n  });\n  return `{${typescript.join(\",\\n\")}}`;\n};\n\nconst renderWorkflowStepTest = (functionRecord: FunctionRecord) => {\n  const functionName = getFunctionName(functionRecord.callback_id);\n  const inputParameters = requiredParametersToTypeScript(\n    functionRecord.input_parameters,\n  );\n  const typescript: string[] = [];\n  typescript.push(\n    `const testWorkflow = DefineWorkflow(${\n      workflowToTypeScript(functionName)\n    });`,\n  );\n  typescript.push(\n    `testWorkflow.addStep(${functionName}, ${inputParameters});`,\n  );\n  typescript.push(`const actual = testWorkflow.steps[0].export();`);\n  typescript.push(\"\");\n  typescript.push(\n    `assertEquals(actual.function_id, \"${\n      getSlackCallbackId(functionRecord)\n    }\");`,\n  );\n  typescript.push(`assertEquals(actual.inputs, ${inputParameters});`);\n  return `() => {${typescript.join(\"\\n\")}}`;\n};\n\nconst renderOutputExistenceTest = (functionRecord: FunctionRecord) => {\n  const functionName = getFunctionName(functionRecord.callback_id);\n  const typescript: string[] = [];\n  typescript.push(\n    `const testWorkflow = DefineWorkflow(${\n      workflowToTypeScript(functionName)\n    });`,\n  );\n  typescript.push(\n    `const step = testWorkflow.addStep(${functionName}, ${\n      requiredParametersToTypeScript(\n        functionRecord.input_parameters,\n      )\n    });`,\n  );\n  for (const parameter of functionRecord.output_parameters) {\n    typescript.push(\n      `assertExists(step.outputs.${parameter.name});`,\n    );\n  }\n  return `() => {${typescript.join(\"\\n\")}}`;\n};\n\nexport function SlackTestFunctionTemplate(\n  functionRecord: FunctionRecord,\n): string {\n  const functionName = getFunctionName(functionRecord.callback_id);\n  const typescript: string[] = [];\n  typescript.push(autogeneratedComment());\n  typescript.push(\n    `import { assertEquals, assertNotStrictEquals } from \"../../../dev_deps.ts\";`,\n  );\n  typescript.push(\n    `import { DefineWorkflow } from \"../../../workflows/mod.ts\";`,\n  );\n  typescript.push(\n    `import { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";`,\n  );\n  typescript.push(renderTypeImports(functionRecord));\n  typescript.push(renderFunctionImport(functionRecord.callback_id));\n  typescript.push(\"\");\n  typescript.push(\n    `Deno.test(\"${functionName} generates valid FunctionManifest\", ${\n      renderFunctionManifestTest(functionRecord)\n    });`,\n  );\n  typescript.push(\"\");\n  typescript.push(\n    `Deno.test(\"${functionName} can be used as a Slack function in a workflow step\", ${\n      renderWorkflowStepTest(functionRecord)\n    });`,\n  );\n\n  if (!functionRecord.output_parameters.length) {\n    return typescript.join(\"\\n\");\n  }\n\n  typescript[1] =\n    `import { assertEquals, assertNotStrictEquals, assertExists } from \"../../../dev_deps.ts\";`;\n  typescript.push(\"\");\n  typescript.push(\n    `Deno.test(\"All outputs of Slack function ${functionName} should exist\", ${\n      renderOutputExistenceTest(functionRecord)\n    });`,\n  );\n\n  return typescript.join(\"\\n\");\n}\n\nexport default SlackTestFunctionTemplate;\n"
  },
  {
    "path": "src/schema/slack/functions/_scripts/src/templates/types.ts",
    "content": "import type { ICustomType } from \"../../../../../../types/types.ts\";\n\nexport type AllowedTypeValue = ICustomType | string;\nexport type AllowedTypeValueObject = Record<string, AllowedTypeValue>;\n"
  },
  {
    "path": "src/schema/slack/functions/_scripts/src/templates/utils.ts",
    "content": "import { toPascalCase } from \"../../../../../../dev_deps.ts\";\nimport type { FunctionProperty, FunctionRecord } from \"../types.ts\";\nimport SchemaTypes from \"../../../../../schema_types.ts\";\nimport SlackTypes from \"../../../../schema_types.ts\";\nimport { InternalSlackTypes } from \"../../../../types/custom/mod.ts\";\nimport type { AllowedTypeValue, AllowedTypeValueObject } from \"./types.ts\";\nimport { isCustomType } from \"../../../../../../types/mod.ts\";\nimport { isArrayFunctionProperty, isObjectFunctionProperty } from \"../utils.ts\";\n\nexport function autogeneratedComment(includeDate?: boolean): string {\n  const dateString = includeDate ? ` on ${new Date().toDateString()}` : \"\";\n  return `/** This file was autogenerated${dateString}. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/`;\n}\n\nexport function renderFunctionImport(callbackId: string): string {\n  return `import ${getFunctionName(callbackId)} from \"./${callbackId}.ts\";`;\n}\n\nexport function getFunctionName(callbackId: string): string {\n  return toPascalCase(callbackId);\n}\n\nexport function getSlackCallbackId(\n  functionRecord: FunctionRecord,\n): string {\n  return `slack#/functions/${functionRecord.callback_id}`;\n}\n\nexport function getParameterType(type: AllowedTypeValue): string {\n  return isCustomType(type) ? type.id : type;\n}\n\nconst getParameterList = (\n  functionRecord: FunctionRecord,\n): FunctionProperty[] => [\n  ...functionRecord.input_parameters,\n  ...functionRecord.output_parameters,\n];\n\nconst hasTypeObject = (\n  types: string[],\n  typeObject: AllowedTypeValueObject,\n): boolean =>\n  types.some((t) =>\n    Object.values(typeObject).map((val) => getParameterType(val)).includes(t)\n  );\n\nconst extractTypes = (properties: FunctionProperty[]): string[] => {\n  let types: Set<string> = new Set();\n  properties.forEach((property) => {\n    types.add(property.type);\n    if (isArrayFunctionProperty(property)) {\n      types = new Set([\n        ...types,\n        ...extractTypes([property.items]),\n      ]);\n    }\n    if (isObjectFunctionProperty(property)) {\n      types = new Set([\n        ...types,\n        ...extractTypes(Object.values(property.properties)),\n      ]);\n    }\n  });\n  return Array.from(types);\n};\n\nexport function renderTypeImports(functionRecord: FunctionRecord) {\n  const typescript: string[] = [];\n  const functionRecordTypes = extractTypes(getParameterList(functionRecord));\n  if (hasTypeObject(functionRecordTypes, SchemaTypes)) {\n    typescript.push('import SchemaTypes from \"../../schema_types.ts\";');\n  }\n  if (hasTypeObject(functionRecordTypes, SlackTypes)) {\n    typescript.push('import SlackTypes from \"../schema_types.ts\";');\n  }\n  if (hasTypeObject(functionRecordTypes, InternalSlackTypes)) {\n    typescript.push(\n      'import { InternalSlackTypes } from \"../types/custom/mod.ts\";',\n    );\n  }\n  return typescript.join(\"\\n\");\n}\n\nexport function sanitize(value: string): string {\n  return JSON.stringify(value);\n}\n"
  },
  {
    "path": "src/schema/slack/functions/_scripts/src/templates/utils_test.ts",
    "content": "import {\n  autogeneratedComment,\n  getFunctionName,\n  getSlackCallbackId,\n  renderFunctionImport,\n  renderTypeImports,\n  sanitize,\n} from \"./utils.ts\";\nimport {\n  assertEquals,\n  assertStringIncludes,\n} from \"../../../../../../dev_deps.ts\";\nimport type { FunctionRecord } from \"../types.ts\";\nimport SchemaTypes from \"../../../../../schema_types.ts\";\nimport SlackTypes from \"../../../../schema_types.ts\";\nimport { InternalSlackTypes } from \"../../../../types/custom/mod.ts\";\n\nconst DESCRIPTION = \"Test the Slack function template\";\nconst TITLE = \"test function\";\nconst CALLBACK_ID = \"test_function\";\nconst SLACK_FUNCTION_TYPE = \"builtin\";\n\nDeno.test(\"Autogenerated comment should contain readme location\", () => {\n  const actual = autogeneratedComment();\n  assertStringIncludes(actual, \"src/schema/slack/functions/_scripts/README.md\");\n});\n\nDeno.test(\"Autogenerated comment shouldn't mention the date by default\", () => {\n  const actual = autogeneratedComment();\n  assertStringIncludes(actual, \"autogenerated.\");\n});\n\nDeno.test(\"Autogenerated comment can include the date\", () => {\n  const actual = autogeneratedComment(true);\n  assertStringIncludes(actual, \"autogenerated on \");\n});\n\nDeno.test(\"Function name should be pascal case\", () => {\n  const actual = getFunctionName(CALLBACK_ID);\n  assertEquals(actual, \"TestFunction\");\n});\n\nDeno.test(\"Function import should contain file path\", () => {\n  const actual = renderFunctionImport(CALLBACK_ID);\n  assertStringIncludes(actual, `./${CALLBACK_ID}.ts`);\n});\n\nDeno.test(\"getSlackCallbackId should generate the valid slack callback_id\", () => {\n  const actual = `slack#/functions/${CALLBACK_ID}`;\n  const expected = getSlackCallbackId({\n    callback_id: CALLBACK_ID,\n    title: TITLE,\n    description: DESCRIPTION,\n    type: SLACK_FUNCTION_TYPE,\n    input_parameters: [],\n    output_parameters: [],\n  });\n  assertStringIncludes(actual, expected);\n});\n\nDeno.test(\"renderTypeImports should render all imports provided with slack and primitive types\", () => {\n  const dfi: FunctionRecord = {\n    callback_id: CALLBACK_ID,\n    title: TITLE,\n    description: DESCRIPTION,\n    type: SLACK_FUNCTION_TYPE,\n    input_parameters: [\n      {\n        type: SlackTypes.channel_id,\n        name: \"channel_id\",\n        title: \"Select a channel\",\n        is_required: true,\n        description: \"Search all channels\",\n      },\n      {\n        type: InternalSlackTypes.form_input_object.id,\n        name: \"fields\",\n        title: \"fields\",\n        is_required: true,\n        description: \"Input fields to be shown on the form\",\n      },\n    ],\n    output_parameters: [\n      {\n        type: SchemaTypes.string,\n        name: \"message_ts\",\n        title: \"Message time stamp\",\n        description: \"Message time stamp\",\n      },\n    ],\n  };\n  const actual = renderTypeImports(dfi);\n  assertStringIncludes(actual, \"SchemaTypes\");\n  assertStringIncludes(actual, \"SlackTypes\");\n  assertStringIncludes(actual, \"InternalSlackTypes\");\n});\n\nDeno.test(\"renderTypeImports should render imports required for array type\", () => {\n  const dfi: FunctionRecord = {\n    callback_id: CALLBACK_ID,\n    title: TITLE,\n    description: DESCRIPTION,\n    type: SLACK_FUNCTION_TYPE,\n    input_parameters: [],\n    output_parameters: [\n      {\n        type: SchemaTypes.array,\n        name: \"user_ids\",\n        title: \"User Ids\",\n        description: \"User Ids\",\n        items: {\n          type: SlackTypes.channel_id,\n        },\n      },\n    ],\n  };\n  const actual = renderTypeImports(dfi);\n  assertStringIncludes(actual, \"SchemaTypes\");\n  assertStringIncludes(actual, \"SlackTypes\");\n});\n\nDeno.test(\"renderTypeImports should render imports required for object type\", () => {\n  const dfi: FunctionRecord = {\n    callback_id: CALLBACK_ID,\n    title: TITLE,\n    description: DESCRIPTION,\n    type: SLACK_FUNCTION_TYPE,\n    input_parameters: [],\n    output_parameters: [\n      {\n        type: SchemaTypes.object,\n        name: \"user_ids\",\n        title: \"User Ids\",\n        description: \"User Ids\",\n        properties: {\n          my_param: {\n            type: SlackTypes.channel_id,\n          },\n        },\n      },\n    ],\n  };\n  const actual = renderTypeImports(dfi);\n  assertStringIncludes(actual, \"SchemaTypes\");\n  assertStringIncludes(actual, \"SlackTypes\");\n});\n\nDeno.test(\"renderTypeImports should render imports required for a nested complex object type\", () => {\n  const dfi: FunctionRecord = {\n    callback_id: CALLBACK_ID,\n    title: TITLE,\n    description: DESCRIPTION,\n    type: SLACK_FUNCTION_TYPE,\n    input_parameters: [],\n    output_parameters: [\n      {\n        type: SchemaTypes.array,\n        items: {\n          type: SchemaTypes.object,\n          properties: {\n            my_slack_type: {\n              type: InternalSlackTypes.form_input_object.id,\n            },\n            my_primitive_type: {\n              type: SlackTypes.channel_id,\n            },\n          },\n        },\n        name: \"user_ids\",\n      },\n    ],\n  };\n  const actual = renderTypeImports(dfi);\n  assertStringIncludes(actual, \"InternalSlackTypes\");\n  assertStringIncludes(actual, \"SchemaTypes\");\n  assertStringIncludes(actual, \"SlackTypes\");\n});\n\nDeno.test(\"renderTypeImports should render imports required for primitive & complex types\", () => {\n  const dfi: FunctionRecord = {\n    callback_id: CALLBACK_ID,\n    title: TITLE,\n    description: DESCRIPTION,\n    type: SLACK_FUNCTION_TYPE,\n    input_parameters: [],\n    output_parameters: [\n      {\n        type: SchemaTypes.array,\n        items: {\n          type: SchemaTypes.string,\n        },\n        name: \"user_ids\",\n      },\n      {\n        type: SchemaTypes.object,\n        name: \"my_object\",\n        properties: {\n          my_param: {\n            type: SchemaTypes.string,\n          },\n        },\n      },\n      {\n        type: SchemaTypes.string,\n        name: \"my_primitive\",\n      },\n    ],\n  };\n  const actual = renderTypeImports(dfi);\n  assertStringIncludes(actual, \"SchemaTypes\");\n});\n\nDeno.test(`${sanitize.name} should properly escape \\\" characters`, () => {\n  const testText = 'Send an \"only visible to you\" message';\n  const actual = sanitize(testText);\n  assertEquals(actual, '\"Send an \\\\\"only visible to you\\\\\" message\"');\n});\n"
  },
  {
    "path": "src/schema/slack/functions/_scripts/src/test/data/function.json",
    "content": "{\n  \"ok\": true,\n  \"functions\": [\n    {\n      \"id\": \"Fn0102\",\n      \"callback_id\": \"send_message\",\n      \"title\": \"Send a message to channel\",\n      \"description\": \"Send a message to channel\",\n      \"type\": \"builtin\",\n      \"input_parameters\": [\n        {\n          \"type\": \"slack#/types/channel_id\",\n          \"name\": \"channel_id\",\n          \"title\": \"Select a channel\",\n          \"is_required\": true,\n          \"description\": \"Search all channels\"\n        },\n        {\n          \"type\": \"slack#/types/rich_text\",\n          \"name\": \"message\",\n          \"title\": \"Add a message\",\n          \"is_required\": true,\n          \"description\": \"Add a message\"\n        },\n        {\n          \"type\": \"slack#/types/message_ts\",\n          \"name\": \"thread_ts\",\n          \"title\": \"Another message's timestamp value\",\n          \"description\": \"Provide another message's ts value to make this message a reply\"\n        },\n        {\n          \"type\": \"object\",\n          \"name\": \"object\",\n          \"title\": \"Object\",\n          \"description\": \"Object type\",\n          \"required\": [\"event_type\", \"event_payload\"],\n          \"additionalProperties\": true,\n          \"properties\": {\n            \"event_type\": {\n              \"type\": \"string\"\n            },\n            \"event_payload\": {\n              \"type\": \"object\"\n            }\n          }\n        },\n        {\n          \"type\": \"array\",\n          \"name\": \"test_array\",\n          \"title\": \"test array\",\n          \"description\": \"test an array\",\n          \"items\": {\n            \"type\": \"string\"\n          }\n        },\n        {\n          \"type\": \"slack#/types/blocks\",\n          \"name\": \"interactive_blocks\",\n          \"title\": \"Button(s) to send with the message\",\n          \"description\": \"Button(s) to send with the message\"\n        }\n      ],\n      \"output_parameters\": [\n        {\n          \"type\": \"slack#/types/message_ts\",\n          \"name\": \"message_ts\",\n          \"title\": \"Message time stamp\",\n          \"is_required\": true,\n          \"description\": \"Message time stamp\"\n        },\n        {\n          \"type\": \"slack#/types/interactivity\",\n          \"name\": \"interactivity\",\n          \"title\": \"interactivity\",\n          \"description\": \"Interactivity context\",\n          \"is_hidden\": true,\n          \"hello big\": true\n        },\n        {\n          \"type\": \"slack#/types/message_context\",\n          \"name\": \"message_context\",\n          \"title\": \"Reference to the message sent\",\n          \"description\": \"Reference to the message sent\",\n          \"is_required\": true\n        }\n      ]\n    },\n    {\n      \"id\": \"Fn03R943D2UW\",\n      \"callback_id\": \"create_out_of_office_event\",\n      \"title\": \"Create an out-of-office event\",\n      \"description\": \"Create an all day out-of-office event in Google Calendar\",\n      \"type\": \"app\",\n      \"input_parameters\": [\n        {\n          \"type\": \"slack#/types/user_context\",\n          \"name\": \"user\",\n          \"description\": \"Id of slack user connected to calendar\",\n          \"title\": \"Id of slack user connected to calendar\",\n          \"is_required\": true\n        },\n        {\n          \"type\": \"slack#/types/date\",\n          \"name\": \"end_date\",\n          \"description\": \"End date of the event ( inclusive. yyyy-mm-dd )\",\n          \"title\": \"End date\",\n          \"is_required\": true\n        }\n      ],\n      \"output_parameters\": [\n        {\n          \"type\": \"string\",\n          \"name\": \"event_id\",\n          \"description\": \"Event id returned by Google calendar app\",\n          \"title\": \"Event id returned by Google calendar app\",\n          \"is_required\": false\n        }\n      ],\n      \"app_id\": \"ADZ494LHY\",\n      \"date_created\": 1659034796,\n      \"date_updated\": 1670840200,\n      \"date_deleted\": 0\n    }\n  ]\n}\n"
  },
  {
    "path": "src/schema/slack/functions/_scripts/src/types.ts",
    "content": "type BaseFunctionProperty = {\n  type: string;\n  description?: string;\n  title?: string;\n};\n\nexport type ObjectFunctionProperty = BaseFunctionProperty & {\n  properties: FunctionProperties;\n  required?: string[];\n  additionalProperties?: boolean;\n};\n\nexport type ArrayFunctionProperty = BaseFunctionProperty & {\n  items: FunctionProperty;\n};\n\nexport type FunctionProperty =\n  | BaseFunctionProperty\n  | ObjectFunctionProperty\n  | ArrayFunctionProperty;\n\nexport type FunctionProperties = {\n  [key: string]: FunctionProperty;\n};\n\nexport type FunctionParameter = FunctionProperty & {\n  name: string;\n  is_required?: boolean;\n  is_hidden?: boolean;\n};\n\nexport type FunctionRecord = {\n  callback_id: string;\n  title: string;\n  description: string;\n  app_id?: string;\n  input_parameters: FunctionParameter[];\n  output_parameters: FunctionParameter[];\n  type?: string;\n};\n\nexport type FunctionsPayload = {\n  ok: boolean;\n  functions: FunctionRecord[];\n};\n"
  },
  {
    "path": "src/schema/slack/functions/_scripts/src/utils.ts",
    "content": "import type {\n  ArrayFunctionProperty,\n  FunctionProperty,\n  FunctionRecord,\n  FunctionsPayload,\n  ObjectFunctionProperty,\n} from \"./types.ts\";\n\nconst FUNCTIONS_JSON_PATH = \"functions.json\";\n\nconst green = \"\\x1b[92m\";\nconst yellow = \"\\x1b[38;5;214m\";\nconst red = \"\\x1b[91m\";\nconst reset = \"\\x1b[0m\";\n\nexport const greenText = (text: string) => green + text + reset;\nexport const yellowText = (text: string) => yellow + text + reset;\nexport const redText = (text: string) => red + text + reset;\n\n// TODO: once List steps work in code, bring this back\nconst FUNCTIONS_TO_IGNORE = [\n  \"update_list_record\",\n  \"share_list_users\",\n  \"lists_activity_feed\",\n  \"list_add_record\",\n  \"delete_list_record\",\n  \"copy_list\",\n];\n\nexport async function getSlackFunctions(\n  functionsPayloadPath: string = FUNCTIONS_JSON_PATH,\n): Promise<FunctionRecord[]> {\n  const functionsPayload: FunctionsPayload = await Deno.readTextFile(\n    functionsPayloadPath,\n  ).then(JSON.parse);\n\n  return functionsPayload.functions.filter((fn) =>\n    fn.type == \"builtin\" && !FUNCTIONS_TO_IGNORE.includes(fn.callback_id)\n  );\n}\n\nexport function isObjectFunctionProperty(\n  property: FunctionProperty,\n): property is ObjectFunctionProperty {\n  return \"properties\" in property;\n}\n\nexport function isArrayFunctionProperty(\n  property: FunctionProperty,\n): property is ArrayFunctionProperty {\n  return \"items\" in property;\n}\n"
  },
  {
    "path": "src/schema/slack/functions/_scripts/src/utils_test.ts",
    "content": "import {\n  getSlackFunctions,\n  greenText,\n  isArrayFunctionProperty,\n  isObjectFunctionProperty,\n  redText,\n  yellowText,\n} from \"./utils.ts\";\nimport { assert, assertEquals, type IsExact } from \"../../../../../dev_deps.ts\";\nimport type {\n  ArrayFunctionProperty,\n  FunctionProperty,\n  ObjectFunctionProperty,\n} from \"./types.ts\";\n\nDeno.test(\"colored text remain consistent\", () => {\n  assertEquals(\"\\x1b[92mtest\\x1b[0m\", greenText(\"test\"));\n  assertEquals(\"\\x1b[91mtest\\x1b[0m\", redText(\"test\"));\n  assertEquals(\"\\x1b[38;5;214mtest\\x1b[0m\", yellowText(\"test\"));\n});\n\nDeno.test(\"Non Slack functions should be filtered\", async () => {\n  const actual = await getSlackFunctions(\n    \"src/schema/slack/functions/_scripts/src/test/data/function.json\",\n  );\n  assertEquals(actual.length, 1);\n});\n\nDeno.test(\"isObjectFunctionProperty distinguishes ObjectFunctionProperty from FunctionProperty\", () => {\n  const property: FunctionProperty = {\n    type: \"object\",\n    properties: {\n      myString: {\n        type: \"string\",\n        description: \"test description\",\n        title: \"String property\",\n      },\n    },\n    required: [],\n    additionalProperties: true,\n  };\n  assert<IsExact<ObjectFunctionProperty, typeof property>>(true);\n  assertEquals(true, isObjectFunctionProperty(property));\n});\n\nDeno.test(\"isArrayFunctionProperty distinguishes ArrayFunctionProperty from FunctionProperty\", () => {\n  const property: FunctionProperty = {\n    type: \"array\",\n    description: \"test description\",\n    title: \"ArrayFunctionProperty\",\n    items: {\n      type: \"string\",\n    },\n  };\n  assert<IsExact<ArrayFunctionProperty, typeof property>>(true);\n  assertEquals(true, isArrayFunctionProperty(property));\n});\n"
  },
  {
    "path": "src/schema/slack/functions/_scripts/src/write_function_files.ts",
    "content": "import {\n  SlackFunctionModTemplate,\n  SlackFunctionTemplate,\n  SlackTestFunctionTemplate,\n} from \"./templates/mod.ts\";\nimport { getSlackFunctions, greenText, redText } from \"./utils.ts\";\nimport type { FunctionRecord } from \"./types.ts\";\n\nconst VALID_FILENAME_REGEX = /^[0-9a-zA-Z_\\-]+$/;\n\nasync function main() {\n  const slackFunctions: FunctionRecord[] = await _internals.getSlackFunctions();\n\n  // Sorting alphabetically cause only a monster would generate these in a random order\n  slackFunctions.sort((a, b) => a.callback_id.localeCompare(b.callback_id));\n\n  await Promise.all(\n    slackFunctions.map(async (functionRecord: FunctionRecord) => {\n      console.log(\n        `Generating code & tests for Slack function: ${\n          greenText(functionRecord.callback_id)\n        }`,\n      );\n      if (!VALID_FILENAME_REGEX.test(functionRecord.callback_id)) {\n        console.log(\n          `${redText(\"FAILURE:\")} Invalid characters in callback_id: ${\n            redText(functionRecord.callback_id)\n          }`,\n        );\n        return;\n      }\n      const filename = `../${functionRecord.callback_id}.ts`;\n      const testFilename = `../${functionRecord.callback_id}_test.ts`;\n\n      const templateString = _internals.SlackFunctionTemplate(functionRecord);\n      const templateTestString = _internals.SlackTestFunctionTemplate(\n        functionRecord,\n      );\n\n      await _internals.writeTextFile(filename, templateString);\n      await _internals.writeTextFile(testFilename, templateTestString);\n    }),\n  );\n\n  console.log(\n    `Generated ${slackFunctions.length} Slack functions with their unit tests`,\n  );\n\n  const modString = _internals.SlackFunctionModTemplate(slackFunctions);\n\n  await _internals.writeTextFile(\"../mod.ts\", modString);\n  console.log(\"Updated functions module export\");\n}\n\nexport const _internals = {\n  main,\n  getSlackFunctions,\n  SlackFunctionModTemplate,\n  SlackFunctionTemplate,\n  SlackTestFunctionTemplate,\n  writeTextFile: (\n    path: string,\n    data: string,\n    options?: Deno.WriteFileOptions,\n  ): ReturnType<typeof Deno.writeTextFile> => {\n    return Deno.writeTextFile(path, data, options);\n  },\n};\n\nif (import.meta.main) {\n  _internals.main();\n}\n"
  },
  {
    "path": "src/schema/slack/functions/add_bookmark.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/add_bookmark\",\n  source_file: \"\",\n  title: \"Add a bookmark\",\n  input_parameters: {\n    properties: {\n      channel_id: {\n        type: SlackTypes.channel_id,\n        description: \"Search all channels\",\n        title: \"Select a channel\",\n      },\n      name: {\n        type: SchemaTypes.string,\n        description: \"Enter the bookmark name\",\n        title: \"Bookmark name\",\n      },\n      link: {\n        type: SchemaTypes.string,\n        description: \"https://docs.acme.com\",\n        title: \"Bookmark Link\",\n      },\n    },\n    required: [\"channel_id\", \"name\", \"link\"],\n  },\n  output_parameters: {\n    properties: {\n      channel_id: {\n        type: SlackTypes.channel_id,\n        description: \"Channel\",\n        title: \"Channel\",\n      },\n      bookmark_name: {\n        type: SchemaTypes.string,\n        description: \"Bookmark name\",\n        title: \"Bookmark name\",\n      },\n      bookmark_link: {\n        type: SchemaTypes.string,\n        description: \"Bookmark link\",\n        title: \"Bookmark link\",\n      },\n    },\n    required: [\"channel_id\", \"bookmark_name\", \"bookmark_link\"],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/add_bookmark_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport AddBookmark from \"./add_bookmark.ts\";\n\nDeno.test(\"AddBookmark generates valid FunctionManifest\", () => {\n  assertEquals(\n    AddBookmark.definition.callback_id,\n    \"slack#/functions/add_bookmark\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Add a bookmark\",\n    input_parameters: {\n      properties: {\n        channel_id: {\n          type: SlackTypes.channel_id,\n          description: \"Search all channels\",\n          title: \"Select a channel\",\n        },\n        name: {\n          type: SchemaTypes.string,\n          description: \"Enter the bookmark name\",\n          title: \"Bookmark name\",\n        },\n        link: {\n          type: SchemaTypes.string,\n          description: \"https://docs.acme.com\",\n          title: \"Bookmark Link\",\n        },\n      },\n      required: [\"channel_id\", \"name\", \"link\"],\n    },\n    output_parameters: {\n      properties: {\n        channel_id: {\n          type: SlackTypes.channel_id,\n          description: \"Channel\",\n          title: \"Channel\",\n        },\n        bookmark_name: {\n          type: SchemaTypes.string,\n          description: \"Bookmark name\",\n          title: \"Bookmark name\",\n        },\n        bookmark_link: {\n          type: SchemaTypes.string,\n          description: \"Bookmark link\",\n          title: \"Bookmark link\",\n        },\n      },\n      required: [\"channel_id\", \"bookmark_name\", \"bookmark_link\"],\n    },\n  };\n  const actual = AddBookmark.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"AddBookmark can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_AddBookmark_slack_function\",\n    title: \"Test AddBookmark\",\n    description: \"This is a generated test to test AddBookmark\",\n  });\n  testWorkflow.addStep(AddBookmark, {\n    channel_id: \"test\",\n    name: \"test\",\n    link: \"test\",\n  });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/add_bookmark\");\n  assertEquals(actual.inputs, {\n    channel_id: \"test\",\n    name: \"test\",\n    link: \"test\",\n  });\n});\n\nDeno.test(\"All outputs of Slack function AddBookmark should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_AddBookmark_slack_function\",\n    title: \"Test AddBookmark\",\n    description: \"This is a generated test to test AddBookmark\",\n  });\n  const step = testWorkflow.addStep(AddBookmark, {\n    channel_id: \"test\",\n    name: \"test\",\n    link: \"test\",\n  });\n  assertExists(step.outputs.channel_id);\n  assertExists(step.outputs.bookmark_name);\n  assertExists(step.outputs.bookmark_link);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/add_pin.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/add_pin\",\n  source_file: \"\",\n  title: \"Pin a message\",\n  input_parameters: {\n    properties: {\n      channel_id: {\n        type: SlackTypes.channel_id,\n        description: \"Search all channels\",\n        title: \"Select a channel\",\n      },\n      message: {\n        type: SchemaTypes.string,\n        description: \"Enter a message link or message timestamp\",\n        title: \"Message link or message timestamp\",\n      },\n    },\n    required: [\"channel_id\", \"message\"],\n  },\n  output_parameters: { properties: {}, required: [] },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/add_pin_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { assertEquals, assertNotStrictEquals } from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport AddPin from \"./add_pin.ts\";\n\nDeno.test(\"AddPin generates valid FunctionManifest\", () => {\n  assertEquals(AddPin.definition.callback_id, \"slack#/functions/add_pin\");\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Pin a message\",\n    input_parameters: {\n      properties: {\n        channel_id: {\n          type: SlackTypes.channel_id,\n          description: \"Search all channels\",\n          title: \"Select a channel\",\n        },\n        message: {\n          type: SchemaTypes.string,\n          description: \"Enter a message link or message timestamp\",\n          title: \"Message link or message timestamp\",\n        },\n      },\n      required: [\"channel_id\", \"message\"],\n    },\n    output_parameters: { properties: {}, required: [] },\n  };\n  const actual = AddPin.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"AddPin can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_AddPin_slack_function\",\n    title: \"Test AddPin\",\n    description: \"This is a generated test to test AddPin\",\n  });\n  testWorkflow.addStep(AddPin, { channel_id: \"test\", message: \"test\" });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/add_pin\");\n  assertEquals(actual.inputs, { channel_id: \"test\", message: \"test\" });\n});\n"
  },
  {
    "path": "src/schema/slack/functions/add_reaction.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/add_reaction\",\n  source_file: \"\",\n  title: \"Add a reaction to a message\",\n  input_parameters: {\n    properties: {\n      message_context: {\n        type: SlackTypes.message_context,\n        description: \"Select a message to react to\",\n        title: \"Select a message to react to\",\n      },\n      emoji: {\n        type: SchemaTypes.string,\n        description: \"Reaction (emoji) name\",\n        title: \"Emoji\",\n      },\n    },\n    required: [\"message_context\", \"emoji\"],\n  },\n  output_parameters: { properties: {}, required: [] },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/add_reaction_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { assertEquals, assertNotStrictEquals } from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport AddReaction from \"./add_reaction.ts\";\n\nDeno.test(\"AddReaction generates valid FunctionManifest\", () => {\n  assertEquals(\n    AddReaction.definition.callback_id,\n    \"slack#/functions/add_reaction\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Add a reaction to a message\",\n    input_parameters: {\n      properties: {\n        message_context: {\n          type: SlackTypes.message_context,\n          description: \"Select a message to react to\",\n          title: \"Select a message to react to\",\n        },\n        emoji: {\n          type: SchemaTypes.string,\n          description: \"Reaction (emoji) name\",\n          title: \"Emoji\",\n        },\n      },\n      required: [\"message_context\", \"emoji\"],\n    },\n    output_parameters: { properties: {}, required: [] },\n  };\n  const actual = AddReaction.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"AddReaction can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_AddReaction_slack_function\",\n    title: \"Test AddReaction\",\n    description: \"This is a generated test to test AddReaction\",\n  });\n  testWorkflow.addStep(AddReaction, { message_context: \"test\", emoji: \"test\" });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/add_reaction\");\n  assertEquals(actual.inputs, { message_context: \"test\", emoji: \"test\" });\n});\n"
  },
  {
    "path": "src/schema/slack/functions/add_user_to_usergroup.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/add_user_to_usergroup\",\n  source_file: \"\",\n  title: \"Add people to a user group\",\n  description: \"Additional permissions might be required\",\n  input_parameters: {\n    properties: {\n      usergroup_id: {\n        type: SlackTypes.usergroup_id,\n        description: \"Search all user groups\",\n        title: \"Select a user group\",\n      },\n      user_ids: {\n        type: SchemaTypes.array,\n        description: \"Search all people\",\n        title: \"Select member(s)\",\n        items: { type: SlackTypes.user_id },\n      },\n    },\n    required: [\"usergroup_id\", \"user_ids\"],\n  },\n  output_parameters: {\n    properties: {\n      usergroup_id: {\n        type: SlackTypes.usergroup_id,\n        description: \"User group\",\n        title: \"User group\",\n      },\n    },\n    required: [\"usergroup_id\"],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/add_user_to_usergroup_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport AddUserToUsergroup from \"./add_user_to_usergroup.ts\";\n\nDeno.test(\"AddUserToUsergroup generates valid FunctionManifest\", () => {\n  assertEquals(\n    AddUserToUsergroup.definition.callback_id,\n    \"slack#/functions/add_user_to_usergroup\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Add people to a user group\",\n    description: \"Additional permissions might be required\",\n    input_parameters: {\n      properties: {\n        usergroup_id: {\n          type: SlackTypes.usergroup_id,\n          description: \"Search all user groups\",\n          title: \"Select a user group\",\n        },\n        user_ids: {\n          type: SchemaTypes.array,\n          description: \"Search all people\",\n          title: \"Select member(s)\",\n          items: { type: SlackTypes.user_id },\n        },\n      },\n      required: [\"usergroup_id\", \"user_ids\"],\n    },\n    output_parameters: {\n      properties: {\n        usergroup_id: {\n          type: SlackTypes.usergroup_id,\n          description: \"User group\",\n          title: \"User group\",\n        },\n      },\n      required: [\"usergroup_id\"],\n    },\n  };\n  const actual = AddUserToUsergroup.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"AddUserToUsergroup can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_AddUserToUsergroup_slack_function\",\n    title: \"Test AddUserToUsergroup\",\n    description: \"This is a generated test to test AddUserToUsergroup\",\n  });\n  testWorkflow.addStep(AddUserToUsergroup, {\n    usergroup_id: \"test\",\n    user_ids: \"test\",\n  });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/add_user_to_usergroup\");\n  assertEquals(actual.inputs, { usergroup_id: \"test\", user_ids: \"test\" });\n});\n\nDeno.test(\"All outputs of Slack function AddUserToUsergroup should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_AddUserToUsergroup_slack_function\",\n    title: \"Test AddUserToUsergroup\",\n    description: \"This is a generated test to test AddUserToUsergroup\",\n  });\n  const step = testWorkflow.addStep(AddUserToUsergroup, {\n    usergroup_id: \"test\",\n    user_ids: \"test\",\n  });\n  assertExists(step.outputs.usergroup_id);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/archive_channel.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/archive_channel\",\n  source_file: \"\",\n  title: \"Archive a channel\",\n  input_parameters: {\n    properties: {\n      channel_id: {\n        type: SlackTypes.channel_id,\n        description: \"Search all channels\",\n        title: \"Select a channel\",\n      },\n    },\n    required: [\"channel_id\"],\n  },\n  output_parameters: {\n    properties: {\n      channel_id: {\n        type: SlackTypes.channel_id,\n        description: \"Channel name\",\n        title: \"Channel name\",\n      },\n    },\n    required: [\"channel_id\"],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/archive_channel_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport ArchiveChannel from \"./archive_channel.ts\";\n\nDeno.test(\"ArchiveChannel generates valid FunctionManifest\", () => {\n  assertEquals(\n    ArchiveChannel.definition.callback_id,\n    \"slack#/functions/archive_channel\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Archive a channel\",\n    input_parameters: {\n      properties: {\n        channel_id: {\n          type: SlackTypes.channel_id,\n          description: \"Search all channels\",\n          title: \"Select a channel\",\n        },\n      },\n      required: [\"channel_id\"],\n    },\n    output_parameters: {\n      properties: {\n        channel_id: {\n          type: SlackTypes.channel_id,\n          description: \"Channel name\",\n          title: \"Channel name\",\n        },\n      },\n      required: [\"channel_id\"],\n    },\n  };\n  const actual = ArchiveChannel.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"ArchiveChannel can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_ArchiveChannel_slack_function\",\n    title: \"Test ArchiveChannel\",\n    description: \"This is a generated test to test ArchiveChannel\",\n  });\n  testWorkflow.addStep(ArchiveChannel, { channel_id: \"test\" });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/archive_channel\");\n  assertEquals(actual.inputs, { channel_id: \"test\" });\n});\n\nDeno.test(\"All outputs of Slack function ArchiveChannel should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_ArchiveChannel_slack_function\",\n    title: \"Test ArchiveChannel\",\n    description: \"This is a generated test to test ArchiveChannel\",\n  });\n  const step = testWorkflow.addStep(ArchiveChannel, { channel_id: \"test\" });\n  assertExists(step.outputs.channel_id);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/canvas_copy.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/canvas_copy\",\n  source_file: \"\",\n  title: \"Copy a canvas\",\n  input_parameters: {\n    properties: {\n      canvas_id: {\n        type: SlackTypes.canvas_id,\n        description: \"Search all canvases\",\n        title: \"Select a canvas\",\n      },\n      title: {\n        type: SchemaTypes.string,\n        description: \"Enter a canvas name\",\n        title: \"Canvas name\",\n      },\n      owner_id: {\n        type: SlackTypes.user_id,\n        description: \"Person\",\n        title: \"Canvas owner\",\n      },\n      placeholder_values: {\n        type: SchemaTypes.object,\n        description: \"Variables\",\n        title: \"Variables\",\n      },\n    },\n    required: [\"canvas_id\", \"title\", \"owner_id\"],\n  },\n  output_parameters: {\n    properties: {\n      canvas_id: {\n        type: SlackTypes.canvas_id,\n        description: \"Canvas link\",\n        title: \"Canvas link\",\n      },\n    },\n    required: [\"canvas_id\"],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/canvas_copy_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport CanvasCopy from \"./canvas_copy.ts\";\n\nDeno.test(\"CanvasCopy generates valid FunctionManifest\", () => {\n  assertEquals(\n    CanvasCopy.definition.callback_id,\n    \"slack#/functions/canvas_copy\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Copy a canvas\",\n    input_parameters: {\n      properties: {\n        canvas_id: {\n          type: SlackTypes.canvas_id,\n          description: \"Search all canvases\",\n          title: \"Select a canvas\",\n        },\n        title: {\n          type: SchemaTypes.string,\n          description: \"Enter a canvas name\",\n          title: \"Canvas name\",\n        },\n        owner_id: {\n          type: SlackTypes.user_id,\n          description: \"Person\",\n          title: \"Canvas owner\",\n        },\n        placeholder_values: {\n          type: SchemaTypes.object,\n          description: \"Variables\",\n          title: \"Variables\",\n        },\n      },\n      required: [\"canvas_id\", \"title\", \"owner_id\"],\n    },\n    output_parameters: {\n      properties: {\n        canvas_id: {\n          type: SlackTypes.canvas_id,\n          description: \"Canvas link\",\n          title: \"Canvas link\",\n        },\n      },\n      required: [\"canvas_id\"],\n    },\n  };\n  const actual = CanvasCopy.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"CanvasCopy can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_CanvasCopy_slack_function\",\n    title: \"Test CanvasCopy\",\n    description: \"This is a generated test to test CanvasCopy\",\n  });\n  testWorkflow.addStep(CanvasCopy, {\n    canvas_id: \"test\",\n    title: \"test\",\n    owner_id: \"test\",\n  });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/canvas_copy\");\n  assertEquals(actual.inputs, {\n    canvas_id: \"test\",\n    title: \"test\",\n    owner_id: \"test\",\n  });\n});\n\nDeno.test(\"All outputs of Slack function CanvasCopy should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_CanvasCopy_slack_function\",\n    title: \"Test CanvasCopy\",\n    description: \"This is a generated test to test CanvasCopy\",\n  });\n  const step = testWorkflow.addStep(CanvasCopy, {\n    canvas_id: \"test\",\n    title: \"test\",\n    owner_id: \"test\",\n  });\n  assertExists(step.outputs.canvas_id);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/canvas_create.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/canvas_create\",\n  source_file: \"\",\n  title: \"Create a canvas\",\n  input_parameters: {\n    properties: {\n      title: {\n        type: SchemaTypes.string,\n        description: \"Enter a canvas name\",\n        title: \"Canvas name\",\n      },\n      canvas_create_type: {\n        type: SchemaTypes.string,\n        description: \"Type of creation\",\n        title: \"Type of creation\",\n      },\n      canvas_template_id: {\n        type: SlackTypes.canvas_template_id,\n        description: \"Select an option\",\n        title: \"Select a canvas template\",\n      },\n      owner_id: {\n        type: SlackTypes.user_id,\n        description: \"Person\",\n        title: \"Canvas owner\",\n      },\n      content: {\n        type: SlackTypes.expanded_rich_text,\n        description: \"Add content to the canvas\",\n        title: \"Add content\",\n      },\n      placeholder_values: {\n        type: SchemaTypes.object,\n        description: \"Variables\",\n        title: \"Variables\",\n      },\n    },\n    required: [\"title\", \"owner_id\"],\n  },\n  output_parameters: {\n    properties: {\n      canvas_id: {\n        type: SlackTypes.canvas_id,\n        description: \"Canvas link\",\n        title: \"Canvas link\",\n      },\n    },\n    required: [\"canvas_id\"],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/canvas_create_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport CanvasCreate from \"./canvas_create.ts\";\n\nDeno.test(\"CanvasCreate generates valid FunctionManifest\", () => {\n  assertEquals(\n    CanvasCreate.definition.callback_id,\n    \"slack#/functions/canvas_create\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Create a canvas\",\n    input_parameters: {\n      properties: {\n        title: {\n          type: SchemaTypes.string,\n          description: \"Enter a canvas name\",\n          title: \"Canvas name\",\n        },\n        canvas_create_type: {\n          type: SchemaTypes.string,\n          description: \"Type of creation\",\n          title: \"Type of creation\",\n        },\n        canvas_template_id: {\n          type: SlackTypes.canvas_template_id,\n          description: \"Select an option\",\n          title: \"Select a canvas template\",\n        },\n        owner_id: {\n          type: SlackTypes.user_id,\n          description: \"Person\",\n          title: \"Canvas owner\",\n        },\n        content: {\n          type: SlackTypes.expanded_rich_text,\n          description: \"Add content to the canvas\",\n          title: \"Add content\",\n        },\n        placeholder_values: {\n          type: SchemaTypes.object,\n          description: \"Variables\",\n          title: \"Variables\",\n        },\n      },\n      required: [\"title\", \"owner_id\"],\n    },\n    output_parameters: {\n      properties: {\n        canvas_id: {\n          type: SlackTypes.canvas_id,\n          description: \"Canvas link\",\n          title: \"Canvas link\",\n        },\n      },\n      required: [\"canvas_id\"],\n    },\n  };\n  const actual = CanvasCreate.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"CanvasCreate can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_CanvasCreate_slack_function\",\n    title: \"Test CanvasCreate\",\n    description: \"This is a generated test to test CanvasCreate\",\n  });\n  testWorkflow.addStep(CanvasCreate, { title: \"test\", owner_id: \"test\" });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/canvas_create\");\n  assertEquals(actual.inputs, { title: \"test\", owner_id: \"test\" });\n});\n\nDeno.test(\"All outputs of Slack function CanvasCreate should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_CanvasCreate_slack_function\",\n    title: \"Test CanvasCreate\",\n    description: \"This is a generated test to test CanvasCreate\",\n  });\n  const step = testWorkflow.addStep(CanvasCreate, {\n    title: \"test\",\n    owner_id: \"test\",\n  });\n  assertExists(step.outputs.canvas_id);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/canvas_update_content.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/canvas_update_content\",\n  source_file: \"\",\n  title: \"Update a canvas\",\n  input_parameters: {\n    properties: {\n      canvas_update_type: {\n        type: SchemaTypes.string,\n        description: \"Type of update\",\n        title: \"Type of update\",\n      },\n      channel_id: {\n        type: SlackTypes.channel_id,\n        description: \"Channel name\",\n        title: \"Select a channel\",\n      },\n      canvas_id: {\n        type: SlackTypes.canvas_id,\n        description: \"Search standalone canvases\",\n        title: \"Select a canvas\",\n      },\n      section_id: {\n        type: SchemaTypes.string,\n        description: \"Select an option\",\n        title: \"Choose which section to update\",\n      },\n      action: {\n        type: SchemaTypes.string,\n        description: \"Select an option\",\n        title: \"How do you want to update?\",\n      },\n      content: {\n        type: SlackTypes.expanded_rich_text,\n        description: \"Add content to the canvas\",\n        title: \"Content\",\n      },\n    },\n    required: [\"action\", \"content\"],\n  },\n  output_parameters: {\n    properties: {\n      canvas_id: {\n        type: SlackTypes.canvas_id,\n        description: \"Canvas link\",\n        title: \"Canvas link\",\n      },\n    },\n    required: [\"canvas_id\"],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/canvas_update_content_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport CanvasUpdateContent from \"./canvas_update_content.ts\";\n\nDeno.test(\"CanvasUpdateContent generates valid FunctionManifest\", () => {\n  assertEquals(\n    CanvasUpdateContent.definition.callback_id,\n    \"slack#/functions/canvas_update_content\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Update a canvas\",\n    input_parameters: {\n      properties: {\n        canvas_update_type: {\n          type: SchemaTypes.string,\n          description: \"Type of update\",\n          title: \"Type of update\",\n        },\n        channel_id: {\n          type: SlackTypes.channel_id,\n          description: \"Channel name\",\n          title: \"Select a channel\",\n        },\n        canvas_id: {\n          type: SlackTypes.canvas_id,\n          description: \"Search standalone canvases\",\n          title: \"Select a canvas\",\n        },\n        section_id: {\n          type: SchemaTypes.string,\n          description: \"Select an option\",\n          title: \"Choose which section to update\",\n        },\n        action: {\n          type: SchemaTypes.string,\n          description: \"Select an option\",\n          title: \"How do you want to update?\",\n        },\n        content: {\n          type: SlackTypes.expanded_rich_text,\n          description: \"Add content to the canvas\",\n          title: \"Content\",\n        },\n      },\n      required: [\"action\", \"content\"],\n    },\n    output_parameters: {\n      properties: {\n        canvas_id: {\n          type: SlackTypes.canvas_id,\n          description: \"Canvas link\",\n          title: \"Canvas link\",\n        },\n      },\n      required: [\"canvas_id\"],\n    },\n  };\n  const actual = CanvasUpdateContent.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"CanvasUpdateContent can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_CanvasUpdateContent_slack_function\",\n    title: \"Test CanvasUpdateContent\",\n    description: \"This is a generated test to test CanvasUpdateContent\",\n  });\n  testWorkflow.addStep(CanvasUpdateContent, {\n    action: \"test\",\n    content: \"test\",\n  });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/canvas_update_content\");\n  assertEquals(actual.inputs, { action: \"test\", content: \"test\" });\n});\n\nDeno.test(\"All outputs of Slack function CanvasUpdateContent should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_CanvasUpdateContent_slack_function\",\n    title: \"Test CanvasUpdateContent\",\n    description: \"This is a generated test to test CanvasUpdateContent\",\n  });\n  const step = testWorkflow.addStep(CanvasUpdateContent, {\n    action: \"test\",\n    content: \"test\",\n  });\n  assertExists(step.outputs.canvas_id);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/channel_canvas_create.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/channel_canvas_create\",\n  source_file: \"\",\n  title: \"Create channel canvas\",\n  input_parameters: {\n    properties: {\n      channel_id: {\n        type: SlackTypes.channel_id,\n        description: \"Channel name\",\n        title: \"Select a channel\",\n      },\n      canvas_create_type: {\n        type: SchemaTypes.string,\n        description: \"Type of creation\",\n        title: \"Type of creation\",\n      },\n      canvas_template_id: {\n        type: SlackTypes.canvas_template_id,\n        description: \"Select an option\",\n        title: \"Select a canvas template\",\n      },\n      content: {\n        type: SlackTypes.expanded_rich_text,\n        description: \"Add content to the canvas\",\n        title: \"Add content\",\n      },\n      placeholder_values: {\n        type: SchemaTypes.object,\n        description: \"Variables\",\n        title: \"Variables\",\n      },\n    },\n    required: [\"channel_id\"],\n  },\n  output_parameters: {\n    properties: {\n      canvas_id: {\n        type: SlackTypes.canvas_id,\n        description: \"Canvas link\",\n        title: \"Canvas link\",\n      },\n    },\n    required: [\"canvas_id\"],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/channel_canvas_create_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport ChannelCanvasCreate from \"./channel_canvas_create.ts\";\n\nDeno.test(\"ChannelCanvasCreate generates valid FunctionManifest\", () => {\n  assertEquals(\n    ChannelCanvasCreate.definition.callback_id,\n    \"slack#/functions/channel_canvas_create\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Create channel canvas\",\n    input_parameters: {\n      properties: {\n        channel_id: {\n          type: SlackTypes.channel_id,\n          description: \"Channel name\",\n          title: \"Select a channel\",\n        },\n        canvas_create_type: {\n          type: SchemaTypes.string,\n          description: \"Type of creation\",\n          title: \"Type of creation\",\n        },\n        canvas_template_id: {\n          type: SlackTypes.canvas_template_id,\n          description: \"Select an option\",\n          title: \"Select a canvas template\",\n        },\n        content: {\n          type: SlackTypes.expanded_rich_text,\n          description: \"Add content to the canvas\",\n          title: \"Add content\",\n        },\n        placeholder_values: {\n          type: SchemaTypes.object,\n          description: \"Variables\",\n          title: \"Variables\",\n        },\n      },\n      required: [\"channel_id\"],\n    },\n    output_parameters: {\n      properties: {\n        canvas_id: {\n          type: SlackTypes.canvas_id,\n          description: \"Canvas link\",\n          title: \"Canvas link\",\n        },\n      },\n      required: [\"canvas_id\"],\n    },\n  };\n  const actual = ChannelCanvasCreate.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"ChannelCanvasCreate can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_ChannelCanvasCreate_slack_function\",\n    title: \"Test ChannelCanvasCreate\",\n    description: \"This is a generated test to test ChannelCanvasCreate\",\n  });\n  testWorkflow.addStep(ChannelCanvasCreate, { channel_id: \"test\" });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/channel_canvas_create\");\n  assertEquals(actual.inputs, { channel_id: \"test\" });\n});\n\nDeno.test(\"All outputs of Slack function ChannelCanvasCreate should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_ChannelCanvasCreate_slack_function\",\n    title: \"Test ChannelCanvasCreate\",\n    description: \"This is a generated test to test ChannelCanvasCreate\",\n  });\n  const step = testWorkflow.addStep(ChannelCanvasCreate, {\n    channel_id: \"test\",\n  });\n  assertExists(step.outputs.canvas_id);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/create_channel.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/create_channel\",\n  source_file: \"\",\n  title: \"Create a channel\",\n  input_parameters: {\n    properties: {\n      team_id: {\n        type: SlackTypes.team_id,\n        description: \"Search all workspaces\",\n        title: \"Select a workspace\",\n      },\n      channel_name: {\n        type: SchemaTypes.string,\n        description: \"Enter a channel name\",\n        title: \"Channel name\",\n      },\n      manager_ids: {\n        type: SchemaTypes.array,\n        description: \"Search all people\",\n        title: \"Select Channel Manager(s)\",\n        items: { type: SlackTypes.user_id },\n      },\n      is_private: {\n        type: SchemaTypes.boolean,\n        description: \"Make this channel private\",\n        title: \"Make channel private\",\n      },\n    },\n    required: [\"channel_name\"],\n  },\n  output_parameters: {\n    properties: {\n      channel_id: {\n        type: SlackTypes.channel_id,\n        description: \"Channel name\",\n        title: \"Channel name\",\n      },\n      manager_ids: {\n        type: SchemaTypes.array,\n        description: \"Person(s) who were made channel manager\",\n        title: \"Person(s) who were made channel manager\",\n        items: { type: SlackTypes.user_id },\n      },\n    },\n    required: [\"channel_id\"],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/create_channel_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport CreateChannel from \"./create_channel.ts\";\n\nDeno.test(\"CreateChannel generates valid FunctionManifest\", () => {\n  assertEquals(\n    CreateChannel.definition.callback_id,\n    \"slack#/functions/create_channel\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Create a channel\",\n    input_parameters: {\n      properties: {\n        team_id: {\n          type: SlackTypes.team_id,\n          description: \"Search all workspaces\",\n          title: \"Select a workspace\",\n        },\n        channel_name: {\n          type: SchemaTypes.string,\n          description: \"Enter a channel name\",\n          title: \"Channel name\",\n        },\n        manager_ids: {\n          type: SchemaTypes.array,\n          description: \"Search all people\",\n          title: \"Select Channel Manager(s)\",\n          items: { type: SlackTypes.user_id },\n        },\n        is_private: {\n          type: SchemaTypes.boolean,\n          description: \"Make this channel private\",\n          title: \"Make channel private\",\n        },\n      },\n      required: [\"channel_name\"],\n    },\n    output_parameters: {\n      properties: {\n        channel_id: {\n          type: SlackTypes.channel_id,\n          description: \"Channel name\",\n          title: \"Channel name\",\n        },\n        manager_ids: {\n          type: SchemaTypes.array,\n          description: \"Person(s) who were made channel manager\",\n          title: \"Person(s) who were made channel manager\",\n          items: { type: SlackTypes.user_id },\n        },\n      },\n      required: [\"channel_id\"],\n    },\n  };\n  const actual = CreateChannel.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"CreateChannel can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_CreateChannel_slack_function\",\n    title: \"Test CreateChannel\",\n    description: \"This is a generated test to test CreateChannel\",\n  });\n  testWorkflow.addStep(CreateChannel, { channel_name: \"test\" });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/create_channel\");\n  assertEquals(actual.inputs, { channel_name: \"test\" });\n});\n\nDeno.test(\"All outputs of Slack function CreateChannel should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_CreateChannel_slack_function\",\n    title: \"Test CreateChannel\",\n    description: \"This is a generated test to test CreateChannel\",\n  });\n  const step = testWorkflow.addStep(CreateChannel, { channel_name: \"test\" });\n  assertExists(step.outputs.channel_id);\n  assertExists(step.outputs.manager_ids);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/create_usergroup.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/create_usergroup\",\n  source_file: \"\",\n  title: \"Create a user group\",\n  description: \"Additional permissions might be required\",\n  input_parameters: {\n    properties: {\n      team_id: {\n        type: SlackTypes.team_id,\n        description: \"Search all teams\",\n        title: \"Select a team\",\n      },\n      usergroup_handle: {\n        type: SchemaTypes.string,\n        description: \"Ex: accounts-team\",\n        title: \"Handle\",\n      },\n      usergroup_name: {\n        type: SchemaTypes.string,\n        description: \"Ex: Accounts Team\",\n        title: \"Display name\",\n      },\n    },\n    required: [\"usergroup_handle\", \"usergroup_name\"],\n  },\n  output_parameters: {\n    properties: {\n      usergroup_id: {\n        type: SlackTypes.usergroup_id,\n        description: \"User group\",\n        title: \"User group\",\n      },\n    },\n    required: [\"usergroup_id\"],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/create_usergroup_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport CreateUsergroup from \"./create_usergroup.ts\";\n\nDeno.test(\"CreateUsergroup generates valid FunctionManifest\", () => {\n  assertEquals(\n    CreateUsergroup.definition.callback_id,\n    \"slack#/functions/create_usergroup\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Create a user group\",\n    description: \"Additional permissions might be required\",\n    input_parameters: {\n      properties: {\n        team_id: {\n          type: SlackTypes.team_id,\n          description: \"Search all teams\",\n          title: \"Select a team\",\n        },\n        usergroup_handle: {\n          type: SchemaTypes.string,\n          description: \"Ex: accounts-team\",\n          title: \"Handle\",\n        },\n        usergroup_name: {\n          type: SchemaTypes.string,\n          description: \"Ex: Accounts Team\",\n          title: \"Display name\",\n        },\n      },\n      required: [\"usergroup_handle\", \"usergroup_name\"],\n    },\n    output_parameters: {\n      properties: {\n        usergroup_id: {\n          type: SlackTypes.usergroup_id,\n          description: \"User group\",\n          title: \"User group\",\n        },\n      },\n      required: [\"usergroup_id\"],\n    },\n  };\n  const actual = CreateUsergroup.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"CreateUsergroup can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_CreateUsergroup_slack_function\",\n    title: \"Test CreateUsergroup\",\n    description: \"This is a generated test to test CreateUsergroup\",\n  });\n  testWorkflow.addStep(CreateUsergroup, {\n    usergroup_handle: \"test\",\n    usergroup_name: \"test\",\n  });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/create_usergroup\");\n  assertEquals(actual.inputs, {\n    usergroup_handle: \"test\",\n    usergroup_name: \"test\",\n  });\n});\n\nDeno.test(\"All outputs of Slack function CreateUsergroup should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_CreateUsergroup_slack_function\",\n    title: \"Test CreateUsergroup\",\n    description: \"This is a generated test to test CreateUsergroup\",\n  });\n  const step = testWorkflow.addStep(CreateUsergroup, {\n    usergroup_handle: \"test\",\n    usergroup_name: \"test\",\n  });\n  assertExists(step.outputs.usergroup_id);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/delay.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/delay\",\n  source_file: \"\",\n  title: \"Delay this workflow\",\n  description: \"Pauses the workflow at this step\",\n  input_parameters: {\n    properties: {\n      minutes_to_delay: {\n        type: SchemaTypes.integer,\n        description: \"Enter number of minutes\",\n        title: \"Delay\",\n      },\n    },\n    required: [\"minutes_to_delay\"],\n  },\n  output_parameters: { properties: {}, required: [] },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/delay_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { assertEquals, assertNotStrictEquals } from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport Delay from \"./delay.ts\";\n\nDeno.test(\"Delay generates valid FunctionManifest\", () => {\n  assertEquals(Delay.definition.callback_id, \"slack#/functions/delay\");\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Delay this workflow\",\n    description: \"Pauses the workflow at this step\",\n    input_parameters: {\n      properties: {\n        minutes_to_delay: {\n          type: SchemaTypes.integer,\n          description: \"Enter number of minutes\",\n          title: \"Delay\",\n        },\n      },\n      required: [\"minutes_to_delay\"],\n    },\n    output_parameters: { properties: {}, required: [] },\n  };\n  const actual = Delay.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"Delay can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_Delay_slack_function\",\n    title: \"Test Delay\",\n    description: \"This is a generated test to test Delay\",\n  });\n  testWorkflow.addStep(Delay, { minutes_to_delay: \"test\" });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/delay\");\n  assertEquals(actual.inputs, { minutes_to_delay: \"test\" });\n});\n"
  },
  {
    "path": "src/schema/slack/functions/invite_user_to_channel.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/invite_user_to_channel\",\n  source_file: \"\",\n  title: \"Add people to a channel\",\n  description:\n    \"You or the people using the workflow must have permission to invite others to the channel\",\n  input_parameters: {\n    properties: {\n      channel_ids: {\n        type: SchemaTypes.array,\n        description: \"Search all channels\",\n        title: \"Select channels\",\n        items: { type: SlackTypes.channel_id },\n      },\n      user_ids: {\n        type: SchemaTypes.array,\n        description: \"Search all people\",\n        title: \"Select member(s)\",\n        items: { type: SlackTypes.user_id },\n      },\n      usergroup_ids: {\n        type: SchemaTypes.array,\n        description: \"Search all user groups\",\n        title: \"Select user groups\",\n        items: { type: SlackTypes.usergroup_id },\n      },\n    },\n    required: [\"channel_ids\"],\n  },\n  output_parameters: {\n    properties: {\n      user_ids: {\n        type: SchemaTypes.array,\n        description: \"Person(s) who were invited\",\n        title: \"Person(s) who were invited\",\n        items: { type: SlackTypes.user_id },\n      },\n      usergroup_ids: {\n        type: SchemaTypes.array,\n        description: \"Usergroup(s) who were invited\",\n        title: \"Usergroup(s) who were invited\",\n        items: { type: SlackTypes.usergroup_id },\n      },\n    },\n    required: [],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/invite_user_to_channel_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport InviteUserToChannel from \"./invite_user_to_channel.ts\";\n\nDeno.test(\"InviteUserToChannel generates valid FunctionManifest\", () => {\n  assertEquals(\n    InviteUserToChannel.definition.callback_id,\n    \"slack#/functions/invite_user_to_channel\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Add people to a channel\",\n    description:\n      \"You or the people using the workflow must have permission to invite others to the channel\",\n    input_parameters: {\n      properties: {\n        channel_ids: {\n          type: SchemaTypes.array,\n          description: \"Search all channels\",\n          title: \"Select channels\",\n          items: { type: SlackTypes.channel_id },\n        },\n        user_ids: {\n          type: SchemaTypes.array,\n          description: \"Search all people\",\n          title: \"Select member(s)\",\n          items: { type: SlackTypes.user_id },\n        },\n        usergroup_ids: {\n          type: SchemaTypes.array,\n          description: \"Search all user groups\",\n          title: \"Select user groups\",\n          items: { type: SlackTypes.usergroup_id },\n        },\n      },\n      required: [\"channel_ids\"],\n    },\n    output_parameters: {\n      properties: {\n        user_ids: {\n          type: SchemaTypes.array,\n          description: \"Person(s) who were invited\",\n          title: \"Person(s) who were invited\",\n          items: { type: SlackTypes.user_id },\n        },\n        usergroup_ids: {\n          type: SchemaTypes.array,\n          description: \"Usergroup(s) who were invited\",\n          title: \"Usergroup(s) who were invited\",\n          items: { type: SlackTypes.usergroup_id },\n        },\n      },\n      required: [],\n    },\n  };\n  const actual = InviteUserToChannel.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"InviteUserToChannel can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_InviteUserToChannel_slack_function\",\n    title: \"Test InviteUserToChannel\",\n    description: \"This is a generated test to test InviteUserToChannel\",\n  });\n  testWorkflow.addStep(InviteUserToChannel, { channel_ids: \"test\" });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/invite_user_to_channel\");\n  assertEquals(actual.inputs, { channel_ids: \"test\" });\n});\n\nDeno.test(\"All outputs of Slack function InviteUserToChannel should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_InviteUserToChannel_slack_function\",\n    title: \"Test InviteUserToChannel\",\n    description: \"This is a generated test to test InviteUserToChannel\",\n  });\n  const step = testWorkflow.addStep(InviteUserToChannel, {\n    channel_ids: \"test\",\n  });\n  assertExists(step.outputs.user_ids);\n  assertExists(step.outputs.usergroup_ids);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/mod.ts",
    "content": "/** This file was autogenerated on Fri Sep 06 2024. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport AddBookmark from \"./add_bookmark.ts\";\nimport AddPin from \"./add_pin.ts\";\nimport AddReaction from \"./add_reaction.ts\";\nimport AddUserToUsergroup from \"./add_user_to_usergroup.ts\";\nimport ArchiveChannel from \"./archive_channel.ts\";\nimport CanvasCopy from \"./canvas_copy.ts\";\nimport CanvasCreate from \"./canvas_create.ts\";\nimport CanvasUpdateContent from \"./canvas_update_content.ts\";\nimport ChannelCanvasCreate from \"./channel_canvas_create.ts\";\nimport CreateChannel from \"./create_channel.ts\";\nimport CreateUsergroup from \"./create_usergroup.ts\";\nimport Delay from \"./delay.ts\";\nimport InviteUserToChannel from \"./invite_user_to_channel.ts\";\nimport OpenForm from \"./open_form.ts\";\nimport RemoveReaction from \"./remove_reaction.ts\";\nimport RemoveUserFromUsergroup from \"./remove_user_from_usergroup.ts\";\nimport ReplyInThread from \"./reply_in_thread.ts\";\nimport SendDm from \"./send_dm.ts\";\nimport SendEphemeralMessage from \"./send_ephemeral_message.ts\";\nimport SendMessage from \"./send_message.ts\";\nimport ShareCanvas from \"./share_canvas.ts\";\nimport ShareCanvasInThread from \"./share_canvas_in_thread.ts\";\nimport UpdateChannelTopic from \"./update_channel_topic.ts\";\n\nconst SlackFunctions = {\n  AddBookmark,\n  AddPin,\n  AddReaction,\n  AddUserToUsergroup,\n  ArchiveChannel,\n  CanvasCopy,\n  CanvasCreate,\n  CanvasUpdateContent,\n  ChannelCanvasCreate,\n  CreateChannel,\n  CreateUsergroup,\n  Delay,\n  InviteUserToChannel,\n  OpenForm,\n  RemoveReaction,\n  RemoveUserFromUsergroup,\n  ReplyInThread,\n  SendDm,\n  SendEphemeralMessage,\n  SendMessage,\n  ShareCanvas,\n  ShareCanvasInThread,\n  UpdateChannelTopic,\n} as const;\n\nexport default SlackFunctions;\n"
  },
  {
    "path": "src/schema/slack/functions/open_form.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport { InternalSlackTypes } from \"../types/custom/mod.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/open_form\",\n  source_file: \"\",\n  title: \"Collect info in a form\",\n  description: \"Uses a form to collect information\",\n  input_parameters: {\n    properties: {\n      title: {\n        type: SchemaTypes.string,\n        description: \"Title of the form\",\n        title: \"title\",\n      },\n      description: {\n        type: SchemaTypes.string,\n        description: \"Description of the form\",\n        title: \"description\",\n      },\n      submit_label: {\n        type: SchemaTypes.string,\n        description: \"Submit Label of the form\",\n        title: \"submit_label\",\n      },\n      fields: {\n        type: InternalSlackTypes.form_input_object,\n        description: \"Input fields to be shown on the form\",\n        title: \"fields\",\n      },\n      interactivity: {\n        type: SlackTypes.interactivity,\n        description:\n          \"Context about the interactive event that led to opening of the form\",\n        title: \"interactivity\",\n      },\n    },\n    required: [\"title\", \"fields\", \"interactivity\"],\n  },\n  output_parameters: {\n    properties: {\n      fields: {\n        type: SchemaTypes.object,\n        description: \"fields\",\n        title: \"fields\",\n      },\n      interactivity: {\n        type: SlackTypes.interactivity,\n        description: \"Context about the form submit action interactive event\",\n        title: \"interactivity\",\n      },\n      submit_user: {\n        type: SlackTypes.user_id,\n        description: \"Person who submitted the form\",\n        title: \"Person who submitted the form\",\n      },\n      timestamp_started: {\n        type: SlackTypes.timestamp,\n        description: \"Time when step started\",\n        title: \"Time when step started\",\n      },\n      timestamp_completed: {\n        type: SlackTypes.timestamp,\n        description: \"Time when step ended\",\n        title: \"Time when step ended\",\n      },\n    },\n    required: [\n      \"fields\",\n      \"interactivity\",\n      \"submit_user\",\n      \"timestamp_started\",\n      \"timestamp_completed\",\n    ],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/open_form_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport { InternalSlackTypes } from \"../types/custom/mod.ts\";\nimport OpenForm from \"./open_form.ts\";\n\nDeno.test(\"OpenForm generates valid FunctionManifest\", () => {\n  assertEquals(OpenForm.definition.callback_id, \"slack#/functions/open_form\");\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Collect info in a form\",\n    description: \"Uses a form to collect information\",\n    input_parameters: {\n      properties: {\n        title: {\n          type: SchemaTypes.string,\n          description: \"Title of the form\",\n          title: \"title\",\n        },\n        description: {\n          type: SchemaTypes.string,\n          description: \"Description of the form\",\n          title: \"description\",\n        },\n        submit_label: {\n          type: SchemaTypes.string,\n          description: \"Submit Label of the form\",\n          title: \"submit_label\",\n        },\n        fields: {\n          type: InternalSlackTypes.form_input_object,\n          description: \"Input fields to be shown on the form\",\n          title: \"fields\",\n        },\n        interactivity: {\n          type: SlackTypes.interactivity,\n          description:\n            \"Context about the interactive event that led to opening of the form\",\n          title: \"interactivity\",\n        },\n      },\n      required: [\"title\", \"fields\", \"interactivity\"],\n    },\n    output_parameters: {\n      properties: {\n        fields: {\n          type: SchemaTypes.object,\n          description: \"fields\",\n          title: \"fields\",\n        },\n        interactivity: {\n          type: SlackTypes.interactivity,\n          description: \"Context about the form submit action interactive event\",\n          title: \"interactivity\",\n        },\n        submit_user: {\n          type: SlackTypes.user_id,\n          description: \"Person who submitted the form\",\n          title: \"Person who submitted the form\",\n        },\n        timestamp_started: {\n          type: SlackTypes.timestamp,\n          description: \"Time when step started\",\n          title: \"Time when step started\",\n        },\n        timestamp_completed: {\n          type: SlackTypes.timestamp,\n          description: \"Time when step ended\",\n          title: \"Time when step ended\",\n        },\n      },\n      required: [\n        \"fields\",\n        \"interactivity\",\n        \"submit_user\",\n        \"timestamp_started\",\n        \"timestamp_completed\",\n      ],\n    },\n  };\n  const actual = OpenForm.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"OpenForm can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_OpenForm_slack_function\",\n    title: \"Test OpenForm\",\n    description: \"This is a generated test to test OpenForm\",\n  });\n  testWorkflow.addStep(OpenForm, {\n    title: \"test\",\n    fields: \"test\",\n    interactivity: \"test\",\n  });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/open_form\");\n  assertEquals(actual.inputs, {\n    title: \"test\",\n    fields: \"test\",\n    interactivity: \"test\",\n  });\n});\n\nDeno.test(\"All outputs of Slack function OpenForm should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_OpenForm_slack_function\",\n    title: \"Test OpenForm\",\n    description: \"This is a generated test to test OpenForm\",\n  });\n  const step = testWorkflow.addStep(OpenForm, {\n    title: \"test\",\n    fields: \"test\",\n    interactivity: \"test\",\n  });\n  assertExists(step.outputs.fields);\n  assertExists(step.outputs.interactivity);\n  assertExists(step.outputs.submit_user);\n  assertExists(step.outputs.timestamp_started);\n  assertExists(step.outputs.timestamp_completed);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/remove_reaction.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/remove_reaction\",\n  source_file: \"\",\n  title: \"Remove a reaction from a message\",\n  input_parameters: {\n    properties: {\n      message_context: {\n        type: SlackTypes.message_context,\n        description: \"Select a message to unreact from\",\n        title: \"Select a message to unreact from\",\n      },\n      emoji: {\n        type: SchemaTypes.string,\n        description: \"Reaction (emoji) name\",\n        title: \"Emoji\",\n      },\n    },\n    required: [\"message_context\", \"emoji\"],\n  },\n  output_parameters: { properties: {}, required: [] },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/remove_reaction_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { assertEquals, assertNotStrictEquals } from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport RemoveReaction from \"./remove_reaction.ts\";\n\nDeno.test(\"RemoveReaction generates valid FunctionManifest\", () => {\n  assertEquals(\n    RemoveReaction.definition.callback_id,\n    \"slack#/functions/remove_reaction\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Remove a reaction from a message\",\n    input_parameters: {\n      properties: {\n        message_context: {\n          type: SlackTypes.message_context,\n          description: \"Select a message to unreact from\",\n          title: \"Select a message to unreact from\",\n        },\n        emoji: {\n          type: SchemaTypes.string,\n          description: \"Reaction (emoji) name\",\n          title: \"Emoji\",\n        },\n      },\n      required: [\"message_context\", \"emoji\"],\n    },\n    output_parameters: { properties: {}, required: [] },\n  };\n  const actual = RemoveReaction.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"RemoveReaction can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_RemoveReaction_slack_function\",\n    title: \"Test RemoveReaction\",\n    description: \"This is a generated test to test RemoveReaction\",\n  });\n  testWorkflow.addStep(RemoveReaction, {\n    message_context: \"test\",\n    emoji: \"test\",\n  });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/remove_reaction\");\n  assertEquals(actual.inputs, { message_context: \"test\", emoji: \"test\" });\n});\n"
  },
  {
    "path": "src/schema/slack/functions/remove_user_from_usergroup.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/remove_user_from_usergroup\",\n  source_file: \"\",\n  title: \"Remove someone from a user group\",\n  description: \"Additional permissions might be required\",\n  input_parameters: {\n    properties: {\n      usergroup_id: {\n        type: SlackTypes.usergroup_id,\n        description: \"Search all user groups\",\n        title: \"Select a user group\",\n      },\n      user_ids: {\n        type: SchemaTypes.array,\n        description: \"Search all people\",\n        title: \"Select member(s)\",\n        items: { type: SlackTypes.user_id },\n      },\n    },\n    required: [\"usergroup_id\", \"user_ids\"],\n  },\n  output_parameters: {\n    properties: {\n      usergroup_id: {\n        type: SlackTypes.usergroup_id,\n        description: \"User group\",\n        title: \"User group\",\n      },\n    },\n    required: [],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/remove_user_from_usergroup_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport RemoveUserFromUsergroup from \"./remove_user_from_usergroup.ts\";\n\nDeno.test(\"RemoveUserFromUsergroup generates valid FunctionManifest\", () => {\n  assertEquals(\n    RemoveUserFromUsergroup.definition.callback_id,\n    \"slack#/functions/remove_user_from_usergroup\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Remove someone from a user group\",\n    description: \"Additional permissions might be required\",\n    input_parameters: {\n      properties: {\n        usergroup_id: {\n          type: SlackTypes.usergroup_id,\n          description: \"Search all user groups\",\n          title: \"Select a user group\",\n        },\n        user_ids: {\n          type: SchemaTypes.array,\n          description: \"Search all people\",\n          title: \"Select member(s)\",\n          items: { type: SlackTypes.user_id },\n        },\n      },\n      required: [\"usergroup_id\", \"user_ids\"],\n    },\n    output_parameters: {\n      properties: {\n        usergroup_id: {\n          type: SlackTypes.usergroup_id,\n          description: \"User group\",\n          title: \"User group\",\n        },\n      },\n      required: [],\n    },\n  };\n  const actual = RemoveUserFromUsergroup.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"RemoveUserFromUsergroup can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_RemoveUserFromUsergroup_slack_function\",\n    title: \"Test RemoveUserFromUsergroup\",\n    description: \"This is a generated test to test RemoveUserFromUsergroup\",\n  });\n  testWorkflow.addStep(RemoveUserFromUsergroup, {\n    usergroup_id: \"test\",\n    user_ids: \"test\",\n  });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(\n    actual.function_id,\n    \"slack#/functions/remove_user_from_usergroup\",\n  );\n  assertEquals(actual.inputs, { usergroup_id: \"test\", user_ids: \"test\" });\n});\n\nDeno.test(\"All outputs of Slack function RemoveUserFromUsergroup should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_RemoveUserFromUsergroup_slack_function\",\n    title: \"Test RemoveUserFromUsergroup\",\n    description: \"This is a generated test to test RemoveUserFromUsergroup\",\n  });\n  const step = testWorkflow.addStep(RemoveUserFromUsergroup, {\n    usergroup_id: \"test\",\n    user_ids: \"test\",\n  });\n  assertExists(step.outputs.usergroup_id);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/reply_in_thread.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/reply_in_thread\",\n  source_file: \"\",\n  title: \"Reply to a message in thread\",\n  input_parameters: {\n    properties: {\n      message_context: {\n        type: SlackTypes.message_context,\n        description: \"Select a message to reply to\",\n        title: \"Select a message to reply to\",\n      },\n      message: {\n        type: SlackTypes.rich_text,\n        description: \"Add a reply\",\n        title: \"Add a reply\",\n      },\n      reply_broadcast: {\n        type: SchemaTypes.boolean,\n        description: \"Also send to conversation\",\n        title: \"Also send to conversation\",\n      },\n      metadata: {\n        type: SchemaTypes.object,\n        description:\n          \"Metadata you post to Slack is accessible to any app or user who is a member of that workspace\",\n        title: \"Message metadata\",\n        properties: {\n          event_type: { type: SchemaTypes.string },\n          event_payload: { type: SchemaTypes.object },\n        },\n        additionalProperties: true,\n        required: [\"event_type\", \"event_payload\"],\n      },\n      interactive_blocks: {\n        type: SlackTypes.blocks,\n        description: \"Button(s) to send with the message\",\n        title: \"Button(s) to send with the message\",\n      },\n      files: {\n        type: SchemaTypes.array,\n        description: \"File(s) to attach to the message\",\n        title: \"File(s) to attach to the message\",\n        items: { type: SlackTypes.file_id },\n      },\n    },\n    required: [\"message_context\", \"message\"],\n  },\n  output_parameters: {\n    properties: {\n      message_context: {\n        type: SlackTypes.message_context,\n        description: \"Reference to the message sent\",\n        title: \"Reference to the message sent\",\n      },\n      message_link: {\n        type: SchemaTypes.string,\n        description: \"Message link\",\n        title: \"Message link\",\n      },\n      action: {\n        type: SchemaTypes.object,\n        description: \"Button interactivity data\",\n        title: \"Button interactivity data\",\n      },\n      interactivity: {\n        type: SlackTypes.interactivity,\n        description: \"Interactivity context\",\n        title: \"interactivity\",\n      },\n      timestamp_started: {\n        type: SlackTypes.timestamp,\n        description: \"Time when step started\",\n        title: \"Time when step started\",\n      },\n      timestamp_completed: {\n        type: SlackTypes.timestamp,\n        description: \"Time when step ended\",\n        title: \"Time when step ended\",\n      },\n    },\n    required: [\n      \"message_context\",\n      \"message_link\",\n      \"timestamp_started\",\n      \"timestamp_completed\",\n    ],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/reply_in_thread_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport ReplyInThread from \"./reply_in_thread.ts\";\n\nDeno.test(\"ReplyInThread generates valid FunctionManifest\", () => {\n  assertEquals(\n    ReplyInThread.definition.callback_id,\n    \"slack#/functions/reply_in_thread\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Reply to a message in thread\",\n    input_parameters: {\n      properties: {\n        message_context: {\n          type: SlackTypes.message_context,\n          description: \"Select a message to reply to\",\n          title: \"Select a message to reply to\",\n        },\n        message: {\n          type: SlackTypes.rich_text,\n          description: \"Add a reply\",\n          title: \"Add a reply\",\n        },\n        reply_broadcast: {\n          type: SchemaTypes.boolean,\n          description: \"Also send to conversation\",\n          title: \"Also send to conversation\",\n        },\n        metadata: {\n          type: SchemaTypes.object,\n          description:\n            \"Metadata you post to Slack is accessible to any app or user who is a member of that workspace\",\n          title: \"Message metadata\",\n          properties: {\n            event_type: { type: SchemaTypes.string },\n            event_payload: { type: SchemaTypes.object },\n          },\n          additionalProperties: true,\n          required: [\"event_type\", \"event_payload\"],\n        },\n        interactive_blocks: {\n          type: SlackTypes.blocks,\n          description: \"Button(s) to send with the message\",\n          title: \"Button(s) to send with the message\",\n        },\n        files: {\n          type: SchemaTypes.array,\n          description: \"File(s) to attach to the message\",\n          title: \"File(s) to attach to the message\",\n          items: { type: SlackTypes.file_id },\n        },\n      },\n      required: [\"message_context\", \"message\"],\n    },\n    output_parameters: {\n      properties: {\n        message_context: {\n          type: SlackTypes.message_context,\n          description: \"Reference to the message sent\",\n          title: \"Reference to the message sent\",\n        },\n        message_link: {\n          type: SchemaTypes.string,\n          description: \"Message link\",\n          title: \"Message link\",\n        },\n        action: {\n          type: SchemaTypes.object,\n          description: \"Button interactivity data\",\n          title: \"Button interactivity data\",\n        },\n        interactivity: {\n          type: SlackTypes.interactivity,\n          description: \"Interactivity context\",\n          title: \"interactivity\",\n        },\n        timestamp_started: {\n          type: SlackTypes.timestamp,\n          description: \"Time when step started\",\n          title: \"Time when step started\",\n        },\n        timestamp_completed: {\n          type: SlackTypes.timestamp,\n          description: \"Time when step ended\",\n          title: \"Time when step ended\",\n        },\n      },\n      required: [\n        \"message_context\",\n        \"message_link\",\n        \"timestamp_started\",\n        \"timestamp_completed\",\n      ],\n    },\n  };\n  const actual = ReplyInThread.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"ReplyInThread can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_ReplyInThread_slack_function\",\n    title: \"Test ReplyInThread\",\n    description: \"This is a generated test to test ReplyInThread\",\n  });\n  testWorkflow.addStep(ReplyInThread, {\n    message_context: \"test\",\n    message: \"test\",\n  });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/reply_in_thread\");\n  assertEquals(actual.inputs, { message_context: \"test\", message: \"test\" });\n});\n\nDeno.test(\"All outputs of Slack function ReplyInThread should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_ReplyInThread_slack_function\",\n    title: \"Test ReplyInThread\",\n    description: \"This is a generated test to test ReplyInThread\",\n  });\n  const step = testWorkflow.addStep(ReplyInThread, {\n    message_context: \"test\",\n    message: \"test\",\n  });\n  assertExists(step.outputs.message_context);\n  assertExists(step.outputs.message_link);\n  assertExists(step.outputs.action);\n  assertExists(step.outputs.interactivity);\n  assertExists(step.outputs.timestamp_started);\n  assertExists(step.outputs.timestamp_completed);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/send_dm.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/send_dm\",\n  source_file: \"\",\n  title: \"Send a message to a person\",\n  input_parameters: {\n    properties: {\n      user_id: {\n        type: SlackTypes.user_id,\n        description: \"Search all people\",\n        title: \"Select a member\",\n      },\n      message: {\n        type: SlackTypes.rich_text,\n        description: \"Add a message\",\n        title: \"Add a message\",\n      },\n      interactive_blocks: {\n        type: SlackTypes.blocks,\n        description: \"Button(s) to send with the message\",\n        title: \"Button(s) to send with the message\",\n      },\n      files: {\n        type: SchemaTypes.array,\n        description: \"File(s) to attach to the message\",\n        title: \"File(s) to attach to the message\",\n        items: { type: SlackTypes.file_id },\n      },\n    },\n    required: [\"user_id\", \"message\"],\n  },\n  output_parameters: {\n    properties: {\n      message_timestamp: {\n        type: SlackTypes.timestamp,\n        description: \"Message timestamp\",\n        title: \"Message timestamp\",\n      },\n      message_link: {\n        type: SchemaTypes.string,\n        description: \"Message link\",\n        title: \"Message link\",\n      },\n      action: {\n        type: SchemaTypes.object,\n        description: \"Button interactivity data\",\n        title: \"Button interactivity data\",\n      },\n      interactivity: {\n        type: SlackTypes.interactivity,\n        description: \"Interactivity context\",\n        title: \"interactivity\",\n      },\n      message_context: {\n        type: SlackTypes.message_context,\n        description: \"Reference to the message sent\",\n        title: \"Reference to the message sent\",\n      },\n      timestamp_started: {\n        type: SlackTypes.timestamp,\n        description: \"Time when step started\",\n        title: \"Time when step started\",\n      },\n      timestamp_completed: {\n        type: SlackTypes.timestamp,\n        description: \"Time when step ended\",\n        title: \"Time when step ended\",\n      },\n    },\n    required: [\n      \"message_timestamp\",\n      \"message_link\",\n      \"message_context\",\n      \"timestamp_started\",\n      \"timestamp_completed\",\n    ],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/send_dm_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport SendDm from \"./send_dm.ts\";\n\nDeno.test(\"SendDm generates valid FunctionManifest\", () => {\n  assertEquals(SendDm.definition.callback_id, \"slack#/functions/send_dm\");\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Send a message to a person\",\n    input_parameters: {\n      properties: {\n        user_id: {\n          type: SlackTypes.user_id,\n          description: \"Search all people\",\n          title: \"Select a member\",\n        },\n        message: {\n          type: SlackTypes.rich_text,\n          description: \"Add a message\",\n          title: \"Add a message\",\n        },\n        interactive_blocks: {\n          type: SlackTypes.blocks,\n          description: \"Button(s) to send with the message\",\n          title: \"Button(s) to send with the message\",\n        },\n        files: {\n          type: SchemaTypes.array,\n          description: \"File(s) to attach to the message\",\n          title: \"File(s) to attach to the message\",\n          items: { type: SlackTypes.file_id },\n        },\n      },\n      required: [\"user_id\", \"message\"],\n    },\n    output_parameters: {\n      properties: {\n        message_timestamp: {\n          type: SlackTypes.timestamp,\n          description: \"Message timestamp\",\n          title: \"Message timestamp\",\n        },\n        message_link: {\n          type: SchemaTypes.string,\n          description: \"Message link\",\n          title: \"Message link\",\n        },\n        action: {\n          type: SchemaTypes.object,\n          description: \"Button interactivity data\",\n          title: \"Button interactivity data\",\n        },\n        interactivity: {\n          type: SlackTypes.interactivity,\n          description: \"Interactivity context\",\n          title: \"interactivity\",\n        },\n        message_context: {\n          type: SlackTypes.message_context,\n          description: \"Reference to the message sent\",\n          title: \"Reference to the message sent\",\n        },\n        timestamp_started: {\n          type: SlackTypes.timestamp,\n          description: \"Time when step started\",\n          title: \"Time when step started\",\n        },\n        timestamp_completed: {\n          type: SlackTypes.timestamp,\n          description: \"Time when step ended\",\n          title: \"Time when step ended\",\n        },\n      },\n      required: [\n        \"message_timestamp\",\n        \"message_link\",\n        \"message_context\",\n        \"timestamp_started\",\n        \"timestamp_completed\",\n      ],\n    },\n  };\n  const actual = SendDm.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"SendDm can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_SendDm_slack_function\",\n    title: \"Test SendDm\",\n    description: \"This is a generated test to test SendDm\",\n  });\n  testWorkflow.addStep(SendDm, { user_id: \"test\", message: \"test\" });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/send_dm\");\n  assertEquals(actual.inputs, { user_id: \"test\", message: \"test\" });\n});\n\nDeno.test(\"All outputs of Slack function SendDm should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_SendDm_slack_function\",\n    title: \"Test SendDm\",\n    description: \"This is a generated test to test SendDm\",\n  });\n  const step = testWorkflow.addStep(SendDm, {\n    user_id: \"test\",\n    message: \"test\",\n  });\n  assertExists(step.outputs.message_timestamp);\n  assertExists(step.outputs.message_link);\n  assertExists(step.outputs.action);\n  assertExists(step.outputs.interactivity);\n  assertExists(step.outputs.message_context);\n  assertExists(step.outputs.timestamp_started);\n  assertExists(step.outputs.timestamp_completed);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/send_ephemeral_message.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/send_ephemeral_message\",\n  source_file: \"\",\n  title: 'Send an \"only visible to you\" message',\n  description:\n    \"Send a temporary message to someone in a channel that only they can see\",\n  input_parameters: {\n    properties: {\n      channel_id: {\n        type: SlackTypes.channel_id,\n        description: \"Search all channels\",\n        title: \"Select a channel\",\n      },\n      user_id: {\n        type: SlackTypes.user_id,\n        description: \"Search all people\",\n        title: \"Select a member of the channel\",\n      },\n      message: {\n        type: SlackTypes.rich_text,\n        description: \"Add a message\",\n        title: \"Add a message\",\n      },\n      thread_ts: {\n        type: SchemaTypes.string,\n        description:\n          \"Provide another message's ts value to make this message a reply\",\n        title: \"Another message's timestamp value\",\n      },\n    },\n    required: [\"channel_id\", \"user_id\", \"message\"],\n  },\n  output_parameters: {\n    properties: {\n      message_ts: {\n        type: SlackTypes.message_ts,\n        description: \"Message timestamp\",\n        title: \"Message timestamp\",\n      },\n    },\n    required: [\"message_ts\"],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/send_ephemeral_message_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport SendEphemeralMessage from \"./send_ephemeral_message.ts\";\n\nDeno.test(\"SendEphemeralMessage generates valid FunctionManifest\", () => {\n  assertEquals(\n    SendEphemeralMessage.definition.callback_id,\n    \"slack#/functions/send_ephemeral_message\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: 'Send an \"only visible to you\" message',\n    description:\n      \"Send a temporary message to someone in a channel that only they can see\",\n    input_parameters: {\n      properties: {\n        channel_id: {\n          type: SlackTypes.channel_id,\n          description: \"Search all channels\",\n          title: \"Select a channel\",\n        },\n        user_id: {\n          type: SlackTypes.user_id,\n          description: \"Search all people\",\n          title: \"Select a member of the channel\",\n        },\n        message: {\n          type: SlackTypes.rich_text,\n          description: \"Add a message\",\n          title: \"Add a message\",\n        },\n        thread_ts: {\n          type: SchemaTypes.string,\n          description:\n            \"Provide another message's ts value to make this message a reply\",\n          title: \"Another message's timestamp value\",\n        },\n      },\n      required: [\"channel_id\", \"user_id\", \"message\"],\n    },\n    output_parameters: {\n      properties: {\n        message_ts: {\n          type: SlackTypes.message_ts,\n          description: \"Message timestamp\",\n          title: \"Message timestamp\",\n        },\n      },\n      required: [\"message_ts\"],\n    },\n  };\n  const actual = SendEphemeralMessage.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"SendEphemeralMessage can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_SendEphemeralMessage_slack_function\",\n    title: \"Test SendEphemeralMessage\",\n    description: \"This is a generated test to test SendEphemeralMessage\",\n  });\n  testWorkflow.addStep(SendEphemeralMessage, {\n    channel_id: \"test\",\n    user_id: \"test\",\n    message: \"test\",\n  });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/send_ephemeral_message\");\n  assertEquals(actual.inputs, {\n    channel_id: \"test\",\n    user_id: \"test\",\n    message: \"test\",\n  });\n});\n\nDeno.test(\"All outputs of Slack function SendEphemeralMessage should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_SendEphemeralMessage_slack_function\",\n    title: \"Test SendEphemeralMessage\",\n    description: \"This is a generated test to test SendEphemeralMessage\",\n  });\n  const step = testWorkflow.addStep(SendEphemeralMessage, {\n    channel_id: \"test\",\n    user_id: \"test\",\n    message: \"test\",\n  });\n  assertExists(step.outputs.message_ts);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/send_message.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/send_message\",\n  source_file: \"\",\n  title: \"Send a message to a channel\",\n  input_parameters: {\n    properties: {\n      channel_id: {\n        type: SlackTypes.channel_id,\n        description: \"Search all channels\",\n        title: \"Select a channel\",\n      },\n      message: {\n        type: SlackTypes.rich_text,\n        description: \"Add a message\",\n        title: \"Add a message\",\n      },\n      metadata: {\n        type: SchemaTypes.object,\n        description:\n          \"Metadata you post to Slack is accessible to any app or user who is a member of that workspace\",\n        title: \"Message metadata\",\n        properties: {\n          event_type: { type: SchemaTypes.string },\n          event_payload: { type: SchemaTypes.object },\n        },\n        additionalProperties: true,\n        required: [\"event_type\", \"event_payload\"],\n      },\n      interactive_blocks: {\n        type: SlackTypes.blocks,\n        description: \"Button(s) to send with the message\",\n        title: \"Button(s) to send with the message\",\n      },\n      files: {\n        type: SchemaTypes.array,\n        description: \"File(s) to attach to the message\",\n        title: \"File(s) to attach to the message\",\n        items: { type: SlackTypes.file_id },\n      },\n    },\n    required: [\"channel_id\", \"message\"],\n  },\n  output_parameters: {\n    properties: {\n      message_timestamp: {\n        type: SlackTypes.timestamp,\n        description: \"Message timestamp\",\n        title: \"Message timestamp\",\n      },\n      message_link: {\n        type: SchemaTypes.string,\n        description: \"Message link\",\n        title: \"Message link\",\n      },\n      action: {\n        type: SchemaTypes.object,\n        description: \"Button interactivity data\",\n        title: \"Button interactivity data\",\n      },\n      interactivity: {\n        type: SlackTypes.interactivity,\n        description: \"Interactivity context\",\n        title: \"interactivity\",\n      },\n      message_context: {\n        type: SlackTypes.message_context,\n        description: \"Reference to the message sent\",\n        title: \"Reference to the message sent\",\n      },\n      timestamp_started: {\n        type: SlackTypes.timestamp,\n        description: \"Time when step started\",\n        title: \"Time when step started\",\n      },\n      timestamp_completed: {\n        type: SlackTypes.timestamp,\n        description: \"Time when step ended\",\n        title: \"Time when step ended\",\n      },\n    },\n    required: [\n      \"message_timestamp\",\n      \"message_link\",\n      \"message_context\",\n      \"timestamp_started\",\n      \"timestamp_completed\",\n    ],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/send_message_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport SendMessage from \"./send_message.ts\";\n\nDeno.test(\"SendMessage generates valid FunctionManifest\", () => {\n  assertEquals(\n    SendMessage.definition.callback_id,\n    \"slack#/functions/send_message\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Send a message to a channel\",\n    input_parameters: {\n      properties: {\n        channel_id: {\n          type: SlackTypes.channel_id,\n          description: \"Search all channels\",\n          title: \"Select a channel\",\n        },\n        message: {\n          type: SlackTypes.rich_text,\n          description: \"Add a message\",\n          title: \"Add a message\",\n        },\n        metadata: {\n          type: SchemaTypes.object,\n          description:\n            \"Metadata you post to Slack is accessible to any app or user who is a member of that workspace\",\n          title: \"Message metadata\",\n          properties: {\n            event_type: { type: SchemaTypes.string },\n            event_payload: { type: SchemaTypes.object },\n          },\n          additionalProperties: true,\n          required: [\"event_type\", \"event_payload\"],\n        },\n        interactive_blocks: {\n          type: SlackTypes.blocks,\n          description: \"Button(s) to send with the message\",\n          title: \"Button(s) to send with the message\",\n        },\n        files: {\n          type: SchemaTypes.array,\n          description: \"File(s) to attach to the message\",\n          title: \"File(s) to attach to the message\",\n          items: { type: SlackTypes.file_id },\n        },\n      },\n      required: [\"channel_id\", \"message\"],\n    },\n    output_parameters: {\n      properties: {\n        message_timestamp: {\n          type: SlackTypes.timestamp,\n          description: \"Message timestamp\",\n          title: \"Message timestamp\",\n        },\n        message_link: {\n          type: SchemaTypes.string,\n          description: \"Message link\",\n          title: \"Message link\",\n        },\n        action: {\n          type: SchemaTypes.object,\n          description: \"Button interactivity data\",\n          title: \"Button interactivity data\",\n        },\n        interactivity: {\n          type: SlackTypes.interactivity,\n          description: \"Interactivity context\",\n          title: \"interactivity\",\n        },\n        message_context: {\n          type: SlackTypes.message_context,\n          description: \"Reference to the message sent\",\n          title: \"Reference to the message sent\",\n        },\n        timestamp_started: {\n          type: SlackTypes.timestamp,\n          description: \"Time when step started\",\n          title: \"Time when step started\",\n        },\n        timestamp_completed: {\n          type: SlackTypes.timestamp,\n          description: \"Time when step ended\",\n          title: \"Time when step ended\",\n        },\n      },\n      required: [\n        \"message_timestamp\",\n        \"message_link\",\n        \"message_context\",\n        \"timestamp_started\",\n        \"timestamp_completed\",\n      ],\n    },\n  };\n  const actual = SendMessage.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"SendMessage can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_SendMessage_slack_function\",\n    title: \"Test SendMessage\",\n    description: \"This is a generated test to test SendMessage\",\n  });\n  testWorkflow.addStep(SendMessage, { channel_id: \"test\", message: \"test\" });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/send_message\");\n  assertEquals(actual.inputs, { channel_id: \"test\", message: \"test\" });\n});\n\nDeno.test(\"All outputs of Slack function SendMessage should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_SendMessage_slack_function\",\n    title: \"Test SendMessage\",\n    description: \"This is a generated test to test SendMessage\",\n  });\n  const step = testWorkflow.addStep(SendMessage, {\n    channel_id: \"test\",\n    message: \"test\",\n  });\n  assertExists(step.outputs.message_timestamp);\n  assertExists(step.outputs.message_link);\n  assertExists(step.outputs.action);\n  assertExists(step.outputs.interactivity);\n  assertExists(step.outputs.message_context);\n  assertExists(step.outputs.timestamp_started);\n  assertExists(step.outputs.timestamp_completed);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/share_canvas.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/share_canvas\",\n  source_file: \"\",\n  title: \"Share a canvas\",\n  input_parameters: {\n    properties: {\n      canvas_id: {\n        type: SlackTypes.canvas_id,\n        description: \"Search all canvases\",\n        title: \"Select a canvas\",\n      },\n      channel_ids: {\n        type: SchemaTypes.array,\n        description: \"Select channels\",\n        title: \"Select channels\",\n        items: { type: SlackTypes.channel_id },\n      },\n      user_ids: {\n        type: SchemaTypes.array,\n        description: \"Search users\",\n        title: \"Select people\",\n        items: { type: SlackTypes.user_id },\n      },\n      access_level: {\n        type: SchemaTypes.string,\n        description: \"Select an option\",\n        title: \"Select access level\",\n      },\n      message: {\n        type: SlackTypes.rich_text,\n        description: \"Add a message\",\n        title: \"Add a message\",\n      },\n    },\n    required: [\"canvas_id\", \"access_level\"],\n  },\n  output_parameters: {\n    properties: {\n      canvas_id: {\n        type: SlackTypes.canvas_id,\n        description: \"Canvas link\",\n        title: \"Canvas link\",\n      },\n    },\n    required: [],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/share_canvas_in_thread.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/share_canvas_in_thread\",\n  source_file: \"\",\n  title: \"Share canvas in thread\",\n  input_parameters: {\n    properties: {\n      canvas_id: {\n        type: SlackTypes.canvas_id,\n        description: \"Search all canvases\",\n        title: \"Select a canvas\",\n      },\n      message_context: {\n        type: SlackTypes.message_context,\n        description: \"Select a message to reply to\",\n        title: \"Select a message to reply to\",\n      },\n      access_level: {\n        type: SchemaTypes.string,\n        description: \"Select an option\",\n        title: \"Select access level\",\n      },\n      message: {\n        type: SlackTypes.rich_text,\n        description: \"Add a message\",\n        title: \"Add a message\",\n      },\n      reply_broadcast: {\n        type: SchemaTypes.boolean,\n        description: \"Also send to conversation\",\n        title: \"Also send to conversation\",\n      },\n    },\n    required: [\"canvas_id\", \"message_context\", \"access_level\"],\n  },\n  output_parameters: {\n    properties: {\n      canvas_id: {\n        type: SlackTypes.canvas_id,\n        description: \"Canvas link\",\n        title: \"Canvas link\",\n      },\n      message_context: {\n        type: SlackTypes.message_context,\n        description: \"Reference to the message sent\",\n        title: \"Reference to the message sent\",\n      },\n    },\n    required: [],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/share_canvas_in_thread_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport ShareCanvasInThread from \"./share_canvas_in_thread.ts\";\n\nDeno.test(\"ShareCanvasInThread generates valid FunctionManifest\", () => {\n  assertEquals(\n    ShareCanvasInThread.definition.callback_id,\n    \"slack#/functions/share_canvas_in_thread\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Share canvas in thread\",\n    input_parameters: {\n      properties: {\n        canvas_id: {\n          type: SlackTypes.canvas_id,\n          description: \"Search all canvases\",\n          title: \"Select a canvas\",\n        },\n        message_context: {\n          type: SlackTypes.message_context,\n          description: \"Select a message to reply to\",\n          title: \"Select a message to reply to\",\n        },\n        access_level: {\n          type: SchemaTypes.string,\n          description: \"Select an option\",\n          title: \"Select access level\",\n        },\n        message: {\n          type: SlackTypes.rich_text,\n          description: \"Add a message\",\n          title: \"Add a message\",\n        },\n        reply_broadcast: {\n          type: SchemaTypes.boolean,\n          description: \"Also send to conversation\",\n          title: \"Also send to conversation\",\n        },\n      },\n      required: [\"canvas_id\", \"message_context\", \"access_level\"],\n    },\n    output_parameters: {\n      properties: {\n        canvas_id: {\n          type: SlackTypes.canvas_id,\n          description: \"Canvas link\",\n          title: \"Canvas link\",\n        },\n        message_context: {\n          type: SlackTypes.message_context,\n          description: \"Reference to the message sent\",\n          title: \"Reference to the message sent\",\n        },\n      },\n      required: [],\n    },\n  };\n  const actual = ShareCanvasInThread.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"ShareCanvasInThread can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_ShareCanvasInThread_slack_function\",\n    title: \"Test ShareCanvasInThread\",\n    description: \"This is a generated test to test ShareCanvasInThread\",\n  });\n  testWorkflow.addStep(ShareCanvasInThread, {\n    canvas_id: \"test\",\n    message_context: \"test\",\n    access_level: \"test\",\n  });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/share_canvas_in_thread\");\n  assertEquals(actual.inputs, {\n    canvas_id: \"test\",\n    message_context: \"test\",\n    access_level: \"test\",\n  });\n});\n\nDeno.test(\"All outputs of Slack function ShareCanvasInThread should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_ShareCanvasInThread_slack_function\",\n    title: \"Test ShareCanvasInThread\",\n    description: \"This is a generated test to test ShareCanvasInThread\",\n  });\n  const step = testWorkflow.addStep(ShareCanvasInThread, {\n    canvas_id: \"test\",\n    message_context: \"test\",\n    access_level: \"test\",\n  });\n  assertExists(step.outputs.canvas_id);\n  assertExists(step.outputs.message_context);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/share_canvas_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport ShareCanvas from \"./share_canvas.ts\";\n\nDeno.test(\"ShareCanvas generates valid FunctionManifest\", () => {\n  assertEquals(\n    ShareCanvas.definition.callback_id,\n    \"slack#/functions/share_canvas\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Share a canvas\",\n    input_parameters: {\n      properties: {\n        canvas_id: {\n          type: SlackTypes.canvas_id,\n          description: \"Search all canvases\",\n          title: \"Select a canvas\",\n        },\n        channel_ids: {\n          type: SchemaTypes.array,\n          description: \"Select channels\",\n          title: \"Select channels\",\n          items: { type: SlackTypes.channel_id },\n        },\n        user_ids: {\n          type: SchemaTypes.array,\n          description: \"Search users\",\n          title: \"Select people\",\n          items: { type: SlackTypes.user_id },\n        },\n        access_level: {\n          type: SchemaTypes.string,\n          description: \"Select an option\",\n          title: \"Select access level\",\n        },\n        message: {\n          type: SlackTypes.rich_text,\n          description: \"Add a message\",\n          title: \"Add a message\",\n        },\n      },\n      required: [\"canvas_id\", \"access_level\"],\n    },\n    output_parameters: {\n      properties: {\n        canvas_id: {\n          type: SlackTypes.canvas_id,\n          description: \"Canvas link\",\n          title: \"Canvas link\",\n        },\n      },\n      required: [],\n    },\n  };\n  const actual = ShareCanvas.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"ShareCanvas can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_ShareCanvas_slack_function\",\n    title: \"Test ShareCanvas\",\n    description: \"This is a generated test to test ShareCanvas\",\n  });\n  testWorkflow.addStep(ShareCanvas, {\n    canvas_id: \"test\",\n    access_level: \"test\",\n  });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/share_canvas\");\n  assertEquals(actual.inputs, { canvas_id: \"test\", access_level: \"test\" });\n});\n\nDeno.test(\"All outputs of Slack function ShareCanvas should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_ShareCanvas_slack_function\",\n    title: \"Test ShareCanvas\",\n    description: \"This is a generated test to test ShareCanvas\",\n  });\n  const step = testWorkflow.addStep(ShareCanvas, {\n    canvas_id: \"test\",\n    access_level: \"test\",\n  });\n  assertExists(step.outputs.canvas_id);\n});\n"
  },
  {
    "path": "src/schema/slack/functions/update_channel_topic.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport { DefineFunction } from \"../../../functions/mod.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\n\nexport default DefineFunction({\n  callback_id: \"slack#/functions/update_channel_topic\",\n  source_file: \"\",\n  title: \"Update the channel topic\",\n  description:\n    \"You or the people using the workflow must be members of the channel\",\n  input_parameters: {\n    properties: {\n      channel_id: {\n        type: SlackTypes.channel_id,\n        description: \"Search all channels\",\n        title: \"Select a channel\",\n      },\n      topic: {\n        type: SchemaTypes.string,\n        description: \"Enter a topic\",\n        title: \"Add a topic\",\n      },\n    },\n    required: [\"channel_id\", \"topic\"],\n  },\n  output_parameters: {\n    properties: {\n      topic: {\n        type: SchemaTypes.string,\n        description: \"Channel topic\",\n        title: \"Channel topic\",\n      },\n    },\n    required: [\"topic\"],\n  },\n});\n"
  },
  {
    "path": "src/schema/slack/functions/update_channel_topic_test.ts",
    "content": "/** This file was autogenerated. Follow the steps in src/schema/slack/functions/_scripts/README.md to rebuild **/\nimport {\n  assertEquals,\n  assertExists,\n  assertNotStrictEquals,\n} from \"../../../dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../workflows/mod.ts\";\nimport type { ManifestFunctionSchema } from \"../../../manifest/manifest_schema.ts\";\nimport SchemaTypes from \"../../schema_types.ts\";\nimport SlackTypes from \"../schema_types.ts\";\nimport UpdateChannelTopic from \"./update_channel_topic.ts\";\n\nDeno.test(\"UpdateChannelTopic generates valid FunctionManifest\", () => {\n  assertEquals(\n    UpdateChannelTopic.definition.callback_id,\n    \"slack#/functions/update_channel_topic\",\n  );\n  const expected: ManifestFunctionSchema = {\n    source_file: \"\",\n    title: \"Update the channel topic\",\n    description:\n      \"You or the people using the workflow must be members of the channel\",\n    input_parameters: {\n      properties: {\n        channel_id: {\n          type: SlackTypes.channel_id,\n          description: \"Search all channels\",\n          title: \"Select a channel\",\n        },\n        topic: {\n          type: SchemaTypes.string,\n          description: \"Enter a topic\",\n          title: \"Add a topic\",\n        },\n      },\n      required: [\"channel_id\", \"topic\"],\n    },\n    output_parameters: {\n      properties: {\n        topic: {\n          type: SchemaTypes.string,\n          description: \"Channel topic\",\n          title: \"Channel topic\",\n        },\n      },\n      required: [\"topic\"],\n    },\n  };\n  const actual = UpdateChannelTopic.export();\n\n  assertNotStrictEquals(actual, expected);\n});\n\nDeno.test(\"UpdateChannelTopic can be used as a Slack function in a workflow step\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_UpdateChannelTopic_slack_function\",\n    title: \"Test UpdateChannelTopic\",\n    description: \"This is a generated test to test UpdateChannelTopic\",\n  });\n  testWorkflow.addStep(UpdateChannelTopic, {\n    channel_id: \"test\",\n    topic: \"test\",\n  });\n  const actual = testWorkflow.steps[0].export();\n\n  assertEquals(actual.function_id, \"slack#/functions/update_channel_topic\");\n  assertEquals(actual.inputs, { channel_id: \"test\", topic: \"test\" });\n});\n\nDeno.test(\"All outputs of Slack function UpdateChannelTopic should exist\", () => {\n  const testWorkflow = DefineWorkflow({\n    callback_id: \"test_UpdateChannelTopic_slack_function\",\n    title: \"Test UpdateChannelTopic\",\n    description: \"This is a generated test to test UpdateChannelTopic\",\n  });\n  const step = testWorkflow.addStep(UpdateChannelTopic, {\n    channel_id: \"test\",\n    topic: \"test\",\n  });\n  assertExists(step.outputs.topic);\n});\n"
  },
  {
    "path": "src/schema/slack/mod.ts",
    "content": "import SlackTypes from \"./schema_types.ts\";\nimport SlackFunctions from \"./functions/mod.ts\";\n\nconst SlackSchema = {\n  types: SlackTypes,\n  functions: SlackFunctions,\n} as const;\n\nexport default SlackSchema;\n"
  },
  {
    "path": "src/schema/slack/schema_types.ts",
    "content": "import { SlackPrimitiveTypes } from \"./types/mod.ts\";\nimport { CustomSlackTypes } from \"./types/custom/mod.ts\";\n\nexport default { ...SlackPrimitiveTypes, ...CustomSlackTypes };\n"
  },
  {
    "path": "src/schema/slack/types/custom/custom_slack_types_test.ts",
    "content": "import { assertEquals } from \"../../../../dev_deps.ts\";\nimport { CustomSlackTypes, InternalSlackTypes } from \"./mod.ts\";\n\nDeno.test(\"Custom Slack Types are stringified correctly\", () => {\n  const { interactivity, user_context, message_context } = CustomSlackTypes;\n  const { form_input_object } = InternalSlackTypes;\n  assertEquals(`${interactivity}`, interactivity.id);\n  assertEquals(`${user_context}`, user_context.id);\n  assertEquals(`${message_context}`, message_context.id);\n  assertEquals(`${form_input_object}`, form_input_object.id);\n});\n"
  },
  {
    "path": "src/schema/slack/types/custom/form_input.ts",
    "content": "import SchemaTypes from \"../../../schema_types.ts\";\nimport { DefineType } from \"../../../../types/mod.ts\";\n\nconst FormInput = DefineType({\n  name: \"slack#/types/form_input_object\",\n  description: \"Input fields to be shown on the form\",\n  type: SchemaTypes.object,\n  properties: {\n    required: {\n      type: SchemaTypes.array,\n      items: {\n        type: SchemaTypes.string,\n      },\n    },\n    elements: {\n      type: SchemaTypes.array,\n      items: {\n        type: SchemaTypes.object,\n      },\n    },\n  },\n  required: [\"elements\"],\n});\n\nexport { FormInput };\n"
  },
  {
    "path": "src/schema/slack/types/custom/interactivity.ts",
    "content": "import SchemaTypes from \"../../../schema_types.ts\";\nimport { DefineType } from \"../../../../types/mod.ts\";\nimport { UserContextType } from \"./user_context.ts\";\n\nconst InteractivityType = DefineType({\n  name: \"slack#/types/interactivity\",\n  description: \"Context about a user interaction\",\n  type: SchemaTypes.object,\n  properties: {\n    interactivity_pointer: {\n      type: SchemaTypes.string,\n    },\n    interactor: {\n      type: UserContextType,\n    },\n  },\n  required: [\"interactivity_pointer\", \"interactor\"],\n});\n\nexport { InteractivityType };\n"
  },
  {
    "path": "src/schema/slack/types/custom/message_context.ts",
    "content": "import SchemaTypes from \"../../../schema_types.ts\";\nimport { SlackPrimitiveTypes } from \"../../types/mod.ts\";\nimport { DefineType } from \"../../../../types/mod.ts\";\n\nconst MessageContextType = DefineType({\n  name: \"slack#/types/message_context\",\n  type: SchemaTypes.object,\n  properties: {\n    message_ts: {\n      type: SlackPrimitiveTypes.message_ts,\n    },\n    user_id: {\n      type: SlackPrimitiveTypes.user_id,\n    },\n    channel_id: {\n      type: SlackPrimitiveTypes.channel_id,\n    },\n  },\n  required: [\"message_ts\"],\n});\n\nexport { MessageContextType };\n"
  },
  {
    "path": "src/schema/slack/types/custom/mod.ts",
    "content": "import { InteractivityType } from \"./interactivity.ts\";\nimport { UserContextType } from \"./user_context.ts\";\nimport { FormInput } from \"./form_input.ts\";\nimport { MessageContextType } from \"./message_context.ts\";\n\nexport const CustomSlackTypes = {\n  interactivity: InteractivityType,\n  user_context: UserContextType,\n  message_context: MessageContextType,\n};\n\nexport const InternalSlackTypes = {\n  form_input_object: FormInput,\n};\n"
  },
  {
    "path": "src/schema/slack/types/custom/user_context.ts",
    "content": "import SchemaTypes from \"../../../schema_types.ts\";\nimport { SlackPrimitiveTypes } from \"../../types/mod.ts\";\nimport { DefineType } from \"../../../../types/mod.ts\";\n\nconst UserContextType = DefineType({\n  name: \"slack#/types/user_context\",\n  type: SchemaTypes.object,\n  properties: {\n    id: {\n      type: SlackPrimitiveTypes.user_id,\n    },\n    secret: {\n      type: SchemaTypes.string,\n    },\n  },\n  required: [\"id\", \"secret\"],\n});\n\nexport { UserContextType };\n"
  },
  {
    "path": "src/schema/slack/types/mod.ts",
    "content": "const SlackPrimitiveTypes = {\n  blocks: \"slack#/types/blocks\",\n  canvas_id: \"slack#/types/canvas_id\",\n  canvas_template_id: \"slack#/types/canvas_template_id\",\n  channel_id: \"slack#/types/channel_id\",\n  date: \"slack#/types/date\",\n  expanded_rich_text: \"slack#/types/expanded_rich_text\",\n  file_id: \"slack#/types/file_id\",\n  // TODO: once List steps work in code, bring this back\n  // list_id: \"slack#/types/list_id\",\n  message_ts: \"slack#/types/message_ts\",\n  oauth2: \"slack#/types/credential/oauth2\",\n  rich_text: \"slack#/types/rich_text\",\n  salesforce_record_id: \"slack#/types/salesforce_record_id\",\n  team_id: \"slack#/types/team_id\",\n  timestamp: \"slack#/types/timestamp\",\n  user_id: \"slack#/types/user_id\",\n  usergroup_id: \"slack#/types/usergroup_id\",\n} as const;\n\nexport type ValidSlackPrimitiveTypes =\n  typeof SlackPrimitiveTypes[keyof typeof SlackPrimitiveTypes];\n\nexport { SlackPrimitiveTypes };\n"
  },
  {
    "path": "src/schema/types.ts",
    "content": "type BaseSchemaType = {\n  types?: {\n    [key: string]: string;\n  };\n};\n\n// Allow for sub-schema, i.e. schema.slack.types...\nexport type SchemaType = BaseSchemaType & {\n  [key: string]: BaseSchemaType;\n};\n"
  },
  {
    "path": "src/test_utils.ts",
    "content": "import { assertEquals } from \"./dev_deps.ts\";\n\n// deno-lint-ignore ban-types\ntype IsAny<T> = unknown extends T ? T extends {} ? T : never : never;\ntype NotAny<T> = T extends IsAny<T> ? never : T;\n\n/** @description Prevents a value of type `any` from being passed into `assertEquals` */\nexport const assertEqualsTypedValues = <T>(\n  actual: NotAny<T>,\n  expected: NotAny<T>,\n  msg?: string,\n): void => assertEquals<T>(actual, expected, msg);\n\n/**\n * Checks whether T includes U.\n */\nexport type CanBe<T, U> = Extract<T, U> extends never ? false : true;\n/**\n * Checks whether T can never include U.\n */\nexport type CannotBe<T, U> = Extract<T, U> extends never ? true : false;\n/**\n * Checks whether the provided type parameter allows to be undefined.\n * Useful for checking optionality.\n */\nexport type CanBeUndefined<T> = CanBe<T, undefined> extends true ? true\n  : false;\nexport type CannotBeUndefined<T> = CannotBe<T, undefined> extends true ? true\n  : false;\n"
  },
  {
    "path": "src/test_utils_test.ts",
    "content": "import { assertExists } from \"https://deno.land/std@0.152.0/testing/asserts.ts\";\nimport { assert, fail } from \"./dev_deps.ts\";\nimport {\n  assertEqualsTypedValues,\n  type CanBe,\n  type CanBeUndefined,\n  type CannotBe,\n  type CannotBeUndefined,\n} from \"./test_utils.ts\";\n\nDeno.test(\"CanBe\", async (t) => {\n  const T = {\n    test: \"\",\n    notIncluded: \"\",\n  };\n\n  const U = {\n    test: \"\",\n  };\n\n  await t.step(\n    `should be true when ${Object(T).name} includes ${Object(U).name}`,\n    () => {\n      assert<CanBe<typeof T, typeof U>>(true);\n    },\n  );\n\n  await t.step(\n    `should be false when ${Object(U).name} does not include ${Object(T).name}`,\n    () => {\n      assert<CanBe<typeof U, typeof T>>(false);\n    },\n  );\n});\n\nDeno.test(\"CannotBe\", async (t) => {\n  const T = {\n    test: \"\",\n    notIncluded: \"\",\n  };\n\n  const U = {\n    test: \"\",\n  };\n\n  await t.step(\n    `should be true when ${Object(U).name} can never include ${Object(T).name}`,\n    () => {\n      assert<CannotBe<typeof U, typeof T>>(true);\n    },\n  );\n\n  await t.step(\n    `should be false when ${Object(U).name} does not include ${Object(T).name}`,\n    () => {\n      assert<CannotBe<typeof T, typeof U>>(false);\n    },\n  );\n});\n\nDeno.test(\"CanBeUndefined\", async (t) => {\n  await t.step(\"should be true when variable can be undefined\", () => {\n    const canBeUndefined: string | undefined = undefined;\n    assert<CanBeUndefined<typeof canBeUndefined>>(true);\n  });\n\n  await t.step(\"should be false when variable cannot be undefined\", () => {\n    const cannotBeUndefined = \"\";\n    assert<CanBeUndefined<typeof cannotBeUndefined>>(false);\n  });\n});\n\nDeno.test(\"CannotBeUndefined\", async (t) => {\n  await t.step(\"should be true when variable can not be undefined\", () => {\n    const cannotBeUndefined = \"\";\n    assert<CannotBeUndefined<typeof cannotBeUndefined>>(true);\n  });\n\n  await t.step(\"should be false when variable cannot be undefined\", () => {\n    const canBeUndefined: string | undefined = undefined;\n    assert<CannotBeUndefined<typeof canBeUndefined>>(false);\n  });\n});\n\nDeno.test(assertEqualsTypedValues.name, async (t) => {\n  type T = {\n    output?: {\n      out: boolean;\n    };\n  };\n\n  await t.step(\n    \"should assert true when object follow the same type and value\",\n    () => {\n      const typedValue: T = {\n        output: {\n          out: true,\n        },\n      };\n      assertEqualsTypedValues(typedValue, {\n        output: {\n          out: true,\n        },\n      });\n    },\n  );\n\n  await t.step(\n    \"should raise error when objects do not follow the same type and value\",\n    () => {\n      const typedValue: T = {\n        output: {\n          out: true,\n        },\n      };\n\n      try {\n        assertEqualsTypedValues(typedValue, {\n          output: {\n            out: false,\n          },\n        });\n        fail(`${assertEqualsTypedValues} should have raised an exception`);\n      } catch (error) {\n        assertExists(error);\n      }\n    },\n  );\n});\n"
  },
  {
    "path": "src/type_utils.ts",
    "content": "/** @description Defines accepted recursion depth values */\nexport type RecursionDepthLevel = 0 | 1 | 2 | 3 | 4 | 5;\n\n/** @description Defines the maximum number of times we allow for recursion */\nexport type MaxRecursionDepth = 5;\n\n/** @description Increases the recursion depth value one at a time */\nexport type IncreaseDepth<Depth extends RecursionDepthLevel = 0> = Depth extends\n  0 ? 1\n  : Depth extends 1 ? 2\n  : Depth extends 2 ? 3\n  : Depth extends 3 ? 4\n  : Depth extends 4 ? 5\n  : Depth extends 5 ? MaxRecursionDepth\n  : MaxRecursionDepth;\n\n/** @description Provides typeahead for passed strict string values while allowing any other string to be passed as well */\n// deno-lint-ignore ban-types\nexport type LooseStringAutocomplete<T> = T | (string & {});\n"
  },
  {
    "path": "src/types/mod.ts",
    "content": "import type { SlackManifest } from \"../manifest/mod.ts\";\nimport type { ManifestCustomTypeSchema } from \"../manifest/manifest_schema.ts\";\nimport type { CustomTypeDefinition, ICustomType } from \"./types.ts\";\nimport { isTypedArray, isTypedObject } from \"../parameters/mod.ts\";\nimport type {\n  TypedObjectProperties,\n  TypedObjectRequiredProperties,\n} from \"../parameters/definition_types.ts\";\n\n// Helper that uses a type predicate for narrowing down to a Custom Type\nexport const isCustomType = (type: string | ICustomType): type is ICustomType =>\n  type instanceof CustomType;\n\nexport function DefineType<\n  Props extends TypedObjectProperties,\n  RequiredProps extends TypedObjectRequiredProperties<Props>,\n  Def extends CustomTypeDefinition<Props, RequiredProps>,\n>(\n  definition: Def,\n): CustomType<Props, RequiredProps, Def> {\n  return new CustomType(definition);\n}\n\nexport class CustomType<\n  Props extends TypedObjectProperties,\n  RequiredProps extends TypedObjectRequiredProperties<Props>,\n  Def extends CustomTypeDefinition<Props, RequiredProps>,\n> implements ICustomType {\n  public id: string;\n  public title: string | undefined;\n  public description: string | undefined;\n\n  constructor(\n    public definition: Def,\n  ) {\n    this.id = definition.name;\n    this.definition = definition;\n    this.description = definition.description;\n    this.title = definition.title;\n  }\n\n  private generateReferenceString() {\n    return this.id.includes(\"#/\") ? this.id : `#/types/${this.id}`;\n  }\n\n  toString() {\n    return this.generateReferenceString();\n  }\n  toJSON() {\n    return this.generateReferenceString();\n  }\n\n  registerParameterTypes(manifest: SlackManifest) {\n    if (isCustomType(this.definition.type)) {\n      manifest.registerType(this.definition.type);\n    } else if (isTypedArray(this.definition)) {\n      if (isCustomType(this.definition.items.type)) {\n        manifest.registerType(this.definition.items.type);\n      }\n    } else if (isTypedObject(this.definition)) {\n      Object.values(this.definition.properties)?.forEach((property) => {\n        if (isCustomType(property.type)) {\n          manifest.registerType(property.type);\n        }\n      });\n    }\n  }\n\n  export(): ManifestCustomTypeSchema {\n    // remove name from the definition we pass to the manifest\n    const { name: _n, ...definition } = this.definition;\n    // Using JSON.stringify to force any custom types into their string reference\n    return JSON.parse(JSON.stringify(definition));\n  }\n}\n"
  },
  {
    "path": "src/types/types.ts",
    "content": "import type {\n  ParameterDefinitionWithGenerics,\n  TypedObjectProperties,\n  TypedObjectRequiredProperties,\n} from \"../parameters/definition_types.ts\";\nimport type { SlackManifest } from \"../manifest/mod.ts\";\nimport type { ManifestCustomTypeSchema } from \"../manifest/manifest_schema.ts\";\n\nexport type CustomTypeDefinition<\n  Props extends TypedObjectProperties,\n  RequiredProps extends TypedObjectRequiredProperties<Props>,\n> =\n  & { name: string }\n  & ParameterDefinitionWithGenerics<Props, RequiredProps>;\n\nexport interface ICustomType<\n  Props extends TypedObjectProperties = TypedObjectProperties,\n  RequiredProps extends TypedObjectRequiredProperties<Props> =\n    TypedObjectRequiredProperties<Props>,\n> {\n  id: string;\n  definition: CustomTypeDefinition<Props, RequiredProps>;\n  description?: string;\n  registerParameterTypes: (manifest: SlackManifest) => void;\n  export(): ManifestCustomTypeSchema;\n}\n"
  },
  {
    "path": "src/types/types_test.ts",
    "content": "import { DefineType } from \"./mod.ts\";\nimport { assertEquals } from \"../dev_deps.ts\";\n\nDeno.test(\"DefineType test against id using the name parameter\", () => {\n  const Type = DefineType({\n    title: \"Title\",\n    description: \"Description\",\n    name: \"Name\",\n    type: \"string\",\n  });\n\n  assertEquals(Type.id, \"Name\");\n});\n\nDeno.test(\"DefineType test toString using the name parameter\", () => {\n  const Type = DefineType({\n    title: \"Title\",\n    description: \"Description\",\n    name: \"Name\",\n    type: \"string\",\n  });\n\n  const typeJson = Type.toJSON();\n\n  assertEquals(typeJson, \"#/types/Name\");\n});\n\nDeno.test(\"DefineType test export using the name parameter\", () => {\n  const Type = DefineType({\n    title: \"Title\",\n    description: \"Description\",\n    name: \"Name\",\n    type: \"string\",\n  });\n\n  const exportType = Type.export();\n\n  assertEquals(exportType, {\n    title: \"Title\",\n    description: \"Description\",\n    type: \"string\",\n  });\n});\n"
  },
  {
    "path": "src/types.ts",
    "content": "// ----------------------------------------------------------------------------\n// Invocation\n// ----------------------------------------------------------------------------\n\n// This is the schema received from the runtime\n// TODO: flush this out as we add support for other payloads\nexport type InvocationPayload<Body> = {\n  // TODO: type this out to handle multiple body types\n  body: Body;\n  context: {\n    bot_access_token: string;\n    variables: Record<string, string>;\n  };\n};\n\n// ----------------------------------------------------------------------------\n// Env\n// ----------------------------------------------------------------------------\nexport type Env = Record<string, string>;\n\nexport type { SlackAPIClient, Trigger } from \"./deps.ts\";\n"
  },
  {
    "path": "src/workflows/mod.ts",
    "content": "import type { SlackManifest } from \"../manifest/mod.ts\";\nimport type { ManifestWorkflowSchema } from \"../manifest/manifest_schema.ts\";\nimport type { ISlackFunctionDefinition } from \"../functions/types.ts\";\nimport type {\n  ParameterSetDefinition,\n  ParameterVariableType,\n  PossibleParameterKeys,\n} from \"../parameters/types.ts\";\nimport { ParameterVariable } from \"../parameters/mod.ts\";\nimport {\n  TypedWorkflowStepDefinition,\n  UntypedWorkflowStepDefinition,\n  type WorkflowStepDefinition,\n} from \"./workflow-step.ts\";\nimport type {\n  ISlackWorkflow,\n  SlackWorkflowDefinitionArgs,\n  WorkflowInputs,\n  WorkflowOutputs,\n  WorkflowStepInputs,\n} from \"./types.ts\";\n\nexport const DefineWorkflow = <\n  Inputs extends ParameterSetDefinition,\n  Outputs extends ParameterSetDefinition,\n  RequiredInputs extends PossibleParameterKeys<Inputs>,\n  RequiredOutputs extends PossibleParameterKeys<Outputs>,\n  CallbackID extends string,\n>(\n  definition: SlackWorkflowDefinitionArgs<\n    Inputs,\n    Outputs,\n    RequiredInputs,\n    RequiredOutputs,\n    CallbackID\n  >,\n) => {\n  return new WorkflowDefinition(definition);\n};\n\nexport class WorkflowDefinition<\n  Inputs extends ParameterSetDefinition,\n  Outputs extends ParameterSetDefinition,\n  RequiredInputs extends PossibleParameterKeys<Inputs>,\n  RequiredOutputs extends PossibleParameterKeys<Outputs>,\n  CallbackID extends string,\n> implements ISlackWorkflow {\n  public id: string;\n  public definition: SlackWorkflowDefinitionArgs<\n    Inputs,\n    Outputs,\n    RequiredInputs,\n    RequiredOutputs,\n    CallbackID\n  >;\n\n  public inputs: WorkflowInputs<\n    Inputs,\n    RequiredInputs\n  >;\n\n  public outputs: WorkflowOutputs<\n    Outputs,\n    RequiredOutputs\n  >;\n\n  steps: WorkflowStepDefinition[] = [];\n\n  constructor(\n    definition: SlackWorkflowDefinitionArgs<\n      Inputs,\n      Outputs,\n      RequiredInputs,\n      RequiredOutputs,\n      CallbackID\n    >,\n  ) {\n    this.id = definition.callback_id;\n    this.definition = definition;\n    this.inputs = {} as WorkflowInputs<\n      Inputs,\n      RequiredInputs\n    >;\n    this.outputs = {} as WorkflowOutputs<\n      Outputs,\n      RequiredOutputs\n    >;\n\n    for (\n      const [inputName, inputDefinition] of Object.entries(\n        definition.input_parameters?.properties\n          ? definition.input_parameters.properties\n          : {},\n      )\n    ) {\n      // deno-lint-ignore ban-ts-comment\n      //@ts-ignore\n      this.inputs[\n        inputName as keyof Inputs\n      ] = ParameterVariable(\n        \"inputs\",\n        inputName,\n        inputDefinition,\n      ) as ParameterVariableType<\n        Inputs[typeof inputName]\n      >;\n    }\n  }\n\n  // Supports adding a typed step where an ISlackFunctionDefinition reference is used, which produces typed inputs and outputs\n  // and the functionReference string can be rerived from that ISlackFunctionDefinition reference\n  // Important that this overload is 1st, as it's the more specific match, and preffered type if it matches\n  addStep<\n    StepInputs extends ParameterSetDefinition,\n    StepOutputs extends ParameterSetDefinition,\n    RequiredStepInputs extends PossibleParameterKeys<StepInputs>,\n    RequiredStepOutputs extends PossibleParameterKeys<StepOutputs>,\n  >(\n    slackFunction: ISlackFunctionDefinition<\n      StepInputs,\n      StepOutputs,\n      RequiredStepInputs,\n      RequiredStepOutputs\n    >,\n    inputs: WorkflowStepInputs<StepInputs, RequiredStepInputs>,\n  ): TypedWorkflowStepDefinition<\n    StepInputs,\n    StepOutputs,\n    RequiredStepInputs,\n    RequiredStepOutputs\n  >;\n\n  // Supports adding an untyped step by using a plain function reference string and input configuration\n  // This won't support any typed inputs or outputs on the step, but can be useful when adding a step w/o the type definition available\n  addStep(\n    functionReference: string,\n    // This is essentially an untyped step input configuration\n    inputs: WorkflowStepInputs<\n      ParameterSetDefinition,\n      PossibleParameterKeys<ParameterSetDefinition>\n    >,\n  ): UntypedWorkflowStepDefinition;\n\n  // The runtime implementation of addStep handles both signatures (straight function-reference & config, or ISlackFunctionDefinition)\n  addStep<\n    StepInputs extends ParameterSetDefinition,\n    StepOutputs extends ParameterSetDefinition,\n    RequiredStepInputs extends PossibleParameterKeys<StepInputs>,\n    RequiredStepOutputs extends PossibleParameterKeys<StepOutputs>,\n  >(\n    functionOrReference:\n      | string\n      | ISlackFunctionDefinition<\n        StepInputs,\n        StepOutputs,\n        RequiredStepInputs,\n        RequiredStepOutputs\n      >,\n    inputs: WorkflowStepInputs<StepInputs, RequiredStepInputs>,\n  ): WorkflowStepDefinition {\n    const stepId = `${this.steps.length}`;\n\n    if (typeof functionOrReference === \"string\") {\n      const newStep = new UntypedWorkflowStepDefinition(\n        stepId,\n        functionOrReference,\n        inputs,\n      );\n      this.steps.push(newStep);\n      return newStep;\n    }\n\n    const slackFunction = functionOrReference as ISlackFunctionDefinition<\n      StepInputs,\n      StepOutputs,\n      RequiredStepInputs,\n      RequiredStepOutputs\n    >;\n    const newStep = new TypedWorkflowStepDefinition(\n      stepId,\n      slackFunction,\n      inputs,\n    );\n\n    this.steps.push(newStep);\n\n    return newStep;\n  }\n\n  export(): ManifestWorkflowSchema {\n    return {\n      title: this.definition.title,\n      description: this.definition.description,\n      input_parameters: this.definition.input_parameters,\n      steps: this.steps.map((s) => s.export()),\n    };\n  }\n\n  registerStepFunctions(manifest: SlackManifest) {\n    this.steps.forEach((s) => s.registerFunction(manifest));\n  }\n\n  registerParameterTypes(manifest: SlackManifest) {\n    const { input_parameters: inputParams, output_parameters: outputParams } =\n      this.definition;\n    manifest.registerTypes(inputParams?.properties ?? {});\n    manifest.registerTypes(outputParams?.properties ?? {});\n  }\n\n  toJSON() {\n    return this.export();\n  }\n}\n"
  },
  {
    "path": "src/workflows/types.ts",
    "content": "import type { SlackManifest } from \"../manifest/mod.ts\";\nimport type { ManifestWorkflowSchema } from \"../manifest/manifest_schema.ts\";\nimport type {\n  ParameterPropertiesDefinition,\n  ParameterSetDefinition,\n  ParameterVariableType,\n  PossibleParameterKeys,\n} from \"../parameters/types.ts\";\n\nexport interface ISlackWorkflow {\n  id: string;\n  export: () => ManifestWorkflowSchema;\n  registerStepFunctions: (manifest: SlackManifest) => void;\n  registerParameterTypes: (manfest: SlackManifest) => void;\n}\n\nexport type SlackWorkflowDefinition<Definition> = Definition extends\n  SlackWorkflowDefinitionArgs<infer I, infer O, infer RI, infer RO, infer CB>\n  ? SlackWorkflowDefinitionArgs<I, O, RI, RO, CB>\n  : never;\n\nexport type SlackWorkflowDefinitionArgs<\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInputs extends PossibleParameterKeys<InputParameters>,\n  RequiredOutputs extends PossibleParameterKeys<OutputParameters>,\n  CallbackID extends string,\n> = {\n  callback_id: CallbackID;\n  title: string;\n  description?: string;\n  \"input_parameters\"?: ParameterPropertiesDefinition<\n    InputParameters,\n    RequiredInputs\n  >;\n  \"output_parameters\"?: ParameterPropertiesDefinition<\n    OutputParameters,\n    RequiredOutputs\n  >;\n};\n\nexport type WorkflowInputs<\n  Params extends ParameterSetDefinition,\n  RequiredParams extends PossibleParameterKeys<Params>,\n> = WorkflowParameterReferences<Params, RequiredParams>;\n\nexport type WorkflowOutputs<\n  Params extends ParameterSetDefinition,\n  RequiredParams extends PossibleParameterKeys<Params>,\n> = WorkflowParameterReferences<Params, RequiredParams>;\n\nexport type WorkflowStepOutputs<\n  Params extends ParameterSetDefinition,\n  RequiredParams extends PossibleParameterKeys<Params>,\n> = WorkflowParameterReferences<Params, RequiredParams>;\n\ntype WorkflowParameterReferences<\n  Params extends ParameterSetDefinition,\n  Required extends PossibleParameterKeys<Params>,\n> =\n  & {\n    [name in Required[number]]: ParameterVariableType<Params[name]>;\n  }\n  & {\n    [name in keyof Params]?: ParameterVariableType<Params[name]>;\n  };\n\n// Workflow Step inputs are different than workflow inputs/outputs or workflow step outputs.\n// They are purely the config values for the step, and not definitions that can be referenced\n// as variables like you can with workflow inputs and workflow step outputs\nexport type WorkflowStepInputs<\n  InputParameters extends ParameterSetDefinition,\n  RequiredInputs extends PossibleParameterKeys<InputParameters>,\n> =\n  & {\n    // deno-lint-ignore no-explicit-any\n    [k in RequiredInputs[number]]: any;\n  }\n  & {\n    // deno-lint-ignore no-explicit-any\n    [k in keyof InputParameters]?: any;\n  };\n"
  },
  {
    "path": "src/workflows/workflow-step.ts",
    "content": "import type { SlackManifest } from \"../manifest/mod.ts\";\nimport type {\n  ManifestFunction,\n  ManifestWorkflowStepSchema,\n} from \"../manifest/manifest_schema.ts\";\nimport type { ISlackFunctionDefinition } from \"../functions/types.ts\";\nimport {\n  CreateUntypedObjectParameterVariable,\n  ParameterVariable,\n} from \"../parameters/mod.ts\";\nimport type {\n  ParameterSetDefinition,\n  ParameterVariableType,\n  PossibleParameterKeys,\n} from \"../parameters/types.ts\";\nimport type { WorkflowStepInputs, WorkflowStepOutputs } from \"./types.ts\";\n\nconst localFnPrefix = \"#/functions/\";\n\nexport type WorkflowStepDefinition =\n  // deno-lint-ignore no-explicit-any\n  | TypedWorkflowStepDefinition<any, any, any, any>\n  | UntypedWorkflowStepDefinition;\n\nabstract class BaseWorkflowStepDefinition {\n  protected stepId: string;\n\n  protected functionReference: string;\n\n  protected inputs: Record<string, unknown>;\n\n  constructor(\n    stepId: string,\n    functionReference: string,\n    inputs: Record<string, unknown>,\n  ) {\n    this.stepId = stepId;\n    // ensures the function reference is a full path - local functions will only be passing in the function callback id\n    this.functionReference = functionReference.includes(\"#/\")\n      ? functionReference\n      : `${localFnPrefix}${functionReference}`;\n    this.inputs = inputs;\n  }\n\n  templatizeInputs() {\n    const templatizedInputs: ManifestWorkflowStepSchema[\"inputs\"] =\n      {} as ManifestWorkflowStepSchema[\"inputs\"];\n\n    for (const [inputName, inputValue] of Object.entries(this.inputs)) {\n      try {\n        templatizedInputs[inputName] = JSON.parse(JSON.stringify(inputValue));\n      } catch {\n        templatizedInputs[inputName] = undefined;\n      }\n    }\n\n    return templatizedInputs;\n  }\n\n  export(): ManifestWorkflowStepSchema {\n    return {\n      id: this.stepId,\n      function_id: this.functionReference,\n      inputs: this.templatizeInputs(),\n    };\n  }\n\n  toJSON() {\n    return this.export();\n  }\n\n  registerFunction(_manifest: SlackManifest) {\n    // default is a noop, only steps using a function definition will register themselves on the manifest\n  }\n\n  protected isLocalFunctionReference(): boolean {\n    return this.functionReference.startsWith(localFnPrefix);\n  }\n}\n\nexport class TypedWorkflowStepDefinition<\n  InputParameters extends ParameterSetDefinition,\n  OutputParameters extends ParameterSetDefinition,\n  RequiredInputs extends PossibleParameterKeys<InputParameters>,\n  RequiredOutputs extends PossibleParameterKeys<OutputParameters>,\n> extends BaseWorkflowStepDefinition {\n  public definition: ISlackFunctionDefinition<\n    InputParameters,\n    OutputParameters,\n    RequiredInputs,\n    RequiredOutputs\n  >;\n\n  public outputs: WorkflowStepOutputs<\n    OutputParameters,\n    RequiredOutputs\n  >;\n\n  constructor(\n    stepId: string,\n    slackFunction: ISlackFunctionDefinition<\n      InputParameters,\n      OutputParameters,\n      RequiredInputs,\n      RequiredOutputs\n    >,\n    inputs: WorkflowStepInputs<InputParameters, RequiredInputs>,\n  ) {\n    super(stepId, slackFunction.id, inputs);\n\n    this.definition = slackFunction;\n\n    this.outputs = {} as WorkflowStepOutputs<\n      OutputParameters,\n      RequiredOutputs\n    >;\n\n    // Setup step outputs for use in input template expressions\n    for (\n      const [outputName, outputDefinition] of Object.entries(\n        slackFunction?.definition?.output_parameters?.properties ?? {},\n      )\n    ) {\n      // deno-lint-ignore ban-ts-comment\n      //@ts-ignore\n      this.outputs[\n        outputName as keyof OutputParameters\n      ] = ParameterVariable(\n        `steps.${this.stepId}`,\n        outputName,\n        outputDefinition,\n      ) as ParameterVariableType<\n        OutputParameters[typeof outputName]\n      >;\n    }\n  }\n\n  override registerFunction(manifest: SlackManifest) {\n    if (this.isLocalFunctionReference()) {\n      manifest.registerFunction(this.definition as ManifestFunction);\n    }\n  }\n}\n\nexport class UntypedWorkflowStepDefinition extends BaseWorkflowStepDefinition {\n  public outputs: ReturnType<typeof CreateUntypedObjectParameterVariable>;\n\n  constructor(\n    stepId: string,\n    functionReference: string,\n    // deno-lint-ignore no-explicit-any\n    inputs: WorkflowStepInputs<any, any>,\n  ) {\n    super(stepId, functionReference, inputs);\n\n    this.outputs = CreateUntypedObjectParameterVariable(\n      `steps.${stepId}`,\n      \"\",\n    );\n  }\n}\n"
  },
  {
    "path": "tests/integration/functions/runtime_context/array_parameters_test.ts",
    "content": "import { assert, assertEquals, type IsAny } from \"../../../../src/dev_deps.ts\";\nimport type {\n  CanBeUndefined,\n  CannotBeUndefined,\n} from \"../../../../src/test_utils.ts\";\nimport { DefineFunction, DefineType, Schema } from \"../../../../src/mod.ts\";\nimport { DefineProperty } from \"../../../../src/parameters/define_property.ts\";\nimport type {\n  EnrichedSlackFunctionHandler,\n} from \"../../../../src/functions/types.ts\";\nimport { SlackFunctionTester } from \"../../../../src/functions/tester/mod.ts\";\nimport { assertEqualsTypedValues } from \"../../../../src/test_utils.ts\";\n\n/**\n * Custom function handler tests, exercising Array inputs/outputs\n */\nDeno.test(\"Custom function using an input of Typed Arrays of Custom Types of DefineProperty-wrapped typed objects should honor required and optional properties and allow for referencing additional properties\", () => {\n  const obj = DefineProperty({\n    type: Schema.types.object,\n    properties: {\n      aString: {\n        type: Schema.types.string,\n      },\n      anOptionalString: {\n        type: Schema.types.string,\n      },\n    },\n    required: [\"aString\"],\n    additionalProperties: true,\n  });\n  const customType = DefineType({\n    name: \"customType\",\n    ...obj,\n  });\n\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        arr: {\n          type: Schema.types.array,\n          items: {\n            type: customType,\n          },\n        },\n      },\n      required: [\"arr\"],\n    },\n  });\n\n  const sharedInputs = {\n    arr: [{ aString: \"hi\" }, { aString: \"hello\", anOptionalString: \"goodbye\" }],\n  };\n\n  const handler: EnrichedSlackFunctionHandler<typeof TestFunction.definition> =\n    (\n      { inputs },\n    ) => {\n      const { arr } = inputs;\n      const first = arr[0];\n      const second = arr[1];\n      assert<CannotBeUndefined<typeof first.aString>>(true);\n      assert<CanBeUndefined<typeof first.anOptionalString>>(true);\n      assert<CannotBeUndefined<typeof second.aString>>(true);\n      assert<CanBeUndefined<typeof second.anOptionalString>>(true);\n      assert<IsAny<typeof first.somethingRandom>>(true);\n      assert<IsAny<typeof second.andNowForSomethingCompletelyDifferent>>(true);\n      assertEqualsTypedValues(\n        first.aString,\n        sharedInputs.arr[0].aString,\n      );\n      assertEqualsTypedValues(\n        first.anOptionalString,\n        undefined,\n      );\n      assertEqualsTypedValues(\n        second.aString,\n        sharedInputs.arr[1].aString,\n      );\n      assertEqualsTypedValues(\n        second.anOptionalString,\n        sharedInputs.arr[1].anOptionalString,\n      );\n\n      return {\n        outputs: inputs,\n      };\n    };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n  handler(createContext({ inputs: sharedInputs }));\n});\n\nDeno.test(\"Custom function using an input of Typed Arrays of Custom Types of DefineProperty-wrapped typed objects should honor additionalProperties=false\", () => {\n  const obj = DefineProperty({\n    type: Schema.types.object,\n    properties: {\n      aString: {\n        type: Schema.types.string,\n      },\n      anOptionalString: {\n        type: Schema.types.string,\n      },\n    },\n    required: [\"aString\"],\n    additionalProperties: false,\n  });\n  const customType = DefineType({\n    name: \"customType\",\n    ...obj,\n  });\n\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        arr: {\n          type: Schema.types.array,\n          items: {\n            type: customType,\n          },\n        },\n      },\n      required: [\"arr\"],\n    },\n  });\n\n  const sharedInputs = {\n    arr: [{ aString: \"hi\" }, { aString: \"hello\", anOptionalString: \"goodbye\" }],\n  };\n\n  const handler: EnrichedSlackFunctionHandler<typeof TestFunction.definition> =\n    (\n      { inputs },\n    ) => {\n      const { arr } = inputs;\n      const first = arr[0];\n      const second = arr[1];\n      assert<CannotBeUndefined<typeof first.aString>>(true);\n      assert<CanBeUndefined<typeof first.anOptionalString>>(true);\n      assert<CannotBeUndefined<typeof second.aString>>(true);\n      assert<CanBeUndefined<typeof second.anOptionalString>>(true);\n      assertEqualsTypedValues(\n        first.aString,\n        sharedInputs.arr[0].aString,\n      );\n      assertEqualsTypedValues(\n        first.anOptionalString,\n        undefined,\n      );\n      assertEqualsTypedValues(\n        second.aString,\n        sharedInputs.arr[1].aString,\n      );\n      assertEqualsTypedValues(\n        second.anOptionalString,\n        sharedInputs.arr[1].anOptionalString,\n      );\n      // @ts-expect-error batman cannot exist\n      assertEquals(first.batman, undefined);\n      // @ts-expect-error robin cannot exist\n      assertEquals(second.robin, undefined);\n\n      return {\n        outputs: inputs,\n      };\n    };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n  handler(createContext({ inputs: sharedInputs }));\n});\n\nDeno.test(\"Custom function using an input of Typed Arrays of DefineProperty-wrapped typed objects should honor required and optional properties and allow for referencing additional properties\", () => {\n  const obj = DefineProperty({\n    type: Schema.types.object,\n    properties: {\n      aString: {\n        type: Schema.types.string,\n      },\n      anOptionalString: {\n        type: Schema.types.string,\n      },\n    },\n    required: [\"aString\"],\n    additionalProperties: true,\n  });\n\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        arr: {\n          type: Schema.types.array,\n          items: obj,\n        },\n      },\n      required: [\"arr\"],\n    },\n  });\n\n  const sharedInputs = {\n    arr: [{ aString: \"hi\" }, { aString: \"hello\", anOptionalString: \"goodbye\" }],\n  };\n\n  const handler: EnrichedSlackFunctionHandler<typeof TestFunction.definition> =\n    (\n      { inputs },\n    ) => {\n      const { arr } = inputs;\n      const first = arr[0];\n      const second = arr[1];\n      assert<CannotBeUndefined<typeof first.aString>>(true);\n      assert<CanBeUndefined<typeof first.anOptionalString>>(true);\n      assert<CannotBeUndefined<typeof second.aString>>(true);\n      assert<CanBeUndefined<typeof second.anOptionalString>>(true);\n      assert<IsAny<typeof first.somethingRandom>>(true);\n      assert<IsAny<typeof second.andNowForSomethingCompletelyDifferent>>(true);\n      assertEqualsTypedValues(\n        first.aString,\n        sharedInputs.arr[0].aString,\n      );\n      assertEqualsTypedValues(\n        first.anOptionalString,\n        undefined,\n      );\n      assertEqualsTypedValues(\n        second.aString,\n        sharedInputs.arr[1].aString,\n      );\n      assertEqualsTypedValues(\n        second.anOptionalString,\n        sharedInputs.arr[1].anOptionalString,\n      );\n\n      return {\n        outputs: inputs,\n      };\n    };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n  handler(createContext({ inputs: sharedInputs }));\n});\n\nDeno.test(\"Custom function using an input of Typed Arrays of DefineProperty-wrapped typed objects should honor additionalProperties=false\", () => {\n  const obj = DefineProperty({\n    type: Schema.types.object,\n    properties: {\n      aString: {\n        type: Schema.types.string,\n      },\n      anOptionalString: {\n        type: Schema.types.string,\n      },\n    },\n    required: [\"aString\"],\n    additionalProperties: false,\n  });\n\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        arr: {\n          type: Schema.types.array,\n          items: obj,\n        },\n      },\n      required: [\"arr\"],\n    },\n  });\n\n  const sharedInputs = {\n    arr: [{ aString: \"hi\" }, { aString: \"hello\", anOptionalString: \"goodbye\" }],\n  };\n\n  const handler: EnrichedSlackFunctionHandler<typeof TestFunction.definition> =\n    (\n      { inputs },\n    ) => {\n      const { arr } = inputs;\n      const first = arr[0];\n      const second = arr[1];\n      assert<CannotBeUndefined<typeof first.aString>>(true);\n      assert<CanBeUndefined<typeof first.anOptionalString>>(true);\n      assert<CannotBeUndefined<typeof second.aString>>(true);\n      assert<CanBeUndefined<typeof second.anOptionalString>>(true);\n      assertEqualsTypedValues(\n        first.aString,\n        sharedInputs.arr[0].aString,\n      );\n      assertEqualsTypedValues(\n        first.anOptionalString,\n        undefined,\n      );\n      assertEqualsTypedValues(\n        second.aString,\n        sharedInputs.arr[1].aString,\n      );\n      assertEqualsTypedValues(\n        second.anOptionalString,\n        sharedInputs.arr[1].anOptionalString,\n      );\n      // @ts-expect-error batman cannot exist\n      assertEquals(first.batman, undefined);\n      // @ts-expect-error robin cannot exist\n      assertEquals(second.robin, undefined);\n\n      return {\n        outputs: inputs,\n      };\n    };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n  handler(createContext({ inputs: sharedInputs }));\n});\n\nDeno.test(\"Custom function using untyped Arrays and typed arrays of strings\", () => {\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        anUntypedArray: {\n          type: Schema.types.array,\n        },\n        aTypedArray: {\n          type: Schema.types.array,\n          items: {\n            type: \"string\",\n          },\n        },\n      },\n      required: [\"aTypedArray\", \"anUntypedArray\"],\n    },\n    output_parameters: {\n      properties: {\n        anUntypedArray: {\n          type: Schema.types.array,\n        },\n        aTypedArray: {\n          type: Schema.types.array,\n          items: {\n            type: \"string\",\n          },\n        },\n      },\n      required: [\"aTypedArray\", \"anUntypedArray\"],\n    },\n  });\n\n  const sharedInputs = {\n    aTypedArray: [\"hello\"],\n    anUntypedArray: [1, \"goodbye\"],\n  };\n\n  const handler: EnrichedSlackFunctionHandler<typeof TestFunction.definition> =\n    (\n      { inputs },\n    ) => {\n      const { aTypedArray, anUntypedArray } = inputs;\n      assert<IsAny<typeof anUntypedArray[0]>>(true);\n      assert<IsAny<typeof aTypedArray[0]>>(false);\n\n      assert<CannotBeUndefined<typeof aTypedArray>>(true);\n      assert<CannotBeUndefined<typeof anUntypedArray>>(true);\n\n      // These tests are a little weird, could technically be undefined if these arrays are empty\n      assert<CannotBeUndefined<typeof aTypedArray[0]>>(true);\n      assert<CannotBeUndefined<typeof aTypedArray[0]>>(true);\n\n      return {\n        outputs: inputs,\n      };\n    };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n\n  const result = handler(createContext({ inputs: sharedInputs }));\n  assertEqualsTypedValues(sharedInputs, result.outputs);\n});\n/**\n * TODO: the next two particular tests lead to the array items being typed as `any`\n\nDeno.test(\"Custom function using Typed Arrays of Custom Types of unwrapped typed objects should honor required and optional properties\", () => {\n  const obj = {\n    type: Schema.types.object,\n    properties: {\n      aString: {\n        type: Schema.types.string,\n      },\n      anOptionalString: {\n        type: Schema.types.string,\n      },\n    },\n    required: [\"aString\"],\n    additionalProperties: true,\n  };\n  const customType = DefineType({\n    name: \"customType\",\n    ...obj,\n  });\n\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        arr: {\n          type: Schema.types.array,\n          items: {\n            type: customType,\n          },\n        },\n      },\n      required: [\"arr\"],\n    },\n  });\n\n  const sharedInputs = {\n    arr: [{ aString: \"hi\" }, { aString: \"hello\", anOptionalString: \"goodbye\" }],\n  };\n\n  const handler: EnrichedSlackFunctionHandler<typeof TestFunction.definition> =\n    (\n      { inputs },\n    ) => {\n      const { arr } = inputs;\n      const first = arr[0];\n      const second = arr[1];\n      assert<CannotBeUndefined<typeof first.aString>>(true);\n      assert<CanBeUndefined<typeof first.anOptionalString>>(true);\n      assert<CannotBeUndefined<typeof second.aString>>(true);\n      assert<CanBeUndefined<typeof second.anOptionalString>>(true);\n      assertEqualsTypedValues(\n        first.aString,\n        sharedInputs.arr[0].aString,\n      );\n      assertEqualsTypedValues(\n        first.anOptionalString,\n        undefined,\n      );\n      assertEqualsTypedValues(\n        second.aString,\n        sharedInputs.arr[1].aString,\n      );\n      assertEqualsTypedValues(\n        second.anOptionalString,\n        sharedInputs.arr[1].anOptionalString,\n      );\n\n      return {\n        outputs: inputs,\n      };\n    };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n  handler(createContext({ inputs: sharedInputs }));\n});\nDeno.test(\"Custom function using Typed Arrays of unwrapped typed objects should honor required and optional properties\", () => {\n  const obj = {\n    type: Schema.types.object,\n    properties: {\n      aString: {\n        type: Schema.types.string,\n      },\n      anOptionalString: {\n        type: Schema.types.string,\n      },\n    },\n    required: [\"aString\"],\n    additionalProperties: true,\n  };\n\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        arr: {\n          type: Schema.types.array,\n          items: obj,\n        },\n      },\n      required: [\"arr\"],\n    },\n  });\n\n  const sharedInputs = {\n    arr: [{ aString: \"hi\" }, { aString: \"hello\", anOptionalString: \"goodbye\" }],\n  };\n\n  const handler: EnrichedSlackFunctionHandler<typeof TestFunction.definition> =\n    (\n      { inputs },\n    ) => {\n      const { arr } = inputs;\n      const first = arr[0];\n      const second = arr[1];\n      assert<CannotBeUndefined<typeof first.aString>>(true);\n      assert<CanBeUndefined<typeof first.anOptionalString>>(true);\n      assert<CannotBeUndefined<typeof second.aString>>(true);\n      assert<CanBeUndefined<typeof second.anOptionalString>>(true);\n      assertEqualsTypedValues(\n        first.aString,\n        sharedInputs.arr[0].aString,\n      );\n      assertEqualsTypedValues(\n        first.anOptionalString,\n        undefined,\n      );\n      assertEqualsTypedValues(\n        second.aString,\n        sharedInputs.arr[1].aString,\n      );\n      assertEqualsTypedValues(\n        second.anOptionalString,\n        sharedInputs.arr[1].anOptionalString,\n      );\n\n      return {\n        outputs: inputs,\n      };\n    };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n  handler(createContext({ inputs: sharedInputs }));\n});\n*/\n"
  },
  {
    "path": "tests/integration/functions/runtime_context/custom_type_parameters_test.ts",
    "content": "import { assert, type IsAny, type IsExact } from \"../../../../src/dev_deps.ts\";\nimport type {\n  CanBe,\n  CanBeUndefined,\n  CannotBeUndefined,\n} from \"../../../../src/test_utils.ts\";\nimport { DefineFunction, DefineType, Schema } from \"../../../../src/mod.ts\";\nimport type {\n  EnrichedSlackFunctionHandler,\n} from \"../../../../src/functions/types.ts\";\nimport { SlackFunctionTester } from \"../../../../src/functions/tester/mod.ts\";\nimport { assertEqualsTypedValues } from \"../../../../src/test_utils.ts\";\nimport { InternalSlackTypes } from \"../../../../src/schema/slack/types/custom/mod.ts\";\n\n/**\n * Custom function handler tests, exercising Custom Type inputs/outputs, including Slack/internal custom Slack types\n */\n\nDeno.test(\"Custom function using Slack's FormInput internal Custom Type input should provide correct typedobject typing in a function handler context\", () => {\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        formInput: {\n          type: InternalSlackTypes.form_input_object,\n        },\n      },\n      required: [\"formInput\"],\n    },\n    output_parameters: {\n      properties: {\n        formInput: {\n          type: InternalSlackTypes.form_input_object,\n        },\n      },\n      required: [\"formInput\"],\n    },\n  });\n\n  const sharedInputs = {\n    formInput: {\n      required: [],\n      elements: [],\n    },\n  };\n\n  const validHandler: EnrichedSlackFunctionHandler<\n    typeof TestFunction.definition\n  > = (\n    { inputs },\n  ) => {\n    const { formInput } = inputs;\n\n    assert<CanBeUndefined<typeof formInput.required>>(\n      true,\n    );\n    assert<CanBe<typeof formInput.required, string[]>>(true);\n\n    assert<CannotBeUndefined<typeof formInput.elements>>(\n      true,\n    );\n    // deno-lint-ignore no-explicit-any\n    assert<CanBe<typeof formInput.elements, any[]>>(true);\n\n    return {\n      outputs: inputs,\n    };\n  };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n\n  validHandler(createContext({ inputs: sharedInputs }));\n});\n\nDeno.test(\"Custom function using Slack's message-context Custom Type input should provide correct typedobject typing in a function handler context\", () => {\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        msgContext: {\n          type: Schema.slack.types.message_context,\n        },\n      },\n      required: [\"msgContext\"],\n    },\n    output_parameters: {\n      properties: {\n        msgContext: {\n          type: Schema.slack.types.message_context,\n        },\n      },\n      required: [\"msgContext\"],\n    },\n  });\n\n  const sharedInputs = {\n    msgContext: {\n      message_ts: \"1234.567\",\n      channel_id: \"C12345\",\n    },\n  };\n\n  const validHandler: EnrichedSlackFunctionHandler<\n    typeof TestFunction.definition\n  > = (\n    { inputs },\n  ) => {\n    const { msgContext } = inputs;\n\n    // channel_id sub-property\n    assert<CanBeUndefined<typeof msgContext.channel_id>>(\n      true,\n    );\n    assert<CanBe<typeof msgContext.channel_id, string>>(true);\n    // user_id sub-property\n    assert<CanBeUndefined<typeof msgContext.user_id>>(\n      true,\n    );\n    assert<CanBe<typeof msgContext.user_id, string>>(true);\n    // message_ts sub-property\n    assert<CannotBeUndefined<typeof msgContext.message_ts>>(\n      true,\n    );\n    assert<IsExact<typeof msgContext.message_ts, string>>(true);\n\n    return {\n      outputs: inputs,\n    };\n  };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n\n  validHandler(createContext({ inputs: sharedInputs }));\n});\n\nDeno.test(\"Custom function using a Custom Type input for an unwrapped typedobject with mixed required/optional properties should provide correct typedobject typing in a function handler context\", () => {\n  const myType = DefineType({\n    name: \"custom\",\n    type: Schema.types.object,\n    properties: {\n      required_property: { type: \"string\" },\n      optional_property: { type: \"string\" },\n    },\n    required: [\"required_property\"],\n  });\n\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        custom_type: {\n          type: myType,\n        },\n      },\n      required: [\"custom_type\"],\n    },\n    output_parameters: {\n      properties: {\n        custom_type: {\n          type: myType,\n        },\n      },\n      required: [\"custom_type\"],\n    },\n  });\n\n  const sharedInputs = {\n    custom_type: {\n      required_property: \"i am a necessity\",\n    },\n  };\n\n  const validHandler: EnrichedSlackFunctionHandler<\n    typeof TestFunction.definition\n  > = (\n    { inputs },\n  ) => {\n    const { custom_type } = inputs;\n\n    assert<IsAny<typeof custom_type>>(false);\n    assert<IsExact<typeof custom_type.required_property, string>>(true);\n    assert<CanBeUndefined<typeof custom_type.optional_property>>(true);\n    assert<CanBe<typeof custom_type.optional_property, string>>(true);\n\n    return {\n      outputs: inputs,\n    };\n  };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n\n  const result = validHandler(createContext({ inputs: sharedInputs }));\n  assertEqualsTypedValues(sharedInputs, result.outputs);\n});\n\nDeno.test(\"Custom function using a Custom Type input for an unwrapped typedobject with mixed required/optional properties should complain if required output not provided\", () => {\n  const myType = DefineType({\n    name: \"custom\",\n    type: Schema.types.object,\n    properties: {\n      required_property: { type: \"string\" },\n      optional_property: { type: \"string\" },\n    },\n    required: [\"required_property\"],\n  });\n\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        custom_type: {\n          type: myType,\n        },\n      },\n      required: [\"custom_type\"],\n    },\n    output_parameters: {\n      properties: {\n        custom_type: {\n          type: myType,\n        },\n      },\n      required: [\"custom_type\"],\n    },\n  });\n\n  // @ts-expect-error Type error if required property isn't returned\n  const _invalidHandler: EnrichedSlackFunctionHandler<\n    typeof TestFunction.definition\n  > = () => {\n    return {\n      outputs: {\n        custom_type: {\n          optional_property: \"im useless\",\n        },\n      },\n    };\n  };\n});\n\nDeno.test(\"Custom function using a Custom Type input for an unwrapped typedobject with additionalProperties=undefined should allow for referencing additional properties\", () => {\n  const myType = DefineType({\n    name: \"custom\",\n    type: Schema.types.object,\n    properties: {\n      required_property: { type: \"string\" },\n      optional_property: { type: \"string\" },\n    },\n    required: [\"required_property\"],\n  });\n\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        custom_type: {\n          type: myType,\n        },\n      },\n      required: [\"custom_type\"],\n    },\n    output_parameters: {\n      properties: {\n        custom_type: {\n          type: myType,\n        },\n      },\n      required: [\"custom_type\"],\n    },\n  });\n\n  const sharedInputs = {\n    custom_type: {\n      required_property: \"i am a necessity\",\n    },\n  };\n\n  const validHandler: EnrichedSlackFunctionHandler<\n    typeof TestFunction.definition\n  > = (\n    { inputs },\n  ) => {\n    const { custom_type } = inputs;\n\n    assert<IsAny<typeof custom_type>>(false);\n    assert<IsAny<typeof custom_type.something>>(true);\n\n    return {\n      outputs: inputs,\n    };\n  };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n\n  const result = validHandler(createContext({ inputs: sharedInputs }));\n  assertEqualsTypedValues(sharedInputs, result.outputs);\n});\n\nDeno.test(\"Custom function using a Custom Type input for an unwrapped typedobject with additionalProperties=true should allow for referencing additional properties\", () => {\n  const myType = DefineType({\n    name: \"custom\",\n    type: Schema.types.object,\n    properties: {\n      required_property: { type: \"string\" },\n      optional_property: { type: \"string\" },\n    },\n    required: [\"required_property\"],\n    additionalProperties: true,\n  });\n\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        custom_type: {\n          type: myType,\n        },\n      },\n      required: [\"custom_type\"],\n    },\n    output_parameters: {\n      properties: {\n        custom_type: {\n          type: myType,\n        },\n      },\n      required: [\"custom_type\"],\n    },\n  });\n\n  const sharedInputs = {\n    custom_type: {\n      required_property: \"i am a necessity\",\n    },\n  };\n\n  const validHandler: EnrichedSlackFunctionHandler<\n    typeof TestFunction.definition\n  > = (\n    { inputs },\n  ) => {\n    const { custom_type } = inputs;\n\n    assert<IsAny<typeof custom_type>>(false);\n    assert<IsAny<typeof custom_type.something>>(true);\n\n    return {\n      outputs: inputs,\n    };\n  };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n\n  const result = validHandler(createContext({ inputs: sharedInputs }));\n  assertEqualsTypedValues(sharedInputs, result.outputs);\n});\n\nDeno.test(\"Custom function using a Custom Type input for an unwrapped typedobject with additionalProperties=false should prevent referencing additional properties\", () => {\n  const myType = DefineType({\n    name: \"custom\",\n    type: Schema.types.object,\n    properties: {\n      required_property: { type: \"string\" },\n      optional_property: { type: \"string\" },\n    },\n    required: [\"required_property\"],\n    additionalProperties: false,\n  });\n\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        custom_type: {\n          type: myType,\n        },\n      },\n      required: [\"custom_type\"],\n    },\n    output_parameters: {\n      properties: {\n        custom_type: {\n          type: myType,\n        },\n      },\n      required: [\"custom_type\"],\n    },\n  });\n\n  const sharedInputs = {\n    custom_type: {\n      required_property: \"i am a necessity\",\n    },\n  };\n\n  const validHandler: EnrichedSlackFunctionHandler<\n    typeof TestFunction.definition\n  > = (\n    { inputs },\n  ) => {\n    const { custom_type } = inputs;\n\n    assert<IsAny<typeof custom_type>>(false);\n    // @ts-expect-error somethingElse cant exist\n    custom_type.somethingElse;\n\n    return {\n      outputs: inputs,\n    };\n  };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n\n  const result = validHandler(createContext({ inputs: sharedInputs }));\n  assertEqualsTypedValues(sharedInputs, result.outputs);\n});\n"
  },
  {
    "path": "tests/integration/functions/runtime_context/empty_undefined_parameters_test.ts",
    "content": "import { DefineFunction } from \"../../../../src/mod.ts\";\nimport type {\n  EnrichedSlackFunctionHandler,\n} from \"../../../../src/functions/types.ts\";\nimport { SlackFunctionTester } from \"../../../../src/functions/tester/mod.ts\";\nimport { assertEqualsTypedValues } from \"../../../../src/test_utils.ts\";\n\n/**\n * Custom function handler tests exercising empty/undefined/ inputs/outputs\n */\nDeno.test(\"Custom function with no inputs or outputs\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> =\n    () => {\n      return {\n        outputs: {},\n      };\n    };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const result = handler(createContext({ inputs: {} }));\n  assertEqualsTypedValues(result.outputs, {});\n});\n\nDeno.test(\"Custom function with undefined inputs and outputs\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    input_parameters: undefined,\n    output_parameters: undefined,\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> =\n    () => {\n      return {\n        outputs: {},\n      };\n    };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const result = handler(createContext({ inputs: {} }));\n  assertEqualsTypedValues(result.outputs, {});\n});\n\nDeno.test(\"Custom function with empty inputs and outputs\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    input_parameters: { properties: {}, required: [] },\n    output_parameters: { properties: {}, required: [] },\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> =\n    () => {\n      return {\n        outputs: {},\n      };\n    };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const result = handler(createContext({ inputs: {} }));\n  assertEqualsTypedValues(result.outputs, {});\n});\n"
  },
  {
    "path": "tests/integration/functions/runtime_context/incomplete_error_status_test.ts",
    "content": "import { DefineFunction } from \"../../../../src/mod.ts\";\nimport type {\n  EnrichedSlackFunctionHandler,\n} from \"../../../../src/functions/types.ts\";\nimport { SlackFunctionTester } from \"../../../../src/functions/tester/mod.ts\";\nimport { assertEqualsTypedValues } from \"../../../../src/test_utils.ts\";\n\n/**\n * Custom function handler tests exercising error or incomplete function return values\n */\n\nDeno.test(\"Custom function that returns completed=false\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    output_parameters: {\n      properties: {\n        example: {\n          type: \"boolean\",\n        },\n      },\n      required: [\"example\"],\n    },\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> =\n    () => {\n      return {\n        completed: false,\n      };\n    };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const result = handler(createContext({ inputs: {} }));\n  assertEqualsTypedValues(result.completed, false);\n});\n\nDeno.test(\"Custom function that returns error\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    output_parameters: {\n      properties: {\n        example: {\n          type: \"string\",\n        },\n      },\n      required: [\"example\"],\n    },\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> =\n    () => {\n      return {\n        error: \"error\",\n      };\n    };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const result = handler(createContext({ inputs: {} }));\n  assertEqualsTypedValues(result.error, \"error\");\n});\n"
  },
  {
    "path": "tests/integration/functions/runtime_context/input_parameter_optionality_test.ts",
    "content": "import { assert } from \"../../../../src/dev_deps.ts\";\nimport type { CanBe, CanBeUndefined } from \"../../../../src/test_utils.ts\";\nimport { DefineFunction, Schema } from \"../../../../src/mod.ts\";\nimport type {\n  EnrichedSlackFunctionHandler,\n} from \"../../../../src/functions/types.ts\";\nimport { SlackFunctionTester } from \"../../../../src/functions/tester/mod.ts\";\n\n/**\n * Custom function handler tests exercising optionality of inputs for primitive types\n */\nDeno.test(\"Custom function with an optional string input provide the string/undefined input in a function handler context\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    input_parameters: {\n      properties: {\n        in: {\n          type: Schema.types.string,\n        },\n      },\n      required: [],\n    },\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> = (\n    { inputs },\n  ) => {\n    assert<CanBe<typeof inputs.in, string>>(true);\n    assert<CanBeUndefined<typeof inputs.in>>(true);\n    return {\n      outputs: {\n        out: inputs.in || \"default\",\n      },\n    };\n  };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const inputs = {};\n  handler(createContext({ inputs }));\n});\n\nDeno.test(\"Custom function with an optional boolean input provide the boolean/undefined input in a function handler context\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    input_parameters: {\n      properties: {\n        in: {\n          type: Schema.types.boolean,\n        },\n      },\n      required: [],\n    },\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> = (\n    { inputs },\n  ) => {\n    assert<CanBe<typeof inputs.in, boolean>>(true);\n    assert<CanBeUndefined<typeof inputs.in>>(true);\n    return {\n      outputs: {\n        out: inputs.in || \"default\",\n      },\n    };\n  };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const inputs = {};\n  handler(createContext({ inputs }));\n});\n\nDeno.test(\"Custom function with an optional integer input provide the number/undefined input in a function handler context\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    input_parameters: {\n      properties: {\n        in: {\n          type: Schema.types.integer,\n        },\n      },\n      required: [],\n    },\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> = (\n    { inputs },\n  ) => {\n    assert<CanBe<typeof inputs.in, number>>(true);\n    assert<CanBeUndefined<typeof inputs.in>>(true);\n    return {\n      outputs: {\n        out: inputs.in || \"default\",\n      },\n    };\n  };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const inputs = {};\n  handler(createContext({ inputs }));\n});\n\nDeno.test(\"Custom function with an optional number input provide the number/undefined input in a function handler context\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    input_parameters: {\n      properties: {\n        in: {\n          type: Schema.types.number,\n        },\n      },\n      required: [],\n    },\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> = (\n    { inputs },\n  ) => {\n    assert<CanBe<typeof inputs.in, number>>(true);\n    assert<CanBeUndefined<typeof inputs.in>>(true);\n    return {\n      outputs: {\n        out: inputs.in || \"default\",\n      },\n    };\n  };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const inputs = {};\n  handler(createContext({ inputs }));\n});\n"
  },
  {
    "path": "tests/integration/functions/runtime_context/input_parameters_test.ts",
    "content": "import { assert, type IsExact } from \"../../../../src/dev_deps.ts\";\nimport { DefineFunction, Schema } from \"../../../../src/mod.ts\";\nimport type {\n  EnrichedSlackFunctionHandler,\n} from \"../../../../src/functions/types.ts\";\nimport { SlackFunctionTester } from \"../../../../src/functions/tester/mod.ts\";\nimport { assertEqualsTypedValues } from \"../../../../src/test_utils.ts\";\n\n/**\n * Custom function handler tests exercising inputs of various primitive types\n */\nDeno.test(\"Custom function with a string input should provide a string input in the function handler context\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    input_parameters: {\n      properties: {\n        in: {\n          type: Schema.types.string,\n        },\n      },\n      required: [\"in\"],\n    },\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> = (\n    { inputs },\n  ) => {\n    assert<IsExact<typeof inputs.in, string>>(true);\n    return {\n      outputs: {},\n    };\n  };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const inputs = { in: \"test\" };\n  const result = handler(createContext({ inputs }));\n  assertEqualsTypedValues(result.outputs, {});\n});\n\nDeno.test(\"Custom function with a boolean input should provide a boolean input in the function handler context\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    input_parameters: {\n      properties: {\n        in: {\n          type: Schema.types.boolean,\n        },\n      },\n      required: [\"in\"],\n    },\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> = (\n    { inputs },\n  ) => {\n    assert<IsExact<typeof inputs.in, boolean>>(true);\n    return {\n      outputs: {},\n    };\n  };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const inputs = { in: false };\n  const result = handler(createContext({ inputs }));\n  assertEqualsTypedValues(result.outputs, {});\n});\n\nDeno.test(\"Custom function with an integer input should provide a number input in the function handler context\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    input_parameters: {\n      properties: {\n        in: {\n          type: Schema.types.integer,\n        },\n      },\n      required: [\"in\"],\n    },\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> = (\n    { inputs },\n  ) => {\n    assert<IsExact<typeof inputs.in, number>>(true);\n    return {\n      outputs: {},\n    };\n  };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const inputs = { in: 21 };\n  const result = handler(createContext({ inputs }));\n  assertEqualsTypedValues(result.outputs, {});\n});\n\nDeno.test(\"Custom function with a number input should provide a number input in the function handler context\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    input_parameters: {\n      properties: {\n        in: {\n          type: Schema.types.number,\n        },\n      },\n      required: [\"in\"],\n    },\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> = (\n    { inputs },\n  ) => {\n    assert<IsExact<typeof inputs.in, number>>(true);\n    return {\n      outputs: {},\n    };\n  };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const inputs = { in: 21.5 };\n  const result = handler(createContext({ inputs }));\n  assertEqualsTypedValues(result.outputs, {});\n});\n"
  },
  {
    "path": "tests/integration/functions/runtime_context/output_parameter_optionality_test.ts",
    "content": "import { assert } from \"../../../../src/dev_deps.ts\";\nimport type { CanBe, CanBeUndefined } from \"../../../../src/test_utils.ts\";\nimport { DefineFunction, Schema } from \"../../../../src/mod.ts\";\nimport type {\n  EnrichedSlackFunctionHandler,\n} from \"../../../../src/functions/types.ts\";\nimport { SlackFunctionTester } from \"../../../../src/functions/tester/mod.ts\";\n\n/**\n * Custom function handler tests exercising optionality of outputs for primitive types\n */\nDeno.test(\"Custom function with an optional string output returns an output that can be either undefined or a string\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    output_parameters: {\n      properties: {\n        out: {\n          type: Schema.types.string,\n        },\n      },\n      required: [],\n    },\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> =\n    () => {\n      return {\n        outputs: {\n          out: Math.random() > 0.5 ? \"default\" : undefined,\n        },\n      };\n    };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const inputs = {};\n  const result = handler(createContext({ inputs }));\n  const output = result.outputs?.out;\n  assert<CanBeUndefined<typeof output>>(true);\n  assert<CanBe<typeof output, string>>(true);\n});\n\nDeno.test(\"Custom function with an optional boolean output returns an output that can be either undefined or a boolean\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    output_parameters: {\n      properties: {\n        out: {\n          type: Schema.types.boolean,\n        },\n      },\n      required: [],\n    },\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> =\n    () => {\n      return {\n        outputs: {\n          out: Math.random() > 0.5 ? true : undefined,\n        },\n      };\n    };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const inputs = {};\n  const result = handler(createContext({ inputs }));\n  const output = result.outputs?.out;\n  assert<CanBeUndefined<typeof output>>(true);\n  assert<CanBe<typeof output, boolean>>(true);\n});\n\nDeno.test(\"Custom function with an optional integer output returns an output that can be either undefined or a number\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    output_parameters: {\n      properties: {\n        out: {\n          type: Schema.types.integer,\n        },\n      },\n      required: [],\n    },\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> =\n    () => {\n      return {\n        outputs: {\n          out: Math.random() > 0.5 ? 1337 : undefined,\n        },\n      };\n    };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const inputs = {};\n  const result = handler(createContext({ inputs }));\n  const output = result.outputs?.out;\n  assert<CanBeUndefined<typeof output>>(true);\n  assert<CanBe<typeof output, number>>(true);\n});\n\nDeno.test(\"Custom function with an optional number output returns an output that can be either undefined or a number\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    output_parameters: {\n      properties: {\n        out: {\n          type: Schema.types.number,\n        },\n      },\n      required: [],\n    },\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> =\n    () => {\n      return {\n        outputs: {\n          out: Math.random() > 0.5 ? 9.5 : undefined,\n        },\n      };\n    };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const inputs = {};\n  const result = handler(createContext({ inputs }));\n  const output = result.outputs?.out;\n  assert<CanBeUndefined<typeof output>>(true);\n  assert<CanBe<typeof output, number>>(true);\n});\n"
  },
  {
    "path": "tests/integration/functions/runtime_context/output_parameters_test.ts",
    "content": "import { DefineFunction, Schema } from \"../../../../src/mod.ts\";\nimport type {\n  EnrichedSlackFunctionHandler,\n} from \"../../../../src/functions/types.ts\";\nimport { SlackFunctionTester } from \"../../../../src/functions/tester/mod.ts\";\nimport { assertEqualsTypedValues } from \"../../../../src/test_utils.ts\";\n\n/**\n * Custom function handler tests exercising outputs of various primitive types\n */\nDeno.test(\"Custom function with only a string output defined must return a string output\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    output_parameters: {\n      properties: {\n        out: {\n          type: Schema.types.string,\n        },\n      },\n      required: [\"out\"],\n    },\n  });\n  const validHandler: EnrichedSlackFunctionHandler<typeof TestFn.definition> =\n    () => {\n      return {\n        outputs: {\n          out: \"test\",\n        },\n      };\n    };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const result = validHandler(createContext({ inputs: {} }));\n  assertEqualsTypedValues(result.outputs?.out, \"test\");\n  // @ts-expect-error `out` output property must be a string\n  const _invalidHandler: EnrichedSlackFunctionHandler<\n    typeof TestFn.definition\n  > = () => {\n    return {\n      outputs: {\n        out: 1,\n      },\n    };\n  };\n});\n\nDeno.test(\"Custom function with only a boolean output defined must return a boolean output\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    output_parameters: {\n      properties: {\n        out: {\n          type: Schema.types.boolean,\n        },\n      },\n      required: [\"out\"],\n    },\n  });\n  const validHandler: EnrichedSlackFunctionHandler<typeof TestFn.definition> =\n    () => {\n      return {\n        outputs: {\n          out: true,\n        },\n      };\n    };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const result = validHandler(createContext({ inputs: {} }));\n  assertEqualsTypedValues(result.outputs?.out, true);\n  // @ts-expect-error `out` output property must be a boolean\n  const _invalidHandler: EnrichedSlackFunctionHandler<\n    typeof TestFn.definition\n  > = () => {\n    return {\n      outputs: {\n        out: \"haha\",\n      },\n    };\n  };\n});\n\nDeno.test(\"Custom function with only an integer output defined must return a number output\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    output_parameters: {\n      properties: {\n        out: {\n          type: Schema.types.integer,\n        },\n      },\n      required: [\"out\"],\n    },\n  });\n  const validHandler: EnrichedSlackFunctionHandler<typeof TestFn.definition> =\n    () => {\n      return {\n        outputs: {\n          out: 14,\n        },\n      };\n    };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const result = validHandler(createContext({ inputs: {} }));\n  assertEqualsTypedValues(result.outputs?.out, 14);\n  // @ts-expect-error `out` output property must be an integer\n  const _invalidHandler: EnrichedSlackFunctionHandler<\n    typeof TestFn.definition\n  > = () => {\n    return {\n      outputs: {\n        out: \"haha\",\n      },\n    };\n  };\n});\n\nDeno.test(\"Custom function with only a number output defined must return a number output\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    output_parameters: {\n      properties: {\n        out: {\n          type: Schema.types.number,\n        },\n      },\n      required: [\"out\"],\n    },\n  });\n  const validHandler: EnrichedSlackFunctionHandler<typeof TestFn.definition> =\n    () => {\n      return {\n        outputs: {\n          out: 14.2,\n        },\n      };\n    };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const result = validHandler(createContext({ inputs: {} }));\n  assertEqualsTypedValues(result.outputs?.out, 14.2);\n  // @ts-expect-error `out` output property must be an integer\n  const _invalidHandler: EnrichedSlackFunctionHandler<\n    typeof TestFn.definition\n  > = () => {\n    return {\n      outputs: {\n        out: \"haha\",\n      },\n    };\n  };\n});\n"
  },
  {
    "path": "tests/integration/functions/runtime_context/typed_object_property_test.ts",
    "content": "import {\n  assert,\n  assertEquals,\n  assertExists,\n  type IsAny,\n  type IsExact,\n} from \"../../../../src/dev_deps.ts\";\nimport type {\n  CanBe,\n  CanBeUndefined,\n  CannotBeUndefined,\n} from \"../../../../src/test_utils.ts\";\nimport { DefineFunction, Schema } from \"../../../../src/mod.ts\";\nimport { DefineProperty } from \"../../../../src/parameters/define_property.ts\";\nimport type {\n  EnrichedSlackFunctionHandler,\n} from \"../../../../src/functions/types.ts\";\nimport { SlackFunctionTester } from \"../../../../src/functions/tester/mod.ts\";\nimport { assertEqualsTypedValues } from \"../../../../src/test_utils.ts\";\n\n/**\n * Custom function handler tests, exercising Typed Object inputs/outputs\n */\nDeno.test(\"Custom function with a required DefineProperty-wrapped typedobject input with a required string property and a required DefineProperty-wrapped typedobject output should provide correct typing in a function handler context and complain if required output not provided\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    input_parameters: {\n      properties: {\n        anObject: DefineProperty({\n          type: Schema.types.object,\n          properties: { in: { type: \"string\" } },\n          required: [\"in\"],\n        }),\n      },\n      required: [\"anObject\"],\n    },\n    output_parameters: {\n      properties: {\n        anObject: DefineProperty({\n          type: Schema.types.object,\n          properties: { out: { type: \"string\" } },\n          required: [\"out\"],\n        }),\n      },\n      required: [\"anObject\"],\n    },\n  });\n\n  const validHandler: EnrichedSlackFunctionHandler<typeof TestFn.definition> = (\n    { inputs },\n  ) => {\n    assert<CannotBeUndefined<typeof inputs.anObject.in>>(true);\n    assert<IsExact<typeof inputs.anObject.in, string>>(true);\n    return {\n      outputs: {\n        anObject: {\n          out: inputs.anObject.in,\n        },\n      },\n    };\n  };\n\n  // @ts-expect-error Type error if required property isn't returned\n  const _invalidHandler: EnrichedSlackFunctionHandler<\n    typeof TestFn.definition\n  > = (_arg) => {\n    return {\n      outputs: {\n        anObject: {},\n      },\n    };\n  };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const result = validHandler(\n    createContext({ inputs: { anObject: { in: \"test\" } } }),\n  );\n  assertEqualsTypedValues(result.outputs?.anObject.out, \"test\");\n});\n\nDeno.test(\"Custom function with a required DefineProperty-wrapped typedobject input with an optional string property should provide correct typing in a function handler context\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    input_parameters: {\n      properties: {\n        anObject: DefineProperty({\n          type: Schema.types.object,\n          properties: { in: { type: \"string\" } },\n          required: [],\n        }),\n      },\n      required: [\"anObject\"],\n    },\n  });\n  const handler: EnrichedSlackFunctionHandler<typeof TestFn.definition> = (\n    { inputs },\n  ) => {\n    assert<CanBeUndefined<typeof inputs.anObject.in>>(true);\n    assert<CanBe<typeof inputs.anObject.in, string>>(true);\n    return {\n      outputs: {},\n    };\n  };\n  const { createContext } = SlackFunctionTester(TestFn);\n  const _result = handler(\n    createContext({ inputs: { anObject: { in: \"test\" } } }),\n  );\n});\n\nDeno.test(\"Custom function with a required output DefineProperty-wrapped typedobject with mixed required/optional property requirements should complain if required object properties are not returned by function\", () => {\n  const TestFn = DefineFunction({\n    callback_id: \"test\",\n    title: \"test fn\",\n    source_file: \"test.ts\",\n    output_parameters: {\n      properties: {\n        anObject: DefineProperty({\n          type: Schema.types.object,\n          properties: { req: { type: \"string\" }, opt: { type: \"string\" } },\n          required: [\"req\"],\n        }),\n      },\n      required: [\"anObject\"],\n    },\n  });\n\n  // Ensure no error raised if object required property provided\n  const _reqHandler: EnrichedSlackFunctionHandler<typeof TestFn.definition> = (\n    _arg,\n  ) => {\n    return {\n      outputs: {\n        anObject: { req: \"i'm here\" },\n      },\n    };\n  };\n\n  //@ts-expect-error anObject.req is a required property and must be defined\n  const _optHandler: EnrichedSlackFunctionHandler<typeof TestFn.definition> = (\n    _arg,\n  ) => {\n    return {\n      outputs: {\n        anObject: { opt: \"i'm here\" },\n      },\n    };\n  };\n\n  // Ensure no error raised if object required property provided\n  const _mixedHandler: EnrichedSlackFunctionHandler<typeof TestFn.definition> =\n    (\n      _arg,\n    ) => {\n      return {\n        outputs: {\n          anObject: {\n            req: \"i'm here\",\n            opt: \"i'm here\",\n          },\n        },\n      };\n    };\n});\n\nDeno.test(\"Custom function with an input of DefineProperty-wrapped Typed Object with additional properties allows referencing into additional properties in a function handler context\", () => {\n  const obj = DefineProperty({\n    type: Schema.types.object,\n    properties: {\n      aString: { type: Schema.types.string },\n    },\n    required: [],\n    additionalProperties: true,\n  });\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        addlPropertiesObj: obj,\n      },\n      required: [\"addlPropertiesObj\"],\n    },\n    output_parameters: {\n      properties: {\n        addlPropertiesObj: obj,\n      },\n      required: [\"addlPropertiesObj\"],\n    },\n  });\n\n  const sharedInputs = {\n    addlPropertiesObj: { aString: \"hi\", somethingElse: \"ello\" },\n  };\n\n  const handler: EnrichedSlackFunctionHandler<typeof TestFunction.definition> =\n    (\n      { inputs },\n    ) => {\n      const { addlPropertiesObj } = inputs;\n      assertEqualsTypedValues(\n        addlPropertiesObj,\n        sharedInputs.addlPropertiesObj,\n      );\n      assertEqualsTypedValues(\n        addlPropertiesObj.aString,\n        sharedInputs.addlPropertiesObj.aString,\n      );\n      assert<IsAny<typeof addlPropertiesObj.somethingElse>>(true);\n      assert<IsAny<typeof addlPropertiesObj.anythingElse>>(true);\n      assertEquals(addlPropertiesObj.somethingElse, \"ello\");\n      assertEquals(addlPropertiesObj.anythingElse, undefined);\n      return {\n        outputs: inputs,\n      };\n    };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n\n  const result = handler(createContext({ inputs: sharedInputs }));\n  assertEqualsTypedValues(sharedInputs, result.outputs);\n  assertExists(result.outputs?.addlPropertiesObj);\n  assertExists(result.outputs?.addlPropertiesObj.aString);\n  assertEquals(result.outputs?.addlPropertiesObj.somethingElse, \"ello\");\n  assertEquals(result.outputs?.addlPropertiesObj.anythingElse, undefined);\n  if (result.outputs) {\n    assert<IsAny<typeof result.outputs.addlPropertiesObj.anythingElse>>(true);\n  }\n  result.outputs.addlPropertiesObj.anothaOne;\n});\n\nDeno.test(\"Custom function with an input of DefineProperty-wrapped Typed Object without additional properties prevents referencing into additional properties in a function handler context\", () => {\n  const obj = DefineProperty({\n    type: Schema.types.object,\n    properties: {\n      aString: { type: Schema.types.string },\n    },\n    required: [],\n    additionalProperties: false,\n  });\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        noAddlPropertiesObj: obj,\n      },\n      required: [\"noAddlPropertiesObj\"],\n    },\n    output_parameters: {\n      properties: {\n        noAddlPropertiesObj: obj,\n      },\n      required: [\"noAddlPropertiesObj\"],\n    },\n  });\n\n  const sharedInputs = {\n    noAddlPropertiesObj: { aString: \"hi\" },\n  };\n\n  const handler: EnrichedSlackFunctionHandler<typeof TestFunction.definition> =\n    (\n      { inputs },\n    ) => {\n      const { noAddlPropertiesObj } = inputs;\n      assertEqualsTypedValues(\n        noAddlPropertiesObj,\n        sharedInputs.noAddlPropertiesObj,\n      );\n      assertEqualsTypedValues(\n        noAddlPropertiesObj.aString,\n        sharedInputs.noAddlPropertiesObj.aString,\n      );\n\n      // @ts-expect-error anythingElse cant exist\n      assertEquals(noAddlPropertiesObj.anythingElse, undefined);\n      return {\n        outputs: inputs,\n      };\n    };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n\n  const result = handler(createContext({ inputs: sharedInputs }));\n  assertEqualsTypedValues(sharedInputs, result.outputs);\n  assertExists(result.outputs?.noAddlPropertiesObj);\n  assertExists(result.outputs?.noAddlPropertiesObj.aString);\n\n  // @ts-expect-error anythingElse cant exist\n  assertEquals(result.outputs?.noAddlPropertiesObj.anythingElse, undefined);\n});\n\nDeno.test(\"Custom function using an unwrapped Typed Object input with additionalProperties=undefined should allow referencing additional properties in a function handler context\", () => {\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        addlPropertiesObj: {\n          type: Schema.types.object,\n          properties: {\n            aString: { type: Schema.types.string },\n          },\n          required: [],\n        },\n      },\n      required: [\"addlPropertiesObj\"],\n    },\n    output_parameters: {\n      properties: {\n        addlPropertiesObj: {\n          type: Schema.types.object,\n          properties: {\n            aString: { type: Schema.types.string },\n          },\n          required: [],\n        },\n      },\n      required: [\"addlPropertiesObj\"],\n    },\n  });\n\n  const sharedInputs = {\n    addlPropertiesObj: { aString: \"hi\" },\n  };\n\n  const handler: EnrichedSlackFunctionHandler<typeof TestFunction.definition> =\n    (\n      { inputs },\n    ) => {\n      const { addlPropertiesObj } = inputs;\n      assertEqualsTypedValues(\n        addlPropertiesObj,\n        sharedInputs.addlPropertiesObj,\n      );\n      assertEqualsTypedValues(\n        addlPropertiesObj.aString,\n        sharedInputs.addlPropertiesObj.aString,\n      );\n      assert<IsAny<typeof addlPropertiesObj.anythingElse>>(true);\n      assertEquals(addlPropertiesObj.anythingElse, undefined);\n      return {\n        outputs: inputs,\n      };\n    };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n\n  const result = handler(createContext({ inputs: sharedInputs }));\n  assertEqualsTypedValues(sharedInputs, result.outputs);\n  assertExists(result.outputs?.addlPropertiesObj);\n  assertExists(result.outputs?.addlPropertiesObj.aString);\n  assertEquals(result.outputs?.addlPropertiesObj.anythingElse, undefined);\n});\n\nDeno.test(\"Custom function using an unwrapped Typed Object input with additionalProperties=false should prevent referencing additional properties in a function handler context\", () => {\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        noAddlPropertiesObj: {\n          type: Schema.types.object,\n          properties: {\n            aString: { type: Schema.types.string },\n          },\n          required: [],\n          additionalProperties: false,\n        },\n      },\n      required: [\"noAddlPropertiesObj\"],\n    },\n    output_parameters: {\n      properties: {\n        noAddlPropertiesObj: {\n          type: Schema.types.object,\n          properties: {\n            aString: { type: Schema.types.string },\n          },\n          required: [],\n          additionalProperties: false,\n        },\n      },\n      required: [\"noAddlPropertiesObj\"],\n    },\n  });\n\n  const sharedInputs = {\n    noAddlPropertiesObj: { aString: \"hi\" },\n  };\n\n  const handler: EnrichedSlackFunctionHandler<typeof TestFunction.definition> =\n    (\n      { inputs },\n    ) => {\n      const { noAddlPropertiesObj } = inputs;\n      assertEqualsTypedValues(\n        noAddlPropertiesObj,\n        sharedInputs.noAddlPropertiesObj,\n      );\n      assertEqualsTypedValues(\n        noAddlPropertiesObj.aString,\n        sharedInputs.noAddlPropertiesObj.aString,\n      );\n      // @ts-expect-error anythingElse cant exist\n      assertEquals(noAddlPropertiesObj.anythingElse, undefined);\n      return {\n        outputs: inputs,\n      };\n    };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n\n  const result = handler(createContext({ inputs: sharedInputs }));\n  assertEqualsTypedValues(sharedInputs, result.outputs);\n  assertExists(result.outputs?.noAddlPropertiesObj);\n  assertExists(result.outputs?.noAddlPropertiesObj.aString);\n\n  // @ts-expect-error anythingElse cant exist\n  assertEquals(result.outputs?.noAddlPropertiesObj.anythingElse, undefined);\n});\n"
  },
  {
    "path": "tests/integration/functions/runtime_context/untyped_object_property_test.ts",
    "content": "import { assert, assertExists, type IsAny } from \"../../../../src/dev_deps.ts\";\nimport { DefineFunction, Schema } from \"../../../../src/mod.ts\";\nimport type {\n  EnrichedSlackFunctionHandler,\n} from \"../../../../src/functions/types.ts\";\nimport { SlackFunctionTester } from \"../../../../src/functions/tester/mod.ts\";\n\n/**\n * Custom function handler tests, exercising Untyped Object inputs/outputs\n */\nDeno.test(\"Custom function using untyped Objects should allow for referencing any property in a function handler context\", () => {\n  const TestFunction = DefineFunction({\n    callback_id: \"my_callback_id\",\n    source_file: \"test\",\n    title: \"Test\",\n    input_parameters: {\n      properties: {\n        untypedObj: {\n          type: Schema.types.object,\n        },\n      },\n      required: [\"untypedObj\"],\n    },\n    output_parameters: {\n      properties: {\n        untypedObj: {\n          type: Schema.types.object,\n        },\n      },\n      required: [\"untypedObj\"],\n    },\n  });\n\n  const sharedInputs = {\n    untypedObj: { aString: \"hi\" },\n  };\n\n  const handler: EnrichedSlackFunctionHandler<typeof TestFunction.definition> =\n    (\n      { inputs },\n    ) => {\n      const { untypedObj } = inputs;\n\n      assert<IsAny<typeof untypedObj>>(true);\n      assert<IsAny<typeof untypedObj.aString>>(true);\n\n      return {\n        outputs: {\n          untypedObj: { literallyAnything: \"ok\" },\n        },\n      };\n    };\n\n  const { createContext } = SlackFunctionTester(TestFunction);\n\n  const result = handler(createContext({ inputs: sharedInputs }));\n\n  assertExists(result.outputs?.untypedObj);\n  if (result.outputs?.untypedObj) {\n    assert<IsAny<typeof result.outputs.untypedObj>>(true);\n  }\n});\n"
  },
  {
    "path": "tests/integration/parameters/parameter_variable_test.ts",
    "content": "import { DefineProperty } from \"../../../src/parameters/define_property.ts\";\nimport { ParameterVariable } from \"../../../src/parameters/mod.ts\";\nimport type { SingleParameterVariable } from \"../../../src/parameters/types.ts\";\nimport { DefineType } from \"../../../src/types/mod.ts\";\nimport SchemaTypes from \"../../../src/schema/schema_types.ts\";\nimport {\n  assert,\n  assertStrictEquals,\n  type IsAny,\n  type IsExact,\n} from \"../../../src/dev_deps.ts\";\nimport type { CannotBeUndefined } from \"../../../src/test_utils.ts\";\n/**\n * Typed Object required/optional property definitions should never yield undefined ParameterVariable properties\n */\nDeno.test(\"ParameterVariable DefineProperty-wrapped typed object with all optional properties should never yield object with potentially undefined properties\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [],\n  });\n  const param = ParameterVariable(\"\", \"incident\", obj);\n\n  assert<CannotBeUndefined<typeof param.id>>(true);\n  assert<CannotBeUndefined<typeof param.name>>(true);\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n});\n\nDeno.test(\"ParameterVariable DefineProperty-wrapped typed object with all required properties should never yield object with potentially undefined properties\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"id\", \"name\"],\n  });\n\n  const param = ParameterVariable(\"\", \"incident\", obj);\n  assert<CannotBeUndefined<typeof param.id>>(true);\n  assert<CannotBeUndefined<typeof param.name>>(true);\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n});\n\nDeno.test(\"ParameterVariable DefineProperty-wrapped typed object with mix of optional and required properties should never yield object with potentially undefined properties\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"id\"],\n  });\n  const param = ParameterVariable(\"\", \"incident\", obj);\n\n  assert<CannotBeUndefined<typeof param.id>>(true);\n  assert<CannotBeUndefined<typeof param.name>>(true);\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n});\n\nDeno.test(\"ParameterVariable using Custom Type with DefineProperty-wrapped typed object with optional property should never yield object with potentially undefined property\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      aString: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [],\n  });\n  const customType = DefineType({\n    name: \"customType\",\n    ...obj,\n  });\n  const param = ParameterVariable(\"\", \"myCustomType\", {\n    type: customType,\n  });\n\n  assert<CannotBeUndefined<typeof param.aString>>(true);\n  assertStrictEquals(`${param}`, \"{{myCustomType}}\");\n  assertStrictEquals(`${param.aString}`, \"{{myCustomType.aString}}\");\n});\n\nDeno.test(\"ParameterVariable using Custom Type with DefineProperty-wrapped typed object with required property should never yield object with potentially undefined property\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      aString: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"aString\"],\n  });\n  const customType = DefineType({\n    name: \"customType\",\n    ...obj,\n  });\n  const param = ParameterVariable(\"\", \"myCustomType\", {\n    type: customType,\n  });\n\n  assert<CannotBeUndefined<typeof param.aString>>(true);\n  assertStrictEquals(`${param}`, \"{{myCustomType}}\");\n  assertStrictEquals(`${param.aString}`, \"{{myCustomType.aString}}\");\n});\n\n/**\n * Typed Object additionalProperties controls whether ParameterVariable allows access to additional properties\n */\nDeno.test(\"ParameterVariable DefineProperty-wrapped typed object with all optional properties and undefined additionalProperties allows access to additional properties\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [],\n  });\n  const param = ParameterVariable(\"\", \"incident\", obj);\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable DefineProperty-wrapped typed object with all required properties and undefined additionalProperties allows access to additional properties\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"id\", \"name\"],\n  });\n  const param = ParameterVariable(\"\", \"incident\", obj);\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable DefineProperty-wrapped typed object with mix of required and optional properties and undefined additionalProperties allows access to additional properties\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"id\"],\n  });\n  const param = ParameterVariable(\"\", \"incident\", obj);\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable DefineProperty-wrapped typed object with all optional properties and additionalProperties=true allows access to additional properties\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [],\n    additionalProperties: true,\n  });\n  const param = ParameterVariable(\"\", \"incident\", obj);\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable DefineProperty-wrapped typed object with all required properties and additionalProperties=true allows access to additional properties\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"id\", \"name\"],\n    additionalProperties: true,\n  });\n  const param = ParameterVariable(\"\", \"incident\", obj);\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable DefineProperty-wrapped typed object with mix of required and optional properties and additionalProperties=true allows access to additional properties\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"id\"],\n    additionalProperties: true,\n  });\n  const param = ParameterVariable(\"\", \"incident\", obj);\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable DefineProperty-wrapped typed object with all optional properties and additionalProperties=false prevents access to additional properties\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [],\n    additionalProperties: false,\n  });\n  const param = ParameterVariable(\"\", \"incident\", obj);\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  //@ts-expect-error foo doesn't exist\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable DefineProperty-wrapped typed object with all required properties and additionalProperties=false prevents access to additional properties\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"id\", \"name\"],\n    additionalProperties: false,\n  });\n  const param = ParameterVariable(\"\", \"incident\", obj);\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  //@ts-expect-error foo doesn't exist\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable DefineProperty-wrapped typed object with mix of required and optional properties and additionalProperties=false prevents access to additional properties\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      id: {\n        type: SchemaTypes.integer,\n      },\n      name: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"id\"],\n    additionalProperties: false,\n  });\n  const param = ParameterVariable(\"\", \"incident\", obj);\n\n  assertStrictEquals(`${param}`, \"{{incident}}\");\n  assertStrictEquals(`${param.id}`, \"{{incident.id}}\");\n  assertStrictEquals(`${param.name}`, \"{{incident.name}}\");\n  //@ts-expect-error foo doesn't exist\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable using Custom Type with DefineProperty-wrapped typed object with optional property and additionalProperties=true yields object that allows access to additional properties\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      aString: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [],\n    additionalProperties: true,\n  });\n  const customType = DefineType({\n    name: \"customType\",\n    ...obj,\n  });\n  const paramName = \"myCustomType\";\n  const param = ParameterVariable(\"\", paramName, {\n    type: customType,\n  });\n\n  assert<CannotBeUndefined<typeof param.aString>>(true);\n  assertStrictEquals(`${param}`, `{{${paramName}}}`);\n  assertStrictEquals(`${param.aString}`, `{{${paramName}.aString}}`);\n  assertStrictEquals(`${param.foo.bar}`, `{{${paramName}.foo.bar}}`);\n});\n\nDeno.test(\"ParameterVariable using Custom Type with DefineProperty-wrapped typed object with required property and additionalProperties=true yields object that allows access to additional properties\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      aString: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"aString\"],\n    additionalProperties: true,\n  });\n  const customType = DefineType({\n    name: \"customType\",\n    ...obj,\n  });\n  const paramName = \"myCustomType\";\n  const param = ParameterVariable(\"\", paramName, {\n    type: customType,\n  });\n\n  assert<CannotBeUndefined<typeof param.aString>>(true);\n  assertStrictEquals(`${param}`, `{{${paramName}}}`);\n  assertStrictEquals(`${param.aString}`, `{{${paramName}.aString}}`);\n  assertStrictEquals(`${param.foo.bar}`, `{{${paramName}.foo.bar}}`);\n});\n\nDeno.test(\"ParameterVariable using Custom Type with DefineProperty-wrapped typed object with optional property and additionalProperties=false yields object that prevents access to additional properties\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      aString: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [],\n    additionalProperties: false,\n  });\n  const customType = DefineType({\n    name: \"customType\",\n    ...obj,\n  });\n  const paramName = \"myCustomType\";\n  const param = ParameterVariable(\"\", paramName, {\n    type: customType,\n  });\n\n  assert<CannotBeUndefined<typeof param.aString>>(true);\n  assertStrictEquals(`${param}`, `{{${paramName}}}`);\n  assertStrictEquals(`${param.aString}`, `{{${paramName}.aString}}`);\n  //@ts-expect-error foo doesn't exist\n  assertStrictEquals(`${param.foo.bar}`, `{{${paramName}.foo.bar}}`);\n});\n\nDeno.test(\"ParameterVariable using Custom Type with DefineProperty-wrapped typed object with required property and additionalProperties=false yields object that prevents access to additional properties\", () => {\n  const obj = DefineProperty({\n    type: SchemaTypes.object,\n    properties: {\n      aString: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"aString\"],\n    additionalProperties: false,\n  });\n  const customType = DefineType({\n    name: \"customType\",\n    ...obj,\n  });\n  const paramName = \"myCustomType\";\n  const param = ParameterVariable(\"\", paramName, {\n    type: customType,\n  });\n\n  assert<CannotBeUndefined<typeof param.aString>>(true);\n  assertStrictEquals(`${param}`, `{{${paramName}}}`);\n  assertStrictEquals(`${param.aString}`, `{{${paramName}.aString}}`);\n  //@ts-expect-error foo doesn't exist\n  assertStrictEquals(`${param.foo.bar}`, `{{${paramName}.foo.bar}}`);\n});\n\n/**\n * ParameterVariable wrapped parameters result in specific types returned\n */\nDeno.test(\"ParameterVariable using Custom Type string should yield SingleParameterVariable type\", () => {\n  const customType = DefineType({\n    name: \"customTypeString\",\n    type: SchemaTypes.string,\n  });\n  const param = ParameterVariable(\"\", \"myCustomTypeString\", {\n    type: customType,\n  });\n\n  assert<IsExact<typeof param, SingleParameterVariable>>(true);\n  assertStrictEquals(`${param}`, \"{{myCustomTypeString}}\");\n});\n\nDeno.test(\"ParameterVariable using Custom Type with untypedarray should yield SingleParameterVariable type\", () => {\n  const customType = DefineType({\n    name: \"customTypeArray\",\n    type: SchemaTypes.array,\n  });\n  const param = ParameterVariable(\"\", \"myCustomTypeArray\", {\n    type: customType,\n  });\n\n  assert<IsExact<typeof param, SingleParameterVariable>>(true);\n  assertStrictEquals(`${param}`, \"{{myCustomTypeArray}}\");\n});\n\nDeno.test(\"ParameterVariable using Custom Type untyped object should yield any type\", () => {\n  const customType = DefineType({\n    name: \"customTypeObject\",\n    type: SchemaTypes.object,\n  });\n  const param = ParameterVariable(\"\", \"myCustomTypeObject\", {\n    type: customType,\n  });\n\n  assert<IsAny<typeof param>>(true);\n  assertStrictEquals(`${param}`, \"{{myCustomTypeObject}}\");\n  assertStrictEquals(`${param.foo}`, \"{{myCustomTypeObject.foo}}\");\n  assertStrictEquals(`${param.foo.bar}`, \"{{myCustomTypeObject.foo.bar}}\");\n  assertStrictEquals(\n    `${param.foo.bar.baz}`,\n    \"{{myCustomTypeObject.foo.bar.baz}}\",\n  );\n});\n"
  },
  {
    "path": "tests/integration/parameters/parameter_variable_unwrapped_test.ts",
    "content": "import { DefineType } from \"../../../src/types/mod.ts\";\nimport SchemaTypes from \"../../../src/schema/schema_types.ts\";\nimport { ParameterVariable } from \"../../../src/parameters/mod.ts\";\nimport {\n  assert,\n  assertStrictEquals,\n  type IsAny,\n} from \"../../../src/dev_deps.ts\";\nimport type { CannotBeUndefined } from \"../../../src/test_utils.ts\";\n\nDeno.test(\"ParameterVariable with unwrapped typed object with an optional Custom Type property should yield an object with a definite value for its sub-properties\", () => {\n  const StringType = DefineType({\n    name: \"stringType\",\n    type: SchemaTypes.string,\n    minLength: 2,\n  });\n\n  const param = ParameterVariable(\"\", \"myObjectParam\", {\n    type: SchemaTypes.object,\n    properties: {\n      aString: {\n        type: StringType,\n      },\n    },\n    required: [],\n  });\n\n  assertStrictEquals(`${param}`, \"{{myObjectParam}}\");\n  assertStrictEquals(`${param.aString}`, \"{{myObjectParam.aString}}\");\n});\n\nDeno.test(\"ParameterVariable using Custom Type with unwrapped object referencing another Custom Type as property should yield an object with a definite value for its sub-properties\", () => {\n  const StringType = DefineType({\n    name: \"stringType\",\n    type: SchemaTypes.string,\n    minLength: 2,\n  });\n  const customType = DefineType({\n    name: \"customTypeWithCustomType\",\n    type: SchemaTypes.object,\n    properties: {\n      customType: {\n        type: StringType,\n      },\n    },\n    required: [],\n  });\n  const param = ParameterVariable(\"\", \"myNestedCustomType\", {\n    type: customType,\n  });\n\n  assertStrictEquals(`${param}`, \"{{myNestedCustomType}}\");\n  assertStrictEquals(\n    `${param.customType}`,\n    \"{{myNestedCustomType.customType}}\",\n  );\n});\n\nDeno.test(\"ParameterVariable using Custom Type with unwrapped typed object with optional property yields object with no undefined property\", () => {\n  const customType = DefineType({\n    name: \"customType\",\n    type: SchemaTypes.object,\n    properties: {\n      aString: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [],\n  });\n  const param = ParameterVariable(\"\", \"myCustomType\", {\n    type: customType,\n  });\n\n  assert<CannotBeUndefined<typeof param.aString>>(true);\n  assertStrictEquals(`${param}`, \"{{myCustomType}}\");\n  assertStrictEquals(`${param.aString}`, \"{{myCustomType.aString}}\");\n});\n\nDeno.test(\"ParameterVariable using Custom Type with unwrapped typed object with optional property and additionalProperties=true yields object that allows access to additional properties\", () => {\n  const customType = DefineType({\n    name: \"customType\",\n    type: SchemaTypes.object,\n    properties: {\n      aString: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [],\n    additionalProperties: true,\n  });\n  const param = ParameterVariable(\"\", \"myCustomType\", {\n    type: customType,\n  });\n\n  assert<IsAny<typeof param.additionalProp>>(true);\n  assert<CannotBeUndefined<typeof param.aString>>(true);\n  assertStrictEquals(`${param}`, \"{{myCustomType}}\");\n  assertStrictEquals(`${param.aString}`, \"{{myCustomType.aString}}\");\n  assertStrictEquals(`${param.foo.bar}`, \"{{myCustomType.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable using Custom Type with unwrapped typed object with optional property and additionalProperties=false yields object that prevents access to additional properties\", () => {\n  const customType = DefineType({\n    name: \"customType\",\n    type: SchemaTypes.object,\n    properties: {\n      aString: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [],\n    additionalProperties: false,\n  });\n  const param = ParameterVariable(\"\", \"myCustomType\", {\n    type: customType,\n  });\n\n  assert<CannotBeUndefined<typeof param.aString>>(true);\n  assertStrictEquals(`${param}`, \"{{myCustomType}}\");\n  assertStrictEquals(`${param.aString}`, \"{{myCustomType.aString}}\");\n  //@ts-expect-error foo doesn't exist\n  assertStrictEquals(`${param.foo.bar}`, \"{{myCustomType.foo.bar}}\");\n});\n\n/**\n * TODO: below tests fail because unwrapped typed object yields a SingleParameterVariable, which is incorrect. Only happens when required properties are set.\n\nDeno.test(\"ParameterVariable using Custom Type with unwrapped typed object with required property yields object with no undefined property\", () => {\n  const customType = DefineType({\n    name: \"customType\",\n    type: SchemaTypes.object,\n    properties: {\n      aString: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"aString\"],\n  });\n  const param = ParameterVariable(\"\", \"myCustomType\", {\n    type: customType,\n  });\n\n  assert<CannotBeUndefined<typeof param.aString>>(true);\n  assertStrictEquals(`${param}`, \"{{myCustomType}}\");\n  assertStrictEquals(`${param.aString}`, \"{{myCustomType.aString}}\");\n});\n\nDeno.test(\"ParameterVariable using Custom Type with unwrapped typed object with required property and additionalProperties=true yields object that allows access to additional properties\", () => {\n  const customType = DefineType({\n    name: \"customType\",\n    type: SchemaTypes.object,\n    properties: {\n      aString: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"aString\"],\n    additionalProperties: true,\n  });\n  const param = ParameterVariable(\"\", \"myCustomType\", {\n    type: customType,\n  });\n\n  assert<CannotBeUndefined<typeof param.aString>>(true);\n  assertStrictEquals(`${param}`, \"{{myCustomType}}\");\n  assertStrictEquals(`${param.aString}`, \"{{myCustomType.aString}}\");\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n\nDeno.test(\"ParameterVariable using Custom Type with unwrapped typed object with required property and additionalProperties=false yields object that prevents access to additional properties\", () => {\n  const customType = DefineType({\n    name: \"customType\",\n    type: SchemaTypes.object,\n    properties: {\n      aString: {\n        type: SchemaTypes.string,\n      },\n    },\n    required: [\"aString\"],\n    additionalProperties: false,\n  });\n  const param = ParameterVariable(\"\", \"myCustomType\", {\n    type: customType,\n  });\n\n  assert<CannotBeUndefined<typeof param.aString>>(true);\n  assertStrictEquals(`${param}`, \"{{myCustomType}}\");\n  assertStrictEquals(`${param.aString}`, \"{{myCustomType.aString}}\");\n  // TODO: current failure mode of this test actually causes this next line to incorrectly pass\n  //@ts-expect-error foo doesn't exist\n  assertStrictEquals(`${param.foo.bar}`, \"{{incident.foo.bar}}\");\n});\n*/\n"
  },
  {
    "path": "tests/integration/schema/slack/functions/_scripts/write_function_files_test.ts",
    "content": "import {\n  assertEquals,\n  assertExists,\n  mock,\n} from \"../../../../../../src/dev_deps.ts\";\nimport { _internals } from \"../../../../../../src/schema/slack/functions/_scripts/src/write_function_files.ts\";\nimport { getSlackFunctions } from \"../../../../../../src/schema/slack/functions/_scripts/src/utils.ts\";\n\nDeno.test(`write_function_files.ts ${_internals.main.name} function generates valid typescript`, async () => {\n  const getSlackFunctionsStub = mock.stub(\n    _internals,\n    \"getSlackFunctions\",\n    (\n      functionsPayloadPath =\n        \"src/schema/slack/functions/_scripts/src/test/data/function.json\",\n    ) => {\n      return getSlackFunctions(\n        functionsPayloadPath,\n      );\n    },\n  );\n\n  const outputs: Record<string, string> = {};\n\n  const writeTextFileStub = mock.stub(\n    _internals,\n    \"writeTextFile\",\n    // deno-lint-ignore require-await\n    async (path, data) => {\n      outputs[path] = data;\n    },\n  );\n\n  try {\n    await _internals.main();\n    mock.assertSpyCalls(writeTextFileStub, 3);\n    mock.assertSpyCalls(getSlackFunctionsStub, 1);\n\n    const generatedContent = outputs[\"../send_message.ts\"];\n    assertExists(generatedContent);\n\n    const command = new Deno.Command(Deno.execPath(), {\n      cwd: `${Deno.cwd()}/src/schema/slack/functions/`,\n      args: [\n        \"eval\",\n        generatedContent,\n      ],\n    });\n    const { code, stdout, stderr } = await command.output();\n    const textDecoder = new TextDecoder();\n\n    assertEquals(\n      textDecoder.decode(stderr),\n      \"\",\n      \"The generated TypeScript content is not valid\",\n    );\n    assertEquals(\n      textDecoder.decode(stdout),\n      \"\",\n      \"The generated TypeScript should not print to the console\",\n    );\n    assertEquals(code, 0);\n  } finally {\n    writeTextFileStub.restore();\n    getSlackFunctionsStub.restore();\n  }\n});\n"
  },
  {
    "path": "tests/integration/workflows/workflows_test.ts",
    "content": "import { assertEquals } from \"../../../src/dev_deps.ts\";\nimport { DefineWorkflow } from \"../../../src/workflows/mod.ts\";\nimport { DefineFunction } from \"../../../src/mod.ts\";\nimport SlackTypes from \"../../../src/schema/slack/schema_types.ts\";\n\nDeno.test(\"Multi-step Workflow should export correct double-brace-wrapped step input values\", () => {\n  const TestFunction = DefineFunction({\n    callback_id: \"no_params\",\n    title: \"Test function\",\n    source_file: \"\",\n    input_parameters: {\n      properties: {\n        email: {\n          type: \"string\",\n        },\n        name: {\n          type: \"string\",\n        },\n        manager: {\n          type: \"object\",\n          properties: {\n            email: { type: \"string\" },\n            name: { type: \"string\" },\n          },\n          required: [],\n        },\n      },\n      required: [\"email\"],\n    },\n    output_parameters: {\n      properties: {\n        url: {\n          type: \"string\",\n        },\n        manager: {\n          type: \"object\",\n          properties: {\n            email: { type: \"string\" },\n            name: { type: \"string\" },\n          },\n          required: [],\n        },\n      },\n      required: [\"url\"],\n    },\n  });\n\n  const workflow = DefineWorkflow({\n    callback_id: \"test_wf\",\n    title: \"test\",\n    input_parameters: {\n      properties: {\n        email: {\n          type: \"string\",\n        },\n        name: {\n          type: \"string\",\n        },\n        manager: {\n          type: \"object\",\n          properties: {\n            email: { type: \"string\" },\n            name: { type: \"string\" },\n          },\n          required: [\"name\"],\n        },\n      },\n      required: [\"email\", \"manager\"],\n    },\n  });\n\n  assertEquals(workflow.id, workflow.definition.callback_id);\n  assertEquals(workflow.definition.title, \"test\");\n  assertEquals(workflow.definition.description, undefined);\n\n  // Add a DefineFunction result as a step\n  const step1 = workflow.addStep(TestFunction, {\n    email: workflow.inputs.email,\n    name: `A name: ${workflow.inputs.name}`,\n    manager: {\n      name: workflow.inputs.manager.name,\n      email: workflow.inputs.manager.email,\n    },\n  });\n\n  // add a manually configured step\n  const step2 = workflow.addStep(\"slack#/functions/create_channel\", {\n    channel_name: `test-channel-${workflow.inputs.name}-${step1.outputs.url}`,\n  });\n\n  // another manually configured step, reyling on outputs of another manually configured step\n  workflow.addStep(\"slack#/functions/send_message\", {\n    channel_id: \"C123123\",\n    message: `Channel Created <#${step2.outputs.channel_id}>`,\n  });\n\n  const exportedWorkflow = workflow.export();\n  const step1Inputs = exportedWorkflow.steps[0].inputs;\n  const step2Inputs = exportedWorkflow.steps[1].inputs;\n  const step3Inputs = exportedWorkflow.steps[2].inputs;\n\n  assertEquals(exportedWorkflow.steps.length, 3);\n  assertEquals(exportedWorkflow.title, \"test\");\n  assertEquals(exportedWorkflow?.input_parameters?.properties.email, {\n    type: \"string\",\n  });\n  assertEquals(`${step1Inputs.email}`, \"{{inputs.email}}\");\n  assertEquals(`${step1Inputs.name}`, \"A name: {{inputs.name}}\");\n  assertEquals(`${step1.outputs.url}`, \"{{steps.0.url}}\");\n  assertEquals(`${step1.outputs.manager?.email}`, \"{{steps.0.manager.email}}\");\n  assertEquals(`${step1.outputs.manager?.name}`, \"{{steps.0.manager.name}}\");\n\n  assertEquals(\n    `${step2Inputs.channel_name}`,\n    \"test-channel-{{inputs.name}}-{{steps.0.url}}\",\n  );\n\n  assertEquals(`${step3Inputs.channel_id}`, \"C123123\");\n  assertEquals(\n    `${step3Inputs.message}`,\n    \"Channel Created <#{{steps.1.channel_id}}>\",\n  );\n});\n\nDeno.test(\"Workflow addStep returns appropriate output types and properties for interactivity and user context types\", () => {\n  const TestFunction = DefineFunction({\n    source_file: \"./test.ts\",\n    callback_id: \"test\",\n    title: \"Test\",\n    output_parameters: {\n      properties: {\n        interactivity: {\n          type: SlackTypes.interactivity,\n        },\n        user: {\n          type: SlackTypes.user_context,\n        },\n      },\n      required: [\"interactivity\"],\n    },\n  });\n\n  const TestWorkflow = DefineWorkflow({\n    callback_id: \"test\",\n    title: \"Test\",\n  });\n\n  const step1 = TestWorkflow.addStep(TestFunction, {});\n\n  assertEquals(\n    `${step1.outputs.interactivity}`,\n    `{{steps.0.interactivity}}`,\n  );\n  assertEquals(\n    `${step1.outputs.interactivity.interactivity_pointer}`,\n    `{{steps.0.interactivity.interactivity_pointer}}`,\n  );\n  assertEquals(\n    `${step1.outputs.interactivity.interactor.id}`,\n    `{{steps.0.interactivity.interactor.id}}`,\n  );\n  assertEquals(\n    `${step1.outputs.interactivity.interactor.secret}`,\n    `{{steps.0.interactivity.interactor.secret}}`,\n  );\n  assertEquals(\n    `${step1.outputs.user}`,\n    `{{steps.0.user}}`,\n  );\n  assertEquals(\n    `${step1.outputs.user?.id}`,\n    `{{steps.0.user.id}}`,\n  );\n  assertEquals(\n    `${step1.outputs.user?.secret}`,\n    `{{steps.0.user.secret}}`,\n  );\n});\n\n// Deno will exit on uncaught exceptions.\n// JSON.parse will raise an exception when given undefined.\n// By setting undefined manually to reflect the case of an\n// undefined inputValue, which will allow Deno to continue\n// and pass the undefined values up to the validation API\n// -- which will then communicate back to the CLI the specific\n// validation errors it ran in to.\nDeno.test(\"Malformed workflow step inputs should be undefined\", () => {\n  const TestFunction = DefineFunction({\n    callback_id: \"test_undefined\",\n    title: \"Test function\",\n    source_file: \"\",\n    input_parameters: {\n      properties: {\n        message: {\n          type: \"string\",\n        },\n      },\n      required: [\"message\"],\n    },\n  });\n\n  const workflow = DefineWorkflow({\n    callback_id: \"test_wf\",\n    title: \"test\",\n  });\n\n  const malformedFunctionStep = workflow.addStep(TestFunction, {\n    message: undefined,\n  });\n\n  workflow.addStep(\"slack#/functions/send_message\", {\n    channel_id: \"C12345\",\n    message: malformedFunctionStep.outputs.message,\n  });\n\n  const exportedWorkflow = workflow.export();\n\n  assertEquals(exportedWorkflow.steps[0].inputs.message, undefined);\n});\n"
  }
]