From 19f48380057d8f4d3c50293386df7c2830696f0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20Estrada?= Date: Thu, 25 Apr 2024 12:13:40 -0500 Subject: [PATCH 01/48] Adding the ecosystem report collab space (#157) --- templates/invited_ecosystem_report | 1 + templates/meeting_base_ecosystem_report | 23 +++++++++++++++++++++++ templates/minutes_base_ecosystem_report | 24 ++++++++++++++++++++++++ templates/observers_ecosystem_report | 0 4 files changed, 48 insertions(+) create mode 100644 templates/invited_ecosystem_report create mode 100644 templates/meeting_base_ecosystem_report create mode 100644 templates/minutes_base_ecosystem_report create mode 100644 templates/observers_ecosystem_report 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/meeting_base_ecosystem_report b/templates/meeting_base_ecosystem_report new file mode 100644 index 0000000..9f6075e --- /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" +GITHUB_ORG="openjs-foundation" +HOST="OpenJS Foundation" +REPO="ecosystem-report-collab-space" +GROUP_NAME="Ecosystem Report Collab Space" +AGENDA_TAG=ecosystem-report-agenda +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/minutes_base_ecosystem_report b/templates/minutes_base_ecosystem_report new file mode 100644 index 0000000..7682e1c --- /dev/null +++ b/templates/minutes_base_ecosystem_report @@ -0,0 +1,24 @@ +## Links + +* **GitHub Issue**: $GITHUB_ISSUE$ +* **Minutes Google Doc**: $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 `+GoogleCalendar` at the bottom right to add to your own Google calendar. diff --git a/templates/observers_ecosystem_report b/templates/observers_ecosystem_report new file mode 100644 index 0000000..e69de29 From 0f11f6f346f5b86d45769005b94778eaf3918455 Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Mon, 29 Apr 2024 12:15:38 -0400 Subject: [PATCH 02/48] fix: remove maxResults - maxResults seems to block generation in some cases. Not sure why there would be more results than expected but first one seems to be the right one so removing should be ok Signed-off-by: Michael Dawson --- create-node-meeting-artifacts.js | 1 - 1 file changed, 1 deletion(-) diff --git a/create-node-meeting-artifacts.js b/create-node-meeting-artifacts.js index 719a0e4..47ec647 100644 --- a/create-node-meeting-artifacts.js +++ b/create-node-meeting-artifacts.js @@ -64,7 +64,6 @@ ghauth(authOptions, (err, authData) => { 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; From d305d6364c2e7c4bf5106c009767686c969bd088 Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Fri, 10 May 2024 10:14:02 -0400 Subject: [PATCH 03/48] Add reminders section with spotlight as first entry (#158) --- templates/minutes_base_tsc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/templates/minutes_base_tsc b/templates/minutes_base_tsc index 6905ffa..8830613 100644 --- a/templates/minutes_base_tsc +++ b/templates/minutes_base_tsc @@ -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. From 6ba74138f93ffb49e17de1966f3b55e86deab83f Mon Sep 17 00:00:00 2001 From: Benjamin Gruenbaum Date: Sat, 11 May 2024 22:08:46 +0300 Subject: [PATCH 04/48] Change Yegiz from voting to regular TSC member --- templates/invited_tsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/invited_tsc b/templates/invited_tsc index 0131065..40c5df7 100644 --- a/templates/invited_tsc +++ b/templates/invited_tsc @@ -1,5 +1,4 @@ * 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) @@ -25,4 +24,5 @@ * Danielle Adams @danielleadams (regular member) * Myles Borins @MylesBorins (regular member) * Rich Trott @Trott (regular member) +* Yagiz Nizipli @anonrig (regular member) * Joe Sepi @sepi@joesepi.com (Guest - Node.js CPC rep) From a519f8befb2a03e8be986cd0ced155d8f8723879 Mon Sep 17 00:00:00 2001 From: RafaelGSS Date: Wed, 15 May 2024 12:21:55 -0300 Subject: [PATCH 05/48] Add Marco Ippolito to voting TSC --- templates/invited_tsc | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/invited_tsc b/templates/invited_tsc index 40c5df7..49eda2d 100644 --- a/templates/invited_tsc +++ b/templates/invited_tsc @@ -7,6 +7,7 @@ * 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) From d0cb043f80c03314d5072979612a96d9617e7651 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 28 Jun 2024 23:57:00 +0200 Subject: [PATCH 06/48] Remove Myles from invited_tsc (#162) --- templates/invited_tsc | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/invited_tsc b/templates/invited_tsc index 49eda2d..f370257 100644 --- a/templates/invited_tsc +++ b/templates/invited_tsc @@ -23,7 +23,6 @@ * Colin Ihrig @cjihrig (regular member) * Shelley Vohr @codebytere (regular member) * Danielle Adams @danielleadams (regular member) -* Myles Borins @MylesBorins (regular member) * Rich Trott @Trott (regular member) * Yagiz Nizipli @anonrig (regular member) * Joe Sepi @sepi@joesepi.com (Guest - Node.js CPC rep) From 94ba1a1515d0d5b5173821bc73943ee8235859fe Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Thu, 4 Jul 2024 22:29:15 +0200 Subject: [PATCH 07/48] Remove Danielle from invited_tsc (#163) --- templates/invited_tsc | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/invited_tsc b/templates/invited_tsc index f370257..f6f0e17 100644 --- a/templates/invited_tsc +++ b/templates/invited_tsc @@ -22,7 +22,6 @@ * Ben Noordhuis @bnoordhuis (regular member) * Colin Ihrig @cjihrig (regular member) * Shelley Vohr @codebytere (regular member) -* Danielle Adams @danielleadams (regular member) * Rich Trott @Trott (regular member) * Yagiz Nizipli @anonrig (regular member) * Joe Sepi @sepi@joesepi.com (Guest - Node.js CPC rep) From f27d58c944b128e09c5d9e18390c97654fb0a99a Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Thu, 18 Jul 2024 08:29:34 -0400 Subject: [PATCH 08/48] change invited_tsc to reflect membership change (#164) Refs: https://github.com/nodejs/node/pull/53888 --- templates/invited_tsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/invited_tsc b/templates/invited_tsc index f6f0e17..5234fc6 100644 --- a/templates/invited_tsc +++ b/templates/invited_tsc @@ -1,4 +1,5 @@ * 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) @@ -23,5 +24,4 @@ * Colin Ihrig @cjihrig (regular member) * Shelley Vohr @codebytere (regular member) * Rich Trott @Trott (regular member) -* Yagiz Nizipli @anonrig (regular member) * Joe Sepi @sepi@joesepi.com (Guest - Node.js CPC rep) From c018b3a5cd7c3c86169a6be4a781c06221312af5 Mon Sep 17 00:00:00 2001 From: Benjamin Gruenbaum Date: Fri, 26 Jul 2024 00:52:41 +0300 Subject: [PATCH 09/48] update meeting template --- templates/invited_tsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/invited_tsc b/templates/invited_tsc index 5234fc6..5b861c1 100644 --- a/templates/invited_tsc +++ b/templates/invited_tsc @@ -3,7 +3,6 @@ * 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) @@ -22,6 +21,7 @@ * Beth Griggs @BethGriggs (regular member) * Ben Noordhuis @bnoordhuis (regular member) * Colin Ihrig @cjihrig (regular member) +* Geoffrey Booth @GeoffreyBooth (regular member) * Shelley Vohr @codebytere (regular member) * Rich Trott @Trott (regular member) * Joe Sepi @sepi@joesepi.com (Guest - Node.js CPC rep) From 87b1d664557d7a41d1173b043c713749d29e6ca0 Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Tue, 30 Jul 2024 22:47:27 +0100 Subject: [PATCH 10/48] Move apapirovski to regular member (#165) --- templates/invited_tsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/invited_tsc b/templates/invited_tsc index 5b861c1..830bd74 100644 --- a/templates/invited_tsc +++ b/templates/invited_tsc @@ -1,6 +1,5 @@ * 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) * Gireesh Punathil @gireeshpunathil (voting member) @@ -18,6 +17,7 @@ * Paolo Insogna @ShogunPanda (voting member) * MichaΓ«l Zasso @targos (voting member) * Tobias Nießen @tniessen (voting member) +* Anatoli Papirovski @apapirovski (regular member) * Beth Griggs @BethGriggs (regular member) * Ben Noordhuis @bnoordhuis (regular member) * Colin Ihrig @cjihrig (regular member) From 65849004b714f91e492ebc8efbd7dd8de1f3a5a0 Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Wed, 31 Jul 2024 12:43:12 -0400 Subject: [PATCH 11/48] remove performance team meetings (#167) --- templates/invited_performance | 1 - templates/meeting_base_performance | 9 --------- templates/minutes_base_performance | 27 --------------------------- templates/observers_performance | 0 4 files changed, 37 deletions(-) delete mode 100644 templates/invited_performance delete mode 100644 templates/meeting_base_performance delete mode 100644 templates/minutes_base_performance delete mode 100644 templates/observers_performance 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/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/minutes_base_performance b/templates/minutes_base_performance deleted file mode 100644 index aab88fc..0000000 --- a/templates/minutes_base_performance +++ /dev/null @@ -1,27 +0,0 @@ -# $TITLE$ - -## Links - -* **Recording**: -* **GitHub Issue**: $GITHUB_ISSUE$ - -## Present - -$INVITED$ -$OBSERVERS$ - -## Agenda - -## Announcements - -*Extracted from **performance-agenda** labelled issues and pull requests from the **nodejs org** prior to the meeting. - -$AGENDA_CONTENT$ - -## Q&A, Other - -## Upcoming Meetings - -* **Node.js Foundation Calendar**: https://nodejs.org/calendar - -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. diff --git a/templates/observers_performance b/templates/observers_performance deleted file mode 100644 index e69de29..0000000 From 27cee3ab3a803084a09f154de5489664b9b0d96a Mon Sep 17 00:00:00 2001 From: Tobie Langel Date: Wed, 31 Jul 2024 18:43:36 +0200 Subject: [PATCH 12/48] Fix some minor issues in CPC template (#160) --- templates/minutes_base_cross_project_council | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/minutes_base_cross_project_council b/templates/minutes_base_cross_project_council index 8d5baa2..3791f64 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. From dcf5068dc85c0d0eade7c16a009149d41ddf3e12 Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Tue, 6 Aug 2024 15:57:58 +0200 Subject: [PATCH 13/48] chore: add typescript team to calendar (#168) --- templates/invited_typescript | 1 + templates/meeting_base_typescript | 22 ++++++++++++++++++++++ templates/minutes_base_typescript | 27 +++++++++++++++++++++++++++ templates/observers_typescript | 0 4 files changed, 50 insertions(+) create mode 100644 templates/invited_typescript create mode 100644 templates/meeting_base_typescript create mode 100644 templates/minutes_base_typescript create mode 100644 templates/observers_typescript 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/meeting_base_typescript b/templates/meeting_base_typescript new file mode 100644 index 0000000..431bb94 --- /dev/null +++ b/templates/meeting_base_typescript @@ -0,0 +1,22 @@ +CALENDAR_FILTER="TypeScript team meeting" +CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +USER="nodejs" +REPO="typescript" +GROUP_NAME="TypeScript team" +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/minutes_base_typescript b/templates/minutes_base_typescript new file mode 100644 index 0000000..93b305a --- /dev/null +++ b/templates/minutes_base_typescript @@ -0,0 +1,27 @@ +# $TITLE$ + +## Links + +* **Recording**: +* **GitHub Issue**: $GITHUB_ISSUE$ + +## Present + +$INVITED$ +$OBSERVERS$ + +## Agenda + +## Announcements + +* Extracted from **typescript-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/observers_typescript b/templates/observers_typescript new file mode 100644 index 0000000..e69de29 From 0588cd46d6a956df04bed89a6d8fa08480fda0d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulises=20Gasc=C3=B3n?= Date: Tue, 17 Sep 2024 17:46:18 +0200 Subject: [PATCH 14/48] feat: add Openjs sustainability collab space (#169) --- templates/invited_sustainability_collab | 1 + templates/meeting_base_sustainability_collab | 23 +++++++++++++++++++ templates/minutes_base_sustainability_collab | 24 ++++++++++++++++++++ templates/observers_sustainability_collab | 0 4 files changed, 48 insertions(+) create mode 100644 templates/invited_sustainability_collab create mode 100644 templates/meeting_base_sustainability_collab create mode 100644 templates/minutes_base_sustainability_collab create mode 100644 templates/observers_sustainability_collab 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/meeting_base_sustainability_collab b/templates/meeting_base_sustainability_collab new file mode 100644 index 0000000..b28aee2 --- /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" +GITHUB_ORG="openjs-foundation" +HOST="OpenJS Foundation" +REPO="sustainability-collab-space" +GROUP_NAME="Sustainability Collaboration Space" +AGENDA_TAG=sustainability-agenda +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 +" \ No newline at end of file diff --git a/templates/minutes_base_sustainability_collab b/templates/minutes_base_sustainability_collab new file mode 100644 index 0000000..0c8ecca --- /dev/null +++ b/templates/minutes_base_sustainability_collab @@ -0,0 +1,24 @@ +## Links + +* **GitHub Issue**: $GITHUB_ISSUE$ +* **Minutes Google Doc**: $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 `+GoogleCalendar` at the bottom right to add to your own Google calendar. \ No newline at end of file diff --git a/templates/observers_sustainability_collab b/templates/observers_sustainability_collab new file mode 100644 index 0000000..e69de29 From e0310b830371a56549cffdb2e3ec04b3b5249e3f Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Tue, 17 Sep 2024 12:47:15 -0400 Subject: [PATCH 15/48] feat: fixup template for sustainability space Signed-off-by: Michael Dawson --- templates/meeting_base_sustainability_collab | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/meeting_base_sustainability_collab b/templates/meeting_base_sustainability_collab index b28aee2..9c05896 100644 --- a/templates/meeting_base_sustainability_collab +++ b/templates/meeting_base_sustainability_collab @@ -5,7 +5,7 @@ GITHUB_ORG="openjs-foundation" HOST="OpenJS Foundation" REPO="sustainability-collab-space" GROUP_NAME="Sustainability Collaboration Space" -AGENDA_TAG=sustainability-agenda +AGENDA_TAG=sustainability-agenda JOINING_INSTRUCTIONS=" link for participants: @@ -20,4 +20,4 @@ availability. * :+1: - Attending * :-1: - Not attending * :confused: - Not sure yet -" \ No newline at end of file +" From b948e9f017be449fa1e3699390044ed54a5f7a56 Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Mon, 7 Oct 2024 12:59:58 -0400 Subject: [PATCH 16/48] doc: update calendar link for TSC meeting (#170) Signed-off-by: Michael Dawson --- templates/meeting_base_tsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/meeting_base_tsc b/templates/meeting_base_tsc index d11535c..5cd6b31 100644 --- a/templates/meeting_base_tsc +++ b/templates/meeting_base_tsc @@ -1,5 +1,5 @@ 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" From 0a0bd97a829673c4fd1a5e64a5d0fe3c1bef2490 Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Mon, 7 Oct 2024 14:08:38 -0400 Subject: [PATCH 17/48] doc: update calendar link for all meetings (#171) Signed-off-by: Michael Dawson --- templates/meeting_base_Release | 2 +- templates/meeting_base_benchmarking | 2 +- templates/meeting_base_build | 2 +- templates/meeting_base_commcomm | 2 +- templates/meeting_base_cross_project_council | 2 +- templates/meeting_base_diag | 2 +- templates/meeting_base_diag_deepdive | 2 +- templates/meeting_base_loaders | 2 +- templates/meeting_base_michael | 2 +- templates/meeting_base_modules | 2 +- templates/meeting_base_next-10 | 2 +- templates/meeting_base_outreach | 2 +- templates/meeting_base_package-maintenance | 2 +- templates/meeting_base_security-wg | 2 +- templates/meeting_base_tooling | 2 +- templates/meeting_base_typescript | 2 +- templates/meeting_base_userfeedback | 2 +- templates/meeting_base_uvwasi | 2 +- templates/meeting_base_web-server-frameworks | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/templates/meeting_base_Release b/templates/meeting_base_Release index b61af77..740ac54 100644 --- a/templates/meeting_base_Release +++ b/templates/meeting_base_Release @@ -1,5 +1,5 @@ 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" diff --git a/templates/meeting_base_benchmarking b/templates/meeting_base_benchmarking index 6f8fd7f..1c5fe55 100644 --- a/templates/meeting_base_benchmarking +++ b/templates/meeting_base_benchmarking @@ -1,5 +1,5 @@ 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" diff --git a/templates/meeting_base_build b/templates/meeting_base_build index 2083bfc..eb0f62c 100644 --- a/templates/meeting_base_build +++ b/templates/meeting_base_build @@ -1,5 +1,5 @@ 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 diff --git a/templates/meeting_base_commcomm b/templates/meeting_base_commcomm index f66436d..876ccff 100644 --- a/templates/meeting_base_commcomm +++ b/templates/meeting_base_commcomm @@ -1,5 +1,5 @@ CALENDAR_FILTER="Node.js Community Committee" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="community-committee" GROUP_NAME="Community Committee" diff --git a/templates/meeting_base_cross_project_council b/templates/meeting_base_cross_project_council index 5c33909..87df473 100644 --- a/templates/meeting_base_cross_project_council +++ b/templates/meeting_base_cross_project_council @@ -1,5 +1,5 @@ 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" diff --git a/templates/meeting_base_diag b/templates/meeting_base_diag index 9566b2f..a1e1bc8 100644 --- a/templates/meeting_base_diag +++ b/templates/meeting_base_diag @@ -1,5 +1,5 @@ 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" diff --git a/templates/meeting_base_diag_deepdive b/templates/meeting_base_diag_deepdive index c82521e..ae790ee 100644 --- a/templates/meeting_base_diag_deepdive +++ b/templates/meeting_base_diag_deepdive @@ -1,5 +1,5 @@ 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" diff --git a/templates/meeting_base_loaders b/templates/meeting_base_loaders index f15518c..76479d5 100644 --- a/templates/meeting_base_loaders +++ b/templates/meeting_base_loaders @@ -1,5 +1,5 @@ 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" diff --git a/templates/meeting_base_michael b/templates/meeting_base_michael index c57939f..994e75e 100644 --- a/templates/meeting_base_michael +++ b/templates/meeting_base_michael @@ -1,5 +1,5 @@ CALENDAR_FILTER="Node.js TSC Meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="mhdawson" REPO="temp-testing" AGENDA_TAG=tsc-agenda diff --git a/templates/meeting_base_modules b/templates/meeting_base_modules index 635f2f6..f2b7ab4 100644 --- a/templates/meeting_base_modules +++ b/templates/meeting_base_modules @@ -1,5 +1,5 @@ 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" diff --git a/templates/meeting_base_next-10 b/templates/meeting_base_next-10 index e2cb711..532f544 100644 --- a/templates/meeting_base_next-10 +++ b/templates/meeting_base_next-10 @@ -1,5 +1,5 @@ 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" diff --git a/templates/meeting_base_outreach b/templates/meeting_base_outreach index 826ca77..9821eb5 100644 --- a/templates/meeting_base_outreach +++ b/templates/meeting_base_outreach @@ -1,5 +1,5 @@ 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" diff --git a/templates/meeting_base_package-maintenance b/templates/meeting_base_package-maintenance index aea49b8..9786fca 100644 --- a/templates/meeting_base_package-maintenance +++ b/templates/meeting_base_package-maintenance @@ -1,5 +1,5 @@ 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" diff --git a/templates/meeting_base_security-wg b/templates/meeting_base_security-wg index 8bd6274..5b5d7a6 100644 --- a/templates/meeting_base_security-wg +++ b/templates/meeting_base_security-wg @@ -1,5 +1,5 @@ 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" diff --git a/templates/meeting_base_tooling b/templates/meeting_base_tooling index 91f00f9..4f8b2a8 100644 --- a/templates/meeting_base_tooling +++ b/templates/meeting_base_tooling @@ -1,5 +1,5 @@ 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" diff --git a/templates/meeting_base_typescript b/templates/meeting_base_typescript index 431bb94..cde188b 100644 --- a/templates/meeting_base_typescript +++ b/templates/meeting_base_typescript @@ -1,5 +1,5 @@ CALENDAR_FILTER="TypeScript team meeting" -CALENDAR_ID="nodejs.org_nr77ama8p7d7f9ajrpnu506c98@group.calendar.google.com" +CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" REPO="typescript" GROUP_NAME="TypeScript team" diff --git a/templates/meeting_base_userfeedback b/templates/meeting_base_userfeedback index 8a19398..9ef0d0f 100644 --- a/templates/meeting_base_userfeedback +++ b/templates/meeting_base_userfeedback @@ -1,5 +1,5 @@ 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" diff --git a/templates/meeting_base_uvwasi b/templates/meeting_base_uvwasi index e9adaac..94cf690 100644 --- a/templates/meeting_base_uvwasi +++ b/templates/meeting_base_uvwasi @@ -1,5 +1,5 @@ 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" diff --git a/templates/meeting_base_web-server-frameworks b/templates/meeting_base_web-server-frameworks index 31323f4..6249570 100644 --- a/templates/meeting_base_web-server-frameworks +++ b/templates/meeting_base_web-server-frameworks @@ -1,5 +1,5 @@ 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" From cf464e2bc571b1c09df9111201f939ecdcf56fe4 Mon Sep 17 00:00:00 2001 From: Richard Lau Date: Thu, 31 Oct 2024 17:44:09 +0000 Subject: [PATCH 18/48] fix: correct Joe's @-mention in invited_tsc (#172) Refs: https://github.com/nodejs/TSC/issues/1643#issuecomment-2444870565 --- templates/invited_tsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/invited_tsc b/templates/invited_tsc index 830bd74..0218fe7 100644 --- a/templates/invited_tsc +++ b/templates/invited_tsc @@ -24,4 +24,4 @@ * Geoffrey Booth @GeoffreyBooth (regular member) * Shelley Vohr @codebytere (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) From 72302c1f448349e0cda650ab4c5a944b351d6c06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Zasso?= Date: Fri, 3 Jan 2025 22:06:53 +0100 Subject: [PATCH 19/48] move MoLow to regular member (#175) Refs: https://github.com/nodejs/TSC/issues/1668 --- templates/invited_tsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/invited_tsc b/templates/invited_tsc index 0218fe7..d9e4037 100644 --- a/templates/invited_tsc +++ b/templates/invited_tsc @@ -9,7 +9,6 @@ * Marco Ippolito @marco-ippolito (voting member) * Matteo Collina @mcollina (voting member) * Michael Dawson @mhdawson (voting member) -* Moshe Atlow @MoLow (voting member) * Rafael Gonzaga @RafaelGSS (voting member) * Richard Lau @richardlau (voting member) * Robert Nagy @ronag (voting member) @@ -22,6 +21,7 @@ * 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) * Rich Trott @Trott (regular member) * Joe Sepi @joesepi (Guest - Node.js CPC rep) From 905db485f9dd8114f748bd103e58efacab9ce34e Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Mon, 27 Jan 2025 23:11:12 +0100 Subject: [PATCH 20/48] remove apapirovski from `invited_tsc` (#176) Refs: https://github.com/nodejs/node/pull/56580 --- templates/invited_tsc | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/invited_tsc b/templates/invited_tsc index d9e4037..7e66157 100644 --- a/templates/invited_tsc +++ b/templates/invited_tsc @@ -16,7 +16,6 @@ * Paolo Insogna @ShogunPanda (voting member) * MichaΓ«l Zasso @targos (voting member) * Tobias Nießen @tniessen (voting member) -* Anatoli Papirovski @apapirovski (regular member) * Beth Griggs @BethGriggs (regular member) * Ben Noordhuis @bnoordhuis (regular member) * Colin Ihrig @cjihrig (regular member) From a8df67ff97b6cc0c248ca360ee1220bd1aa1e5cf Mon Sep 17 00:00:00 2001 From: Michael Dawson Date: Tue, 11 Mar 2025 15:13:17 -0400 Subject: [PATCH 21/48] Add Darshan (#177) --- templates/invited_tsc | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/invited_tsc b/templates/invited_tsc index 7e66157..63b8ff7 100644 --- a/templates/invited_tsc +++ b/templates/invited_tsc @@ -10,6 +10,7 @@ * Matteo Collina @mcollina (voting member) * Michael Dawson @mhdawson (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) From bb86d5d45da49520146a6999c7923dcd86606f6f Mon Sep 17 00:00:00 2001 From: Rafael Gonzaga Date: Wed, 28 May 2025 16:46:53 -0300 Subject: [PATCH 22/48] update: add panva to voting members Refs: https://github.com/nodejs/TSC/issues/1740 --- templates/invited_tsc | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/invited_tsc b/templates/invited_tsc index 63b8ff7..920acec 100644 --- a/templates/invited_tsc +++ b/templates/invited_tsc @@ -9,6 +9,7 @@ * Marco Ippolito @marco-ippolito (voting member) * Matteo Collina @mcollina (voting member) * Michael Dawson @mhdawson (voting member) +* Filip Skokan @panva (voting member) * Rafael Gonzaga @RafaelGSS (voting member) * Darshan Sen @RaisinTen (voting member) * Richard Lau @richardlau (voting member) From bb436558ab2af25e9171db1a975f3c7a2f39d07e Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Fri, 4 Jul 2025 20:34:22 +0200 Subject: [PATCH 23/48] feat: updated, refactored and cleaned up --- .env.example | 16 ++ .../workflows/create-meeting-artifacts.yml | 70 +++++ .nvmrc | 1 + .prettierrc.json | 11 + CONTRIBUTING.md | 10 +- LICENSE.md | 8 +- README.md | 245 ++++++++++++++++-- TEMPLATES_DOCUMENTATION.md | 16 ++ bin/create-meeting | 45 +++- create-node-meeting-artifacts.js | 158 ----------- create-node-meeting-artifacts.mjs | 115 ++++++++ eslint.config.mjs | 67 +++++ global.d.ts | 22 ++ package.json | 78 +++++- src/config.mjs | 40 +++ src/constants.mjs | 52 ++++ src/github.mjs | 45 ++++ src/google.mjs | 133 ++++++++++ src/meeting.mjs | 138 ++++++++++ src/types.d.ts | 111 ++++++++ src/utils.mjs | 100 +++++++ 21 files changed, 1276 insertions(+), 205 deletions(-) create mode 100644 .env.example create mode 100644 .github/workflows/create-meeting-artifacts.yml create mode 100644 .nvmrc create mode 100644 .prettierrc.json delete mode 100644 create-node-meeting-artifacts.js create mode 100644 create-node-meeting-artifacts.mjs create mode 100644 eslint.config.mjs create mode 100644 global.d.ts create mode 100644 src/config.mjs create mode 100644 src/constants.mjs create mode 100644 src/github.mjs create mode 100644 src/google.mjs create mode 100644 src/meeting.mjs create mode 100644 src/types.d.ts create mode 100644 src/utils.mjs diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..a49eade --- /dev/null +++ b/.env.example @@ -0,0 +1,16 @@ +# GitHub Authentication +GITHUB_TOKEN=your_github_personal_access_token_here + +# Google APIs Authentication +# You can get these from the Google Cloud Console +GOOGLE_CLIENT_ID=your_google_client_id_here +GOOGLE_CLIENT_SECRET=your_google_client_secret_here +GOOGLE_REDIRECT_URI=http://localhost:3000/oauth2callback + +# Optional: Google Service Account (for GitHub Actions) +# GOOGLE_SERVICE_ACCOUNT_EMAIL=your_service_account_email_here +# GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY=your_service_account_private_key_here + +# Optional: Directory paths (defaults to current directory and home) +# MEETINGS_CONFIG_DIR=./ +# MEETINGS_OUTPUT_DIR=~/.make-node-meeting/ diff --git a/.github/workflows/create-meeting-artifacts.yml b/.github/workflows/create-meeting-artifacts.yml new file mode 100644 index 0000000..430f8b3 --- /dev/null +++ b/.github/workflows/create-meeting-artifacts.yml @@ -0,0 +1,70 @@ +name: Create Meeting Artifacts + +on: + # Run every Monday at 10 AM UTC (adjust as needed) + schedule: + - cron: '0 10 * * 1' + + # Allow manual triggering + workflow_dispatch: + inputs: + meeting_group: + description: 'Meeting group to create artifacts for' + required: true + type: choice + options: + - uvwasi + - tsc + - build + - diag + - diag_deepdive + - typescript + - Release + - cross_project_council + - modules + - tooling + - security-wg + - next-10 + - package-maintenance + - package_metadata_interop + - ecosystem_report + - sustainability_collab + - standards + - security_collab + - loaders + - web-server-frameworks + +jobs: + create-artifacts: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Create meeting artifacts + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GOOGLE_SERVICE_ACCOUNT_EMAIL: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_EMAIL }} + GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY }} + run: node create-node-meeting-artifacts.mjs ${{ github.event.inputs.meeting_group }} + + - name: Upload artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: meeting-artifacts-${{ github.event.inputs.meeting_group || 'tsc' }} + path: | + ~/.make-node-meeting/ + minutes_temp.txt + retention-days: 7 + if-no-files-found: ignore diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 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/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..d3bf184 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,240 @@ -# 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 Google Docs 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 and Drive APIs enabled +- OAuth credentials or Service Account credentials -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 +### Google Authentication +#### Option 1: OAuth (for local development) +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 and Google Drive API +4. Create OAuth 2.0 credentials (Desktop Application) +5. Add credentials to `.env` file + +#### Option 2: Service Account (for GitHub Actions) + +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 and Google Drive API +4. Create a Service Account and generate JSON key file +5. Add credentials to environment variables + +## 🎯 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_ ``` -echo -n "Previous Meeting Minutes Google Docs URL: " -read prev_doc_url -echo -n "This Meeting Minutes Google Docs URL: " -read curr_doc_url + +See [TEMPLATES_DOCUMENTATION.md](./TEMPLATES_DOCUMENTATION.md) for detailed template examples and variable explanations. + +### 2. Update GitHub Actions Workflow + +Add your meeting group to `.github/workflows/create-meeting-artifacts.yml`: + +```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 +``` + +### 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" + } +} +``` + +**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. **Google Doc**: Meeting minutes document stored in the `/nodejs-meetings` folder +3. **Console Output**: Links to both the created issue and document + +## πŸ”§ Configuration + +### Environment Variables + +#### Required + +- `GITHUB_TOKEN`: GitHub Personal Access Token + +#### Google Authentication (choose one) + +**OAuth (Local Development):** + +- `GOOGLE_CLIENT_ID`: OAuth client ID +- `GOOGLE_CLIENT_SECRET`: OAuth client secret +- `GOOGLE_REDIRECT_URI`: OAuth redirect URI (default: `http://localhost:3000/oauth2callback`) + +**Service Account (GitHub Actions):** + +- `GOOGLE_SERVICE_ACCOUNT_EMAIL`: Service account email +- `GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY`: Service account private key (JSON format) + +#### Optional + +- `MEETINGS_CONFIG_DIR`: Configuration directory (default: `./`) +- `MEETINGS_OUTPUT_DIR`: Output directory (default: `~/.make-node-meeting/`) + +### 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" +JOINING_INSTRUCTIONS="Meeting join instructions" ``` diff --git a/TEMPLATES_DOCUMENTATION.md b/TEMPLATES_DOCUMENTATION.md index 1bda9e6..d65351c 100644 --- a/TEMPLATES_DOCUMENTATION.md +++ b/TEMPLATES_DOCUMENTATION.md @@ -12,23 +12,29 @@ There are only four variables in the documents that _need_ to be changed: - **``:** Name of an observer in a group's meetings. # 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 +49,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,9 +66,11 @@ 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 @@ -92,7 +102,9 @@ Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. ``` ## Minutes Base Example + **File:** `minutes_base_commcomm` + ``` @@ -125,8 +137,10 @@ Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. ``` # Observers + List meeting observers who will consistently attend meetings _as observers_. **File:** `observers_` + ``` * * @@ -135,7 +149,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 index 5d9c984..e2cea8b 100755 --- a/bin/create-meeting +++ b/bin/create-meeting @@ -1,14 +1,43 @@ #!/usr/bin/env node -'use strict'; -const path = require('path'); -const { spawn } = require('child_process'); +/** + * CLI wrapper for create-node-meeting-artifacts + * + * This script provides a convenient way to run the meeting artifacts creator + * with the specified group name. + * + * Usage: + * create-meeting + * create-meeting tsc + * create-meeting benchmarking + */ -let group = process.argv[2]; -if(group === undefined) { - console.error('please pass in group as a argument'); +import { spawn } from 'node:child_process'; + +const group = process.argv[2]; + +if (group === undefined) { + console.error('Please pass in group as an argument'); + console.error('Usage: create-meeting '); + console.error('Example: create-meeting tsc'); process.exit(1); } -group = group.toLowerCase() + '-meeting'; -spawn('npm', [ 'run', group ], { stdio: 'inherit' }); +const scriptName = group.toLowerCase() + '-meeting'; + +console.log(`Running meeting artifacts creation for group: ${group}`); + +// Spawn the npm script with proper stdio inheritance +const child = spawn('npm', ['run', scriptName], { + stdio: 'inherit', + shell: true, +}); + +child.on('close', code => { + process.exit(code); +}); + +child.on('error', error => { + console.error(`Error running script: ${error.message}`); + process.exit(1); +}); diff --git a/create-node-meeting-artifacts.js b/create-node-meeting-artifacts.js deleted file mode 100644 index 47ec647..0000000 --- a/create-node-meeting-artifacts.js +++ /dev/null @@ -1,158 +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, '.'), - }, (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..6a8b3b3 --- /dev/null +++ b/create-node-meeting-artifacts.mjs @@ -0,0 +1,115 @@ +#!/usr/bin/env node + +/** + * Node.js Meeting Artifacts Creator + * + * Creates GitHub issues and Google Docs for Node.js team meetings. + * Reads meeting configuration from templates, fetches calendar events, + * creates meeting minutes documents, and posts GitHub issues. + * + * Environment Variables Required: + * - GITHUB_TOKEN: Personal access token for GitHub API + * - GOOGLE_CLIENT_ID: Google OAuth client ID + * - GOOGLE_CLIENT_SECRET: Google OAuth client secret + * + * Optional Environment Variables: + * - GOOGLE_SERVICE_ACCOUNT_EMAIL: Service account email (for GitHub Actions) + * - GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY: Service account private key (for GitHub Actions) + * - GOOGLE_REDIRECT_URI: OAuth redirect URI (default: http://localhost:3000/oauth2callback) + * - MEETINGS_CONFIG_DIR: Directory containing meeting templates (default: ./) + * - MEETINGS_OUTPUT_DIR: Directory for meeting output files (default: ~/.make-node-meeting/) + * + * Usage: + * node create-node-meeting-artifacts.mjs [meetingGroup] + * npm run tsc-meeting + * npm run dev -- tsc + */ + +import { join, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { getConfig } from './src/config.mjs'; +import { createGitHubIssue } from './src/github.mjs'; +import { + createGoogleAuthClient, + createGoogleClients, + findNextMeetingEvent, + uploadMinutesDocument, +} from './src/google.mjs'; +import { + readMeetingConfig, + generateMeetingIssue, + createMinutesDocument, + createMeetingInfoString, +} from './src/meeting.mjs'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +// Step 1: Application configuration +const config = getConfig(); + +// Step 2: Initialize Google authentication and API clients +const googleAuth = createGoogleAuthClient(config.google); +const { calendarClient, driveClient } = await createGoogleClients(googleAuth); + +// Step 3: Read meeting configuration from templates +const meetingConfig = await readMeetingConfig( + config.meetingGroup, + config.directories.templates +); + +// Step 4: Find next meeting event in calendar +const event = await findNextMeetingEvent(calendarClient, meetingConfig); +const meetingTime = new Date(event.start.dateTime).toISOString(); + +// Step 5: Generate meeting information string for external tools +const meetingInfo = createMeetingInfoString(meetingConfig, meetingTime); + +// Step 6: Generate meeting issue content using make-node-meeting tool +const makeNodeMeetingPath = join( + __dirname, + 'node_modules/make-node-meeting/make-node-meeting.sh' +); + +const { title, content } = await generateMeetingIssue( + meetingConfig.meetingGroupForTag, + meetingInfo, + config.directories.output, + makeNodeMeetingPath +); + +// Step 7: Create minutes document content using node-meeting-agenda tool +const agendaToolPath = join( + __dirname, + 'node_modules/node-meeting-agenda/node-meeting-agenda.js' +); + +const minutesContent = await createMinutesDocument( + meetingConfig.meetingGroupForTag, + meetingConfig.githubOrg, + meetingConfig, + title, + config.directories.templates, + config.meetingGroup, + agendaToolPath +); + +// Step 8: Upload minutes document to Google Drive +const uploadResult = await uploadMinutesDocument( + driveClient, + title, + minutesContent +); + +// Step 9: Create GitHub issue with Google Doc link +const issue = await createGitHubIssue( + config.githubToken, + meetingConfig, + title, + content, + uploadResult.id +); + +// Output success information with links +console.log(`Created GitHub issue: ${issue.html_url}`); +console.log(`Doc: https://docs.google.com/document/d/${uploadResult.id}`); diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..2516438 --- /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..1816a1d --- /dev/null +++ b/global.d.ts @@ -0,0 +1,22 @@ +/** + * 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 { drive_v3 } from '@googleapis/drive'; +import type { RestEndpointMethodTypes } from '@octokit/rest'; +import type { GoogleAuth } from 'google-auth-library'; + +declare global { + // Google API type aliases + type CalendarEvent = calendar_v3.Schema$Event; + type CalendarClient = calendar_v3.Calendar; + type DriveFile = drive_v3.Schema$File; + type DriveClient = drive_v3.Drive; + type GoogleAuthClient = GoogleAuth; + + // GitHub API type aliases + type GitHubIssue = + RestEndpointMethodTypes['issues']['create']['response']['data']; +} diff --git a/package.json b/package.json index 4a54335..4f19708 100644 --- a/package.json +++ b/package.json @@ -2,28 +2,80 @@ "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" }, + "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", + "@googleapis/calendar": "^9.7.0", + "@googleapis/drive": "^8.12.0", + "@octokit/rest": "^21.0.2", + "dedent": "^1.5.3", + "google-auth-library": "^9.14.1", "make-node-meeting": "^1.0.11", "node-meeting-agenda": "^1.0.4", "properties-parser": "^0.3.1" }, - "devDependencies": {}, + "devDependencies": { + "@eslint/js": "^9.30.1", + "@types/properties-parser": "^0.3.3", + "eslint": "^9.19.0", + "eslint-plugin-import-x": "^4.16.1", + "eslint-plugin-jsdoc": "^51.3.3", + "globals": "^16.3.0", + "prettier": "^3.4.2" + }, "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", + "uvwasi-meeting": "node create-node-meeting-artifacts.mjs uvwasi", + "uvwasi-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs uvwasi", + "tsc-meeting": "node create-node-meeting-artifacts.mjs tsc", + "tsc-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs tsc", + "build-meeting": "node create-node-meeting-artifacts.mjs build", + "build-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs build", + "diag-meeting": "node create-node-meeting-artifacts.mjs diag", + "diag-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs diag", + "diag-deepdive-meeting": "node create-node-meeting-artifacts.mjs diag_deepdive", + "diag-deepdive-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs diag_deepdive", + "typescript-meeting": "node create-node-meeting-artifacts.mjs typescript", + "typescript-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs typescript", + "release-meeting": "node create-node-meeting-artifacts.mjs Release", + "release-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs Release", + "cross-project-council-meeting": "node create-node-meeting-artifacts.mjs cross_project_council", + "cross-project-council-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs cross_project_council", + "modules-meeting": "node create-node-meeting-artifacts.mjs modules", + "modules-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs modules", + "tooling-meeting": "node create-node-meeting-artifacts.mjs tooling", + "tooling-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs tooling", + "security-wg-meeting": "node create-node-meeting-artifacts.mjs security-wg", + "security-wg-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs security-wg", + "next-10-meeting": "node create-node-meeting-artifacts.mjs next-10", + "next-10-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs next-10", + "package-maintenance-meeting": "node create-node-meeting-artifacts.mjs package-maintenance", + "package-maintenance-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs package-maintenance", + "package-metadata-interop-meeting": "node create-node-meeting-artifacts.mjs package_metadata_interop", + "package-metadata-interop-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs package_metadata_interop", + "ecosystem-report-meeting": "node create-node-meeting-artifacts.mjs ecosystem_report", + "ecosystem-report-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs ecosystem_report", + "sustainability-collab-meeting": "node create-node-meeting-artifacts.mjs sustainability_collab", + "sustainability-collab-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs sustainability_collab", + "standards-meeting": "node create-node-meeting-artifacts.mjs standards", + "standards-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs standards", + "security-collab-meeting": "node create-node-meeting-artifacts.mjs security_collab", + "security-collab-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs security_collab", + "loaders-meeting": "node create-node-meeting-artifacts.mjs loaders", + "loaders-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs loaders", + "web-server-frameworks-meeting": "node create-node-meeting-artifacts.mjs 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..6a56d33 --- /dev/null +++ b/src/config.mjs @@ -0,0 +1,40 @@ +import { homedir } from 'node:os'; +import { join, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +import { DEFAULT_CONFIG } from './constants.mjs'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +/** + * 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] || 'tsc', + + // GitHub personal access token from environment + githubToken: process.env.GITHUB_TOKEN, + + // Google authentication configuration - supports both OAuth and Service Account + google: { + // OAuth credentials for local development + clientId: process.env.GOOGLE_CLIENT_ID, + clientSecret: process.env.GOOGLE_CLIENT_SECRET, + redirectUri: process.env.GOOGLE_REDIRECT_URI || DEFAULT_CONFIG.redirectUri, + + // Service Account credentials for GitHub Actions automation + serviceAccountEmail: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL, + serviceAccountPrivateKey: process.env.GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY, + }, + + // Directory paths for templates, output, and configuration + directories: { + config: process.env.MEETINGS_CONFIG_DIR || './', + output: + process.env.MEETINGS_OUTPUT_DIR || join(homedir(), '.make-node-meeting'), + templates: join(dirname(__dirname), 'templates'), + }, +}); diff --git a/src/constants.mjs b/src/constants.mjs new file mode 100644 index 0000000..f407658 --- /dev/null +++ b/src/constants.mjs @@ -0,0 +1,52 @@ +// Google API scopes required for calendar and drive access +export const GOOGLE_SCOPES = [ + // Read calendar events to find next meeting + 'https://www.googleapis.com/auth/calendar.readonly', + // Create Google Docs for minutes + 'https://www.googleapis.com/auth/drive.file', +]; + +// Default configuration values +export const DEFAULT_CONFIG = { + // Default OAuth redirect URI for local development + redirectUri: 'http://localhost:3000/oauth2callback', + // Default GitHub organization name + githubOrg: 'nodejs', + // Default GitHub issue assignee + assignee: 'mhdawson', +}; + +// Template variable placeholders used in meeting minutes +export const TEMPLATE_VARIABLES = { + // Placeholder for meeting title + TITLE: '$TITLE$', + // Placeholder for agenda content + AGENDA_CONTENT: '$AGENDA_CONTENT$', + // Placeholder for invited attendees list + INVITED: '$INVITED$', + // Placeholder for observers list + OBSERVERS: '$OBSERVERS$', +}; + +// Google Docs MIME type for creating documents +export const GOOGLE_DOC_MIME_TYPE = 'application/vnd.google-apps.document'; + +// Google Drive folder MIME type for creating/searching folders +export const GOOGLE_FOLDER_MIME_TYPE = 'application/vnd.google-apps.folder'; + +// Name of the Google Drive folder where meeting minutes are stored +export const NODEJS_MEETINGS_FOLDER_NAME = 'nodejs-meetings'; + +// Regular expressions for updating issue content +export const ISSUE_UPDATE_REGEXES = { + // Matches empty Google Doc placeholder to replace with actual link + EMPTY_GOOGLE_DOC: /\* \*\*Minutes Google Doc\*\*: <>/, + // Matches previous minutes placeholder line to remove + PREVIOUS_MINUTES: /\* _Previous Minutes Google Doc: <>_/, +}; + +// Time constants for date calculations +export const TIME_CONSTANTS = { + // Week in milliseconds for calendar search + WEEK_IN_MS: 7 * 24 * 60 * 60 * 1000, +}; diff --git a/src/github.mjs b/src/github.mjs new file mode 100644 index 0000000..c32a7e1 --- /dev/null +++ b/src/github.mjs @@ -0,0 +1,45 @@ +import { Octokit } from '@octokit/rest'; + +import { DEFAULT_CONFIG } from './constants.mjs'; +import { updateIssueContent } from './utils.mjs'; + +/** + * Creates GitHub issue with meeting information and Google Doc link + * @param {string} githubToken - GitHub personal access token + * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration object + * @param {string} title - Issue title + * @param {string} content - Issue content + * @param {string} documentId - Google Doc ID to link in issue + * @returns {Promise} Created issue data + */ +export const createGitHubIssue = async ( + githubToken, + meetingConfig, + title, + content, + documentId +) => { + // Initialize GitHub API client with authentication token + const octokit = new Octokit({ auth: githubToken }); + + // Update the issue content to include the Google Doc link and clean up placeholders + const updatedContent = updateIssueContent(content, documentId); + + // Extract issue label from config, removing quotes if present + const issueLabel = meetingConfig.properties.ISSUE_LABEL?.replace(/"/g, ''); + + // Create the GitHub issue with meeting information + const response = await octokit.rest.issues.create({ + // Repository information from meeting config + owner: meetingConfig.properties.USER?.replace(/"/g, ''), + repo: meetingConfig.properties.REPO?.replace(/"/g, ''), + title, + body: updatedContent, + // Default assignee for Node.js meetings + assignee: DEFAULT_CONFIG.assignee, + // Add label if specified in config + labels: issueLabel ? [issueLabel] : undefined, + }); + + return response.data; +}; diff --git a/src/google.mjs b/src/google.mjs new file mode 100644 index 0000000..7cd473e --- /dev/null +++ b/src/google.mjs @@ -0,0 +1,133 @@ +import { calendar } from '@googleapis/calendar'; +import { drive } from '@googleapis/drive'; +import { GoogleAuth } from 'google-auth-library'; + +import { + GOOGLE_SCOPES, + GOOGLE_DOC_MIME_TYPE, + GOOGLE_FOLDER_MIME_TYPE, + NODEJS_MEETINGS_FOLDER_NAME, + TIME_CONSTANTS, +} from './constants.mjs'; + +/** + * Creates a Google Auth client using either OAuth or service account credentials + * @param {import('./types.d.ts').GoogleConfig} gConfig - Google authentication configuration + * @returns {GoogleAuthClient} Configured Google Auth client + */ +export const createGoogleAuthClient = gConfig => { + // Use Service Account authentication for automated environments (GitHub Actions) + if (gConfig.serviceAccountEmail && gConfig.serviceAccountPrivateKey) { + return new GoogleAuth({ + credentials: { + client_email: gConfig.serviceAccountEmail, + // Replace escaped newlines with actual newlines in private key + private_key: gConfig.serviceAccountPrivateKey.replace(/\\n/g, '\n'), + }, + scopes: GOOGLE_SCOPES, + }); + } + + // Use OAuth authentication for local development + return new GoogleAuth({ + credentials: { + client_id: gConfig.clientId, + client_secret: gConfig.clientSecret, + redirect_uris: [gConfig.redirectUri], + }, + scopes: GOOGLE_SCOPES, + }); +}; + +/** + * Finds the next meeting event in Google Calendar within the next week + * @param {CalendarClient} 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(); + const nextWeek = new Date(now.getTime() + TIME_CONSTANTS.WEEK_IN_MS); + + // 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: now.toISOString(), + timeMax: nextWeek.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('Could not find calendar event for the next week'); + } + + // Return the first (next) event found + return response.data.items[0]; +}; + +/** + * Finds a folder by name in Google Drive + * @param {DriveClient} driveClient - Google Drive client + * @param {string} folderName - Name of the folder to find + * @returns {Promise} Folder ID + */ +export const findFolder = async (driveClient, folderName) => { + // Search for the folder (assuming it exists) + const searchResponse = await driveClient.files.list({ + q: `name='${folderName}' and mimeType='${GOOGLE_FOLDER_MIME_TYPE}' and trashed=false`, + fields: 'files(id, name)', + }); + + // Return the folder ID + if (searchResponse.data.files && searchResponse.data.files.length > 0) { + return searchResponse.data.files[0].id; + } + + throw new Error(`${folderName} folder not found in Google Drive`); +}; + +/** + * Uploads minutes document to Google Drive in the nodejs-meetings folder + * @param {DriveClient} driveClient - Google Drive client + * @param {string} title - Document title + * @param {string} content - Document content + * @returns {Promise} Upload result with document ID + */ +export const uploadMinutesDocument = async (driveClient, title, content) => { + // Find the nodejs-meetings folder + const folderId = await findFolder(driveClient, NODEJS_MEETINGS_FOLDER_NAME); + + // Create a new Google Doc with the meeting minutes content in the correct folder + const response = await driveClient.files.create({ + requestBody: { + name: title, + mimeType: GOOGLE_DOC_MIME_TYPE, + parents: [folderId], + }, + media: { mimeType: 'text/plain', body: content }, + }); + + return response.data; +}; + +/** + * Creates Google API clients from authenticated client + * @param {GoogleAuthClient} googleAuth - Authenticated Google Auth client + * @returns {Promise} Google API clients + */ +export const createGoogleClients = async googleAuth => { + // Get the authenticated client for API calls + const authClient = await googleAuth.getClient(); + + // Return configured Calendar and Drive API clients + return { + calendarClient: calendar({ version: 'v3', auth: authClient }), + driveClient: drive({ version: 'v3', auth: authClient }), + }; +}; diff --git a/src/meeting.mjs b/src/meeting.mjs new file mode 100644 index 0000000..261cccf --- /dev/null +++ b/src/meeting.mjs @@ -0,0 +1,138 @@ +import { readFile, writeFile } from 'node:fs/promises'; +import { join } from 'node:path'; + +import parser from 'properties-parser'; + +import { DEFAULT_CONFIG } from './constants.mjs'; +import { + executeCommand, + createMeetingInfo, + processMinutesTemplate, +} from './utils.mjs'; + +/** + * Reads and parses meeting configuration from template files + * @param {string} meetingGroup - The meeting group name + * @param {string} templatesDir - Directory containing template files + * @returns {Promise} Meeting configuration object + */ +export const readMeetingConfig = async (meetingGroup, templatesDir) => { + // Read all template files asynchronously + const invited = await readFile( + join(templatesDir, `invited_${meetingGroup}`), + 'utf8' + ); + + const observers = await readFile( + join(templatesDir, `observers_${meetingGroup}`), + 'utf8' + ); + + const baseMeetingInfo = await readFile( + join(templatesDir, `meeting_base_${meetingGroup}`), + 'utf8' + ); + + // Parse meeting properties from the base info + const meetingProperties = parser.parse(baseMeetingInfo); + + return { + invited, + observers, + baseMeetingInfo, + properties: meetingProperties, + meetingGroupForTag: meetingProperties.AGENDA_TAG + ? meetingProperties.AGENDA_TAG.replace('-agenda', '') + : meetingGroup, + githubOrg: meetingProperties.GITHUB_ORG + ? meetingProperties.GITHUB_ORG.replace(/"/g, '') + : DEFAULT_CONFIG.githubOrg, + }; +}; + +/** + * Generates meeting issue content using make-node-meeting tool + * @param {string} meetingGroupForTag - Meeting group tag for file naming + * @param {string} meetingInfo - Complete meeting information string + * @param {string} outputDir - Output directory for meeting files + * @param {string} toolPath - Path to make-node-meeting tool + * @returns {Promise} Object containing issue title and content + */ +export const generateMeetingIssue = async ( + meetingGroupForTag, + meetingInfo, + outputDir, + toolPath +) => { + // Write meeting info to file for make-node-meeting tool + const meetingConfigPath = join(outputDir, `${meetingGroupForTag}.sh`); + + await writeFile(meetingConfigPath, meetingInfo); + + // Generate issue content using external tool + const newIssue = await executeCommand('bash', [toolPath, meetingGroupForTag]); + + // Parse title and content from tool output + const issueLines = newIssue.split('\n'); + const title = issueLines[1]; + const content = issueLines.slice(4).join('\n'); + + return { title, content }; +}; + +/** + * Creates meeting minutes document content by processing template + * @param {string} meetingGroupForTag - Meeting group tag for agenda generation + * @param {string} githubOrg - GitHub organization name + * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration object + * @param {string} title - Meeting title + * @param {string} templatesDir - Templates directory path + * @param {string} meetingGroup - Original meeting group name + * @param {string} agendaToolPath - Path to node-meeting-agenda tool + * @returns {Promise} Processed minutes document content + */ +export const createMinutesDocument = async ( + meetingGroupForTag, + githubOrg, + meetingConfig, + title, + templatesDir, + meetingGroup, + agendaToolPath +) => { + // Get agenda information using external tool + const agendaInfo = await executeCommand('node', [ + agendaToolPath, + `${meetingGroupForTag}-agenda`, + githubOrg, + ]); + + // Read minutes template file asynchronously + const minutesDoc = await readFile( + join(templatesDir, `minutes_base_${meetingGroup}`), + 'utf8' + ); + + // Process template variables and return final document + return processMinutesTemplate( + minutesDoc, + title, + agendaInfo, + meetingConfig.invited, + meetingConfig.observers + ); +}; + +/** + * Creates complete meeting information string for make-node-meeting tool + * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration object + * @param {string} meetingTime - ISO string of meeting time + * @returns {string} Complete meeting information for tool consumption + */ +export const createMeetingInfoString = (meetingConfig, meetingTime) => + createMeetingInfo( + meetingConfig.baseMeetingInfo, + meetingTime, + meetingConfig.invited, + meetingConfig.observers + ); diff --git a/src/types.d.ts b/src/types.d.ts new file mode 100644 index 0000000..1204de7 --- /dev/null +++ b/src/types.d.ts @@ -0,0 +1,111 @@ +/** + * TypeScript definitions for the Node.js Meeting Artifacts Creator + */ + +/** + * Application configuration object + */ +export interface AppConfig { + /** Meeting group from command line argument */ + meetingGroup: string; + /** GitHub personal access token */ + githubToken: string; + /** Google API configuration */ + google: GoogleConfig; + /** Directory paths configuration */ + directories: DirectoryConfig; +} + +/** + * Google authentication configuration + */ +export interface GoogleConfig { + /** OAuth client ID */ + clientId?: string; + /** OAuth client secret */ + clientSecret?: string; + /** OAuth redirect URI */ + redirectUri?: string; + /** Service account email for automation */ + serviceAccountEmail?: string; + /** Service account private key for automation */ + serviceAccountPrivateKey?: string; +} + +/** + * Directory paths configuration + */ +export interface DirectoryConfig { + /** Configuration directory path */ + config: string; + /** Output directory for meeting files */ + output: string; + /** 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 group tag for file naming */ + meetingGroupForTag: string; + /** GitHub organization name */ + githubOrg: string; +} + +/** + * 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?: string; + /** GitHub repository name */ + REPO?: string; + /** Optional GitHub issue label */ + ISSUE_LABEL?: string; + /** Meeting agenda tag */ + AGENDA_TAG?: string; + /** GitHub organization override */ + GITHUB_ORG?: string; + /** Meeting location */ + LOCATION?: string; + /** Meeting time */ + TIME?: string; + /** Meeting day */ + DAY?: string; + /** Meeting frequency */ + FREQUENCY?: string; +} + +/** + * Google API clients container + */ +export interface GoogleClients { + /** Google Calendar API client */ + calendarClient: CalendarClient; + /** Google Drive API client */ + driveClient: DriveClient; +} + +/** + * Meeting issue generation result + */ +export interface MeetingIssueResult { + /** Generated issue title */ + title: string; + /** Generated issue content */ + content: string; +} diff --git a/src/utils.mjs b/src/utils.mjs new file mode 100644 index 0000000..d4ffc45 --- /dev/null +++ b/src/utils.mjs @@ -0,0 +1,100 @@ +import { spawn } from 'node:child_process'; + +import dedent from 'dedent'; + +import { TEMPLATE_VARIABLES, ISSUE_UPDATE_REGEXES } from './constants.mjs'; + +/** + * Executes a shell command asynchronously + * @param {string} command - Command to execute + * @param {string[]} args - Command arguments + * @returns {Promise} Command output + */ +export const executeCommand = (command, args) => { + return new Promise((resolve, reject) => { + // Spawn child process to execute the command + const process = spawn(command, args); + + let stdout = ''; + let stderr = ''; + + // Collect stdout data as it comes in + process.stdout.on('data', data => { + stdout += data.toString(); + }); + + // Collect stderr data for error reporting + process.stderr.on('data', data => { + stderr += data.toString(); + }); + + process.on('close', code => { + if (code === 0) { + return resolve(stdout); + } + + reject(new Error(`Command failed with code ${code}: ${stderr}`)); + }); + }); +}; + +/** + * Creates meeting information string using template + * @param {string} baseMeetingInfo - Base meeting information + * @param {string} meetingTime - ISO string of meeting time + * @param {string} invited - Invited attendees + * @param {string} observers - Observers + * @returns {string} Complete meeting information + */ +export const createMeetingInfo = ( + baseMeetingInfo, + meetingTime, + invited, + observers +) => dedent` + ${baseMeetingInfo}MEETING_TIME="${meetingTime}" + INVITEES="${invited} + + ### Observers/Guests + ${observers}" + `; + +/** + * Replaces template variables in minutes document + * @param {string} minutesDoc - Minutes document template + * @param {string} title - Meeting title + * @param {string} agendaInfo - Agenda information + * @param {string} invited - Invited attendees + * @param {string} observers - Observers + * @returns {string} Processed minutes document + */ +export const processMinutesTemplate = ( + minutesDoc, + title, + agendaInfo, + invited, + observers +) => { + // Replace all template placeholders with actual content + return minutesDoc + .replace(TEMPLATE_VARIABLES.TITLE, title) + .replace(TEMPLATE_VARIABLES.AGENDA_CONTENT, agendaInfo) + .replace(TEMPLATE_VARIABLES.INVITED, invited) + .replace(TEMPLATE_VARIABLES.OBSERVERS, observers); +}; + +/** + * Updates issue content with Google Doc link and cleans up placeholders + * @param {string} content - Original issue content + * @param {string} documentId - Google Doc ID + * @returns {string} Updated content + */ +export const updateIssueContent = (content, documentId) => + content + // Replace empty Google Doc placeholder with actual link + .replace( + ISSUE_UPDATE_REGEXES.EMPTY_GOOGLE_DOC, + `* Minutes Google Doc: ` + ) + // Remove previous minutes placeholder line + .replace(ISSUE_UPDATE_REGEXES.PREVIOUS_MINUTES, ''); From 3015c4884419e1a15583f46a1c39bf0cae0a81d4 Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Fri, 15 Aug 2025 00:23:24 +0200 Subject: [PATCH 24/48] feat: updated code/refactored added hackmd integration --- .env.example | 19 +- .../workflows/create-meeting-artifacts.yml | 13 +- .github/workflows/test.yml | 39 + .gitignore | 16 +- .prettierignore | 1 + AUTHORS | 1 + README.md | 49 +- TEMPLATES_DOCUMENTATION.md | 20 +- create-node-meeting-artifacts.mjs | 128 +- eslint.config.mjs | 2 +- global.d.ts | 9 +- package-lock.json | 3621 +++++++++++++++++ package.json | 24 +- src/config.mjs | 31 +- src/constants.mjs | 59 +- src/github.mjs | 81 +- src/google.mjs | 106 +- src/hackmd.mjs | 47 + src/meeting.mjs | 204 +- src/types.d.ts | 74 +- src/utils.mjs | 100 - src/utils/dates.mjs | 52 + src/utils/templates.mjs | 46 + src/utils/urls.mjs | 43 + templates/meeting_base_cross_project_council | 1 - templates/meeting_base_ecosystem_report | 1 - templates/meeting_base_nodejsafrica | 1 - .../meeting_base_package_metadata_interop | 1 - templates/meeting_base_security_collab | 1 - templates/meeting_base_standards | 1 - templates/meeting_base_sustainability_collab | 1 - templates/meeting_base_tsc | 2 +- templates/meeting_issue.md | 38 + test/config.test.mjs | 123 + test/snapshots/README.md | 0 35 files changed, 4412 insertions(+), 543 deletions(-) create mode 100644 .github/workflows/test.yml create mode 100644 .prettierignore create mode 100644 package-lock.json create mode 100644 src/hackmd.mjs delete mode 100644 src/utils.mjs create mode 100644 src/utils/dates.mjs create mode 100644 src/utils/templates.mjs create mode 100644 src/utils/urls.mjs create mode 100644 templates/meeting_issue.md create mode 100644 test/config.test.mjs create mode 100644 test/snapshots/README.md diff --git a/.env.example b/.env.example index a49eade..2363fe6 100644 --- a/.env.example +++ b/.env.example @@ -1,15 +1,14 @@ -# GitHub Authentication -GITHUB_TOKEN=your_github_personal_access_token_here +# Required: GitHub Token (Needs "repo" permissions) +# GITHUB_TOKEN=your_personal_access_token_or_org_token -# Google APIs Authentication -# You can get these from the Google Cloud Console -GOOGLE_CLIENT_ID=your_google_client_id_here -GOOGLE_CLIENT_SECRET=your_google_client_secret_here -GOOGLE_REDIRECT_URI=http://localhost:3000/oauth2callback +# Required: HackMD Configuration +# HACKMD_API_TOKEN=your_hackmd_api_token +# HACKMD_TEAM_NAME=your_hackmd_team_name_optional # Defaults to your own personal space -# Optional: Google Service Account (for GitHub Actions) -# GOOGLE_SERVICE_ACCOUNT_EMAIL=your_service_account_email_here -# GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY=your_service_account_private_key_here +# 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 # Optional: Directory paths (defaults to current directory and home) # MEETINGS_CONFIG_DIR=./ diff --git a/.github/workflows/create-meeting-artifacts.yml b/.github/workflows/create-meeting-artifacts.yml index 430f8b3..d65cecb 100644 --- a/.github/workflows/create-meeting-artifacts.yml +++ b/.github/workflows/create-meeting-artifacts.yml @@ -40,10 +40,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version-file: '.nvmrc' cache: 'npm' @@ -54,17 +54,18 @@ jobs: - name: Create meeting artifacts env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - GOOGLE_SERVICE_ACCOUNT_EMAIL: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_EMAIL }} - GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY: ${{ secrets.GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY }} + HACKMD_API_TOKEN: ${{ secrets.HACKMD_API_TOKEN }} + HACKMD_TEAM_NAME: ${{ secrets.HACKMD_TEAM_NAME }} + 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@v4 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: meeting-artifacts-${{ github.event.inputs.meeting_group || 'tsc' }} path: | ~/.make-node-meeting/ - minutes_temp.txt + *.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/.prettierignore b/.prettierignore new file mode 100644 index 0000000..0350652 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +templates/ diff --git a/AUTHORS b/AUTHORS index 38f115d..2886933 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1 +1,2 @@ Michael Dawson +Claudio Wunder diff --git a/README.md b/README.md index d3bf184..480664c 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # Node.js Meeting Artifacts Creator -A modern Node.js application that creates GitHub issues and Google Docs 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. +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. ## πŸ“‹ Requirements - Node.js 22+ (LTS) - GitHub Personal Access Token -- Google Cloud Project with Calendar and Drive APIs enabled -- OAuth credentials or Service Account credentials +- Google Cloud Project with Calendar API enabled (for meeting scheduling) +- Google API Key for Calendar access +- HackMD API Token (for meeting minutes) ## πŸ”‘ Authentication Setup @@ -18,23 +19,25 @@ A modern Node.js application that creates GitHub issues and Google Docs for Node - `repo` (Full control of private repositories) - `user` (Read user information) -### Google Authentication +### HackMD Authentication -#### Option 1: OAuth (for local development) +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 -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 and Google Drive API -4. Create OAuth 2.0 credentials (Desktop Application) -5. Add credentials to `.env` file +### Google Authentication (Calendar Only) -#### Option 2: Service Account (for GitHub Actions) +#### 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 and Google Drive API -4. Create a Service Account and generate JSON key file -5. Add credentials to environment variables +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 @@ -195,8 +198,8 @@ node --env-file=.env create-node-meeting-artifacts.mjs tsc The application creates: 1. **GitHub Issue**: Posted to the configured repository with meeting details and agenda -2. **Google Doc**: Meeting minutes document stored in the `/nodejs-meetings` folder -3. **Console Output**: Links to both the created issue and document +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 @@ -205,19 +208,15 @@ The application creates: #### Required - `GITHUB_TOKEN`: GitHub Personal Access Token +- `HACKMD_API_TOKEN`: HackMD API token for creating and managing documents -#### Google Authentication (choose one) - -**OAuth (Local Development):** +#### HackMD Configuration (optional) -- `GOOGLE_CLIENT_ID`: OAuth client ID -- `GOOGLE_CLIENT_SECRET`: OAuth client secret -- `GOOGLE_REDIRECT_URI`: OAuth redirect URI (default: `http://localhost:3000/oauth2callback`) +- `HACKMD_TEAM_NAME`: HackMD team name/path for team workspaces -**Service Account (GitHub Actions):** +#### Google Authentication -- `GOOGLE_SERVICE_ACCOUNT_EMAIL`: Service account email -- `GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY`: Service account private key (JSON format) +- `GOOGLE_API_KEY`: Google Calendar API Key for read-only calendar access #### Optional diff --git a/TEMPLATES_DOCUMENTATION.md b/TEMPLATES_DOCUMENTATION.md index d65351c..de16d8e 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,6 +13,22 @@ 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. diff --git a/create-node-meeting-artifacts.mjs b/create-node-meeting-artifacts.mjs index 6a8b3b3..b59cc9f 100644 --- a/create-node-meeting-artifacts.mjs +++ b/create-node-meeting-artifacts.mjs @@ -3,19 +3,17 @@ /** * Node.js Meeting Artifacts Creator * - * Creates GitHub issues and Google Docs for Node.js team meetings. + * Creates GitHub issues and HackMD documents for Node.js team meetings. * Reads meeting configuration from templates, fetches calendar events, * creates meeting minutes documents, and posts GitHub issues. * * Environment Variables Required: * - GITHUB_TOKEN: Personal access token for GitHub API - * - GOOGLE_CLIENT_ID: Google OAuth client ID - * - GOOGLE_CLIENT_SECRET: Google OAuth client secret + * - HACKMD_API_TOKEN: HackMD API token for creating documents + * - GOOGLE_API_KEY: Google Calendar API key for read-only calendar access * * Optional Environment Variables: - * - GOOGLE_SERVICE_ACCOUNT_EMAIL: Service account email (for GitHub Actions) - * - GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY: Service account private key (for GitHub Actions) - * - GOOGLE_REDIRECT_URI: OAuth redirect URI (default: http://localhost:3000/oauth2callback) + * - HACKMD_TEAM_NAME: HackMD team name/path (optional) * - MEETINGS_CONFIG_DIR: Directory containing meeting templates (default: ./) * - MEETINGS_OUTPUT_DIR: Directory for meeting output files (default: ~/.make-node-meeting/) * @@ -25,91 +23,81 @@ * npm run dev -- tsc */ -import { join, dirname } from 'node:path'; -import { fileURLToPath } from 'node:url'; - import { getConfig } from './src/config.mjs'; -import { createGitHubIssue } from './src/github.mjs'; -import { - createGoogleAuthClient, - createGoogleClients, - findNextMeetingEvent, - uploadMinutesDocument, -} from './src/google.mjs'; -import { - readMeetingConfig, - generateMeetingIssue, - createMinutesDocument, - createMeetingInfoString, -} from './src/meeting.mjs'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); +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 authentication and API clients -const googleAuth = createGoogleAuthClient(config.google); -const { calendarClient, driveClient } = await createGoogleClients(googleAuth); +// Step 2: Initialize Google Calendar client with API Key +const calendarClient = google.createGoogleCalendarClient(config.google); -// Step 3: Read meeting configuration from templates -const meetingConfig = await readMeetingConfig( - config.meetingGroup, - config.directories.templates +// Step 3: Initialize HackMD client +const hackmdClient = hackmd.createHackMDClientInstance( + config.hackmd.apiToken, + config.hackmd.teamName ); -// Step 4: Find next meeting event in calendar -const event = await findNextMeetingEvent(calendarClient, meetingConfig); -const meetingTime = new Date(event.start.dateTime).toISOString(); +// Step 4: Read meeting configuration from templates +const meetingConfig = await meetings.readMeetingConfig(config); -// Step 5: Generate meeting information string for external tools -const meetingInfo = createMeetingInfoString(meetingConfig, meetingTime); +// Step 5: Find next meeting event in calendar +const event = await google.findNextMeetingEvent(calendarClient, meetingConfig); -// Step 6: Generate meeting issue content using make-node-meeting tool -const makeNodeMeetingPath = join( - __dirname, - 'node_modules/make-node-meeting/make-node-meeting.sh' -); +// Step 6: Extract meeting date from event +const meetingDate = new Date(event.start.dateTime); -const { title, content } = await generateMeetingIssue( - meetingConfig.meetingGroupForTag, - meetingInfo, - config.directories.output, - makeNodeMeetingPath +// Step 7: Get Meeting Title +const meetingTitle = meetings.generateMeetingTitle( + config, + meetingConfig, + meetingDate ); -// Step 7: Create minutes document content using node-meeting-agenda tool -const agendaToolPath = join( - __dirname, - 'node_modules/node-meeting-agenda/node-meeting-agenda.js' +// Step 8: Generate meeting issue content using native implementation +const issueContent = await meetings.generateMeetingIssue( + config, + meetingConfig, + meetingDate ); -const minutesContent = await createMinutesDocument( - meetingConfig.meetingGroupForTag, - meetingConfig.githubOrg, +// Step 9: Create GitHub issue with HackMD link +const githubIssue = await github.createGitHubIssue( + config, meetingConfig, - title, - config.directories.templates, - config.meetingGroup, - agendaToolPath + meetingTitle, + issueContent ); -// Step 8: Upload minutes document to Google Drive -const uploadResult = await uploadMinutesDocument( - driveClient, - title, - minutesContent +// Step 10: Create HackMD document with meeting notes +const hackmdNote = await hackmd.createMeetingNotesDocument( + hackmdClient, + meetingTitle, + '' ); -// Step 9: Create GitHub issue with Google Doc link -const issue = await createGitHubIssue( - config.githubToken, +// Step 11: Get the HackMD document link +const noteLink = hackmdNote.publishLink || `https://hackmd.io/${hackmdNote.id}`; + +// Step 12: Update the minutes content with the HackMD link +const minutesContent = await meetings.generateMeetingMinutes( + config, meetingConfig, - title, - content, - uploadResult.id + meetingTitle, + noteLink, + githubIssue.html_url +); + +// Step 13: 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: ${issue.html_url}`); -console.log(`Doc: https://docs.google.com/document/d/${uploadResult.id}`); +console.log(`Created GitHub issue: ${githubIssue.html_url}`); +console.log(`Created HackMD document: ${noteLink}`); diff --git a/eslint.config.mjs b/eslint.config.mjs index 2516438..979c62d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -7,7 +7,7 @@ export default [ pluginJs.configs.recommended, importX.flatConfigs.recommended, { - ignores: ['node_modules/', '.env*', 'minutes_temp.txt'], + ignores: ['node_modules', '.env*', 'minutes_temp.txt'], }, { files: ['**/*.mjs'], diff --git a/global.d.ts b/global.d.ts index 1816a1d..29df2df 100644 --- a/global.d.ts +++ b/global.d.ts @@ -4,17 +4,16 @@ */ import type { calendar_v3 } from '@googleapis/calendar'; -import type { drive_v3 } from '@googleapis/drive'; import type { RestEndpointMethodTypes } from '@octokit/rest'; -import type { GoogleAuth } from 'google-auth-library'; +import type { HackMDAPI } 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 DriveFile = drive_v3.Schema$File; - type DriveClient = drive_v3.Drive; - type GoogleAuthClient = GoogleAuth; + type HackMDClient = HackMDAPI; + type HackMDNote = SingleNote; // GitHub API type aliases type GitHubIssue = diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a124483 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3621 @@ +{ + "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", + "make-node-meeting": "^2.0.0", + "node-meeting-agenda": "^2.0.1", + "properties-parser": "^0.6.0" + }, + "bin": { + "create-meeting": "bin/create-meeting" + }, + "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/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.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-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/application-config": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/application-config/-/application-config-2.0.0.tgz", + "integrity": "sha512-NC5/0guSZK3/UgUDfCk/riByXzqz0owL1L3r63JPSBzYk5QALrp3bLxbsR7qeSfvYfFmAhnp3dbqYsW3U9MpZQ==", + "license": "MIT", + "dependencies": { + "application-config-path": "^0.1.0", + "load-json-file": "^6.2.0", + "write-json-file": "^4.2.0" + }, + "engines": { + "node": ">=8.3" + } + }, + "node_modules/application-config-path": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/application-config-path/-/application-config-path-0.1.1.tgz", + "integrity": "sha512-zy9cHePtMP0YhwG+CfHm0bgwdnga2X3gZexpdCwEj//dpb+TKajtiC8REEUJUSq6Ab4f9cgNy2l8ObXzCXFkEw==", + "license": "MIT" + }, + "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.11.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", + "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "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/bl": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.1.tgz", + "integrity": "sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ==", + "license": "MIT", + "dependencies": { + "readable-stream": "^3.0.1" + } + }, + "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": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "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", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "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/buffer-from": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz", + "integrity": "sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==", + "license": "MIT" + }, + "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/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "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==", + "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==", + "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/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "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/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha512-+AWBwjGadtksxjOQSFDhPNQbed7icNXApT4+2BNpsXzcCBiInq2H9XW0O8sfHFaPmnQRs7cg/P0fAr2IWQSW0g==", + "license": "BSD", + "dependencies": { + "readable-stream": "~1.1.9" + } + }, + "node_modules/duplexer2/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, + "node_modules/duplexer2/node_modules/readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "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/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.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/ghauth": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/ghauth/-/ghauth-5.0.2.tgz", + "integrity": "sha512-9mEbxQgiUw6LmjInaDrcOwZMEfFJ0kekbH3ii5MmwhsJaxXTC2RgGzn+1BewUavn1Q8RwnPKN37aATyeZf3tWQ==", + "license": "MIT", + "dependencies": { + "application-config": "^2.0.0", + "node-fetch": "^2.6.0", + "ora": "^4.0.5", + "read": "^1.0.7" + } + }, + "node_modules/ghauth/node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/ghissues": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/ghissues/-/ghissues-1.1.4.tgz", + "integrity": "sha512-YFBe75cEank9WogH1i1kRqDoH8Z+l+O9Y/CopmWCGhsOg9NR0PdMo8E+8THTwCXFAFv4NPhqLQOOPZypnDStjQ==", + "license": "MIT", + "dependencies": { + "ghutils": "^4.0.0" + } + }, + "node_modules/ghrepos": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ghrepos/-/ghrepos-2.1.0.tgz", + "integrity": "sha512-6GM0ohSDTAv7xD6GsKfxJiV/CajoofRyUwu0E8l29d1o6lFAUxmmyMP/FH33afA20ZrXzxxcTtN6TsYvudMoAg==", + "license": "MIT", + "dependencies": { + "ghutils": "~3.2.0" + } + }, + "node_modules/ghrepos/node_modules/ghutils": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/ghutils/-/ghutils-3.2.6.tgz", + "integrity": "sha512-WpYHgLQkqU7Cv147wKUEThyj6qKHCdnAG2CL9RRsRQImVdLGdVqblJ3JUnj3ToQwgm1ALPS+FXgR0448AgGPUg==", + "license": "MIT", + "dependencies": { + "jsonist": "~2.1.0", + "xtend": "~4.0.1" + } + }, + "node_modules/ghrepos/node_modules/jsonist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/jsonist/-/jsonist-2.1.2.tgz", + "integrity": "sha512-8yqmWJAC2VaYoSKQAbsfgCpGY5o/1etWzx6ZxaZrC4iGaHrHUZEo+a2MyF8w+2uTavTlHdLWaZUoR19UfBstxQ==", + "license": "MIT", + "dependencies": { + "bl": "~3.0.0", + "hyperquest": "~2.1.3", + "json-stringify-safe": "~5.0.1", + "xtend": "~4.0.1" + } + }, + "node_modules/ghutils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ghutils/-/ghutils-4.0.0.tgz", + "integrity": "sha512-WRme8qe6SX0WCN1cY9F4hE8/dhjZti2q7i7cDuT0kV7PZZrGceFRNP5ZSQM1+RjHpcqvODQi5YSZSsqG+Yk3zQ==", + "license": "MIT", + "dependencies": { + "jsonist": "~3.0.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/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "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==", + "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/hyperquest": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/hyperquest/-/hyperquest-2.1.3.tgz", + "integrity": "sha512-fUuDOrB47PqNK/BAMOS13v41UoaqIxqSLHX6CAbOD7OfT+/GCWO1/vPLfTNutOeXrv1ikuaZ3yux+33Z9vh+rw==", + "license": "MIT", + "dependencies": { + "buffer-from": "^0.1.1", + "duplexer2": "~0.0.2", + "through2": "~0.6.3" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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": "BSD-3-Clause" + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "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/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT" + }, + "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-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "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-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "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/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, + "node_modules/jsonist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonist/-/jsonist-3.0.1.tgz", + "integrity": "sha512-+lrAqdk5BO36j5RG7MSY2M8XqBqBwJRt/+iSfLmpelBBsi0kFflqhtlROeioDA5MlHNhZxm0Doslr7QSRzCqTQ==", + "license": "MIT", + "dependencies": { + "bl": "~4.0.0", + "hyperquest": "~2.1.3", + "json-stringify-safe": "~5.0.1" + } + }, + "node_modules/jsonist/node_modules/bl": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.4.tgz", + "integrity": "sha512-7tdr4EpSd7jJ6tuQ21vu2ke8w7pNEstzj1O8wwq6sNNzO3UDi5MA8Gny/gquCj7r2C6fHudg8tKRGyjRgmvNxQ==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "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/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/load-json-file": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz", + "integrity": "sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.15", + "parse-json": "^5.0.0", + "strip-bom": "^4.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/log-symbols": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", + "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", + "license": "MIT", + "dependencies": { + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/log-symbols/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "license": "MIT", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/make-node-meeting": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/make-node-meeting/-/make-node-meeting-2.0.0.tgz", + "integrity": "sha512-QhC2KV4iNMF5XaNEUSSZ73nTGLsUhNEAu0MYI/C66ThJyJXrXguQ29wvl0AxFtFBMVaHgifeO8JE2yGJh+yjAw==", + "license": "MIT", + "dependencies": { + "node-meeting-agenda": "^2.0.1" + }, + "bin": { + "make-node-meeting": "make-node-meeting.sh" + } + }, + "node_modules/map-async": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/map-async/-/map-async-0.1.1.tgz", + "integrity": "sha512-nim726/DRF1yuTrx3qNJcFNqJCgiFD98eh/49EdI5LWTspX4whkVvT0hOcMEVJWCijKkp519vCY1uD05Hklc6w==" + }, + "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/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "license": "MIT", + "engines": { + "node": ">=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/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "license": "ISC" + }, + "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/node-meeting-agenda": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/node-meeting-agenda/-/node-meeting-agenda-2.0.1.tgz", + "integrity": "sha512-x6cn5sVUDVlWMlgwSH0rWVa/DW7qnN7L4mEiNiXsdqPfZY3BoXEOFPh11YM4OB/cSekT/oB4+mJghvb2TwkO7g==", + "license": "MIT", + "dependencies": { + "ghauth": "^5.0.1", + "ghissues": "~1.1.2", + "ghrepos": "2.1", + "map-async": "~0.1.1" + }, + "bin": { + "node-meeting-agenda": "node-meeting-agenda.js" + } + }, + "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/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/ora": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-4.1.1.tgz", + "integrity": "sha512-sjYP8QyVWBpBZWD6Vr1M/KwknSw6kJOz41tvGMlwWeClHBtYKTbHMki1PsLZnxKpXMPbTKv9b3pjQu3REib96A==", + "license": "MIT", + "dependencies": { + "chalk": "^3.0.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.2.0", + "is-interactive": "^1.0.0", + "log-symbols": "^3.0.0", + "mute-stream": "0.0.8", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "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-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "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/properties-parser": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/properties-parser/-/properties-parser-0.6.0.tgz", + "integrity": "sha512-qvr2cSmoA0dln0MARAKwBzPkkXn7FqwX+RVVNpMdMJc7rt9mqO2cXwluxtux9fHrLhjnPFaQkS8BM0kFrTCnSw==", + "license": "MIT", + "engines": { + "node": ">= 0.3.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/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readable-stream/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "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/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "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/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC" + }, + "node_modules/sort-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz", + "integrity": "sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg==", + "license": "MIT", + "dependencies": { + "is-plain-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "license": "MIT" + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/through2": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", + "integrity": "sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==", + "license": "MIT", + "dependencies": { + "readable-stream": ">=1.0.33-1 <1.1.0-0", + "xtend": ">=4.0.0 <4.1.0-0" + } + }, + "node_modules/through2/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "license": "MIT" + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "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/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "license": "MIT", + "dependencies": { + "is-typedarray": "^1.0.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/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "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/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "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/write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/write-json-file": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/write-json-file/-/write-json-file-4.3.0.tgz", + "integrity": "sha512-PxiShnxf0IlnQuMYOPPhPkhExoCQuTUNPOa/2JWCYTmBquU9njyyDuwRKN26IZBlp4yn1nt+Agh2HOOBl+55HQ==", + "license": "MIT", + "dependencies": { + "detect-indent": "^6.0.0", + "graceful-fs": "^4.1.15", + "is-plain-obj": "^2.0.0", + "make-dir": "^3.0.0", + "sort-keys": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "engines": { + "node": ">=8.3" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "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 4f19708..8910535 100644 --- a/package.json +++ b/package.json @@ -11,23 +11,20 @@ "node": ">=22.0.0" }, "dependencies": { - "@googleapis/calendar": "^9.7.0", - "@googleapis/drive": "^8.12.0", - "@octokit/rest": "^21.0.2", - "dedent": "^1.5.3", - "google-auth-library": "^9.14.1", - "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", + "properties-parser": "^0.6.0" }, "devDependencies": { - "@eslint/js": "^9.30.1", + "@eslint/js": "^9.33.0", "@types/properties-parser": "^0.3.3", - "eslint": "^9.19.0", + "eslint": "^9.33.0", "eslint-plugin-import-x": "^4.16.1", - "eslint-plugin-jsdoc": "^51.3.3", + "eslint-plugin-jsdoc": "^54.0.0", "globals": "^16.3.0", - "prettier": "^3.4.2" + "prettier": "^3.6.2" }, "scripts": { "dev": "node --env-file=.env create-node-meeting-artifacts.mjs", @@ -36,6 +33,9 @@ "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": "node create-node-meeting-artifacts.mjs uvwasi", "uvwasi-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs uvwasi", "tsc-meeting": "node create-node-meeting-artifacts.mjs tsc", diff --git a/src/config.mjs b/src/config.mjs index 6a56d33..891ff4f 100644 --- a/src/config.mjs +++ b/src/config.mjs @@ -1,11 +1,7 @@ import { homedir } from 'node:os'; import { join, dirname } from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { DEFAULT_CONFIG } from './constants.mjs'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); +const defaultMeetingsDirectory = join(homedir(), '.make-node-meeting'); /** * Gets the application configuration from environment variables and arguments @@ -13,28 +9,29 @@ const __dirname = dirname(__filename); */ export const getConfig = () => ({ // Meeting group from command line argument, defaults to 'tsc' - meetingGroup: process.argv[2] || 'tsc', + meetingGroup: process.argv[2], // GitHub personal access token from environment githubToken: process.env.GITHUB_TOKEN, - // Google authentication configuration - supports both OAuth and Service Account + // Google authentication configuration - now uses API Keys for simplicity google: { - // OAuth credentials for local development - clientId: process.env.GOOGLE_CLIENT_ID, - clientSecret: process.env.GOOGLE_CLIENT_SECRET, - redirectUri: process.env.GOOGLE_REDIRECT_URI || DEFAULT_CONFIG.redirectUri, + // Google API Key for Calendar access (preferred method) + apiKey: process.env.GOOGLE_API_KEY, + }, - // Service Account credentials for GitHub Actions automation - serviceAccountEmail: process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL, - serviceAccountPrivateKey: process.env.GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY, + // HackMD configuration for meeting notes + hackmd: { + // HackMD API token for authentication + apiToken: process.env.HACKMD_API_TOKEN, + // HackMD team name + teamName: process.env.HACKMD_TEAM_NAME, }, // Directory paths for templates, output, and configuration directories: { config: process.env.MEETINGS_CONFIG_DIR || './', - output: - process.env.MEETINGS_OUTPUT_DIR || join(homedir(), '.make-node-meeting'), - templates: join(dirname(__dirname), 'templates'), + output: process.env.MEETINGS_OUTPUT_DIR || defaultMeetingsDirectory, + templates: join(dirname(import.meta.dirname), 'templates'), }, }); diff --git a/src/constants.mjs b/src/constants.mjs index f407658..1dde19e 100644 --- a/src/constants.mjs +++ b/src/constants.mjs @@ -1,48 +1,9 @@ -// Google API scopes required for calendar and drive access -export const GOOGLE_SCOPES = [ - // Read calendar events to find next meeting - 'https://www.googleapis.com/auth/calendar.readonly', - // Create Google Docs for minutes - 'https://www.googleapis.com/auth/drive.file', -]; - // Default configuration values export const DEFAULT_CONFIG = { - // Default OAuth redirect URI for local development - redirectUri: 'http://localhost:3000/oauth2callback', // Default GitHub organization name githubOrg: 'nodejs', - // Default GitHub issue assignee - assignee: 'mhdawson', -}; - -// Template variable placeholders used in meeting minutes -export const TEMPLATE_VARIABLES = { - // Placeholder for meeting title - TITLE: '$TITLE$', - // Placeholder for agenda content - AGENDA_CONTENT: '$AGENDA_CONTENT$', - // Placeholder for invited attendees list - INVITED: '$INVITED$', - // Placeholder for observers list - OBSERVERS: '$OBSERVERS$', -}; - -// Google Docs MIME type for creating documents -export const GOOGLE_DOC_MIME_TYPE = 'application/vnd.google-apps.document'; - -// Google Drive folder MIME type for creating/searching folders -export const GOOGLE_FOLDER_MIME_TYPE = 'application/vnd.google-apps.folder'; - -// Name of the Google Drive folder where meeting minutes are stored -export const NODEJS_MEETINGS_FOLDER_NAME = 'nodejs-meetings'; - -// Regular expressions for updating issue content -export const ISSUE_UPDATE_REGEXES = { - // Matches empty Google Doc placeholder to replace with actual link - EMPTY_GOOGLE_DOC: /\* \*\*Minutes Google Doc\*\*: <>/, - // Matches previous minutes placeholder line to remove - PREVIOUS_MINUTES: /\* _Previous Minutes Google Doc: <>_/, + // Default Host of the Meeting + defaultHost: 'Node.js', }; // Time constants for date calculations @@ -50,3 +11,19 @@ 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' }, +]; diff --git a/src/github.mjs b/src/github.mjs index c32a7e1..61bfc52 100644 --- a/src/github.mjs +++ b/src/github.mjs @@ -1,45 +1,90 @@ import { Octokit } from '@octokit/rest'; -import { DEFAULT_CONFIG } from './constants.mjs'; -import { updateIssueContent } from './utils.mjs'; - /** * Creates GitHub issue with meeting information and Google Doc link - * @param {string} githubToken - GitHub personal access token + * @param {import('./types.d.ts').AppConfig} config - Application configuration * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration object * @param {string} title - Issue title * @param {string} content - Issue content - * @param {string} documentId - Google Doc ID to link in issue * @returns {Promise} Created issue data */ export const createGitHubIssue = async ( - githubToken, + config, meetingConfig, title, - content, - documentId + content ) => { // Initialize GitHub API client with authentication token - const octokit = new Octokit({ auth: githubToken }); - - // Update the issue content to include the Google Doc link and clean up placeholders - const updatedContent = updateIssueContent(content, documentId); + const octokit = new Octokit({ auth: config.githubToken }); // Extract issue label from config, removing quotes if present - const issueLabel = meetingConfig.properties.ISSUE_LABEL?.replace(/"/g, ''); + const issueLabel = meetingConfig.properties.ISSUE_LABEL; // Create the GitHub issue with meeting information const response = await octokit.rest.issues.create({ // Repository information from meeting config - owner: meetingConfig.properties.USER?.replace(/"/g, ''), - repo: meetingConfig.properties.REPO?.replace(/"/g, ''), + owner: meetingConfig.properties.USER, + repo: meetingConfig.properties.REPO, title, - body: updatedContent, - // Default assignee for Node.js meetings - assignee: DEFAULT_CONFIG.assignee, + body: content, // Add label if specified in config labels: issueLabel ? [issueLabel] : undefined, }); return response.data; }; + +/** + * Fetches GitHub issues from all repositories in an organization with a specific label + * @param {string} token - GitHub personal access token + * @param {string} org - GitHub organization name (e.g., 'nodejs') + * @param {string} label - Label to filter by (e.g., 'tsc-agenda') + * @returns {Promise} Formatted markdown string of issues + */ +export const fetchAgendaIssues = async (token, org, label) => { + const octokit = new Octokit({ auth: token }); + + // Get all public repositories in the organization + const repos = await octokit.paginate(octokit.rest.repos.listForOrg, { + org, + type: 'public', + per_page: 100, + }); + + // Fetch issues from all repositories concurrently + const issuePromises = repos.map(async repo => { + const issues = await octokit.paginate(octokit.rest.issues.listForRepo, { + owner: org, + repo: repo.name, + labels: label, + state: 'open', + per_page: 100, + }); + + const filteredIssues = issues.filter(issue => !issue.pull_request); // Exclude PRs + + return { repoName: repo.name, issues: filteredIssues }; + }); + + const repoIssues = await Promise.all(issuePromises); + + // Format issues as markdown + let agendaMarkdown = ''; + + repoIssues.forEach(({ repoName, issues }) => { + if (issues.length > 0) { + agendaMarkdown += `### ${org}/${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(); +}; diff --git a/src/google.mjs b/src/google.mjs index 7cd473e..f47edf4 100644 --- a/src/google.mjs +++ b/src/google.mjs @@ -1,52 +1,33 @@ import { calendar } from '@googleapis/calendar'; -import { drive } from '@googleapis/drive'; -import { GoogleAuth } from 'google-auth-library'; -import { - GOOGLE_SCOPES, - GOOGLE_DOC_MIME_TYPE, - GOOGLE_FOLDER_MIME_TYPE, - NODEJS_MEETINGS_FOLDER_NAME, - TIME_CONSTANTS, -} from './constants.mjs'; +import { TIME_CONSTANTS } from './constants.mjs'; /** - * Creates a Google Auth client using either OAuth or service account credentials - * @param {import('./types.d.ts').GoogleConfig} gConfig - Google authentication configuration - * @returns {GoogleAuthClient} Configured Google Auth client + * 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 createGoogleAuthClient = gConfig => { - // Use Service Account authentication for automated environments (GitHub Actions) - if (gConfig.serviceAccountEmail && gConfig.serviceAccountPrivateKey) { - return new GoogleAuth({ - credentials: { - client_email: gConfig.serviceAccountEmail, - // Replace escaped newlines with actual newlines in private key - private_key: gConfig.serviceAccountPrivateKey.replace(/\\n/g, '\n'), - }, - scopes: GOOGLE_SCOPES, +export const createGoogleCalendarClient = gConfig => { + // Use API Key authentication (simpler and more straightforward) + if (gConfig.apiKey) { + return calendar({ + version: 'v3', + auth: gConfig.apiKey, }); } - // Use OAuth authentication for local development - return new GoogleAuth({ - credentials: { - client_id: gConfig.clientId, - client_secret: gConfig.clientSecret, - redirect_uris: [gConfig.redirectUri], - }, - scopes: GOOGLE_SCOPES, - }); + throw new Error('Google API Key is required for Google Calendar access'); }; /** * Finds the next meeting event in Google Calendar within the next week - * @param {CalendarClient} calendarClient - Google Calendar client + * @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(); + const nextWeek = new Date(now.getTime() + TIME_CONSTANTS.WEEK_IN_MS); // Search for events in the specified calendar using the filter text @@ -70,64 +51,3 @@ export const findNextMeetingEvent = async (calendarClient, meetingConfig) => { // Return the first (next) event found return response.data.items[0]; }; - -/** - * Finds a folder by name in Google Drive - * @param {DriveClient} driveClient - Google Drive client - * @param {string} folderName - Name of the folder to find - * @returns {Promise} Folder ID - */ -export const findFolder = async (driveClient, folderName) => { - // Search for the folder (assuming it exists) - const searchResponse = await driveClient.files.list({ - q: `name='${folderName}' and mimeType='${GOOGLE_FOLDER_MIME_TYPE}' and trashed=false`, - fields: 'files(id, name)', - }); - - // Return the folder ID - if (searchResponse.data.files && searchResponse.data.files.length > 0) { - return searchResponse.data.files[0].id; - } - - throw new Error(`${folderName} folder not found in Google Drive`); -}; - -/** - * Uploads minutes document to Google Drive in the nodejs-meetings folder - * @param {DriveClient} driveClient - Google Drive client - * @param {string} title - Document title - * @param {string} content - Document content - * @returns {Promise} Upload result with document ID - */ -export const uploadMinutesDocument = async (driveClient, title, content) => { - // Find the nodejs-meetings folder - const folderId = await findFolder(driveClient, NODEJS_MEETINGS_FOLDER_NAME); - - // Create a new Google Doc with the meeting minutes content in the correct folder - const response = await driveClient.files.create({ - requestBody: { - name: title, - mimeType: GOOGLE_DOC_MIME_TYPE, - parents: [folderId], - }, - media: { mimeType: 'text/plain', body: content }, - }); - - return response.data; -}; - -/** - * Creates Google API clients from authenticated client - * @param {GoogleAuthClient} googleAuth - Authenticated Google Auth client - * @returns {Promise} Google API clients - */ -export const createGoogleClients = async googleAuth => { - // Get the authenticated client for API calls - const authClient = await googleAuth.getClient(); - - // Return configured Calendar and Drive API clients - return { - calendarClient: calendar({ version: 'v3', auth: authClient }), - driveClient: drive({ version: 'v3', auth: authClient }), - }; -}; diff --git a/src/hackmd.mjs b/src/hackmd.mjs new file mode 100644 index 0000000..6d7034a --- /dev/null +++ b/src/hackmd.mjs @@ -0,0 +1,47 @@ +import HackMDAPI from '@hackmd/api'; + +/** + * Creates the default permissions for our generated docs + * @type {Pick} */ +const hackMdPermissions = { + readPermission: 'guest', // Allow signed-in users to read + writePermission: 'signed_in', // Allow signed-in users to write + commentPermission: 'signed_in_users', // Allow signed-in users to comment +}; + +/** + * Creates a HackMD API client + * @param {string} apiToken - HackMD API token + * @param {string} teamPath - HackMD team path/name (optional) + * @returns {HackMDClient} Configured HackMD API client + */ +export const createHackMDClientInstance = (apiToken, teamPath) => { + // Use team-specific API endpoint if teamPath is provided + const baseURL = teamPath + ? `https://api.hackmd.io/v1/teams/${teamPath}` + : 'https://api.hackmd.io/v1'; + + return new HackMDAPI(apiToken, baseURL); +}; + +/** + * Creates a new meeting notes document in HackMD + * @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) => { + // Create a new note with the meeting content + return hackmdClient.createNote({ title, content, ...hackMdPermissions }); +}; + +/** + * Updates an existing meeting notes document in HackMD + * @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) => + hackmdClient.updateNote(noteId, { content }); diff --git a/src/meeting.mjs b/src/meeting.mjs index 261cccf..6eeb47f 100644 --- a/src/meeting.mjs +++ b/src/meeting.mjs @@ -1,138 +1,164 @@ -import { readFile, writeFile } from 'node:fs/promises'; +import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; -import parser from 'properties-parser'; - import { DEFAULT_CONFIG } from './constants.mjs'; -import { - executeCommand, - createMeetingInfo, - processMinutesTemplate, -} from './utils.mjs'; +import * as github from './github.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 {string} meetingGroup - The meeting group name - * @param {string} templatesDir - Directory containing template files + * @param {import('./types.d.ts').AppConfig} config - Application configuration * @returns {Promise} Meeting configuration object */ -export const readMeetingConfig = async (meetingGroup, templatesDir) => { +export const readMeetingConfig = async config => { // Read all template files asynchronously const invited = await readFile( - join(templatesDir, `invited_${meetingGroup}`), + join(config.directories.templates, `invited_${config.meetingGroup}`), 'utf8' ); const observers = await readFile( - join(templatesDir, `observers_${meetingGroup}`), + join(config.directories.templates, `observers_${config.meetingGroup}`), 'utf8' ); const baseMeetingInfo = await readFile( - join(templatesDir, `meeting_base_${meetingGroup}`), + join(config.directories.templates, `meeting_base_${config.meetingGroup}`), 'utf8' ); - // Parse meeting properties from the base info - const meetingProperties = parser.parse(baseMeetingInfo); - return { invited, observers, baseMeetingInfo, - properties: meetingProperties, - meetingGroupForTag: meetingProperties.AGENDA_TAG - ? meetingProperties.AGENDA_TAG.replace('-agenda', '') - : meetingGroup, - githubOrg: meetingProperties.GITHUB_ORG - ? meetingProperties.GITHUB_ORG.replace(/"/g, '') - : DEFAULT_CONFIG.githubOrg, + properties: templates.parseMeetingProperties(baseMeetingInfo), }; }; /** - * Generates meeting issue content using make-node-meeting tool - * @param {string} meetingGroupForTag - Meeting group tag for file naming - * @param {string} meetingInfo - Complete meeting information string - * @param {string} outputDir - Output directory for meeting files - * @param {string} toolPath - Path to make-node-meeting tool - * @returns {Promise} Object containing issue title and content + * 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.defaultHost; + const groupName = props.GROUP_NAME ?? config.meetingGroup; + + const utcShort = meetingDate.toISOString().split('T')[0]; + + return `${host} ${groupName} Meeting ${utcShort}`; +}; + +/** + * 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 {Date} meetingDate - Date of the meeting */ export const generateMeetingIssue = async ( - meetingGroupForTag, - meetingInfo, - outputDir, - toolPath + config, + meetingConfig, + meetingDate ) => { - // Write meeting info to file for make-node-meeting tool - const meetingConfigPath = join(outputDir, `${meetingGroupForTag}.sh`); + const props = meetingConfig.properties; - await writeFile(meetingConfigPath, meetingInfo); + const joiningInstructions = props.JOINING_INSTRUCTIONS ?? ''; + const githubOrg = props.USER ?? DEFAULT_CONFIG.githubOrg; - // Generate issue content using external tool - const newIssue = await executeCommand('bash', [toolPath, meetingGroupForTag]); + const groupName = props.GROUP_NAME ?? config.meetingGroup; + const agendaTag = props.AGENDA_TAG ?? `${config.meetingGroup}-agenda`; - // Parse title and content from tool output - const issueLines = newIssue.split('\n'); - const title = issueLines[1]; - const content = issueLines.slice(4).join('\n'); + // Format the meeting date and timezones + const { utc, timezones } = dates.formatTimezones(meetingDate); - return { title, content }; + // Generate timezone conversion links + const timeAndDateLink = urls.generateTimeAndDateLink(meetingDate, groupName); + const wolframLink = urls.generateWolframAlphaLink(meetingDate); + + // Fetch agenda issues from GitHub + const agendaContent = await github.fetchAgendaIssues( + config.githubToken, + githubOrg, + agendaTag + ); + + // 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: agendaContent ?? '*No agenda items found.*', + INVITEES: meetingConfig.invited, + JOINING_INSTRUCTIONS: joiningInstructions, + OBSERVERS: meetingConfig.observers ?? '', + }; + + return templates.parseVariables(template, templateVariables); }; /** * Creates meeting minutes document content by processing template - * @param {string} meetingGroupForTag - Meeting group tag for agenda generation - * @param {string} githubOrg - GitHub organization name - * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration object - * @param {string} title - Meeting title - * @param {string} templatesDir - Templates directory path - * @param {string} meetingGroup - Original meeting group name - * @param {string} agendaToolPath - Path to node-meeting-agenda tool + * @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} minutesDocLink - Minutes document link (optional) + * @param {string} githubIssueLink - GitHub issue link (optional) * @returns {Promise} Processed minutes document content */ -export const createMinutesDocument = async ( - meetingGroupForTag, - githubOrg, +export const generateMeetingMinutes = async ( + config, meetingConfig, - title, - templatesDir, - meetingGroup, - agendaToolPath + meetingTitle, + minutesDocLink = '$MINUTES_DOC$', + githubIssueLink = '$GITHUB_ISSUE$' ) => { - // Get agenda information using external tool - const agendaInfo = await executeCommand('node', [ - agendaToolPath, - `${meetingGroupForTag}-agenda`, - githubOrg, - ]); + const props = meetingConfig.properties; - // Read minutes template file asynchronously - const minutesDoc = await readFile( - join(templatesDir, `minutes_base_${meetingGroup}`), - 'utf8' - ); + const githubOrg = props.USER ?? DEFAULT_CONFIG.githubOrg; - // Process template variables and return final document - return processMinutesTemplate( - minutesDoc, - title, - agendaInfo, - meetingConfig.invited, - meetingConfig.observers + const agendaTag = props.AGENDA_TAG ?? `${config.meetingGroup}-agenda`; + + // Get agenda information using native implementation + const agendaInfo = await github.fetchAgendaIssues( + config.githubToken, + githubOrg, + agendaTag ); -}; -/** - * Creates complete meeting information string for make-node-meeting tool - * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration object - * @param {string} meetingTime - ISO string of meeting time - * @returns {string} Complete meeting information for tool consumption - */ -export const createMeetingInfoString = (meetingConfig, meetingTime) => - createMeetingInfo( - meetingConfig.baseMeetingInfo, - meetingTime, - meetingConfig.invited, - meetingConfig.observers + // 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: agendaInfo, + 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 index 1204de7..b5fbe01 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -10,26 +10,30 @@ export interface AppConfig { meetingGroup: string; /** GitHub personal access token */ githubToken: string; - /** Google API configuration */ + /** Google API configuration (Calendar only) */ google: GoogleConfig; + /** HackMD API configuration */ + hackmd: HackMDConfig; /** Directory paths configuration */ directories: DirectoryConfig; } /** - * Google authentication configuration + * Google authentication configuration (Calendar only) */ export interface GoogleConfig { - /** OAuth client ID */ - clientId?: string; - /** OAuth client secret */ - clientSecret?: string; - /** OAuth redirect URI */ - redirectUri?: string; - /** Service account email for automation */ - serviceAccountEmail?: string; - /** Service account private key for automation */ - serviceAccountPrivateKey?: string; + /** Google API Key for Calendar access */ + apiKey?: string; +} + +/** + * HackMD API configuration + */ +export interface HackMDConfig { + /** HackMD API token */ + apiToken: string; + /** HackMD team name/path */ + teamName?: string; } /** @@ -56,10 +60,6 @@ export interface MeetingConfig { baseMeetingInfo: string; /** Parsed meeting properties */ properties: MeetingProperties; - /** Meeting group tag for file naming */ - meetingGroupForTag: string; - /** GitHub organization name */ - githubOrg: string; } /** @@ -70,42 +70,26 @@ export interface MeetingProperties { CALENDAR_ID?: string; /** Text filter for calendar events */ CALENDAR_FILTER?: string; - /** GitHub repository owner */ + /** 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; - /** Meeting agenda tag */ - AGENDA_TAG?: string; - /** GitHub organization override */ - GITHUB_ORG?: string; - /** Meeting location */ + /** Meeting joining instructions */ + JOINING_INSTRUCTIONS?: string; + /** Meeting location (deprecated) */ LOCATION?: string; - /** Meeting time */ + /** Meeting time (deprecated) */ TIME?: string; - /** Meeting day */ + /** Meeting day (deprecated) */ DAY?: string; - /** Meeting frequency */ + /** Meeting frequency (deprecated) */ FREQUENCY?: string; } - -/** - * Google API clients container - */ -export interface GoogleClients { - /** Google Calendar API client */ - calendarClient: CalendarClient; - /** Google Drive API client */ - driveClient: DriveClient; -} - -/** - * Meeting issue generation result - */ -export interface MeetingIssueResult { - /** Generated issue title */ - title: string; - /** Generated issue content */ - content: string; -} diff --git a/src/utils.mjs b/src/utils.mjs deleted file mode 100644 index d4ffc45..0000000 --- a/src/utils.mjs +++ /dev/null @@ -1,100 +0,0 @@ -import { spawn } from 'node:child_process'; - -import dedent from 'dedent'; - -import { TEMPLATE_VARIABLES, ISSUE_UPDATE_REGEXES } from './constants.mjs'; - -/** - * Executes a shell command asynchronously - * @param {string} command - Command to execute - * @param {string[]} args - Command arguments - * @returns {Promise} Command output - */ -export const executeCommand = (command, args) => { - return new Promise((resolve, reject) => { - // Spawn child process to execute the command - const process = spawn(command, args); - - let stdout = ''; - let stderr = ''; - - // Collect stdout data as it comes in - process.stdout.on('data', data => { - stdout += data.toString(); - }); - - // Collect stderr data for error reporting - process.stderr.on('data', data => { - stderr += data.toString(); - }); - - process.on('close', code => { - if (code === 0) { - return resolve(stdout); - } - - reject(new Error(`Command failed with code ${code}: ${stderr}`)); - }); - }); -}; - -/** - * Creates meeting information string using template - * @param {string} baseMeetingInfo - Base meeting information - * @param {string} meetingTime - ISO string of meeting time - * @param {string} invited - Invited attendees - * @param {string} observers - Observers - * @returns {string} Complete meeting information - */ -export const createMeetingInfo = ( - baseMeetingInfo, - meetingTime, - invited, - observers -) => dedent` - ${baseMeetingInfo}MEETING_TIME="${meetingTime}" - INVITEES="${invited} - - ### Observers/Guests - ${observers}" - `; - -/** - * Replaces template variables in minutes document - * @param {string} minutesDoc - Minutes document template - * @param {string} title - Meeting title - * @param {string} agendaInfo - Agenda information - * @param {string} invited - Invited attendees - * @param {string} observers - Observers - * @returns {string} Processed minutes document - */ -export const processMinutesTemplate = ( - minutesDoc, - title, - agendaInfo, - invited, - observers -) => { - // Replace all template placeholders with actual content - return minutesDoc - .replace(TEMPLATE_VARIABLES.TITLE, title) - .replace(TEMPLATE_VARIABLES.AGENDA_CONTENT, agendaInfo) - .replace(TEMPLATE_VARIABLES.INVITED, invited) - .replace(TEMPLATE_VARIABLES.OBSERVERS, observers); -}; - -/** - * Updates issue content with Google Doc link and cleans up placeholders - * @param {string} content - Original issue content - * @param {string} documentId - Google Doc ID - * @returns {string} Updated content - */ -export const updateIssueContent = (content, documentId) => - content - // Replace empty Google Doc placeholder with actual link - .replace( - ISSUE_UPDATE_REGEXES.EMPTY_GOOGLE_DOC, - `* Minutes Google Doc: ` - ) - // Remove previous minutes placeholder line - .replace(ISSUE_UPDATE_REGEXES.PREVIOUS_MINUTES, ''); diff --git a/src/utils/dates.mjs b/src/utils/dates.mjs new file mode 100644 index 0000000..05a587b --- /dev/null +++ b/src/utils/dates.mjs @@ -0,0 +1,52 @@ +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 => { + const utcOptions = { + weekday: 'short', + day: '2-digit', + month: 'short', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + hour12: true, + timeZone: 'UTC', + }; + + return { + utc: formatDateTime(meetingDate, utcOptions), + timezones: RELEVANT_TIMEZONES.map(({ label, tz }) => { + const tzOptions = { + weekday: 'short', + day: '2-digit', + month: 'short', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + hour12: true, + timeZone: tz, + }; + + return { + label, + time: formatDateTime(meetingDate, tzOptions), + }; + }), + }; +}; diff --git a/src/utils/templates.mjs b/src/utils/templates.mjs new file mode 100644 index 0000000..6ca65d1 --- /dev/null +++ b/src/utils/templates.mjs @@ -0,0 +1,46 @@ +/** + * 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; +}; + +/** + * Simple parser for template properties (KEY="value" format) + * @param {string} content - Template content + * @returns {Record} Parsed properties + */ +export const parseMeetingProperties = content => { + const properties = {}; + + // Handle multiline properties first with a generic regex + // Matches: KEY="multiline content" where content can span multiple lines + const multilineMatches = content.matchAll( + /^([A-Z_][A-Z0-9_]*)="([\s\S]*?)"$/gm + ); + + for (const match of multilineMatches) { + const [, key, value] = match; + + properties[key] = value; + } + + return properties; +}; 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/meeting_base_cross_project_council b/templates/meeting_base_cross_project_council index 87df473..257349e 100644 --- a/templates/meeting_base_cross_project_council +++ b/templates/meeting_base_cross_project_council @@ -2,7 +2,6 @@ CALENDAR_FILTER="Cross Project Council Meeting" 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" diff --git a/templates/meeting_base_ecosystem_report b/templates/meeting_base_ecosystem_report index 9f6075e..45c23fb 100644 --- a/templates/meeting_base_ecosystem_report +++ b/templates/meeting_base_ecosystem_report @@ -1,7 +1,6 @@ CALENDAR_FILTER="Ecosystem Report Collab Space" CALENDAR_ID="linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com" USER="openjs-foundation" -GITHUB_ORG="openjs-foundation" HOST="OpenJS Foundation" REPO="ecosystem-report-collab-space" GROUP_NAME="Ecosystem Report Collab Space" diff --git a/templates/meeting_base_nodejsafrica b/templates/meeting_base_nodejsafrica index b22bfdd..4f9ced5 100644 --- a/templates/meeting_base_nodejsafrica +++ b/templates/meeting_base_nodejsafrica @@ -1,7 +1,6 @@ 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" diff --git a/templates/meeting_base_package_metadata_interop b/templates/meeting_base_package_metadata_interop index 12c13d3..8c933cc 100644 --- a/templates/meeting_base_package_metadata_interop +++ b/templates/meeting_base_package_metadata_interop @@ -1,7 +1,6 @@ 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" diff --git a/templates/meeting_base_security_collab b/templates/meeting_base_security_collab index b6ea01f..9176730 100644 --- a/templates/meeting_base_security_collab +++ b/templates/meeting_base_security_collab @@ -1,7 +1,6 @@ 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" diff --git a/templates/meeting_base_standards b/templates/meeting_base_standards index 05b157e..183908a 100644 --- a/templates/meeting_base_standards +++ b/templates/meeting_base_standards @@ -1,7 +1,6 @@ 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" diff --git a/templates/meeting_base_sustainability_collab b/templates/meeting_base_sustainability_collab index 9c05896..8483139 100644 --- a/templates/meeting_base_sustainability_collab +++ b/templates/meeting_base_sustainability_collab @@ -1,7 +1,6 @@ CALENDAR_FILTER="Sustainability Collaboration Space" CALENDAR_ID="linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com" USER="openjs-foundation" -GITHUB_ORG="openjs-foundation" HOST="OpenJS Foundation" REPO="sustainability-collab-space" GROUP_NAME="Sustainability Collaboration Space" diff --git a/templates/meeting_base_tsc b/templates/meeting_base_tsc index 5cd6b31..33c6511 100644 --- a/templates/meeting_base_tsc +++ b/templates/meeting_base_tsc @@ -1,6 +1,6 @@ CALENDAR_FILTER="Node.js TSC Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" -USER="nodejs" +USER="nodejs-test-tsc" REPO="TSC" HOST="Node.js" GROUP_NAME="Technical Steering Committee (TSC)" diff --git a/templates/meeting_issue.md b/templates/meeting_issue.md new file mode 100644 index 0000000..f4d39b6 --- /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 Google Doc: <$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/test/config.test.mjs b/test/config.test.mjs new file mode 100644 index 0000000..b153dbc --- /dev/null +++ b/test/config.test.mjs @@ -0,0 +1,123 @@ +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'; + process.env.HACKMD_TEAM_NAME = 'nodejs'; + + const config = getConfig(); + + assert.strictEqual(config.hackmd.apiToken, 'hackmd_token'); + assert.strictEqual(config.hackmd.teamName, 'nodejs'); + }); + + it('should read directory config from environment', () => { + process.env.MEETINGS_CONFIG_DIR = '/custom/config'; + process.env.MEETINGS_OUTPUT_DIR = '/custom/output'; + + const config = getConfig(); + + assert.strictEqual(config.directories.config, '/custom/config'); + assert.strictEqual(config.directories.output, '/custom/output'); + }); + + it('should use default directories when not provided', () => { + const config = getConfig(); + + assert.strictEqual(config.directories.config, './'); + assert.ok(config.directories.output.includes('.make-node-meeting')); + assert.ok(config.directories.templates.includes('templates')); + }); + + 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/snapshots/README.md b/test/snapshots/README.md new file mode 100644 index 0000000..e69de29 From b84cef006449a4f6f366b2b966d3144e9af12e03 Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Fri, 15 Aug 2025 00:54:07 +0200 Subject: [PATCH 25/48] chore: code cleanup and improvements --- .env.example | 4 - README.md | 10 +- create-node-meeting-artifacts.mjs | 1 - package-lock.json | 870 +----------------------------- package.json | 3 +- src/types.d.ts | 16 - src/utils/dates.mjs | 40 +- test/config.test.mjs | 18 - test/meeting.test.mjs | 164 ++++++ test/utils/dates.test.mjs | 148 +++++ test/utils/templates.test.mjs | 266 +++++++++ test/utils/urls.test.mjs | 211 ++++++++ 12 files changed, 814 insertions(+), 937 deletions(-) create mode 100644 test/meeting.test.mjs create mode 100644 test/utils/dates.test.mjs create mode 100644 test/utils/templates.test.mjs create mode 100644 test/utils/urls.test.mjs diff --git a/.env.example b/.env.example index 2363fe6..b82a747 100644 --- a/.env.example +++ b/.env.example @@ -9,7 +9,3 @@ # 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 - -# Optional: Directory paths (defaults to current directory and home) -# MEETINGS_CONFIG_DIR=./ -# MEETINGS_OUTPUT_DIR=~/.make-node-meeting/ diff --git a/README.md b/README.md index 480664c..6f26df1 100644 --- a/README.md +++ b/README.md @@ -209,19 +209,11 @@ The application creates: - `GITHUB_TOKEN`: GitHub Personal Access Token - `HACKMD_API_TOKEN`: HackMD API token for creating and managing documents - -#### HackMD Configuration (optional) - -- `HACKMD_TEAM_NAME`: HackMD team name/path for team workspaces - -#### Google Authentication - - `GOOGLE_API_KEY`: Google Calendar API Key for read-only calendar access #### Optional -- `MEETINGS_CONFIG_DIR`: Configuration directory (default: `./`) -- `MEETINGS_OUTPUT_DIR`: Output directory (default: `~/.make-node-meeting/`) +- `HACKMD_TEAM_NAME`: HackMD team name/path for team workspaces ### Meeting Base Configuration diff --git a/create-node-meeting-artifacts.mjs b/create-node-meeting-artifacts.mjs index b59cc9f..259da8b 100644 --- a/create-node-meeting-artifacts.mjs +++ b/create-node-meeting-artifacts.mjs @@ -15,7 +15,6 @@ * Optional Environment Variables: * - HACKMD_TEAM_NAME: HackMD team name/path (optional) * - MEETINGS_CONFIG_DIR: Directory containing meeting templates (default: ./) - * - MEETINGS_OUTPUT_DIR: Directory for meeting output files (default: ~/.make-node-meeting/) * * Usage: * node create-node-meeting-artifacts.mjs [meetingGroup] diff --git a/package-lock.json b/package-lock.json index a124483..3fc3ab9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,10 +12,7 @@ "@googleapis/calendar": "^11.0.1", "@hackmd/api": "^2.5.0", "@octokit/rest": "^22.0.0", - "dedent": "^1.6.0", - "make-node-meeting": "^2.0.0", - "node-meeting-agenda": "^2.0.1", - "properties-parser": "^0.6.0" + "dedent": "^1.6.0" }, "bin": { "create-meeting": "bin/create-meeting" @@ -33,29 +30,6 @@ "node": ">=22.0.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@emnapi/core": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz", @@ -948,19 +922,11 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "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" @@ -972,26 +938,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/application-config": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/application-config/-/application-config-2.0.0.tgz", - "integrity": "sha512-NC5/0guSZK3/UgUDfCk/riByXzqz0owL1L3r63JPSBzYk5QALrp3bLxbsR7qeSfvYfFmAhnp3dbqYsW3U9MpZQ==", - "license": "MIT", - "dependencies": { - "application-config-path": "^0.1.0", - "load-json-file": "^6.2.0", - "write-json-file": "^4.2.0" - }, - "engines": { - "node": ">=8.3" - } - }, - "node_modules/application-config-path": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/application-config-path/-/application-config-path-0.1.1.tgz", - "integrity": "sha512-zy9cHePtMP0YhwG+CfHm0bgwdnga2X3gZexpdCwEj//dpb+TKajtiC8REEUJUSq6Ab4f9cgNy2l8ObXzCXFkEw==", - "license": "MIT" - }, "node_modules/are-docs-informative": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", @@ -1068,15 +1014,6 @@ "node": "*" } }, - "node_modules/bl": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.1.tgz", - "integrity": "sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ==", - "license": "MIT", - "dependencies": { - "readable-stream": "^3.0.1" - } - }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -1088,42 +1025,12 @@ "concat-map": "0.0.1" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "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", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "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/buffer-from": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz", - "integrity": "sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg==", - "license": "MIT" - }, "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", @@ -1180,43 +1087,11 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, "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" @@ -1229,6 +1104,7 @@ "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": { @@ -1260,12 +1136,6 @@ "dev": true, "license": "MIT" }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1328,18 +1198,6 @@ "dev": true, "license": "MIT" }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1349,15 +1207,6 @@ "node": ">=0.4.0" } }, - "node_modules/detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -1372,33 +1221,6 @@ "node": ">= 0.4" } }, - "node_modules/duplexer2": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", - "integrity": "sha512-+AWBwjGadtksxjOQSFDhPNQbed7icNXApT4+2BNpsXzcCBiInq2H9XW0O8sfHFaPmnQRs7cg/P0fAr2IWQSW0g==", - "license": "BSD", - "dependencies": { - "readable-stream": "~1.1.9" - } - }, - "node_modules/duplexer2/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "license": "MIT" - }, - "node_modules/duplexer2/node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -1408,15 +1230,6 @@ "safe-buffer": "^5.0.1" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.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", @@ -1985,87 +1798,6 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, - "node_modules/ghauth": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/ghauth/-/ghauth-5.0.2.tgz", - "integrity": "sha512-9mEbxQgiUw6LmjInaDrcOwZMEfFJ0kekbH3ii5MmwhsJaxXTC2RgGzn+1BewUavn1Q8RwnPKN37aATyeZf3tWQ==", - "license": "MIT", - "dependencies": { - "application-config": "^2.0.0", - "node-fetch": "^2.6.0", - "ora": "^4.0.5", - "read": "^1.0.7" - } - }, - "node_modules/ghauth/node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/ghissues": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/ghissues/-/ghissues-1.1.4.tgz", - "integrity": "sha512-YFBe75cEank9WogH1i1kRqDoH8Z+l+O9Y/CopmWCGhsOg9NR0PdMo8E+8THTwCXFAFv4NPhqLQOOPZypnDStjQ==", - "license": "MIT", - "dependencies": { - "ghutils": "^4.0.0" - } - }, - "node_modules/ghrepos": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ghrepos/-/ghrepos-2.1.0.tgz", - "integrity": "sha512-6GM0ohSDTAv7xD6GsKfxJiV/CajoofRyUwu0E8l29d1o6lFAUxmmyMP/FH33afA20ZrXzxxcTtN6TsYvudMoAg==", - "license": "MIT", - "dependencies": { - "ghutils": "~3.2.0" - } - }, - "node_modules/ghrepos/node_modules/ghutils": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/ghutils/-/ghutils-3.2.6.tgz", - "integrity": "sha512-WpYHgLQkqU7Cv147wKUEThyj6qKHCdnAG2CL9RRsRQImVdLGdVqblJ3JUnj3ToQwgm1ALPS+FXgR0448AgGPUg==", - "license": "MIT", - "dependencies": { - "jsonist": "~2.1.0", - "xtend": "~4.0.1" - } - }, - "node_modules/ghrepos/node_modules/jsonist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/jsonist/-/jsonist-2.1.2.tgz", - "integrity": "sha512-8yqmWJAC2VaYoSKQAbsfgCpGY5o/1etWzx6ZxaZrC4iGaHrHUZEo+a2MyF8w+2uTavTlHdLWaZUoR19UfBstxQ==", - "license": "MIT", - "dependencies": { - "bl": "~3.0.0", - "hyperquest": "~2.1.3", - "json-stringify-safe": "~5.0.1", - "xtend": "~4.0.1" - } - }, - "node_modules/ghutils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ghutils/-/ghutils-4.0.0.tgz", - "integrity": "sha512-WRme8qe6SX0WCN1cY9F4hE8/dhjZti2q7i7cDuT0kV7PZZrGceFRNP5ZSQM1+RjHpcqvODQi5YSZSsqG+Yk3zQ==", - "license": "MIT", - "dependencies": { - "jsonist": "~3.0.1" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2147,12 +1879,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, "node_modules/gtoken": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", @@ -2170,6 +1896,7 @@ "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" @@ -2227,37 +1954,6 @@ "node": ">= 14" } }, - "node_modules/hyperquest": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/hyperquest/-/hyperquest-2.1.3.tgz", - "integrity": "sha512-fUuDOrB47PqNK/BAMOS13v41UoaqIxqSLHX6CAbOD7OfT+/GCWO1/vPLfTNutOeXrv1ikuaZ3yux+33Z9vh+rw==", - "license": "MIT", - "dependencies": { - "buffer-from": "^0.1.1", - "duplexer2": "~0.0.2", - "through2": "~0.6.3" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "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": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2289,23 +1985,12 @@ "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/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "license": "MIT" - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -2329,30 +2014,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "license": "MIT" - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2360,12 +2021,6 @@ "dev": true, "license": "ISC" }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -2405,12 +2060,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "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", @@ -2425,34 +2074,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "license": "ISC" - }, - "node_modules/jsonist": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/jsonist/-/jsonist-3.0.1.tgz", - "integrity": "sha512-+lrAqdk5BO36j5RG7MSY2M8XqBqBwJRt/+iSfLmpelBBsi0kFflqhtlROeioDA5MlHNhZxm0Doslr7QSRzCqTQ==", - "license": "MIT", - "dependencies": { - "bl": "~4.0.0", - "hyperquest": "~2.1.3", - "json-stringify-safe": "~5.0.1" - } - }, - "node_modules/jsonist/node_modules/bl": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.4.tgz", - "integrity": "sha512-7tdr4EpSd7jJ6tuQ21vu2ke8w7pNEstzj1O8wwq6sNNzO3UDi5MA8Gny/gquCj7r2C6fHudg8tKRGyjRgmvNxQ==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, "node_modules/jwa": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", @@ -2498,27 +2119,6 @@ "node": ">= 0.8.0" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" - }, - "node_modules/load-json-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-6.2.0.tgz", - "integrity": "sha512-gUD/epcRms75Cw8RT1pUdHugZYM5ce64ucs2GEISABwkRsOQr0q2wm/MV2TKThycIe5e0ytRweW2RZxclogCdQ==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.15", - "parse-json": "^5.0.0", - "strip-bom": "^4.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2542,130 +2142,6 @@ "dev": true, "license": "MIT" }, - "node_modules/log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "license": "MIT", - "dependencies": { - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/log-symbols/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" - }, - "node_modules/log-symbols/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "license": "MIT", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/make-node-meeting": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/make-node-meeting/-/make-node-meeting-2.0.0.tgz", - "integrity": "sha512-QhC2KV4iNMF5XaNEUSSZ73nTGLsUhNEAu0MYI/C66ThJyJXrXguQ29wvl0AxFtFBMVaHgifeO8JE2yGJh+yjAw==", - "license": "MIT", - "dependencies": { - "node-meeting-agenda": "^2.0.1" - }, - "bin": { - "make-node-meeting": "make-node-meeting.sh" - } - }, - "node_modules/map-async": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/map-async/-/map-async-0.1.1.tgz", - "integrity": "sha512-nim726/DRF1yuTrx3qNJcFNqJCgiFD98eh/49EdI5LWTspX4whkVvT0hOcMEVJWCijKkp519vCY1uD05Hklc6w==" - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -2696,15 +2172,6 @@ "node": ">= 0.6" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2724,12 +2191,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "license": "ISC" - }, "node_modules/napi-postinstall": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.3.tgz", @@ -2791,21 +2252,6 @@ "url": "https://opencollective.com/node-fetch" } }, - "node_modules/node-meeting-agenda": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/node-meeting-agenda/-/node-meeting-agenda-2.0.1.tgz", - "integrity": "sha512-x6cn5sVUDVlWMlgwSH0rWVa/DW7qnN7L4mEiNiXsdqPfZY3BoXEOFPh11YM4OB/cSekT/oB4+mJghvb2TwkO7g==", - "license": "MIT", - "dependencies": { - "ghauth": "^5.0.1", - "ghissues": "~1.1.2", - "ghrepos": "2.1", - "map-async": "~0.1.1" - }, - "bin": { - "node-meeting-agenda": "node-meeting-agenda.js" - } - }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -2818,21 +2264,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -2851,41 +2282,6 @@ "node": ">= 0.8.0" } }, - "node_modules/ora": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-4.1.1.tgz", - "integrity": "sha512-sjYP8QyVWBpBZWD6Vr1M/KwknSw6kJOz41tvGMlwWeClHBtYKTbHMki1PsLZnxKpXMPbTKv9b3pjQu3REib96A==", - "license": "MIT", - "dependencies": { - "chalk": "^3.0.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.2.0", - "is-interactive": "^1.0.0", - "log-symbols": "^3.0.0", - "mute-stream": "0.0.8", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -2941,24 +2337,6 @@ "parse-statements": "1.0.11" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/parse-statements": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", @@ -2986,12 +2364,6 @@ "node": ">=8" } }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3018,15 +2390,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/properties-parser": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/properties-parser/-/properties-parser-0.6.0.tgz", - "integrity": "sha512-qvr2cSmoA0dln0MARAKwBzPkkXn7FqwX+RVVNpMdMJc7rt9mqO2cXwluxtux9fHrLhjnPFaQkS8BM0kFrTCnSw==", - "license": "MIT", - "engines": { - "node": ">= 0.3.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", @@ -3058,41 +2421,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/read": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", - "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", - "license": "ISC", - "dependencies": { - "mute-stream": "~0.0.4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readable-stream/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3113,19 +2441,6 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -3254,27 +2569,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" - }, - "node_modules/sort-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz", - "integrity": "sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg==", - "license": "MIT", - "dependencies": { - "is-plain-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/spdx-exceptions": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", @@ -3310,33 +2604,6 @@ "node": ">=12.0.0" } }, - "node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "license": "MIT" - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3354,6 +2621,7 @@ "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" @@ -3362,40 +2630,6 @@ "node": ">=8" } }, - "node_modules/through2": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.6.5.tgz", - "integrity": "sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg==", - "license": "MIT", - "dependencies": { - "readable-stream": ">=1.0.33-1 <1.1.0-0", - "xtend": ">=4.0.0 <4.1.0-0" - } - }, - "node_modules/through2/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "license": "MIT" - }, - "node_modules/through2/node_modules/readable-stream": { - "version": "1.0.34", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", - "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -3415,24 +2649,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "license": "MIT", - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, "node_modules/undici-types": { "version": "7.10.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", @@ -3497,21 +2713,6 @@ "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", "license": "BSD" }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", @@ -3521,22 +2722,6 @@ "node": ">= 8" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3563,47 +2748,6 @@ "node": ">=0.10.0" } }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/write-json-file": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/write-json-file/-/write-json-file-4.3.0.tgz", - "integrity": "sha512-PxiShnxf0IlnQuMYOPPhPkhExoCQuTUNPOa/2JWCYTmBquU9njyyDuwRKN26IZBlp4yn1nt+Agh2HOOBl+55HQ==", - "license": "MIT", - "dependencies": { - "detect-indent": "^6.0.0", - "graceful-fs": "^4.1.15", - "is-plain-obj": "^2.0.0", - "make-dir": "^3.0.0", - "sort-keys": "^4.0.0", - "write-file-atomic": "^3.0.0" - }, - "engines": { - "node": ">=8.3" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 8910535..2e244ae 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,7 @@ "@googleapis/calendar": "^11.0.1", "@hackmd/api": "^2.5.0", "@octokit/rest": "^22.0.0", - "dedent": "^1.6.0", - "properties-parser": "^0.6.0" + "dedent": "^1.6.0" }, "devDependencies": { "@eslint/js": "^9.33.0", diff --git a/src/types.d.ts b/src/types.d.ts index b5fbe01..3efcc46 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,7 +1,3 @@ -/** - * TypeScript definitions for the Node.js Meeting Artifacts Creator - */ - /** * Application configuration object */ @@ -40,10 +36,6 @@ export interface HackMDConfig { * Directory paths configuration */ export interface DirectoryConfig { - /** Configuration directory path */ - config: string; - /** Output directory for meeting files */ - output: string; /** Templates directory path */ templates: string; } @@ -84,12 +76,4 @@ export interface MeetingProperties { ISSUE_LABEL?: string; /** Meeting joining instructions */ JOINING_INSTRUCTIONS?: string; - /** Meeting location (deprecated) */ - LOCATION?: string; - /** Meeting time (deprecated) */ - TIME?: string; - /** Meeting day (deprecated) */ - DAY?: string; - /** Meeting frequency (deprecated) */ - FREQUENCY?: string; } diff --git a/src/utils/dates.mjs b/src/utils/dates.mjs index 05a587b..1ee01bb 100644 --- a/src/utils/dates.mjs +++ b/src/utils/dates.mjs @@ -17,8 +17,8 @@ export const formatDateTime = (date, options = {}) => { * @param {Date} meetingDate - The meeting date * @returns {Object} Object with formatted times for different timezones */ -export const formatTimezones = meetingDate => { - const utcOptions = { +export const formatTimezones = meetingDate => ({ + utc: formatDateTime(meetingDate, { weekday: 'short', day: '2-digit', month: 'short', @@ -27,26 +27,18 @@ export const formatTimezones = meetingDate => { minute: '2-digit', hour12: true, timeZone: 'UTC', - }; - - return { - utc: formatDateTime(meetingDate, utcOptions), - timezones: RELEVANT_TIMEZONES.map(({ label, tz }) => { - const tzOptions = { - weekday: 'short', - day: '2-digit', - month: 'short', - year: 'numeric', - hour: '2-digit', - minute: '2-digit', - hour12: true, - timeZone: tz, - }; - - return { - label, - time: formatDateTime(meetingDate, tzOptions), - }; + }), + 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/test/config.test.mjs b/test/config.test.mjs index b153dbc..c8d97a1 100644 --- a/test/config.test.mjs +++ b/test/config.test.mjs @@ -71,24 +71,6 @@ describe('Config', () => { assert.strictEqual(config.hackmd.teamName, 'nodejs'); }); - it('should read directory config from environment', () => { - process.env.MEETINGS_CONFIG_DIR = '/custom/config'; - process.env.MEETINGS_OUTPUT_DIR = '/custom/output'; - - const config = getConfig(); - - assert.strictEqual(config.directories.config, '/custom/config'); - assert.strictEqual(config.directories.output, '/custom/output'); - }); - - it('should use default directories when not provided', () => { - const config = getConfig(); - - assert.strictEqual(config.directories.config, './'); - assert.ok(config.directories.output.includes('.make-node-meeting')); - assert.ok(config.directories.templates.includes('templates')); - }); - it('should handle undefined environment variables gracefully', () => { // Clear all relevant env vars delete process.env.GITHUB_TOKEN; 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/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..6346576 --- /dev/null +++ b/test/utils/templates.test.mjs @@ -0,0 +1,266 @@ +import assert from 'node:assert'; +import { describe, it } from 'node:test'; + +import { + parseVariables, + parseMeetingProperties, +} 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` + ); + }); + }); + + describe('parseMeetingProperties', () => { + it('should parse simple property', () => { + const content = 'NAME="John Doe"'; + + const result = parseMeetingProperties(content); + + assert.deepStrictEqual(result, { NAME: 'John Doe' }); + }); + + it('should parse multiple properties', () => { + const content = `NAME="John Doe" +EMAIL="john@example.com" +ROLE="Developer"`; + + const result = parseMeetingProperties(content); + + assert.deepStrictEqual(result, { + NAME: 'John Doe', + EMAIL: 'john@example.com', + ROLE: 'Developer', + }); + }); + + it('should parse multiline property values', () => { + const content = `DESCRIPTION="This is a +multiline description +with several lines"`; + + const result = parseMeetingProperties(content); + + assert.deepStrictEqual(result, { + DESCRIPTION: 'This is a\nmultiline description\nwith several lines', + }); + }); + + it('should parse properties with underscores and numbers', () => { + const content = `VAR_1="value1" +VAR_2_TEST="value2" +VAR3="value3"`; + + const result = parseMeetingProperties(content); + + assert.deepStrictEqual(result, { + VAR_1: 'value1', + VAR_2_TEST: 'value2', + VAR3: 'value3', + }); + }); + + it('should handle empty property values', () => { + const content = 'EMPTY=""'; + + const result = parseMeetingProperties(content); + + assert.deepStrictEqual(result, { EMPTY: '' }); + }); + + it('should handle properties with special characters in values', () => { + const content = 'SPECIAL="Value with $pecial ch@rs & symbols!"'; + + const result = parseMeetingProperties(content); + + assert.deepStrictEqual(result, { + SPECIAL: 'Value with $pecial ch@rs & symbols!', + }); + }); + + it('should handle properties with quotes in values', () => { + const content = `QUOTED="He said \\"Hello\\" to me"`; + + const result = parseMeetingProperties(content); + + assert.deepStrictEqual(result, { + QUOTED: 'He said \\"Hello\\" to me', + }); + }); + + it('should handle content with no properties', () => { + const content = 'Just some text without properties'; + + const result = parseMeetingProperties(content); + + assert.deepStrictEqual(result, {}); + }); + + it('should handle empty content', () => { + const content = ''; + + const result = parseMeetingProperties(content); + + assert.deepStrictEqual(result, {}); + }); + + it('should handle mixed content with properties and other text', () => { + const content = `Some random text +NAME="John Doe" +More text here +EMAIL="john@example.com" +Final text`; + + const result = parseMeetingProperties(content); + + assert.deepStrictEqual(result, { + NAME: 'John Doe', + EMAIL: 'john@example.com', + }); + }); + + it('should handle properties with markdown-like content', () => { + const content = `DESCRIPTION="# Meeting Notes + +## Agenda +- Item 1 +- Item 2 + +**Important**: Don't forget!"`; + + const result = parseMeetingProperties(content); + + assert.deepStrictEqual(result, { + DESCRIPTION: `# Meeting Notes + +## Agenda +- Item 1 +- Item 2 + +**Important**: Don't forget!`, + }); + }); + + it('should handle properties with URLs and special formatting', () => { + const content = `LINK="https://example.com/path?param=value&other=123" +INSTRUCTIONS="Join at: https://zoom.us/j/123456789 +Passcode: 123456"`; + + const result = parseMeetingProperties(content); + + assert.deepStrictEqual(result, { + LINK: 'https://example.com/path?param=value&other=123', + INSTRUCTIONS: `Join at: https://zoom.us/j/123456789 +Passcode: 123456`, + }); + }); + }); +}); 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')); + }); + }); +}); From 156d5a08de4b988fb6806aee5126fff20ae45d5d Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Fri, 15 Aug 2025 00:56:38 +0200 Subject: [PATCH 26/48] chore: revert file change --- templates/meeting_base_tsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/meeting_base_tsc b/templates/meeting_base_tsc index 33c6511..5cd6b31 100644 --- a/templates/meeting_base_tsc +++ b/templates/meeting_base_tsc @@ -1,6 +1,6 @@ CALENDAR_FILTER="Node.js TSC Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" -USER="nodejs-test-tsc" +USER="nodejs" REPO="TSC" HOST="Node.js" GROUP_NAME="Technical Steering Committee (TSC)" From 21c841265f34d9294b28fbb634f7f734c97d9580 Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Fri, 15 Aug 2025 02:54:11 +0200 Subject: [PATCH 27/48] chore: code review --- create-node-meeting-artifacts.mjs | 84 +++++++++++++++--------------- src/constants.mjs | 9 +++- src/github.mjs | 85 ++++++++++++++----------------- src/google.mjs | 13 +---- src/hackmd.mjs | 24 +++------ src/meeting.mjs | 72 ++++++++++++++++---------- templates/meeting_base_tsc | 2 +- 7 files changed, 146 insertions(+), 143 deletions(-) diff --git a/create-node-meeting-artifacts.mjs b/create-node-meeting-artifacts.mjs index 259da8b..6466fd0 100644 --- a/create-node-meeting-artifacts.mjs +++ b/create-node-meeting-artifacts.mjs @@ -3,19 +3,6 @@ /** * Node.js Meeting Artifacts Creator * - * Creates GitHub issues and HackMD documents for Node.js team meetings. - * Reads meeting configuration from templates, fetches calendar events, - * creates meeting minutes documents, and posts GitHub issues. - * - * Environment Variables Required: - * - GITHUB_TOKEN: Personal access token for GitHub API - * - HACKMD_API_TOKEN: HackMD API token for creating documents - * - GOOGLE_API_KEY: Google Calendar API key for read-only calendar access - * - * Optional Environment Variables: - * - HACKMD_TEAM_NAME: HackMD team name/path (optional) - * - MEETINGS_CONFIG_DIR: Directory containing meeting templates (default: ./) - * * Usage: * node create-node-meeting-artifacts.mjs [meetingGroup] * npm run tsc-meeting @@ -32,65 +19,82 @@ import * as meetings from './src/meeting.mjs'; const config = getConfig(); // Step 2: Initialize Google Calendar client with API Key -const calendarClient = google.createGoogleCalendarClient(config.google); +const calendarClient = google.createCalendarClient(config.google); -// Step 3: Initialize HackMD client -const hackmdClient = hackmd.createHackMDClientInstance( - config.hackmd.apiToken, - config.hackmd.teamName -); +// Step 3: Initialize GitHub client +const githubClient = github.createGitHubClient(config); + +// Step 4: Initialize HackMD client +const hackmdClient = hackmd.createHackMDClient(config); -// Step 4: Read meeting configuration from templates +// Step 5: Read meeting configuration from templates const meetingConfig = await meetings.readMeetingConfig(config); -// Step 5: Find next meeting event in calendar +// Step 6: Find next meeting event in calendar const event = await google.findNextMeetingEvent(calendarClient, meetingConfig); -// Step 6: Extract meeting date from event +// Step 7: Extract meeting date from event const meetingDate = new Date(event.start.dateTime); -// Step 7: Get Meeting Title +// Step 8: Get Meeting Title const meetingTitle = meetings.generateMeetingTitle( config, meetingConfig, meetingDate ); -// Step 8: Generate meeting issue content using native implementation -const issueContent = await meetings.generateMeetingIssue( +// Step 9: Get agenda information using native implementation +const gitHubAgendaIssues = await github.getAgendaIssues( + githubClient, config, - meetingConfig, - meetingDate + meetingConfig ); -// Step 9: Create GitHub issue with HackMD link -const githubIssue = await github.createGitHubIssue( - config, - meetingConfig, - meetingTitle, - issueContent +// Step 10: Parse meeting agenda from GitHub issues +const meetingAgenda = meetings.generateMeetingAgenda( + gitHubAgendaIssues, + meetingConfig ); -// Step 10: Create HackMD document with meeting notes +// Step 11: Create HackMD document with meeting notes const hackmdNote = await hackmd.createMeetingNotesDocument( hackmdClient, meetingTitle, '' ); -// Step 11: Get the HackMD document link -const noteLink = hackmdNote.publishLink || `https://hackmd.io/${hackmdNote.id}`; +// 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 12: Update the minutes content with the HackMD link +// Step 15: Update the minutes content with the HackMD link const minutesContent = await meetings.generateMeetingMinutes( config, meetingConfig, meetingTitle, - noteLink, + meetingAgenda, + minutesDocLink, githubIssue.html_url ); -// Step 13: Update the HackMD document with the self-referencing link +// Step 16: Update the HackMD document with the self-referencing link await hackmd.updateMeetingNotesDocument( hackmdClient, hackmdNote.id, @@ -99,4 +103,4 @@ await hackmd.updateMeetingNotesDocument( // Output success information with links console.log(`Created GitHub issue: ${githubIssue.html_url}`); -console.log(`Created HackMD document: ${noteLink}`); +console.log(`Created HackMD document: ${minutesDocLink}`); diff --git a/src/constants.mjs b/src/constants.mjs index 1dde19e..897a892 100644 --- a/src/constants.mjs +++ b/src/constants.mjs @@ -3,7 +3,7 @@ export const DEFAULT_CONFIG = { // Default GitHub organization name githubOrg: 'nodejs', // Default Host of the Meeting - defaultHost: 'Node.js', + host: 'Node.js', }; // Time constants for date calculations @@ -27,3 +27,10 @@ export const RELEVANT_TIMEZONES = [ { 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 index 61bfc52..e1bb747 100644 --- a/src/github.mjs +++ b/src/github.mjs @@ -1,34 +1,42 @@ import { Octokit } from '@octokit/rest'; +import { DEFAULT_CONFIG } from './constants.mjs'; + /** - * Creates GitHub issue with meeting information and Google Doc link + * 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 ( - config, - meetingConfig, + { rest }, + { properties }, title, content ) => { - // Initialize GitHub API client with authentication token - const octokit = new Octokit({ auth: config.githubToken }); + const githubOrg = properties.USER ?? DEFAULT_CONFIG.githubOrg; - // Extract issue label from config, removing quotes if present - const issueLabel = meetingConfig.properties.ISSUE_LABEL; + const issueLabel = properties.ISSUE_LABEL + ? [properties.ISSUE_LABEL] + : undefined; // Create the GitHub issue with meeting information - const response = await octokit.rest.issues.create({ - // Repository information from meeting config - owner: meetingConfig.properties.USER, - repo: meetingConfig.properties.REPO, + const response = await rest.issues.create({ + owner: githubOrg, + repo: properties.REPO, title, body: content, - // Add label if specified in config - labels: issueLabel ? [issueLabel] : undefined, + labels: issueLabel, }); return response.data; @@ -36,55 +44,40 @@ export const createGitHubIssue = async ( /** * Fetches GitHub issues from all repositories in an organization with a specific label - * @param {string} token - GitHub personal access token - * @param {string} org - GitHub organization name (e.g., 'nodejs') - * @param {string} label - Label to filter by (e.g., 'tsc-agenda') - * @returns {Promise} Formatted markdown string of issues + * @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<{ repoName: string, issues: Array }> } Formatted markdown string of issues */ -export const fetchAgendaIssues = async (token, org, label) => { - const octokit = new Octokit({ auth: token }); +export const getAgendaIssues = async ( + { paginate, rest }, + { meetingGroup }, + { properties } +) => { + const githubOrg = properties.USER ?? DEFAULT_CONFIG.githubOrg; + const agendaTag = properties.AGENDA_TAG ?? `${meetingGroup}-agenda`; // Get all public repositories in the organization - const repos = await octokit.paginate(octokit.rest.repos.listForOrg, { - org, + const repos = await paginate(rest.repos.listForOrg, { + org: properties.USER, type: 'public', per_page: 100, }); // Fetch issues from all repositories concurrently const issuePromises = repos.map(async repo => { - const issues = await octokit.paginate(octokit.rest.issues.listForRepo, { - owner: org, + const issues = await paginate(rest.issues.listForRepo, { + owner: githubOrg, repo: repo.name, - labels: label, + labels: agendaTag, state: 'open', per_page: 100, }); - const filteredIssues = issues.filter(issue => !issue.pull_request); // Exclude PRs + const filteredIssues = issues.filter(({ pull_request }) => !pull_request); // Exclude PRs return { repoName: repo.name, issues: filteredIssues }; }); - const repoIssues = await Promise.all(issuePromises); - - // Format issues as markdown - let agendaMarkdown = ''; - - repoIssues.forEach(({ repoName, issues }) => { - if (issues.length > 0) { - agendaMarkdown += `### ${org}/${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(); + return Promise.all(issuePromises); }; diff --git a/src/google.mjs b/src/google.mjs index f47edf4..52af36e 100644 --- a/src/google.mjs +++ b/src/google.mjs @@ -7,17 +7,8 @@ import { TIME_CONSTANTS } from './constants.mjs'; * @param {import('./types.d.ts').GoogleConfig} gConfig - Google configuration object * @returns {CalendarClient} Authenticated Google Calendar client */ -export const createGoogleCalendarClient = gConfig => { - // Use API Key authentication (simpler and more straightforward) - if (gConfig.apiKey) { - return calendar({ - version: 'v3', - auth: gConfig.apiKey, - }); - } - - throw new Error('Google API Key is required for Google Calendar access'); -}; +export const createCalendarClient = ({ apiKey: auth }) => + calendar({ version: 'v3', auth }); /** * Finds the next meeting event in Google Calendar within the next week diff --git a/src/hackmd.mjs b/src/hackmd.mjs index 6d7034a..90b16aa 100644 --- a/src/hackmd.mjs +++ b/src/hackmd.mjs @@ -1,24 +1,16 @@ import HackMDAPI from '@hackmd/api'; -/** - * Creates the default permissions for our generated docs - * @type {Pick} */ -const hackMdPermissions = { - readPermission: 'guest', // Allow signed-in users to read - writePermission: 'signed_in', // Allow signed-in users to write - commentPermission: 'signed_in_users', // Allow signed-in users to comment -}; +import { HACKMD_DEFAULT_PERMISSIONS } from './constants.mjs'; /** * Creates a HackMD API client - * @param {string} apiToken - HackMD API token - * @param {string} teamPath - HackMD team path/name (optional) + * @param {import('./types.d.ts').AppConfig} config - Application configuration * @returns {HackMDClient} Configured HackMD API client */ -export const createHackMDClientInstance = (apiToken, teamPath) => { +export const createHackMDClient = ({ hackmd: { apiToken, teamName } }) => { // Use team-specific API endpoint if teamPath is provided - const baseURL = teamPath - ? `https://api.hackmd.io/v1/teams/${teamPath}` + const baseURL = teamName + ? `https://api.hackmd.io/v1/teams/${teamName}` : 'https://api.hackmd.io/v1'; return new HackMDAPI(apiToken, baseURL); @@ -31,10 +23,8 @@ export const createHackMDClientInstance = (apiToken, teamPath) => { * @param {string} content - Document content in Markdown * @returns {Promise} Created note data with ID and URLs */ -export const createMeetingNotesDocument = (hackmdClient, title, content) => { - // Create a new note with the meeting content - return hackmdClient.createNote({ title, content, ...hackMdPermissions }); -}; +export const createMeetingNotesDocument = (hackmdClient, title, content) => + hackmdClient.createNote({ title, content, ...HACKMD_DEFAULT_PERMISSIONS }); /** * Updates an existing meeting notes document in HackMD diff --git a/src/meeting.mjs b/src/meeting.mjs index 6eeb47f..60a401f 100644 --- a/src/meeting.mjs +++ b/src/meeting.mjs @@ -2,7 +2,6 @@ import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; import { DEFAULT_CONFIG } from './constants.mjs'; -import * as github from './github.mjs'; import * as dates from './utils/dates.mjs'; import * as templates from './utils/templates.mjs'; import * as urls from './utils/urls.mjs'; @@ -47,7 +46,7 @@ export const readMeetingConfig = async config => { export const generateMeetingTitle = (config, meetingConfig, meetingDate) => { const props = meetingConfig.properties; - const host = props.HOST ?? DEFAULT_CONFIG.defaultHost; + const host = props.HOST ?? DEFAULT_CONFIG.host; const groupName = props.GROUP_NAME ?? config.meetingGroup; const utcShort = meetingDate.toISOString().split('T')[0]; @@ -55,16 +54,52 @@ export const generateMeetingTitle = (config, meetingConfig, meetingDate) => { return `${host} ${groupName} Meeting ${utcShort}`; }; +/** + * Generates the meeting agenda from the list of agenda issues + * @param {Array<{ repoName: string, issues: Array }>} agendaIssues - List of agenda issues + * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration + * @returns {Promise} Formatted meeting agenda + */ +export const generateMeetingAgenda = (agendaIssues, meetingConfig) => { + const props = meetingConfig.properties; + + const githubOrg = props.USER ?? DEFAULT_CONFIG.githubOrg; + + // Format issues as markdown + let agendaMarkdown = ''; + + agendaIssues.forEach(({ repoName, issues }) => { + if (issues.length > 0) { + agendaMarkdown += `### ${githubOrg}/${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 + meetingDate, + meetingAgenda, + minutesDocLink ) => { const props = meetingConfig.properties; @@ -81,13 +116,6 @@ export const generateMeetingIssue = async ( const timeAndDateLink = urls.generateTimeAndDateLink(meetingDate, groupName); const wolframLink = urls.generateWolframAlphaLink(meetingDate); - // Fetch agenda issues from GitHub - const agendaContent = await github.fetchAgendaIssues( - config.githubToken, - githubOrg, - agendaTag - ); - // Generate timezone table const timezoneTable = timezones .map(({ label, time }) => `| ${label.padEnd(13)} | ${time} |`) @@ -105,9 +133,10 @@ export const generateMeetingIssue = async ( WOLFRAM_ALPHA_LINK: wolframLink, AGENDA_LABEL: agendaTag, GITHUB_ORG: githubOrg, - AGENDA_CONTENT: agendaContent ?? '*No agenda items found.*', + AGENDA_CONTENT: meetingAgenda ?? '*No agenda items found.*', INVITEES: meetingConfig.invited, JOINING_INSTRUCTIONS: joiningInstructions, + MINUTES_DOC: minutesDocLink, OBSERVERS: meetingConfig.observers ?? '', }; @@ -119,6 +148,7 @@ export const generateMeetingIssue = async ( * @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 @@ -127,22 +157,10 @@ export const generateMeetingMinutes = async ( config, meetingConfig, meetingTitle, - minutesDocLink = '$MINUTES_DOC$', - githubIssueLink = '$GITHUB_ISSUE$' + meetingAgenda, + minutesDocLink, + githubIssueLink ) => { - const props = meetingConfig.properties; - - const githubOrg = props.USER ?? DEFAULT_CONFIG.githubOrg; - - const agendaTag = props.AGENDA_TAG ?? `${config.meetingGroup}-agenda`; - - // Get agenda information using native implementation - const agendaInfo = await github.fetchAgendaIssues( - config.githubToken, - githubOrg, - agendaTag - ); - // Read and process the meeting minutes template const templatePath = join( config.directories.templates, @@ -153,7 +171,7 @@ export const generateMeetingMinutes = async ( const templateVariables = { TITLE: meetingTitle, - AGENDA_CONTENT: agendaInfo, + AGENDA_CONTENT: meetingAgenda, INVITED: meetingConfig.invited, OBSERVERS: meetingConfig.observers, MINUTES_DOC: minutesDocLink, diff --git a/templates/meeting_base_tsc b/templates/meeting_base_tsc index 5cd6b31..33c6511 100644 --- a/templates/meeting_base_tsc +++ b/templates/meeting_base_tsc @@ -1,6 +1,6 @@ CALENDAR_FILTER="Node.js TSC Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" -USER="nodejs" +USER="nodejs-test-tsc" REPO="TSC" HOST="Node.js" GROUP_NAME="Technical Steering Committee (TSC)" From 4f12fffc3452bb108c0dafa57bfeecc4e69ad648 Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Wed, 17 Sep 2025 22:09:25 +0200 Subject: [PATCH 28/48] Update package.json Co-authored-by: Aviv Keller --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2e244ae..722cbc9 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "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" From 419736147281f0120bf016a1c7102db866ffe0fa Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Wed, 17 Sep 2025 22:09:40 +0200 Subject: [PATCH 29/48] Update package.json Co-authored-by: Aviv Keller --- package.json | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 722cbc9..aad53b2 100644 --- a/package.json +++ b/package.json @@ -35,45 +35,45 @@ "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": "node create-node-meeting-artifacts.mjs uvwasi", + "uvwasi-meeting": "create-meeting uvwasi", "uvwasi-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs uvwasi", - "tsc-meeting": "node create-node-meeting-artifacts.mjs tsc", + "tsc-meeting": "create-meeting tsc", "tsc-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs tsc", - "build-meeting": "node create-node-meeting-artifacts.mjs build", + "build-meeting": "create-meeting build", "build-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs build", - "diag-meeting": "node create-node-meeting-artifacts.mjs diag", + "diag-meeting": "create-meeting diag", "diag-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs diag", - "diag-deepdive-meeting": "node create-node-meeting-artifacts.mjs diag_deepdive", + "diag-deepdive-meeting": "create-meeting diag_deepdive", "diag-deepdive-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs diag_deepdive", - "typescript-meeting": "node create-node-meeting-artifacts.mjs typescript", + "typescript-meeting": "create-meeting typescript", "typescript-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs typescript", - "release-meeting": "node create-node-meeting-artifacts.mjs Release", + "release-meeting": "create-meeting Release", "release-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs Release", - "cross-project-council-meeting": "node create-node-meeting-artifacts.mjs cross_project_council", + "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": "node create-node-meeting-artifacts.mjs modules", + "modules-meeting": "create-meeting modules", "modules-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs modules", - "tooling-meeting": "node create-node-meeting-artifacts.mjs tooling", + "tooling-meeting": "create-meeting tooling", "tooling-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs tooling", - "security-wg-meeting": "node create-node-meeting-artifacts.mjs security-wg", + "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": "node create-node-meeting-artifacts.mjs next-10", + "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": "node create-node-meeting-artifacts.mjs package-maintenance", + "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": "node create-node-meeting-artifacts.mjs package_metadata_interop", + "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": "node create-node-meeting-artifacts.mjs ecosystem_report", + "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": "node create-node-meeting-artifacts.mjs sustainability_collab", + "sustainability-collab-meeting": "create-meeting sustainability_collab", "sustainability-collab-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs sustainability_collab", - "standards-meeting": "node create-node-meeting-artifacts.mjs standards", + "standards-meeting": "create-meeting standards", "standards-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs standards", - "security-collab-meeting": "node create-node-meeting-artifacts.mjs security_collab", + "security-collab-meeting": "create-meeting security_collab", "security-collab-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs security_collab", - "loaders-meeting": "node create-node-meeting-artifacts.mjs loaders", + "loaders-meeting": "create-meeting loaders", "loaders-meeting:dev": "node --env-file=.env create-node-meeting-artifacts.mjs loaders", - "web-server-frameworks-meeting": "node create-node-meeting-artifacts.mjs web-server-frameworks", + "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": "", From 805b91f712cccae4c27f6a0029358b6ccff04b9b Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Wed, 17 Sep 2025 22:23:11 +0200 Subject: [PATCH 30/48] chore: some refactor --- .../workflows/create-meeting-artifacts.yml | 8 ++++ bin/create-meeting | 43 ------------------- 2 files changed, 8 insertions(+), 43 deletions(-) delete mode 100755 bin/create-meeting diff --git a/.github/workflows/create-meeting-artifacts.yml b/.github/workflows/create-meeting-artifacts.yml index d65cecb..ec8e07e 100644 --- a/.github/workflows/create-meeting-artifacts.yml +++ b/.github/workflows/create-meeting-artifacts.yml @@ -51,10 +51,18 @@ jobs: - name: Install dependencies run: npm ci + - 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}} + - name: Create meeting artifacts env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} HACKMD_API_TOKEN: ${{ secrets.HACKMD_API_TOKEN }} + # TODO: This should be part of the meeting artifact as different meetings + # might use different HackMD teams. HACKMD_TEAM_NAME: ${{ secrets.HACKMD_TEAM_NAME }} GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} run: node create-node-meeting-artifacts.mjs ${{ github.event.inputs.meeting_group }} diff --git a/bin/create-meeting b/bin/create-meeting deleted file mode 100755 index e2cea8b..0000000 --- a/bin/create-meeting +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env node - -/** - * CLI wrapper for create-node-meeting-artifacts - * - * This script provides a convenient way to run the meeting artifacts creator - * with the specified group name. - * - * Usage: - * create-meeting - * create-meeting tsc - * create-meeting benchmarking - */ - -import { spawn } from 'node:child_process'; - -const group = process.argv[2]; - -if (group === undefined) { - console.error('Please pass in group as an argument'); - console.error('Usage: create-meeting '); - console.error('Example: create-meeting tsc'); - process.exit(1); -} - -const scriptName = group.toLowerCase() + '-meeting'; - -console.log(`Running meeting artifacts creation for group: ${group}`); - -// Spawn the npm script with proper stdio inheritance -const child = spawn('npm', ['run', scriptName], { - stdio: 'inherit', - shell: true, -}); - -child.on('close', code => { - process.exit(code); -}); - -child.on('error', error => { - console.error(`Error running script: ${error.message}`); - process.exit(1); -}); From e38f004fe6a1fca726b573420baa0ba0fcb16257 Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Wed, 17 Sep 2025 22:27:28 +0200 Subject: [PATCH 31/48] fikx: org fix --- templates/meeting_base_tsc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/meeting_base_tsc b/templates/meeting_base_tsc index 33c6511..5cd6b31 100644 --- a/templates/meeting_base_tsc +++ b/templates/meeting_base_tsc @@ -1,6 +1,6 @@ CALENDAR_FILTER="Node.js TSC Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" -USER="nodejs-test-tsc" +USER="nodejs" REPO="TSC" HOST="Node.js" GROUP_NAME="Technical Steering Committee (TSC)" From 26dc685709901c31ccfff85ad2c7fd6953ab2131 Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Wed, 17 Sep 2025 22:45:06 +0200 Subject: [PATCH 32/48] fix: fixed formatting and meeting files --- ...ml => create-meeting-artifacts-manual.yml} | 59 +++++++------ .../create-meeting-artifacts-scheduled.yml | 82 +++++++++++++++++++ templates/invited_commcomm | 1 - templates/invited_michael | 1 - templates/invited_nodejsafrica | 2 - templates/invited_nodejsafrica_leadership | 1 - templates/invited_nodejsafrica_members | 1 - templates/meeting_base_Release | 1 + templates/meeting_base_benchmarking | 1 + templates/meeting_base_build | 1 + templates/meeting_base_commcomm | 22 ----- templates/meeting_base_cross_project_council | 1 + templates/meeting_base_diag | 1 + templates/meeting_base_diag_deepdive | 1 + templates/meeting_base_ecosystem_report | 1 + templates/meeting_base_loaders | 1 + templates/meeting_base_michael | 15 ---- templates/meeting_base_modules | 1 + templates/meeting_base_next-10 | 1 + templates/meeting_base_nodejsafrica | 16 ---- templates/meeting_base_outreach | 1 + templates/meeting_base_package-maintenance | 1 + .../meeting_base_package_metadata_interop | 1 + templates/meeting_base_security-wg | 1 + templates/meeting_base_security_collab | 1 + templates/meeting_base_standards | 1 + templates/meeting_base_sustainability_collab | 1 + templates/meeting_base_tooling | 1 + templates/meeting_base_tsc | 1 + templates/meeting_base_typescript | 1 + templates/meeting_base_userfeedback | 1 + templates/meeting_base_uvwasi | 1 + templates/meeting_base_web-server-frameworks | 1 + templates/minutes_base_commcomm | 35 -------- templates/minutes_base_michael | 28 ------- templates/minutes_base_nodejsafrica | 28 ------- templates/observers_commcomm | 3 - templates/observers_michael | 0 templates/observers_nodejsafrica | 0 39 files changed, 137 insertions(+), 180 deletions(-) rename .github/workflows/{create-meeting-artifacts.yml => create-meeting-artifacts-manual.yml} (60%) create mode 100644 .github/workflows/create-meeting-artifacts-scheduled.yml delete mode 100644 templates/invited_commcomm delete mode 100644 templates/invited_michael delete mode 100644 templates/invited_nodejsafrica delete mode 100644 templates/invited_nodejsafrica_leadership delete mode 100644 templates/invited_nodejsafrica_members delete mode 100644 templates/meeting_base_commcomm delete mode 100644 templates/meeting_base_michael delete mode 100644 templates/meeting_base_nodejsafrica delete mode 100644 templates/minutes_base_commcomm delete mode 100644 templates/minutes_base_michael delete mode 100644 templates/minutes_base_nodejsafrica delete mode 100644 templates/observers_commcomm delete mode 100644 templates/observers_michael delete mode 100644 templates/observers_nodejsafrica diff --git a/.github/workflows/create-meeting-artifacts.yml b/.github/workflows/create-meeting-artifacts-manual.yml similarity index 60% rename from .github/workflows/create-meeting-artifacts.yml rename to .github/workflows/create-meeting-artifacts-manual.yml index ec8e07e..42c211c 100644 --- a/.github/workflows/create-meeting-artifacts.yml +++ b/.github/workflows/create-meeting-artifacts-manual.yml @@ -1,11 +1,6 @@ -name: Create Meeting Artifacts +name: Create Meeting Artifacts (Manual) on: - # Run every Monday at 10 AM UTC (adjust as needed) - schedule: - - cron: '0 10 * * 1' - - # Allow manual triggering workflow_dispatch: inputs: meeting_group: @@ -13,37 +8,39 @@ on: required: true type: choice options: - - uvwasi - - tsc + - benchmarking - build + - cross_project_council - diag - diag_deepdive - - typescript - - Release - - cross_project_council + - ecosystem_report + - loaders - modules - - tooling - - security-wg - next-10 + - outreach - package-maintenance - package_metadata_interop - - ecosystem_report - - sustainability_collab - - standards + - Release + - security-wg - security_collab - - loaders + - 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 + uses: actions/checkout@v4 - name: Setup Node.js - uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 + uses: actions/setup-node@v4 with: node-version-file: '.nvmrc' cache: 'npm' @@ -51,25 +48,33 @@ jobs: - name: Install dependencies run: npm ci - - uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 + - name: Read USER field from meeting_base file + id: read-vars + run: | + meeting_group="${{ github.event.inputs.meeting_group }}" + user=$(grep '^USER=' "templates/meeting_base_${meeting_group}" | cut -d'=' -f2 | xargs) + hackmd_team_name=$(grep '^HACKMD_TEAM_NAME=' "templates/meeting_base_${meeting_group}" | cut -d'=' -f2 | xargs) + echo "user=$user" >> $GITHUB_OUTPUT + echo "hackmd_team_name=$hackmd_team_name" >> $GITHUB_OUTPUT + + - uses: actions/create-github-app-token@v2 id: app-token with: app-id: ${{ secrets.BOT_ID }} - private-key: ${{ secrets.BOT_PRIVATE_KEY}} + private-key: ${{ secrets.BOT_PRIVATE_KEY }} + owner: ${{ steps.read-vars.outputs.user }} - name: Create meeting artifacts env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} HACKMD_API_TOKEN: ${{ secrets.HACKMD_API_TOKEN }} - # TODO: This should be part of the meeting artifact as different meetings - # might use different HackMD teams. - HACKMD_TEAM_NAME: ${{ secrets.HACKMD_TEAM_NAME }} + HACKMD_TEAM_NAME: ${{ steps.read-vars.outputs.hackmd_team_name }} 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 + uses: actions/upload-artifact@v4 with: name: meeting-artifacts-${{ github.event.inputs.meeting_group || 'tsc' }} path: | diff --git a/.github/workflows/create-meeting-artifacts-scheduled.yml b/.github/workflows/create-meeting-artifacts-scheduled.yml new file mode 100644 index 0000000..0e5cfd6 --- /dev/null +++ b/.github/workflows/create-meeting-artifacts-scheduled.yml @@ -0,0 +1,82 @@ +name: Create Meeting Artifacts (Scheduled) + +on: + schedule: + - cron: '0 10 * * 1' + +jobs: + create-artifacts: + runs-on: ubuntu-latest + strategy: + 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@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Read USER field from meeting_base file + id: read-vars + run: | + meeting_group="${{ matrix.meeting_group }}" + user=$(grep '^USER=' "templates/meeting_base_${meeting_group}" | cut -d'=' -f2 | xargs) + hackmd_team_name=$(grep '^HACKMD_TEAM_NAME=' "templates/meeting_base_${meeting_group}" | cut -d'=' -f2 | xargs) + echo "user=$user" >> $GITHUB_OUTPUT + echo "hackmd_team_name=$hackmd_team_name" >> $GITHUB_OUTPUT + + - uses: actions/create-github-app-token@v2 + 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 }} + HACKMD_TEAM_NAME: ${{ steps.read-vars.outputs.hackmd_team_name }} + 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@v4 + with: + name: meeting-artifacts-${{ matrix.meeting_group }} + path: | + ~/.make-node-meeting/ + *.md + retention-days: 7 + if-no-files-found: ignore 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_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/meeting_base_Release b/templates/meeting_base_Release index 740ac54..10b1578 100644 --- a/templates/meeting_base_Release +++ b/templates/meeting_base_Release @@ -1,6 +1,7 @@ CALENDAR_FILTER="Node.js Release Working Group Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" +HACKMD_TEAM_NAME="openjs-nodejs" REPO="Release" GROUP_NAME="Release WorkGroup" JOINING_INSTRUCTIONS=" diff --git a/templates/meeting_base_benchmarking b/templates/meeting_base_benchmarking index 1c5fe55..05b8473 100644 --- a/templates/meeting_base_benchmarking +++ b/templates/meeting_base_benchmarking @@ -1,6 +1,7 @@ CALENDAR_FILTER="Benchmarking WG Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" +HACKMD_TEAM_NAME="openjs-nodejs" REPO="benchmarking" GROUP_NAME="Benchmarking WorkGroup" JOINING_INSTRUCTIONS=" diff --git a/templates/meeting_base_build b/templates/meeting_base_build index eb0f62c..02b2067 100644 --- a/templates/meeting_base_build +++ b/templates/meeting_base_build @@ -4,6 +4,7 @@ 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 876ccff..0000000 --- a/templates/meeting_base_commcomm +++ /dev/null @@ -1,22 +0,0 @@ -CALENDAR_FILTER="Node.js Community Committee" -CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@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 257349e..53fb8ae 100644 --- a/templates/meeting_base_cross_project_council +++ b/templates/meeting_base_cross_project_council @@ -2,6 +2,7 @@ CALENDAR_FILTER="Cross Project Council Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" CALENDAR_ID="linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com" USER="openjs-foundation" +HACKMD_TEAM_NAME="openjs-nodejs" HOST="OpenJS Foundation" REPO="cross-project-council" GROUP_NAME="Cross Project Council" diff --git a/templates/meeting_base_diag b/templates/meeting_base_diag index a1e1bc8..e020e76 100644 --- a/templates/meeting_base_diag +++ b/templates/meeting_base_diag @@ -1,6 +1,7 @@ CALENDAR_FILTER="Diagnostics WG Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" +HACKMD_TEAM_NAME="openjs-nodejs" REPO="diagnostics" GROUP_NAME="Diagnostics WorkGroup" AGENDA_TAG=diag-agenda diff --git a/templates/meeting_base_diag_deepdive b/templates/meeting_base_diag_deepdive index ae790ee..f49ae19 100644 --- a/templates/meeting_base_diag_deepdive +++ b/templates/meeting_base_diag_deepdive @@ -1,6 +1,7 @@ CALENDAR_FILTER="Diagnostics Deep Dive Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" +HACKMD_TEAM_NAME="openjs-nodejs" REPO="diagnostics" GROUP_NAME="Diagnostics Deep Dive" AGENDA_TAG=diag-deepdive-agenda diff --git a/templates/meeting_base_ecosystem_report b/templates/meeting_base_ecosystem_report index 45c23fb..47539a9 100644 --- a/templates/meeting_base_ecosystem_report +++ b/templates/meeting_base_ecosystem_report @@ -1,6 +1,7 @@ CALENDAR_FILTER="Ecosystem Report Collab Space" CALENDAR_ID="linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com" USER="openjs-foundation" +HACKMD_TEAM_NAME="openjs-nodejs" HOST="OpenJS Foundation" REPO="ecosystem-report-collab-space" GROUP_NAME="Ecosystem Report Collab Space" diff --git a/templates/meeting_base_loaders b/templates/meeting_base_loaders index 76479d5..993260c 100644 --- a/templates/meeting_base_loaders +++ b/templates/meeting_base_loaders @@ -1,6 +1,7 @@ CALENDAR_FILTER="Loaders Team Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" +HACKMD_TEAM_NAME="openjs-nodejs" REPO="loaders" GROUP_NAME="Loaders Team" JOINING_INSTRUCTIONS="* link for participants: diff --git a/templates/meeting_base_michael b/templates/meeting_base_michael deleted file mode 100644 index 994e75e..0000000 --- a/templates/meeting_base_michael +++ /dev/null @@ -1,15 +0,0 @@ -CALENDAR_FILTER="Node.js TSC Meeting" -CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@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 f2b7ab4..6e7ac03 100644 --- a/templates/meeting_base_modules +++ b/templates/meeting_base_modules @@ -1,6 +1,7 @@ CALENDAR_FILTER="Modules Team Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" +HACKMD_TEAM_NAME="openjs-nodejs" REPO="modules" GROUP_NAME="Modules Team" JOINING_INSTRUCTIONS="* link for participants: diff --git a/templates/meeting_base_next-10 b/templates/meeting_base_next-10 index 532f544..e55f77f 100644 --- a/templates/meeting_base_next-10 +++ b/templates/meeting_base_next-10 @@ -1,6 +1,7 @@ CALENDAR_FILTER="Node.js Next 10 years" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" +HACKMD_TEAM_NAME="openjs-nodejs" REPO="next-10" GROUP_NAME="Next 10 Years team" JOINING_INSTRUCTIONS=" diff --git a/templates/meeting_base_nodejsafrica b/templates/meeting_base_nodejsafrica deleted file mode 100644 index 4f9ced5..0000000 --- a/templates/meeting_base_nodejsafrica +++ /dev/null @@ -1,16 +0,0 @@ -CALENDAR_FILTER="Node.js Africa Membership/Leadership" -CALENDAR_ID="node.js.africa@gmail.com" -USER="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 9821eb5..5c65353 100644 --- a/templates/meeting_base_outreach +++ b/templates/meeting_base_outreach @@ -1,6 +1,7 @@ CALENDAR_FILTER="Node.js Outreach Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" +HACKMD_TEAM_NAME="openjs-nodejs" REPO="outreach" GROUP_NAME="Outreach" AGENDA_TAG=outreach-agenda diff --git a/templates/meeting_base_package-maintenance b/templates/meeting_base_package-maintenance index 9786fca..cba4c66 100644 --- a/templates/meeting_base_package-maintenance +++ b/templates/meeting_base_package-maintenance @@ -1,6 +1,7 @@ CALENDAR_FILTER="Package Maintenance" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" +HACKMD_TEAM_NAME="openjs-nodejs" REPO="package-maintenance" GROUP_NAME="Package Maintenance Team" JOINING_INSTRUCTIONS=" diff --git a/templates/meeting_base_package_metadata_interop b/templates/meeting_base_package_metadata_interop index 8c933cc..4bb3295 100644 --- a/templates/meeting_base_package_metadata_interop +++ b/templates/meeting_base_package_metadata_interop @@ -1,6 +1,7 @@ CALENDAR_FILTER="Package Metadata Interoperability" CALENDAR_ID="linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com" USER="openjs-foundation" +HACKMD_TEAM_NAME="openjs-nodejs" HOST="OpenJS Foundation" REPO="package-metadata-interoperability-collab-space" GROUP_NAME="Package Metadata Interoperability Collab Space" diff --git a/templates/meeting_base_security-wg b/templates/meeting_base_security-wg index 5b5d7a6..c70cd8d 100644 --- a/templates/meeting_base_security-wg +++ b/templates/meeting_base_security-wg @@ -3,6 +3,7 @@ CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@ 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 9176730..e54b14d 100644 --- a/templates/meeting_base_security_collab +++ b/templates/meeting_base_security_collab @@ -5,6 +5,7 @@ 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 183908a..6268bd4 100644 --- a/templates/meeting_base_standards +++ b/templates/meeting_base_standards @@ -1,6 +1,7 @@ CALENDAR_FILTER="Standards Working Group Meeting" CALENDAR_ID="linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com" USER="openjs-foundation" +HACKMD_TEAM_NAME="openjs-nodejs" HOST="OpenJS Foundation" REPO="standards" GROUP_NAME="Standards Working Group" diff --git a/templates/meeting_base_sustainability_collab b/templates/meeting_base_sustainability_collab index 8483139..8175069 100644 --- a/templates/meeting_base_sustainability_collab +++ b/templates/meeting_base_sustainability_collab @@ -1,6 +1,7 @@ CALENDAR_FILTER="Sustainability Collaboration Space" CALENDAR_ID="linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com" USER="openjs-foundation" +HACKMD_TEAM_NAME="openjs-nodejs" HOST="OpenJS Foundation" REPO="sustainability-collab-space" GROUP_NAME="Sustainability Collaboration Space" diff --git a/templates/meeting_base_tooling b/templates/meeting_base_tooling index 4f8b2a8..45fe746 100644 --- a/templates/meeting_base_tooling +++ b/templates/meeting_base_tooling @@ -1,6 +1,7 @@ CALENDAR_FILTER="Node.js Tooling" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" +HACKMD_TEAM_NAME="openjs-nodejs" REPO="tooling" GROUP_NAME="Tooling Group" JOINING_INSTRUCTIONS=" diff --git a/templates/meeting_base_tsc b/templates/meeting_base_tsc index 5cd6b31..621ed96 100644 --- a/templates/meeting_base_tsc +++ b/templates/meeting_base_tsc @@ -1,6 +1,7 @@ CALENDAR_FILTER="Node.js TSC Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" +HACKMD_TEAM_NAME="openjs-nodejs" REPO="TSC" HOST="Node.js" GROUP_NAME="Technical Steering Committee (TSC)" diff --git a/templates/meeting_base_typescript b/templates/meeting_base_typescript index cde188b..bb4314e 100644 --- a/templates/meeting_base_typescript +++ b/templates/meeting_base_typescript @@ -1,6 +1,7 @@ CALENDAR_FILTER="TypeScript team meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" +HACKMD_TEAM_NAME="openjs-nodejs" REPO="typescript" GROUP_NAME="TypeScript team" JOINING_INSTRUCTIONS="https://zoom.us/j/95749675148 diff --git a/templates/meeting_base_userfeedback b/templates/meeting_base_userfeedback index 9ef0d0f..8a89501 100644 --- a/templates/meeting_base_userfeedback +++ b/templates/meeting_base_userfeedback @@ -1,6 +1,7 @@ CALENDAR_FILTER="Node.js User Feedback Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" +HACKMD_TEAM_NAME="openjs-nodejs" REPO="user-feedback" GROUP_NAME="User Feedback" AGENDA_TAG=user-feedback-agenda diff --git a/templates/meeting_base_uvwasi b/templates/meeting_base_uvwasi index 94cf690..202dc9f 100644 --- a/templates/meeting_base_uvwasi +++ b/templates/meeting_base_uvwasi @@ -1,6 +1,7 @@ CALENDAR_FILTER="Node.js uvwasi team meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" +HACKMD_TEAM_NAME="openjs-nodejs" REPO="uvwasi" GROUP_NAME="uvwasi team" AGENDA_TAG=uvwasi-agenda diff --git a/templates/meeting_base_web-server-frameworks b/templates/meeting_base_web-server-frameworks index 6249570..d69a14a 100644 --- a/templates/meeting_base_web-server-frameworks +++ b/templates/meeting_base_web-server-frameworks @@ -3,6 +3,7 @@ CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@ 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/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_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_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/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_michael deleted file mode 100644 index e69de29..0000000 diff --git a/templates/observers_nodejsafrica b/templates/observers_nodejsafrica deleted file mode 100644 index e69de29..0000000 From 6d4096a8bec8c171e7b361feb8a03c77db0e6d12 Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Wed, 17 Sep 2025 22:45:31 +0200 Subject: [PATCH 33/48] refactor: update step names to improve clarity in meeting artifact workflows --- .github/workflows/create-meeting-artifacts-manual.yml | 2 +- .github/workflows/create-meeting-artifacts-scheduled.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/create-meeting-artifacts-manual.yml b/.github/workflows/create-meeting-artifacts-manual.yml index 42c211c..d4c19e8 100644 --- a/.github/workflows/create-meeting-artifacts-manual.yml +++ b/.github/workflows/create-meeting-artifacts-manual.yml @@ -48,7 +48,7 @@ jobs: - name: Install dependencies run: npm ci - - name: Read USER field from meeting_base file + - name: Read Meeting Variables id: read-vars run: | meeting_group="${{ github.event.inputs.meeting_group }}" diff --git a/.github/workflows/create-meeting-artifacts-scheduled.yml b/.github/workflows/create-meeting-artifacts-scheduled.yml index 0e5cfd6..3fc058e 100644 --- a/.github/workflows/create-meeting-artifacts-scheduled.yml +++ b/.github/workflows/create-meeting-artifacts-scheduled.yml @@ -46,7 +46,7 @@ jobs: - name: Install dependencies run: npm ci - - name: Read USER field from meeting_base file + - name: Read Meeting Variables id: read-vars run: | meeting_group="${{ matrix.meeting_group }}" From 9a853ebd6ee37cd4828b7038e4be95fde43320eb Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Wed, 17 Sep 2025 22:50:00 +0200 Subject: [PATCH 34/48] chore: use hashed versions --- .github/workflows/create-meeting-artifacts-manual.yml | 9 +++++---- .github/workflows/create-meeting-artifacts-scheduled.yml | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/create-meeting-artifacts-manual.yml b/.github/workflows/create-meeting-artifacts-manual.yml index d4c19e8..37cfa8f 100644 --- a/.github/workflows/create-meeting-artifacts-manual.yml +++ b/.github/workflows/create-meeting-artifacts-manual.yml @@ -37,10 +37,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version-file: '.nvmrc' cache: 'npm' @@ -57,7 +57,8 @@ jobs: echo "user=$user" >> $GITHUB_OUTPUT echo "hackmd_team_name=$hackmd_team_name" >> $GITHUB_OUTPUT - - uses: actions/create-github-app-token@v2 + - name: Create GitHub App Token + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 id: app-token with: app-id: ${{ secrets.BOT_ID }} @@ -74,7 +75,7 @@ jobs: - name: Upload artifacts if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: meeting-artifacts-${{ github.event.inputs.meeting_group || 'tsc' }} path: | diff --git a/.github/workflows/create-meeting-artifacts-scheduled.yml b/.github/workflows/create-meeting-artifacts-scheduled.yml index 3fc058e..deaac67 100644 --- a/.github/workflows/create-meeting-artifacts-scheduled.yml +++ b/.github/workflows/create-meeting-artifacts-scheduled.yml @@ -35,10 +35,10 @@ jobs: - web-server-frameworks steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3 with: node-version-file: '.nvmrc' cache: 'npm' @@ -55,7 +55,8 @@ jobs: echo "user=$user" >> $GITHUB_OUTPUT echo "hackmd_team_name=$hackmd_team_name" >> $GITHUB_OUTPUT - - uses: actions/create-github-app-token@v2 + - name: Create GitHub App Token + uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 id: app-token with: app-id: ${{ secrets.BOT_ID }} @@ -72,7 +73,7 @@ jobs: - name: Upload artifacts if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: meeting-artifacts-${{ matrix.meeting_group }} path: | From 3fd1eaa47aceb97b2750a3e27210834b3856e3ee Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Thu, 18 Sep 2025 13:42:10 +0200 Subject: [PATCH 35/48] chore: fixed grabbing agenda items and added more tests and hackmd parsing --- .../create-meeting-artifacts-manual.yml | 3 --- .../create-meeting-artifacts-scheduled.yml | 3 --- README.md | 27 ++++++++++++++----- create-node-meeting-artifacts.mjs | 8 +++--- src/config.mjs | 2 -- src/github.mjs | 11 ++++---- src/hackmd.mjs | 7 +++-- src/types.d.ts | 4 +-- templates/meeting_base_Release | 2 +- templates/meeting_base_benchmarking | 2 +- templates/meeting_base_cross_project_council | 2 +- templates/meeting_base_diag | 2 +- templates/meeting_base_diag_deepdive | 2 +- templates/meeting_base_ecosystem_report | 2 +- templates/meeting_base_loaders | 2 +- templates/meeting_base_modules | 2 +- templates/meeting_base_next-10 | 2 +- templates/meeting_base_outreach | 2 +- templates/meeting_base_package-maintenance | 2 +- .../meeting_base_package_metadata_interop | 2 +- templates/meeting_base_standards | 2 +- templates/meeting_base_sustainability_collab | 2 +- templates/meeting_base_tooling | 2 +- templates/meeting_base_tsc | 2 +- templates/meeting_base_typescript | 2 +- templates/meeting_base_userfeedback | 2 +- templates/meeting_base_uvwasi | 2 +- test/config.test.mjs | 2 -- 28 files changed, 56 insertions(+), 49 deletions(-) diff --git a/.github/workflows/create-meeting-artifacts-manual.yml b/.github/workflows/create-meeting-artifacts-manual.yml index 37cfa8f..d819e7a 100644 --- a/.github/workflows/create-meeting-artifacts-manual.yml +++ b/.github/workflows/create-meeting-artifacts-manual.yml @@ -53,9 +53,7 @@ jobs: run: | meeting_group="${{ github.event.inputs.meeting_group }}" user=$(grep '^USER=' "templates/meeting_base_${meeting_group}" | cut -d'=' -f2 | xargs) - hackmd_team_name=$(grep '^HACKMD_TEAM_NAME=' "templates/meeting_base_${meeting_group}" | cut -d'=' -f2 | xargs) echo "user=$user" >> $GITHUB_OUTPUT - echo "hackmd_team_name=$hackmd_team_name" >> $GITHUB_OUTPUT - name: Create GitHub App Token uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 @@ -69,7 +67,6 @@ jobs: env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} HACKMD_API_TOKEN: ${{ secrets.HACKMD_API_TOKEN }} - HACKMD_TEAM_NAME: ${{ steps.read-vars.outputs.hackmd_team_name }} GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} run: node create-node-meeting-artifacts.mjs ${{ github.event.inputs.meeting_group }} diff --git a/.github/workflows/create-meeting-artifacts-scheduled.yml b/.github/workflows/create-meeting-artifacts-scheduled.yml index deaac67..431fa6b 100644 --- a/.github/workflows/create-meeting-artifacts-scheduled.yml +++ b/.github/workflows/create-meeting-artifacts-scheduled.yml @@ -51,9 +51,7 @@ jobs: run: | meeting_group="${{ matrix.meeting_group }}" user=$(grep '^USER=' "templates/meeting_base_${meeting_group}" | cut -d'=' -f2 | xargs) - hackmd_team_name=$(grep '^HACKMD_TEAM_NAME=' "templates/meeting_base_${meeting_group}" | cut -d'=' -f2 | xargs) echo "user=$user" >> $GITHUB_OUTPUT - echo "hackmd_team_name=$hackmd_team_name" >> $GITHUB_OUTPUT - name: Create GitHub App Token uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4 @@ -67,7 +65,6 @@ jobs: env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} HACKMD_API_TOKEN: ${{ secrets.HACKMD_API_TOKEN }} - HACKMD_TEAM_NAME: ${{ steps.read-vars.outputs.hackmd_team_name }} GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} run: node create-node-meeting-artifacts.mjs ${{ matrix.meeting_group }} diff --git a/README.md b/README.md index 6f26df1..c9ae205 100644 --- a/README.md +++ b/README.md @@ -123,9 +123,14 @@ templates/minutes_base_ See [TEMPLATES_DOCUMENTATION.md](./TEMPLATES_DOCUMENTATION.md) for detailed template examples and variable explanations. -### 2. Update GitHub Actions Workflow +### 2. Update GitHub Actions Workflows -Add your meeting group to `.github/workflows/create-meeting-artifacts.yml`: +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: @@ -142,6 +147,19 @@ workflow_dispatch: - 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: @@ -211,10 +229,6 @@ The application creates: - `HACKMD_API_TOKEN`: HackMD API token for creating and managing documents - `GOOGLE_API_KEY`: Google Calendar API Key for read-only calendar access -#### Optional - -- `HACKMD_TEAM_NAME`: HackMD team name/path for team workspaces - ### Meeting Base Configuration Each `meeting_base_` file contains: @@ -227,5 +241,6 @@ 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/create-node-meeting-artifacts.mjs b/create-node-meeting-artifacts.mjs index 6466fd0..c88d956 100644 --- a/create-node-meeting-artifacts.mjs +++ b/create-node-meeting-artifacts.mjs @@ -24,12 +24,12 @@ const calendarClient = google.createCalendarClient(config.google); // Step 3: Initialize GitHub client const githubClient = github.createGitHubClient(config); -// Step 4: Initialize HackMD client -const hackmdClient = hackmd.createHackMDClient(config); - -// Step 5: Read meeting configuration from templates +// 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); diff --git a/src/config.mjs b/src/config.mjs index 891ff4f..38ad883 100644 --- a/src/config.mjs +++ b/src/config.mjs @@ -24,8 +24,6 @@ export const getConfig = () => ({ hackmd: { // HackMD API token for authentication apiToken: process.env.HACKMD_API_TOKEN, - // HackMD team name - teamName: process.env.HACKMD_TEAM_NAME, }, // Directory paths for templates, output, and configuration diff --git a/src/github.mjs b/src/github.mjs index e1bb747..b075c5d 100644 --- a/src/github.mjs +++ b/src/github.mjs @@ -59,14 +59,14 @@ export const getAgendaIssues = async ( // Get all public repositories in the organization const repos = await paginate(rest.repos.listForOrg, { - org: properties.USER, + org: githubOrg, type: 'public', per_page: 100, }); - // Fetch issues from all repositories concurrently + // Fetch issues and PRs from all repositories concurrently const issuePromises = repos.map(async repo => { - const issues = await paginate(rest.issues.listForRepo, { + const items = await paginate(rest.issues.listForRepo, { owner: githubOrg, repo: repo.name, labels: agendaTag, @@ -74,9 +74,8 @@ export const getAgendaIssues = async ( per_page: 100, }); - const filteredIssues = issues.filter(({ pull_request }) => !pull_request); // Exclude PRs - - return { repoName: repo.name, issues: filteredIssues }; + // Include both issues and PRs for agenda items + return { repoName: repo.name, issues: items }; }); return Promise.all(issuePromises); diff --git a/src/hackmd.mjs b/src/hackmd.mjs index 90b16aa..647e0a9 100644 --- a/src/hackmd.mjs +++ b/src/hackmd.mjs @@ -5,10 +5,13 @@ 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, teamName } }) => { - // Use team-specific API endpoint if teamPath is provided +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'; diff --git a/src/types.d.ts b/src/types.d.ts index 3efcc46..e7a2d94 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -28,8 +28,6 @@ export interface GoogleConfig { export interface HackMDConfig { /** HackMD API token */ apiToken: string; - /** HackMD team name/path */ - teamName?: string; } /** @@ -74,6 +72,8 @@ export interface MeetingProperties { 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/templates/meeting_base_Release b/templates/meeting_base_Release index 10b1578..a528f10 100644 --- a/templates/meeting_base_Release +++ b/templates/meeting_base_Release @@ -1,9 +1,9 @@ CALENDAR_FILTER="Node.js Release Working Group Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" -HACKMD_TEAM_NAME="openjs-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 05b8473..cd777eb 100644 --- a/templates/meeting_base_benchmarking +++ b/templates/meeting_base_benchmarking @@ -1,9 +1,9 @@ CALENDAR_FILTER="Benchmarking WG Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" -HACKMD_TEAM_NAME="openjs-nodejs" REPO="benchmarking" GROUP_NAME="Benchmarking WorkGroup" +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" * link for participants: diff --git a/templates/meeting_base_cross_project_council b/templates/meeting_base_cross_project_council index 53fb8ae..7562202 100644 --- a/templates/meeting_base_cross_project_council +++ b/templates/meeting_base_cross_project_council @@ -2,12 +2,12 @@ CALENDAR_FILTER="Cross Project Council Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" CALENDAR_ID="linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com" USER="openjs-foundation" -HACKMD_TEAM_NAME="openjs-nodejs" 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 diff --git a/templates/meeting_base_diag b/templates/meeting_base_diag index e020e76..ca9025b 100644 --- a/templates/meeting_base_diag +++ b/templates/meeting_base_diag @@ -1,10 +1,10 @@ CALENDAR_FILTER="Diagnostics WG Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" -HACKMD_TEAM_NAME="openjs-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 f49ae19..b985171 100644 --- a/templates/meeting_base_diag_deepdive +++ b/templates/meeting_base_diag_deepdive @@ -1,10 +1,10 @@ CALENDAR_FILTER="Diagnostics Deep Dive Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" -HACKMD_TEAM_NAME="openjs-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 index 47539a9..1125ebf 100644 --- a/templates/meeting_base_ecosystem_report +++ b/templates/meeting_base_ecosystem_report @@ -1,11 +1,11 @@ CALENDAR_FILTER="Ecosystem Report Collab Space" CALENDAR_ID="linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com" USER="openjs-foundation" -HACKMD_TEAM_NAME="openjs-nodejs" 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: diff --git a/templates/meeting_base_loaders b/templates/meeting_base_loaders index 993260c..9ab34aa 100644 --- a/templates/meeting_base_loaders +++ b/templates/meeting_base_loaders @@ -1,9 +1,9 @@ CALENDAR_FILTER="Loaders Team Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" -HACKMD_TEAM_NAME="openjs-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_modules b/templates/meeting_base_modules index 6e7ac03..f77b4e4 100644 --- a/templates/meeting_base_modules +++ b/templates/meeting_base_modules @@ -1,9 +1,9 @@ CALENDAR_FILTER="Modules Team Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" -HACKMD_TEAM_NAME="openjs-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 e55f77f..74dbc77 100644 --- a/templates/meeting_base_next-10 +++ b/templates/meeting_base_next-10 @@ -1,9 +1,9 @@ CALENDAR_FILTER="Node.js Next 10 years" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" -HACKMD_TEAM_NAME="openjs-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_outreach b/templates/meeting_base_outreach index 5c65353..26204dc 100644 --- a/templates/meeting_base_outreach +++ b/templates/meeting_base_outreach @@ -1,10 +1,10 @@ CALENDAR_FILTER="Node.js Outreach Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" -HACKMD_TEAM_NAME="openjs-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 cba4c66..de127ff 100644 --- a/templates/meeting_base_package-maintenance +++ b/templates/meeting_base_package-maintenance @@ -1,9 +1,9 @@ CALENDAR_FILTER="Package Maintenance" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" -HACKMD_TEAM_NAME="openjs-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 4bb3295..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" -HACKMD_TEAM_NAME="openjs-nodejs" 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_standards b/templates/meeting_base_standards index 6268bd4..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" -HACKMD_TEAM_NAME="openjs-nodejs" 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 index 8175069..1c5767c 100644 --- a/templates/meeting_base_sustainability_collab +++ b/templates/meeting_base_sustainability_collab @@ -1,11 +1,11 @@ CALENDAR_FILTER="Sustainability Collaboration Space" CALENDAR_ID="linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com" USER="openjs-foundation" -HACKMD_TEAM_NAME="openjs-nodejs" 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: diff --git a/templates/meeting_base_tooling b/templates/meeting_base_tooling index 45fe746..0028923 100644 --- a/templates/meeting_base_tooling +++ b/templates/meeting_base_tooling @@ -1,9 +1,9 @@ CALENDAR_FILTER="Node.js Tooling" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" -HACKMD_TEAM_NAME="openjs-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 621ed96..7b41a64 100644 --- a/templates/meeting_base_tsc +++ b/templates/meeting_base_tsc @@ -1,10 +1,10 @@ CALENDAR_FILTER="Node.js TSC Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" -HACKMD_TEAM_NAME="openjs-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 index bb4314e..b2919d6 100644 --- a/templates/meeting_base_typescript +++ b/templates/meeting_base_typescript @@ -1,9 +1,9 @@ CALENDAR_FILTER="TypeScript team meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" -HACKMD_TEAM_NAME="openjs-nodejs" REPO="typescript" GROUP_NAME="TypeScript team" +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS="https://zoom.us/j/95749675148 * link for participants: <> diff --git a/templates/meeting_base_userfeedback b/templates/meeting_base_userfeedback index 8a89501..ee46718 100644 --- a/templates/meeting_base_userfeedback +++ b/templates/meeting_base_userfeedback @@ -1,10 +1,10 @@ CALENDAR_FILTER="Node.js User Feedback Meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" -HACKMD_TEAM_NAME="openjs-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 202dc9f..ec3880a 100644 --- a/templates/meeting_base_uvwasi +++ b/templates/meeting_base_uvwasi @@ -1,10 +1,10 @@ CALENDAR_FILTER="Node.js uvwasi team meeting" CALENDAR_ID="c_16f0ae5d3a22625175d199dbdb1cac84c2d09eab7f173e94f558417cb5cdbfd8@group.calendar.google.com" USER="nodejs" -HACKMD_TEAM_NAME="openjs-nodejs" REPO="uvwasi" GROUP_NAME="uvwasi team" AGENDA_TAG=uvwasi-agenda +HACKMD_TEAM_NAME="openjs-nodejs" JOINING_INSTRUCTIONS=" * link for participants: diff --git a/test/config.test.mjs b/test/config.test.mjs index c8d97a1..6dc92a3 100644 --- a/test/config.test.mjs +++ b/test/config.test.mjs @@ -63,12 +63,10 @@ describe('Config', () => { it('should read HackMD config from environment', () => { process.env.HACKMD_API_TOKEN = 'hackmd_token'; - process.env.HACKMD_TEAM_NAME = 'nodejs'; const config = getConfig(); assert.strictEqual(config.hackmd.apiToken, 'hackmd_token'); - assert.strictEqual(config.hackmd.teamName, 'nodejs'); }); it('should handle undefined environment variables gracefully', () => { From f03070b856f4e6276063d0bc5758c2092b223df8 Mon Sep 17 00:00:00 2001 From: Aviv Keller Date: Sat, 27 Sep 2025 11:58:16 -0400 Subject: [PATCH 36/48] feat(meetings): add web (#183) --- package.json | 2 ++ templates/invited_web | 1 + templates/meeting_base_web | 22 ++++++++++++++++++++++ templates/minutes_base_web | 27 +++++++++++++++++++++++++++ templates/observers_web | 0 5 files changed, 52 insertions(+) create mode 100644 templates/invited_web create mode 100644 templates/meeting_base_web create mode 100644 templates/minutes_base_web create mode 100644 templates/observers_web diff --git a/package.json b/package.json index aad53b2..76d53dc 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,8 @@ "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" }, 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_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/minutes_base_web b/templates/minutes_base_web new file mode 100644 index 0000000..21c14b2 --- /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 `+GoogleCalendar` at the bottom right to add to your own Google calendar. diff --git a/templates/observers_web b/templates/observers_web new file mode 100644 index 0000000..e69de29 From 8d99074cfbae71afd8e436a4177af4c13f247daf Mon Sep 17 00:00:00 2001 From: Jon Church Date: Sat, 27 Sep 2025 11:59:19 -0400 Subject: [PATCH 37/48] fix: turn off fail fast, let other wg run if one fails (#184) --- .github/workflows/create-meeting-artifacts-scheduled.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/create-meeting-artifacts-scheduled.yml b/.github/workflows/create-meeting-artifacts-scheduled.yml index 431fa6b..219fa10 100644 --- a/.github/workflows/create-meeting-artifacts-scheduled.yml +++ b/.github/workflows/create-meeting-artifacts-scheduled.yml @@ -8,6 +8,7 @@ jobs: create-artifacts: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: meeting_group: - benchmarking From 9b0c37c4a6b3f0b0549fcdece2955e59d447384b Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Sat, 27 Sep 2025 18:02:42 +0200 Subject: [PATCH 38/48] chore: allow scheduled manual run --- .github/workflows/create-meeting-artifacts-scheduled.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/create-meeting-artifacts-scheduled.yml b/.github/workflows/create-meeting-artifacts-scheduled.yml index 219fa10..2cc8a0b 100644 --- a/.github/workflows/create-meeting-artifacts-scheduled.yml +++ b/.github/workflows/create-meeting-artifacts-scheduled.yml @@ -1,6 +1,7 @@ name: Create Meeting Artifacts (Scheduled) on: + workflow_dispatch: schedule: - cron: '0 10 * * 1' From 0bdee79fa6341d9088af65abef9ee956c5c31116 Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Sat, 27 Sep 2025 18:43:17 +0200 Subject: [PATCH 39/48] chore: fixed some things regarding apis --- create-node-meeting-artifacts.mjs | 22 ++++++----- global.d.ts | 4 +- src/google.mjs | 28 ++++++++++---- src/hackmd.mjs | 61 +++++++++++++++++++++++-------- 4 files changed, 80 insertions(+), 35 deletions(-) diff --git a/create-node-meeting-artifacts.mjs b/create-node-meeting-artifacts.mjs index c88d956..67f3713 100644 --- a/create-node-meeting-artifacts.mjs +++ b/create-node-meeting-artifacts.mjs @@ -18,18 +18,18 @@ import * as meetings from './src/meeting.mjs'; // Step 1: Application configuration const config = getConfig(); -// Step 2: Initialize Google Calendar client with API Key +// Step 2: Initialize HackMD client with meeting configuration +const hackmdClient = hackmd.createHackMDClient(config); + +// Step 3: Initialize Google Calendar client with API Key const calendarClient = google.createCalendarClient(config.google); -// Step 3: Initialize GitHub client +// Step 4: Initialize GitHub client const githubClient = github.createGitHubClient(config); -// Step 4: Read meeting configuration from templates +// Step 5: 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); @@ -56,11 +56,12 @@ const meetingAgenda = meetings.generateMeetingAgenda( meetingConfig ); -// Step 11: Create HackMD document with meeting notes +// Step 11: Create HackMD document with meeting notes and tags const hackmdNote = await hackmd.createMeetingNotesDocument( hackmdClient, meetingTitle, - '' + '', + meetingConfig ); // Step 12: Get the HackMD document link @@ -76,7 +77,7 @@ const issueContent = await meetings.generateMeetingIssue( minutesDocLink ); -// Step 14: Create GitHub issue with HackMD link +// // Step 14: Create GitHub issue with HackMD link const githubIssue = await github.createGitHubIssue( githubClient, meetingConfig, @@ -98,7 +99,8 @@ const minutesContent = await meetings.generateMeetingMinutes( await hackmd.updateMeetingNotesDocument( hackmdClient, hackmdNote.id, - minutesContent + minutesContent, + meetingConfig ); // Output success information with links diff --git a/global.d.ts b/global.d.ts index 29df2df..dfc2a0c 100644 --- a/global.d.ts +++ b/global.d.ts @@ -5,14 +5,14 @@ import type { calendar_v3 } from '@googleapis/calendar'; import type { RestEndpointMethodTypes } from '@octokit/rest'; -import type { HackMDAPI } from '@hackmd/api'; +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 = HackMDAPI; + type HackMDClient = API; type HackMDNote = SingleNote; // GitHub API type aliases diff --git a/src/google.mjs b/src/google.mjs index 52af36e..27b5400 100644 --- a/src/google.mjs +++ b/src/google.mjs @@ -1,7 +1,5 @@ import { calendar } from '@googleapis/calendar'; -import { TIME_CONSTANTS } from './constants.mjs'; - /** * Creates an authenticated Google Calendar client using API Key * @param {import('./types.d.ts').GoogleConfig} gConfig - Google configuration object @@ -11,7 +9,7 @@ export const createCalendarClient = ({ apiKey: auth }) => calendar({ version: 'v3', auth }); /** - * Finds the next meeting event in Google Calendar within the next week + * 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 @@ -19,13 +17,25 @@ export const createCalendarClient = ({ apiKey: auth }) => export const findNextMeetingEvent = async (calendarClient, meetingConfig) => { const now = new Date(); - const nextWeek = new Date(now.getTime() + TIME_CONSTANTS.WEEK_IN_MS); + // 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: now.toISOString(), - timeMax: nextWeek.toISOString(), + 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( @@ -36,7 +46,11 @@ export const findNextMeetingEvent = async (calendarClient, meetingConfig) => { // Ensure we found at least one event if (!response.data.items || response.data.items.length === 0) { - throw new Error('Could not find calendar event for the next week'); + 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 diff --git a/src/hackmd.mjs b/src/hackmd.mjs index 647e0a9..cdc7aa1 100644 --- a/src/hackmd.mjs +++ b/src/hackmd.mjs @@ -5,36 +5,65 @@ 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); +export const createHackMDClient = ({ hackmd: { apiToken } }) => { + return new HackMDAPI(apiToken); }; /** - * Creates a new meeting notes document in HackMD + * 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 + * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration for tags * @returns {Promise} Created note data with ID and URLs */ -export const createMeetingNotesDocument = (hackmdClient, title, content) => - hackmdClient.createNote({ title, content, ...HACKMD_DEFAULT_PERMISSIONS }); +export const createMeetingNotesDocument = ( + hackmdClient, + title, + content, + meetingConfig +) => { + const meetingTag = + meetingConfig?.properties?.GROUP_NAME ?? + meetingConfig?.properties?.AGENDA_TAG; + + const teamName = meetingConfig?.properties?.HACKMD_TEAM_NAME; + + const noteOptions = { + title, + content, + tags: [meetingTag, 'Meetings'], + ...HACKMD_DEFAULT_PERMISSIONS, + }; + + if (teamName) { + return hackmdClient.createTeamNote(teamName, noteOptions); + } + + return hackmdClient.createNote(noteOptions); +}; /** - * Updates an existing meeting notes document in HackMD + * 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 + * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration for team context * @returns {Promise} Updated note data */ -export const updateMeetingNotesDocument = (hackmdClient, noteId, content) => - hackmdClient.updateNote(noteId, { content }); +export const updateMeetingNotesDocument = ( + hackmdClient, + noteId, + content, + meetingConfig +) => { + const teamName = meetingConfig?.properties?.HACKMD_TEAM_NAME; + + if (teamName) { + return hackmdClient.updateTeamNote(teamName, noteId, { content }); + } + + return hackmdClient.updateNote(noteId, { content }); +}; From b4978cba0fdbcecb372239d7f768e7e5419bc6f3 Mon Sep 17 00:00:00 2001 From: avivkeller Date: Thu, 18 Sep 2025 09:44:55 -0400 Subject: [PATCH 40/48] fix(search): use advanced search --- create-node-meeting-artifacts.mjs | 5 +---- package-lock.json | 2 +- src/constants.mjs | 3 +++ src/github.mjs | 36 +++++++++++-------------------- src/meeting.mjs | 13 ++++------- 5 files changed, 22 insertions(+), 37 deletions(-) diff --git a/create-node-meeting-artifacts.mjs b/create-node-meeting-artifacts.mjs index 67f3713..fb2836a 100644 --- a/create-node-meeting-artifacts.mjs +++ b/create-node-meeting-artifacts.mjs @@ -51,10 +51,7 @@ const gitHubAgendaIssues = await github.getAgendaIssues( ); // Step 10: Parse meeting agenda from GitHub issues -const meetingAgenda = meetings.generateMeetingAgenda( - gitHubAgendaIssues, - meetingConfig -); +const meetingAgenda = meetings.generateMeetingAgenda(gitHubAgendaIssues); // Step 11: Create HackMD document with meeting notes and tags const hackmdNote = await hackmd.createMeetingNotesDocument( diff --git a/package-lock.json b/package-lock.json index 3fc3ab9..e9ef128 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "dedent": "^1.6.0" }, "bin": { - "create-meeting": "bin/create-meeting" + "create-meeting": "create-node-meeting-artifacts.mjs" }, "devDependencies": { "@eslint/js": "^9.33.0", diff --git a/src/constants.mjs b/src/constants.mjs index 897a892..2f9cd4a 100644 --- a/src/constants.mjs +++ b/src/constants.mjs @@ -34,3 +34,6 @@ export const HACKMD_DEFAULT_PERMISSIONS = { writePermission: 'signed_in', commentPermission: 'signed_in_users', }; + +export const REPOSITORY_URL_PREFIX_LENGTH = 'https://api.github.com/repos/' + .length; diff --git a/src/github.mjs b/src/github.mjs index b075c5d..e2c4507 100644 --- a/src/github.mjs +++ b/src/github.mjs @@ -1,6 +1,6 @@ import { Octokit } from '@octokit/rest'; -import { DEFAULT_CONFIG } from './constants.mjs'; +import { DEFAULT_CONFIG, REPOSITORY_URL_PREFIX_LENGTH } from './constants.mjs'; /** * Creates a GitHub API client @@ -47,36 +47,26 @@ export const createGitHubIssue = async ( * @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<{ repoName: string, issues: Array }> } Formatted markdown string of issues + * @returns {Promise<{ [key: string]: Array }>} Formatted markdown string of issues */ export const getAgendaIssues = async ( - { paginate, rest }, + githubClient, { meetingGroup }, { properties } ) => { const githubOrg = properties.USER ?? DEFAULT_CONFIG.githubOrg; const agendaTag = properties.AGENDA_TAG ?? `${meetingGroup}-agenda`; - // Get all public repositories in the organization - const repos = await paginate(rest.repos.listForOrg, { - org: githubOrg, - type: 'public', - per_page: 100, + // Get all issues/PRs in the organization + const issues = await githubClient.paginate('GET /search/issues', { + q: `label:${agendaTag} org:${githubOrg}`, + advanced_search: true, }); - // Fetch issues and PRs from all repositories concurrently - const issuePromises = repos.map(async repo => { - const items = await paginate(rest.issues.listForRepo, { - owner: githubOrg, - repo: repo.name, - labels: agendaTag, - state: 'open', - per_page: 100, - }); - - // Include both issues and PRs for agenda items - return { repoName: repo.name, issues: items }; - }); - - return Promise.all(issuePromises); + return issues.reduce((obj, issue) => { + (obj[issue.repository_url.slice(REPOSITORY_URL_PREFIX_LENGTH)] ||= []).push( + issue + ); + return obj; + }, {}); }; diff --git a/src/meeting.mjs b/src/meeting.mjs index 60a401f..c56f042 100644 --- a/src/meeting.mjs +++ b/src/meeting.mjs @@ -56,21 +56,16 @@ export const generateMeetingTitle = (config, meetingConfig, meetingDate) => { /** * Generates the meeting agenda from the list of agenda issues - * @param {Array<{ repoName: string, issues: Array }>} agendaIssues - List of agenda issues - * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration + * @param {Array<{ [key: string]: Array }>} agendaIssues - List of agenda issues * @returns {Promise} Formatted meeting agenda */ -export const generateMeetingAgenda = (agendaIssues, meetingConfig) => { - const props = meetingConfig.properties; - - const githubOrg = props.USER ?? DEFAULT_CONFIG.githubOrg; - +export const generateMeetingAgenda = agendaIssues => { // Format issues as markdown let agendaMarkdown = ''; - agendaIssues.forEach(({ repoName, issues }) => { + Object.entries(agendaIssues).forEach(([repoName, issues]) => { if (issues.length > 0) { - agendaMarkdown += `### ${githubOrg}/${repoName}\n\n`; + agendaMarkdown += `### ${repoName}\n\n`; issues.forEach(issue => { // Escape markdown characters in title From 8363149030be03be45775a3a5456b9873f3f671c Mon Sep 17 00:00:00 2001 From: avivkeller Date: Sat, 27 Sep 2025 14:46:40 -0400 Subject: [PATCH 41/48] use helper --- src/constants.mjs | 3 --- src/github.mjs | 26 +++++++++++++++++--------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/constants.mjs b/src/constants.mjs index 2f9cd4a..897a892 100644 --- a/src/constants.mjs +++ b/src/constants.mjs @@ -34,6 +34,3 @@ export const HACKMD_DEFAULT_PERMISSIONS = { writePermission: 'signed_in', commentPermission: 'signed_in_users', }; - -export const REPOSITORY_URL_PREFIX_LENGTH = 'https://api.github.com/repos/' - .length; diff --git a/src/github.mjs b/src/github.mjs index e2c4507..ec828b9 100644 --- a/src/github.mjs +++ b/src/github.mjs @@ -1,6 +1,6 @@ import { Octokit } from '@octokit/rest'; -import { DEFAULT_CONFIG, REPOSITORY_URL_PREFIX_LENGTH } from './constants.mjs'; +import { DEFAULT_CONFIG } from './constants.mjs'; /** * Creates a GitHub API client @@ -42,12 +42,25 @@ export const createGitHubIssue = async ( 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 }>} Formatted markdown string of issues + * @returns {Promise<{ [key: string]: Array }>} Meeting agenda */ export const getAgendaIssues = async ( githubClient, @@ -59,14 +72,9 @@ export const getAgendaIssues = async ( // Get all issues/PRs in the organization const issues = await githubClient.paginate('GET /search/issues', { - q: `label:${agendaTag} org:${githubOrg}`, + q: `is:open label:${agendaTag} org:${githubOrg}`, advanced_search: true, }); - return issues.reduce((obj, issue) => { - (obj[issue.repository_url.slice(REPOSITORY_URL_PREFIX_LENGTH)] ||= []).push( - issue - ); - return obj; - }, {}); + return sortIssuesByRepo(issues); }; From ff902cdd1be5734051dac8e52678223165fe0665 Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Sun, 28 Sep 2025 12:22:14 +0200 Subject: [PATCH 42/48] chore: updated package-lock --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index e9ef128..c244540 100644 --- a/package-lock.json +++ b/package-lock.json @@ -962,9 +962,9 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", + "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", From 093d35fd6a67623e991d20a373afdadecd3ae996 Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Sun, 28 Sep 2025 12:38:16 +0200 Subject: [PATCH 43/48] hotfix: fix hackmd --- create-node-meeting-artifacts.mjs | 22 ++++++------ src/hackmd.mjs | 56 ++++++++++++------------------- 2 files changed, 32 insertions(+), 46 deletions(-) diff --git a/create-node-meeting-artifacts.mjs b/create-node-meeting-artifacts.mjs index fb2836a..1dc404f 100644 --- a/create-node-meeting-artifacts.mjs +++ b/create-node-meeting-artifacts.mjs @@ -18,18 +18,18 @@ import * as meetings from './src/meeting.mjs'; // Step 1: Application configuration const config = getConfig(); -// Step 2: Initialize HackMD client with meeting configuration -const hackmdClient = hackmd.createHackMDClient(config); - -// Step 3: Initialize Google Calendar client with API Key +// Step 2: Initialize Google Calendar client with API Key const calendarClient = google.createCalendarClient(config.google); -// Step 4: Initialize GitHub client +// Step 3: Initialize GitHub client const githubClient = github.createGitHubClient(config); -// Step 5: Read meeting configuration from templates +// 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); @@ -57,14 +57,15 @@ const meetingAgenda = meetings.generateMeetingAgenda(gitHubAgendaIssues); const hackmdNote = await hackmd.createMeetingNotesDocument( hackmdClient, meetingTitle, - '', - meetingConfig + '' ); // Step 12: Get the HackMD document link const minutesDocLink = hackmdNote.publishLink || `https://hackmd.io/${hackmdNote.id}`; +console.log({ hackmdNote, minutesDocLink }); + // Step 13: Generate meeting issue content using native implementation const issueContent = await meetings.generateMeetingIssue( config, @@ -74,7 +75,7 @@ const issueContent = await meetings.generateMeetingIssue( minutesDocLink ); -// // Step 14: Create GitHub issue with HackMD link +// Step 14: Create GitHub issue with HackMD link const githubIssue = await github.createGitHubIssue( githubClient, meetingConfig, @@ -96,8 +97,7 @@ const minutesContent = await meetings.generateMeetingMinutes( await hackmd.updateMeetingNotesDocument( hackmdClient, hackmdNote.id, - minutesContent, - meetingConfig + minutesContent ); // Output success information with links diff --git a/src/hackmd.mjs b/src/hackmd.mjs index cdc7aa1..96096cf 100644 --- a/src/hackmd.mjs +++ b/src/hackmd.mjs @@ -5,10 +5,18 @@ 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 } }) => { - return new HackMDAPI(apiToken); +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); }; /** @@ -16,33 +24,20 @@ export const createHackMDClient = ({ hackmd: { apiToken } }) => { * @param {HackMDAPI} hackmdClient - HackMD API client * @param {string} title - Document title * @param {string} content - Document content in Markdown - * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration for tags * @returns {Promise} Created note data with ID and URLs */ -export const createMeetingNotesDocument = ( - hackmdClient, - title, - content, - meetingConfig -) => { - const meetingTag = - meetingConfig?.properties?.GROUP_NAME ?? - meetingConfig?.properties?.AGENDA_TAG; - - const teamName = meetingConfig?.properties?.HACKMD_TEAM_NAME; - +export const createMeetingNotesDocument = (hackmdClient, title, content) => { const noteOptions = { title, content, - tags: [meetingTag, 'Meetings'], + parentFolderId: '', ...HACKMD_DEFAULT_PERMISSIONS, }; - if (teamName) { - return hackmdClient.createTeamNote(teamName, noteOptions); - } - - return hackmdClient.createNote(noteOptions); + // apparently it can return either { note: {...} } or just {...} + return hackmdClient + .createNote(noteOptions) + .then(response => response?.note ?? response); }; /** @@ -50,20 +45,11 @@ export const createMeetingNotesDocument = ( * @param {HackMDClient} hackmdClient - HackMD API client * @param {string} noteId - HackMD note ID * @param {string} content - Updated document content in Markdown - * @param {import('./types.d.ts').MeetingConfig} meetingConfig - Meeting configuration for team context * @returns {Promise} Updated note data */ -export const updateMeetingNotesDocument = ( - hackmdClient, - noteId, - content, - meetingConfig -) => { - const teamName = meetingConfig?.properties?.HACKMD_TEAM_NAME; - - if (teamName) { - return hackmdClient.updateTeamNote(teamName, noteId, { content }); - } - - return hackmdClient.updateNote(noteId, { content }); +export const updateMeetingNotesDocument = (hackmdClient, noteId, content) => { + // apparently it can return either { note: {...} } or just {...} + return hackmdClient + .updateNote(noteId, { content }) + .then(response => response?.note ?? response); }; From 50a2dbd57a371c1645d33187ee290dac8b239da5 Mon Sep 17 00:00:00 2001 From: Aviv Keller Date: Sun, 28 Sep 2025 16:25:58 -0400 Subject: [PATCH 44/48] fix(properties): allow quote-less props (#185) * fix(properties): allow quote-less props * fixup! * fixup! --- package-lock.json | 15 +++- package.json | 3 +- src/meeting.mjs | 4 +- src/utils/templates.mjs | 23 ------ test/utils/templates.test.mjs | 150 +--------------------------------- 5 files changed, 20 insertions(+), 175 deletions(-) diff --git a/package-lock.json b/package-lock.json index c244540..e6462f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ "@googleapis/calendar": "^11.0.1", "@hackmd/api": "^2.5.0", "@octokit/rest": "^22.0.0", - "dedent": "^1.6.0" + "dedent": "^1.6.0", + "dotenv": "^17.2.2" }, "bin": { "create-meeting": "create-node-meeting-artifacts.mjs" @@ -1207,6 +1208,18 @@ "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", diff --git a/package.json b/package.json index 76d53dc..e5dc8fd 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,8 @@ "@googleapis/calendar": "^11.0.1", "@hackmd/api": "^2.5.0", "@octokit/rest": "^22.0.0", - "dedent": "^1.6.0" + "dedent": "^1.6.0", + "dotenv": "^17.2.2" }, "devDependencies": { "@eslint/js": "^9.33.0", diff --git a/src/meeting.mjs b/src/meeting.mjs index c56f042..09bd62c 100644 --- a/src/meeting.mjs +++ b/src/meeting.mjs @@ -1,6 +1,8 @@ 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'; @@ -32,7 +34,7 @@ export const readMeetingConfig = async config => { invited, observers, baseMeetingInfo, - properties: templates.parseMeetingProperties(baseMeetingInfo), + properties: parse(baseMeetingInfo), }; }; diff --git a/src/utils/templates.mjs b/src/utils/templates.mjs index 6ca65d1..78a997e 100644 --- a/src/utils/templates.mjs +++ b/src/utils/templates.mjs @@ -21,26 +21,3 @@ export const parseVariables = (template, variables) => { return processed; }; - -/** - * Simple parser for template properties (KEY="value" format) - * @param {string} content - Template content - * @returns {Record} Parsed properties - */ -export const parseMeetingProperties = content => { - const properties = {}; - - // Handle multiline properties first with a generic regex - // Matches: KEY="multiline content" where content can span multiple lines - const multilineMatches = content.matchAll( - /^([A-Z_][A-Z0-9_]*)="([\s\S]*?)"$/gm - ); - - for (const match of multilineMatches) { - const [, key, value] = match; - - properties[key] = value; - } - - return properties; -}; diff --git a/test/utils/templates.test.mjs b/test/utils/templates.test.mjs index 6346576..7e05127 100644 --- a/test/utils/templates.test.mjs +++ b/test/utils/templates.test.mjs @@ -1,10 +1,7 @@ import assert from 'node:assert'; import { describe, it } from 'node:test'; -import { - parseVariables, - parseMeetingProperties, -} from '../../src/utils/templates.mjs'; +import { parseVariables } from '../../src/utils/templates.mjs'; describe('Utils - Templates', () => { describe('parseVariables', () => { @@ -118,149 +115,4 @@ Line 3: Hello again` ); }); }); - - describe('parseMeetingProperties', () => { - it('should parse simple property', () => { - const content = 'NAME="John Doe"'; - - const result = parseMeetingProperties(content); - - assert.deepStrictEqual(result, { NAME: 'John Doe' }); - }); - - it('should parse multiple properties', () => { - const content = `NAME="John Doe" -EMAIL="john@example.com" -ROLE="Developer"`; - - const result = parseMeetingProperties(content); - - assert.deepStrictEqual(result, { - NAME: 'John Doe', - EMAIL: 'john@example.com', - ROLE: 'Developer', - }); - }); - - it('should parse multiline property values', () => { - const content = `DESCRIPTION="This is a -multiline description -with several lines"`; - - const result = parseMeetingProperties(content); - - assert.deepStrictEqual(result, { - DESCRIPTION: 'This is a\nmultiline description\nwith several lines', - }); - }); - - it('should parse properties with underscores and numbers', () => { - const content = `VAR_1="value1" -VAR_2_TEST="value2" -VAR3="value3"`; - - const result = parseMeetingProperties(content); - - assert.deepStrictEqual(result, { - VAR_1: 'value1', - VAR_2_TEST: 'value2', - VAR3: 'value3', - }); - }); - - it('should handle empty property values', () => { - const content = 'EMPTY=""'; - - const result = parseMeetingProperties(content); - - assert.deepStrictEqual(result, { EMPTY: '' }); - }); - - it('should handle properties with special characters in values', () => { - const content = 'SPECIAL="Value with $pecial ch@rs & symbols!"'; - - const result = parseMeetingProperties(content); - - assert.deepStrictEqual(result, { - SPECIAL: 'Value with $pecial ch@rs & symbols!', - }); - }); - - it('should handle properties with quotes in values', () => { - const content = `QUOTED="He said \\"Hello\\" to me"`; - - const result = parseMeetingProperties(content); - - assert.deepStrictEqual(result, { - QUOTED: 'He said \\"Hello\\" to me', - }); - }); - - it('should handle content with no properties', () => { - const content = 'Just some text without properties'; - - const result = parseMeetingProperties(content); - - assert.deepStrictEqual(result, {}); - }); - - it('should handle empty content', () => { - const content = ''; - - const result = parseMeetingProperties(content); - - assert.deepStrictEqual(result, {}); - }); - - it('should handle mixed content with properties and other text', () => { - const content = `Some random text -NAME="John Doe" -More text here -EMAIL="john@example.com" -Final text`; - - const result = parseMeetingProperties(content); - - assert.deepStrictEqual(result, { - NAME: 'John Doe', - EMAIL: 'john@example.com', - }); - }); - - it('should handle properties with markdown-like content', () => { - const content = `DESCRIPTION="# Meeting Notes - -## Agenda -- Item 1 -- Item 2 - -**Important**: Don't forget!"`; - - const result = parseMeetingProperties(content); - - assert.deepStrictEqual(result, { - DESCRIPTION: `# Meeting Notes - -## Agenda -- Item 1 -- Item 2 - -**Important**: Don't forget!`, - }); - }); - - it('should handle properties with URLs and special formatting', () => { - const content = `LINK="https://example.com/path?param=value&other=123" -INSTRUCTIONS="Join at: https://zoom.us/j/123456789 -Passcode: 123456"`; - - const result = parseMeetingProperties(content); - - assert.deepStrictEqual(result, { - LINK: 'https://example.com/path?param=value&other=123', - INSTRUCTIONS: `Join at: https://zoom.us/j/123456789 -Passcode: 123456`, - }); - }); - }); }); From e402b702984d11edc8305282b6fe555c8f37945a Mon Sep 17 00:00:00 2001 From: Claudio Wunder Date: Sun, 28 Sep 2025 22:29:27 +0200 Subject: [PATCH 45/48] hotfix: remove a console.log --- create-node-meeting-artifacts.mjs | 2 -- 1 file changed, 2 deletions(-) diff --git a/create-node-meeting-artifacts.mjs b/create-node-meeting-artifacts.mjs index 1dc404f..70a0bd1 100644 --- a/create-node-meeting-artifacts.mjs +++ b/create-node-meeting-artifacts.mjs @@ -64,8 +64,6 @@ const hackmdNote = await hackmd.createMeetingNotesDocument( const minutesDocLink = hackmdNote.publishLink || `https://hackmd.io/${hackmdNote.id}`; -console.log({ hackmdNote, minutesDocLink }); - // Step 13: Generate meeting issue content using native implementation const issueContent = await meetings.generateMeetingIssue( config, From 96688fbb61dd8b8f022fa6518ff9cbad74f02da3 Mon Sep 17 00:00:00 2001 From: Aviv Keller Date: Mon, 29 Sep 2025 14:56:13 -0400 Subject: [PATCH 46/48] Update meeting link format in council template (#187) --- templates/meeting_base_cross_project_council | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/meeting_base_cross_project_council b/templates/meeting_base_cross_project_council index 7562202..cd5c3de 100644 --- a/templates/meeting_base_cross_project_council +++ b/templates/meeting_base_cross_project_council @@ -10,7 +10,7 @@ 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: From 9e30695a6ba22ee375ae69b2f4a5ad4d6e7dacc6 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Fri, 3 Oct 2025 16:40:29 +0100 Subject: [PATCH 47/48] chore: remove mhdawson from tsc meeting invite (#190) --- templates/invited_tsc | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/invited_tsc b/templates/invited_tsc index 920acec..27d09bf 100644 --- a/templates/invited_tsc +++ b/templates/invited_tsc @@ -8,7 +8,6 @@ * Chengzhong Wu @legendecas (voting member) * Marco Ippolito @marco-ippolito (voting member) * Matteo Collina @mcollina (voting member) -* Michael Dawson @mhdawson (voting member) * Filip Skokan @panva (voting member) * Rafael Gonzaga @RafaelGSS (voting member) * Darshan Sen @RaisinTen (voting member) From 1cfb39034cce9300780ee5b06b87d85a5685910c Mon Sep 17 00:00:00 2001 From: Richard Lau Date: Sat, 4 Oct 2025 14:53:57 +0100 Subject: [PATCH 48/48] Update templates (#191) --- TEMPLATES_DOCUMENTATION.md | 8 ++++---- templates/meeting_issue.md | 2 +- templates/minutes_base_Release | 4 ++-- templates/minutes_base_benchmarking | 4 ++-- templates/minutes_base_build | 4 ++-- templates/minutes_base_cross_project_council | 2 +- templates/minutes_base_diag | 4 ++-- templates/minutes_base_diag_deepdive | 4 ++-- templates/minutes_base_ecosystem_report | 4 ++-- templates/minutes_base_modules | 2 +- templates/minutes_base_next-10 | 2 +- templates/minutes_base_outreach | 4 ++-- templates/minutes_base_package-maintenance | 4 ++-- templates/minutes_base_package_metadata_interop | 4 ++-- templates/minutes_base_security-wg | 4 ++-- templates/minutes_base_security_collab | 2 +- templates/minutes_base_standards | 2 +- templates/minutes_base_sustainability_collab | 4 ++-- templates/minutes_base_tooling | 4 ++-- templates/minutes_base_tsc | 4 ++-- templates/minutes_base_typescript | 2 +- templates/minutes_base_userfeedback | 4 ++-- templates/minutes_base_uvwasi | 2 +- templates/minutes_base_web | 2 +- templates/minutes_base_web-server-frameworks | 2 +- 25 files changed, 42 insertions(+), 42 deletions(-) diff --git a/TEMPLATES_DOCUMENTATION.md b/TEMPLATES_DOCUMENTATION.md index de16d8e..279950f 100644 --- a/TEMPLATES_DOCUMENTATION.md +++ b/TEMPLATES_DOCUMENTATION.md @@ -94,7 +94,7 @@ A basic outline for the meeting minutes to be autogenerated in Google Docs. The * **Recording**: * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -115,7 +115,7 @@ $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. ``` @@ -130,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 @@ -151,7 +151,7 @@ $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 diff --git a/templates/meeting_issue.md b/templates/meeting_issue.md index f4d39b6..15716c1 100644 --- a/templates/meeting_issue.md +++ b/templates/meeting_issue.md @@ -13,7 +13,7 @@ Or in your local time: ## Links -* Minutes Google Doc: <$MINUTES_DOC$> +* Minutes: <$MINUTES_DOC$> ## Agenda 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_cross_project_council b/templates/minutes_base_cross_project_council index 3791f64..5768faa 100644 --- a/templates/minutes_base_cross_project_council +++ b/templates/minutes_base_cross_project_council @@ -47,4 +47,4 @@ Please review regularly our list of dates and reminders, our quarterly review is - **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 index 7682e1c..280bbc7 100644 --- a/templates/minutes_base_ecosystem_report +++ b/templates/minutes_base_ecosystem_report @@ -1,7 +1,7 @@ ## Links * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -21,4 +21,4 @@ $AGENDA_CONTENT$ * **OpenJS Foundation Calendar**: https://calendar.google.com/calendar/u/0/embed?src=linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com -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_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_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 index 0c8ecca..26c9ac8 100644 --- a/templates/minutes_base_sustainability_collab +++ b/templates/minutes_base_sustainability_collab @@ -1,7 +1,7 @@ ## Links * **GitHub Issue**: $GITHUB_ISSUE$ -* **Minutes Google Doc**: $MINUTES_DOC$ +* **Minutes**: $MINUTES_DOC$ ## Present @@ -21,4 +21,4 @@ $AGENDA_CONTENT$ * **OpenJS Foundation Calendar**: https://calendar.google.com/calendar/u/0/embed?src=linuxfoundation.org_fuop4ufv766f9avc517ujs4i0g@group.calendar.google.com -Click `+GoogleCalendar` at the bottom right to add to your own Google calendar. \ No newline at end of file +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 8830613..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 @@ -31,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_typescript b/templates/minutes_base_typescript index 93b305a..f921f8e 100644 --- a/templates/minutes_base_typescript +++ b/templates/minutes_base_typescript @@ -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_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 index 21c14b2..00467bd 100644 --- a/templates/minutes_base_web +++ b/templates/minutes_base_web @@ -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-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.