diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..b82a747 --- /dev/null +++ b/.env.example @@ -0,0 +1,11 @@ +# Required: GitHub Token (Needs "repo" permissions) +# GITHUB_TOKEN=your_personal_access_token_or_org_token + +# Required: HackMD Configuration +# HACKMD_API_TOKEN=your_hackmd_api_token +# HACKMD_TEAM_NAME=your_hackmd_team_name_optional # Defaults to your own personal space + +# Google Calendar API Authentication +# You can get this from the Google Cloud Console +# Create an API Key and restrict it to the Google Calendar API +# GOOGLE_API_KEY=your_google_calendar_api_key_here diff --git a/.github/workflows/create-meeting-artifacts-manual.yml b/.github/workflows/create-meeting-artifacts-manual.yml new file mode 100644 index 0000000..d819e7a --- /dev/null +++ b/.github/workflows/create-meeting-artifacts-manual.yml @@ -0,0 +1,82 @@ +name: Create Meeting Artifacts (Manual) + +on: + workflow_dispatch: + inputs: + meeting_group: + description: 'Meeting group to create artifacts for' + required: true + type: choice + options: + - benchmarking + - build + - cross_project_council + - diag + - diag_deepdive + - ecosystem_report + - loaders + - modules + - next-10 + - outreach + - package-maintenance + - package_metadata_interop + - Release + - security-wg + - security_collab + - standards + - sustainability_collab + - tsc + - tooling + - typescript + - uvwasi + - userfeedback + - web-server-frameworks + +jobs: + create-artifacts: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Setup Node.js + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + with: + node-version-file: '.nvmrc' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Read Meeting Variables + id: read-vars + run: | + meeting_group="${{ github.event.inputs.meeting_group }}" + user=$(grep '^USER=' "templates/meeting_base_${meeting_group}" | cut -d'=' -f2 | xargs) + echo "user=$user" >> $GITHUB_OUTPUT + + - name: Create GitHub App Token + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 + id: app-token + with: + app-id: ${{ secrets.BOT_ID }} + private-key: ${{ secrets.BOT_PRIVATE_KEY }} + owner: ${{ steps.read-vars.outputs.user }} + + - name: Create meeting artifacts + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} + HACKMD_API_TOKEN: ${{ secrets.HACKMD_API_TOKEN }} + GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} + run: node create-node-meeting-artifacts.mjs ${{ github.event.inputs.meeting_group }} + + - name: Upload artifacts + if: always() + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + with: + name: meeting-artifacts-${{ github.event.inputs.meeting_group || 'tsc' }} + path: | + ~/.make-node-meeting/ + *.md + retention-days: 7 + if-no-files-found: ignore diff --git a/.github/workflows/create-meeting-artifacts-scheduled.yml b/.github/workflows/create-meeting-artifacts-scheduled.yml new file mode 100644 index 0000000..2cc8a0b --- /dev/null +++ b/.github/workflows/create-meeting-artifacts-scheduled.yml @@ -0,0 +1,82 @@ +name: Create Meeting Artifacts (Scheduled) + +on: + workflow_dispatch: + schedule: + - cron: '0 10 * * 1' + +jobs: + create-artifacts: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + meeting_group: + - benchmarking + - build + - cross_project_council + - diag + - diag_deepdive + - ecosystem_report + - loaders + - modules + - next-10 + - outreach + - package-maintenance + - package_metadata_interop + - Release + - security-wg + - security_collab + - standards + - sustainability_collab + - tsc + - tooling + - typescript + - uvwasi + - userfeedback + - web-server-frameworks + steps: + - name: Checkout repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Setup Node.js + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + with: + node-version-file: '.nvmrc' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Read Meeting Variables + id: read-vars + run: | + meeting_group="${{ matrix.meeting_group }}" + user=$(grep '^USER=' "templates/meeting_base_${meeting_group}" | cut -d'=' -f2 | xargs) + echo "user=$user" >> $GITHUB_OUTPUT + + - name: Create GitHub App Token + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 + id: app-token + with: + app-id: ${{ secrets.BOT_ID }} + private-key: ${{ secrets.BOT_PRIVATE_KEY }} + owner: ${{ steps.read-vars.outputs.user }} + + - name: Create meeting artifacts + env: + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} + HACKMD_API_TOKEN: ${{ secrets.HACKMD_API_TOKEN }} + GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} + run: node create-node-meeting-artifacts.mjs ${{ matrix.meeting_group }} + + - name: Upload artifacts + if: always() + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + with: + name: meeting-artifacts-${{ matrix.meeting_group }} + path: | + ~/.make-node-meeting/ + *.md + retention-days: 7 + if-no-files-found: ignore diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..df86373 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,39 @@ +name: Test Suite + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [22.x, 'latest'] + + steps: + - name: Checkout repository + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + with: + node-version: ${{ matrix.node-version }} + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run linting + run: npm run lint + + - name: Run tests + run: npm test + + - name: Check format + run: npm run format:check diff --git a/.gitignore b/.gitignore index 5afa0b2..7f00be4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,15 @@ +# Node.js Packages node_modules/ -client_secret.json -package-lock.json + +# Environment File +.env + +# Test artifacts and temporary files +test/fixtures/temp/ +test/snapshots/*.snap +*.log +coverage/ + +# Files created during unit test runs +*.sh +meeting-test/ diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..0350652 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +templates/ diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..f49129f --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,11 @@ +{ + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "jsxSingleQuote": false, + "trailingComma": "es5", + "bracketSpacing": true, + "bracketSameLine": false, + "arrowParens": "avoid" +} diff --git a/AUTHORS b/AUTHORS index 38f115d..2886933 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1 +1,2 @@ Michael Dawson +Claudio Wunder diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dee67d4..f87e3b9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,7 +40,7 @@ Writing good commit logs is important. A commit log should describe what changed and why. Follow these guidelines when writing one: 1. The first line should be a short description of the change - (e.g. "get-metadata: check if the committer matches the author"). + (e.g. "get-metadata: check if the committer matches the author"). 2. Keep the second line blank. 3. Wrap all lines at 72 columns. @@ -90,11 +90,11 @@ in this project. By making a contribution to this project, I certify that: -* (a) The contribution was created in whole or in part by me and I +- (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or -* (b) The contribution is based upon previous work that, to the best +- (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part @@ -102,11 +102,11 @@ By making a contribution to this project, I certify that: permitted to submit under a different license), as indicated in the file; or -* (c) The contribution was provided directly to me by some other +- (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. -* (d) I understand and agree that this project and the contribution +- (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with diff --git a/LICENSE.md b/LICENSE.md index 26a999d..dc92bbb 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,10 +1,8 @@ -The MIT License (MIT) -===================== +# The MIT License (MIT) -Copyright (c) 2018 create-node-meeting-artifacts authors -------------------------------------------------------------- +## Copyright (c) 2018 create-node-meeting-artifacts authors -*contributors listed at * +_contributors listed at _ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/README.md b/README.md index 6b037e5..c9ae205 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,246 @@ -# create-node-meeting-artifacts -Tool to create artifacts for node.js team meetings +# Node.js Meeting Artifacts Creator -Uses the Foundation calendar to find the next instance of the meeting, -and then creates an issue and matching google doc for minutes. The key -thing is that it automatically creates the issue in github and the doc -in google docs as opposed to just creating the content. +A modern Node.js application that creates GitHub issues and HackMD documents for Node.js team meetings. This tool automates the process of reading meeting configuration, fetching calendar events, creating meeting minutes documents, and posting GitHub issues. -Re-uses make-node-meeting for much of the content generation. +## 📋 Requirements -The hardest part to get going is doing the google auth setup -as described in: https://github.com/mhdawson/google-auth-wrapper. +- Node.js 22+ (LTS) +- GitHub Personal Access Token +- Google Cloud Project with Calendar API enabled (for meeting scheduling) +- Google API Key for Calendar access +- HackMD API Token (for meeting minutes) -Basic documentation for each of the templates lives in [TEMPLATES_DOCUMENTATION.md](./TEMPLATES_DOCUMENTATION.md) +## 🔑 Authentication Setup -Currently I'm testing out for generation of the Node.js TSC meetings. +### GitHub Authentication +1. Create a [GitHub Personal Access Token](https://github.com/settings/tokens) +2. Grant the following permissions: + - `repo` (Full control of private repositories) + - `user` (Read user information) -NOTE: The following must be commented out of make-node-meeting +### HackMD Authentication +1. Go to [HackMD](https://hackmd.io/) and sign in to your account +2. Navigate to Account Settings > API Tokens +3. Create a new API token for the meeting artifacts tool +4. Optionally, create or join a team workspace for better organization +### Google Authentication (Calendar Only) + +#### API Key Authentication (Recommended) + +1. Go to [Google Cloud Console](https://console.cloud.google.com/) +2. Create a new project or select an existing one +3. Enable the Google Calendar API +4. Go to **Credentials** → **Create Credentials** → **API Key** +5. Restrict the API key to the Google Calendar API for security +6. Add the API key to your environment variables as `GOOGLE_API_KEY` + +**Note:** API Keys provide simplified authentication and are sufficient for read-only calendar access. They don't require complex OAuth flows or service account setup. + +## 🎯 Available Meeting Commands + +| Meeting Group | Production Command | Development Command | +| ------------------------ | ------------------------------------------ | ---------------------------------------------- | +| UVWASI | `npm run uvwasi-meeting` | `npm run uvwasi-meeting:dev` | +| TSC | `npm run tsc-meeting` | `npm run tsc-meeting:dev` | +| Build | `npm run build-meeting` | `npm run build-meeting:dev` | +| Diagnostics | `npm run diag-meeting` | `npm run diag-meeting:dev` | +| Diagnostics Deep Dive | `npm run diag-deepdive-meeting` | `npm run diag-deepdive-meeting:dev` | +| TypeScript | `npm run typescript-meeting` | `npm run typescript-meeting:dev` | +| Release | `npm run release-meeting` | `npm run release-meeting:dev` | +| Cross Project Council | `npm run cross-project-council-meeting` | `npm run cross-project-council-meeting:dev` | +| Modules | `npm run modules-meeting` | `npm run modules-meeting:dev` | +| Tooling | `npm run tooling-meeting` | `npm run tooling-meeting:dev` | +| Security WG | `npm run security-wg-meeting` | `npm run security-wg-meeting:dev` | +| Next-10 | `npm run next-10-meeting` | `npm run next-10-meeting:dev` | +| Package Maintenance | `npm run package-maintenance-meeting` | `npm run package-maintenance-meeting:dev` | +| Package Metadata Interop | `npm run package-metadata-interop-meeting` | `npm run package-metadata-interop-meeting:dev` | +| Ecosystem Report | `npm run ecosystem-report-meeting` | `npm run ecosystem-report-meeting:dev` | +| Sustainability Collab | `npm run sustainability-collab-meeting` | `npm run sustainability-collab-meeting:dev` | +| Standards | `npm run standards-meeting` | `npm run standards-meeting:dev` | +| Security Collab | `npm run security-collab-meeting` | `npm run security-collab-meeting:dev` | +| Loaders | `npm run loaders-meeting` | `npm run loaders-meeting:dev` | +| Web Server Frameworks | `npm run web-server-frameworks-meeting` | `npm run web-server-frameworks-meeting:dev` | + +## 📁 Project Structure + +``` +create-node-meeting-artifacts/ +├── src/ +│ ├── config.mjs # Configuration management +│ ├── constants.mjs # Application constants +│ ├── github.mjs # GitHub API integration +│ ├── google.mjs # Google APIs integration +│ ├── meeting.mjs # Meeting operations +│ └── utils.mjs # Utility functions +├── templates/ # Meeting templates +├── .nvmrc # Node.js version +├── .env.example # Environment variables example +├── create-node-meeting-artifacts.mjs # Main application +├── TEMPLATES_DOCUMENTATION.md # Template creation guide +└── README.md # This file +``` + +## 📝 Meeting Templates + +Meeting configurations are stored in the `templates/` directory. Each meeting group requires four template files: + +- `invited_`: List of invited attendees (GitHub team mentions) +- `observers_`: List of observers with their details +- `meeting_base_`: Base meeting configuration (calendar ID, GitHub repo, etc.) +- `minutes_base_`: Template for meeting minutes document + +For detailed information about creating new templates, see [TEMPLATES_DOCUMENTATION.md](./TEMPLATES_DOCUMENTATION.md). + +### Template Variables + +Templates support the following replacement variables: + +- `$TITLE$`: Meeting title +- `$AGENDA_CONTENT$`: Auto-generated agenda items +- `$INVITED$`: Invited attendees list +- `$OBSERVERS$`: Observers list +- `$GITHUB_ISSUE$`: GitHub issue URL +- `$MINUTES_DOC$`: Google Doc URL + +## ➕ Adding New Meeting Groups + +To add a new meeting group to the system, you need to create templates and update configuration in three places: + +### 1. Create Template Files + +Create four template files in the `templates/` directory following the naming convention: + +```bash +# Replace with your meeting group identifier +templates/invited_ +templates/observers_ +templates/meeting_base_ +templates/minutes_base_ +``` + +See [TEMPLATES_DOCUMENTATION.md](./TEMPLATES_DOCUMENTATION.md) for detailed template examples and variable explanations. + +### 2. Update GitHub Actions Workflows + +Add your meeting group to both workflow files: + +- `.github/workflows/create-meeting-artifacts-manual.yml` +- `.github/workflows/create-meeting-artifacts-scheduled.yml` + +For manual workflow, add your group to the `options` list under `workflow_dispatch.inputs.meeting_group`: + +```yaml +workflow_dispatch: + inputs: + meeting_group: + description: 'Meeting group to create artifacts for' + required: true + type: choice + options: + - uvwasi + - tsc + - build + # ... existing groups ... + - your-new-group # Add your group here +``` + +For scheduled workflow, add your group to the `matrix.meeting_group` list: + +```yaml +strategy: + matrix: + meeting_group: + - uvwasi + - tsc + - build + # ... existing groups ... + - your-new-group # Add your group here +``` + +### 3. Update Package.json Scripts + +Add npm scripts to `package.json` following this pattern: + +```json +{ + "scripts": { + "your-meeting-group-meeting": "node create-node-meeting-artifacts.mjs your_meeting_group", + "your-meeting-group-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs your_meeting_group" + } +} ``` -echo -n "Previous Meeting Minutes Google Docs URL: " -read prev_doc_url -echo -n "This Meeting Minutes Google Docs URL: " -read curr_doc_url + +**Important Notes:** + +- Use **kebab-case** for script names: `your-meeting-group-meeting` +- Use **snake_case** for the actual group parameter: `your_meeting_group` +- Always create both production and development (`:dev`) versions +- The development version uses `--env-file=.env` for local testing + +## 🏗️ Development + +### Environment Setup + +1. Clone the repository +2. Install dependencies: `npm install` +3. Copy `.env.example` to `.env` and configure your credentials +4. Create meeting artifacts: `npm run -meeting:dev` + +### Code Quality + +```bash +npm run lint # Run ESLint +npm run lint:fix # Fix ESLint issues automatically +npm run format # Format code with Prettier +npm run format:check # Check code formatting +npm run check # Run both linting and formatting checks +``` + +## 🚀 Usage + +### Local Development + +```bash +# Using npm scripts (recommended) +npm run tsc-meeting:dev + +# Direct execution +node --env-file=.env create-node-meeting-artifacts.mjs tsc +``` + +## 📂 Output + +The application creates: + +1. **GitHub Issue**: Posted to the configured repository with meeting details and agenda +2. **HackMD Document**: Meeting minutes document in Markdown format with collaborative editing +3. **Console Output**: Links to both the created issue and HackMD document + +## 🔧 Configuration + +### Environment Variables + +#### Required + +- `GITHUB_TOKEN`: GitHub Personal Access Token +- `HACKMD_API_TOKEN`: HackMD API token for creating and managing documents +- `GOOGLE_API_KEY`: Google Calendar API Key for read-only calendar access + +### Meeting Base Configuration + +Each `meeting_base_` file contains: + +```bash +CALENDAR_FILTER="Meeting Name in Calendar" +CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +USER="nodejs" +REPO="repository-name" +GROUP_NAME="Full Group Name" +AGENDA_TAG="agenda-label" +ISSUE_LABEL="optional-issue-label" +HACKMD_TEAM_NAME="openjs-nodejs" +JOINING_INSTRUCTIONS="Meeting join instructions" ``` diff --git a/TEMPLATES_DOCUMENTATION.md b/TEMPLATES_DOCUMENTATION.md index 1bda9e6..279950f 100644 --- a/TEMPLATES_DOCUMENTATION.md +++ b/TEMPLATES_DOCUMENTATION.md @@ -1,6 +1,8 @@ This document contains empty versions of each template needed to successfully create meeting artifacts for a Committee, Working Group, Initiative, or Team. These documents need to go in the [/templates](./templates) directory in this repository. -There are only four variables in the documents that _need_ to be changed: +## Template Variables + +There are several variables in the documents that need to be configured: - **``:** The name of the Committee, Working Group, Initiative, or Team that meeting artifacts are being created for. - **``:** The abbreviated or shortened name for a group, used in each filename to connect associated files together. @@ -11,24 +13,46 @@ There are only four variables in the documents that _need_ to be changed: - **``:** an optional label for the created issue itself. - **``:** Name of an observer in a group's meetings. +## Meeting Properties Reference + +The following properties are available in meeting base templates and can be used in meeting issue generation: + +- **`CALENDAR_FILTER`:** The name of calendar events that mark the group's meeting date/time +- **`CALENDAR_ID`:** The Google Calendar ID for the Node.js calendar (typically `nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com`) +- **`USER`:** The GitHub username/organization (typically `nodejs`) +- **`REPO`:** The repository name where meeting issues are created +- **`GROUP_NAME`:** The full name of the Committee, Working Group, Initiative, or Team +- **`AGENDA_TAG`:** The label used to search for agenda items in GitHub issues and PRs +- **`HOST`:** Meeting host information +- **`JOINING_INSTRUCTIONS`:** Instructions for joining the meeting (Zoom links, YouTube streams, etc.) +- **`ISSUE_LABEL`:** Optional label to apply to the created meeting issue + +These properties are defined in the `meeting_base_` template files and are substituted when generating meeting issues. + # Invited + The [GitHub Team](https://help.github.com/articles/about-teams/) to invite. The @mention should be a GitHub Team whose members are all invidiuals who are always invited. **File:** `invited_` + ``` * Members: @nodejs/ ``` ## Invited Example + **File:** `invited_commcomm` + ``` * CommComm Members: @nodejs/community-committee ``` # Meeting Base + A base of metadata and some content for the issue to be created on time, with agenda items automatically created. **File:** `meeting_base_` + ``` CALENDAR_FILTER="" CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" @@ -43,7 +67,9 @@ JOINING_INSTRUCTIONS=" ``` ## Meeting Base Example + **File:** `meeting_base_commcomm` + ``` CALENDAR_FILTER="Node.js Community Committee" CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" @@ -58,15 +84,17 @@ JOINING_INSTRUCTIONS=" ``` # Minutes Base + A basic outline for the meeting minutes to be autogenerated in Google Docs. The only basic change from the default template is the message about what label agenda items are extracted from. **File:** `minutes_base_` + ``` ## Links * **Recording**: * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -87,12 +115,14 @@ $AGENDA_CONTENT$ * **Node.js Foundation Calendar**: https://nodejs.org/calendar -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. ``` ## Minutes Base Example + **File:** `minutes_base_commcomm` + ``` @@ -100,7 +130,7 @@ Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. * **Recording**: * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -121,12 +151,14 @@ $AGENDA_CONTENT$ * **Node.js Foundation Calendar**: https://nodejs.org/calendar -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. ``` # Observers + List meeting observers who will consistently attend meetings _as observers_. **File:** `observers_` + ``` * * @@ -135,7 +167,9 @@ List meeting observers who will consistently attend meetings _as observers_. ``` ## Observers Example + **File:** `observers_commcomm` + ``` * @therebelrobot (Oz Haven - observer) * @ParidelPooya (Pooya Paridel - observer) diff --git a/bin/create-meeting b/bin/create-meeting deleted file mode 100755 index 5d9c984..0000000 --- a/bin/create-meeting +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env node -'use strict'; - -const path = require('path'); -const { spawn } = require('child_process'); - -let group = process.argv[2]; -if(group === undefined) { - console.error('please pass in group as a argument'); - process.exit(1); -} -group = group.toLowerCase() + '-meeting'; - -spawn('npm', [ 'run', group ], { stdio: 'inherit' }); diff --git a/create-node-meeting-artifacts.js b/create-node-meeting-artifacts.js deleted file mode 100644 index 719a0e4..0000000 --- a/create-node-meeting-artifacts.js +++ /dev/null @@ -1,159 +0,0 @@ -'use strict' -const fs = require('fs'); -const path = require('path'); -const process = require('process'); -const child_process = require('child_process'); -const GitHubApi = require("github"); -const ghauth = require('ghauth'); -const parser = require('properties-parser'); -const gcal = require('google-calendar'); -const googleAuth = require('google-auth-wrapper'); -const gdriveWrapper = require('google-drive-wrapper'); -const meetingGroup = process.argv[2] || 'tsc'; - -const authOptions = { configName: 'iojs-tools', scopes: [ 'user', 'repo' ] }; -const repos = []; - -let githubOrg = 'nodejs'; - -const github = new GitHubApi({ -}); - -ghauth(authOptions, (err, authData) => { - if (err) { - throw err; - } - - // first authenticate to google and github - googleAuth.execute('./', 'client_secret', (googleAuthToken, google) => { - github.authenticate({ - type: "token", - token: authData.token - }); - - // ok all authenticated - - // read in the configuration for the meeting - const invited = fs.readFileSync(path.join('templates', - 'invited_' + - meetingGroup)).toString(); - const observers = fs.readFileSync(path.join('templates', - 'observers_' + - meetingGroup)).toString(); - const baseMeetingInfo = fs.readFileSync(path.join('templates', - 'meeting_base_' + - meetingGroup)); - const meetingProperties = parser.parse(baseMeetingInfo); - - var meetingGroupForTag = meetingGroup; - if (meetingProperties.AGENDA_TAG) { - meetingGroupForTag = meetingProperties.AGENDA_TAG.replace('-agenda', ''); - } - - if (meetingProperties.GITHUB_ORG) { - githubOrg = meetingProperties.GITHUB_ORG.replace(/"/g, ''); - } - - // find the next meeting instance in the google calendar. We assume 1 meeting - // in the next week - const calendar = google.calendar('v3'); - calendar.events.list({ - auth: googleAuthToken, - calendarId: meetingProperties.CALENDAR_ID.replace(/"/g, ''), - timeMin: (new Date()).toISOString(), - timeMax: (new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)).toISOString(), - singleEvents: true, - q: meetingProperties.CALENDAR_FILTER.replace(/"/g, '').replace(/ /g, '.'), - maxResults: 2, - }, (err, response) => { - if (err) { - throw err; - } - - const events = response.items; - if (events.length == 0) { - console.log('Could not find calendar event'); - } else { - // extract the time and complete the meeting info for make-node-meeting - const meetingTime = (new Date(events[0].start.dateTime)).toISOString(); - const meetingInfo = baseMeetingInfo.toString() + - 'MEETING_TIME="' + meetingTime + '"\n' + - 'INVITEES="' + - invited + - '\n' + - '### Observers/Guests\n' + - observers + - '"'; - - fs.writeFileSync( - path.join(process.env.HOME, - '.make-node-meeting/' + meetingGroupForTag + '.sh'), - meetingInfo); - - // generate the meeting issue content with make-node-meeting - var newIssue = child_process.spawnSync( - path.join(__dirname, 'node_modules/make-node-meeting/make-node-meeting.sh'), - [ meetingGroupForTag ]).stdout.toString(); - - // parse out the title - const issueLines = newIssue.split('\n'); - const title = issueLines[1]; - newIssue = issueLines.splice(4).join('\n'); - - - // create the minutes document - const agendaInfo = child_process.spawnSync( - 'node', - ['node_modules/node-meeting-agenda/node-meeting-agenda.js', - meetingGroupForTag + '-agenda', githubOrg]).stdout.toString(); - let minutesDoc = fs.readFileSync(path.join('templates', - 'minutes_base_' + - meetingGroup)).toString(); - - minutesDoc = minutesDoc.replace('$TITLE$', title); - minutesDoc = minutesDoc.replace('$AGENDA_CONTENT$', agendaInfo); - minutesDoc = minutesDoc.replace('$INVITED$', invited); - minutesDoc = minutesDoc.replace('$OBSERVERS$', observers); - const minutesDocName = path.join(__dirname, 'minutes_temp.txt'); - fs.writeFileSync( minutesDocName, minutesDoc); - - // upload the minutes doc - const wrapper = new gdriveWrapper(googleAuthToken, google, 'dummy' ); - wrapper.getMetaForFilename('/nodejs-meetings', function(err, parentMeta) { - if (err !== null) { - console.log('Directory called "nodejs-meetings" does not exist, exiting'); - process.exit(-1); - } - - wrapper.uploadFile(title, minutesDocName, - { parent: parentMeta.id, - compress: false, - encrypt: false, - convert: true, - mimeType: 'application/vnd.google-apps.document' }, - function(err, meta) { - if (err !== null) { - console.log('Failed to upload minutes file'); - console.log(err); - } else { - // create the issue in github - newIssue = newIssue.toString().replace( - /\* \*\*Minutes Google Doc\*\*: <>/, - '* Minutes Google Doc: '); - newIssue = newIssue.replace(/\* _Previous Minutes Google Doc: <>_/,''); - let issueLabel = (meetingProperties.ISSUE_LABEL || '').replace(/"/g, ''); - github.issues.create({ - owner: meetingProperties.USER.replace(/"/g, ''), - repo: meetingProperties.REPO.replace(/"/g, ''), - title: title, - body: newIssue, - assignee: "mhdawson", - labels: issueLabel ? [issueLabel] : undefined - }); - } - }); - }); - } - }); - }); -}) diff --git a/create-node-meeting-artifacts.mjs b/create-node-meeting-artifacts.mjs new file mode 100644 index 0000000..70a0bd1 --- /dev/null +++ b/create-node-meeting-artifacts.mjs @@ -0,0 +1,103 @@ +#!/usr/bin/env node + +/** + * Node.js Meeting Artifacts Creator + * + * Usage: + * node create-node-meeting-artifacts.mjs [meetingGroup] + * npm run tsc-meeting + * npm run dev -- tsc + */ + +import { getConfig } from './src/config.mjs'; +import * as github from './src/github.mjs'; +import * as google from './src/google.mjs'; +import * as hackmd from './src/hackmd.mjs'; +import * as meetings from './src/meeting.mjs'; + +// Step 1: Application configuration +const config = getConfig(); + +// Step 2: Initialize Google Calendar client with API Key +const calendarClient = google.createCalendarClient(config.google); + +// Step 3: Initialize GitHub client +const githubClient = github.createGitHubClient(config); + +// Step 4: Read meeting configuration from templates +const meetingConfig = await meetings.readMeetingConfig(config); + +// Step 5: Initialize HackMD client with meeting configuration +const hackmdClient = hackmd.createHackMDClient(config, meetingConfig); + +// Step 6: Find next meeting event in calendar +const event = await google.findNextMeetingEvent(calendarClient, meetingConfig); + +// Step 7: Extract meeting date from event +const meetingDate = new Date(event.start.dateTime); + +// Step 8: Get Meeting Title +const meetingTitle = meetings.generateMeetingTitle( + config, + meetingConfig, + meetingDate +); + +// Step 9: Get agenda information using native implementation +const gitHubAgendaIssues = await github.getAgendaIssues( + githubClient, + config, + meetingConfig +); + +// Step 10: Parse meeting agenda from GitHub issues +const meetingAgenda = meetings.generateMeetingAgenda(gitHubAgendaIssues); + +// Step 11: Create HackMD document with meeting notes and tags +const hackmdNote = await hackmd.createMeetingNotesDocument( + hackmdClient, + meetingTitle, + '' +); + +// Step 12: Get the HackMD document link +const minutesDocLink = + hackmdNote.publishLink || `https://hackmd.io/${hackmdNote.id}`; + +// Step 13: Generate meeting issue content using native implementation +const issueContent = await meetings.generateMeetingIssue( + config, + meetingConfig, + meetingDate, + meetingAgenda, + minutesDocLink +); + +// Step 14: Create GitHub issue with HackMD link +const githubIssue = await github.createGitHubIssue( + githubClient, + meetingConfig, + meetingTitle, + issueContent +); + +// Step 15: Update the minutes content with the HackMD link +const minutesContent = await meetings.generateMeetingMinutes( + config, + meetingConfig, + meetingTitle, + meetingAgenda, + minutesDocLink, + githubIssue.html_url +); + +// Step 16: Update the HackMD document with the self-referencing link +await hackmd.updateMeetingNotesDocument( + hackmdClient, + hackmdNote.id, + minutesContent +); + +// Output success information with links +console.log(`Created GitHub issue: ${githubIssue.html_url}`); +console.log(`Created HackMD document: ${minutesDocLink}`); diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..979c62d --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,67 @@ +import pluginJs from '@eslint/js'; +import importX from 'eslint-plugin-import-x'; +import jsdoc from 'eslint-plugin-jsdoc'; +import globals from 'globals'; + +export default [ + pluginJs.configs.recommended, + importX.flatConfigs.recommended, + { + ignores: ['node_modules', '.env*', 'minutes_temp.txt'], + }, + { + files: ['**/*.mjs'], + rules: { + 'object-shorthand': 'error', + 'import-x/namespace': 'off', + 'import-x/no-named-as-default': 'off', + 'import-x/no-named-as-default-member': 'off', + 'import-x/no-unresolved': 'off', + 'import-x/order': [ + 'error', + { + groups: [ + 'builtin', + 'external', + 'internal', + ['sibling', 'parent'], + 'index', + 'unknown', + ], + 'newlines-between': 'always', + alphabetize: { + order: 'asc', + caseInsensitive: true, + }, + }, + ], + }, + }, + { + files: ['src/**/*.mjs', 'bin/**/*', '*.mjs'], + plugins: { + jsdoc, + }, + languageOptions: { + ecmaVersion: 'latest', + globals: { ...globals.nodeBuiltin }, + }, + rules: { + 'jsdoc/check-alignment': 'error', + 'jsdoc/check-indentation': 'error', + 'jsdoc/require-jsdoc': [ + 'error', + { + require: { + FunctionDeclaration: true, + MethodDefinition: true, + ClassDeclaration: true, + ArrowFunctionExpression: true, + FunctionExpression: true, + }, + }, + ], + 'jsdoc/require-param': 'error', + }, + }, +]; diff --git a/global.d.ts b/global.d.ts new file mode 100644 index 0000000..dfc2a0c --- /dev/null +++ b/global.d.ts @@ -0,0 +1,21 @@ +/** + * Global TypeScript definitions for JSDoc usage + * This file makes types from third-party libraries available globally without imports in JSDoc comments + */ + +import type { calendar_v3 } from '@googleapis/calendar'; +import type { RestEndpointMethodTypes } from '@octokit/rest'; +import type { API } from '@hackmd/api'; +import type { SingleNote } from '@hackmd/api/dist/type.d.ts'; + +declare global { + // Google API type aliases + type CalendarEvent = calendar_v3.Schema$Event; + type CalendarClient = calendar_v3.Calendar; + type HackMDClient = API; + type HackMDNote = SingleNote; + + // GitHub API type aliases + type GitHubIssue = + RestEndpointMethodTypes['issues']['create']['response']['data']; +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..e6462f1 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2778 @@ +{ + "name": "create-node-meeting-artifacts", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "create-node-meeting-artifacts", + "version": "0.0.1", + "license": "MIT", + "dependencies": { + "@googleapis/calendar": "^11.0.1", + "@hackmd/api": "^2.5.0", + "@octokit/rest": "^22.0.0", + "dedent": "^1.6.0", + "dotenv": "^17.2.2" + }, + "bin": { + "create-meeting": "create-node-meeting-artifacts.mjs" + }, + "devDependencies": { + "@eslint/js": "^9.33.0", + "@types/properties-parser": "^0.3.3", + "eslint": "^9.33.0", + "eslint-plugin-import-x": "^4.16.1", + "eslint-plugin-jsdoc": "^54.0.0", + "globals": "^16.3.0", + "prettier": "^3.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", + "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/core/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/@emnapi/runtime": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", + "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz", + "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.52.0.tgz", + "integrity": "sha512-BXuN7BII+8AyNtn57euU2Yxo9yA/KUDNzrpXyi3pfqKmBhhysR6ZWOebFh3vyPoqA3/j1SOvGgucElMGwlXing==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.8", + "@typescript-eslint/types": "^8.34.1", + "comment-parser": "1.4.1", + "esquery": "^1.6.0", + "jsdoc-type-pratt-parser": "~4.1.0" + }, + "engines": { + "node": ">=20.11.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", + "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.33.0.tgz", + "integrity": "sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@googleapis/calendar": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/@googleapis/calendar/-/calendar-11.0.1.tgz", + "integrity": "sha512-XR6/V8SjuOyGMMx5shcKB2Ouyqkw2OG5a1srCb6ifPkIJG5qu/aocQ4VVizPc89izgdCGaKqcSpRFNJUsUuizg==", + "license": "Apache-2.0", + "dependencies": { + "googleapis-common": "^8.0.2-rc.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@hackmd/api": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@hackmd/api/-/api-2.5.0.tgz", + "integrity": "sha512-eG4COWt2odRwiUHgJc3vP73iUS2vuAN1ECpPbKF0kRNJt6YerlsAsf3TNBs/CfVuemsC3g7JeAZpIrT7gmsTRQ==", + "license": "MIT", + "dependencies": { + "axios": "^1.8.4", + "tslib": "^1.14.1" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", + "integrity": "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.10.0" + } + }, + "node_modules/@octokit/auth-token": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", + "license": "MIT", + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/core": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.3.tgz", + "integrity": "sha512-oNXsh2ywth5aowwIa7RKtawnkdH6LgU1ztfP9AIUCQCvzysB+WeU8o2kyyosDPwBZutPpjZDKPQGIzzrfTWweQ==", + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "^9.0.1", + "@octokit/request": "^10.0.2", + "@octokit/request-error": "^7.0.0", + "@octokit/types": "^14.0.0", + "before-after-hook": "^4.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/endpoint": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.0.tgz", + "integrity": "sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/graphql": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.1.tgz", + "integrity": "sha512-j1nQNU1ZxNFx2ZtKmL4sMrs4egy5h65OMDmSbVyuCzjOcwsHq6EaYjOTGXPQxgfiN8dJ4CriYHk6zF050WEULg==", + "license": "MIT", + "dependencies": { + "@octokit/request": "^10.0.2", + "@octokit/types": "^14.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-25.1.0.tgz", + "integrity": "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==", + "license": "MIT" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-13.1.1.tgz", + "integrity": "sha512-q9iQGlZlxAVNRN2jDNskJW/Cafy7/XE52wjZ5TTvyhyOD904Cvx//DNyoO3J/MXJ0ve3rPoNWKEg5iZrisQSuw==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.1.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz", + "integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-16.0.0.tgz", + "integrity": "sha512-kJVUQk6/dx/gRNLWUnAWKFs1kVPn5O5CYZyssyEoNYaFedqZxsfYs7DwI3d67hGz4qOwaJ1dpm07hOAD1BXx6g==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.1.0" + }, + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/request": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.3.tgz", + "integrity": "sha512-V6jhKokg35vk098iBqp2FBKunk3kMTXlmq+PtbV9Gl3TfskWlebSofU9uunVKhUN7xl+0+i5vt0TGTG8/p/7HA==", + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^11.0.0", + "@octokit/request-error": "^7.0.0", + "@octokit/types": "^14.0.0", + "fast-content-type-parse": "^3.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/request-error": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.0.0.tgz", + "integrity": "sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg==", + "license": "MIT", + "dependencies": { + "@octokit/types": "^14.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/rest": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.0.tgz", + "integrity": "sha512-z6tmTu9BTnw51jYGulxrlernpsQYXpui1RK21vmXn8yF5bp6iX16yfTtJYGK5Mh1qDkvDOmp2n8sRMcQmR8jiA==", + "license": "MIT", + "dependencies": { + "@octokit/core": "^7.0.2", + "@octokit/plugin-paginate-rest": "^13.0.1", + "@octokit/plugin-request-log": "^6.0.0", + "@octokit/plugin-rest-endpoint-methods": "^16.0.0" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/@octokit/types": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-14.1.0.tgz", + "integrity": "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==", + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^25.1.0" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz", + "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tybys/wasm-util/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.2.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz", + "integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.10.0" + } + }, + "node_modules/@types/properties-parser": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@types/properties-parser/-/properties-parser-0.3.3.tgz", + "integrity": "sha512-VZhGpE+QQ2JNbaY4B4Y2iM/jdUsq3HO75uBKLk07VT6P2Kg1aifeYL6I3RosFniSdAb4PtuH5UaY8jXU7JeIYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.39.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.1.tgz", + "integrity": "sha512-7sPDKQQp+S11laqTrhHqeAbsCfMkwJMrV7oTDvtDds4mEofJYir414bYKUEb8YPUm9QL3U+8f6L6YExSoAGdQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.11.1.tgz", + "integrity": "sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.11.1.tgz", + "integrity": "sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.11.1.tgz", + "integrity": "sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.11.1.tgz", + "integrity": "sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.11.1.tgz", + "integrity": "sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.11.1.tgz", + "integrity": "sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.11.1.tgz", + "integrity": "sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.11.1.tgz", + "integrity": "sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.11.1.tgz", + "integrity": "sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.11.1.tgz", + "integrity": "sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.11.1.tgz", + "integrity": "sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.11.1.tgz", + "integrity": "sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.11.1.tgz", + "integrity": "sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.11.1.tgz", + "integrity": "sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.11.1.tgz", + "integrity": "sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.11.1.tgz", + "integrity": "sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.11.1.tgz", + "integrity": "sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.11.1.tgz", + "integrity": "sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.11.1.tgz", + "integrity": "sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/before-after-hook": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", + "license": "Apache-2.0" + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dotenv": { + "version": "17.2.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.2.tgz", + "integrity": "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.33.0.tgz", + "integrity": "sha512-TS9bTNIryDzStCpJN93aC5VRSW3uTx9sClUn4B87pwiCaJh220otoI0X8mJKr+VcPtniMdN8GKjlwgWGUv5ZKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.1", + "@eslint/core": "^0.15.2", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.33.0", + "@eslint/plugin-kit": "^0.3.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-import-context": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.9.tgz", + "integrity": "sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-tsconfig": "^4.10.1", + "stable-hash-x": "^0.2.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-context" + }, + "peerDependencies": { + "unrs-resolver": "^1.0.0" + }, + "peerDependenciesMeta": { + "unrs-resolver": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-import-x": { + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import-x/-/eslint-plugin-import-x-4.16.1.tgz", + "integrity": "sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "^8.35.0", + "comment-parser": "^1.4.1", + "debug": "^4.4.1", + "eslint-import-context": "^0.1.9", + "is-glob": "^4.0.3", + "minimatch": "^9.0.3 || ^10.0.1", + "semver": "^7.7.2", + "stable-hash-x": "^0.2.0", + "unrs-resolver": "^1.9.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-import-x" + }, + "peerDependencies": { + "@typescript-eslint/utils": "^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "eslint-import-resolver-node": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/utils": { + "optional": true + }, + "eslint-import-resolver-node": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-import-x/node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/eslint-plugin-jsdoc": { + "version": "54.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-54.0.0.tgz", + "integrity": "sha512-8w5c8OmmD5WD5MNQy1AhmYbiyV4IlSscXUyg5MwvN3BI/bLUmRpeEXc+Mj37y2UZsLhzvHyCscQenUzvbLxQ7Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/jsdoccomment": "~0.52.0", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.1", + "debug": "^4.4.1", + "escape-string-regexp": "^4.0.0", + "espree": "^10.4.0", + "esquery": "^1.6.0", + "parse-imports-exports": "^0.2.4", + "semver": "^7.7.2", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": ">=20.11.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-content-type-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gaxios": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.1.tgz", + "integrity": "sha512-Odju3uBUJyVCkW64nLD4wKLhbh93bh6vIg/ZIXkWiLPBrdgtc65+tls/qml+un3pr6JqYVFDZbbmLDQT68rTOQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-7.0.1.tgz", + "integrity": "sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-tsconfig": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz", + "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/google-auth-library": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.2.1.tgz", + "integrity": "sha512-HMxFl2NfeHYnaL1HoRIN1XgorKS+6CDaM+z9LSSN+i/nKDDL4KFFEWogMXu7jV4HZQy2MsxpY+wA5XIf3w410A==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.0.0", + "gcp-metadata": "^7.0.0", + "google-logging-utils": "^1.0.0", + "gtoken": "^8.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.1.tgz", + "integrity": "sha512-rcX58I7nqpu4mbKztFeOAObbomBbHU2oIb/d3tJfF3dizGSApqtSwYJigGCooHdnMyQBIw8BrWyK96w3YXgr6A==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/googleapis-common": { + "version": "8.0.2-rc.0", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-8.0.2-rc.0.tgz", + "integrity": "sha512-JTcxRvmFa9Ec1uyfMEimEMeeKq1sHNZX3vn2qmoUMtnvixXXvcqTcbDZvEZXkEWpGlPlOf4joyep6/qs0BrLyg==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "gaxios": "^7.0.0-rc.4", + "google-auth-library": "^10.0.0-rc.1", + "qs": "^6.7.0", + "url-template": "^2.0.8" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gtoken": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", + "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", + "license": "MIT", + "dependencies": { + "gaxios": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", + "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/napi-postinstall": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz", + "integrity": "sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-imports-exports": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", + "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-statements": "1.0.11" + } + }, + "node_modules/parse-statements": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", + "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/stable-hash-x": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/stable-hash-x/-/stable-hash-x-0.2.0.tgz", + "integrity": "sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/undici-types": { + "version": "7.10.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", + "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/universal-user-agent": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "license": "ISC" + }, + "node_modules/unrs-resolver": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.11.1.tgz", + "integrity": "sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.0" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.11.1", + "@unrs/resolver-binding-android-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-arm64": "1.11.1", + "@unrs/resolver-binding-darwin-x64": "1.11.1", + "@unrs/resolver-binding-freebsd-x64": "1.11.1", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.11.1", + "@unrs/resolver-binding-linux-arm64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-arm64-musl": "1.11.1", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-riscv64-musl": "1.11.1", + "@unrs/resolver-binding-linux-s390x-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-gnu": "1.11.1", + "@unrs/resolver-binding-linux-x64-musl": "1.11.1", + "@unrs/resolver-binding-wasm32-wasi": "1.11.1", + "@unrs/resolver-binding-win32-arm64-msvc": "1.11.1", + "@unrs/resolver-binding-win32-ia32-msvc": "1.11.1", + "@unrs/resolver-binding-win32-x64-msvc": "1.11.1" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", + "license": "BSD" + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json index 4a54335..e5dc8fd 100644 --- a/package.json +++ b/package.json @@ -2,28 +2,82 @@ "name": "create-node-meeting-artifacts", "version": "0.0.1", "description": "Creates issue and minutes doc for node meeting", - "main": "index.js", + "type": "module", + "main": "create-node-meeting-artifacts.mjs", "bin": { - "create-meeting": "./bin/create-meeting" + "create-meeting": "./create-node-meeting-artifacts.mjs" + }, + "engines": { + "node": ">=22.0.0" }, "dependencies": { - "ghauth": "^3.2.1", - "github": "^11.0.0", - "google-auth-wrapper": "^0.5.0", - "google-calendar": "^1.3.2", - "google-drive-wrapper": "^0.6.0", - "make-node-meeting": "^1.0.11", - "node-meeting-agenda": "^1.0.4", - "properties-parser": "^0.3.1" + "@googleapis/calendar": "^11.0.1", + "@hackmd/api": "^2.5.0", + "@octokit/rest": "^22.0.0", + "dedent": "^1.6.0", + "dotenv": "^17.2.2" + }, + "devDependencies": { + "@eslint/js": "^9.33.0", + "@types/properties-parser": "^0.3.3", + "eslint": "^9.33.0", + "eslint-plugin-import-x": "^4.16.1", + "eslint-plugin-jsdoc": "^54.0.0", + "globals": "^16.3.0", + "prettier": "^3.6.2" }, - "devDependencies": {}, "scripts": { - "tsc-meeting": "node create-node-meeting-artifacts.js tsc", - "benchmarking-meeting": "node create-node-meeting-artifacts.js benchmarking", - "release-meeting": "node create-node-meeting-artifacts.js Release", - "cc-meeting": "node create-node-meeting-artifacts.js commcomm", - "diag": "node create-node-meeting-artifacts.js diag", - "all": "npm run tsc-meeting && npm run benchmarking-meeting && npm run release-meeting && npm run cc-meeting && npm run diag" + "dev": "node --env-file=.env create-node-meeting-artifacts.mjs", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write .", + "format:check": "prettier --check .", + "check": "npm run lint && npm run format:check", + "test": "node --test test/**/*.test.mjs", + "test:watch": "node --test --watch test/**/*.test.mjs", + "test:coverage": "node --test --experimental-test-coverage test/**/*.test.mjs", + "uvwasi-meeting": "create-meeting uvwasi", + "uvwasi-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs uvwasi", + "tsc-meeting": "create-meeting tsc", + "tsc-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs tsc", + "build-meeting": "create-meeting build", + "build-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs build", + "diag-meeting": "create-meeting diag", + "diag-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs diag", + "diag-deepdive-meeting": "create-meeting diag_deepdive", + "diag-deepdive-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs diag_deepdive", + "typescript-meeting": "create-meeting typescript", + "typescript-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs typescript", + "release-meeting": "create-meeting Release", + "release-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs Release", + "cross-project-council-meeting": "create-meeting cross_project_council", + "cross-project-council-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs cross_project_council", + "modules-meeting": "create-meeting modules", + "modules-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs modules", + "tooling-meeting": "create-meeting tooling", + "tooling-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs tooling", + "security-wg-meeting": "create-meeting security-wg", + "security-wg-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs security-wg", + "next-10-meeting": "create-meeting next-10", + "next-10-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs next-10", + "package-maintenance-meeting": "create-meeting package-maintenance", + "package-maintenance-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs package-maintenance", + "package-metadata-interop-meeting": "create-meeting package_metadata_interop", + "package-metadata-interop-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs package_metadata_interop", + "ecosystem-report-meeting": "create-meeting ecosystem_report", + "ecosystem-report-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs ecosystem_report", + "sustainability-collab-meeting": "create-meeting sustainability_collab", + "sustainability-collab-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs sustainability_collab", + "standards-meeting": "create-meeting standards", + "standards-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs standards", + "security-collab-meeting": "create-meeting security_collab", + "security-collab-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs security_collab", + "loaders-meeting": "create-meeting loaders", + "loaders-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs loaders", + "web": "create-meeting web", + "web:dev": "node --env-file=.env create-node-meeting-artifacts.mjs web", + "web-server-frameworks-meeting": "create-meeting web-server-frameworks", + "web-server-frameworks-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs web-server-frameworks" }, "author": "", "license": "MIT" diff --git a/src/config.mjs b/src/config.mjs new file mode 100644 index 0000000..38ad883 --- /dev/null +++ b/src/config.mjs @@ -0,0 +1,35 @@ +import { homedir } from 'node:os'; +import { join, dirname } from 'node:path'; + +const defaultMeetingsDirectory = join(homedir(), '.make-node-meeting'); + +/** + * Gets the application configuration from environment variables and arguments + * @returns {import('./types.d.ts').AppConfig} Application configuration object + */ +export const getConfig = () => ({ + // Meeting group from command line argument, defaults to 'tsc' + meetingGroup: process.argv[2], + + // GitHub personal access token from environment + githubToken: process.env.GITHUB_TOKEN, + + // Google authentication configuration - now uses API Keys for simplicity + google: { + // Google API Key for Calendar access (preferred method) + apiKey: process.env.GOOGLE_API_KEY, + }, + + // HackMD configuration for meeting notes + hackmd: { + // HackMD API token for authentication + apiToken: process.env.HACKMD_API_TOKEN, + }, + + // Directory paths for templates, output, and configuration + directories: { + config: process.env.MEETINGS_CONFIG_DIR || './', + output: process.env.MEETINGS_OUTPUT_DIR || defaultMeetingsDirectory, + templates: join(dirname(import.meta.dirname), 'templates'), + }, +}); diff --git a/src/constants.mjs b/src/constants.mjs new file mode 100644 index 0000000..897a892 --- /dev/null +++ b/src/constants.mjs @@ -0,0 +1,36 @@ +// Default configuration values +export const DEFAULT_CONFIG = { + // Default GitHub organization name + githubOrg: 'nodejs', + // Default Host of the Meeting + host: 'Node.js', +}; + +// Time constants for date calculations +export const TIME_CONSTANTS = { + // Week in milliseconds for calendar search + WEEK_IN_MS: 7 * 24 * 60 * 60 * 1000, +}; + +// Relevant Timezones for Date manipulation +export const RELEVANT_TIMEZONES = [ + { label: 'US / Pacific', tz: 'America/Los_Angeles' }, + { label: 'US / Mountain', tz: 'America/Denver' }, + { label: 'US / Central', tz: 'America/Chicago' }, + { label: 'US / Eastern', tz: 'America/New_York' }, + { label: 'EU / Western', tz: 'Europe/London' }, + { label: 'EU / Central', tz: 'Europe/Amsterdam' }, + { label: 'EU / Eastern', tz: 'Europe/Helsinki' }, + { label: 'Moscow', tz: 'Europe/Moscow' }, + { label: 'Chennai', tz: 'Asia/Kolkata' }, + { label: 'Hangzhou', tz: 'Asia/Shanghai' }, + { label: 'Tokyo', tz: 'Asia/Tokyo' }, + { label: 'Sydney', tz: 'Australia/Sydney' }, +]; + +// Creates the default permissions for our generated docs +export const HACKMD_DEFAULT_PERMISSIONS = { + readPermission: 'guest', + writePermission: 'signed_in', + commentPermission: 'signed_in_users', +}; diff --git a/src/github.mjs b/src/github.mjs new file mode 100644 index 0000000..ec828b9 --- /dev/null +++ b/src/github.mjs @@ -0,0 +1,80 @@ +import { Octokit } from '@octokit/rest'; + +import { DEFAULT_CONFIG } from './constants.mjs'; + +/** + * Creates a GitHub API client + * @param {import('./types.d.ts').AppConfig} config - Application configuration + * @returns {import('@octokit/rest').Octokit} Configured GitHub API client + */ +export const createGitHubClient = ({ githubToken: auth }) => + new Octokit({ auth }); + +/** + * Creates GitHub issue with meeting information and Google Doc link + * @param {import('@octokit/rest').Octokit} githubClient - Authenticated GitHub API client + * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration object + * @param {string} title - Issue title + * @param {string} content - Issue content + * @returns {Promise} Created issue data + */ +export const createGitHubIssue = async ( + { rest }, + { properties }, + title, + content +) => { + const githubOrg = properties.USER ?? DEFAULT_CONFIG.githubOrg; + + const issueLabel = properties.ISSUE_LABEL + ? [properties.ISSUE_LABEL] + : undefined; + + // Create the GitHub issue with meeting information + const response = await rest.issues.create({ + owner: githubOrg, + repo: properties.REPO, + title, + body: content, + labels: issueLabel, + }); + + return response.data; +}; + +/** + * Sorts issues by repository + * @param {Array} issues The issues to sort + * @returns {Promise<{ [key: string]: Array }>} Sorted issues + */ +export const sortIssuesByRepo = issues => + issues.reduce((obj, issue) => { + (obj[issue.repository_url.split('/').slice(-2).join('/')] ||= []).push( + issue + ); + return obj; + }, {}); + +/** + * Fetches GitHub issues from all repositories in an organization with a specific label + * @param {import('@octokit/rest').Octokit} githubClient - Authenticated GitHub API client + * @param {import('./types.d.ts').AppConfig} config - Application configuration + * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration + * @returns {Promise<{ [key: string]: Array }>} Meeting agenda + */ +export const getAgendaIssues = async ( + githubClient, + { meetingGroup }, + { properties } +) => { + const githubOrg = properties.USER ?? DEFAULT_CONFIG.githubOrg; + const agendaTag = properties.AGENDA_TAG ?? `${meetingGroup}-agenda`; + + // Get all issues/PRs in the organization + const issues = await githubClient.paginate('GET /search/issues', { + q: `is:open label:${agendaTag} org:${githubOrg}`, + advanced_search: true, + }); + + return sortIssuesByRepo(issues); +}; diff --git a/src/google.mjs b/src/google.mjs new file mode 100644 index 0000000..27b5400 --- /dev/null +++ b/src/google.mjs @@ -0,0 +1,58 @@ +import { calendar } from '@googleapis/calendar'; + +/** + * Creates an authenticated Google Calendar client using API Key + * @param {import('./types.d.ts').GoogleConfig} gConfig - Google configuration object + * @returns {CalendarClient} Authenticated Google Calendar client + */ +export const createCalendarClient = ({ apiKey: auth }) => + calendar({ version: 'v3', auth }); + +/** + * Finds the next meeting event in Google Calendar for the current week + * @param {import('@googleapis/calendar').calendar_v3.Calendar} calendarClient - Google Calendar client + * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration object + * @returns {Promise} Calendar event object + */ +export const findNextMeetingEvent = async (calendarClient, meetingConfig) => { + const now = new Date(); + + // Calculate the start of the current week (Saturday 00:00:00 UTC) + // This handles the scenario where we want a full week from Saturday to Friday + const daysSinceStartOfWeek = (now.getUTCDay() + 1) % 7; // Saturday = 0, Sunday = 1, ..., Friday = 6 + const weekStart = new Date(now); + + weekStart.setUTCDate(now.getUTCDate() - daysSinceStartOfWeek); + weekStart.setUTCHours(0, 0, 0, 0); + + // Calculate the end of the week (Friday 23:59:59 UTC) + const weekEnd = new Date(weekStart); + + weekEnd.setUTCDate(weekStart.getUTCDate() + 6); + weekEnd.setUTCHours(23, 59, 59, 999); + + // Search for events in the specified calendar using the filter text + const response = await calendarClient.events.list({ + calendarId: meetingConfig.properties.CALENDAR_ID?.replace(/"/g, ''), + timeMin: weekStart.toISOString(), + timeMax: weekEnd.toISOString(), + singleEvents: true, + // Replace spaces with dots for Google Calendar search compatibility + q: meetingConfig.properties.CALENDAR_FILTER?.replace(/"/g, '').replace( + / /g, + '.' + ), + }); + + // Ensure we found at least one event + if (!response.data.items || response.data.items.length === 0) { + throw new Error( + `No meeting found for ${meetingConfig?.properties?.GROUP_NAME || 'this group'} ` + + `in the current week (${weekStart.toISOString().split('T')[0]} to ${weekEnd.toISOString().split('T')[0]}). ` + + `This is expected for bi-weekly meetings or meetings that don't occur every week.` + ); + } + + // Return the first (next) event found + return response.data.items[0]; +}; diff --git a/src/hackmd.mjs b/src/hackmd.mjs new file mode 100644 index 0000000..96096cf --- /dev/null +++ b/src/hackmd.mjs @@ -0,0 +1,55 @@ +import HackMDAPI from '@hackmd/api'; + +import { HACKMD_DEFAULT_PERMISSIONS } from './constants.mjs'; + +/** + * Creates a HackMD API client + * @param {import('./types.d.ts').AppConfig} config - Application configuration + * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration + * @returns {HackMDClient} Configured HackMD API client + */ +export const createHackMDClient = ({ hackmd: { apiToken } }, meetingConfig) => { + // Use team-specific API endpoint if teamName is provided in meeting config + const teamName = meetingConfig.properties.HACKMD_TEAM_NAME; + + const baseURL = teamName + ? `https://api.hackmd.io/v1/teams/${teamName}` + : 'https://api.hackmd.io/v1'; + + return new HackMDAPI(apiToken, baseURL); +}; + +/** + * Creates a new meeting notes document in HackMD with appropriate tags + * @param {HackMDAPI} hackmdClient - HackMD API client + * @param {string} title - Document title + * @param {string} content - Document content in Markdown + * @returns {Promise} Created note data with ID and URLs + */ +export const createMeetingNotesDocument = (hackmdClient, title, content) => { + const noteOptions = { + title, + content, + parentFolderId: '', + ...HACKMD_DEFAULT_PERMISSIONS, + }; + + // apparently it can return either { note: {...} } or just {...} + return hackmdClient + .createNote(noteOptions) + .then(response => response?.note ?? response); +}; + +/** + * Updates an existing meeting notes document in HackMD with retry logic + * @param {HackMDClient} hackmdClient - HackMD API client + * @param {string} noteId - HackMD note ID + * @param {string} content - Updated document content in Markdown + * @returns {Promise} Updated note data + */ +export const updateMeetingNotesDocument = (hackmdClient, noteId, content) => { + // apparently it can return either { note: {...} } or just {...} + return hackmdClient + .updateNote(noteId, { content }) + .then(response => response?.note ?? response); +}; diff --git a/src/meeting.mjs b/src/meeting.mjs new file mode 100644 index 0000000..09bd62c --- /dev/null +++ b/src/meeting.mjs @@ -0,0 +1,179 @@ +import { readFile } from 'node:fs/promises'; +import { join } from 'node:path'; + +import { parse } from 'dotenv'; + +import { DEFAULT_CONFIG } from './constants.mjs'; +import * as dates from './utils/dates.mjs'; +import * as templates from './utils/templates.mjs'; +import * as urls from './utils/urls.mjs'; + +/** + * Reads and parses meeting configuration from template files + * @param {import('./types.d.ts').AppConfig} config - Application configuration + * @returns {Promise} Meeting configuration object + */ +export const readMeetingConfig = async config => { + // Read all template files asynchronously + const invited = await readFile( + join(config.directories.templates, `invited_${config.meetingGroup}`), + 'utf8' + ); + + const observers = await readFile( + join(config.directories.templates, `observers_${config.meetingGroup}`), + 'utf8' + ); + + const baseMeetingInfo = await readFile( + join(config.directories.templates, `meeting_base_${config.meetingGroup}`), + 'utf8' + ); + + return { + invited, + observers, + baseMeetingInfo, + properties: parse(baseMeetingInfo), + }; +}; + +/** + * Generates the meeting title based on the meeting configuration + * @param {import('./types.d.ts').AppConfig} config - Application configuration + * @param {import('./types.d.ts').MeetingConfig} meetingConfig + * @param {Date} meetingDate - Date of the meeting + * @returns {Promise} Generated meeting title + */ +export const generateMeetingTitle = (config, meetingConfig, meetingDate) => { + const props = meetingConfig.properties; + + const host = props.HOST ?? DEFAULT_CONFIG.host; + const groupName = props.GROUP_NAME ?? config.meetingGroup; + + const utcShort = meetingDate.toISOString().split('T')[0]; + + return `${host} ${groupName} Meeting ${utcShort}`; +}; + +/** + * Generates the meeting agenda from the list of agenda issues + * @param {Array<{ [key: string]: Array }>} agendaIssues - List of agenda issues + * @returns {Promise} Formatted meeting agenda + */ +export const generateMeetingAgenda = agendaIssues => { + // Format issues as markdown + let agendaMarkdown = ''; + + Object.entries(agendaIssues).forEach(([repoName, issues]) => { + if (issues.length > 0) { + agendaMarkdown += `### ${repoName}\n\n`; + + issues.forEach(issue => { + // Escape markdown characters in title + const cleanTitle = issue.title.replace(/([[\]])/g, '\\$1'); + + agendaMarkdown += `* ${cleanTitle} [#${issue.number}](${issue.html_url})\n`; + }); + + agendaMarkdown += '\n'; + } + }); + + return agendaMarkdown.trim(); +}; + +/** + * Generates meeting issue content directly (replaces make-node-meeting.sh) + * @param {import('./types.d.ts').AppConfig} config - Application configuration + * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration + * @param {string} meetingAgenda - Meeting agenda (optional) + * @param {string} minutesDocLink - Minutes document link (optional) + * @param {Date} meetingDate - Date of the meeting + */ +export const generateMeetingIssue = async ( + config, + meetingConfig, + meetingDate, + meetingAgenda, + minutesDocLink +) => { + const props = meetingConfig.properties; + + const joiningInstructions = props.JOINING_INSTRUCTIONS ?? ''; + const githubOrg = props.USER ?? DEFAULT_CONFIG.githubOrg; + + const groupName = props.GROUP_NAME ?? config.meetingGroup; + const agendaTag = props.AGENDA_TAG ?? `${config.meetingGroup}-agenda`; + + // Format the meeting date and timezones + const { utc, timezones } = dates.formatTimezones(meetingDate); + + // Generate timezone conversion links + const timeAndDateLink = urls.generateTimeAndDateLink(meetingDate, groupName); + const wolframLink = urls.generateWolframAlphaLink(meetingDate); + + // Generate timezone table + const timezoneTable = timezones + .map(({ label, time }) => `| ${label.padEnd(13)} | ${time} |`) + .join('\n'); + + // Read and process the meeting issue template + const templatePath = join(config.directories.templates, 'meeting_issue.md'); + + const template = await readFile(templatePath, 'utf8'); + + const templateVariables = { + UTC_TIME: utc, + TIMEZONE_TABLE: timezoneTable, + TIME_AND_DATE_LINK: timeAndDateLink, + WOLFRAM_ALPHA_LINK: wolframLink, + AGENDA_LABEL: agendaTag, + GITHUB_ORG: githubOrg, + AGENDA_CONTENT: meetingAgenda ?? '*No agenda items found.*', + INVITEES: meetingConfig.invited, + JOINING_INSTRUCTIONS: joiningInstructions, + MINUTES_DOC: minutesDocLink, + OBSERVERS: meetingConfig.observers ?? '', + }; + + return templates.parseVariables(template, templateVariables); +}; + +/** + * Creates meeting minutes document content by processing template + * @param {import('./types.d.ts').AppConfig} config - Application configuration + * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration + * @param {string} meetingTitle - Meeting title + * @param {string} meetingAgenda - Meeting agenda (optional) + * @param {string} minutesDocLink - Minutes document link (optional) + * @param {string} githubIssueLink - GitHub issue link (optional) + * @returns {Promise} Processed minutes document content + */ +export const generateMeetingMinutes = async ( + config, + meetingConfig, + meetingTitle, + meetingAgenda, + minutesDocLink, + githubIssueLink +) => { + // Read and process the meeting minutes template + const templatePath = join( + config.directories.templates, + `minutes_base_${config.meetingGroup}` + ); + + const template = await readFile(templatePath, 'utf8'); + + const templateVariables = { + TITLE: meetingTitle, + AGENDA_CONTENT: meetingAgenda, + INVITED: meetingConfig.invited, + OBSERVERS: meetingConfig.observers, + MINUTES_DOC: minutesDocLink, + GITHUB_ISSUE: githubIssueLink, + }; + + return templates.parseVariables(template, templateVariables); +}; diff --git a/src/types.d.ts b/src/types.d.ts new file mode 100644 index 0000000..e7a2d94 --- /dev/null +++ b/src/types.d.ts @@ -0,0 +1,79 @@ +/** + * Application configuration object + */ +export interface AppConfig { + /** Meeting group from command line argument */ + meetingGroup: string; + /** GitHub personal access token */ + githubToken: string; + /** Google API configuration (Calendar only) */ + google: GoogleConfig; + /** HackMD API configuration */ + hackmd: HackMDConfig; + /** Directory paths configuration */ + directories: DirectoryConfig; +} + +/** + * Google authentication configuration (Calendar only) + */ +export interface GoogleConfig { + /** Google API Key for Calendar access */ + apiKey?: string; +} + +/** + * HackMD API configuration + */ +export interface HackMDConfig { + /** HackMD API token */ + apiToken: string; +} + +/** + * Directory paths configuration + */ +export interface DirectoryConfig { + /** Templates directory path */ + templates: string; +} + +/** + * Meeting configuration object parsed from templates + */ +export interface MeetingConfig { + /** Invited attendees list */ + invited: string; + /** Observers list */ + observers: string; + /** Base meeting information */ + baseMeetingInfo: string; + /** Parsed meeting properties */ + properties: MeetingProperties; +} + +/** + * Meeting properties parsed from template file + */ +export interface MeetingProperties { + /** Calendar ID to search for events */ + CALENDAR_ID?: string; + /** Text filter for calendar events */ + CALENDAR_FILTER?: string; + /** GitHub repository owner/user */ + USER?: string; + /** GitHub repository name */ + REPO?: string; + /** Host organization name (e.g. "Node.js", "OpenJS Foundation") */ + HOST?: string; + /** Display name for the meeting group */ + GROUP_NAME?: string; + /** Meeting agenda tag for labeling issues */ + AGENDA_TAG?: string; + /** Optional GitHub issue label */ + ISSUE_LABEL?: string; + /** HackMD team name for creating documents */ + HACKMD_TEAM_NAME?: string; + /** Meeting joining instructions */ + JOINING_INSTRUCTIONS?: string; +} diff --git a/src/utils/dates.mjs b/src/utils/dates.mjs new file mode 100644 index 0000000..1ee01bb --- /dev/null +++ b/src/utils/dates.mjs @@ -0,0 +1,44 @@ +import { RELEVANT_TIMEZONES } from '../constants.mjs'; + +/** + * Generic datetime formatter that accepts DateTimeFormat options + * @param {Date} date - The date to format + * @param {Intl.DateTimeFormatOptions} options - DateTimeFormat options + * @returns {string} Formatted date/time string + */ +export const formatDateTime = (date, options = {}) => { + const formatter = new Intl.DateTimeFormat('en-US', options); + + return formatter.format(date); +}; + +/** + * Formats a date to different timezones for the meeting schedule + * @param {Date} meetingDate - The meeting date + * @returns {Object} Object with formatted times for different timezones + */ +export const formatTimezones = meetingDate => ({ + utc: formatDateTime(meetingDate, { + weekday: 'short', + day: '2-digit', + month: 'short', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + hour12: true, + timeZone: 'UTC', + }), + timezones: RELEVANT_TIMEZONES.map(({ label, tz }) => ({ + label, + time: formatDateTime(meetingDate, { + weekday: 'short', + day: '2-digit', + month: 'short', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + hour12: true, + timeZone: tz, + }), + })), +}); diff --git a/src/utils/templates.mjs b/src/utils/templates.mjs new file mode 100644 index 0000000..78a997e --- /dev/null +++ b/src/utils/templates.mjs @@ -0,0 +1,23 @@ +/** + * Process template with variables + * @param {string} template - The template content + * @param {Object} variables - Object with template variables + * @returns {string} Processed template + */ +export const parseVariables = (template, variables) => { + let processed = template; + + for (const [key, value] of Object.entries(variables)) { + const placeholder = `$${key}$`; + + processed = processed.replace( + new RegExp(placeholder.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), + value || '' + ); + } + + // Replace any remaining placeholders with empty strings + processed = processed.replace(/\$[A-Z_]+\$/g, ''); + + return processed; +}; diff --git a/src/utils/urls.mjs b/src/utils/urls.mjs new file mode 100644 index 0000000..0bbbd19 --- /dev/null +++ b/src/utils/urls.mjs @@ -0,0 +1,43 @@ +import { formatDateTime } from './dates.mjs'; + +/** + * Generates TimeAndDate.com world clock link + * @param {Date} meetingDate - The meeting date + * @param {string} groupName - The meeting group name + * @returns {string} TimeAndDate.com URL + */ +export const generateTimeAndDateLink = (meetingDate, groupName) => { + const encodedGroupName = encodeURIComponent(groupName); + + const utcShort = meetingDate.toISOString().split('T')[0]; + + const isoDateTime = meetingDate + .toISOString() + .replace(/[-:]/g, '') + .split('.')[0]; + + return `https://www.timeanddate.com/worldclock/fixedtime.html?msg=Node.js+Foundation+${encodedGroupName}+Meeting+${utcShort}&iso=${isoDateTime}`; +}; + +/** + * Generates WolframAlpha timezone conversion link + * @param {Date} meetingDate - The meeting date + * @returns {string} WolframAlpha URL + */ +export const generateWolframAlphaLink = meetingDate => { + const utcTime = formatDateTime(meetingDate, { + timeZone: 'UTC', + hour: '2-digit', + minute: '2-digit', + hour12: true, + }); + + const utcDate = formatDateTime(meetingDate, { + timeZone: 'UTC', + month: 'short', + day: 'numeric', + year: 'numeric', + }); + + return `https://www.wolframalpha.com/input/?i=${encodeURIComponent(utcTime)}+UTC%2C+${encodeURIComponent(utcDate)}+in+local+time`; +}; diff --git a/templates/invited_commcomm b/templates/invited_commcomm deleted file mode 100644 index f1ce46a..0000000 --- a/templates/invited_commcomm +++ /dev/null @@ -1 +0,0 @@ -* CommComm Members: @nodejs/community-committee diff --git a/templates/invited_ecosystem_report b/templates/invited_ecosystem_report new file mode 100644 index 0000000..7e5a86a --- /dev/null +++ b/templates/invited_ecosystem_report @@ -0,0 +1 @@ +* Ecosystem Report Collab Space Members: @openjs/ecosystem-report diff --git a/templates/invited_michael b/templates/invited_michael deleted file mode 100644 index 67032c4..0000000 --- a/templates/invited_michael +++ /dev/null @@ -1 +0,0 @@ -* @mhdawson diff --git a/templates/invited_nodejsafrica b/templates/invited_nodejsafrica deleted file mode 100644 index 865df56..0000000 --- a/templates/invited_nodejsafrica +++ /dev/null @@ -1,2 +0,0 @@ -* Node.js Africa Members: @nodejsafrica/members -* Node.js Africa Members: @nodejsafrica/leadership diff --git a/templates/invited_nodejsafrica_leadership b/templates/invited_nodejsafrica_leadership deleted file mode 100644 index 217e719..0000000 --- a/templates/invited_nodejsafrica_leadership +++ /dev/null @@ -1 +0,0 @@ -* Node.js Africa Members: @nodejsafrica/leadership diff --git a/templates/invited_nodejsafrica_members b/templates/invited_nodejsafrica_members deleted file mode 100644 index 2803112..0000000 --- a/templates/invited_nodejsafrica_members +++ /dev/null @@ -1 +0,0 @@ -* Node.js Africa Members: @nodejsafrica/members diff --git a/templates/invited_performance b/templates/invited_performance deleted file mode 100644 index 2573fda..0000000 --- a/templates/invited_performance +++ /dev/null @@ -1 +0,0 @@ -* Performance team: @nodejs/performance diff --git a/templates/invited_sustainability_collab b/templates/invited_sustainability_collab new file mode 100644 index 0000000..2a94766 --- /dev/null +++ b/templates/invited_sustainability_collab @@ -0,0 +1 @@ +* Ecosystem Report Collab Space Members: @openjs-foundation/sustainability-collaboration-space \ No newline at end of file diff --git a/templates/invited_tsc b/templates/invited_tsc index 0131065..27d09bf 100644 --- a/templates/invited_tsc +++ b/templates/invited_tsc @@ -1,17 +1,16 @@ * Antoine du Hamel @aduh95 (voting member) * Yagiz Nizipli @anonrig (voting member) -* Anatoli Papirovski @apapirovski (voting member) * Benjamin Gruenbaum @benjamingr (voting member) * Ruben Bridgewater @BridgeAR (voting member) -* Geoffrey Booth @GeoffreyBooth (voting member) * Gireesh Punathil @gireeshpunathil (voting member) * James Snell @jasnell (voting member) * Joyee Cheung @joyeecheung (voting member) * Chengzhong Wu @legendecas (voting member) +* Marco Ippolito @marco-ippolito (voting member) * Matteo Collina @mcollina (voting member) -* Michael Dawson @mhdawson (voting member) -* Moshe Atlow @MoLow (voting member) +* Filip Skokan @panva (voting member) * Rafael Gonzaga @RafaelGSS (voting member) +* Darshan Sen @RaisinTen (voting member) * Richard Lau @richardlau (voting member) * Robert Nagy @ronag (voting member) * Ruy Adorno @ruyadorno (voting member) @@ -21,8 +20,8 @@ * Beth Griggs @BethGriggs (regular member) * Ben Noordhuis @bnoordhuis (regular member) * Colin Ihrig @cjihrig (regular member) +* Geoffrey Booth @GeoffreyBooth (regular member) +* Moshe Atlow @MoLow (regular member) * Shelley Vohr @codebytere (regular member) -* Danielle Adams @danielleadams (regular member) -* Myles Borins @MylesBorins (regular member) * Rich Trott @Trott (regular member) -* Joe Sepi @sepi@joesepi.com (Guest - Node.js CPC rep) +* Joe Sepi @joesepi (Guest - Node.js CPC rep) diff --git a/templates/invited_typescript b/templates/invited_typescript new file mode 100644 index 0000000..fc6fbe7 --- /dev/null +++ b/templates/invited_typescript @@ -0,0 +1 @@ +* Typescript team: @nodejs/typescript diff --git a/templates/invited_web b/templates/invited_web new file mode 100644 index 0000000..aa32a46 --- /dev/null +++ b/templates/invited_web @@ -0,0 +1 @@ +* @nodejs/web \ No newline at end of file diff --git a/templates/meeting_base_Release b/templates/meeting_base_Release index b61af77..a528f10 100644 --- a/templates/meeting_base_Release +++ b/templates/meeting_base_Release @@ -1,8 +1,9 @@ CALENDAR_FILTER="Node.js Release Working Group Meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="Release" GROUP_NAME="Release WorkGroup" +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" Join URL: diff --git a/templates/meeting_base_benchmarking b/templates/meeting_base_benchmarking index 6f8fd7f..cd777eb 100644 --- a/templates/meeting_base_benchmarking +++ b/templates/meeting_base_benchmarking @@ -1,8 +1,9 @@ CALENDAR_FILTER="Benchmarking WG Meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="benchmarking" GROUP_NAME="Benchmarking WorkGroup" +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" * link for participants: diff --git a/templates/meeting_base_build b/templates/meeting_base_build index 2083bfc..02b2067 100644 --- a/templates/meeting_base_build +++ b/templates/meeting_base_build @@ -1,9 +1,10 @@ CALENDAR_FILTER="Build WG Meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="build" AGENDA_TAG=build-agenda GROUP_NAME="Build WorkGroup" +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" * link for participants: diff --git a/templates/meeting_base_commcomm b/templates/meeting_base_commcomm deleted file mode 100644 index f66436d..0000000 --- a/templates/meeting_base_commcomm +++ /dev/null @@ -1,22 +0,0 @@ -CALENDAR_FILTER="Node.js Community Committee" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" -USER="nodejs" -REPO="community-committee" -GROUP_NAME="Community Committee" -AGENDA_TAG=cc-agenda -JOINING_INSTRUCTIONS=" - -* Link for participants: -* For those who just want to watch: - ---- - -**Invitees** - -Please use the following emoji reactions in this post to indicate your -availability. - -* :+1: - Attending -* :-1: - Not attending -* :confused: - Not sure yet -" diff --git a/templates/meeting_base_cross_project_council b/templates/meeting_base_cross_project_council index 5c33909..cd5c3de 100644 --- a/templates/meeting_base_cross_project_council +++ b/templates/meeting_base_cross_project_council @@ -1,16 +1,16 @@ CALENDAR_FILTER="Cross Project Council Meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" CALENDAR_ID="linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com" USER="openjs-foundation" -GITHUB_ORG="openjs-foundation" HOST="OpenJS Foundation" REPO="cross-project-council" GROUP_NAME="Cross Project Council" AGENDA_TAG=cross-project-council-agenda ISSUE_LABEL=cpc-meeting +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" -link for participants: Please refer to the OpenJS public calendar for meeting link +link for participants: Please refer to for meeting link * For those who just want to watch: diff --git a/templates/meeting_base_diag b/templates/meeting_base_diag index 9566b2f..ca9025b 100644 --- a/templates/meeting_base_diag +++ b/templates/meeting_base_diag @@ -1,9 +1,10 @@ CALENDAR_FILTER="Diagnostics WG Meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="diagnostics" GROUP_NAME="Diagnostics WorkGroup" AGENDA_TAG=diag-agenda +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" * link for participants: diff --git a/templates/meeting_base_diag_deepdive b/templates/meeting_base_diag_deepdive index c82521e..b985171 100644 --- a/templates/meeting_base_diag_deepdive +++ b/templates/meeting_base_diag_deepdive @@ -1,9 +1,10 @@ CALENDAR_FILTER="Diagnostics Deep Dive Meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="diagnostics" GROUP_NAME="Diagnostics Deep Dive" AGENDA_TAG=diag-deepdive-agenda +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" * link for participants: diff --git a/templates/meeting_base_ecosystem_report b/templates/meeting_base_ecosystem_report new file mode 100644 index 0000000..1125ebf --- /dev/null +++ b/templates/meeting_base_ecosystem_report @@ -0,0 +1,23 @@ +CALENDAR_FILTER="Ecosystem Report Collab Space" +CALENDAR_ID="linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com" +USER="openjs-foundation" +HOST="OpenJS Foundation" +REPO="ecosystem-report-collab-space" +GROUP_NAME="Ecosystem Report Collab Space" +AGENDA_TAG=ecosystem-report-agenda +HACKMD_TEAM_NAME="openjs-nodejs" +JOINING_INSTRUCTIONS=" + +link for participants: + +--- + +**Invitees** + +Please use the following emoji reactions in this post to indicate your +availability. + +* :+1: - Attending +* :-1: - Not attending +* :confused: - Not sure yet +" diff --git a/templates/meeting_base_loaders b/templates/meeting_base_loaders index f15518c..9ab34aa 100644 --- a/templates/meeting_base_loaders +++ b/templates/meeting_base_loaders @@ -1,8 +1,9 @@ CALENDAR_FILTER="Loaders Team Meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="loaders" GROUP_NAME="Loaders Team" +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS="* link for participants: * For those who just want to watch: " diff --git a/templates/meeting_base_michael b/templates/meeting_base_michael deleted file mode 100644 index c57939f..0000000 --- a/templates/meeting_base_michael +++ /dev/null @@ -1,15 +0,0 @@ -CALENDAR_FILTER="Node.js TSC Meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" -USER="mhdawson" -REPO="temp-testing" -AGENDA_TAG=tsc-agenda -GROUP_NAME="Technical Steering Committee (TSC)" -JOINING_INSTRUCTIONS=" - -Uberconference; participants should have the link & numbers, contact me if you don't. - -## Public participation - -We stream our conference call straight to YouTube so anyone can listen to it live, it should start playing at **** when we turn it on. There's usually a short cat-herding time at the start of the meeting and then occasionally we have some quick private business to attend to before we can start recording & streaming. So be patient and it should show up. - -Many of us will be on IRC in #node-dev on Freenode if you'd like to interact, we have a Q/A session scheduled at the end of the meeting if you'd like us to discuss anything in particular. @nodejs/collaborators in particular if there's anything you need from the TSC that's not worth putting on as a separate agenda item, this is a good place for that" diff --git a/templates/meeting_base_modules b/templates/meeting_base_modules index 635f2f6..f77b4e4 100644 --- a/templates/meeting_base_modules +++ b/templates/meeting_base_modules @@ -1,8 +1,9 @@ CALENDAR_FILTER="Modules Team Meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="modules" GROUP_NAME="Modules Team" +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS="* link for participants: * For those who just want to watch: " diff --git a/templates/meeting_base_next-10 b/templates/meeting_base_next-10 index e2cb711..74dbc77 100644 --- a/templates/meeting_base_next-10 +++ b/templates/meeting_base_next-10 @@ -1,8 +1,9 @@ CALENDAR_FILTER="Node.js Next 10 years" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="next-10" GROUP_NAME="Next 10 Years team" +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" * link for participants: diff --git a/templates/meeting_base_nodejsafrica b/templates/meeting_base_nodejsafrica deleted file mode 100644 index b22bfdd..0000000 --- a/templates/meeting_base_nodejsafrica +++ /dev/null @@ -1,17 +0,0 @@ -CALENDAR_FILTER="Node.js Africa Membership/Leadership" -CALENDAR_ID="node.js.africa@gmail.com" -USER="nodejsafrica" -GITHUB_ORG="nodejsafrica" -HOST="Node.js Africa Team" -REPO="admin" -GROUP_NAME="leadership" -AGENDA_TAG=leadershp-agenda -JOINING_INSTRUCTIONS=" - -* Link for participants: -* We stream our conference call straight to YouTube so anyone can listen to it live. - It should start playing at when we turn it on. - There's usually a short cat-herding time at the start of the meeting and then occasionally we - have some quick private business to attend to before we can start recording & streaming. - So be patient and it should show up. -* For those who just want to watch: " diff --git a/templates/meeting_base_outreach b/templates/meeting_base_outreach index 826ca77..26204dc 100644 --- a/templates/meeting_base_outreach +++ b/templates/meeting_base_outreach @@ -1,9 +1,10 @@ CALENDAR_FILTER="Node.js Outreach Meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="outreach" GROUP_NAME="Outreach" AGENDA_TAG=outreach-agenda +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" * Link for participants: diff --git a/templates/meeting_base_package-maintenance b/templates/meeting_base_package-maintenance index aea49b8..de127ff 100644 --- a/templates/meeting_base_package-maintenance +++ b/templates/meeting_base_package-maintenance @@ -1,8 +1,9 @@ CALENDAR_FILTER="Package Maintenance" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="package-maintenance" GROUP_NAME="Package Maintenance Team" +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" * link for participants: diff --git a/templates/meeting_base_package_metadata_interop b/templates/meeting_base_package_metadata_interop index 12c13d3..5905b1a 100644 --- a/templates/meeting_base_package_metadata_interop +++ b/templates/meeting_base_package_metadata_interop @@ -1,11 +1,11 @@ CALENDAR_FILTER="Package Metadata Interoperability" CALENDAR_ID="linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com" USER="openjs-foundation" -GITHUB_ORG="openjs-foundation" HOST="OpenJS Foundation" REPO="package-metadata-interoperability-collab-space" GROUP_NAME="Package Metadata Interoperability Collab Space" AGENDA_TAG=package-metadata-agenda +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" link for participants: Zoom link: <> diff --git a/templates/meeting_base_performance b/templates/meeting_base_performance deleted file mode 100644 index e04fb2f..0000000 --- a/templates/meeting_base_performance +++ /dev/null @@ -1,9 +0,0 @@ -CALENDAR_FILTER="Node.js Performance Team Meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" -USER="nodejs" -REPO="performance" -GROUP_NAME="Performance Team" -JOINING_INSTRUCTIONS=" - -* link for participants: https://zoom.us/j/94710602723 -" diff --git a/templates/meeting_base_security-wg b/templates/meeting_base_security-wg index 8bd6274..c70cd8d 100644 --- a/templates/meeting_base_security-wg +++ b/templates/meeting_base_security-wg @@ -1,8 +1,9 @@ CALENDAR_FILTER="Security-WG meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="security-wg" GROUP_NAME="Security team" +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS="https://zoom.us/j/92309450775 * link for participants: <> diff --git a/templates/meeting_base_security_collab b/templates/meeting_base_security_collab index b6ea01f..e54b14d 100644 --- a/templates/meeting_base_security_collab +++ b/templates/meeting_base_security_collab @@ -1,11 +1,11 @@ CALENDAR_FILTER="Security Collab Space meeting" CALENDAR_ID="linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com" USER="openjs-foundation" -GITHUB_ORG="openjs-foundation" HOST="OpenJS Foundation" REPO="security-collab-space" GROUP_NAME="Security Collab Space" AGENDA_TAG=security-agenda +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" link for participants: Zoom link: diff --git a/templates/meeting_base_standards b/templates/meeting_base_standards index 05b157e..107dbdc 100644 --- a/templates/meeting_base_standards +++ b/templates/meeting_base_standards @@ -1,11 +1,11 @@ CALENDAR_FILTER="Standards Working Group Meeting" CALENDAR_ID="linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com" USER="openjs-foundation" -GITHUB_ORG="openjs-foundation" HOST="OpenJS Foundation" REPO="standards" GROUP_NAME="Standards Working Group" AGENDA_TAG=standards-agenda +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" link for participants: Zoom link: diff --git a/templates/meeting_base_sustainability_collab b/templates/meeting_base_sustainability_collab new file mode 100644 index 0000000..1c5767c --- /dev/null +++ b/templates/meeting_base_sustainability_collab @@ -0,0 +1,23 @@ +CALENDAR_FILTER="Sustainability Collaboration Space" +CALENDAR_ID="linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com" +USER="openjs-foundation" +HOST="OpenJS Foundation" +REPO="sustainability-collab-space" +GROUP_NAME="Sustainability Collaboration Space" +AGENDA_TAG=sustainability-agenda +HACKMD_TEAM_NAME="openjs-nodejs" +JOINING_INSTRUCTIONS=" + +link for participants: + +--- + +**Invitees** + +Please use the following emoji reactions in this post to indicate your +availability. + +* :+1: - Attending +* :-1: - Not attending +* :confused: - Not sure yet +" diff --git a/templates/meeting_base_tooling b/templates/meeting_base_tooling index 91f00f9..0028923 100644 --- a/templates/meeting_base_tooling +++ b/templates/meeting_base_tooling @@ -1,8 +1,9 @@ CALENDAR_FILTER="Node.js Tooling" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="tooling" GROUP_NAME="Tooling Group" +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" * link for participants: diff --git a/templates/meeting_base_tsc b/templates/meeting_base_tsc index d11535c..7b41a64 100644 --- a/templates/meeting_base_tsc +++ b/templates/meeting_base_tsc @@ -1,9 +1,10 @@ CALENDAR_FILTER="Node.js TSC Meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="TSC" HOST="Node.js" GROUP_NAME="Technical Steering Committee (TSC)" +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" Zoom link: diff --git a/templates/meeting_base_typescript b/templates/meeting_base_typescript new file mode 100644 index 0000000..b2919d6 --- /dev/null +++ b/templates/meeting_base_typescript @@ -0,0 +1,23 @@ +CALENDAR_FILTER="TypeScript team meeting" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" +USER="nodejs" +REPO="typescript" +GROUP_NAME="TypeScript team" +HACKMD_TEAM_NAME="openjs-nodejs" +JOINING_INSTRUCTIONS="https://zoom.us/j/95749675148 + +* link for participants: <> +* For those who just want to watch We stream our conference call straight to YouTube so anyone can listen to it live, it should start playing at when we turn it on. There's usually a short cat-herding time at the start of the meeting and then occasionally we have some quick private business to attend to before we can start recording & streaming. So be patient and it should show up. +* youtube admin page: + +--- + +**Invitees** + +Please use the following emoji reactions in this post to indicate your +availability. + +* :+1: - Attending +* :-1: - Not attending +* :confused: - Not sure yet +" diff --git a/templates/meeting_base_userfeedback b/templates/meeting_base_userfeedback index 8a19398..ee46718 100644 --- a/templates/meeting_base_userfeedback +++ b/templates/meeting_base_userfeedback @@ -1,9 +1,10 @@ CALENDAR_FILTER="Node.js User Feedback Meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="user-feedback" GROUP_NAME="User Feedback" AGENDA_TAG=user-feedback-agenda +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" * link for participants: diff --git a/templates/meeting_base_uvwasi b/templates/meeting_base_uvwasi index e9adaac..ec3880a 100644 --- a/templates/meeting_base_uvwasi +++ b/templates/meeting_base_uvwasi @@ -1,9 +1,10 @@ CALENDAR_FILTER="Node.js uvwasi team meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="uvwasi" GROUP_NAME="uvwasi team" AGENDA_TAG=uvwasi-agenda +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" * link for participants: diff --git a/templates/meeting_base_web b/templates/meeting_base_web new file mode 100644 index 0000000..fffa791 --- /dev/null +++ b/templates/meeting_base_web @@ -0,0 +1,22 @@ +CALENDAR_FILTER="Web Team Meeting" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" +USER="nodejs" +REPO="web-team" +AGENDA_TAG=web-agenda +GROUP_NAME="Web Team" +HACKMD_TEAM_NAME="openjs-nodejs" +JOINING_INSTRUCTIONS=" + +* Check LFX for your invite link. If you are having issues, reach out to @avivkeller. + +--- + +**Invitees** + +Please use the following emoji reactions in this post to indicate your +availability. + +* :+1: - Attending +* :-1: - Not attending +* :confused: - Not sure +" diff --git a/templates/meeting_base_web-server-frameworks b/templates/meeting_base_web-server-frameworks index 31323f4..d69a14a 100644 --- a/templates/meeting_base_web-server-frameworks +++ b/templates/meeting_base_web-server-frameworks @@ -1,8 +1,9 @@ CALENDAR_FILTER="Web Server Frameworks" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="web-server-frameworks" GROUP_NAME="Web Server Frameworks" +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" * link for participants: diff --git a/templates/meeting_issue.md b/templates/meeting_issue.md new file mode 100644 index 0000000..15716c1 --- /dev/null +++ b/templates/meeting_issue.md @@ -0,0 +1,38 @@ +## Time + +**UTC $UTC_TIME$**: + +| Timezone | Date/Time | +| -------- | --------- | +$TIMEZONE_TABLE$ + +Or in your local time: + +* $TIME_AND_DATE_LINK$ +* or $WOLFRAM_ALPHA_LINK$ + +## Links + +* Minutes: <$MINUTES_DOC$> + +## Agenda + +Extracted from **$AGENDA_LABEL$** labelled issues and pull requests from the **$GITHUB_ORG$ org** prior to the meeting. + +$AGENDA_CONTENT$ + +## Invited + +$INVITEES$ + +### Observers/Guests + +$OBSERVERS$ + +## Notes + +The agenda comes from issues labelled with `$AGENDA_LABEL$` across **all of the repositories in the $GITHUB_ORG$ org**. Please label any additional issues that should be on the agenda before the meeting starts. + +## Joining the meeting + +$JOINING_INSTRUCTIONS$ diff --git a/templates/minutes_base_Release b/templates/minutes_base_Release index dec4ff8..1b4cfaf 100644 --- a/templates/minutes_base_Release +++ b/templates/minutes_base_Release @@ -4,7 +4,7 @@ * **Recording**: * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -25,4 +25,4 @@ $AGENDA_CONTENT$ * **Node.js Project Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_benchmarking b/templates/minutes_base_benchmarking index 183f643..1dc1c92 100644 --- a/templates/minutes_base_benchmarking +++ b/templates/minutes_base_benchmarking @@ -4,7 +4,7 @@ * **Recording**: * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -25,4 +25,4 @@ $AGENDA_CONTENT$ * **Node.js Project Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_build b/templates/minutes_base_build index ab9d848..e0f6d88 100644 --- a/templates/minutes_base_build +++ b/templates/minutes_base_build @@ -4,7 +4,7 @@ * **Recording**: * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -25,4 +25,4 @@ $AGENDA_CONTENT$ * **Node.js Project Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_commcomm b/templates/minutes_base_commcomm deleted file mode 100644 index a41f360..0000000 --- a/templates/minutes_base_commcomm +++ /dev/null @@ -1,35 +0,0 @@ -# $TITLE$ - -## Links - -* **Recording**: -* **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ - -## Present - -$INVITED$ -$OBSERVERS$ -*Members and Observers: In order to facilitate attendance tracking, don't hesitate do add yourselves to the minutes doc* - -## Agenda - -### Announcements - -### Initiative updates - -### CPC and Board Meeting Updates - -### Issues and PRs - -*Extracted from **cc-agenda** labelled issues and pull requests from the **nodejs org** prior to the meeting. - -$AGENDA_CONTENT$ - -### Q&A, Other - -### Upcoming Meetings - -* **Node.js Project Calendar**: - -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. diff --git a/templates/minutes_base_cross_project_council b/templates/minutes_base_cross_project_council index 8d5baa2..5768faa 100644 --- a/templates/minutes_base_cross_project_council +++ b/templates/minutes_base_cross_project_council @@ -24,7 +24,7 @@ $OBSERVERS$ - https://github.com/openjs-foundation/cross-project-council/labels/waiting-on-staff-update - https://github.com/openjs-foundation/cross-project-council/labels/waiting-on-website-update -*Extracted from **cross-project-council-agenda** labeled issues and pull requests from the **openjs-foundation org** prior to the meeting. +_Extracted from **cross-project-council-agenda** labeled issues and pull requests from the **openjs-foundation org** prior to the meeting._ $AGENDA_CONTENT$ @@ -45,6 +45,6 @@ Please review regularly our list of dates and reminders, our quarterly review is ## Upcoming Meetings -* **Calendar**: +- **Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_diag b/templates/minutes_base_diag index 412894f..7a3f340 100644 --- a/templates/minutes_base_diag +++ b/templates/minutes_base_diag @@ -4,7 +4,7 @@ * **Recording**: * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -25,4 +25,4 @@ $AGENDA_CONTENT$ * **Node.js Project Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_diag_deepdive b/templates/minutes_base_diag_deepdive index 4af479e..34c294b 100644 --- a/templates/minutes_base_diag_deepdive +++ b/templates/minutes_base_diag_deepdive @@ -4,7 +4,7 @@ * **Recording**: * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -25,4 +25,4 @@ $AGENDA_CONTENT$ * **Node.js Project Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_ecosystem_report b/templates/minutes_base_ecosystem_report new file mode 100644 index 0000000..280bbc7 --- /dev/null +++ b/templates/minutes_base_ecosystem_report @@ -0,0 +1,24 @@ +## Links + +* **GitHub Issue**: $GITHUB_ISSUE$ +* **Minutes**: $MINUTES_DOC$ + +## Present + +$INVITED$ + +## Agenda + +## Announcements + +*Extracted from **** labelled issues and pull requests from the **Ecosystem report collab space** prior to the meeting. + +$AGENDA_CONTENT$ + +## Q&A, Other + +## Upcoming Meetings + +* **OpenJS Foundation Calendar**: https://calendar.google.com/calendar/u/0/embed?src=linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com + +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_michael b/templates/minutes_base_michael deleted file mode 100644 index 26e5e3b..0000000 --- a/templates/minutes_base_michael +++ /dev/null @@ -1,28 +0,0 @@ -# $TITLE$ - -## Links - -* **Recording**: -* **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ - -## Present - -$INVITED$ -$OBSERVERS$ - -## Agenda - -### Announcements - -*Extracted from **tsc-agenda** labeled issues and pull requests from the **nodejs org** prior to the meeting. - -$AGENDA_CONTENT$ - -## Q&A, Other - -## Upcoming Meetings - -* **Node.js Project Calendar**: - -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. diff --git a/templates/minutes_base_modules b/templates/minutes_base_modules index be34441..a44377c 100644 --- a/templates/minutes_base_modules +++ b/templates/minutes_base_modules @@ -4,7 +4,7 @@ * **Recording**: * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present diff --git a/templates/minutes_base_next-10 b/templates/minutes_base_next-10 index 517c262..2fbd845 100644 --- a/templates/minutes_base_next-10 +++ b/templates/minutes_base_next-10 @@ -24,4 +24,4 @@ $AGENDA_CONTENT$ * **Node.js Project Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_nodejsafrica b/templates/minutes_base_nodejsafrica deleted file mode 100644 index 6c8a337..0000000 --- a/templates/minutes_base_nodejsafrica +++ /dev/null @@ -1,28 +0,0 @@ -# $TITLE$ - -## Links - -* **Recording**: -* **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ - -## Present - -$INVITED$ -$OBSERVERS$ - -## Agenda - -### Announcements - -*Extracted from **leadership-agenda** labeled issues and pull requests from the **nodejs africa** prior to the meeting. - -$AGENDA_CONTENT$ - -## Q&A, Other - -## Upcoming Meetings - -* **Node.js Africa Calendar**: - -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. diff --git a/templates/minutes_base_outreach b/templates/minutes_base_outreach index 88a814e..02b3fd4 100644 --- a/templates/minutes_base_outreach +++ b/templates/minutes_base_outreach @@ -4,7 +4,7 @@ * **Recording**: * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -26,4 +26,4 @@ $AGENDA_CONTENT$ * **Node.js Project Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_package-maintenance b/templates/minutes_base_package-maintenance index da1ef6e..6711896 100644 --- a/templates/minutes_base_package-maintenance +++ b/templates/minutes_base_package-maintenance @@ -4,7 +4,7 @@ * **Recording**: * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -29,4 +29,4 @@ Ask participants about the state of [Project Board](https://github.com/nodejs/pa * **Node.js Project Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_package_metadata_interop b/templates/minutes_base_package_metadata_interop index 283c106..06796a9 100644 --- a/templates/minutes_base_package_metadata_interop +++ b/templates/minutes_base_package_metadata_interop @@ -2,7 +2,7 @@ * **Recording**: * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -23,4 +23,4 @@ $AGENDA_CONTENT$ * **Node.js Foundation Calendar**: https://nodejs.org/calendar -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_security-wg b/templates/minutes_base_security-wg index af4d621..3161b36 100644 --- a/templates/minutes_base_security-wg +++ b/templates/minutes_base_security-wg @@ -4,7 +4,7 @@ * **Recording**: * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -28,4 +28,4 @@ $AGENDA_CONTENT$ * **Node.js Project Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_security_collab b/templates/minutes_base_security_collab index cdc621f..9a17a99 100644 --- a/templates/minutes_base_security_collab +++ b/templates/minutes_base_security_collab @@ -24,4 +24,4 @@ $AGENDA_CONTENT$ * **Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_standards b/templates/minutes_base_standards index de18ddc..6ed2dae 100644 --- a/templates/minutes_base_standards +++ b/templates/minutes_base_standards @@ -24,4 +24,4 @@ $AGENDA_CONTENT$ * **Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_sustainability_collab b/templates/minutes_base_sustainability_collab new file mode 100644 index 0000000..26c9ac8 --- /dev/null +++ b/templates/minutes_base_sustainability_collab @@ -0,0 +1,24 @@ +## Links + +* **GitHub Issue**: $GITHUB_ISSUE$ +* **Minutes**: $MINUTES_DOC$ + +## Present + +$INVITED$ + +## Agenda + +## Announcements + +*Extracted from **** labelled issues and pull requests from the **Ecosystem report collab space** prior to the meeting. + +$AGENDA_CONTENT$ + +## Q&A, Other + +## Upcoming Meetings + +* **OpenJS Foundation Calendar**: https://calendar.google.com/calendar/u/0/embed?src=linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com + +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. \ No newline at end of file diff --git a/templates/minutes_base_tooling b/templates/minutes_base_tooling index 8ce6d92..bd1dbd9 100644 --- a/templates/minutes_base_tooling +++ b/templates/minutes_base_tooling @@ -4,7 +4,7 @@ * **Recording**: * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -25,4 +25,4 @@ $AGENDA_CONTENT$ * **Node.js Project Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_tsc b/templates/minutes_base_tsc index 6905ffa..faaeab0 100644 --- a/templates/minutes_base_tsc +++ b/templates/minutes_base_tsc @@ -4,7 +4,7 @@ * **Recording**: * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -15,6 +15,10 @@ $OBSERVERS$ ### Announcements +### Reminders + +* Remember to nominate people for the [contributor spotlight](https://github.com/nodejs/node/blob/main/doc/contributing/reconizing-contributors.md#bi-monthly-contributor-spotlight) + ### CPC and Board Meeting Updates *Extracted from **tsc-agenda** labeled issues and pull requests from the **nodejs org** prior to the meeting. @@ -27,4 +31,4 @@ $AGENDA_CONTENT$ * **Node.js Project Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_performance b/templates/minutes_base_typescript similarity index 54% rename from templates/minutes_base_performance rename to templates/minutes_base_typescript index aab88fc..f921f8e 100644 --- a/templates/minutes_base_performance +++ b/templates/minutes_base_typescript @@ -14,7 +14,7 @@ $OBSERVERS$ ## Announcements -*Extracted from **performance-agenda** labelled issues and pull requests from the **nodejs org** prior to the meeting. +* Extracted from **typescript-agenda** labelled issues and pull requests from the **nodejs org** prior to the meeting. $AGENDA_CONTENT$ @@ -22,6 +22,6 @@ $AGENDA_CONTENT$ ## Upcoming Meetings -* **Node.js Foundation Calendar**: https://nodejs.org/calendar +* **Node.js Project Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_userfeedback b/templates/minutes_base_userfeedback index a9cef3f..afc8f2a 100644 --- a/templates/minutes_base_userfeedback +++ b/templates/minutes_base_userfeedback @@ -2,7 +2,7 @@ * **Recording**: * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -23,4 +23,4 @@ $AGENDA_CONTENT$ * **Node.js Project Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_uvwasi b/templates/minutes_base_uvwasi index 7c414e8..f61e7de 100644 --- a/templates/minutes_base_uvwasi +++ b/templates/minutes_base_uvwasi @@ -24,4 +24,4 @@ $AGENDA_CONTENT$ * **Node.js Project Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_web b/templates/minutes_base_web new file mode 100644 index 0000000..00467bd --- /dev/null +++ b/templates/minutes_base_web @@ -0,0 +1,27 @@ +# $TITLE$ + +## Links + +* **Recording**: +* **GitHub Issue**: $GITHUB_ISSUE$ + +## Present + +$INVITED$ +$OBSERVERS$ + +## Agenda + +## Announcements + +* Extracted from **web-agenda** labelled issues and pull requests from the **nodejs org** prior to the meeting. + +$AGENDA_CONTENT$ + +## Q&A, Other + +## Upcoming Meetings + +* **Node.js Project Calendar**: + +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/minutes_base_web-server-frameworks b/templates/minutes_base_web-server-frameworks index 0535c4e..14fa35e 100644 --- a/templates/minutes_base_web-server-frameworks +++ b/templates/minutes_base_web-server-frameworks @@ -24,4 +24,4 @@ $AGENDA_CONTENT$ * **Node.js Project Calendar**: -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. +Click `Add to Google Calendar` at the bottom left to add to your own Google calendar. diff --git a/templates/observers_commcomm b/templates/observers_commcomm deleted file mode 100644 index 891f3ca..0000000 --- a/templates/observers_commcomm +++ /dev/null @@ -1,3 +0,0 @@ -Feel free to follow along on the YouTube live stream, or attend meeting as a guest -by calling in to Zoom, using the links below. If you will be attending as a guest, -please comment on this issue to let us know you'll be joining. diff --git a/templates/observers_michael b/templates/observers_ecosystem_report similarity index 100% rename from templates/observers_michael rename to templates/observers_ecosystem_report diff --git a/templates/observers_nodejsafrica b/templates/observers_sustainability_collab similarity index 100% rename from templates/observers_nodejsafrica rename to templates/observers_sustainability_collab diff --git a/templates/observers_performance b/templates/observers_typescript similarity index 100% rename from templates/observers_performance rename to templates/observers_typescript diff --git a/templates/observers_web b/templates/observers_web new file mode 100644 index 0000000..e69de29 diff --git a/test/config.test.mjs b/test/config.test.mjs new file mode 100644 index 0000000..6dc92a3 --- /dev/null +++ b/test/config.test.mjs @@ -0,0 +1,103 @@ +import assert from 'node:assert'; +import process from 'node:process'; +import { describe, it, beforeEach, afterEach } from 'node:test'; + +import { getConfig } from '../src/config.mjs'; + +describe('Config', () => { + let originalEnv; + let originalArgv; + + beforeEach(() => { + // Save original environment and argv + originalEnv = { ...process.env }; + originalArgv = [...process.argv]; + }); + + afterEach(() => { + // Restore original environment and argv + process.env = originalEnv; + process.argv = originalArgv; + }); + + describe('getConfig', () => { + it('should return empty meeting group when no argument provided', () => { + process.argv = ['node', 'script.mjs']; + + const config = getConfig(); + + assert.strictEqual(config.meetingGroup, undefined); + }); + + it('should use command line argument for meeting group', () => { + process.argv = ['node', 'script.mjs', 'build']; + + const config = getConfig(); + + assert.strictEqual(config.meetingGroup, 'build'); + }); + + it('should read GitHub token from environment', () => { + process.env.GITHUB_TOKEN = 'test_token'; + + const config = getConfig(); + + assert.strictEqual(config.githubToken, 'test_token'); + }); + + it('should read Google API Key config from environment', () => { + process.env.GOOGLE_API_KEY = 'test_google_api_key_123'; + + const config = getConfig(); + + assert.strictEqual(config.google.apiKey, 'test_google_api_key_123'); + }); + + it('should handle missing Google API Key gracefully', () => { + delete process.env.GOOGLE_API_KEY; + + const config = getConfig(); + + assert.strictEqual(config.google.apiKey, undefined); + }); + + it('should read HackMD config from environment', () => { + process.env.HACKMD_API_TOKEN = 'hackmd_token'; + + const config = getConfig(); + + assert.strictEqual(config.hackmd.apiToken, 'hackmd_token'); + }); + + it('should handle undefined environment variables gracefully', () => { + // Clear all relevant env vars + delete process.env.GITHUB_TOKEN; + delete process.env.HACKMD_API_TOKEN; + delete process.env.GOOGLE_CLIENT_ID; + + const config = getConfig(); + + assert.strictEqual(config.githubToken, undefined); + assert.strictEqual(config.hackmd.apiToken, undefined); + assert.strictEqual(config.google.clientId, undefined); + }); + + it('should contain all required config sections', () => { + const config = getConfig(); + + assert.ok('meetingGroup' in config); + assert.ok('githubToken' in config); + assert.ok('google' in config); + assert.ok('hackmd' in config); + assert.ok('directories' in config); + }); + + it('should contain all required directory paths', () => { + const config = getConfig(); + + assert.ok('config' in config.directories); + assert.ok('output' in config.directories); + assert.ok('templates' in config.directories); + }); + }); +}); diff --git a/test/meeting.test.mjs b/test/meeting.test.mjs new file mode 100644 index 0000000..9163cae --- /dev/null +++ b/test/meeting.test.mjs @@ -0,0 +1,164 @@ +import assert from 'node:assert'; +import { describe, it } from 'node:test'; + +import { generateMeetingTitle } from '../src/meeting.mjs'; + +describe('Meeting', () => { + describe('generateMeetingTitle', () => { + it('should generate meeting title with default values', () => { + const config = { meetingGroup: 'tsc' }; + const meetingConfig = { properties: {} }; + const meetingDate = new Date('2023-10-15T14:30:00Z'); + + const result = generateMeetingTitle(config, meetingConfig, meetingDate); + + assert.strictEqual(result, 'Node.js tsc Meeting 2023-10-15'); + }); + + it('should use HOST from properties', () => { + const config = { meetingGroup: 'tsc' }; + + const meetingConfig = { + properties: { + HOST: 'OpenJS Foundation', + }, + }; + + const meetingDate = new Date('2023-10-15T14:30:00Z'); + + const result = generateMeetingTitle(config, meetingConfig, meetingDate); + + assert.strictEqual(result, 'OpenJS Foundation tsc Meeting 2023-10-15'); + }); + + it('should use GROUP_NAME from properties', () => { + const config = { meetingGroup: 'tsc' }; + + const meetingConfig = { + properties: { + GROUP_NAME: 'Technical Steering Committee', + }, + }; + + const meetingDate = new Date('2023-10-15T14:30:00Z'); + + const result = generateMeetingTitle(config, meetingConfig, meetingDate); + + assert.strictEqual( + result, + 'Node.js Technical Steering Committee Meeting 2023-10-15' + ); + }); + + it('should use both custom HOST and GROUP_NAME', () => { + const config = { meetingGroup: 'tsc' }; + + const meetingConfig = { + properties: { + HOST: 'OpenJS Foundation', + GROUP_NAME: 'Technical Steering Committee', + }, + }; + + const meetingDate = new Date('2023-10-15T14:30:00Z'); + + const result = generateMeetingTitle(config, meetingConfig, meetingDate); + + assert.strictEqual( + result, + 'OpenJS Foundation Technical Steering Committee Meeting 2023-10-15' + ); + }); + + it('should handle different date formats correctly', () => { + const config = { meetingGroup: 'build' }; + const meetingConfig = { properties: {} }; + const meetingDate = new Date('2023-12-31T23:59:59Z'); + + const result = generateMeetingTitle(config, meetingConfig, meetingDate); + + assert.strictEqual(result, 'Node.js build Meeting 2023-12-31'); + }); + + it('should handle different meeting groups', () => { + const config = { meetingGroup: 'security-wg' }; + + const meetingConfig = { + properties: { + GROUP_NAME: 'Security Working Group', + }, + }; + + const meetingDate = new Date('2023-10-15T14:30:00Z'); + + const result = generateMeetingTitle(config, meetingConfig, meetingDate); + + assert.strictEqual( + result, + 'Node.js Security Working Group Meeting 2023-10-15' + ); + }); + + it('should handle edge case dates', () => { + const config = { meetingGroup: 'tsc' }; + const meetingConfig = { properties: {} }; + const meetingDate = new Date('2024-02-29T12:00:00Z'); // Leap year + + const result = generateMeetingTitle(config, meetingConfig, meetingDate); + + assert.strictEqual(result, 'Node.js tsc Meeting 2024-02-29'); + }); + + it('should handle null/undefined properties gracefully', () => { + const config = { meetingGroup: 'tsc' }; + + const meetingConfig = { + properties: { + HOST: null, + GROUP_NAME: undefined, + }, + }; + + const meetingDate = new Date('2023-10-15T14:30:00Z'); + + const result = generateMeetingTitle(config, meetingConfig, meetingDate); + + // Should fall back to defaults + assert.strictEqual(result, 'Node.js tsc Meeting 2023-10-15'); + }); + + it('should handle empty string properties', () => { + const config = { meetingGroup: 'tsc' }; + + const meetingConfig = { + properties: { + HOST: '', + GROUP_NAME: '', + }, + }; + + const meetingDate = new Date('2023-10-15T14:30:00Z'); + + const result = generateMeetingTitle(config, meetingConfig, meetingDate); + + // Empty strings are used as-is (nullish coalescing doesn't catch empty strings) + assert.strictEqual(result, ' Meeting 2023-10-15'); + }); + + it('should handle very long meeting group names', () => { + const config = { + meetingGroup: 'very-long-working-group-name-for-testing-purposes', + }; + + const meetingConfig = { properties: {} }; + const meetingDate = new Date('2023-10-15T14:30:00Z'); + + const result = generateMeetingTitle(config, meetingConfig, meetingDate); + + assert.strictEqual( + result, + 'Node.js very-long-working-group-name-for-testing-purposes Meeting 2023-10-15' + ); + }); + }); +}); diff --git a/test/snapshots/README.md b/test/snapshots/README.md new file mode 100644 index 0000000..e69de29 diff --git a/test/utils/dates.test.mjs b/test/utils/dates.test.mjs new file mode 100644 index 0000000..f504ed1 --- /dev/null +++ b/test/utils/dates.test.mjs @@ -0,0 +1,148 @@ +import assert from 'node:assert'; +import { describe, it } from 'node:test'; + +import { formatDateTime, formatTimezones } from '../../src/utils/dates.mjs'; + +describe('Utils - Dates', () => { + describe('formatDateTime', () => { + it('should format date with default options', () => { + const date = new Date('2023-10-15T14:30:00Z'); + const result = formatDateTime(date); + + assert.strictEqual(typeof result, 'string'); + assert.ok(result.length > 0); + }); + + it('should format date with custom options', () => { + const date = new Date('2023-10-15T14:30:00Z'); + const options = { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + }; + + const result = formatDateTime(date, options); + + assert.strictEqual(typeof result, 'string'); + assert.ok(result.includes('2023')); + assert.ok(result.includes('October')); + }); + + it('should format date with timezone option', () => { + const date = new Date('2023-10-15T14:30:00Z'); + const options = { + timeZone: 'America/New_York', + hour: '2-digit', + minute: '2-digit', + hour12: true, + }; + + const result = formatDateTime(date, options); + + assert.strictEqual(typeof result, 'string'); + assert.ok(result.includes('AM') || result.includes('PM')); + }); + + it('should handle edge cases with invalid dates gracefully', () => { + const invalidDate = new Date('invalid'); + + assert.throws(() => { + formatDateTime(invalidDate); + }); + }); + }); + + describe('formatTimezones', () => { + it('should return object with utc and timezones properties', () => { + const date = new Date('2023-10-15T14:30:00Z'); + const result = formatTimezones(date); + + assert.strictEqual(typeof result, 'object'); + assert.ok(Object.hasOwn(result, 'utc')); + assert.ok(Object.hasOwn(result, 'timezones')); + }); + + it('should format UTC time correctly', () => { + const date = new Date('2023-10-15T14:30:00Z'); + const result = formatTimezones(date); + + assert.strictEqual(typeof result.utc, 'string'); + assert.ok(result.utc.includes('2023')); + assert.ok(result.utc.includes('Oct')); + assert.ok(result.utc.includes('2:30')); + }); + + it('should return array of timezone objects', () => { + const date = new Date('2023-10-15T14:30:00Z'); + const result = formatTimezones(date); + + assert.ok(Array.isArray(result.timezones)); + assert.ok(result.timezones.length > 0); + + // Check first timezone object structure + const firstTz = result.timezones[0]; + + assert.ok(Object.hasOwn(firstTz, 'label')); + assert.ok(Object.hasOwn(firstTz, 'time')); + assert.strictEqual(typeof firstTz.label, 'string'); + assert.strictEqual(typeof firstTz.time, 'string'); + }); + + it('should include all expected timezones', () => { + const date = new Date('2023-10-15T14:30:00Z'); + const result = formatTimezones(date); + + const expectedLabels = [ + 'US / Pacific', + 'US / Mountain', + 'US / Central', + 'US / Eastern', + 'EU / Western', + 'EU / Central', + 'EU / Eastern', + 'Moscow', + 'Chennai', + 'Hangzhou', + 'Tokyo', + 'Sydney', + ]; + + const actualLabels = result.timezones.map(tz => tz.label); + + expectedLabels.forEach(label => { + assert.ok(actualLabels.includes(label), `Missing timezone: ${label}`); + }); + }); + + it('should format times for different timezones', () => { + const date = new Date('2023-10-15T14:30:00Z'); + const result = formatTimezones(date); + + // Check that different timezones have different times + const times = result.timezones.map(tz => tz.time); + const uniqueTimes = new Set(times); + + // Should have multiple unique times since timezones differ + assert.ok(uniqueTimes.size > 1); + }); + + it('should handle midnight edge case', () => { + const date = new Date('2023-10-15T00:00:00Z'); + const result = formatTimezones(date); + + assert.strictEqual(typeof result.utc, 'string'); + assert.ok(result.utc.includes('12:00')); + assert.ok(result.timezones.length > 0); + }); + + it('should handle noon edge case', () => { + const date = new Date('2023-10-15T12:00:00Z'); + const result = formatTimezones(date); + + assert.strictEqual(typeof result.utc, 'string'); + assert.ok(result.utc.includes('12:00')); + assert.ok(result.timezones.length > 0); + }); + }); +}); diff --git a/test/utils/templates.test.mjs b/test/utils/templates.test.mjs new file mode 100644 index 0000000..7e05127 --- /dev/null +++ b/test/utils/templates.test.mjs @@ -0,0 +1,118 @@ +import assert from 'node:assert'; +import { describe, it } from 'node:test'; + +import { parseVariables } from '../../src/utils/templates.mjs'; + +describe('Utils - Templates', () => { + describe('parseVariables', () => { + it('should replace single variable in template', () => { + const template = 'Hello $NAME$, welcome!'; + const variables = { NAME: 'John' }; + + const result = parseVariables(template, variables); + + assert.strictEqual(result, 'Hello John, welcome!'); + }); + + it('should replace multiple variables in template', () => { + const template = 'Meeting: $TITLE$ on $DATE$ at $TIME$'; + const variables = { + TITLE: 'TSC Meeting', + DATE: '2023-10-15', + TIME: '14:30', + }; + + const result = parseVariables(template, variables); + + assert.strictEqual(result, 'Meeting: TSC Meeting on 2023-10-15 at 14:30'); + }); + + it('should replace same variable multiple times', () => { + const template = "$NAME$ loves $NAME$'s work"; + const variables = { NAME: 'Alice' }; + + const result = parseVariables(template, variables); + + assert.strictEqual(result, "Alice loves Alice's work"); + }); + + it('should handle empty string values', () => { + const template = 'Value: $EMPTY$'; + const variables = { EMPTY: '' }; + + const result = parseVariables(template, variables); + + assert.strictEqual(result, 'Value: '); + }); + + it('should handle null/undefined values', () => { + const template = 'Value: $NULL$ and $UNDEFINED$'; + const variables = { NULL: null, UNDEFINED: undefined }; + + const result = parseVariables(template, variables); + + assert.strictEqual(result, 'Value: and '); + }); + + it('should remove unmatched placeholders', () => { + const template = 'Hello $NAME$, your $UNMATCHED$ is ready'; + const variables = { NAME: 'Bob' }; + + const result = parseVariables(template, variables); + + assert.strictEqual(result, 'Hello Bob, your is ready'); + }); + + it('should handle template with no placeholders', () => { + const template = 'No placeholders here'; + const variables = { NAME: 'Alice' }; + + const result = parseVariables(template, variables); + + assert.strictEqual(result, 'No placeholders here'); + }); + + it('should handle empty template', () => { + const template = ''; + const variables = { NAME: 'Alice' }; + + const result = parseVariables(template, variables); + + assert.strictEqual(result, ''); + }); + + it('should handle special characters in values', () => { + const template = 'Pattern: $PATTERN$'; + const variables = { PATTERN: '$.*+?^{}()|[]\\' }; + + const result = parseVariables(template, variables); + + assert.strictEqual(result, 'Pattern: $.*+?^{}()|[]\\'); + }); + + it('should handle variables with underscores and numbers', () => { + const template = '$VAR_1$ and $VAR_2_TEST$'; + const variables = { VAR_1: 'first', VAR_2_TEST: 'second' }; + + const result = parseVariables(template, variables); + + assert.strictEqual(result, 'first and second'); + }); + + it('should handle multiline templates', () => { + const template = `Line 1: $VAR1$ +Line 2: $VAR2$ +Line 3: $VAR1$ again`; + const variables = { VAR1: 'Hello', VAR2: 'World' }; + + const result = parseVariables(template, variables); + + assert.strictEqual( + result, + `Line 1: Hello +Line 2: World +Line 3: Hello again` + ); + }); + }); +}); diff --git a/test/utils/urls.test.mjs b/test/utils/urls.test.mjs new file mode 100644 index 0000000..2e09ee8 --- /dev/null +++ b/test/utils/urls.test.mjs @@ -0,0 +1,211 @@ +import assert from 'node:assert'; +import { describe, it } from 'node:test'; +import { URL } from 'node:url'; + +import { + generateTimeAndDateLink, + generateWolframAlphaLink, +} from '../../src/utils/urls.mjs'; + +describe('Utils - URLs', () => { + describe('generateTimeAndDateLink', () => { + it('should generate valid TimeAndDate.com link', () => { + const meetingDate = new Date('2023-10-15T14:30:00Z'); + const groupName = 'TSC'; + + const result = generateTimeAndDateLink(meetingDate, groupName); + + assert.strictEqual(typeof result, 'string'); + assert.ok( + result.startsWith( + 'https://www.timeanddate.com/worldclock/fixedtime.html' + ) + ); + }); + + it('should include encoded group name in URL', () => { + const meetingDate = new Date('2023-10-15T14:30:00Z'); + const groupName = 'Security Working Group'; + + const result = generateTimeAndDateLink(meetingDate, groupName); + + assert.ok(result.includes(encodeURIComponent('Security Working Group'))); + assert.ok(result.includes('Security%20Working%20Group')); + }); + + it('should include formatted date in URL', () => { + const meetingDate = new Date('2023-10-15T14:30:00Z'); + const groupName = 'TSC'; + + const result = generateTimeAndDateLink(meetingDate, groupName); + + assert.ok(result.includes('2023-10-15')); + }); + + it('should include ISO datetime in URL', () => { + const meetingDate = new Date('2023-10-15T14:30:00Z'); + const groupName = 'TSC'; + + const result = generateTimeAndDateLink(meetingDate, groupName); + + assert.ok(result.includes('iso=20231015T1430')); + }); + + it('should handle group names with special characters', () => { + const meetingDate = new Date('2023-10-15T14:30:00Z'); + const groupName = 'Build & Release'; + + const result = generateTimeAndDateLink(meetingDate, groupName); + + assert.ok(result.includes(encodeURIComponent('Build & Release'))); + assert.strictEqual(typeof result, 'string'); + }); + + it('should handle midnight meeting times', () => { + const meetingDate = new Date('2023-10-15T00:00:00Z'); + const groupName = 'TSC'; + + const result = generateTimeAndDateLink(meetingDate, groupName); + + assert.ok(result.includes('iso=20231015T0000')); + assert.ok(result.includes('2023-10-15')); + }); + + it('should handle end of year dates', () => { + const meetingDate = new Date('2023-12-31T23:59:00Z'); + const groupName = 'TSC'; + + const result = generateTimeAndDateLink(meetingDate, groupName); + + assert.ok(result.includes('2023-12-31')); + assert.ok(result.includes('iso=20231231T2359')); + }); + + it('should handle single character group names', () => { + const meetingDate = new Date('2023-10-15T14:30:00Z'); + const groupName = 'X'; + + const result = generateTimeAndDateLink(meetingDate, groupName); + + assert.ok(result.includes('X')); + assert.strictEqual(typeof result, 'string'); + }); + + it('should properly encode spaces and special characters', () => { + const meetingDate = new Date('2023-10-15T14:30:00Z'); + const groupName = 'Node.js Foundation Group'; + + const result = generateTimeAndDateLink(meetingDate, groupName); + + // Should not contain unencoded spaces + assert.ok(!result.includes('Node.js Foundation Group')); + // Should contain encoded version + assert.ok( + result.includes(encodeURIComponent('Node.js Foundation Group')) + ); + }); + }); + + describe('generateWolframAlphaLink', () => { + it('should generate valid WolframAlpha link', () => { + const meetingDate = new Date('2023-10-15T14:30:00Z'); + + const result = generateWolframAlphaLink(meetingDate); + + assert.strictEqual(typeof result, 'string'); + assert.ok(result.startsWith('https://www.wolframalpha.com/input/?i=')); + }); + + it('should include UTC time in URL', () => { + const meetingDate = new Date('2023-10-15T14:30:00Z'); + + const result = generateWolframAlphaLink(meetingDate); + + // Should include 2:30 PM format + assert.ok(result.includes('2%3A30') || result.includes('2:30')); + assert.ok(result.includes('PM')); + }); + + it('should include UTC date in URL', () => { + const meetingDate = new Date('2023-10-15T14:30:00Z'); + + const result = generateWolframAlphaLink(meetingDate); + + assert.ok(result.includes('Oct')); + assert.ok(result.includes('15')); + assert.ok(result.includes('2023')); + }); + + it('should include "local time" query in URL', () => { + const meetingDate = new Date('2023-10-15T14:30:00Z'); + + const result = generateWolframAlphaLink(meetingDate); + + assert.ok( + result.includes('local%20time') || result.includes('local+time') + ); + }); + + it('should handle midnight times', () => { + const meetingDate = new Date('2023-10-15T00:00:00Z'); + + const result = generateWolframAlphaLink(meetingDate); + + assert.ok(result.includes('12%3A00') || result.includes('12:00')); + assert.ok(result.includes('AM')); + }); + + it('should handle noon times', () => { + const meetingDate = new Date('2023-10-15T12:00:00Z'); + + const result = generateWolframAlphaLink(meetingDate); + + assert.ok(result.includes('12%3A00') || result.includes('12:00')); + assert.ok(result.includes('PM')); + }); + + it('should handle single digit minutes', () => { + const meetingDate = new Date('2023-10-15T14:05:00Z'); + + const result = generateWolframAlphaLink(meetingDate); + + assert.ok(result.includes('2%3A05') || result.includes('2:05')); + }); + + it('should handle end of year dates', () => { + const meetingDate = new Date('2023-12-31T23:59:00Z'); + + const result = generateWolframAlphaLink(meetingDate); + + assert.ok(result.includes('Dec')); + assert.ok(result.includes('31')); + assert.ok(result.includes('2023')); + assert.ok(result.includes('11%3A59') || result.includes('11:59')); + assert.ok(result.includes('PM')); + }); + + it('should properly encode the URL', () => { + const meetingDate = new Date('2023-10-15T14:30:00Z'); + + const result = generateWolframAlphaLink(meetingDate); + + // Should be a valid URL format + assert.doesNotThrow(() => new URL(result)); + + // Should contain proper encoding + assert.ok(result.includes('%2C') || result.includes(',')); + }); + + it('should handle beginning of year dates', () => { + const meetingDate = new Date('2023-01-01T00:00:00Z'); + + const result = generateWolframAlphaLink(meetingDate); + + assert.ok(result.includes('Jan')); + assert.ok(result.includes('1')); + assert.ok(result.includes('2023')); + assert.ok(result.includes('12%3A00') || result.includes('12:00')); + assert.ok(result.includes('AM')); + }); + }); +});