Compare commits

..

45 Commits

Author SHA1 Message Date
codegen-sh[bot] 5384ee8aae Fix: Table column widths are not retained in copy/paste 2025-03-30 21:46:27 +00:00
codegen-sh[bot] 97c3ea7da8 Allow inline code to be bolded and italicized (#8843)
Co-authored-by: codegen-sh[bot] <131295404+codegen-sh[bot]@users.noreply.github.com>
2025-03-30 14:44:21 -07:00
Tom Moor 4af2b032dd fix: New comments are measured incorrectly (#8838)
* fix: New comments are measured incorrectly

* Remove defaultRect so we can always return a DOMRect
2025-03-30 11:48:51 -07:00
Tom Moor c52d9a850d fix: Paste partially over code prevents pasting PM nodes (#8836)
* fix: Paste over any inline code prevents pasting nodes
closes #8825

* Add inclusive logic for isNodeActive
2025-03-30 11:48:44 -07:00
Tom Moor 588e5bc17f fix: Reduce gap between at symbol and name in user mentions (#8839) 2025-03-30 17:26:35 +00:00
Tom Moor a2bd0edd82 chore: Missing react key in SuggestionMenu (#8837) 2025-03-30 14:36:15 +00:00
Tom Moor ca0f0638c9 fix: Handle deleted user in NotificationHelper (#8835) 2025-03-29 19:11:04 -07:00
Tom Moor f13e6a3691 fix: Show @ symbol on mentions in email snippets (#8833) 2025-03-30 00:26:18 +00:00
Hemachandar dcb7b86df8 Store import error in DB (#8811) 2025-03-29 06:08:07 -07:00
Hemachandar 45c6e72c6d Manage collection subscriptions when user (or) group is removed from a collection (#8821)
* Manage collection subscriptions when user (or) group is removed from a collection

* rename collection task

* rename document task

* remove unnecessary actor filter
2025-03-29 06:07:57 -07:00
codegen-sh[bot] a51456deb3 Add missing JSDoc to shared components (#8829)
Co-authored-by: codegen-sh[bot] <131295404+codegen-sh[bot]@users.noreply.github.com>
2025-03-28 14:27:30 -07:00
Aditya Sharma 3ffe7e7671 fix: conversion b/w checkbox & other list types (#8828) 2025-03-28 14:19:12 -07:00
Hemachandar a7fe6c9af3 Include non-deleted imports for cleanup (#8822) 2025-03-28 05:46:36 -07:00
codegen-sh[bot] 52c673261b Add JSDoc to hooks in app/hooks directory (#8819)
* Add JSDoc to hooks in app/hooks directory

* lint

---------

Co-authored-by: codegen-sh[bot] <131295404+codegen-sh[bot]@users.noreply.github.com>
Co-authored-by: Tom Moor <tom.moor@gmail.com>
2025-03-28 02:37:32 +00:00
Tom Moor 60c0a53a1f chore: Change lint rule to trigger on actor rather than branch name (#8820) 2025-03-28 02:24:00 +00:00
Tom Moor 66fae19034 fix: Improve performance of notification queries (#8809)
* Remove onlySubscribers

* refactor

* perf
2025-03-27 19:10:32 -07:00
codegen-sh[bot] 37ea6bb92b Add JSDoc comments to AvatarWithPresence component (#8817)
* Add JSDoc comments to AvatarWithPresence component

* lint

---------

Co-authored-by: codegen-sh[bot] <131295404+codegen-sh[bot]@users.noreply.github.com>
Co-authored-by: Tom Moor <tom@getoutline.com>
2025-03-27 19:07:24 -07:00
Tom Moor 762816adbc Update lint.yml (#8818) 2025-03-28 01:58:24 +00:00
Tom Moor d1b24b15d5 chore: Attempt auto-lint of Codegen PR's (#8816) 2025-03-28 01:42:28 +00:00
Hemachandar 877b7ad0df fix: Handle index collision when creating a collection (#8803)
* fix: Handle index collision when creating a collection

* move to sequelize hooks

* index maxLen parity between api and model

* remove beforeUpdate hook

* use common indexLen in model

* beforeUpdate hook..

* test
2025-03-27 02:50:40 -07:00
Tom Moor e98d931aaa Remove maintainers from probot behavior (#8808) 2025-03-26 23:37:59 +00:00
Tom Moor ba7d102a72 perf: Avoid querying all users in team for common notification types (#8806) 2025-03-26 16:19:45 -07:00
codegen-sh[bot] ab1f00e919 fix: handle missing user error during Notion import (#8801)
* fix: handle missing user error during Notion import

* lint

* typesafe check

---------

Co-authored-by: codegen-sh[bot] <131295404+codegen-sh[bot]@users.noreply.github.com>
Co-authored-by: hmacr <hmac.devo@gmail.com>
2025-03-26 07:46:53 -07:00
dependabot[bot] 34cb31ff43 chore(deps): bump ioredis from 5.4.1 to 5.6.0 (#8789)
Bumps [ioredis](https://github.com/luin/ioredis) from 5.4.1 to 5.6.0.
- [Release notes](https://github.com/luin/ioredis/releases)
- [Changelog](https://github.com/redis/ioredis/blob/main/CHANGELOG.md)
- [Commits](https://github.com/luin/ioredis/compare/v5.4.1...v5.6.0)

---
updated-dependencies:
- dependency-name: ioredis
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-26 05:48:58 -07:00
codegen-sh[bot] aac95c2b2e Add SMTP_SERVICE environment variable for well-known services (#8781)
* Add SMTP_SERVICE environment variable for well-known services

* Fix PR #8777: Restore code in teams.ts and users.ts

* The rest of the work

* fix validation

---------

Co-authored-by: codegen-sh[bot] <131295404+codegen-sh[bot]@users.noreply.github.com>
Co-authored-by: Tom Moor <tom.moor@gmail.com>
2025-03-26 05:48:47 -07:00
dependabot[bot] 0dd6ef5196 chore(deps): bump vite from 5.4.14 to 5.4.15 (#8798)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.14 to 5.4.15.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v5.4.15/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v5.4.15/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-26 01:35:02 +00:00
dependabot[bot] 5cd11002d1 chore(deps): bump the aws group with 5 updates (#8788)
Bumps the aws group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3) | `3.772.0` | `3.774.0` |
| [@aws-sdk/lib-storage](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/lib/lib-storage) | `3.772.0` | `3.774.0` |
| [@aws-sdk/s3-presigned-post](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages/s3-presigned-post) | `3.772.0` | `3.774.0` |
| [@aws-sdk/s3-request-presigner](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages/s3-request-presigner) | `3.772.0` | `3.774.0` |
| [@aws-sdk/signature-v4-crt](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages/signature-v4-crt) | `3.758.0` | `3.774.0` |


Updates `@aws-sdk/client-s3` from 3.772.0 to 3.774.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.774.0/clients/client-s3)

Updates `@aws-sdk/lib-storage` from 3.772.0 to 3.774.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/lib/lib-storage/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.774.0/lib/lib-storage)

Updates `@aws-sdk/s3-presigned-post` from 3.772.0 to 3.774.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages/s3-presigned-post/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.774.0/packages/s3-presigned-post)

Updates `@aws-sdk/s3-request-presigner` from 3.772.0 to 3.774.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages/s3-request-presigner/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.774.0/packages/s3-request-presigner)

Updates `@aws-sdk/signature-v4-crt` from 3.758.0 to 3.774.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages/signature-v4-crt/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.774.0/packages/signature-v4-crt)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-s3"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: aws
- dependency-name: "@aws-sdk/lib-storage"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: aws
- dependency-name: "@aws-sdk/s3-presigned-post"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: aws
- dependency-name: "@aws-sdk/s3-request-presigner"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: aws
- dependency-name: "@aws-sdk/signature-v4-crt"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: aws
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-25 18:29:29 -07:00
dependabot[bot] 5334f7ae08 chore(deps-dev): bump @types/node from 20.17.16 to 20.17.27 (#8790)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 20.17.16 to 20.17.27.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-25 18:29:18 -07:00
dependabot[bot] df1de2b822 chore(deps): bump zod from 3.23.8 to 3.24.2 (#8791)
Bumps [zod](https://github.com/colinhacks/zod) from 3.23.8 to 3.24.2.
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Changelog](https://github.com/colinhacks/zod/blob/main/CHANGELOG.md)
- [Commits](https://github.com/colinhacks/zod/compare/v3.23.8...v3.24.2)

---
updated-dependencies:
- dependency-name: zod
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-25 18:29:08 -07:00
dependabot[bot] deb93ef767 chore(deps): bump pg from 8.12.0 to 8.14.1 (#8792)
Bumps [pg](https://github.com/brianc/node-postgres/tree/HEAD/packages/pg) from 8.12.0 to 8.14.1.
- [Changelog](https://github.com/brianc/node-postgres/blob/master/CHANGELOG.md)
- [Commits](https://github.com/brianc/node-postgres/commits/pg@8.14.1/packages/pg)

---
updated-dependencies:
- dependency-name: pg
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-25 18:28:59 -07:00
Tom Moor 5bef4c4b55 fix: typeerror cannot read properties of undefined reading lang (#8794)
* fix: Access of undefined with invalid code lang
closes #8793

* test
2025-03-25 12:31:53 +00:00
Tom Moor 72bff1ec8a Revert "Change @aws-sdk dependency update frequency from weekly to monthly (#…" (#8787)
This reverts commit 323c5f5978.
2025-03-25 11:58:21 +00:00
Tom Moor c12b257098 fix: Use configured proxy for OIDC server-to-server requests (#8776) 2025-03-25 04:31:16 -07:00
Hemachandar f6da244c33 fix: Handle empty text blocks from Notion response (#8785) 2025-03-25 04:31:06 -07:00
Tom Moor ab55e0bed9 feat: Add XML as code formatting option (#8767)
Refactor to achieve this
2025-03-24 14:58:05 -07:00
dependabot[bot] 84ae9a2c31 chore(deps): bump datadog-metrics from 0.11.2 to 0.12.1 (#8773)
* chore(deps): bump datadog-metrics from 0.11.2 to 0.12.1

Bumps [datadog-metrics](https://github.com/dbader/node-datadog-metrics) from 0.11.2 to 0.12.1.
- [Release notes](https://github.com/dbader/node-datadog-metrics/releases)
- [Commits](https://github.com/dbader/node-datadog-metrics/compare/v0.11.2...v0.12.1)

---
updated-dependencies:
- dependency-name: datadog-metrics
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix: flush now returns a promise

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Tom Moor <tom.moor@gmail.com>
2025-03-24 21:38:54 +00:00
Hemachandar 5c4eb32c26 fix: Release redis lock only when it hasn't expired (#8765)
* fix: Suppress redlock release errors

* release only when lock hasn't expired
2025-03-24 14:37:36 -07:00
dependabot[bot] 10b8f11e0b chore(deps): bump the babel group with 4 updates (#8769)
Bumps the babel group with 4 updates: [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core), [@babel/plugin-transform-regenerator](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-regenerator), [@babel/cli](https://github.com/babel/babel/tree/HEAD/packages/babel-cli) and [@babel/preset-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-typescript).


Updates `@babel/core` from 7.26.9 to 7.26.10
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.10/packages/babel-core)

Updates `@babel/plugin-transform-regenerator` from 7.25.9 to 7.27.0
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.27.0/packages/babel-plugin-transform-regenerator)

Updates `@babel/cli` from 7.26.4 to 7.27.0
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.27.0/packages/babel-cli)

Updates `@babel/preset-typescript` from 7.26.0 to 7.27.0
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.27.0/packages/babel-preset-typescript)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: babel
- dependency-name: "@babel/plugin-transform-regenerator"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: babel
- dependency-name: "@babel/cli"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: babel
- dependency-name: "@babel/preset-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: babel
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-24 14:27:21 -07:00
dependabot[bot] 0a4c3bd633 chore(deps): bump the aws group with 4 updates (#8770)
Bumps the aws group with 4 updates: [@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3), [@aws-sdk/lib-storage](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/lib/lib-storage), [@aws-sdk/s3-presigned-post](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages/s3-presigned-post) and [@aws-sdk/s3-request-presigner](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/packages/s3-request-presigner).


Updates `@aws-sdk/client-s3` from 3.758.0 to 3.772.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.772.0/clients/client-s3)

Updates `@aws-sdk/lib-storage` from 3.758.0 to 3.772.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/lib/lib-storage/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.772.0/lib/lib-storage)

Updates `@aws-sdk/s3-presigned-post` from 3.758.0 to 3.772.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages/s3-presigned-post/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.772.0/packages/s3-presigned-post)

Updates `@aws-sdk/s3-request-presigner` from 3.758.0 to 3.772.0
- [Release notes](https://github.com/aws/aws-sdk-js-v3/releases)
- [Changelog](https://github.com/aws/aws-sdk-js-v3/blob/main/packages/s3-request-presigner/CHANGELOG.md)
- [Commits](https://github.com/aws/aws-sdk-js-v3/commits/v3.772.0/packages/s3-request-presigner)

---
updated-dependencies:
- dependency-name: "@aws-sdk/client-s3"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: aws
- dependency-name: "@aws-sdk/lib-storage"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: aws
- dependency-name: "@aws-sdk/s3-presigned-post"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: aws
- dependency-name: "@aws-sdk/s3-request-presigner"
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: aws
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-24 14:27:13 -07:00
dependabot[bot] 580cf52fd3 chore(deps-dev): bump rollup-plugin-webpack-stats from 2.0.1 to 2.0.3 (#8771)
Bumps [rollup-plugin-webpack-stats](https://github.com/relative-ci/rollup-plugin-webpack-stats) from 2.0.1 to 2.0.3.
- [Release notes](https://github.com/relative-ci/rollup-plugin-webpack-stats/releases)
- [Commits](https://github.com/relative-ci/rollup-plugin-webpack-stats/compare/v2.0.1...v2.0.3)

---
updated-dependencies:
- dependency-name: rollup-plugin-webpack-stats
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-24 14:27:06 -07:00
dependabot[bot] ee1fd65a19 chore(deps): bump prosemirror-commands from 1.6.2 to 1.7.0 (#8772)
Bumps [prosemirror-commands](https://github.com/prosemirror/prosemirror-commands) from 1.6.2 to 1.7.0.
- [Changelog](https://github.com/ProseMirror/prosemirror-commands/blob/master/CHANGELOG.md)
- [Commits](https://github.com/prosemirror/prosemirror-commands/compare/1.6.2...1.7.0)

---
updated-dependencies:
- dependency-name: prosemirror-commands
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-24 14:26:59 -07:00
codegen-sh[bot] 323c5f5978 Change @aws-sdk dependency update frequency from weekly to monthly (#8774)
Co-authored-by: codegen-sh[bot] <131295404+codegen-sh[bot]@users.noreply.github.com>
2025-03-24 14:24:18 -07:00
Tom Moor bdb34a202c fix: Disable Notion import when env is not available (#8761) 2025-03-24 02:52:54 +00:00
Tom Moor 40278b2d9a fix: notifications.pixel requests hang (#8760)
tests
2025-03-24 00:52:54 +00:00
Tom Moor a69ef1f3c9 quick: Remove expired temporary AWS keys from fixture data (#8755)
* fix: Remove temporary AWS keys causing false positive alerts

* Previously missed PR feedback

* snap
2025-03-23 19:31:38 +00:00
79 changed files with 2103 additions and 660 deletions
+1 -5
View File
@@ -205,14 +205,10 @@ SENTRY_TUNNEL=
# To support sending outgoing transactional emails such as "document updated" or
# "you've been invited" you'll need to provide authentication for an SMTP server
SMTP_HOST=
SMTP_PORT=
SMTP_SERVICE=
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_FROM_EMAIL=
SMTP_REPLY_EMAIL=
SMTP_TLS_CIPHERS=
SMTP_SECURE=true
# The default interface language. See translate.getoutline.com for a list of
# available language codes and their rough percentage translated.
+2
View File
@@ -15,6 +15,8 @@ requestInfoDefaultTitles:
requestInfoLabelToAdd: more information needed
requestInfoUserstoExclude:
- tommoor
# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome
+30
View File
@@ -0,0 +1,30 @@
name: Lint
on:
pull_request:
branches: [ main ]
jobs:
run-linters:
if: startsWith(github.actor, 'codegen-sh')
name: Run linters
runs-on: ubuntu-latest
permissions:
# Give the default GITHUB_TOKEN write permission to commit and push the
# added or changed files to the repository.
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20.x
cache: 'yarn'
- run: yarn install --frozen-lockfile
- run: yarn lint --fix
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: 'Applied automatic fixes'
+4
View File
@@ -171,6 +171,10 @@
"description": "smtp.example.com (optional)",
"required": false
},
"SMTP_SERVICE": {
"description": "Well-known SMTP service name for nodemailer (optional, e.g. 'gmail', 'SES')",
"required": false
},
"SMTP_PORT": {
"description": "1234 (optional)",
"required": false
@@ -7,17 +7,43 @@ import User from "~/models/User";
import Tooltip from "~/components/Tooltip";
import Avatar, { AvatarSize } from "./Avatar";
/**
* Props for the AvatarWithPresence component
*/
type Props = {
/** The user to display the avatar for */
user: User;
/** Whether the user is currently present in the document */
isPresent: boolean;
/** Whether the user is currently editing the document */
isEditing: boolean;
/** Whether the user is currently observing the document */
isObserving: boolean;
/** Whether this avatar represents the current user */
isCurrentUser: boolean;
/** Optional click handler for the avatar */
onClick?: React.MouseEventHandler<HTMLImageElement>;
/** Size of the avatar, defaults to AvatarSize.Large */
size?: AvatarSize;
/** Optional inline styles to apply to the avatar wrapper */
style?: React.CSSProperties;
};
/**
* AvatarWithPresence component displays a user's avatar with visual indicators
* for their current status (present, editing, observing).
*
* The component shows different visual states:
* - Present users have full opacity
* - Non-present users have reduced opacity
* - Observing users have a colored border matching their user color
* - Hovering shows a colored border
*
* A tooltip displays the user's name and current status.
*
* @param props - Component properties
* @returns React component
*/
function AvatarWithPresence({
onClick,
user,
@@ -64,16 +90,33 @@ function AvatarWithPresence({
);
}
/**
* Centered container for tooltip content
*/
const Centered = styled.div`
text-align: center;
`;
/**
* Props for the AvatarPresence styled component
*/
type AvatarWrapperProps = {
/** Whether the user is currently present */
$isPresent: boolean;
/** Whether the user is currently observing */
$isObserving: boolean;
/** The user's color for border highlighting */
$color: string;
};
/**
* Styled component that wraps the Avatar and provides visual indicators
* for the user's presence status.
*
* - Adjusts opacity based on presence
* - Adds colored borders for observing users
* - Handles hover effects
*/
const AvatarPresence = styled.div<AvatarWrapperProps>`
opacity: ${(props) => (props.$isPresent ? 1 : 0.5)};
transition: opacity 250ms ease-in-out;
+5 -1
View File
@@ -2,6 +2,11 @@ import React from "react";
import styled from "styled-components";
import { fadeIn } from "~/styles/animations";
/**
* Fade in animation for a component.
*
* @param timing - The duration of the fade in animation, default is 250ms.
*/
const Fade = styled.span<{ timing?: number | string }>`
animation: ${fadeIn} ${(props) => props.timing || "250ms"} ease-in-out;
`;
@@ -17,7 +22,6 @@ type Props = {
*/
export const ConditionalFade = ({ animate, children }: Props) => {
const [isAnimated] = React.useState(animate);
return isAnimated ? <Fade>{children}</Fade> : <>{children}</>;
};
+2 -3
View File
@@ -645,12 +645,11 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
"section" in item ? item.section?.({ t }) : undefined;
const response = (
<>
<React.Fragment key={`${index}-${item.name}`}>
{currentHeading !== previousHeading && (
<Header key={currentHeading}>{currentHeading}</Header>
)}
<ListItem
key={index}
onPointerMove={handlePointerMove}
onPointerDown={handlePointerDown}
>
@@ -659,7 +658,7 @@ function SuggestionsMenu<T extends MenuItem>(props: Props<T>) {
onClick: () => handleClickItem(item),
})}
</ListItem>
</>
</React.Fragment>
);
previousHeading = currentHeading;
+3 -3
View File
@@ -10,8 +10,8 @@ import {
import { Decoration, DecorationSet } from "prosemirror-view";
import * as React from "react";
import { v4 } from "uuid";
import { LANGUAGES } from "@shared/editor/extensions/Prism";
import Extension, { WidgetProps } from "@shared/editor/lib/Extension";
import { codeLanguages } from "@shared/editor/lib/code";
import isMarkdown from "@shared/editor/lib/isMarkdown";
import normalizePastedMarkdown from "@shared/editor/lib/markdown/normalize";
import { isRemoteTransaction } from "@shared/editor/lib/multiplayer";
@@ -88,7 +88,7 @@ export default class PasteHandler extends Extension {
// If the users selection is currently in a code block then paste
// as plain text, ignore all formatting and HTML content.
if (isInCode(state)) {
if (isInCode(state, { inclusive: true })) {
event.preventDefault();
view.dispatch(state.tr.insertText(text));
return true;
@@ -228,7 +228,7 @@ export default class PasteHandler extends Extension {
state.tr
.replaceSelectionWith(
state.schema.nodes.code_block.create({
language: Object.keys(LANGUAGES).includes(
language: Object.keys(codeLanguages).includes(
vscodeMeta.mode
)
? vscodeMeta.mode
+13 -12
View File
@@ -2,8 +2,11 @@ import { CopyIcon, ExpandedIcon } from "outline-icons";
import { Node as ProseMirrorNode } from "prosemirror-model";
import { EditorState } from "prosemirror-state";
import * as React from "react";
import { LANGUAGES } from "@shared/editor/extensions/Prism";
import { getFrequentCodeLanguages } from "@shared/editor/lib/code";
import {
getFrequentCodeLanguages,
codeLanguages,
getLabelForLanguage,
} from "@shared/editor/lib/code";
import { MenuItem } from "@shared/editor/types";
import { Dictionary } from "~/hooks/useDictionary";
@@ -14,20 +17,19 @@ export default function codeMenuItems(
): MenuItem[] {
const node = state.selection.$from.node();
const allLanguages = Object.entries(LANGUAGES) as [
keyof typeof LANGUAGES,
string
][];
const frequentLanguages = getFrequentCodeLanguages();
const frequentLangMenuItems = frequentLanguages.map((value) => {
const label = LANGUAGES[value];
const label = codeLanguages[value]?.label;
return langToMenuItem({ node, value, label });
});
const remainingLangMenuItems = allLanguages
.filter(([value]) => !frequentLanguages.includes(value))
.map(([value, label]) => langToMenuItem({ node, value, label }));
const remainingLangMenuItems = Object.entries(codeLanguages)
.filter(
([value]) =>
!frequentLanguages.includes(value as keyof typeof codeLanguages)
)
.map(([value, item]) => langToMenuItem({ node, value, label: item.label }));
const languageMenuItems = frequentLangMenuItems.length
? [
@@ -52,8 +54,7 @@ export default function codeMenuItems(
visible: !readOnly,
name: "code_block",
icon: <ExpandedIcon />,
// @ts-expect-error We have a fallback for incorrect mapping
label: LANGUAGES[node.attrs.language ?? "none"],
label: getLabelForLanguage(node.attrs.language ?? "none"),
children: languageMenuItems,
},
];
+5
View File
@@ -1,6 +1,11 @@
import * as React from "react";
import { useTranslation } from "react-i18next";
/**
* Hook that provides a dictionary of translated UI strings.
*
* @returns An object containing all translated UI strings used throughout the application
*/
export default function useDictionary() {
const { t } = useTranslation();
+9
View File
@@ -1,6 +1,15 @@
import * as React from "react";
import useWindowSize from "./useWindowSize";
/**
* Hook to calculate the maximum height for an element based on its position and viewport size.
*
* @param options Configuration options
* @param options.elementRef A ref pointing to the element to calculate max height for
* @param options.maxViewportPercentage The maximum height of the element as a percentage of the viewport
* @param options.margin The margin to apply to the positioning
* @returns Object containing the calculated maxHeight and a function to recalculate it
*/
const useMaxHeight = ({
elementRef,
maxViewportPercentage = 90,
+6
View File
@@ -1,5 +1,11 @@
import { useState, useEffect } from "react";
/**
* Hook to check if a media query matches the current viewport.
*
* @param query The CSS media query to check against
* @returns boolean indicating whether the media query matches
*/
export default function useMediaQuery(query: string): boolean {
const [matches, setMatches] = useState<boolean>(false);
+5
View File
@@ -1,6 +1,11 @@
import { breakpoints } from "@shared/styles";
import useMediaQuery from "~/hooks/useMediaQuery";
/**
* Hook to detect if the current viewport is mobile-sized.
*
* @returns boolean indicating whether the current viewport is mobile-sized
*/
export default function useMobile(): boolean {
return useMediaQuery(`(max-width: ${breakpoints.tablet - 1}px)`);
}
+5
View File
@@ -1,6 +1,11 @@
import React from "react";
import { useLocation } from "react-router-dom";
/**
* Hook to access URL query parameters from the current location.
*
* @returns URLSearchParams object containing the current URL query parameters
*/
export default function useQuery() {
const location = useLocation();
+5
View File
@@ -2,6 +2,11 @@ import { MobXProviderContext } from "mobx-react";
import * as React from "react";
import RootStore from "~/stores";
/**
* Hook to access the MobX stores from the React context.
*
* @returns The root store containing all application stores
*/
export default function useStores() {
return React.useContext(MobXProviderContext) as typeof RootStore;
}
+5
View File
@@ -1,5 +1,10 @@
import * as React from "react";
/**
* Hook that executes a callback when the component unmounts.
*
* @param callback Function to be called on component unmount
*/
const useUnmount = (callback: (...args: Array<any>) => any) => {
const ref = React.useRef(callback);
ref.current = callback;
+6
View File
@@ -1,5 +1,11 @@
import { useLayoutEffect, useState } from "react";
/**
* Hook to get the current viewport height, accounting for mobile virtual keyboards.
* Uses the VisualViewport API when available, falling back to window.innerHeight.
*
* @returns The current viewport height in pixels
*/
export default function useViewportHeight(): number | void {
// https://developer.mozilla.org/en-US/docs/Web/API/VisualViewport#browser_compatibility
// Note: No support in Firefox at time of writing, however this mainly exists
+7
View File
@@ -13,6 +13,13 @@ const defaultOptions = {
throttle: 100,
};
/**
* Hook to track the window's scroll position.
*
* @param options Configuration options
* @param options.throttle Time in milliseconds to throttle the scroll event
* @returns Object containing the current scroll position (x, y coordinates)
*/
export default function useWindowScrollPosition(options: {
throttle: number;
}): {
+3
View File
@@ -15,6 +15,9 @@ class Import extends Model {
/** The name of the import. */
name: string;
/** Descriptive error message when the import errors out. */
error: string | null;
/** The current state of the import. */
@Field
@observable
@@ -16,6 +16,7 @@ import { useDocumentContext } from "~/components/DocumentContext";
import Facepile from "~/components/Facepile";
import Fade from "~/components/Fade";
import { ResizingHeightContainer } from "~/components/ResizingHeightContainer";
import useBoolean from "~/hooks/useBoolean";
import { useLocationSidebarContext } from "~/hooks/useLocationSidebarContext";
import useOnClickOutside from "~/hooks/useOnClickOutside";
import usePersistedState from "~/hooks/usePersistedState";
@@ -63,7 +64,7 @@ function CommentThread({
const history = useHistory();
const location = useLocation();
const sidebarContext = useLocationSidebarContext();
const [autoFocus, setAutoFocus] = React.useState(thread.isNew);
const [autoFocus, setAutoFocusOn, setAutoFocusOff] = useBoolean(thread.isNew);
const can = usePolicy(document);
@@ -156,9 +157,9 @@ function CommentThread({
React.useEffect(() => {
if (!focused && autoFocus) {
setAutoFocus(false);
setAutoFocusOff();
}
}, [focused, autoFocus]);
}, [focused, autoFocus, setAutoFocusOff]);
React.useEffect(() => {
if (focused) {
@@ -273,7 +274,7 @@ function CommentThread({
)}
</ResizingHeightContainer>
{!focused && !recessed && !draft && canReply && (
<Reply onClick={() => setAutoFocus(true)}>{t("Reply")}</Reply>
<Reply onClick={setAutoFocusOn}>{t("Reply")}</Reply>
)}
</Thread>
);
@@ -15,6 +15,7 @@ import Time from "~/components/Time";
import useCurrentUser from "~/hooks/useCurrentUser";
import useStores from "~/hooks/useStores";
import { ImportMenu } from "~/menus/ImportMenu";
import isCloudHosted from "~/utils/isCloudHosted";
type Props = {
/** Import that's displayed as list item. */
@@ -29,6 +30,10 @@ export const ImportListItem = observer(({ importModel }: Props) => {
const showProgress =
importModel.state !== ImportState.Canceled &&
importModel.state !== ImportState.Errored;
const showErrorInfo =
!isCloudHosted &&
importModel.state === ImportState.Errored &&
!!importModel.error;
const stateMap = React.useMemo(
() => ({
@@ -114,6 +119,12 @@ export const ImportListItem = observer(({ importModel }: Props) => {
subtitle={
<>
{stateMap[importModel.state]}&nbsp;&nbsp;
{showErrorInfo && (
<>
{importModel.error}
{`. ${t("Check server logs for more details.")}`}&nbsp;&nbsp;
</>
)}
{t(`{{userName}} requested`, {
userName:
user.id === importModel.createdBy.id
+19 -18
View File
@@ -48,16 +48,16 @@
"> 0.25%, not dead"
],
"dependencies": {
"@aws-sdk/client-s3": "3.758.0",
"@aws-sdk/lib-storage": "3.758.0",
"@aws-sdk/s3-presigned-post": "3.758.0",
"@aws-sdk/s3-request-presigner": "3.758.0",
"@aws-sdk/signature-v4-crt": "^3.758.0",
"@babel/core": "^7.26.9",
"@aws-sdk/client-s3": "3.774.0",
"@aws-sdk/lib-storage": "3.774.0",
"@aws-sdk/s3-presigned-post": "3.774.0",
"@aws-sdk/s3-request-presigner": "3.774.0",
"@aws-sdk/signature-v4-crt": "^3.774.0",
"@babel/core": "^7.26.10",
"@babel/plugin-proposal-decorators": "^7.25.9",
"@babel/plugin-transform-class-properties": "^7.25.9",
"@babel/plugin-transform-destructuring": "^7.25.9",
"@babel/plugin-transform-regenerator": "^7.25.9",
"@babel/plugin-transform-regenerator": "^7.27.0",
"@babel/preset-env": "^7.26.9",
"@babel/preset-react": "^7.26.3",
"@benrbray/prosemirror-math": "^0.2.2",
@@ -110,7 +110,7 @@
"copy-to-clipboard": "^3.3.3",
"core-js": "^3.37.0",
"crypto-js": "^4.2.0",
"datadog-metrics": "^0.11.2",
"datadog-metrics": "^0.12.1",
"date-fns": "^3.6.0",
"dd-trace": "^5.40.0",
"diff": "^5.2.0",
@@ -129,11 +129,12 @@
"fuzzy-search": "^3.2.1",
"glob": "^8.1.0",
"http-errors": "2.0.0",
"https-proxy-agent": "^7.0.6",
"i18next": "^22.5.1",
"i18next-fs-backend": "^2.6.0",
"i18next-http-backend": "^2.7.3",
"invariant": "^2.2.4",
"ioredis": "^5.4.1",
"ioredis": "^5.6.0",
"is-printable-key-event": "^1.0.0",
"jsdom": "^22.1.0",
"jsonwebtoken": "^9.0.0",
@@ -172,13 +173,13 @@
"passport-oauth2": "^1.8.0",
"passport-slack-oauth2": "^1.2.0",
"patch-package": "^7.0.2",
"pg": "^8.12.0",
"pg": "^8.14.1",
"pg-tsquery": "^8.4.2",
"pluralize": "^8.0.0",
"png-chunks-extract": "^1.0.0",
"polished": "^4.3.1",
"prosemirror-codemark": "^0.4.2",
"prosemirror-commands": "^1.6.2",
"prosemirror-commands": "^1.7.0",
"prosemirror-dropcursor": "^1.8.1",
"prosemirror-gapcursor": "^1.3.2",
"prosemirror-history": "^1.4.1",
@@ -247,7 +248,7 @@
"uuid": "^8.3.2",
"validator": "13.12.0",
"vaul": "^1.1.2",
"vite": "^5.4.14",
"vite": "^5.4.15",
"vite-plugin-pwa": "^0.20.3",
"winston": "^3.17.0",
"ws": "^7.5.10",
@@ -256,11 +257,11 @@
"y-protocols": "^1.0.6",
"yauzl": "^2.10.0",
"yjs": "^13.6.1",
"zod": "^3.23.8"
"zod": "^3.24.2"
},
"devDependencies": {
"@babel/cli": "^7.26.4",
"@babel/preset-typescript": "^7.26.0",
"@babel/cli": "^7.27.0",
"@babel/preset-typescript": "^7.27.0",
"@faker-js/faker": "^8.4.1",
"@relative-ci/agent": "^4.2.14",
"@testing-library/react": "^12.0.0",
@@ -295,7 +296,7 @@
"@types/markdown-it-emoji": "^2.0.4",
"@types/mime-types": "^2.1.4",
"@types/natural-sort": "^0.0.24",
"@types/node": "20.17.16",
"@types/node": "20.17.27",
"@types/node-fetch": "^2.6.9",
"@types/nodemailer": "^6.4.17",
"@types/passport-oauth2": "^1.4.17",
@@ -359,7 +360,7 @@
"prettier": "^2.8.8",
"react-refresh": "^0.14.2",
"rimraf": "^2.5.4",
"rollup-plugin-webpack-stats": "^2.0.1",
"rollup-plugin-webpack-stats": "^2.0.3",
"terser": "^5.39.0",
"typescript": "^5.7.3",
"vite-plugin-static-copy": "^0.17.0",
@@ -377,4 +378,4 @@
"prismjs": "1.30.0"
},
"version": "0.82.0"
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ import { Hook, PluginManager } from "@server/utils/PluginManager";
import config from "../plugin.json";
import router from "./auth/email";
const enabled = !!env.SMTP_HOST || env.isDevelopment;
const enabled = !!(env.SMTP_HOST || env.SMTP_SERVICE) || env.isDevelopment;
if (enabled) {
PluginManager.add({
+6 -1
View File
@@ -81,7 +81,12 @@ export const Notion = observer(() => {
}, [t, appName, oauthError]);
return (
<Button type="submit" onClick={() => redirectTo(authUrl)} neutral>
<Button
type="submit"
onClick={() => redirectTo(authUrl)}
disabled={!env.NOTION_CLIENT_ID}
neutral
>
{t("Import")}
</Button>
);
+1 -1
View File
@@ -5,8 +5,8 @@ import environment from "@server/utils/environment";
import { CannotUseWithout } from "@server/utils/validators";
class NotionPluginEnvironment extends Environment {
@IsOptional()
@Public
@IsOptional()
public NOTION_CLIENT_ID = this.toOptionalString(environment.NOTION_CLIENT_ID);
@IsOptional()
+23 -10
View File
@@ -1,4 +1,6 @@
import {
APIErrorCode,
APIResponseError,
Client,
isFullPage,
isFullPageOrDatabase,
@@ -247,19 +249,30 @@ export class NotionClient {
private async fetchUsername(userId: string) {
await this.limiter();
const user = await this.client.users.retrieve({ user_id: userId });
try {
const user = await this.client.users.retrieve({ user_id: userId });
if (user.type === "person" || !user.bot.owner) {
if (user.type === "person" || !user.bot.owner) {
return user.name;
}
// bot belongs to a user, get the user's name.
if (user.bot.owner.type === "user" && isFullUser(user.bot.owner.user)) {
return user.bot.owner.user.name;
}
// bot belongs to a workspace, fallback to bot's name.
return user.name;
} catch (error) {
// Handle the case where a user can't be found
if (
error instanceof APIResponseError &&
error.code === APIErrorCode.ObjectNotFound
) {
return "Unknown";
}
throw error;
}
// bot belongs to a user, get the user's name.
if (user.bot.owner.type === "user" && isFullUser(user.bot.owner.user)) {
return user.bot.owner.user.name;
}
// bot belongs to a workspace, fallback to bot's name.
return user.name;
}
private parseTitle(item: PageObjectResponse | DatabaseObjectResponse) {
@@ -1,12 +1,22 @@
import { Node } from "prosemirror-model";
import { ProsemirrorHelper } from "@server/models/helpers/ProsemirrorHelper";
import data from "@server/test/fixtures/notion-page.json";
import nodesWithEmptyTextNode from "@server/test/fixtures/notion-page-with-empty-text-nodes.json";
import allNodes from "@server/test/fixtures/notion-page.json";
import { NotionConverter, NotionPage } from "./NotionConverter";
describe("NotionConverter", () => {
it("converts a page", () => {
const response = NotionConverter.page({
children: data,
children: allNodes,
} as NotionPage);
expect(response).toMatchSnapshot();
expect(ProsemirrorHelper.toProsemirror(response)).toBeInstanceOf(Node);
});
it("converts a page with empty text nodes", () => {
const response = NotionConverter.page({
children: nodesWithEmptyTextNode,
} as NotionPage);
expect(response).toMatchSnapshot();
+16 -13
View File
@@ -179,11 +179,15 @@ export class NotionConverter {
}
private static bookmark(item: BookmarkBlockObjectResponse) {
const caption = item.bookmark.caption
.map(this.rich_text_to_plaintext)
.join("");
return {
type: "paragraph",
content: [
{
text: item.bookmark.caption.map(this.rich_text_to_plaintext).join(""),
text: caption || item.bookmark.url,
type: "text",
marks: [
{
@@ -221,17 +225,14 @@ export class NotionConverter {
}
private static code(item: CodeBlockObjectResponse) {
const text = item.code.rich_text.map(this.rich_text_to_plaintext).join("");
return {
type: "code_fence",
attrs: {
language: item.code.language,
},
content: [
{
type: "text",
text: item.code.rich_text.map(this.rich_text_to_plaintext).join(""),
},
],
content: text ? [{ type: "text", text }] : undefined,
};
}
@@ -364,12 +365,14 @@ export class NotionConverter {
private static equation(item: EquationBlockObjectResponse) {
return {
type: "math_block",
content: [
{
type: "text",
text: item.equation.expression,
},
],
content: item.equation.expression
? [
{
type: "text",
text: item.equation.expression,
},
]
: undefined,
};
}
@@ -1983,3 +1983,148 @@ exports[`NotionConverter converts a page 1`] = `
"type": "doc",
}
`;
exports[`NotionConverter converts a page with empty text nodes 1`] = `
{
"content": [
{
"content": [],
"type": "paragraph",
},
{
"attrs": {
"language": "javascript",
},
"content": undefined,
"type": "code_fence",
},
{
"content": [],
"type": "paragraph",
},
{
"content": [
{
"content": [
{
"text": "E",
"type": "text",
},
],
"type": "math_inline",
},
],
"type": "paragraph",
},
{
"content": [],
"type": "paragraph",
},
{
"content": [
{
"marks": [
{
"attrs": {
"href": "http://github.com/outline/",
},
"type": "link",
},
],
"text": "http://github.com/outline/",
"type": "text",
},
],
"type": "paragraph",
},
{
"content": [],
"type": "paragraph",
},
{
"content": [
{
"marks": [
{
"attrs": {
"href": "https://github.com/outline/outline",
},
"type": "link",
},
],
"text": "https://github.com/outline/outline",
"type": "text",
},
],
"type": "paragraph",
},
{
"content": [],
"type": "paragraph",
},
{
"content": undefined,
"type": "math_block",
},
{
"content": [],
"type": "paragraph",
},
{
"content": [
{
"marks": [
{
"attrs": {
"href": "https://google.com",
"title": null,
},
"type": "link",
},
],
"text": "https://google.com",
"type": "text",
},
],
"type": "paragraph",
},
{
"content": [],
"type": "paragraph",
},
{
"content": [
{
"marks": [
{
"attrs": {
"href": "https://github.com/outline/outline",
},
"type": "link",
},
],
"text": "https://github.com/outline/outline",
"type": "text",
},
],
"type": "paragraph",
},
{
"content": [],
"type": "paragraph",
},
{
"attrs": {
"href": "https://prod-files-secure.s3.us-west-2.amazonaws.com/2f3fcad6-fc32-434b-b6b2-a03ca7893c4d/49bfa851-95c1-458b-abb0-88ed591f7712/Empty_pdf.pdf",
"title": "",
},
"type": "attachment",
},
{
"content": [],
"type": "paragraph",
},
],
"type": "doc",
}
`;
+28
View File
@@ -0,0 +1,28 @@
import { HttpsProxyAgent } from "https-proxy-agent";
import OAuth2Strategy, { Strategy } from "passport-oauth2";
export class OIDCStrategy extends Strategy {
constructor(
options: OAuth2Strategy.StrategyOptionsWithRequest,
verify: OAuth2Strategy.VerifyFunctionWithRequest
) {
super(options, verify);
if (process.env.https_proxy) {
const httpsProxyAgent = new HttpsProxyAgent(process.env.https_proxy);
this._oauth2.setAgent(httpsProxyAgent);
}
}
authenticate(req: any, options: any) {
options.originalQuery = req.query;
super.authenticate(req, options);
}
authorizationParams(options: any) {
return {
...(options.originalQuery || {}),
...(super.authorizationParams?.(options) || {}),
};
}
}
+2 -16
View File
@@ -2,7 +2,6 @@ import passport from "@outlinewiki/koa-passport";
import type { Context } from "koa";
import Router from "koa-router";
import get from "lodash/get";
import { Strategy } from "passport-oauth2";
import { slugifyDomain } from "@shared/utils/domains";
import { parseEmail } from "@shared/utils/email";
import accountProvisioner from "@server/commands/accountProvisioner";
@@ -21,24 +20,11 @@ import {
} from "@server/utils/passport";
import config from "../../plugin.json";
import env from "../env";
import { OIDCStrategy } from "./OIDCStrategy";
const router = new Router();
const scopes = env.OIDC_SCOPES.split(" ");
const authorizationParams = Strategy.prototype.authorizationParams;
Strategy.prototype.authorizationParams = function (options) {
return {
...(options.originalQuery || {}),
...(authorizationParams.bind(this)(options) || {}),
};
};
const authenticate = Strategy.prototype.authenticate;
Strategy.prototype.authenticate = function (req, options) {
options.originalQuery = req.query;
authenticate.bind(this)(req, options);
};
if (
env.OIDC_CLIENT_ID &&
env.OIDC_CLIENT_SECRET &&
@@ -48,7 +34,7 @@ if (
) {
passport.use(
config.id,
new Strategy(
new OIDCStrategy(
{
authorizationURL: env.OIDC_AUTH_URI,
tokenURL: env.OIDC_TOKEN_URI,
+12 -1
View File
@@ -33,7 +33,7 @@ export class Mailer {
transporter: Transporter | undefined;
constructor() {
if (env.SMTP_HOST) {
if (env.SMTP_HOST || env.SMTP_SERVICE) {
this.transporter = nodemailer.createTransport(this.getOptions());
}
if (useTestEmailService) {
@@ -198,6 +198,17 @@ export class Mailer {
};
private getOptions(): SMTPTransport.Options {
// nodemailer will use the service config to determine host/port
if (env.SMTP_SERVICE) {
return {
service: env.SMTP_SERVICE,
auth: {
user: env.SMTP_USERNAME,
pass: env.SMTP_PASSWORD,
},
};
}
return {
name: env.SMTP_NAME,
host: env.SMTP_HOST,
+13 -3
View File
@@ -15,7 +15,7 @@ import {
} from "class-validator";
import uniq from "lodash/uniq";
import { languages } from "@shared/i18n";
import { CannotUseWithout } from "@server/utils/validators";
import { CannotUseWith, CannotUseWithout } from "@server/utils/validators";
import Deprecated from "./models/decorators/Deprecated";
import { getArg } from "./utils/args";
import { Public, PublicEnvironmentRegister } from "./utils/decorators/Public";
@@ -291,10 +291,19 @@ export class Environment {
/**
* The host of your SMTP server for enabling emails.
*/
public SMTP_HOST = environment.SMTP_HOST;
@CannotUseWith("SMTP_SERVICE")
public SMTP_HOST = this.toOptionalString(environment.SMTP_HOST);
/**
* The service name of a well-known SMTP service for nodemailer.
* See https://community.nodemailer.com/2-0-0-beta/setup-smtp/well-known-services/
*/
@CannotUseWith("SMTP_HOST")
public SMTP_SERVICE = this.toOptionalString(environment.SMTP_SERVICE);
@Public
public EMAIL_ENABLED = !!this.SMTP_HOST || this.isDevelopment;
public EMAIL_ENABLED =
!!(this.SMTP_HOST || this.SMTP_SERVICE) || this.isDevelopment;
/**
* Optional hostname of the client, used for identifying to the server
@@ -307,6 +316,7 @@ export class Environment {
*/
@IsNumber()
@IsOptional()
@CannotUseWith("SMTP_SERVICE")
public SMTP_PORT = this.toOptionalNumber(environment.SMTP_PORT);
/**
+1 -3
View File
@@ -48,9 +48,7 @@ class Metrics {
return Promise.resolve();
}
return new Promise((resolve, reject) => {
ddMetrics.flush(resolve, reject);
});
return ddMetrics.flush();
}
}
@@ -0,0 +1,37 @@
"use strict";
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.sequelize.transaction(async transaction => {
await queryInterface.addColumn(
"imports",
"error",
{
type: Sequelize.STRING,
allowNull: true,
},
{ transaction }
);
await queryInterface.addColumn(
"import_tasks",
"error",
{
type: Sequelize.STRING,
allowNull: true,
},
{ transaction }
);
});
},
async down(queryInterface, Sequelize) {
await queryInterface.sequelize.transaction(async transaction => {
await queryInterface.removeColumn("imports", "error", { transaction });
await queryInterface.removeColumn("import_tasks", "error", {
transaction,
});
});
},
};
+45 -2
View File
@@ -1,4 +1,5 @@
/* eslint-disable lines-between-class-members */
import fractionalIndex from "fractional-index";
import find from "lodash/find";
import findIndex from "lodash/findIndex";
import remove from "lodash/remove";
@@ -11,6 +12,8 @@ import {
InferAttributes,
InferCreationAttributes,
EmptyResultError,
type CreateOptions,
type UpdateOptions,
} from "sequelize";
import {
Sequelize,
@@ -32,6 +35,8 @@ import {
BeforeDestroy,
IsDate,
AllowNull,
BeforeCreate,
BeforeUpdate,
} from "sequelize-typescript";
import isUUID from "validator/lib/isUUID";
import type { CollectionSort, ProsemirrorData } from "@shared/types";
@@ -41,7 +46,9 @@ import { sortNavigationNodes } from "@shared/utils/collections";
import slugify from "@shared/utils/slugify";
import { CollectionValidation } from "@shared/validations";
import { ValidationError } from "@server/errors";
import removeIndexCollision from "@server/utils/removeIndexCollision";
import { generateUrlId } from "@server/utils/url";
import { ValidateIndex } from "@server/validation";
import Document from "./Document";
import FileOperation from "./FileOperation";
import Group from "./Group";
@@ -217,8 +224,8 @@ class Collection extends ParanoidModel<
color: string | null;
@Length({
max: 256,
msg: `index must be 256 characters or less`,
max: ValidateIndex.maxLength,
msg: `index must be ${ValidateIndex.maxLength} characters or less`,
})
@Column
index: string | null;
@@ -324,6 +331,30 @@ class Collection extends ParanoidModel<
}
}
@BeforeCreate
static async setIndex(model: Collection, options: CreateOptions<Collection>) {
if (model.index) {
model.index = await removeIndexCollision(model.teamId, model.index, {
transaction: options.transaction,
});
return;
}
const firstCollectionForTeam = await this.findOne({
where: {
teamId: model.teamId,
},
order: [
// using LC_COLLATE:"C" because we need byte order to drive the sorting
Sequelize.literal('"collection"."index" collate "C"'),
["updatedAt", "DESC"],
],
...options,
});
model.index = fractionalIndex(null, firstCollectionForTeam?.index ?? null);
}
@AfterCreate
static async onAfterCreate(
model: Collection,
@@ -343,6 +374,18 @@ class Collection extends ParanoidModel<
});
}
@BeforeUpdate
static async checkIndex(
model: Collection,
options: UpdateOptions<Collection>
) {
if (model.index && model.changed("index")) {
model.index = await removeIndexCollision(model.teamId, model.index, {
transaction: options.transaction,
});
}
}
// associations
@BelongsTo(() => FileOperation, "importId")
+3
View File
@@ -60,6 +60,9 @@ class Import<T extends ImportableIntegrationService> extends ParanoidModel<
@Column(DataType.INTEGER)
documentCount: number;
@Column
error: string | null;
// associations
@BelongsTo(() => Integration, "integrationId")
+3
View File
@@ -45,6 +45,9 @@ class ImportTask<T extends ImportableIntegrationService> extends IdModel<
@Column(DataType.JSONB)
output: ImportTaskOutput | null;
@Column
error: string | null;
// associations
@BelongsTo(() => Import, "importId")
+1 -1
View File
@@ -190,7 +190,7 @@ class Team extends ParanoidModel<
* @return {boolean} Whether to show email login options
*/
get emailSigninEnabled(): boolean {
return this.guestSignin && (!!env.SMTP_HOST || env.isDevelopment);
return this.guestSignin && env.EMAIL_ENABLED;
}
get url() {
@@ -9,7 +9,7 @@ import NotificationHelper from "./NotificationHelper";
describe("NotificationHelper", () => {
describe("getCommentNotificationRecipients", () => {
it("should return users who have notification enabled for comment creation and are subscribed to the document in case of parent comment", async () => {
it("should only return users who have notification enabled for comment creation and are subscribed to the document in case of new thread", async () => {
const documentAuthor = await buildUser();
const document = await buildDocument({
userId: documentAuthor.id,
@@ -54,7 +54,7 @@ describe("NotificationHelper", () => {
expect(recipients[0].id).toEqual(notificationEnabledUser.id);
});
it("should return users who have notification enabled for comment creation and are in the thread in case of child comment", async () => {
it("should only return users who have notification enabled for comment creation and are in the thread in case of child comment", async () => {
const documentAuthor = await buildUser();
const document = await buildDocument({
userId: documentAuthor.id,
@@ -112,32 +112,104 @@ describe("NotificationHelper", () => {
expect(recipients.length).toEqual(1);
expect(recipients[0].id).toEqual(notificationEnabledUserInThread.id);
});
});
describe("getDocumentNotificationRecipients", () => {
it("should return all users who have notification enabled for the event", async () => {
it("should not return users who have notification disabled for comment creation and are in the thread in case of child comment", async () => {
const documentAuthor = await buildUser();
const document = await buildDocument({
userId: documentAuthor.id,
teamId: documentAuthor.teamId,
});
const notificationEnabledUser = await buildUser({
const notificationEnabledUserInThread = await buildUser({
teamId: document.teamId,
notificationSettings: { [NotificationEventType.UpdateDocument]: true },
notificationSettings: { [NotificationEventType.CreateComment]: false },
});
const notificationEnabledUserNotInThread = await buildUser({
teamId: document.teamId,
notificationSettings: { [NotificationEventType.CreateComment]: true },
});
const notificationDisabledUser = await buildUser({
teamId: document.teamId,
notificationSettings: {
[NotificationEventType.CreateComment]: false,
},
});
await Promise.all([
buildSubscription({
userId: documentAuthor.id,
documentId: document.id,
}),
buildSubscription({
userId: notificationEnabledUserInThread.id,
documentId: document.id,
}),
buildSubscription({
userId: notificationEnabledUserNotInThread.id,
documentId: document.id,
}),
buildSubscription({
userId: notificationDisabledUser.id,
documentId: document.id,
}),
]);
const parentComment = await buildComment({
documentId: document.id,
userId: notificationEnabledUserInThread.id,
});
const childComment = await buildComment({
documentId: document.id,
userId: documentAuthor.id,
parentCommentId: parentComment.id,
});
const recipients =
await NotificationHelper.getDocumentNotificationRecipients({
await NotificationHelper.getCommentNotificationRecipients(
document,
notificationType: NotificationEventType.UpdateDocument,
onlySubscribers: false,
actorId: documentAuthor.id,
});
childComment,
childComment.createdById
);
expect(recipients.length).toEqual(1);
expect(recipients[0].id).toEqual(notificationEnabledUser.id);
expect(recipients.length).toEqual(0);
});
it("should return users who have notification enabled and are in the thread but not explicitly subscribed to document", async () => {
const documentAuthor = await buildUser();
const document = await buildDocument({
userId: documentAuthor.id,
teamId: documentAuthor.teamId,
});
const notificationEnabledUserInThread = await buildUser({
teamId: document.teamId,
notificationSettings: { [NotificationEventType.CreateComment]: true },
});
await buildUser({
teamId: document.teamId,
notificationSettings: {
[NotificationEventType.CreateComment]: false,
},
});
const parentComment = await buildComment({
documentId: document.id,
userId: notificationEnabledUserInThread.id,
});
const childComment = await buildComment({
documentId: document.id,
userId: documentAuthor.id,
parentCommentId: parentComment.id,
});
const recipients =
await NotificationHelper.getCommentNotificationRecipients(
document,
childComment,
childComment.createdById
);
expect(recipients.length).toEqual(1);
expect(recipients[0].id).toEqual(notificationEnabledUserInThread.id);
});
});
describe("getDocumentNotificationRecipients", () => {
it("should return users who have subscribed to the document", async () => {
const documentAuthor = await buildUser();
const document = await buildDocument({
@@ -150,11 +222,17 @@ describe("NotificationHelper", () => {
documentId: document.id,
});
const deletedUser = await buildUser({ teamId: document.teamId });
await buildSubscription({
userId: deletedUser.id,
documentId: document.id,
});
await deletedUser.destroy();
const recipients =
await NotificationHelper.getDocumentNotificationRecipients({
document,
notificationType: NotificationEventType.UpdateDocument,
onlySubscribers: true,
actorId: documentAuthor.id,
});
@@ -178,7 +256,6 @@ describe("NotificationHelper", () => {
await NotificationHelper.getDocumentNotificationRecipients({
document,
notificationType: NotificationEventType.UpdateDocument,
onlySubscribers: true,
actorId: documentAuthor.id,
});
@@ -216,7 +293,6 @@ describe("NotificationHelper", () => {
await NotificationHelper.getDocumentNotificationRecipients({
document,
notificationType: NotificationEventType.UpdateDocument,
onlySubscribers: true,
actorId: documentAuthor.id,
});
@@ -235,20 +311,19 @@ describe("NotificationHelper", () => {
});
const notificationEnabledUser = await buildUser({
teamId: document.teamId,
notificationSettings: { [NotificationEventType.UpdateDocument]: true },
notificationSettings: { [NotificationEventType.PublishDocument]: true },
});
// suspended user
await buildUser({
suspendedAt: new Date(),
teamId: document.teamId,
notificationSettings: { [NotificationEventType.UpdateDocument]: true },
notificationSettings: { [NotificationEventType.PublishDocument]: true },
});
const recipients =
await NotificationHelper.getDocumentNotificationRecipients({
document,
notificationType: NotificationEventType.UpdateDocument,
onlySubscribers: false,
notificationType: NotificationEventType.PublishDocument,
actorId: documentAuthor.id,
});
+75 -48
View File
@@ -14,7 +14,7 @@ import {
Comment,
View,
} from "@server/models";
import { can } from "@server/policies";
import { canUserAccessDocument } from "@server/utils/permissions";
import { ProsemirrorHelper } from "./ProsemirrorHelper";
export default class NotificationHelper {
@@ -60,18 +60,12 @@ export default class NotificationHelper {
comment: Comment,
actorId: string
): Promise<User[]> => {
let recipients = await this.getDocumentNotificationRecipients({
document,
notificationType: NotificationEventType.CreateComment,
onlySubscribers: !comment.parentCommentId,
actorId,
});
let recipients: User[];
recipients = recipients.filter((recipient) =>
recipient.subscribedToEventType(NotificationEventType.CreateComment)
);
if (recipients.length > 0 && comment.parentCommentId) {
// If this is a reply to another comment, we want to notify all users
// that are involved in the thread of this comment (i.e. the original
// comment and all replies to it).
if (comment.parentCommentId) {
const contextComments = await Comment.findAll({
attributes: ["createdById", "data"],
where: {
@@ -95,13 +89,37 @@ export default class NotificationHelper {
const userIdsInThread = uniq([
...createdUserIdsInThread,
...mentionedUserIdsInThread,
]);
recipients = recipients.filter((r) => userIdsInThread.includes(r.id));
]).filter((userId) => userId !== actorId);
recipients = await User.findAll({
where: {
id: {
[Op.in]: userIdsInThread,
},
teamId: document.teamId,
},
});
recipients = recipients.filter((recipient) =>
recipient.subscribedToEventType(NotificationEventType.CreateComment)
);
} else {
recipients = await this.getDocumentNotificationRecipients({
document,
notificationType: NotificationEventType.CreateComment,
actorId,
// We will check below, this just prevents duplicate queries
disableAccessCheck: true,
});
}
const filtered: User[] = [];
for (const recipient of recipients) {
if (recipient.isSuspended) {
continue;
}
// If this recipient has viewed the document since the comment was made
// then we can avoid sending them a useless notification, yay.
const view = await View.findOne({
@@ -119,7 +137,13 @@ export default class NotificationHelper {
"processor",
`suppressing notification to ${recipient.id} because doc viewed`
);
} else {
continue;
}
// Check the recipient has access to the collection this document is in. Just
// because they are subscribed doesn't mean they still have access to read
// the document.
if (await canUserAccessDocument(recipient, document.id)) {
filtered.push(recipient);
}
}
@@ -132,74 +156,77 @@ export default class NotificationHelper {
*
* @param document The document to get recipients for.
* @param notificationType The notification type for which to find the recipients.
* @param onlySubscribers Whether to consider only the users who have active subscription to the document.
* @param actorId The id of the user that performed the action.
* @param disableAccessCheck Whether to disable the access check for the document.
* @returns A list of recipients
*/
public static getDocumentNotificationRecipients = async ({
document,
notificationType,
onlySubscribers,
actorId,
disableAccessCheck = false,
}: {
document: Document;
notificationType: NotificationEventType;
onlySubscribers: boolean;
actorId: string;
disableAccessCheck?: boolean;
}): Promise<User[]> => {
// First find all the users that have notifications enabled for this event
// type at all and aren't the one that performed the action.
let recipients = await User.findAll({
where: {
id: {
[Op.ne]: actorId,
let recipients: User[];
if (notificationType === NotificationEventType.PublishDocument) {
recipients = await User.findAll({
where: {
id: {
[Op.ne]: actorId,
},
teamId: document.teamId,
notificationSettings: {
[notificationType]: true,
},
},
teamId: document.teamId,
},
});
recipients = recipients.filter((recipient) =>
recipient.subscribedToEventType(notificationType)
);
// Filter further to only those that have a subscription to the document…
if (onlySubscribers) {
});
} else {
const subscriptions = await Subscription.findAll({
attributes: ["userId"],
where: {
userId: recipients.map((recipient) => recipient.id),
userId: {
[Op.ne]: actorId,
},
event: SubscriptionType.Document,
[Op.or]: [
{ collectionId: document.collectionId },
{ documentId: document.id },
],
},
include: [
{
association: "user",
required: true,
},
],
});
const subscribedUserIds = subscriptions.map(
(subscription) => subscription.userId
);
recipients = recipients.filter((recipient) =>
subscribedUserIds.includes(recipient.id)
);
recipients = subscriptions.map((s) => s.user);
}
recipients = recipients.filter((recipient) =>
recipient.subscribedToEventType(notificationType)
);
const filtered = [];
for (const recipient of recipients) {
if (!recipient.email || recipient.isSuspended) {
if (recipient.isSuspended) {
continue;
}
// Check the recipient has access to the collection this document is in. Just
// because they are subscribed doesn't mean they still have access to read
// the document.
const doc = await Document.findByPk(document.id, {
userId: recipient.id,
});
if (can(recipient, "read", doc)) {
if (
disableAccessCheck ||
(await canUserAccessDocument(recipient, document.id))
) {
filtered.push(recipient);
}
}
+1
View File
@@ -11,6 +11,7 @@ export default function presentImport(
service: importModel.service,
state: importModel.state,
documentCount: importModel.documentCount,
error: importModel.error,
createdBy: presentUser(importModel.createdBy),
createdById: importModel.createdById,
createdAt: importModel.createdAt,
@@ -1,44 +1,85 @@
import { Op } from "sequelize";
import { GroupUser } from "@server/models";
import { DocumentGroupEvent, DocumentUserEvent, Event } from "@server/types";
import DocumentSubscriptionTask from "../tasks/DocumentSubscriptionTask";
import {
CollectionGroupEvent,
CollectionUserEvent,
DocumentGroupEvent,
DocumentUserEvent,
Event,
} from "@server/types";
import CollectionSubscriptionRemoveUserTask from "../tasks/CollectionSubscriptionRemoveUserTask";
import DocumentSubscriptionRemoveUserTask from "../tasks/DocumentSubscriptionRemoveUserTask";
import BaseProcessor from "./BaseProcessor";
type ReceivedEvent =
| CollectionUserEvent
| CollectionGroupEvent
| DocumentUserEvent
| DocumentGroupEvent;
export default class DocumentSubscriptionProcessor extends BaseProcessor {
static applicableEvents: Event["name"][] = [
"collections.remove_user",
"collections.remove_group",
"documents.remove_user",
"documents.remove_group",
];
async perform(event: DocumentUserEvent | DocumentGroupEvent) {
async perform(event: ReceivedEvent) {
switch (event.name) {
case "collections.remove_user": {
await CollectionSubscriptionRemoveUserTask.schedule(event);
return;
}
case "collections.remove_group":
return this.handleRemoveGroupFromCollection(event);
case "documents.remove_user": {
await DocumentSubscriptionTask.schedule(event);
await DocumentSubscriptionRemoveUserTask.schedule(event);
return;
}
case "documents.remove_group":
return this.handleGroup(event);
return this.handleRemoveGroupFromDocument(event);
default:
}
}
private async handleGroup(event: DocumentGroupEvent) {
private async handleRemoveGroupFromCollection(event: CollectionGroupEvent) {
await GroupUser.findAllInBatches<GroupUser>(
{
where: {
groupId: event.modelId,
userId: {
[Op.ne]: event.actorId,
},
},
batchLimit: 10,
},
async (groupUsers) => {
await Promise.all(
groupUsers.map((groupUser) =>
DocumentSubscriptionTask.schedule({
CollectionSubscriptionRemoveUserTask.schedule({
...event,
name: "collections.remove_user",
userId: groupUser.userId,
})
)
);
}
);
}
private async handleRemoveGroupFromDocument(event: DocumentGroupEvent) {
await GroupUser.findAllInBatches<GroupUser>(
{
where: {
groupId: event.modelId,
},
batchLimit: 10,
},
async (groupUsers) => {
await Promise.all(
groupUsers.map((groupUser) =>
DocumentSubscriptionRemoveUserTask.schedule({
...event,
name: "documents.remove_user",
userId: groupUser.userId,
+38 -24
View File
@@ -49,33 +49,46 @@ export default abstract class ImportsProcessor<
* @param event The import event
*/
public async perform(event: ImportEvent) {
await sequelize.transaction(async (transaction) => {
const importModel = await Import.findByPk<Import<T>>(event.modelId, {
rejectOnEmpty: true,
paranoid: false,
transaction,
lock: transaction.LOCK.UPDATE,
try {
await sequelize.transaction(async (transaction) => {
const importModel = await Import.findByPk<Import<T>>(event.modelId, {
rejectOnEmpty: true,
paranoid: false,
transaction,
lock: transaction.LOCK.UPDATE,
});
if (
!this.canProcess(importModel) ||
importModel.state === ImportState.Errored ||
importModel.state === ImportState.Canceled
) {
return;
}
switch (event.name) {
case "imports.create":
return this.onCreation(importModel, transaction);
case "imports.processed":
return this.onProcessed(importModel, transaction);
case "imports.delete":
return this.onDeletion(importModel, event, transaction);
}
});
if (
!this.canProcess(importModel) ||
importModel.state === ImportState.Errored ||
importModel.state === ImportState.Canceled
) {
return;
} catch (err) {
if (event.name !== "imports.delete" && err instanceof Error) {
const importModel = await Import.findByPk<Import<T>>(event.modelId, {
rejectOnEmpty: true,
paranoid: false,
});
importModel.error = truncate(err.message, { length: 255 });
await importModel.save();
}
switch (event.name) {
case "imports.create":
return this.onCreation(importModel, transaction);
case "imports.processed":
return this.onProcessed(importModel, transaction);
case "imports.delete":
return this.onDeletion(importModel, event, transaction);
}
});
throw err; // throw error for retry.
}
}
public async onFailed(event: ImportEvent) {
@@ -173,6 +186,7 @@ export default abstract class ImportsProcessor<
}
importModel.state = ImportState.Completed;
importModel.error = null; // unset any error from previous attempts.
await importModel.saveWithCtx(
createContext({
user: importModel.createdBy,
+24 -12
View File
@@ -1,5 +1,6 @@
import { JobOptions } from "bull";
import chunk from "lodash/chunk";
import truncate from "lodash/truncate";
import uniqBy from "lodash/uniqBy";
import { Fragment, Node } from "prosemirror-model";
import { Transaction, WhereOptions } from "sequelize";
@@ -63,20 +64,29 @@ export default abstract class APIImportTask<
return;
}
switch (importTask.state) {
case ImportTaskState.Created: {
importTask.state = ImportTaskState.InProgress;
importTask = await importTask.save();
return await this.onProcess(importTask);
try {
switch (importTask.state) {
case ImportTaskState.Created: {
importTask.state = ImportTaskState.InProgress;
importTask = await importTask.save();
return await this.onProcess(importTask);
}
case ImportTaskState.InProgress:
return await this.onProcess(importTask);
case ImportTaskState.Completed:
return await this.onCompletion(importTask);
default:
}
} catch (err) {
if (err instanceof Error) {
importTask.error = truncate(err.message, { length: 255 });
await importTask.save();
}
case ImportTaskState.InProgress:
return await this.onProcess(importTask);
case ImportTaskState.Completed:
return await this.onCompletion(importTask);
default:
throw err; // throw error for retry.
}
}
@@ -108,6 +118,7 @@ export default abstract class APIImportTask<
await importTask.save({ transaction });
const associatedImport = importTask.import;
associatedImport.error = importTask.error; // copy error from ImportTask that caused the failure.
associatedImport.state = ImportState.Errored;
await associatedImport.saveWithCtx(
createContext({
@@ -155,6 +166,7 @@ export default abstract class APIImportTask<
importTask.output = taskOutputWithReplacements;
importTask.state = ImportTaskState.Completed;
importTask.error = null; // unset any error from previous attempts.
await importTask.save({ transaction });
const associatedImport = importTask.import;
@@ -41,6 +41,7 @@ export default class CleanupOldImportsTask extends BaseTask<Props> {
],
batchLimit: 50,
totalLimit: maxImportsPerTask,
paranoid: false,
},
async (imports) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -0,0 +1,52 @@
import { Transaction } from "sequelize";
import { SubscriptionType } from "@shared/types";
import { createContext } from "@server/context";
import Logger from "@server/logging/Logger";
import { Collection, Subscription, User } from "@server/models";
import { can } from "@server/policies";
import { sequelize } from "@server/storage/database";
import { CollectionUserEvent } from "@server/types";
import BaseTask from "./BaseTask";
export default class CollectionSubscriptionRemoveUserTask extends BaseTask<CollectionUserEvent> {
public async perform(event: CollectionUserEvent) {
const user = await User.findByPk(event.userId);
if (!user) {
return;
}
const collection = await Collection.scope({
method: ["withMembership", user.id],
}).findByPk(event.collectionId);
if (can(user, "read", collection)) {
Logger.debug(
"task",
`Skip unsubscribing user ${user.id} as they have permission to the collection ${event.collectionId} through other means`
);
return;
}
await sequelize.transaction(async (transaction) => {
const subscription = await Subscription.findOne({
where: {
userId: user.id,
collectionId: event.collectionId,
event: SubscriptionType.Document,
},
transaction,
lock: Transaction.LOCK.UPDATE,
});
await subscription?.destroyWithCtx(
createContext({
user,
authType: event.authType,
ip: event.ip,
transaction,
})
);
});
}
}
@@ -54,7 +54,6 @@ export default class DocumentPublishedNotificationsTask extends BaseTask<Documen
await NotificationHelper.getDocumentNotificationRecipients({
document,
notificationType: NotificationEventType.PublishDocument,
onlySubscribers: false,
actorId: document.lastModifiedById,
})
).filter((recipient) => !userIdsMentioned.includes(recipient.id));
@@ -8,11 +8,11 @@ import { sequelize } from "@server/storage/database";
import { DocumentUserEvent } from "@server/types";
import BaseTask from "./BaseTask";
export default class DocumentSubscriptionTask extends BaseTask<DocumentUserEvent> {
export default class DocumentSubscriptionRemoveUserTask extends BaseTask<DocumentUserEvent> {
public async perform(event: DocumentUserEvent) {
const user = await User.findByPk(event.userId);
if (!user || event.name !== "documents.remove_user") {
if (!user) {
return;
}
@@ -56,11 +56,13 @@ export default class ErrorTimedOutImportsTask extends BaseTask<Props> {
await sequelize.transaction(async (transaction) => {
importTask.state = ImportTaskState.Errored;
importTask.error = "Timed out";
await importTask.save({ transaction });
// this import could have been seen before in another import_task.
if (!importsErrored[associatedImport.id]) {
associatedImport.state = ImportState.Errored;
associatedImport.error = "Timed out";
await associatedImport.save({ transaction });
importsErrored[associatedImport.id] = true;
}
@@ -76,7 +76,6 @@ export default class RevisionCreatedNotificationsTask extends BaseTask<RevisionE
await NotificationHelper.getDocumentNotificationRecipients({
document,
notificationType: NotificationEventType.UpdateDocument,
onlySubscribers: true,
actorId: document.lastModifiedById,
})
).filter((recipient) => !userIdsMentioned.includes(recipient.id));
@@ -1327,6 +1327,32 @@ describe("#collections.create", () => {
expect(body.policies[0].abilities.read).toBeTruthy();
});
it("should ensure unique index across the team", async () => {
const team = await buildTeam();
const [adminA, adminB] = await Promise.all([
buildAdmin({ teamId: team.id }),
buildAdmin({ teamId: team.id }),
]);
const resA = await server.post("/api/collections.create", {
body: {
token: adminA.getJwtToken(),
name: "Test A",
},
});
const resB = await server.post("/api/collections.create", {
body: {
token: adminB.getJwtToken(),
name: "Test B",
},
});
const [bodyA, bodyB] = await Promise.all([resA.json(), resB.json()]);
expect(resA.status).toEqual(200);
expect(resB.status).toEqual(200);
expect(bodyA.data.index).not.toEqual(bodyB.data.index);
});
it("if index collision, should updated index of other collection", async () => {
const user = await buildUser();
const createdCollectionAResponse = await server.post(
+16 -22
View File
@@ -1,4 +1,3 @@
import fractionalIndex from "fractional-index";
import invariant from "invariant";
import Router from "koa-router";
import { Sequelize, Op, WhereOptions } from "sequelize";
@@ -42,7 +41,6 @@ import {
import { APIContext } from "@server/types";
import { RateLimiterStrategy } from "@server/utils/RateLimiter";
import { collectionIndexing } from "@server/utils/indexing";
import removeIndexCollision from "@server/utils/removeIndexCollision";
import pagination from "../middlewares/pagination";
import * as T from "./schema";
@@ -55,23 +53,21 @@ router.post(
transaction(),
async (ctx: APIContext<T.CollectionsCreateReq>) => {
const { transaction } = ctx.state;
const { name, color, description, data, permission, sharing, icon, sort } =
ctx.input.body;
let { index } = ctx.input.body;
const {
name,
color,
description,
data,
permission,
sharing,
icon,
sort,
index,
} = ctx.input.body;
const { user } = ctx.state.auth;
authorize(user, "createCollection", user.team);
if (index) {
index = await removeIndexCollision(user.teamId, index, { transaction });
} else {
const first = await Collection.findFirstCollectionForUser(user, {
attributes: ["id", "index"],
transaction,
});
index = fractionalIndex(null, first ? first.index : null);
}
const collection = Collection.build({
name,
content: data,
@@ -959,18 +955,16 @@ router.post(
transaction(),
async (ctx: APIContext<T.CollectionsMoveReq>) => {
const { transaction } = ctx.state;
const { id } = ctx.input.body;
let { index } = ctx.input.body;
const { id, index } = ctx.input.body;
const { user } = ctx.state.auth;
const collection = await Collection.findByPk(id, {
let collection = await Collection.findByPk(id, {
transaction,
lock: transaction.LOCK.UPDATE,
});
authorize(user, "move", collection);
index = await removeIndexCollision(user.teamId, index, { transaction });
await collection.update(
collection = await collection.update(
{
index,
},
@@ -982,14 +976,14 @@ router.post(
name: "collections.move",
collectionId: collection.id,
data: {
index,
index: collection.index,
},
});
ctx.body = {
success: true,
data: {
index,
index: collection.index,
},
};
}
@@ -1,3 +1,5 @@
import queryString from "query-string";
import { v4 as uuidv4 } from "uuid";
import { randomElement } from "@shared/random";
import { NotificationEventType } from "@shared/types";
import {
@@ -273,6 +275,74 @@ describe("#notifications.list", () => {
});
});
describe("#notifications.pixel", () => {
it("should mark notification as viewed", async () => {
const team = await buildTeam();
const user = await buildUser({ teamId: team.id });
const actor = await buildUser({
teamId: team.id,
});
const notification = await buildNotification({
teamId: team.id,
userId: user.id,
actorId: actor.id,
event: NotificationEventType.UpdateDocument,
});
expect(notification.viewedAt).toBeNull();
const res = await server.get(
`/api/notifications.pixel?${queryString.stringify({
id: notification.id,
token: notification.pixelToken,
})}`
);
expect(res.status).toBe(200);
expect(res.headers.get("content-type")).toBe("image/gif");
const reloaded = await notification.reload();
expect(reloaded.viewedAt).not.toBeNull();
});
it("should not mark notification as viewed with invalid token", async () => {
const team = await buildTeam();
const user = await buildUser({ teamId: team.id });
const actor = await buildUser({
teamId: team.id,
});
const notification = await buildNotification({
teamId: team.id,
userId: user.id,
actorId: actor.id,
event: NotificationEventType.UpdateDocument,
});
const res = await server.get(
`/api/notifications.pixel?${queryString.stringify({
id: notification.id,
token: "invalid-token",
})}`
);
expect(res.status).toBe(401);
const reloaded = await notification.reload();
expect(reloaded.viewedAt).toBeNull();
});
it("should return 404 for notification that does not exist", async () => {
const res = await server.get(
`/api/notifications.pixel?${queryString.stringify({
id: uuidv4(),
token: "invalid-token",
})}`
);
expect(res.status).toBe(404);
});
});
describe("#notifications.update", () => {
it("should mark notification as viewed", async () => {
const team = await buildTeam();
@@ -5,6 +5,7 @@ import isNull from "lodash/isNull";
import isUndefined from "lodash/isUndefined";
import { WhereOptions, Op } from "sequelize";
import { NotificationEventType } from "@shared/types";
import { createContext } from "@server/context";
import env from "@server/env";
import { AuthenticationError } from "@server/errors";
import auth from "@server/middlewares/authentication";
@@ -85,7 +86,6 @@ router.post(
auth(),
pagination(),
validate(T.NotificationsListSchema),
transaction(),
async (ctx: APIContext<T.NotificationsListReq>) => {
const { eventType, archived } = ctx.input.body;
const user = ctx.state.auth.user;
@@ -152,17 +152,28 @@ router.get(
const notification = await Notification.unscoped().findByPk(id, {
lock: transaction.LOCK.UPDATE,
rejectOnEmpty: true,
transaction,
rejectOnEmpty: true,
});
if (!notification || !safeEqual(token, notification.pixelToken)) {
if (!safeEqual(token, notification.pixelToken)) {
throw AuthenticationError();
}
if (!notification.viewedAt) {
notification.viewedAt = new Date();
await notification.saveWithCtx(ctx);
const user = await notification.$get("user");
if (user) {
await notification.updateWithCtx(
createContext({
...ctx,
transaction,
user,
}),
{
viewedAt: new Date(),
}
);
}
}
ctx.response.set("Content-Type", "image/gif");
+2 -3
View File
@@ -19,7 +19,6 @@ import { safeEqual } from "@server/utils/crypto";
import * as T from "./schema";
const router = new Router();
const emailEnabled = !!(env.SMTP_HOST || env.isDevelopment);
const handleTeamUpdate = async (ctx: APIContext<T.TeamsUpdateSchemaReq>) => {
const { transaction } = ctx.state;
@@ -68,7 +67,7 @@ router.post(
rateLimiter(RateLimiterStrategy.FivePerHour),
auth(),
async (ctx: APIContext) => {
if (!emailEnabled) {
if (!env.EMAIL_ENABLED) {
throw ValidationError("Email support is not setup for this instance");
}
@@ -101,7 +100,7 @@ router.post(
authorize(user, "delete", team);
if (emailEnabled) {
if (env.EMAIL_ENABLED) {
const deleteConfirmationCode = team.getDeleteConfirmationCode(user);
if (!safeEqual(code, deleteConfirmationCode)) {
+4 -5
View File
@@ -30,7 +30,6 @@ import pagination from "../middlewares/pagination";
import * as T from "./schema";
const router = new Router();
const emailEnabled = !!(env.SMTP_HOST || env.isDevelopment);
router.post(
"users.list",
@@ -210,7 +209,7 @@ router.post(
auth(),
validate(T.UsersUpdateEmailSchema),
async (ctx: APIContext<T.UsersUpdateEmailReq>) => {
if (!emailEnabled) {
if (!env.EMAIL_ENABLED) {
throw ValidationError("Email support is not setup for this instance");
}
@@ -252,7 +251,7 @@ router.get(
transaction(),
validate(T.UsersUpdateEmailConfirmSchema),
async (ctx: APIContext<T.UsersUpdateEmailConfirmReq>) => {
if (!emailEnabled) {
if (!env.EMAIL_ENABLED) {
throw ValidationError("Email support is not setup for this instance");
}
@@ -626,7 +625,7 @@ router.post(
rateLimiter(RateLimiterStrategy.FivePerHour),
auth(),
async (ctx: APIContext) => {
if (!emailEnabled) {
if (!env.EMAIL_ENABLED) {
throw ValidationError("Email support is not setup for this instance");
}
@@ -671,7 +670,7 @@ router.post(
// If we're attempting to delete our own account then a confirmation code
// is required. This acts as CSRF protection.
if ((!id || id === actor.id) && emailEnabled) {
if ((!id || id === actor.id) && env.EMAIL_ENABLED) {
const deleteConfirmationCode = user.deleteConfirmationCode;
if (!safeEqual(code, deleteConfirmationCode)) {
@@ -0,0 +1,509 @@
[
{
"object": "block",
"id": "1c12c2bb-bca8-803b-a8dc-d10fdf86fd60",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T07:49:00.000Z",
"last_edited_time": "2025-03-25T07:49:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "paragraph",
"paragraph": {
"rich_text": [],
"color": "default"
}
},
{
"object": "block",
"id": "1c12c2bb-bca8-809a-a9e5-f33faf274c5f",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T07:56:00.000Z",
"last_edited_time": "2025-03-25T08:09:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "code",
"code": {
"caption": [],
"rich_text": [],
"language": "javascript"
}
},
{
"object": "block",
"id": "1c12c2bb-bca8-80c2-986a-ef8ae792a0f9",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T08:04:00.000Z",
"last_edited_time": "2025-03-25T08:04:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "paragraph",
"paragraph": {
"rich_text": [],
"color": "default"
}
},
{
"object": "block",
"id": "1c12c2bb-bca8-80b3-9180-c6bc089c00fc",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T07:56:00.000Z",
"last_edited_time": "2025-03-25T08:05:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "paragraph",
"paragraph": {
"rich_text": [
{
"type": "equation",
"equation": {
"expression": "E"
},
"annotations": {
"bold": false,
"italic": false,
"strikethrough": false,
"underline": false,
"code": false,
"color": "default"
},
"plain_text": "E",
"href": null
}
],
"color": "default"
}
},
{
"object": "block",
"id": "1c12c2bb-bca8-80ca-974e-c226a1a1ae76",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T08:10:00.000Z",
"last_edited_time": "2025-03-25T08:10:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "paragraph",
"paragraph": {
"rich_text": [],
"color": "default"
}
},
{
"object": "block",
"id": "1c12c2bb-bca8-8028-944b-e2593f20e09a",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T08:09:00.000Z",
"last_edited_time": "2025-03-25T08:10:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "paragraph",
"paragraph": {
"rich_text": [
{
"type": "mention",
"mention": {
"type": "link_mention",
"link_mention": {
"href": "http://github.com/outline/",
"title": "Outline",
"icon_url": "https://github.com/fluidicon.png",
"description": "We're building an open source collaborative knowledge base for modern teams - Outline",
"link_provider": "GitHub",
"thumbnail_url": "https://avatars.githubusercontent.com/u/1765001?s=280&v=4"
}
},
"annotations": {
"bold": false,
"italic": false,
"strikethrough": false,
"underline": false,
"code": false,
"color": "default"
},
"plain_text": "http://github.com/outline/",
"href": "http://github.com/outline/"
}
],
"color": "default"
}
},
{
"object": "block",
"id": "1c12c2bb-bca8-80f8-a272-c519ce56de27",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T08:12:00.000Z",
"last_edited_time": "2025-03-25T08:12:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "paragraph",
"paragraph": {
"rich_text": [],
"color": "default"
}
},
{
"object": "block",
"id": "1c12c2bb-bca8-8096-8a94-e44b32127e73",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T08:12:00.000Z",
"last_edited_time": "2025-03-25T08:12:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "paragraph",
"paragraph": {
"rich_text": [
{
"type": "mention",
"mention": {
"type": "link_preview",
"link_preview": {
"url": "https://github.com/outline/outline"
}
},
"annotations": {
"bold": false,
"italic": false,
"strikethrough": false,
"underline": false,
"code": false,
"color": "default"
},
"plain_text": "https://github.com/outline/outline",
"href": "https://github.com/outline/outline"
}
],
"color": "default"
}
},
{
"object": "block",
"id": "1c12c2bb-bca8-807b-98de-ff2c1d0080f2",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T08:13:00.000Z",
"last_edited_time": "2025-03-25T08:13:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "paragraph",
"paragraph": {
"rich_text": [],
"color": "default"
}
},
{
"object": "block",
"id": "1c12c2bb-bca8-80dd-b816-cb4dd04d4f46",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T08:13:00.000Z",
"last_edited_time": "2025-03-25T08:13:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "equation",
"equation": {
"expression": ""
}
},
{
"object": "block",
"id": "1c12c2bb-bca8-8074-a5e0-f0ba30bd0510",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T08:18:00.000Z",
"last_edited_time": "2025-03-25T08:18:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "paragraph",
"paragraph": {
"rich_text": [],
"color": "default"
}
},
{
"object": "block",
"id": "1c12c2bb-bca8-80dc-8a14-db8b714450f6",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T08:18:00.000Z",
"last_edited_time": "2025-03-25T08:30:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "bookmark",
"bookmark": {
"caption": [],
"url": "https://google.com"
}
},
{
"object": "block",
"id": "1c12c2bb-bca8-80f2-94b4-df422d9cd18e",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T08:24:00.000Z",
"last_edited_time": "2025-03-25T08:24:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "paragraph",
"paragraph": {
"rich_text": [],
"color": "default"
}
},
{
"object": "block",
"id": "1c12c2bb-bca8-8046-946c-d89c2bc7827c",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T08:24:00.000Z",
"last_edited_time": "2025-03-25T08:24:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "1b32c2bb-bca8-816a-99c0-0027ab5c4cb0"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "link_preview",
"link_preview": {
"url": "https://github.com/outline/outline"
}
},
{
"object": "block",
"id": "1c12c2bb-bca8-80e6-80d3-f9501fc1a6fb",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T08:24:00.000Z",
"last_edited_time": "2025-03-25T08:24:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "paragraph",
"paragraph": {
"rich_text": [],
"color": "default"
}
},
{
"object": "block",
"id": "1c12c2bb-bca8-8070-9c41-ca4c3aa1a419",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T08:24:00.000Z",
"last_edited_time": "2025-03-25T08:30:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "pdf",
"pdf": {
"caption": [],
"type": "file",
"file": {
"url": "https://prod-files-secure.s3.us-west-2.amazonaws.com/2f3fcad6-fc32-434b-b6b2-a03ca7893c4d/49bfa851-95c1-458b-abb0-88ed591f7712/Empty_pdf.pdf",
"expiry_time": "2025-03-25T09:32:41.690Z"
}
}
},
{
"object": "block",
"id": "1c12c2bb-bca8-800c-8857-eca03207279f",
"parent": {
"type": "page_id",
"page_id": "1c12c2bb-bca8-8023-ba9a-f8fafab74e48"
},
"created_time": "2025-03-25T08:26:00.000Z",
"last_edited_time": "2025-03-25T08:26:00.000Z",
"created_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"last_edited_by": {
"object": "user",
"id": "b69e71d0-c6c2-428c-ae38-5c020343b36b"
},
"has_children": false,
"archived": false,
"in_trash": false,
"type": "paragraph",
"paragraph": {
"rich_text": [],
"color": "default"
}
}
]
+3 -1
View File
@@ -54,7 +54,9 @@ export class CacheHelper {
}
return value;
} finally {
await lock?.release();
if (lock && lock.expiration > new Date().getTime()) {
await lock.release();
}
}
}
+1 -3
View File
@@ -1,9 +1,6 @@
import crypto from "crypto";
import { addMinutes, subMinutes } from "date-fns";
import type { Context } from "koa";
// Allowed for trusted server<->server connections
// eslint-disable-next-line no-restricted-imports
import fetch from "node-fetch";
import {
StateStoreStoreCallback,
StateStoreVerifyCallback,
@@ -13,6 +10,7 @@ import { getCookieDomain, parseDomain } from "@shared/utils/domains";
import env from "@server/env";
import { Team } from "@server/models";
import { InternalError, OAuthStateMismatchError } from "../errors";
import fetch from "./fetch";
export class StateStore {
key = "state";
+2 -4
View File
@@ -1,5 +1,5 @@
import fractionalIndex from "fractional-index";
import { Op, Sequelize, type FindOptions } from "sequelize";
import { Sequelize, type FindOptions } from "sequelize";
import Collection from "@server/models/Collection";
/**
@@ -31,9 +31,7 @@ export default async function removeIndexCollision(
where: {
teamId,
deletedAt: null,
index: {
[Op.gt]: index,
},
index: Sequelize.literal(`"collection"."index" collate "C" > '${index}'`),
},
attributes: ["id", "index"],
limit: 1,
+28
View File
@@ -29,3 +29,31 @@ export function CannotUseWithout(
});
};
}
export function CannotUseWith(
property: string,
validationOptions?: ValidationOptions
) {
return function (object: Object, propertyName: string) {
registerDecorator({
name: "cannotUseWith",
target: object.constructor,
propertyName,
constraints: [property],
options: validationOptions,
validator: {
validate<T>(value: T, args: ValidationArguments) {
if (value === undefined) {
return true;
}
const obj = args.object as unknown as T;
const forbidden = args.constraints[0] as keyof T;
return obj[forbidden] === undefined;
},
defaultMessage(args: ValidationArguments) {
return `${propertyName} cannot be used with ${args.constraints[0]}.`;
},
},
});
};
}
+1 -1
View File
@@ -232,7 +232,7 @@ export class ValidateDocumentId {
export class ValidateIndex {
public static regex = new RegExp("^[\x20-\x7E]+$");
public static message = "Must be between x20 to x7E ASCII";
public static maxLength = 100;
public static maxLength = 256;
}
export class ValidateURL {
+4
View File
@@ -10,6 +10,10 @@ type Props = {
captureEvents?: "all" | "pointer" | "click";
};
/**
* EventBoundary is a component that prevents events from propagating to parent elements.
* This is useful for preventing clicks or other interactions from bubbling up the DOM tree.
*/
const EventBoundary: React.FC<Props> = ({
children,
className,
+12
View File
@@ -5,14 +5,26 @@ type JustifyValues = CSSProperties["justifyContent"];
type AlignValues = CSSProperties["alignItems"];
/**
* Flex is a styled component that provides a flexible box layout with convenient props.
* It simplifies the use of flexbox CSS properties with a clean, declarative API.
*/
const Flex = styled.div<{
/** Makes the component grow to fill available space */
auto?: boolean;
/** Changes flex direction to column */
column?: boolean;
/** Sets the align-items CSS property */
align?: AlignValues;
/** Sets the justify-content CSS property */
justify?: JustifyValues;
/** Enables flex-wrap */
wrap?: boolean;
/** Controls flex-shrink behavior */
shrink?: boolean;
/** Reverses the direction (row-reverse or column-reverse) */
reverse?: boolean;
/** Sets gap between flex items in pixels */
gap?: number;
}>`
display: flex;
+5
View File
@@ -11,6 +11,11 @@ type Props = {
className?: string;
};
/**
* Squircle is a component that renders a square with rounded corners (squircle shape).
* It's commonly used for app icons, avatars, and other UI elements where a softer
* square shape is desired.
*/
const Squircle: React.FC<Props> = ({
color,
size = 28,
+9 -1
View File
@@ -1,5 +1,5 @@
import { NodeType } from "prosemirror-model";
import { wrapInList, liftListItem } from "prosemirror-schema-list";
import { liftListItem, wrapInList } from "prosemirror-schema-list";
import { Command } from "prosemirror-state";
import { chainTransactions } from "../lib/chainTransactions";
import { findParentNode } from "../queries/findParentNode";
@@ -29,6 +29,14 @@ export default function toggleList(
return liftListItem(itemType)(state, dispatch);
}
const currentItemType = parentList.node.content.firstChild?.type;
if (currentItemType && currentItemType !== itemType) {
return chainTransactions(clearNodes(), wrapInList(listType))(
state,
dispatch
);
}
if (
isList(parentList.node, schema) &&
listType.validContent(parentList.node.content)
+4
View File
@@ -313,6 +313,10 @@ width: 100%;
background: ${props.theme.mentionHoverBackground};
}
&[data-type="user"] {
gap: 0;
}
&.mention-user::before {
content: "@";
}
+4 -59
View File
@@ -4,62 +4,10 @@ import { Node } from "prosemirror-model";
import { Plugin, PluginKey, Transaction } from "prosemirror-state";
import { Decoration, DecorationSet } from "prosemirror-view";
import refractor from "refractor/core";
import { getPrismLangForLanguage } from "../lib/code";
import { isRemoteTransaction } from "../lib/multiplayer";
import { findBlockNodes } from "../queries/findChildren";
export const LANGUAGES = {
none: "Plain text", // additional entry to disable highlighting
bash: "Bash",
clike: "C",
cpp: "C++",
csharp: "C#",
css: "CSS",
docker: "Docker",
elixir: "Elixir",
erlang: "Erlang",
go: "Go",
graphql: "GraphQL",
groovy: "Groovy",
haskell: "Haskell",
hcl: "HCL",
markup: "HTML",
ini: "INI",
java: "Java",
javascript: "JavaScript",
json: "JSON",
jsx: "JSX",
kotlin: "Kotlin",
lisp: "Lisp",
lua: "Lua",
mermaidjs: "Mermaid Diagram",
nginx: "Nginx",
nix: "Nix",
objectivec: "Objective-C",
ocaml: "OCaml",
perl: "Perl",
php: "PHP",
powershell: "Powershell",
protobuf: "Protobuf",
python: "Python",
r: "R",
ruby: "Ruby",
rust: "Rust",
scala: "Scala",
sass: "Sass",
scss: "SCSS",
sql: "SQL",
solidity: "Solidity",
swift: "Swift",
toml: "TOML",
tsx: "TSX",
typescript: "TypeScript",
vb: "Visual Basic",
verilog: "Verilog",
vhdl: "VHDL",
yaml: "YAML",
zig: "Zig",
};
type ParsedNode = {
text: string;
classes: string[];
@@ -109,12 +57,9 @@ function getDecorations({
blocks.forEach((block) => {
let startPos = block.pos + 1;
const language = (
block.node.attrs.language === "mermaidjs"
? "mermaid"
: block.node.attrs.language
) as string;
if (!language || language === "none" || !refractor.registered(language)) {
const language = getPrismLangForLanguage(block.node.attrs.language);
if (!language || !refractor.registered(language)) {
return;
}
+22
View File
@@ -0,0 +1,22 @@
import { getPrismLangForLanguage, getLabelForLanguage } from "./code";
describe("getPrismLangForLanguage", () => {
it("should return the correct Prism language identifier for a given language", () => {
expect(getPrismLangForLanguage("javascript")).toBe("javascript");
expect(getPrismLangForLanguage("mermaidjs")).toBe("mermaid");
expect(getPrismLangForLanguage("xml")).toBe("markup");
expect(getPrismLangForLanguage("unknown")).toBeUndefined();
expect(getPrismLangForLanguage("")).toBeUndefined();
});
});
describe("getLabelForLanguage", () => {
it("should return the correct human-readable label for a given language", () => {
expect(getLabelForLanguage("javascript")).toBe("JavaScript");
expect(getLabelForLanguage("mermaidjs")).toBe("Mermaid Diagram");
expect(getLabelForLanguage("xml")).toBe("XML");
expect(getLabelForLanguage("unknown")).toBe("Plain text");
expect(getLabelForLanguage("none")).toBe("Plain text");
expect(getLabelForLanguage("")).toBe("Plain text");
});
});
+105 -11
View File
@@ -1,14 +1,97 @@
import Storage from "../../utils/Storage";
import { LANGUAGES } from "../extensions/Prism";
const RecentStorageKey = "rme-code-language";
const StorageKey = "frequent-code-languages";
const frequentLanguagesToGet = 5;
const frequentLanguagesToTrack = 10;
export const FrequentlyUsedCount = {
Get: 5,
Track: 10,
/**
* List of supported code languages.
*
* Object key is the language identifier used in the editor, lang is the
* language identifier used by Prism. Note mismatches such as `markup` and
* `mermaid`.
*/
export const codeLanguages = {
none: { lang: "", label: "Plain text" },
bash: { lang: "bash", label: "Bash" },
clike: { lang: "clike", label: "C" },
cpp: { lang: "cpp", label: "C++" },
csharp: { lang: "csharp", label: "C#" },
css: { lang: "css", label: "CSS" },
docker: { lang: "docker", label: "Docker" },
elixir: { lang: "elixir", label: "Elixir" },
erlang: { lang: "erlang", label: "Erlang" },
go: { lang: "go", label: "Go" },
graphql: { lang: "graphql", label: "GraphQL" },
groovy: { lang: "groovy", label: "Groovy" },
haskell: { lang: "haskell", label: "Haskell" },
hcl: { lang: "hcl", label: "HCL" },
markup: { lang: "markup", label: "HTML" },
ini: { lang: "ini", label: "INI" },
java: { lang: "java", label: "Java" },
javascript: { lang: "javascript", label: "JavaScript" },
json: { lang: "json", label: "JSON" },
jsx: { lang: "jsx", label: "JSX" },
kotlin: { lang: "kotlin", label: "Kotlin" },
lisp: { lang: "lisp", label: "Lisp" },
lua: { lang: "lua", label: "Lua" },
mermaidjs: { lang: "mermaid", label: "Mermaid Diagram" },
nginx: { lang: "nginx", label: "Nginx" },
nix: { lang: "nix", label: "Nix" },
objectivec: { lang: "objectivec", label: "Objective-C" },
ocaml: { lang: "ocaml", label: "OCaml" },
perl: { lang: "perl", label: "Perl" },
php: { lang: "php", label: "PHP" },
powershell: { lang: "powershell", label: "Powershell" },
protobuf: { lang: "protobuf", label: "Protobuf" },
python: { lang: "python", label: "Python" },
r: { lang: "r", label: "R" },
ruby: { lang: "ruby", label: "Ruby" },
rust: { lang: "rust", label: "Rust" },
scala: { lang: "scala", label: "Scala" },
sass: { lang: "sass", label: "Sass" },
scss: { lang: "scss", label: "SCSS" },
sql: { lang: "sql", label: "SQL" },
solidity: { lang: "solidity", label: "Solidity" },
swift: { lang: "swift", label: "Swift" },
toml: { lang: "toml", label: "TOML" },
tsx: { lang: "tsx", label: "TSX" },
typescript: { lang: "typescript", label: "TypeScript" },
vb: { lang: "vb", label: "Visual Basic" },
verilog: { lang: "verilog", label: "Verilog" },
vhdl: { lang: "vhdl", label: "VHDL" },
yaml: { lang: "yaml", label: "YAML" },
xml: { lang: "markup", label: "XML" },
zig: { lang: "zig", label: "Zig" },
};
/**
* Get the human-readable label for a given language.
*
* @param language The language identifier.
* @returns The human-readable label for the language.
*/
export const getLabelForLanguage = (language: string) => {
const lang =
codeLanguages[language as keyof typeof codeLanguages] ?? codeLanguages.none;
return lang.label;
};
/**
* Get the Prism language identifier for a given language.
*
* @param language The language identifier.
* @returns The Prism language identifier for the language.
*/
export const getPrismLangForLanguage = (language: string): string | undefined =>
codeLanguages[language as keyof typeof codeLanguages]?.lang;
/**
* Set the most recent code language used.
*
* @param language The language identifier.
*/
export const setRecentCodeLanguage = (language: string) => {
const frequentLangs = (Storage.get(StorageKey) ?? {}) as Record<
string,
@@ -26,14 +109,14 @@ export const setRecentCodeLanguage = (language: string) => {
const frequentLangEntries = Object.entries(frequentLangs);
if (frequentLangEntries.length > FrequentlyUsedCount.Track) {
if (frequentLangEntries.length > frequentLanguagesToTrack) {
sortFrequencies(frequentLangEntries);
const lastEntry = frequentLangEntries[FrequentlyUsedCount.Track];
const lastEntry = frequentLangEntries[frequentLanguagesToTrack];
if (lastEntry[0] === language) {
frequentLangEntries.splice(FrequentlyUsedCount.Track - 1, 1);
frequentLangEntries.splice(frequentLanguagesToTrack - 1, 1);
} else {
frequentLangEntries.splice(FrequentlyUsedCount.Track);
frequentLangEntries.splice(frequentLanguagesToTrack);
}
}
@@ -41,17 +124,28 @@ export const setRecentCodeLanguage = (language: string) => {
Storage.set(RecentStorageKey, language);
};
export const getRecentCodeLanguage = () => Storage.get(RecentStorageKey);
/**
* Get the most recent code language used.
*
* @returns The most recent code language used, or undefined if none is set.
*/
export const getRecentCodeLanguage = () =>
Storage.get(RecentStorageKey) as keyof typeof codeLanguages | undefined;
/**
* Get the most frequent code languages used.
*
* @returns An array of the most frequent code languages used.
*/
export const getFrequentCodeLanguages = () => {
const recentLang = Storage.get(RecentStorageKey);
const frequentLangEntries = Object.entries(Storage.get(StorageKey) ?? {}) as [
keyof typeof LANGUAGES,
keyof typeof codeLanguages,
number
][];
const frequentLangs = sortFrequencies(frequentLangEntries)
.slice(0, FrequentlyUsedCount.Get)
.slice(0, frequentLanguagesToGet)
.map(([lang]) => lang);
const isRecentLangPresent = frequentLangs.includes(recentLang);
+1 -1
View File
@@ -21,7 +21,7 @@ export default class Code extends Mark {
get schema(): MarkSpec {
return {
excludes: "mention placeholder highlight em strong",
excludes: "mention placeholder highlight",
parseDOM: [{ tag: "code", preserveWhitespace: true }],
toDOM: () => ["code", { class: "inline", spellCheck: "false" }],
};
+7 -5
View File
@@ -32,6 +32,11 @@ export default class Mention extends Node {
}
get schema(): NodeSpec {
const toPlainText = (node: ProsemirrorNode) =>
node.attrs.type === MentionType.User
? `@${node.attrs.label}`
: node.attrs.label;
return {
attrs: {
type: {
@@ -88,12 +93,9 @@ export default class Mention extends Node {
"data-actorid": node.attrs.actorId,
"data-url": `mention://${node.attrs.id}/${node.attrs.type}/${node.attrs.modelId}`,
},
String(node.attrs.label),
toPlainText(node),
],
toPlainText: (node) =>
node.attrs.type === MentionType.User
? `@${node.attrs.label}`
: node.attrs.label,
toPlainText,
};
}
+2
View File
@@ -98,6 +98,8 @@ export default class TableCell extends Node {
}
}
// For all other cases, preserve the slice as is to maintain table structure
// including column widths and other attributes
return slice;
},
handleDOMEvents: {
+17 -3
View File
@@ -7,6 +7,8 @@ type Options = {
onlyBlock?: boolean;
/** Only check if the selection is inside a code mark. */
onlyMark?: boolean;
/** If true then code must contain entire selection */
inclusive?: boolean;
};
/**
@@ -20,17 +22,29 @@ export function isInCode(state: EditorState, options?: Options): boolean {
const { nodes, marks } = state.schema;
if (!options?.onlyMark) {
if (nodes.code_block && isNodeActive(nodes.code_block)(state)) {
if (
nodes.code_block &&
isNodeActive(nodes.code_block, undefined, {
inclusive: options?.inclusive,
})(state)
) {
return true;
}
if (nodes.code_fence && isNodeActive(nodes.code_fence)(state)) {
if (
nodes.code_fence &&
isNodeActive(nodes.code_fence, undefined, {
inclusive: options?.inclusive,
})(state)
) {
return true;
}
}
if (!options?.onlyBlock) {
if (marks.code_inline) {
return isMarkActive(marks.code_inline)(state);
return isMarkActive(marks.code_inline, undefined, {
inclusive: options?.inclusive,
})(state);
}
}
+4 -1
View File
@@ -6,6 +6,8 @@ import { getMarksBetween } from "./getMarksBetween";
type Options = {
/** Only return match if the range and attrs is exact */
exact?: boolean;
/** If true then mark must contain entire selection */
inclusive?: boolean;
};
/**
@@ -40,7 +42,8 @@ export const isMarkActive =
Object.keys(attrs).every(
(key) => mark.attrs[key] === attrs[key]
)) &&
(!options?.exact || (start === from && end === to))
(!options?.exact || (start === from && end === to)) &&
(!options?.inclusive || (start <= from && end >= to))
);
}
+34 -10
View File
@@ -3,31 +3,55 @@ import { EditorState } from "prosemirror-state";
import { Primitive } from "utility-types";
import { findParentNode } from "./findParentNode";
type Options = {
/** Only return match if the range and attrs is exact */
exact?: boolean;
/** If true then node must contain entire selection */
inclusive?: boolean;
};
/**
* Checks if a node is active in the current selection or not.
*
* @param type The node type to check.
* @param attrs The attributes to check.
* @param options The options to use.
* @returns A function that checks if a node is active in the current selection or not.
*/
export const isNodeActive =
(type: NodeType, attrs: Record<string, Primitive> = {}) =>
(state: EditorState) => {
(type: NodeType, attrs?: Record<string, Primitive>, options?: Options) =>
(state: EditorState): boolean => {
if (!type) {
return false;
}
const nodeAfter = state.selection.$from.nodeAfter;
let node = nodeAfter?.type === type ? nodeAfter : undefined;
const { from, to } = state.selection;
const nodeWithPos = findParentNode(
(node) =>
node.type === type &&
(!attrs ||
Object.keys(attrs).every((key) => node.attrs[key] === attrs[key]))
)(state.selection);
if (!node) {
const parent = findParentNode((n) => n.type === type)(state.selection);
node = parent?.node;
if (!nodeWithPos) {
return false;
}
if (!Object.keys(attrs).length || !node) {
return !!node;
if (options?.inclusive) {
// Check if the node's position contains the entire selection
return (
nodeWithPos.pos <= from &&
nodeWithPos.pos + nodeWithPos.node.nodeSize >= to
);
}
return node.hasMarkup(type, { ...node.attrs, ...attrs });
if (options?.exact) {
// Check if node's range exactly matches selection
return (
nodeWithPos.pos === from &&
nodeWithPos.pos + nodeWithPos.node.nodeSize === to
);
}
return true;
};
+16 -24
View File
@@ -1,16 +1,5 @@
import { useState, useLayoutEffect } from "react";
const defaultRect = {
top: 0,
left: 0,
bottom: 0,
right: 0,
x: 0,
y: 0,
width: 0,
height: 0,
};
/**
* A hook that returns the size of an element or ref.
*
@@ -19,19 +8,11 @@ const defaultRect = {
*/
export function useComponentSize(
input: HTMLElement | null | React.RefObject<HTMLElement | null>
): DOMRect | typeof defaultRect {
) {
const element = input instanceof HTMLElement ? input : input?.current;
const [size, setSize] = useState(() => element?.getBoundingClientRect());
useLayoutEffect(() => {
const sizeObserver = new ResizeObserver(() => {
element?.dispatchEvent(new CustomEvent("resize"));
});
if (element) {
sizeObserver.observe(element);
}
return () => sizeObserver.disconnect();
}, [element]);
const [size, setSize] = useState<DOMRect | undefined>(
() => element?.getBoundingClientRect() || new DOMRect()
);
useLayoutEffect(() => {
const handleResize = () => {
@@ -55,6 +36,7 @@ export function useComponentSize(
window.addEventListener("click", handleResize);
window.addEventListener("resize", handleResize);
element?.addEventListener("resize", handleResize);
handleResize();
return () => {
window.removeEventListener("click", handleResize);
@@ -63,5 +45,15 @@ export function useComponentSize(
};
});
return size ?? defaultRect;
useLayoutEffect(() => {
const sizeObserver = new ResizeObserver(() => {
element?.dispatchEvent(new CustomEvent("resize"));
});
if (element) {
sizeObserver.observe(element);
}
return () => sizeObserver.disconnect();
}, [element]);
return size ?? new DOMRect();
}
+293 -281
View File
@@ -105,32 +105,32 @@
"@smithy/util-utf8" "^2.0.0"
tslib "^2.6.2"
"@aws-sdk/client-s3@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.758.0.tgz#430708980e86584172ea8e3dc1450be50bd86818"
integrity sha512-f8SlhU9/93OC/WEI6xVJf/x/GoQFj9a/xXK6QCtr5fvCjfSLgMVFmKTiIl/tgtDRzxUDc8YS6EGtbHjJ3Y/atg==
"@aws-sdk/client-s3@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.774.0.tgz#b8fba20417464385ee6c855aaafb67d024d10019"
integrity sha512-HQ5Xi01r/Pv2IpzPTf5acrN0g/yJQalheDCYbBSA8VU31zoYiT/w5kLFWYJHQDB2hRozh2DB/VC/VDmntkWXxA==
dependencies:
"@aws-crypto/sha1-browser" "5.2.0"
"@aws-crypto/sha256-browser" "5.2.0"
"@aws-crypto/sha256-js" "5.2.0"
"@aws-sdk/core" "3.758.0"
"@aws-sdk/credential-provider-node" "3.758.0"
"@aws-sdk/core" "3.774.0"
"@aws-sdk/credential-provider-node" "3.774.0"
"@aws-sdk/middleware-bucket-endpoint" "3.734.0"
"@aws-sdk/middleware-expect-continue" "3.734.0"
"@aws-sdk/middleware-flexible-checksums" "3.758.0"
"@aws-sdk/middleware-host-header" "3.734.0"
"@aws-sdk/middleware-flexible-checksums" "3.774.0"
"@aws-sdk/middleware-host-header" "3.774.0"
"@aws-sdk/middleware-location-constraint" "3.734.0"
"@aws-sdk/middleware-logger" "3.734.0"
"@aws-sdk/middleware-recursion-detection" "3.734.0"
"@aws-sdk/middleware-sdk-s3" "3.758.0"
"@aws-sdk/middleware-recursion-detection" "3.772.0"
"@aws-sdk/middleware-sdk-s3" "3.774.0"
"@aws-sdk/middleware-ssec" "3.734.0"
"@aws-sdk/middleware-user-agent" "3.758.0"
"@aws-sdk/middleware-user-agent" "3.774.0"
"@aws-sdk/region-config-resolver" "3.734.0"
"@aws-sdk/signature-v4-multi-region" "3.758.0"
"@aws-sdk/signature-v4-multi-region" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@aws-sdk/util-endpoints" "3.743.0"
"@aws-sdk/util-user-agent-browser" "3.734.0"
"@aws-sdk/util-user-agent-node" "3.758.0"
"@aws-sdk/util-user-agent-node" "3.774.0"
"@aws-sdk/xml-builder" "3.734.0"
"@smithy/config-resolver" "^4.0.1"
"@smithy/core" "^3.1.5"
@@ -167,23 +167,23 @@
"@smithy/util-waiter" "^4.0.2"
tslib "^2.6.2"
"@aws-sdk/client-sso@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.758.0.tgz#59a249abdfa52125fbe98b1d59c11e4f08ca6527"
integrity sha512-BoGO6IIWrLyLxQG6txJw6RT2urmbtlwfggapNCrNPyYjlXpzTSJhBYjndg7TpDATFd0SXL0zm8y/tXsUXNkdYQ==
"@aws-sdk/client-sso@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.774.0.tgz#8af5eeaa644eb1e4775babaa46de89e1e9f3ab21"
integrity sha512-bN+wd2gpTq+DNJ/fZdam/mX6K3TcVdZBIvxaVtg+imep6xAuRukdFhsoG0cDzk96+WHPCOhkyi+6lFljCof43Q==
dependencies:
"@aws-crypto/sha256-browser" "5.2.0"
"@aws-crypto/sha256-js" "5.2.0"
"@aws-sdk/core" "3.758.0"
"@aws-sdk/middleware-host-header" "3.734.0"
"@aws-sdk/core" "3.774.0"
"@aws-sdk/middleware-host-header" "3.774.0"
"@aws-sdk/middleware-logger" "3.734.0"
"@aws-sdk/middleware-recursion-detection" "3.734.0"
"@aws-sdk/middleware-user-agent" "3.758.0"
"@aws-sdk/middleware-recursion-detection" "3.772.0"
"@aws-sdk/middleware-user-agent" "3.774.0"
"@aws-sdk/region-config-resolver" "3.734.0"
"@aws-sdk/types" "3.734.0"
"@aws-sdk/util-endpoints" "3.743.0"
"@aws-sdk/util-user-agent-browser" "3.734.0"
"@aws-sdk/util-user-agent-node" "3.758.0"
"@aws-sdk/util-user-agent-node" "3.774.0"
"@smithy/config-resolver" "^4.0.1"
"@smithy/core" "^3.1.5"
"@smithy/fetch-http-handler" "^5.0.1"
@@ -211,10 +211,10 @@
"@smithy/util-utf8" "^4.0.0"
tslib "^2.6.2"
"@aws-sdk/core@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.758.0.tgz#d13a4bb95de0460d5269cd5a40503c85b344b0b4"
integrity sha512-0RswbdR9jt/XKemaLNuxi2gGr4xGlHyGxkTdhSQzCyUe9A9OPCoLl3rIESRguQEech+oJnbHk/wuiwHqTuP9sg==
"@aws-sdk/core@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.774.0.tgz#88926fc1c2efefa8fad602217865ee0c5ffca40b"
integrity sha512-JDkAAlPyGWMX42L4Cv8mxybwHTOoFweNbNrOc5oQJhFxZAe1zkW4uLTEfr79vYhnXCFbThCyPpBotmo3U2vULA==
dependencies:
"@aws-sdk/types" "3.734.0"
"@smithy/core" "^3.1.5"
@@ -228,23 +228,23 @@
fast-xml-parser "4.4.1"
tslib "^2.6.2"
"@aws-sdk/credential-provider-env@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.758.0.tgz#6193d1607eedd0929640ff64013f7787f29ff6a1"
integrity sha512-N27eFoRrO6MeUNumtNHDW9WOiwfd59LPXPqDrIa3kWL/s+fOKFHb9xIcF++bAwtcZnAxKkgpDCUP+INNZskE+w==
"@aws-sdk/credential-provider-env@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.774.0.tgz#1bbbb4e9ad0683d691aa65714a23ecdcbc649746"
integrity sha512-FkSDBi9Ly0bmzyrMDeqQq1lGsFMrrd/bIB3c9VD4Llh0sPLxB/DU31+VTPTuQ0pBPz4sX5Vay6tLy43DStzcFQ==
dependencies:
"@aws-sdk/core" "3.758.0"
"@aws-sdk/core" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@smithy/property-provider" "^4.0.1"
"@smithy/types" "^4.1.0"
tslib "^2.6.2"
"@aws-sdk/credential-provider-http@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.758.0.tgz#f7b28d642f2ac933e81a7add08ce582b398c1635"
integrity sha512-Xt9/U8qUCiw1hihztWkNeIR+arg6P+yda10OuCHX6kFVx3auTlU7+hCqs3UxqniGU4dguHuftf3mRpi5/GJ33Q==
"@aws-sdk/credential-provider-http@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.774.0.tgz#af35ae8b64a2d8347631259bf4ca0a2f1944fe35"
integrity sha512-iurWGQColf52HpHeHCQs/LnSjZ0Ufq3VtSQx/6QdZwIhmgbbqvGMAaBJg41SQjWhpqdufE96HzcaCJw/lnCefQ==
dependencies:
"@aws-sdk/core" "3.758.0"
"@aws-sdk/core" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@smithy/fetch-http-handler" "^5.0.1"
"@smithy/node-http-handler" "^4.0.3"
@@ -255,18 +255,18 @@
"@smithy/util-stream" "^4.1.2"
tslib "^2.6.2"
"@aws-sdk/credential-provider-ini@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.758.0.tgz#66457e71d8f5013e18111b25629c2367ed8ef116"
integrity sha512-cymSKMcP5d+OsgetoIZ5QCe1wnp2Q/tq+uIxVdh9MbfdBBEnl9Ecq6dH6VlYS89sp4QKuxHxkWXVnbXU3Q19Aw==
"@aws-sdk/credential-provider-ini@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.774.0.tgz#5908b083a6f839de672c0b6cae7b725308ef3816"
integrity sha512-+AsJOX9pGsnGPAC8wQw7LAO8ZfXzjXTjJxSP1fvg04PX7OBk4zwhVaryH6pu5raan+9cVbfEO1Z7EEMdkweGQA==
dependencies:
"@aws-sdk/core" "3.758.0"
"@aws-sdk/credential-provider-env" "3.758.0"
"@aws-sdk/credential-provider-http" "3.758.0"
"@aws-sdk/credential-provider-process" "3.758.0"
"@aws-sdk/credential-provider-sso" "3.758.0"
"@aws-sdk/credential-provider-web-identity" "3.758.0"
"@aws-sdk/nested-clients" "3.758.0"
"@aws-sdk/core" "3.774.0"
"@aws-sdk/credential-provider-env" "3.774.0"
"@aws-sdk/credential-provider-http" "3.774.0"
"@aws-sdk/credential-provider-process" "3.774.0"
"@aws-sdk/credential-provider-sso" "3.774.0"
"@aws-sdk/credential-provider-web-identity" "3.774.0"
"@aws-sdk/nested-clients" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@smithy/credential-provider-imds" "^4.0.1"
"@smithy/property-provider" "^4.0.1"
@@ -274,17 +274,17 @@
"@smithy/types" "^4.1.0"
tslib "^2.6.2"
"@aws-sdk/credential-provider-node@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.758.0.tgz#b0a5d18e5d7f1b091fd891e2e8088578c0246cef"
integrity sha512-+DaMv63wiq7pJrhIQzZYMn4hSarKiizDoJRvyR7WGhnn0oQ/getX9Z0VNCV3i7lIFoLNTb7WMmQ9k7+z/uD5EQ==
"@aws-sdk/credential-provider-node@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.774.0.tgz#b82ecf506098a62b2f3774aee4377f3089e7bf39"
integrity sha512-/t+TNhHNW6BNyf7Lgv6I0NUfFk6/dz4+6dUjopRxpDVJtp1YvNza0Zhl25ffRkqX4CKmuXyJYusDbbObcsncUA==
dependencies:
"@aws-sdk/credential-provider-env" "3.758.0"
"@aws-sdk/credential-provider-http" "3.758.0"
"@aws-sdk/credential-provider-ini" "3.758.0"
"@aws-sdk/credential-provider-process" "3.758.0"
"@aws-sdk/credential-provider-sso" "3.758.0"
"@aws-sdk/credential-provider-web-identity" "3.758.0"
"@aws-sdk/credential-provider-env" "3.774.0"
"@aws-sdk/credential-provider-http" "3.774.0"
"@aws-sdk/credential-provider-ini" "3.774.0"
"@aws-sdk/credential-provider-process" "3.774.0"
"@aws-sdk/credential-provider-sso" "3.774.0"
"@aws-sdk/credential-provider-web-identity" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@smithy/credential-provider-imds" "^4.0.1"
"@smithy/property-provider" "^4.0.1"
@@ -292,57 +292,57 @@
"@smithy/types" "^4.1.0"
tslib "^2.6.2"
"@aws-sdk/credential-provider-process@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.758.0.tgz#563bfae58049afd9968ca60f61672753834ff506"
integrity sha512-AzcY74QTPqcbXWVgjpPZ3HOmxQZYPROIBz2YINF0OQk0MhezDWV/O7Xec+K1+MPGQO3qS6EDrUUlnPLjsqieHA==
"@aws-sdk/credential-provider-process@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.774.0.tgz#f3e507621cdd9818dc538f8ca142bf323b71c76a"
integrity sha512-lycBRY1NeWa46LefN258m1MRVUPQgvf6TPA6ZYajyq6/dCr6BPeuUoUAyrzePTPlxV/M25YXNiyORHjjwlK0ug==
dependencies:
"@aws-sdk/core" "3.758.0"
"@aws-sdk/core" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@smithy/property-provider" "^4.0.1"
"@smithy/shared-ini-file-loader" "^4.0.1"
"@smithy/types" "^4.1.0"
tslib "^2.6.2"
"@aws-sdk/credential-provider-sso@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.758.0.tgz#5098c196a2dd38ba467aca052fc5193476b8a404"
integrity sha512-x0FYJqcOLUCv8GLLFDYMXRAQKGjoM+L0BG4BiHYZRDf24yQWFCAZsCQAYKo6XZYh2qznbsW6f//qpyJ5b0QVKQ==
"@aws-sdk/credential-provider-sso@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.774.0.tgz#61a384c6fd56b5dd5ec1cae4c8dcc90fd77551fc"
integrity sha512-j7vbGCWF6dVpd9qiT0PQGzY4NKf8KUa86sSoosGGbtu0dV9T/Y0s/fvPZ0F8ZyuPIKUMJaBpIJYZ/ECZRfT2mg==
dependencies:
"@aws-sdk/client-sso" "3.758.0"
"@aws-sdk/core" "3.758.0"
"@aws-sdk/token-providers" "3.758.0"
"@aws-sdk/client-sso" "3.774.0"
"@aws-sdk/core" "3.774.0"
"@aws-sdk/token-providers" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@smithy/property-provider" "^4.0.1"
"@smithy/shared-ini-file-loader" "^4.0.1"
"@smithy/types" "^4.1.0"
tslib "^2.6.2"
"@aws-sdk/credential-provider-web-identity@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.758.0.tgz#ea88729ee0e5de0bf5f31929d60dfd148934b6a5"
integrity sha512-XGguXhBqiCXMXRxcfCAVPlMbm3VyJTou79r/3mxWddHWF0XbhaQiBIbUz6vobVTD25YQRbWSmSch7VA8kI5Lrw==
"@aws-sdk/credential-provider-web-identity@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.774.0.tgz#4063e879ec57357333af542a32a72576908ecd7e"
integrity sha512-kuE5Hdqm9xXdrYBWCU6l2aM3W3HBtZrIBgyf0y41LulJHwld1nvIySus/lILdzbipmUAv9FI07B8TF5y7p/aFA==
dependencies:
"@aws-sdk/core" "3.758.0"
"@aws-sdk/nested-clients" "3.758.0"
"@aws-sdk/core" "3.774.0"
"@aws-sdk/nested-clients" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@smithy/property-provider" "^4.0.1"
"@smithy/types" "^4.1.0"
tslib "^2.6.2"
"@aws-sdk/crt-loader@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/crt-loader/-/crt-loader-3.758.0.tgz#ed9372fc887c34af4408e376832d8684316055fc"
integrity sha512-FnLhsDNK3il4rm6Egv7IEuOf8Oiyte5AVeoYkuqgmkCll58tRZCJXYNG/VE+Tket0s3joueVt7rwvHXnMxG0rg==
"@aws-sdk/crt-loader@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/crt-loader/-/crt-loader-3.774.0.tgz#124ad98cce8320153e010b97f665ecddf0608ed1"
integrity sha512-9pZZLebem2Q6rsT29Tzto0/P92IpwOg4c1viZltl2xnEH7kD4nxG+6v6upzPE5L2pm2S88kZXkXi7jZ6HOIBjQ==
dependencies:
"@aws-sdk/util-user-agent-node" "3.758.0"
"@aws-sdk/util-user-agent-node" "3.774.0"
aws-crt "^1.24.0"
tslib "^2.6.2"
"@aws-sdk/lib-storage@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/lib-storage/-/lib-storage-3.758.0.tgz#6fe6a128e6a1bc062cc3e4bd920774545ce94933"
integrity sha512-g07y7rA505zaTJNPTmvW4zYJA3gThFDE1be7kBUKhTKAdwv8jVSbOiAy2AhClXs2evSUoQiFFtD1xWxLRXPPRQ==
"@aws-sdk/lib-storage@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/lib-storage/-/lib-storage-3.774.0.tgz#f198af2fe92a3fe6e3368577ca836f5382c411b8"
integrity sha512-xB3rD+F5pt+JLJaUt5eakCJ3+CUa8PXk9nxgN2VozfpuvuR6A/l3lnxmP5wYLhw1I9hxJLV9AD1/QYcibdBjtQ==
dependencies:
"@smithy/abort-controller" "^4.0.1"
"@smithy/middleware-endpoint" "^4.0.6"
@@ -375,15 +375,15 @@
"@smithy/types" "^4.1.0"
tslib "^2.6.2"
"@aws-sdk/middleware-flexible-checksums@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.758.0.tgz#50b753e5c83f4fe2ec3578a1768a68336ec86e3c"
integrity sha512-o8Rk71S08YTKLoSobucjnbj97OCGaXgpEDNKXpXaavUM5xLNoHCLSUPRCiEN86Ivqxg1n17Y2nSRhfbsveOXXA==
"@aws-sdk/middleware-flexible-checksums@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.774.0.tgz#d0345541dddda11bf8daafb25dc82809f2627c7f"
integrity sha512-S0vs+U7sEZkRRnjf05KCbEHDduyxGgNPq+ZeaiyWbs5yTZ8wzSYrSzMAKcbCqAseNVYQbpGMXDh8tnzx6H/ihw==
dependencies:
"@aws-crypto/crc32" "5.2.0"
"@aws-crypto/crc32c" "5.2.0"
"@aws-crypto/util" "5.2.0"
"@aws-sdk/core" "3.758.0"
"@aws-sdk/core" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@smithy/is-array-buffer" "^4.0.0"
"@smithy/node-config-provider" "^4.0.1"
@@ -394,10 +394,10 @@
"@smithy/util-utf8" "^4.0.0"
tslib "^2.6.2"
"@aws-sdk/middleware-host-header@3.734.0":
version "3.734.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.734.0.tgz#a9a02c055352f5c435cc925a4e1e79b7ba41b1b5"
integrity sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw==
"@aws-sdk/middleware-host-header@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.774.0.tgz#a71a6884823512f5190b0a56db3234395d0a45ba"
integrity sha512-7QHA0ZyEBVfyJqmIc0FW4MUtPdrWhDsHQudsvBCHFS+mqP5fhpU/o4e5RQ+0M7tQqDE65+8MrZRniRa+Txz3xA==
dependencies:
"@aws-sdk/types" "3.734.0"
"@smithy/protocol-http" "^5.0.1"
@@ -422,22 +422,22 @@
"@smithy/types" "^4.1.0"
tslib "^2.6.2"
"@aws-sdk/middleware-recursion-detection@3.734.0":
version "3.734.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.734.0.tgz#4fa1deb9887455afbb39130f7d9bc89ccee17168"
integrity sha512-CUat2d9ITsFc2XsmeiRQO96iWpxSKYFjxvj27Hc7vo87YUHRnfMfnc8jw1EpxEwMcvBD7LsRa6vDNky6AjcrFA==
"@aws-sdk/middleware-recursion-detection@3.772.0":
version "3.772.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.772.0.tgz#5f9bf624de8dca8791678fadc5cc905e839b3ca3"
integrity sha512-zg0LjJa4v7fcLzn5QzZvtVS+qyvmsnu7oQnb86l6ckduZpWDCDC9+A0ZzcXTrxblPCJd3JqkoG1+Gzi4S4Ny/Q==
dependencies:
"@aws-sdk/types" "3.734.0"
"@smithy/protocol-http" "^5.0.1"
"@smithy/types" "^4.1.0"
tslib "^2.6.2"
"@aws-sdk/middleware-sdk-s3@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.758.0.tgz#75c224a49e47111df880b683debbd8f49f30ca24"
integrity sha512-6mJ2zyyHPYSV6bAcaFpsdoXZJeQlR1QgBnZZ6juY/+dcYiuyWCdyLUbGzSZSE7GTfx6i+9+QWFeoIMlWKgU63A==
"@aws-sdk/middleware-sdk-s3@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.774.0.tgz#e2d078a941550f70abfeed710d966958dd402cf9"
integrity sha512-iMEaOj+S8LZfg7fZaSfXQ8YDtEfOSBiQUllyxzaVhSYlM7IeNDbpBdCWnRi34VrI1J1AuryMdX/foU9JNSTLXg==
dependencies:
"@aws-sdk/core" "3.758.0"
"@aws-sdk/core" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@aws-sdk/util-arn-parser" "3.723.0"
"@smithy/core" "^3.1.5"
@@ -461,12 +461,12 @@
"@smithy/types" "^4.1.0"
tslib "^2.6.2"
"@aws-sdk/middleware-user-agent@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.758.0.tgz#f3c9d2025aa55fd400acb1d699c1fbd6b4f68f34"
integrity sha512-iNyehQXtQlj69JCgfaOssgZD4HeYGOwxcaKeG6F+40cwBjTAi0+Ph1yfDwqk2qiBPIRWJ/9l2LodZbxiBqgrwg==
"@aws-sdk/middleware-user-agent@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.774.0.tgz#dbe66f063cf692de13301a696bb6bc693afd5608"
integrity sha512-SVDeBV6DESgc9zex1Wk5XYbUqRI1tmJYQor47uKqD18r6UaCpvzVOBP4x8l/6hteAYxsWER6ZZmsjBQkenEuFQ==
dependencies:
"@aws-sdk/core" "3.758.0"
"@aws-sdk/core" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@aws-sdk/util-endpoints" "3.743.0"
"@smithy/core" "^3.1.5"
@@ -474,23 +474,23 @@
"@smithy/types" "^4.1.0"
tslib "^2.6.2"
"@aws-sdk/nested-clients@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.758.0.tgz#571c853602d38f5e8faa10178347e711e4f0e444"
integrity sha512-YZ5s7PSvyF3Mt2h1EQulCG93uybprNGbBkPmVuy/HMMfbFTt4iL3SbKjxqvOZelm86epFfj7pvK7FliI2WOEcg==
"@aws-sdk/nested-clients@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.774.0.tgz#b46a8ad0f1b106066ba58387fb8977b228186121"
integrity sha512-00+UiYvxiZaDFVzn87kPpiZ/GiEWNaTNzC82C+bIyXt1M9AnAR6PAnnvMErTFwyG+Un6n2ai/I81wvJ1ftFmeQ==
dependencies:
"@aws-crypto/sha256-browser" "5.2.0"
"@aws-crypto/sha256-js" "5.2.0"
"@aws-sdk/core" "3.758.0"
"@aws-sdk/middleware-host-header" "3.734.0"
"@aws-sdk/core" "3.774.0"
"@aws-sdk/middleware-host-header" "3.774.0"
"@aws-sdk/middleware-logger" "3.734.0"
"@aws-sdk/middleware-recursion-detection" "3.734.0"
"@aws-sdk/middleware-user-agent" "3.758.0"
"@aws-sdk/middleware-recursion-detection" "3.772.0"
"@aws-sdk/middleware-user-agent" "3.774.0"
"@aws-sdk/region-config-resolver" "3.734.0"
"@aws-sdk/types" "3.734.0"
"@aws-sdk/util-endpoints" "3.743.0"
"@aws-sdk/util-user-agent-browser" "3.734.0"
"@aws-sdk/util-user-agent-node" "3.758.0"
"@aws-sdk/util-user-agent-node" "3.774.0"
"@smithy/config-resolver" "^4.0.1"
"@smithy/core" "^3.1.5"
"@smithy/fetch-http-handler" "^5.0.1"
@@ -530,12 +530,12 @@
"@smithy/util-middleware" "^4.0.1"
tslib "^2.6.2"
"@aws-sdk/s3-presigned-post@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/s3-presigned-post/-/s3-presigned-post-3.758.0.tgz#d65319c780a251a688c1a4158d9329cce16f6f21"
integrity sha512-x+TWQ6xYD+/i3IlIM0nn1HGAKCmkG12VslKyJSrZAjwWQeVerj6LPVIJvYFcZbNU5lEPWCnlVqKw3S3NgG+N4Q==
"@aws-sdk/s3-presigned-post@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/s3-presigned-post/-/s3-presigned-post-3.774.0.tgz#3359884307a2937c70632b6979bd47e6690a47c1"
integrity sha512-JttkojrrhYrTPe/svhGuMWjmoI9sbrIQ6RdXUt6JiNFVFMOqSMj5rpue3L9I4WS0Yb1+n7EiDnjLGNM9cY5/Bw==
dependencies:
"@aws-sdk/client-s3" "3.758.0"
"@aws-sdk/client-s3" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@aws-sdk/util-format-url" "3.734.0"
"@smithy/middleware-endpoint" "^4.0.6"
@@ -545,12 +545,12 @@
"@smithy/util-utf8" "^4.0.0"
tslib "^2.6.2"
"@aws-sdk/s3-request-presigner@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.758.0.tgz#e7bbf9251927952584739b5e45464a9f4bdf0739"
integrity sha512-dVyItwu/J1InfJBbCPpHRV9jrsBfI7L0RlDGyS3x/xqBwnm5qpvgNZQasQiyqIl+WJB4f5rZRZHgHuwftqINbA==
"@aws-sdk/s3-request-presigner@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.774.0.tgz#b0e8301a38879a1ff03df3a807fadb88c64dac1f"
integrity sha512-vD37Nq7+ChUkXSoDqkNMXu37R8kRDUo13pOfYgesSI4HA970fjXP1T4Mf2131Ms/NuYbBHNn330+3MAkbbYKxg==
dependencies:
"@aws-sdk/signature-v4-multi-region" "3.758.0"
"@aws-sdk/signature-v4-multi-region" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@aws-sdk/util-format-url" "3.734.0"
"@smithy/middleware-endpoint" "^4.0.6"
@@ -559,13 +559,13 @@
"@smithy/types" "^4.1.0"
tslib "^2.6.2"
"@aws-sdk/signature-v4-crt@^3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-crt/-/signature-v4-crt-3.758.0.tgz#a28b376a7d69019a3cb8e1afa11bfbeef9285662"
integrity sha512-DioxfJ8+KenOwydElJvWwMxw0IN8rTD3lbV3AbURwIp+5FBDz8F5ykMO1o1YUQvrGuUNtala8lX3ScE2L7tw9w==
"@aws-sdk/signature-v4-crt@^3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-crt/-/signature-v4-crt-3.774.0.tgz#22f6c07ac4c96b9128da57a0304f54baa07687bd"
integrity sha512-aXc1Mw+TkDDZqG/8VFexIWqNsYqsArT+auX0QmrCv+x2XgUSpoe++2Ge2CEH4N8ez6/jE9UBqQHLn+0h734X4w==
dependencies:
"@aws-sdk/crt-loader" "3.758.0"
"@aws-sdk/signature-v4-multi-region" "3.758.0"
"@aws-sdk/crt-loader" "3.774.0"
"@aws-sdk/signature-v4-multi-region" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@smithy/querystring-parser" "^4.0.1"
"@smithy/signature-v4" "^5.0.1"
@@ -573,24 +573,24 @@
"@smithy/util-middleware" "^4.0.1"
tslib "^2.6.2"
"@aws-sdk/signature-v4-multi-region@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.758.0.tgz#2ccd34e90120dbf6f29e4f621574efd02e463b79"
integrity sha512-0RPCo8fYJcrenJ6bRtiUbFOSgQ1CX/GpvwtLU2Fam1tS9h2klKK8d74caeV6A1mIUvBU7bhyQ0wMGlwMtn3EYw==
"@aws-sdk/signature-v4-multi-region@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.774.0.tgz#46b2240559560d064d563951af0f23a488336ff9"
integrity sha512-vQwATjZfl5vxfO+BDUH7nUnhfKoIJMnBmOxmUh4N10PWlz3WWwkT/YtH79nVpr+y1eM6GQUSGuNa4Reda6SaFA==
dependencies:
"@aws-sdk/middleware-sdk-s3" "3.758.0"
"@aws-sdk/middleware-sdk-s3" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@smithy/protocol-http" "^5.0.1"
"@smithy/signature-v4" "^5.0.1"
"@smithy/types" "^4.1.0"
tslib "^2.6.2"
"@aws-sdk/token-providers@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.758.0.tgz#fcab3885ba2b222ff8bb7817448d3c786dc2ddf9"
integrity sha512-ckptN1tNrIfQUaGWm/ayW1ddG+imbKN7HHhjFdS4VfItsP0QQOB0+Ov+tpgb4MoNR4JaUghMIVStjIeHN2ks1w==
"@aws-sdk/token-providers@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.774.0.tgz#d0b4b2e6b887bf5b61fbfbb946bfbb863f849d15"
integrity sha512-DDERwCduWFFXj7gx3qvnaB8GlnCUpQ8ZA03qI4QFokWu3EyHNK+hjp3nN5Dg81fI0Z82LRe30Q2uDsLBwNCZDg==
dependencies:
"@aws-sdk/nested-clients" "3.758.0"
"@aws-sdk/nested-clients" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@smithy/property-provider" "^4.0.1"
"@smithy/shared-ini-file-loader" "^4.0.1"
@@ -649,12 +649,12 @@
bowser "^2.11.0"
tslib "^2.6.2"
"@aws-sdk/util-user-agent-node@3.758.0":
version "3.758.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.758.0.tgz#604ccb02a5d11c9cedaea0bea279641ea9d4194d"
integrity sha512-A5EZw85V6WhoKMV2hbuFRvb9NPlxEErb4HPO6/SPXYY4QrjprIzScHxikqcWv1w4J3apB1wto9LPU3IMsYtfrw==
"@aws-sdk/util-user-agent-node@3.774.0":
version "3.774.0"
resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.774.0.tgz#9160dac000b699cbae623a95718fdcb3bf155f0b"
integrity sha512-kFmnK4sf5Wco8mkzO2PszqDXEwtQ5H896tUxqWDQhk67NtOLsHYfg98ymOBWWudth2POaldiIx6KFXtg0DvLLQ==
dependencies:
"@aws-sdk/middleware-user-agent" "3.758.0"
"@aws-sdk/middleware-user-agent" "3.774.0"
"@aws-sdk/types" "3.734.0"
"@smithy/node-config-provider" "^4.0.1"
"@smithy/types" "^4.1.0"
@@ -675,10 +675,10 @@
"@smithy/types" "^4.1.0"
tslib "^2.6.2"
"@babel/cli@^7.26.4":
version "7.26.4"
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.26.4.tgz#4101ff8ee5de8447a6c395397a97921056411d20"
integrity sha512-+mORf3ezU3p3qr+82WvJSnQNE1GAYeoCfEv4fik6B5/2cvKZ75AX8oawWQdXtM9MmndooQj15Jr9kelRFWsuRw==
"@babel/cli@^7.27.0":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.27.0.tgz#076603b25fc7dd88298ea94ab249c8237c7e71cc"
integrity sha512-bZfxn8DRxwiVzDO5CEeV+7IqXeCkzI4yYnrQbpwjT76CUyossQc6RYE7n+xfm0/2k40lPaCpW0FhxYs7EBAetw==
dependencies:
"@jridgewell/trace-mapping" "^0.3.25"
commander "^6.2.0"
@@ -705,34 +705,34 @@
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367"
integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==
"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.20.12", "@babel/core@^7.24.4", "@babel/core@^7.26.9":
version "7.26.9"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.9.tgz#71838542a4b1e49dfed353d7acbc6eb89f4a76f2"
integrity sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==
"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.20.12", "@babel/core@^7.24.4", "@babel/core@^7.26.10":
version "7.26.10"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9"
integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==
dependencies:
"@ampproject/remapping" "^2.2.0"
"@babel/code-frame" "^7.26.2"
"@babel/generator" "^7.26.9"
"@babel/generator" "^7.26.10"
"@babel/helper-compilation-targets" "^7.26.5"
"@babel/helper-module-transforms" "^7.26.0"
"@babel/helpers" "^7.26.9"
"@babel/parser" "^7.26.9"
"@babel/helpers" "^7.26.10"
"@babel/parser" "^7.26.10"
"@babel/template" "^7.26.9"
"@babel/traverse" "^7.26.9"
"@babel/types" "^7.26.9"
"@babel/traverse" "^7.26.10"
"@babel/types" "^7.26.10"
convert-source-map "^2.0.0"
debug "^4.1.0"
gensync "^1.0.0-beta.2"
json5 "^2.2.3"
semver "^6.3.1"
"@babel/generator@^7.26.9", "@babel/generator@^7.7.2":
version "7.26.9"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.9.tgz#75a9482ad3d0cc7188a537aa4910bc59db67cbca"
integrity sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==
"@babel/generator@^7.26.10", "@babel/generator@^7.27.0", "@babel/generator@^7.7.2":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.27.0.tgz#764382b5392e5b9aff93cadb190d0745866cbc2c"
integrity sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==
dependencies:
"@babel/parser" "^7.26.9"
"@babel/types" "^7.26.9"
"@babel/parser" "^7.27.0"
"@babel/types" "^7.27.0"
"@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.25"
jsesc "^3.0.2"
@@ -755,17 +755,17 @@
lru-cache "^5.1.1"
semver "^6.3.1"
"@babel/helper-create-class-features-plugin@^7.25.9":
version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz#7644147706bb90ff613297d49ed5266bde729f83"
integrity sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==
"@babel/helper-create-class-features-plugin@^7.25.9", "@babel/helper-create-class-features-plugin@^7.27.0":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.0.tgz#518fad6a307c6a96f44af14912b2c20abe9bfc30"
integrity sha512-vSGCvMecvFCd/BdpGlhpXYNhhC4ccxyvQWpbGL4CWbvfEoLFWUZuSuf7s9Aw70flgQF+6vptvgK2IfOnKlRmBg==
dependencies:
"@babel/helper-annotate-as-pure" "^7.25.9"
"@babel/helper-member-expression-to-functions" "^7.25.9"
"@babel/helper-optimise-call-expression" "^7.25.9"
"@babel/helper-replace-supers" "^7.25.9"
"@babel/helper-replace-supers" "^7.26.5"
"@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
"@babel/traverse" "^7.25.9"
"@babel/traverse" "^7.27.0"
semver "^6.3.1"
"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.25.9":
@@ -848,7 +848,7 @@
"@babel/helper-wrap-function" "^7.25.9"
"@babel/traverse" "^7.25.9"
"@babel/helper-replace-supers@^7.25.9":
"@babel/helper-replace-supers@^7.25.9", "@babel/helper-replace-supers@^7.26.5":
version "7.26.5"
resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d"
integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==
@@ -889,20 +889,20 @@
"@babel/traverse" "^7.25.9"
"@babel/types" "^7.25.9"
"@babel/helpers@^7.26.9":
version "7.26.10"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.10.tgz#6baea3cd62ec2d0c1068778d63cb1314f6637384"
integrity sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==
"@babel/helpers@^7.26.10":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.0.tgz#53d156098defa8243eab0f32fa17589075a1b808"
integrity sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==
dependencies:
"@babel/template" "^7.26.9"
"@babel/types" "^7.26.10"
"@babel/template" "^7.27.0"
"@babel/types" "^7.27.0"
"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.26.9", "@babel/parser@^7.7.0":
version "7.26.9"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.9.tgz#d9e78bee6dc80f9efd8f2349dcfbbcdace280fd5"
integrity sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==
"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.26.10", "@babel/parser@^7.27.0", "@babel/parser@^7.7.0":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.0.tgz#3d7d6ee268e41d2600091cbd4e145ffee85a44ec"
integrity sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==
dependencies:
"@babel/types" "^7.26.9"
"@babel/types" "^7.27.0"
"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9":
version "7.25.9"
@@ -1263,7 +1263,7 @@
"@babel/helper-module-transforms" "^7.25.9"
"@babel/helper-plugin-utils" "^7.25.9"
"@babel/plugin-transform-modules-commonjs@^7.25.9", "@babel/plugin-transform-modules-commonjs@^7.26.3":
"@babel/plugin-transform-modules-commonjs@^7.26.3":
version "7.26.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz#8f011d44b20d02c3de44d8850d971d8497f981fb"
integrity sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==
@@ -1428,12 +1428,12 @@
"@babel/helper-annotate-as-pure" "^7.25.9"
"@babel/helper-plugin-utils" "^7.25.9"
"@babel/plugin-transform-regenerator@^7.25.9":
version "7.25.9"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz#03a8a4670d6cebae95305ac6defac81ece77740b"
integrity sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==
"@babel/plugin-transform-regenerator@^7.25.9", "@babel/plugin-transform-regenerator@^7.27.0":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.0.tgz#822feebef43d6a59a81f696b2512df5b1682db31"
integrity sha512-LX/vCajUJQDqE7Aum/ELUMZAY19+cDpghxrnyt5I1tV6X5PyC86AOoWXWFYFeIvauyeSA6/ktn4tQVn/3ZifsA==
dependencies:
"@babel/helper-plugin-utils" "^7.25.9"
"@babel/helper-plugin-utils" "^7.26.5"
regenerator-transform "^0.15.2"
"@babel/plugin-transform-regexp-modifiers@^7.26.0":
@@ -1499,13 +1499,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.26.5"
"@babel/plugin-transform-typescript@^7.25.9":
version "7.26.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.7.tgz#64339515ea3eff610160f62499c3ef437d0ac83d"
integrity sha512-5cJurntg+AT+cgelGP9Bt788DKiAw9gIMSMU2NJrLAilnj0m8WZWUNZPSLOmadYsujHutpgElO+50foX+ib/Wg==
"@babel/plugin-transform-typescript@^7.27.0":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.0.tgz#a29fd3481da85601c7e34091296e9746d2cccba8"
integrity sha512-fRGGjO2UEGPjvEcyAZXRXAS8AfdaQoq7HnxAbJoAoW10B9xOKesmmndJv+Sym2a+9FHWZ9KbyyLCe9s0Sn5jtg==
dependencies:
"@babel/helper-annotate-as-pure" "^7.25.9"
"@babel/helper-create-class-features-plugin" "^7.25.9"
"@babel/helper-create-class-features-plugin" "^7.27.0"
"@babel/helper-plugin-utils" "^7.26.5"
"@babel/helper-skip-transparent-expression-wrappers" "^7.25.9"
"@babel/plugin-syntax-typescript" "^7.25.9"
@@ -1637,16 +1637,16 @@
"@babel/plugin-transform-react-jsx-development" "^7.25.9"
"@babel/plugin-transform-react-pure-annotations" "^7.25.9"
"@babel/preset-typescript@^7.26.0":
version "7.26.0"
resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz#4a570f1b8d104a242d923957ffa1eaff142a106d"
integrity sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==
"@babel/preset-typescript@^7.27.0":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.27.0.tgz#4dcb8827225975f4290961b0b089f9c694ca50c7"
integrity sha512-vxaPFfJtHhgeOVXRKuHpHPAOgymmy8V8I65T1q53R7GCZlefKeCaTyDs3zOPHTTbmquvNlQYC5klEvWsBAtrBQ==
dependencies:
"@babel/helper-plugin-utils" "^7.25.9"
"@babel/helper-plugin-utils" "^7.26.5"
"@babel/helper-validator-option" "^7.25.9"
"@babel/plugin-syntax-jsx" "^7.25.9"
"@babel/plugin-transform-modules-commonjs" "^7.25.9"
"@babel/plugin-transform-typescript" "^7.25.9"
"@babel/plugin-transform-modules-commonjs" "^7.26.3"
"@babel/plugin-transform-typescript" "^7.27.0"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.17.8", "@babel/runtime@^7.20.6", "@babel/runtime@^7.21.0", "@babel/runtime@^7.23.2", "@babel/runtime@^7.6.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
version "7.26.10"
@@ -1655,32 +1655,32 @@
dependencies:
regenerator-runtime "^0.14.0"
"@babel/template@^7.25.9", "@babel/template@^7.26.9", "@babel/template@^7.3.3":
version "7.26.9"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2"
integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==
"@babel/template@^7.25.9", "@babel/template@^7.26.9", "@babel/template@^7.27.0", "@babel/template@^7.3.3":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.0.tgz#b253e5406cc1df1c57dcd18f11760c2dbf40c0b4"
integrity sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==
dependencies:
"@babel/code-frame" "^7.26.2"
"@babel/parser" "^7.26.9"
"@babel/types" "^7.26.9"
"@babel/parser" "^7.27.0"
"@babel/types" "^7.27.0"
"@babel/traverse@^7.13.0", "@babel/traverse@^7.25.9", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.8", "@babel/traverse@^7.26.9", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0":
version "7.26.9"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.9.tgz#4398f2394ba66d05d988b2ad13c219a2c857461a"
integrity sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==
"@babel/traverse@^7.13.0", "@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.8", "@babel/traverse@^7.27.0", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.0.tgz#11d7e644779e166c0442f9a07274d02cd91d4a70"
integrity sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==
dependencies:
"@babel/code-frame" "^7.26.2"
"@babel/generator" "^7.26.9"
"@babel/parser" "^7.26.9"
"@babel/template" "^7.26.9"
"@babel/types" "^7.26.9"
"@babel/generator" "^7.27.0"
"@babel/parser" "^7.27.0"
"@babel/template" "^7.27.0"
"@babel/types" "^7.27.0"
debug "^4.3.1"
globals "^11.1.0"
"@babel/types@^7.0.0", "@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.26.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0":
version "7.26.10"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.10.tgz#396382f6335bd4feb65741eacfc808218f859259"
integrity sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==
"@babel/types@^7.0.0", "@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.27.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0":
version "7.27.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.0.tgz#ef9acb6b06c3173f6632d993ecb6d4ae470b4559"
integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==
dependencies:
"@babel/helper-string-parser" "^7.25.9"
"@babel/helper-validator-identifier" "^7.25.9"
@@ -1828,10 +1828,10 @@
enabled "2.0.x"
kuler "^2.0.0"
"@datadog/datadog-api-client@^1.16.0":
version "1.19.0"
resolved "https://registry.yarnpkg.com/@datadog/datadog-api-client/-/datadog-api-client-1.19.0.tgz#3bd7cafa2e83080a0efba836602699ea0441899b"
integrity "sha1-O9fK+i6DCAoO+6g2YCaZ6gRBiZs= sha512-tMkHo7nBFtpDP4dbT/dGIIdittGaTuA6hIY6v4vWb/f+NlJPwnJUf/gBJ9Hwz1HEmlYQMuAnpdFxIQ2P6zjujA=="
"@datadog/datadog-api-client@^1.17.0":
version "1.33.1"
resolved "https://registry.yarnpkg.com/@datadog/datadog-api-client/-/datadog-api-client-1.33.1.tgz#7dcdc86383699c3a4d4414c1a3deacb44a6d3262"
integrity sha512-ztX1/uutcuSdRqZLlirKy8tIn1VbMjBE/oLCcNL8PsDlzztaXkjhRzX71V8LTc35iQcuv9XzjjbUfOLGhypwdw==
dependencies:
"@types/buffer-from" "^1.1.0"
"@types/node" "*"
@@ -1842,7 +1842,6 @@
form-data "^4.0.0"
loglevel "^1.8.1"
pako "^2.0.4"
url-parse "^1.4.3"
"@datadog/libdatadog@^0.5.0":
version "0.5.0"
@@ -5313,10 +5312,10 @@
"@types/node" "*"
form-data "^4.0.0"
"@types/node@*", "@types/node@20.17.16", "@types/node@>=10.0.0", "@types/node@>=13.7.0":
version "20.17.16"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.16.tgz#b33b0edc1bf925b27349e494b871ca4451fabab4"
integrity sha512-vOTpLduLkZXePLxHiHsBLp98mHGnl8RptV4YAO3HfKO5UHjDvySGbxKtpYfy8Sx5+WKcgc45qNreJJRVM3L6mw==
"@types/node@*", "@types/node@20.17.27", "@types/node@>=10.0.0", "@types/node@>=13.7.0":
version "20.17.27"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.27.tgz#dbf0f9e6f905e9004045742f94e8413e20bad776"
integrity sha512-U58sbKhDrthHlxHRJw7ZLiLDZGmAUOZUbpw0S6nL27sYUdhvgBLCRu/keSd6qcTsfArd1sRFCCBxzWATGr/0UA==
dependencies:
undici-types "~6.19.2"
@@ -5818,6 +5817,11 @@ agent-base@6:
dependencies:
debug "4"
agent-base@^7.1.2:
version "7.1.3"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1"
integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==
aggregate-error@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
@@ -7745,12 +7749,12 @@ data-view-byte-offset@^1.0.1:
es-errors "^1.3.0"
is-data-view "^1.0.1"
datadog-metrics@^0.11.2:
version "0.11.2"
resolved "https://registry.yarnpkg.com/datadog-metrics/-/datadog-metrics-0.11.2.tgz#3fadf376773b06da21d5527e8574f5f315aee547"
integrity sha512-gM2Yc9b0fwpsDtcE4Q573Xy0OJw0tp+vsli3ghZ4QRLGKkQtviKj6EFOxRYtFADV5opP+bIIJIclcWmaI76uvg==
datadog-metrics@^0.12.1:
version "0.12.1"
resolved "https://registry.yarnpkg.com/datadog-metrics/-/datadog-metrics-0.12.1.tgz#8d3b91d554f9149990520d05199d08975d3edb7a"
integrity sha512-Gy+17ia7m9Uy+nKQHDd7fljdq0fqqfpgkpxlwW0x1oFKI7RcgDV32pMCfHtv4HKychP6fHtncj3Lf4VN/g4G6A==
dependencies:
"@datadog/datadog-api-client" "^1.16.0"
"@datadog/datadog-api-client" "^1.17.0"
debug "^4.1.0"
date-fns@^2.28.0, date-fns@^2.30.0:
@@ -9754,6 +9758,14 @@ https-proxy-agent@^5.0.1:
agent-base "6"
debug "4"
https-proxy-agent@^7.0.6:
version "7.0.6"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9"
integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==
dependencies:
agent-base "^7.1.2"
debug "4"
human-signals@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
@@ -9968,10 +9980,10 @@ invariant@^2.2.2, invariant@^2.2.4:
dependencies:
loose-envify "^1.0.0"
ioredis@^5.3.2, ioredis@^5.4.1:
version "5.4.1"
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.4.1.tgz#1c56b70b759f01465913887375ed809134296f40"
integrity sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==
ioredis@^5.3.2, ioredis@^5.6.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.6.0.tgz#faa2a27132f8a05c0ddfef400b01d1326df211a0"
integrity sha512-tBZlIIWbndeWBWCXWZiqtOF/yxf6yZX3tAlTJ7nfo5jhd6dctNxF7QnYlZLZ1a0o0pDoen7CgZqO+zjNaFbJAg==
dependencies:
"@ioredis/commands" "^1.1.1"
cluster-key-slot "^1.1.0"
@@ -12730,25 +12742,25 @@ pg-cloudflare@^1.1.1:
resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz#e6d5833015b170e23ae819e8c5d7eaedb472ca98"
integrity "sha1-5tWDMBWxcOI66Bnoxdfq7bRyypg= sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q=="
pg-connection-string@^2.6.1, pg-connection-string@^2.6.4:
version "2.6.4"
resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.4.tgz#f543862adfa49fa4e14bc8a8892d2a84d754246d"
integrity sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==
pg-connection-string@^2.6.1, pg-connection-string@^2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.7.0.tgz#f1d3489e427c62ece022dba98d5262efcb168b37"
integrity sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==
pg-int8@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c"
integrity "sha1-lDvUY79bcbQXARX4D478mgwOt4w= sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="
pg-pool@^3.6.2:
version "3.6.2"
resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.6.2.tgz#3a592370b8ae3f02a7c8130d245bc02fa2c5f3f2"
integrity sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==
pg-pool@^3.8.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.8.0.tgz#e6bce7fc4506a8d6106551363fc5283e5445b776"
integrity sha512-VBw3jiVm6ZOdLBTIcXLNdSotb6Iy3uOCwDGFAksZCXmi10nyRvnP2v3jl4d+IsLYRyXf6o9hIm/ZtUzlByNUdw==
pg-protocol@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.6.1.tgz#21333e6d83b01faaebfe7a33a7ad6bfd9ed38cb3"
integrity sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==
pg-protocol@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.8.0.tgz#c707101dd07813868035a44571488e4b98639d48"
integrity sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g==
pg-tsquery@^8.4.2:
version "8.4.2"
@@ -12766,14 +12778,14 @@ pg-types@^2.1.0:
postgres-date "~1.0.4"
postgres-interval "^1.1.0"
pg@^8.12.0:
version "8.12.0"
resolved "https://registry.yarnpkg.com/pg/-/pg-8.12.0.tgz#9341724db571022490b657908f65aee8db91df79"
integrity sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==
pg@^8.14.1:
version "8.14.1"
resolved "https://registry.yarnpkg.com/pg/-/pg-8.14.1.tgz#2e3d1f287b64797cdfc8d1ba000f61a7ff8d66ed"
integrity sha512-0TdbqfjwIun9Fm/r89oB7RFQ0bLgduAhiIqIXOsyKoiC/L54DbuAAzIEN/9Op0f1Po9X7iCPXGoa/Ah+2aI8Xw==
dependencies:
pg-connection-string "^2.6.4"
pg-pool "^3.6.2"
pg-protocol "^1.6.1"
pg-connection-string "^2.7.0"
pg-pool "^3.8.0"
pg-protocol "^1.8.0"
pg-types "^2.1.0"
pgpass "1.x"
optionalDependencies:
@@ -13050,10 +13062,10 @@ prosemirror-codemark@^0.4.2:
resolved "https://registry.yarnpkg.com/prosemirror-codemark/-/prosemirror-codemark-0.4.2.tgz#b4d0a57c0f1f6c6667e2a1ae7cfb6ba031dfb2e5"
integrity "sha1-tNClfA8fbGZn4qGufPtroDHfsuU= sha512-4n+PnGQToa/vTjn0OiivUvE8/moLtguUAfry8UA4Q8p47MhqT2Qpf2zBLustX5Upi4mSp3z1ZYBqLLovZC6abA=="
prosemirror-commands@^1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.6.2.tgz#d9cf6654912442cff47daa1677eb43ebd0b1f117"
integrity sha512-0nDHH++qcf/BuPLYvmqZTUUsPJUCPBUXt0J1ErTcDIS369CTp773itzLGIgIXG4LJXOlwYCr44+Mh4ii6MP1QA==
prosemirror-commands@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.7.0.tgz#c0a60c808f51157caa146922494fc59fe257f27c"
integrity sha512-6toodS4R/Aah5pdsrIwnTYPEjW70SlO5a66oo5Kk+CIrgJz3ukOoS+FYDGqvQlAX5PxoGWDX1oD++tn5X3pyRA==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-state "^1.0.0"
@@ -13191,7 +13203,7 @@ protobufjs@^7.2.5:
proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity "sha1-4QLxbKNVQkhldV0sno6k8k1Yw+I= sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
pseudomap@^1.0.2:
version "1.0.2"
@@ -13908,17 +13920,17 @@ rollup-plugin-node-resolve@^3.0.0:
is-module "^1.0.0"
resolve "^1.1.6"
rollup-plugin-stats@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/rollup-plugin-stats/-/rollup-plugin-stats-1.3.2.tgz#4dd088465cff3c00688e388787e240dff20aeed8"
integrity sha512-VVCNU//OBzroB73gPmGJIm6xkoi25t2MsPoV6LsWUt4BVXbnrKpYAY3CwwnAfiWzM2KL4PN3SeFe/jZ9Ky7WRQ==
rollup-plugin-stats@1.3.4:
version "1.3.4"
resolved "https://registry.yarnpkg.com/rollup-plugin-stats/-/rollup-plugin-stats-1.3.4.tgz#772f147bb195f7bd77877b95a7e0baf801aeaf94"
integrity sha512-N6PCNucOrRJxqzeAvpEyK9Wf4bxQlCCuIj5+n0XUB0plFU0u1VwjY0GDM6Z3lvs/KDhzJ2anYwxXUZVfmFw8LA==
rollup-plugin-webpack-stats@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/rollup-plugin-webpack-stats/-/rollup-plugin-webpack-stats-2.0.1.tgz#0ca7913227fc42db34ae51a74fa6742a6e8f5a1e"
integrity sha512-1JALgjz4RDe14SowXJxAsQqJxGnj6JF4TIRo0FistxspFr3TyYIWCpO8B5qaes7vfdKxSNTowJtuQTOlZOobMA==
rollup-plugin-webpack-stats@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/rollup-plugin-webpack-stats/-/rollup-plugin-webpack-stats-2.0.3.tgz#918dc46aa652a731bb62239c894e487bf8e5365a"
integrity sha512-tJJaYkP3yQctWRoAn7zh47DszOpwXVaDDmxriu48cohi7Nv/87l8Ry0vHnUd8e9MbpXuKZwEGQjqk1BjYLbYww==
dependencies:
rollup-plugin-stats "1.3.2"
rollup-plugin-stats "1.3.4"
rollup@^0.41.4, rollup@^2.43.1, rollup@^4.20.0, rollup@^4.5.1:
version "4.5.1"
@@ -15138,7 +15150,7 @@ ttl-set@^1.0.0:
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
integrity "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="
integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==
dependencies:
safe-buffer "^5.0.1"
@@ -15430,7 +15442,7 @@ urijs@^1.19.0:
resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.11.tgz#204b0d6b605ae80bea54bea39280cdb7c9f923cc"
integrity "sha1-IEsNa2Ba6AvqVL6jkoDNt8n5I8w= sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ=="
url-parse@^1.4.3, url-parse@^1.5.3:
url-parse@^1.5.3:
version "1.5.10"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
integrity "sha1-nTwvc2wddd070r5QfcwRHx4uqcE= sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ=="
@@ -15603,10 +15615,10 @@ vite-plugin-static-copy@^0.17.0:
fs-extra "^11.1.0"
picocolors "^1.0.0"
vite@^5.4.14:
version "5.4.14"
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.14.tgz#ff8255edb02134df180dcfca1916c37a6abe8408"
integrity sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==
vite@^5.4.15:
version "5.4.15"
resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.15.tgz#2941547f10ebb4bf9b0fa0da863c06711eb7e5e5"
integrity sha512-6ANcZRivqL/4WtwPGTKNaosuNJr5tWiftOC7liM7G9+rMb8+oeJeyzymDu4rTN93seySBmbjSfsS3Vzr19KNtA==
dependencies:
esbuild "^0.21.3"
postcss "^8.4.43"
@@ -16214,7 +16226,7 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity "sha1-ApTrPe4FAo0x7hpfosVWpqrxChs= sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
zod@^3.19.1, zod@^3.23.8:
version "3.23.8"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==
zod@^3.19.1, zod@^3.24.2:
version "3.24.2"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.2.tgz#8efa74126287c675e92f46871cfc8d15c34372b3"
integrity sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==