Compare commits

...

256 Commits

Author SHA1 Message Date
LongYinan
d38b487887 Merge remote-tracking branch 'origin/canary' into beta 2024-04-26 16:22:28 +08:00
EYHN
f015a11181 chore: bump blocksuite (#6707)
## Features
- https://github.com/toeverything/BlockSuite/pull/6785 @zzj3720

## Bugfix
- https://github.com/toeverything/BlockSuite/pull/6894 @Flrande
- https://github.com/toeverything/BlockSuite/pull/6893 @doouding
- https://github.com/toeverything/BlockSuite/pull/6891 @fundon
- https://github.com/toeverything/BlockSuite/pull/6879 @doouding
- https://github.com/toeverything/BlockSuite/pull/6885 @regischen
- https://github.com/toeverything/BlockSuite/pull/6888 @doouding
- https://github.com/toeverything/BlockSuite/pull/6890 @doouding
- https://github.com/toeverything/BlockSuite/pull/6889 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6887 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6877 @EYHN
- https://github.com/toeverything/BlockSuite/pull/6882 @fundon
- https://github.com/toeverything/BlockSuite/pull/6886 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6884 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6881 @fundon
- https://github.com/toeverything/BlockSuite/pull/6880 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6878 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6876 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6873 @pengx17

## Refactor

## Misc
- https://github.com/toeverything/BlockSuite/pull/6874 @doouding
2024-04-26 07:42:37 +00:00
LongYinan
1b7ed9cb12 Merge remote-tracking branch 'origin/canary' into beta 2024-04-26 13:03:09 +08:00
pengx17
cc17d3287e fix: open in chat does not work when the panel is not opened (#6703)
https://github.com/toeverything/AFFiNE/pull/6503 does not work with "open in chat".
IMO resetting tab whenever opening right panel does not make too much sense. @zanwei

fix AFF-951
2024-04-26 03:50:40 +00:00
CatsJuice
5b5c27b6ce feat(core): add ai pricing tip for plans page (#6704) 2024-04-26 03:28:28 +00:00
darkskygit
8bdd940ac8 fix: some event missing with multiple env webhook (#6705) 2024-04-26 02:56:09 +00:00
darkskygit
15c1e46680 chore: use default throttler replace strict throttler (#6698) 2024-04-26 02:18:20 +00:00
DarkSky
98c9ea53a8 Merge commit '2c228a35f8405e18c2886c7503f7027491efe7a3' into beta 2024-04-25 20:40:12 +08:00
forehalo
2c228a35f8 fix(server): stripe webhook calls in random order (#6702) 2024-04-25 12:22:07 +00:00
darkskygit
a0c219e036 feat: use default params if not provided (#6701) 2024-04-25 10:59:46 +00:00
forehalo
3297486e31 fix(server): skip throttle for currentUser (#6700) 2024-04-25 09:45:31 +00:00
pengx17
6237bf18ab build(electron): nsis typo (#6697) 2024-04-25 09:10:34 +00:00
LongYinan
db94cb361a Merge remote-tracking branch 'origin/canary' into beta 2024-04-25 16:46:19 +08:00
darkskygit
ea3f427918 fix: large transaction (#6695) 2024-04-25 08:33:21 +00:00
dependabot
74b7d024be chore(deps): bump rustls from 0.21.10 to 0.21.11 (#6683)
Bumps [rustls](https://github.com/rustls/rustls) from 0.21.10 to 0.21.11.
<details>
<summary>Commits</summary>
<ul>
<li><a href="7b8d1dbc1e"><code>7b8d1db</code></a> Prepare 0.21.11</li>
<li><a href="ebcb4782f2"><code>ebcb478</code></a> complete_io: bail out if progress is impossible</li>
<li><a href="20f35dfb6d"><code>20f35df</code></a> Regression test for <code>complete_io</code> infinite loop bug</li>
<li><a href="2f2aae15a4"><code>2f2aae1</code></a> Don't specially handle unauthenticated close_notify alerts</li>
<li><a href="e163587b98"><code>e163587</code></a> Don't deny warnings from nightly clippy</li>
<li><a href="9f864874cf"><code>9f86487</code></a> server::handy: fix new nightly clippy lint</li>
<li><a href="7e0e8ab599"><code>7e0e8ab</code></a> Correct assorted clippy warnings in test code</li>
<li><a href="3587d98f4e"><code>3587d98</code></a> Apply clippy suggestions from Rust 1.72</li>
<li><a href="d082e837b3"><code>d082e83</code></a> Address <code>clippy::redundant_static_lifetimes</code></li>
<li><a href="5e7a06ca45"><code>5e7a06c</code></a> Address <code>clippy::slow_vector_initialization</code></li>
<li>Additional commits viewable in <a href="https://github.com/rustls/rustls/compare/v/0.21.10...v/0.21.11">compare view</a></li>
</ul>
</details>
<br />

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=rustls&package-manager=cargo&previous-version=0.21.10&new-version=0.21.11)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/toeverything/AFFiNE/network/alerts).

</details>
2024-04-25 07:47:47 +00:00
LongYinan
9e9219205a Merge branch 'canary' into beta 2024-04-25 15:36:58 +08:00
renovate
6af849e875 chore: bump up supertest version to v7 (#6690)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [supertest](https://togithub.com/ladjs/supertest) | [`^6.3.4` -> `^7.0.0`](https://renovatebot.com/diffs/npm/supertest/6.3.4/7.0.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/supertest/7.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/supertest/7.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/supertest/6.3.4/7.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/supertest/6.3.4/7.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>ladjs/supertest (supertest)</summary>

### [`v7.0.0`](https://togithub.com/ladjs/supertest/releases/tag/v7.0.0)

[Compare Source](https://togithub.com/ladjs/supertest/compare/v6.3.4...v7.0.0)

-   Merge pull request [#&#8203;834](https://togithub.com/ladjs/supertest/issues/834) from Bruception/master  [`225118c`](https://togithub.com/ladjs/supertest/commit/225118c)
-   Fix TestAgent not inheriting Agent properties  [`f290431`](https://togithub.com/ladjs/supertest/commit/f290431)
-   fix: bump deps, drop Node.js v<14.16.0  [`1e18c20`](https://togithub.com/ladjs/supertest/commit/1e18c20)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4zMTMuMSIsInVwZGF0ZWRJblZlciI6IjM3LjMxMy4xIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyJdfQ==-->
2024-04-25 07:32:48 +00:00
pengx17
050efe3749 fix(core): update prompts (#6693)
update based on latest docs
2024-04-25 05:26:50 +00:00
EYHN
20a0d0b1db chore: bump blocksuite (#6694)
## Features
- https://github.com/toeverything/BlockSuite/pull/6863 @regischen
- https://github.com/toeverything/BlockSuite/pull/6852 @doouding
- https://github.com/toeverything/BlockSuite/pull/6860 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6856 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6849 @fundon

## Bugfix
- https://github.com/toeverything/BlockSuite/pull/6873 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6870 @golok727
- https://github.com/toeverything/BlockSuite/pull/6851 @golok727
- https://github.com/toeverything/BlockSuite/pull/6848 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6847 @fundon
- https://github.com/toeverything/BlockSuite/pull/6867 @EYHN
- https://github.com/toeverything/BlockSuite/pull/6868 @fundon
- https://github.com/toeverything/BlockSuite/pull/6869 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6865 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6866 @fundon
- https://github.com/toeverything/BlockSuite/pull/6864 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6824 @undefined
- https://github.com/toeverything/BlockSuite/pull/6859 @fundon
- https://github.com/toeverything/BlockSuite/pull/6853 @fundon
- https://github.com/toeverything/BlockSuite/pull/6854 @fundon
- https://github.com/toeverything/BlockSuite/pull/6845 @fourdim

## Refactor
- https://github.com/toeverything/BlockSuite/pull/6872 @fundon

## Misc
2024-04-25 05:12:02 +00:00
EYHN
c0cd33b65a fix(core): fix react error (#6692)
![CleanShot 2024-04-25 at 10.24.10.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/g3jz87HxbjOJpXV3FPT7/ec016ccd-d924-49d8-ad90-3ae0c47c5b8f.png)
2024-04-25 04:39:25 +00:00
pengx17
042be1216c build(electron): nsis updater compatibility fix (#6681) 2024-04-25 03:52:29 +00:00
pengx17
bfcf4a105e build(electron): add nsis build to release-desktop workflow (#6677)
Updater may break after this PR and will be fixed in the next one

After this PR, we will have two windows installer options
- affine-0.14.0-canary.9-canary-windows-x64.exe
- affine-0.14.0-canary.9-canary-windows-x64.nsis.exe (added)
2024-04-25 03:52:27 +00:00
pengx17
0b380f94c7 build(electron): add nsis script for windows installer (#6674)
This pr only includes how to bundle the app into an installer after package step.

todo (not in this pr)
- [ ] make sure updater can work for both nsis & squirrel
- [ ] integrate nsis build into github action workflow

Advantage over Squirrel:
- allowing user to specify the installation location
- better uninstaller

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/b75f1076-62e7-445c-bbf9-d7be00dbfc59.png)

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/c9ddc58c-512e-487e-80c8-7c4bd51482a8.png)

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/cfc5c281-e044-4929-a261-b02a4619117b.png)
2024-04-25 03:30:04 +00:00
forehalo
a697ebe340 feat(core): orm (#6536) 2024-04-25 03:03:45 +00:00
JimmFly
31b284a2d0 feat(core): add fallback component to member list (#6672)
<img width="549" alt="image" src="https://github.com/toeverything/AFFiNE/assets/102217452/7246002f-ebfb-4486-abbc-35bbee8fba0e">
2024-04-25 02:27:39 +00:00
CatsJuice
2a2b1cea28 feat(core): add indicator for general ai onboarding dialog (#6687) 2024-04-25 02:09:52 +00:00
renovate
21cbef4e20 chore: bump up cssnano version to v7 (#6691)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [cssnano](https://togithub.com/cssnano/cssnano) | [`^6.1.2` -> `^7.0.0`](https://renovatebot.com/diffs/npm/cssnano/6.1.2/7.0.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/cssnano/7.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/cssnano/7.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/cssnano/6.1.2/7.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/cssnano/6.1.2/7.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>cssnano/cssnano (cssnano)</summary>

### [`v7.0.0`](https://togithub.com/cssnano/cssnano/compare/cssnano-preset-advanced@6.1.2...cssnano-preset-advanced@7.0.0)

[Compare Source](https://togithub.com/cssnano/cssnano/compare/cssnano@6.1.2...cssnano@7.0.0)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4zMTMuMSIsInVwZGF0ZWRJblZlciI6IjM3LjMxMy4xIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyJdfQ==-->
2024-04-25 01:33:14 +00:00
CatsJuice
59214af3ad fix(core): adjust payment related copywriting (#6655) 2024-04-24 16:05:09 +00:00
fundon
0d8bab18df fix(core): should set type of uploaded file (#6689)
<img width="609" alt="Screenshot 2024-04-24 at 20 43 45" src="https://github.com/toeverything/AFFiNE/assets/27926/8e3897d7-2aab-491c-abfe-8ac553ea99b3">
2024-04-24 15:43:18 +00:00
JimmFly
350fec5397 feat(core): optimize the shift multi-selection function of doc list (#6675)
close TOV-701

https://github.com/toeverything/AFFiNE/assets/102217452/8813e603-1cc2-469f-a7c1-b18e49a14871
2024-04-24 07:56:13 +00:00
doouding
6525c99631 feat(server): new prompt for expand mind map (#6678)
feat(server): new prompt for expand mind map

feat: add migration file
2024-04-24 07:10:11 +00:00
EYHN
e5baa81a50 chore: bump blocksuite (#6673) 2024-04-23 08:39:23 +00:00
darkskygit
cbe9e10d44 chore: add copilot vision dev mode polyfill (#6671) 2024-04-23 07:41:02 +00:00
pengx17
f1b03989fa fix: error handling in sse (#6663) 2024-04-23 03:46:49 +00:00
darkskygit
64ad83f889 feat: handle stream error (#6653) 2024-04-23 03:46:46 +00:00
darkskygit
01a0f60d03 fix: history attachment query (#6670) 2024-04-23 03:34:59 +00:00
EYHN
c425cfa598 chore: bump blocksuite (#6666)
## Features
- https://github.com/toeverything/BlockSuite/pull/6842 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6836 @regischen

## Bugfix
- https://github.com/toeverything/BlockSuite/pull/6837 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6803 @lawvs
- https://github.com/toeverything/BlockSuite/pull/6843 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6838 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6834 @regischen
- https://github.com/toeverything/BlockSuite/pull/6835 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6831 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6827 @golok727
- https://github.com/toeverything/BlockSuite/pull/6828 @regischen

## Refactor

## Misc
2024-04-23 01:38:23 +00:00
regischen
a20a3fbbf8 feat: provide closeStream (#6659)
Upstream https://github.com/toeverything/blocksuite/pull/6836
2024-04-22 14:35:13 +00:00
CatsJuice
6ec97b27c4 feat(core): open desktop directly in subscription landing page (#6661) 2024-04-22 14:21:58 +00:00
pengx17
71a5be5385 fix(server): update prompts (#6664)
fix AFF-921
2024-04-22 14:09:53 +00:00
renovate
62e277d66c chore: bump up oxlint version to v0.3.1 (#6662)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [oxlint](https://oxc-project.github.io) ([source](https://togithub.com/oxc-project/oxc/tree/HEAD/npm/oxlint)) | [`0.3.0` -> `0.3.1`](https://renovatebot.com/diffs/npm/oxlint/0.3.0/0.3.1) | [![age](https://developer.mend.io/api/mc/badges/age/npm/oxlint/0.3.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/oxlint/0.3.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/oxlint/0.3.0/0.3.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/oxlint/0.3.0/0.3.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>oxc-project/oxc (oxlint)</summary>

### [`v0.3.1`](b29aabd6f1...99d46f9e48)

[Compare Source](b29aabd6f1...99d46f9e48)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4zMTMuMSIsInVwZGF0ZWRJblZlciI6IjM3LjMxMy4xIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyJdfQ==-->
2024-04-22 11:24:21 +00:00
CatsJuice
e13024580d feat(core): add ai subscription landing page (#6657) 2024-04-22 09:03:27 +00:00
EYHN
d36b5e14aa feat(core): try cloud link (#6660) 2024-04-22 08:51:02 +00:00
forehalo
94de6f5853 fix(server): always return created timestamp of chat messages (#6658) 2024-04-22 08:39:42 +00:00
EYHN
b7ade43c2e feat(core): adjust help island style (#6651) 2024-04-22 08:27:28 +00:00
EYHN
ab7282213b feat(core): adjust workspace avatar style (#6652)
![CleanShot 2024-04-22 at 12 13 04@2x](https://github.com/toeverything/AFFiNE/assets/13579374/225966de-6c2f-4bdc-a460-b96d15447808)
2024-04-22 08:16:41 +00:00
EYHN
9fd3f29d1b fix(core): fix home page init cloud (#6654) 2024-04-22 08:05:20 +00:00
renovate
6c84b7acac chore: bump up oxlint version to v0.3.0 (#6636)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [oxlint](https://oxc-project.github.io) ([source](https://togithub.com/oxc-project/oxc/tree/HEAD/npm/oxlint)) | [`0.2.17` -> `0.3.0`](https://renovatebot.com/diffs/npm/oxlint/0.2.17/0.3.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/oxlint/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/oxlint/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/oxlint/0.2.17/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/oxlint/0.2.17/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>oxc-project/oxc (oxlint)</summary>

### [`v0.3.0`](https://togithub.com/oxc-project/oxc/releases/tag/oxlint_v0.3.0): oxlint v0.3.0

[Compare Source](04f5fc0186...b29aabd6f1)

#### What's Changed

##### Potential breaking change

-   `-D all` no longer enables nursery rules, use `-D all -D nursery` instead

##### Features

-   support eslint globals by [@&#8203;Boshen](https://togithub.com/Boshen) in [https://github.com/oxc-project/oxc/pull/3038](https://togithub.com/oxc-project/oxc/pull/3038)
-   change no-empty-static-block to correctness
-   implement `--format checkstyle` by [@&#8203;Boshen](https://togithub.com/Boshen) in [https://github.com/oxc-project/oxc/pull/3044](https://togithub.com/oxc-project/oxc/pull/3044)
-   implement `--format unix` by [@&#8203;Boshen](https://togithub.com/Boshen) in [https://github.com/oxc-project/oxc/pull/3039](https://togithub.com/oxc-project/oxc/pull/3039)
-   implement fixer for `typescript-eslint/consistent-type-definitions` by [@&#8203;todor-a](https://togithub.com/todor-a) in [https://github.com/oxc-project/oxc/pull/3045](https://togithub.com/oxc-project/oxc/pull/3045)

##### Fixes

-   fix crashing with `unwrap` in import/no-cycle by [@&#8203;Boshen](https://togithub.com/Boshen) in [https://github.com/oxc-project/oxc/pull/3035](https://togithub.com/oxc-project/oxc/pull/3035)

**Full Changelog**: https://github.com/oxc-project/oxc/compare/oxlint_v0.2.18...oxlint_v0.3.0

### [`v0.2.18`](https://togithub.com/oxc-project/oxc/releases/tag/oxlint_v0.2.18): oxlint v0.2.18

[Compare Source](df11d10a22...04f5fc0186)

#### What's Changed

##### New Rules

-   correctness: eslint-plugin-unicorn no await in promise methods by [@&#8203;camc314](https://togithub.com/camc314) in [https://github.com/oxc-project/oxc/pull/2963](https://togithub.com/oxc-project/oxc/pull/2963)
-   correctness: eslint-plugin-unicorn no single promise in promise methods by [@&#8203;camc314](https://togithub.com/camc314) in [https://github.com/oxc-project/oxc/pull/2962](https://togithub.com/oxc-project/oxc/pull/2962)

##### Features

-   Add --jsdoc-plugin flag by [@&#8203;leaysgur](https://togithub.com/leaysgur) in [https://github.com/oxc-project/oxc/pull/2935](https://togithub.com/oxc-project/oxc/pull/2935)
-   Implement plugin-jsdoc/check-property-names by [@&#8203;leaysgur](https://togithub.com/leaysgur) in [https://github.com/oxc-project/oxc/pull/2989](https://togithub.com/oxc-project/oxc/pull/2989)
-   eslint/max-len by [@&#8203;woai3c](https://togithub.com/woai3c) in [https://github.com/oxc-project/oxc/pull/2874](https://togithub.com/oxc-project/oxc/pull/2874)
-   remove import/no-unresolved by [@&#8203;Boshen](https://togithub.com/Boshen) in [https://github.com/oxc-project/oxc/pull/3023](https://togithub.com/oxc-project/oxc/pull/3023)
-   support `oxlint-disable` alongside `eslint-disable` by [@&#8203;Boshen](https://togithub.com/Boshen) in [https://github.com/oxc-project/oxc/pull/3024](https://togithub.com/oxc-project/oxc/pull/3024)
-   jsdoc: Implement require-property rule by [@&#8203;leaysgur](https://togithub.com/leaysgur) in [https://github.com/oxc-project/oxc/pull/3011](https://togithub.com/oxc-project/oxc/pull/3011)
-   jsdoc: Implement require-property-(type|name|description) rules by [@&#8203;leaysgur](https://togithub.com/leaysgur) in [https://github.com/oxc-project/oxc/pull/3013](https://togithub.com/oxc-project/oxc/pull/3013)

#### New Contributors

-   [@&#8203;branchseer](https://togithub.com/branchseer) made their first contribution in [https://github.com/oxc-project/oxc/pull/2943](https://togithub.com/oxc-project/oxc/pull/2943)
-   [@&#8203;woai3c](https://togithub.com/woai3c) made their first contribution in [https://github.com/oxc-project/oxc/pull/2874](https://togithub.com/oxc-project/oxc/pull/2874)

**Full Changelog**: https://github.com/oxc-project/oxc/compare/oxlint_v0.2.17...oxlint_v0.2.18

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4zMDEuNCIsInVwZGF0ZWRJblZlciI6IjM3LjMxMy4xIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyJdfQ==-->
2024-04-22 07:27:26 +00:00
forehalo
e8bcb75602 fix(server): use post request to consume magic link token (#6656) 2024-04-22 07:15:26 +00:00
random-dudde
f288e3ee25 ci: bash escape vulnerability (#6640)
Co-authored-by: EYHN <cneyhn@gmail.com>
Co-authored-by: LongYinan <lynweklm@gmail.com>
2024-04-22 06:30:11 +00:00
CatsJuice
d7e08215d7 feat(core): show login modal if not logged in when using ai (#6632)
![CleanShot 2024-04-22 at 09.05.42.gif](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/LakojjjzZNf6ogjOVwKE/61b35d07-5d5a-4df9-859b-17cf7a9c20cf.gif)
2024-04-22 03:26:01 +00:00
renovate
efe3b0537e chore: bump up electron version to v30 (#6573)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [electron](https://togithub.com/electron/electron) | [`^29.3.0` -> `^30.0.0`](https://renovatebot.com/diffs/npm/electron/29.3.0/30.0.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/electron/30.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/electron/30.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/electron/29.3.0/30.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/electron/29.3.0/30.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>electron/electron (electron)</summary>

### [`v30.0.0`](https://togithub.com/electron/electron/compare/v29.3.0...v30.0.0)

[Compare Source](https://togithub.com/electron/electron/compare/v29.3.0...v30.0.0)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4yOTMuMCIsInVwZGF0ZWRJblZlciI6IjM3LjI5My4wIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5IiwibGFiZWxzIjpbImRlcGVuZGVuY2llcyJdfQ==-->
2024-04-22 03:13:26 +00:00
darkskygit
ae679a937f fix: copilot storage config (#6650) 2024-04-22 03:00:18 +00:00
pengx17
1dda0fd34c feat: add mixpanel tracking for ai (#6643) 2024-04-20 05:43:35 +00:00
renovate
b4a760574e chore: Lock file maintenance (#5249)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Update | Change |
|---|---|
| lockFileMaintenance | All locks refreshed |

🔧 This Pull Request updates lock files to use the latest dependency versions.

---

### Configuration

📅 **Schedule**: Branch creation - "before 4am on Monday" (UTC), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://togithub.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy44Ny4yIiwidXBkYXRlZEluVmVyIjoiMzcuMjQ1LjAiLCJ0YXJnZXRCcmFuY2giOiJjYW5hcnkifQ==-->
2024-04-19 20:14:13 +00:00
renovate
5077003e84 chore: bump up storybook-dark-mode version to v4 (#6136)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [storybook-dark-mode](https://togithub.com/hipstersmoothie/storybook-dark-mode) | [`^3.0.3` -> `^4.0.0`](https://renovatebot.com/diffs/npm/storybook-dark-mode/3.0.3/4.0.1) | [![age](https://developer.mend.io/api/mc/badges/age/npm/storybook-dark-mode/4.0.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/storybook-dark-mode/4.0.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/storybook-dark-mode/3.0.3/4.0.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/storybook-dark-mode/3.0.3/4.0.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>hipstersmoothie/storybook-dark-mode (storybook-dark-mode)</summary>

### [`v4.0.1`](https://togithub.com/hipstersmoothie/storybook-dark-mode/blob/HEAD/CHANGELOG.md#v401-Mon-Mar-18-2024)

[Compare Source](https://togithub.com/hipstersmoothie/storybook-dark-mode/compare/v4.0.0...v4.0.1)

🎉 This release contains work from a new contributor! 🎉

Thank you, Lauri Luotola ([@&#8203;leiit](https://togithub.com/leiit)), for all your work!

##### 🐛 Bug Fix

-   Fix `useDarkMode` to use correct channel [#&#8203;266](https://togithub.com/hipstersmoothie/storybook-dark-mode/pull/266) ([@&#8203;leiit](https://togithub.com/leiit))

##### 🔩 Dependency Updates

-   Update dependency [@&#8203;storybook/icons](https://togithub.com/storybook/icons) to v1.2.9 [#&#8203;253](https://togithub.com/hipstersmoothie/storybook-dark-mode/pull/253) ([@&#8203;renovate\[bot\]](https://togithub.com/renovate\[bot]))

##### Authors: 2

-   [@&#8203;renovate\[bot\]](https://togithub.com/renovate\[bot])
-   Lauri Luotola ([@&#8203;leiit](https://togithub.com/leiit))

***

### [`v4.0.0`](https://togithub.com/hipstersmoothie/storybook-dark-mode/blob/HEAD/CHANGELOG.md#v400-Fri-Mar-15-2024)

[Compare Source](https://togithub.com/hipstersmoothie/storybook-dark-mode/compare/v3.0.3...v4.0.0)

🎉 This release contains work from new contributors! 🎉

Thanks for all your work!

❤️ Sergey Kozlov ([@&#8203;dartess](https://togithub.com/dartess))

❤️ Rohan Poojary ([@&#8203;RohanPoojary1107](https://togithub.com/RohanPoojary1107))

##### 💥 Breaking Change

-   Storybook 8 [#&#8203;251](https://togithub.com/hipstersmoothie/storybook-dark-mode/pull/251) ([@&#8203;dartess](https://togithub.com/dartess))

##### ⚠️ Pushed to `master`

-   update auto ([@&#8203;hipstersmoothie](https://togithub.com/hipstersmoothie))

##### 📝 Documentation

-   Update addons import in readme code examples [#&#8203;243](https://togithub.com/hipstersmoothie/storybook-dark-mode/pull/243) ([@&#8203;RohanPoojary1107](https://togithub.com/RohanPoojary1107))

##### Authors: 3

-   Andrew Lisowski ([@&#8203;hipstersmoothie](https://togithub.com/hipstersmoothie))
-   Rohan Poojary ([@&#8203;RohanPoojary1107](https://togithub.com/RohanPoojary1107))
-   Sergey Kozlov ([@&#8203;dartess](https://togithub.com/dartess))

***

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4yNDUuMCIsInVwZGF0ZWRJblZlciI6IjM3LjMwMS40IiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-04-19 13:59:11 +00:00
CatsJuice
13e2a3dbae fix(core): remove sidebar user avatar menu item's rightArrow icon (#6633) 2024-04-19 13:47:28 +00:00
forehalo
098e717688 chore(server): remove useless free subscription enum (#6634) 2024-04-19 13:34:59 +00:00
darkskygit
657a5250ad feat: update chat prompt (#6639)
fix AFF-878
fix AFF-852 (maybe)
2024-04-19 12:34:02 +00:00
pengx17
94a70a5f3a chore: bump blocksuite (#6635)
## Features
- https://github.com/toeverything/BlockSuite/pull/6825 @regischen

## Bugfix
- https://github.com/toeverything/BlockSuite/pull/6828 @regischen
- https://github.com/toeverything/BlockSuite/pull/6823 @fundon
- https://github.com/toeverything/BlockSuite/pull/6822 @fundon
- https://github.com/toeverything/BlockSuite/pull/6826 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6794 @fundon
- https://github.com/toeverything/BlockSuite/pull/6821 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6781 @doouding
- https://github.com/toeverything/BlockSuite/pull/6820 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6819 @regischen
- https://github.com/toeverything/BlockSuite/pull/6817 @donteatfriedrice

## Refactor

## Misc
2024-04-19 09:50:17 +00:00
darkskygit
97669acb40 chore: enable ai feature in dev (#6618) 2024-04-19 08:12:53 +00:00
pengx17
6d5dbbd7f4 fix: move toTextStream to affine (#6628) 2024-04-19 07:59:24 +00:00
Brooooooklyn
6d62ba856c chore: apply backend-config to the graphql service (#6631) 2024-04-19 07:35:11 +00:00
EYHN
5e243de392 feat(server): auto refresh session (#6613) 2024-04-19 07:00:12 +00:00
EYHN
a2fa9149ff feat(templates): add stickers (#6629) 2024-04-19 06:47:29 +00:00
EYHN
4085cc6728 fix(core): fix subscribe link for pro plan (#6627) 2024-04-19 06:36:38 +00:00
JimmFly
9d412d22cb fix(core): drag and drop files into doc (#6630)
close #6625
2024-04-19 06:25:17 +00:00
pengx17
8c0732ddf1 fix: handle 401 errors (#6611)
upstream https://github.com/toeverything/blocksuite/pull/6809
2024-04-19 03:51:41 +00:00
pengx17
99bf7c79d1 chore: bump blocksuite (#6626)
## Features
- https://github.com/toeverything/BlockSuite/pull/6809 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6787 @Flrande
- https://github.com/toeverything/BlockSuite/pull/6774 @donteatfriedrice

## Bugfix
- https://github.com/toeverything/BlockSuite/pull/6817 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6811 @regischen
- https://github.com/toeverything/BlockSuite/pull/6814 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6813 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6812 @doouding
- https://github.com/toeverything/BlockSuite/pull/6815 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6808 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6804 @regischen
- https://github.com/toeverything/BlockSuite/pull/6797 @regischen
- https://github.com/toeverything/BlockSuite/pull/6802 @Saul-Mirone
- https://github.com/toeverything/BlockSuite/pull/6801 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6798 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6795 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6790 @lawvs
- https://github.com/toeverything/BlockSuite/pull/6778 @zkwolf
- https://github.com/toeverything/BlockSuite/pull/6792 @fundon

## Refactor

## Misc
- https://github.com/toeverything/BlockSuite/pull/6807 @c0sc0s
- https://github.com/toeverything/BlockSuite/pull/6806 @Saul-Mirone
- https://github.com/toeverything/BlockSuite/pull/6800 @fourdim
2024-04-19 03:51:38 +00:00
fundon
7772a103fa fix(server): dev user configuration issue (#6619) 2024-04-19 03:40:04 +00:00
darkskygit
3cc3af8d5d feat: remove message query (#6622)
related #6620
2024-04-18 16:30:10 +00:00
CatsJuice
7970d9b8c9 feat(core): add local ai onboarding dialog (#6600) 2024-04-18 15:48:20 +00:00
darkskygit
28f2ff24b9 fix: copilot blob process (#6612) 2024-04-18 15:36:30 +00:00
EYHN
6a23fe37a7 feat(core): reduce profile loading time (#6616) 2024-04-18 15:23:12 +00:00
pengx17
c3438fde21 fix(core): always create message for ai (#6620) 2024-04-18 15:09:36 +00:00
EYHN
08cd940e6b fix(server): fix unstable test (#6621) 2024-04-18 14:53:24 +00:00
darkskygit
b3b9e9a056 chore: cleanup outdated api (#6604) 2024-04-18 14:42:46 +00:00
EYHN
a537f8eb0b fix(electron): cookie expires too short (#6615) 2024-04-18 13:41:56 +00:00
EYHN
09832dc940 feat(core): add subscribe link (#6610) 2024-04-18 13:28:32 +00:00
fundon
5437c6567b fix(component): banner should not affect the editor layout (#6605)
https://github.com/toeverything/AFFiNE/assets/27926/6aacb16c-02ca-4693-afe8-628bef507811
2024-04-18 13:16:10 +00:00
CatsJuice
9cb6dcd93d feat(core): check user's subscription at ai onboarding stage (#6608) 2024-04-18 11:28:06 +00:00
pengx17
e232b0b285 fix: should not swallow copilot errors (#6609) 2024-04-18 09:59:31 +00:00
soumyasen10
19f8407d7f fix: disable default save page as (#6548)
Co-authored-by: Peng Xiao <pengxiao@outlook.com>
2024-04-18 16:19:03 +08:00
forehalo
d9e6561762 feat(server): add created timestamp to history (#6607) 2024-04-18 07:06:47 +00:00
pengx17
5fc56a20ac feat: adopt createMessage upload api (#6596) 2024-04-18 06:55:29 +00:00
Peng Xiao
10653eccbc fix: provide user info for copilot (#6606) 2024-04-18 11:10:17 +08:00
DarkSky
ccb3bed91e feat: add blob upload support for copilot (#6584) 2024-04-17 14:05:38 +00:00
pengx17
e806169f60 fix(core): missing copilot param (#6602) 2024-04-17 13:27:16 +00:00
pengx17
f9f0490190 fix(core): provide photoengine (#6574) 2024-04-17 13:27:11 +00:00
pengx17
03a7f9939e fix: ai error handling (#6588)
upstream: https://github.com/toeverything/blocksuite/pull/6775
2024-04-17 13:27:05 +00:00
pengx17
09a984d113 chore: bump blocksuite (#6601)
## Features
- https://github.com/toeverything/BlockSuite/pull/6763 @doouding
- https://github.com/toeverything/BlockSuite/pull/6780 @Saul-Mirone
- https://github.com/toeverything/BlockSuite/pull/6775 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6765 @donteatfriedrice

## Bugfix
- https://github.com/toeverything/BlockSuite/pull/6792 @fundon
- https://github.com/toeverything/BlockSuite/pull/6791 @fundon
- https://github.com/toeverything/BlockSuite/pull/6776 @fundon
- https://github.com/toeverything/BlockSuite/pull/6779 @Flrande
- https://github.com/toeverything/BlockSuite/pull/6771 @fundon
- https://github.com/toeverything/BlockSuite/pull/6769 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6768 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6758 @Flrande
- https://github.com/toeverything/BlockSuite/pull/6743 @fourdim
- https://github.com/toeverything/BlockSuite/pull/6762 @regischen

## Refactor
- https://github.com/toeverything/BlockSuite/pull/6786 @regischen
- https://github.com/toeverything/BlockSuite/pull/6764 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6772 @fundon

## Misc
- https://github.com/toeverything/BlockSuite/pull/6766 @fourdim
2024-04-17 13:27:00 +00:00
JimmFly
bcee1bbd81 feat(component): adjust notify styles (#6578)
Change the styles of `notify.error`, `notify.warning`, and `notify.success` from `alert` to `normal`
Add custom `iconColor` option
2024-04-17 12:36:42 +00:00
JimmFly
8b66c1d752 fix(component): retrieve lost scroll bars in editor (#6597)
close TOV-805
close #6589
2024-04-17 11:59:12 +00:00
EYHN
f86646b931 feat(core): enrich pwa manifest.json (#6599) 2024-04-17 19:14:25 +08:00
liuyi
07aeab6ac8 fix(server): app cannot start in graphql only flavor (#6598) 2024-04-17 18:29:04 +08:00
EYHN
4b933466f4 fix(storybook): remove storybook testing (#6595)
remove tests/storybook

@affine/components storybook still exists
2024-04-17 17:27:33 +08:00
EYHN
ed96c4ece4 fix(core): fix navigate not working (#6594)
navigate sometimes doesn't work

It seems that we shouldn't pass the parent component's navigate function to the child component, but adding an effect to delay the child component rendering seems to work.
2024-04-17 16:43:29 +08:00
DarkSky
ec186a65e7 fix(server): avoid store unpaid subscriptions (#6593) 2024-04-17 16:43:17 +08:00
EYHN
c83d090282 fix(core): revalidate subscription info when open ai usage panel (#6591) 2024-04-17 16:36:12 +08:00
liuyi
e53d5e2e3d chore(server): clean up throttler (#6326) 2024-04-17 16:32:26 +08:00
forehalo
651a99a48e fix(server): avoid store unpaid subscriptions 2024-04-17 16:25:03 +08:00
liuyi
5b315bfc81 fix(server): only returns active subscriptions (#6590) 2024-04-17 16:19:34 +08:00
EYHN
6ca174bb14 fix(core): revalidate subscription info when open ai usage panel 2024-04-17 15:54:01 +08:00
EYHN
c7373d4651 feat(infra): framework (#6403)
* Business are implemented in modules
* All modules are split into `services`, `entities`, `stores`, to achieve better abstraction
* most of asynchronous loading has been changed to `effect` implementation, with a good retry strategy
* critical network requests have been cached, app can be used offline normally.
* user session has been cached, and will be used offline.
* `workspace list provider` and `factory provider` are unified into `workspace flavor provider`
* `page` has been rename to `doc`, unified with `blocksuite`
2024-04-17 15:20:13 +08:00
EYHN
06fda3b62c feat(infra): framework 2024-04-17 15:09:09 +08:00
forehalo
ab17a05df3 fix(core): give page reference correct link (#6587) 2024-04-17 03:56:24 +00:00
forehalo
66a272fb8b fix(core): unable to redirect to same origin paths (#6586) 2024-04-17 03:25:31 +00:00
darkskygit
83d8587a45 fix: missing feature upsert (#6585) 2024-04-17 02:01:01 +00:00
forehalo
e1c292b8b5 feat(server): support registering ai early access users (#6565) 2024-04-16 13:54:08 +00:00
darkskygit
677c4711df feat: unsplash api proxy (#6572) 2024-04-16 13:33:07 +00:00
CatsJuice
bb329944ed feat(core): optimize ai onboarding trigger logic (#6579)
- don't open edgeless ai-onboarding dialog until general ai onboarding and setting modal closed
- clip edgeless ai onboarding thumb to avoid "black border"
- correct "try for free"
- replace edgeless ai onboarding lottie resources
2024-04-16 13:18:10 +00:00
CatsJuice
c222cf7b96 fix(core): wrong pricing state in billing ai card (#6583) 2024-04-16 13:07:23 +00:00
JimmFly
f6fcbd8ea9 fix(core): signIn page style (#6582) 2024-04-16 12:56:12 +00:00
darkskygit
3d15e8353b feat: refresh prompts (#6568) 2024-04-16 10:26:54 +00:00
Brooooooklyn
b1eb0d2bc1 chore: remove vite from resolutions (#6580) 2024-04-16 10:15:56 +00:00
darkskygit
1b0864eb60 feat: check quota correctly (#6561) 2024-04-16 09:41:48 +00:00
regischen
0ca8a23dd8 fix: move help land position (#6571)
Before:
<img width="1614" alt="image" src="https://github.com/toeverything/AFFiNE/assets/58546692/24d4c550-4070-48a5-a851-40d6f26ea120">

After:
<img width="1106" alt="image" src="https://github.com/toeverything/AFFiNE/assets/58546692/88e1b7ea-8a69-45a2-9beb-780dfd1bd996">
<img width="949" alt="image" src="https://github.com/toeverything/AFFiNE/assets/58546692/8fd328c5-e8e8-402a-b18e-436056d2374f">

Cause the icon will be on top of chat input.
2024-04-16 08:11:37 +00:00
pengx17
ebb38a42a0 fix: temporary fix for running electron tests (#6576)
a workaround for spawn EINVAL error.
Found this issue according to https://github.com/node-red/node-red/pull/4652

~~In our case the electron app is spawned by playwright. I am still investigating the issue but don't know how long it takes.~~

Upstream fix: https://github.com/microsoft/playwright/pull/30382
2024-04-16 06:57:17 +00:00
forehalo
37750a820d fix(server): give s3 client a default request timeout (#6483) 2024-04-16 10:28:27 +08:00
renovate
a98471daa2 chore: bump up @testing-library/react version to v15 (#6531)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [@testing-library/react](https://togithub.com/testing-library/react-testing-library) | [`^14.2.1` -> `^15.0.0`](https://renovatebot.com/diffs/npm/@testing-library%2freact/14.2.1/15.0.2) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@testing-library%2freact/15.0.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@testing-library%2freact/15.0.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@testing-library%2freact/14.2.1/15.0.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@testing-library%2freact/14.2.1/15.0.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>testing-library/react-testing-library (@&#8203;testing-library/react)</summary>

### [`v15.0.2`](https://togithub.com/testing-library/react-testing-library/compare/v15.0.1...c63b873072d62c858959c2a19e68f8e2cc0b11be)

[Compare Source](https://togithub.com/testing-library/react-testing-library/compare/v15.0.1...v15.0.2)

### [`v15.0.1`](https://togithub.com/testing-library/react-testing-library/compare/v15.0.0...1645d21950ab8e3c6740b7e51b8a179a4c975c24)

[Compare Source](https://togithub.com/testing-library/react-testing-library/compare/v15.0.0...v15.0.1)

### [`v15.0.0`](https://togithub.com/testing-library/react-testing-library/compare/v14.3.0...787cb85f8baa3d2e2a9916b7dad12c0a76d787a4)

[Compare Source](https://togithub.com/testing-library/react-testing-library/compare/v14.3.1...v15.0.0)

### [`v14.3.1`](https://togithub.com/testing-library/react-testing-library/releases/tag/v14.3.1)

[Compare Source](https://togithub.com/testing-library/react-testing-library/compare/v14.3.0...v14.3.1)

##### Bug Fixes

-   Stop using nullish coalescing ([#&#8203;1300](https://togithub.com/testing-library/react-testing-library/issues/1300)) ([8434a24](8434a24ce7))

### [`v14.3.0`](https://togithub.com/testing-library/react-testing-library/compare/v14.2.2...9c4a46d5b9923c21c936d206614a8febcc939fc2)

[Compare Source](https://togithub.com/testing-library/react-testing-library/compare/v14.2.2...v14.3.0)

### [`v14.2.2`](https://togithub.com/testing-library/react-testing-library/compare/v14.2.1...3da62fd9741ca74bcd0d2bc668ba76a2d8f3751f)

[Compare Source](https://togithub.com/testing-library/react-testing-library/compare/v14.2.1...v14.2.2)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4yNjkuMiIsInVwZGF0ZWRJblZlciI6IjM3LjI5My4wIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-04-15 14:49:10 +00:00
fundon
b2ca8249c1 fix(core): should choose different models based on content or attachments (#6567) 2024-04-15 13:12:57 +00:00
regischen
4b24722f3d chore: bump blocksuite (#6566)
## Features

## Bugfix
- https://github.com/toeverything/BlockSuite/pull/6761 @regischen
- https://github.com/toeverything/BlockSuite/pull/6760 @fundon

## Refactor

## Misc
2024-04-15 12:59:01 +00:00
pengx17
af2b3a43bd chore: bump bs (#6563)
## Features

## Bugfix
- https://github.com/toeverything/BlockSuite/pull/6760 @fundon
- https://github.com/toeverything/BlockSuite/pull/6757 @fundon

## Refactor

## Misc
2024-04-15 10:29:54 +00:00
regischen
50292ba008 feat: add slide action (#6560) 2024-04-15 09:47:46 +00:00
fundon
d00879aceb feat: ai create an image (#6538)
Related to https://github.com/toeverything/blocksuite/pull/6746
2024-04-15 09:32:21 +00:00
pengx17
da320957ed fix: bump bs (#6562)
## Features
- https://github.com/toeverything/BlockSuite/pull/6759 @regischen
- https://github.com/toeverything/BlockSuite/pull/6746 @fundon
- https://github.com/toeverything/BlockSuite/pull/6756 @regischen

## Bugfix
- https://github.com/toeverything/BlockSuite/pull/6757 @fundon
- https://github.com/toeverything/BlockSuite/pull/6750 @fundon

## Refactor

## Misc
2024-04-15 09:20:04 +00:00
regischen
a15ceb8dde fix: add action field in histories gql (#6558) 2024-04-15 08:15:47 +00:00
JimmFly
cbba5d3c17 fix(core): the page flashes when click the share button (#6557)
close TOV-706
2024-04-15 07:39:03 +00:00
CatsJuice
b93e79c59d feat(core): ai onboarding for edgeless mode (#6556) 2024-04-15 07:25:37 +00:00
CatsJuice
257e946d5d feat(core): add ai onboarding (#6544) 2024-04-15 07:25:31 +00:00
EYHN
8bb597d7ad fix(core): fix flaky e2e (#6559) 2024-04-15 07:11:36 +00:00
darkskygit
1a3d1a5421 fix: upgrade plan correctly (#6543) 2024-04-15 06:20:24 +00:00
JimmFly
1217ef258b feat(core): copy to the clipboard whilst creating a shared link (#6555)
close TOV-797
2024-04-15 06:08:45 +00:00
JimmFly
c25c99d75f chore(core): update i18n resources (#6533) 2024-04-15 05:56:56 +00:00
pengx17
6fe0c4be6c fix(electron): close app while on fullscreen should be minimize instead (#6534) 2024-04-15 05:44:58 +00:00
pengx17
e1eb925704 refactor(core): remove copilot client from presets (#6546)
depends on https://github.com/toeverything/blocksuite/pull/6748
2024-04-15 05:31:32 +00:00
pengx17
2b69fde937 chore(core): bump bs (#6553)
## Features

## Bugfix
- https://github.com/toeverything/BlockSuite/pull/6750 @fundon
- https://github.com/toeverything/BlockSuite/pull/6747 @fundon
- https://github.com/toeverything/BlockSuite/pull/6753 @regischen
- https://github.com/toeverything/BlockSuite/pull/6744 @donteatfriedrice

## Refactor
- https://github.com/toeverything/BlockSuite/pull/6748 @pengx17

## Misc
2024-04-15 05:31:29 +00:00
pengx17
9151a5d5e4 fix: storybook build issue (#6554) 2024-04-15 05:19:42 +00:00
Brooooooklyn
c76c1b6abd ci: fix environment variables config in deploy/release (#6552) 2024-04-15 04:27:43 +00:00
donteatfriedrice
7b35722288 feat: bump blocksuite (#6541)
## Features
- https://github.com/toeverything/BlockSuite/pull/6737 @Saul-Mirone
- https://github.com/toeverything/BlockSuite/pull/6735 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6740 @regischen

## Bugfix
- https://github.com/toeverything/BlockSuite/pull/6744 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6742 @doouding

## Refactor

## Misc
- https://github.com/toeverything/BlockSuite/pull/6741 @fundon
2024-04-15 03:06:38 +00:00
CatsJuice
bb1294f425 fix(core): description of cancel is incorrect when cloud subscription is activated (#6530) 2024-04-15 02:54:41 +00:00
EYHN
d11d69ddf6 feat(core): move help island to workbench (#6535) 2024-04-15 02:29:15 +00:00
EYHN
1656b33ce3 feat(core): use zip snapshot for onboarding page (#6495) 2024-04-15 02:16:08 +00:00
pengx17
9b620ecbc9 fix(core): use backend prompts (#6542) 2024-04-12 21:29:14 +08:00
darkskygit
2336638996 fix: cannot query chat history (#6539) 2024-04-12 12:16:43 +00:00
darkskygit
fc51b68674 fix: pick copilot provider depend on model (#6540) 2024-04-12 12:01:39 +00:00
JimmFly
62f90e5f10 fix(core): restore lost activation state of sidebar menu items (#6520)
https://github.com/toeverything/AFFiNE/assets/102217452/41a53a43-e17d-41b5-b8e5-2ca42bcfae0a
2024-04-12 10:36:04 +00:00
CatsJuice
6a535b94c3 feat(core): create and open cloud workspace if not exists after logged in (#6511)
Only execute when `initCloud=true` is specified in the URL search params.
2024-04-12 09:55:45 +00:00
darkskygit
e77475aca5 feat: detailed copilot histories (#6523) 2024-04-12 08:39:32 +00:00
EYHN
9e7a2fcf0e feat(server): add pro quota to dev user (#6532) 2024-04-12 06:45:18 +00:00
EYHN
1e12d4a2cb feat(core): remove ai from experimental features (#6529) 2024-04-12 06:45:10 +00:00
JimmFly
6112e977cc feat(core): adjust the display settings to be independent for each workspace (#6502)
close TOV-793
2024-04-12 06:32:31 +00:00
JimmFly
9c38acf081 feat(core): add new doc button to collection item (#6521)
https://github.com/toeverything/AFFiNE/assets/102217452/3da65392-cee8-4f51-a05d-82e27027fa78
2024-04-12 06:19:11 +00:00
CatsJuice
d40052c748 feat(core): add ai usage in account-setting (#6516) 2024-04-12 06:06:11 +00:00
regischen
244e3fd71b fix: change action (#6519) 2024-04-12 05:17:10 +00:00
pengx17
8769aef0aa fix(core): disable micromark debug output (#6528) 2024-04-12 03:58:53 +00:00
pengx17
4a67c84c73 fix(core): chat panel display on cont in chat (#6527) 2024-04-12 03:58:43 +00:00
fundon
13b39fc5f3 feat(core): ai images (#6506) 2024-04-12 03:58:33 +00:00
pengx17
1697cd76fe chore(core): bump blocksuite (#6525)
## Features
- https://github.com/toeverything/BlockSuite/pull/6728 @fundon
- https://github.com/toeverything/BlockSuite/pull/6714 @doouding
- https://github.com/toeverything/BlockSuite/pull/6733 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6560 @golok727
- https://github.com/toeverything/BlockSuite/pull/6727 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6645 @regischen
- https://github.com/toeverything/BlockSuite/pull/6724 @fundon
- https://github.com/toeverything/BlockSuite/pull/6719 @zzj3720
- https://github.com/toeverything/BlockSuite/pull/6682 @donteatfriedrice

## Bugfix
- https://github.com/toeverything/BlockSuite/pull/6734 @Flrande
- https://github.com/toeverything/BlockSuite/pull/6732 @fourdim
- https://github.com/toeverything/BlockSuite/pull/6726 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6721 @Flrande
- https://github.com/toeverything/BlockSuite/pull/6725 @fundon
- https://github.com/toeverything/BlockSuite/pull/6716 @golok727
- https://github.com/toeverything/BlockSuite/pull/6723 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6722 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6718 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6702 @Tzyito
- https://github.com/toeverything/BlockSuite/pull/6711 @Tzyito
- https://github.com/toeverything/BlockSuite/pull/6694 @fundon
- https://github.com/toeverything/BlockSuite/pull/6717 @golok727

## Refactor
- https://github.com/toeverything/BlockSuite/pull/6672 @Saul-Mirone

## Misc
- https://github.com/toeverything/BlockSuite/pull/6720 @raintoway
2024-04-12 03:58:25 +00:00
pengx17
f03e20b97e fix(core): should pass mode to AffineEditorContainer (#6522)
Without mode there will be some features missing in blocksuite
2024-04-11 11:17:14 +00:00
renovate
66231e0e41 chore: bump up oxlint version to v0.2.17 (#5778)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [oxlint](https://oxc-project.github.io) ([source](https://togithub.com/oxc-project/oxc/tree/HEAD/npm/oxlint)) | [`0.2.14` -> `0.2.17`](https://renovatebot.com/diffs/npm/oxlint/0.2.14/0.2.17) | [![age](https://developer.mend.io/api/mc/badges/age/npm/oxlint/0.2.17?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/oxlint/0.2.17?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/oxlint/0.2.14/0.2.17?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/oxlint/0.2.14/0.2.17?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>oxc-project/oxc (oxlint)</summary>

### [`v0.2.17`](https://togithub.com/oxc-project/oxc/releases/tag/oxlint_v0.2.17): oxlint v0.2.17

[Compare Source](7066d55153...df11d10a22)

##### What's Changed

-   feat(linter): eslint-plugin-jest/prefer-lowercase-title by [@&#8203;eryue0220](https://togithub.com/eryue0220) in [https://github.com/oxc-project/oxc/pull/2911](https://togithub.com/oxc-project/oxc/pull/2911)
-   feat(linter): typescript-eslint/consistent-type-definitions by [@&#8203;todor-a](https://togithub.com/todor-a) in [https://github.com/oxc-project/oxc/pull/2885](https://togithub.com/oxc-project/oxc/pull/2885)
-   fix(cli): fix `oxlint --format json` yields 0 files to lint by [@&#8203;Boshen](https://togithub.com/Boshen) in [https://github.com/oxc-project/oxc/pull/2940](https://togithub.com/oxc-project/oxc/pull/2940)
-   fix(cli): if format is json do not print summary information ([#&#8203;2899](https://togithub.com/oxc-project/oxc/issues/2899)) by [@&#8203;kalvenschraut](https://togithub.com/kalvenschraut) in [https://github.com/oxc-project/oxc/pull/2925](https://togithub.com/oxc-project/oxc/pull/2925)
-   fix(linter): import/no-cycle ignore type-only imports by [@&#8203;JohnDaly](https://togithub.com/JohnDaly) in [https://github.com/oxc-project/oxc/pull/2924](https://togithub.com/oxc-project/oxc/pull/2924)
-   refactor(semantic/jsdoc): Rework JSDoc struct for better Span handling by [@&#8203;leaysgur](https://togithub.com/leaysgur) in [https://github.com/oxc-project/oxc/pull/2917](https://togithub.com/oxc-project/oxc/pull/2917)

##### New Contributors

-   [@&#8203;bradzacher](https://togithub.com/bradzacher) made their first contribution in [https://github.com/oxc-project/oxc/pull/2938](https://togithub.com/oxc-project/oxc/pull/2938)

**Full Changelog**: https://github.com/oxc-project/oxc/compare/oxlint_v0.2.16...oxlint_v0.2.17

### [`v0.2.16`](https://togithub.com/oxc-project/oxc/releases/tag/oxlint_v0.2.16): oxlint v0.2.16

[Compare Source](e7307ed23c...7066d55153)

#### What's Changed

-   feat(linter): [@&#8203;typescript-eslint/prefer-for-of](https://togithub.com/typescript-eslint/prefer-for-of) by [@&#8203;charnog](https://togithub.com/charnog) in [https://github.com/oxc-project/oxc/pull/2789](https://togithub.com/oxc-project/oxc/pull/2789)
-   feat(linter): Implement jsdoc/check-access by [@&#8203;leaysgur](https://togithub.com/leaysgur) in [https://github.com/oxc-project/oxc/pull/2642](https://togithub.com/oxc-project/oxc/pull/2642)
-   feat(linter): Implement jsdoc/empty-tags by [@&#8203;leaysgur](https://togithub.com/leaysgur) in [https://github.com/oxc-project/oxc/pull/2893](https://togithub.com/oxc-project/oxc/pull/2893)
-   feat(linter): eslint-plugin-jest/prefer-mock-promise-sorthand by [@&#8203;eryue0220](https://togithub.com/eryue0220) in [https://github.com/oxc-project/oxc/pull/2864](https://togithub.com/oxc-project/oxc/pull/2864)
-   feat(linter/import): Add `ignoreTypes` option for the `import/no-cycle` rule by [@&#8203;JohnDaly](https://togithub.com/JohnDaly) in [https://github.com/oxc-project/oxc/pull/2905](https://togithub.com/oxc-project/oxc/pull/2905)
-   fix(ast): `FinallyClause` won't get visited as `BlockStatement` anymore. by [@&#8203;rzvxa](https://togithub.com/rzvxa) in [https://github.com/oxc-project/oxc/pull/2881](https://togithub.com/oxc-project/oxc/pull/2881)
-   fix(linter): handle self closing script tags in astro partial loader ([#&#8203;2017](https://togithub.com/oxc-project/oxc/issues/2017)) by [@&#8203;kalvenschraut](https://togithub.com/kalvenschraut) in [https://github.com/oxc-project/oxc/pull/2907](https://togithub.com/oxc-project/oxc/pull/2907)
-   fix(linter): svelte partial loader handle generics ([#&#8203;2875](https://togithub.com/oxc-project/oxc/issues/2875)) by [@&#8203;kalvenschraut](https://togithub.com/kalvenschraut) in [https://github.com/oxc-project/oxc/pull/2906](https://togithub.com/oxc-project/oxc/pull/2906)

#### New Contributors

-   [@&#8203;charnog](https://togithub.com/charnog) made their first contribution in [https://github.com/oxc-project/oxc/pull/2789](https://togithub.com/oxc-project/oxc/pull/2789)
-   [@&#8203;kalvenschraut](https://togithub.com/kalvenschraut) made their first contribution in [https://github.com/oxc-project/oxc/pull/2906](https://togithub.com/oxc-project/oxc/pull/2906)
-   [@&#8203;JohnDaly](https://togithub.com/JohnDaly) made their first contribution in [https://github.com/oxc-project/oxc/pull/2905](https://togithub.com/oxc-project/oxc/pull/2905)

**Full Changelog**: https://github.com/oxc-project/oxc/compare/oxlint_v0.2.15...oxlint_v0.2.16

### [`v0.2.15`](https://togithub.com/oxc-project/oxc/releases/tag/oxlint_v0.2.15): oxlint v0.2.15

[Compare Source](b1343d7bcb...e7307ed23c)

#### What's Changed

-   feat(linter): default_param_last by [@&#8203;JoSeBu1](https://togithub.com/JoSeBu1) in [https://github.com/oxc-project/oxc/pull/2756](https://togithub.com/oxc-project/oxc/pull/2756)
-   feat(linter): eslint-plugin-jest/no-untyped-mock-factory by [@&#8203;eryue0220](https://togithub.com/eryue0220) in [https://github.com/oxc-project/oxc/pull/2807](https://togithub.com/oxc-project/oxc/pull/2807)
-   feat(linter): eslint-plugin-jest/prefer-comparison-matcher by [@&#8203;eryue0220](https://togithub.com/eryue0220) in [https://github.com/oxc-project/oxc/pull/2806](https://togithub.com/oxc-project/oxc/pull/2806)
-   feat(linter): eslint-plugin-react checked-requires-onchange-or-readonly by [@&#8203;keita-hino](https://togithub.com/keita-hino) in [https://github.com/oxc-project/oxc/pull/2754](https://togithub.com/oxc-project/oxc/pull/2754)
-   feat(linter): eslint/no-iterator by [@&#8203;JoSeBu1](https://togithub.com/JoSeBu1) in [https://github.com/oxc-project/oxc/pull/2758](https://togithub.com/oxc-project/oxc/pull/2758)
-   feat(linter): fallback to the default tsconfig path by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [https://github.com/oxc-project/oxc/pull/2842](https://togithub.com/oxc-project/oxc/pull/2842)
-   feat(linter): no_script_url by [@&#8203;JoSeBu1](https://togithub.com/JoSeBu1) in [https://github.com/oxc-project/oxc/pull/2761](https://togithub.com/oxc-project/oxc/pull/2761)
-   feat(linter/import) check deep namespace in namespace rule by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [https://github.com/oxc-project/oxc/pull/2805](https://togithub.com/oxc-project/oxc/pull/2805)
-   feat(linter/import) check module import in no_duplicates by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [https://github.com/oxc-project/oxc/pull/2771](https://togithub.com/oxc-project/oxc/pull/2771)
-   feat(linter/import) check type import in no_duplicates by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [https://github.com/oxc-project/oxc/pull/2777](https://togithub.com/oxc-project/oxc/pull/2777)
-   feat(linter/import) support allow_computed option in namespace by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [https://github.com/oxc-project/oxc/pull/2840](https://togithub.com/oxc-project/oxc/pull/2840)
-   feat(linter/import) support check re-export in named by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [https://github.com/oxc-project/oxc/pull/2769](https://togithub.com/oxc-project/oxc/pull/2769)
-   feat(linter/import): ignore type-only imports and exports in no_unresolved by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [https://github.com/oxc-project/oxc/pull/2849](https://togithub.com/oxc-project/oxc/pull/2849)
-   fix(linter/import): false positive for indirect export in namespace by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [https://github.com/oxc-project/oxc/pull/2862](https://togithub.com/oxc-project/oxc/pull/2862)
-   fix(linter/import): ignore export declaration in no-duplicates by [@&#8203;Dunqing](https://togithub.com/Dunqing) in [https://github.com/oxc-project/oxc/pull/2863](https://togithub.com/oxc-project/oxc/pull/2863)
-   fix(linter/max-lines): only report codes that exceed the line limit by [@&#8203;mysteryven](https://togithub.com/mysteryven) in [https://github.com/oxc-project/oxc/pull/2778](https://togithub.com/oxc-project/oxc/pull/2778)
-   fix(parser): add support for empty module declaration by [@&#8203;rzvxa](https://togithub.com/rzvxa) in [https://github.com/oxc-project/oxc/pull/2834](https://togithub.com/oxc-project/oxc/pull/2834)

#### New Contributors

-   [@&#8203;rzvxa](https://togithub.com/rzvxa) made their first contribution in [https://github.com/oxc-project/oxc/pull/2764](https://togithub.com/oxc-project/oxc/pull/2764)

**Full Changelog**: https://github.com/oxc-project/oxc/compare/oxlint_v0.2.14...oxlint_v0.2.15

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4xNTMuMiIsInVwZGF0ZWRJblZlciI6IjM3LjI2OS4yIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-04-11 10:51:02 +00:00
JimmFly
7a1eb63d42 fix(core): unexpected routing jump behavior (#6524)
https://github.com/toeverything/AFFiNE/assets/102217452/b4dba402-b07d-4f8e-a118-a35eb6e7d317
2024-04-11 10:30:45 +00:00
renovate
313952c45d chore: bump up all non-major dependencies (#6235)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [@aws-sdk/client-s3](https://togithub.com/aws/aws-sdk-js-v3/tree/main/clients/client-s3) ([source](https://togithub.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3)) | [`3.537.0` -> `3.552.0`](https://renovatebot.com/diffs/npm/@aws-sdk%2fclient-s3/3.537.0/3.552.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@aws-sdk%2fclient-s3/3.552.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@aws-sdk%2fclient-s3/3.552.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@aws-sdk%2fclient-s3/3.537.0/3.552.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@aws-sdk%2fclient-s3/3.537.0/3.552.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@electron-forge/maker-base](https://togithub.com/electron/forge) | [`7.3.0` -> `7.3.1`](https://renovatebot.com/diffs/npm/@electron-forge%2fmaker-base/7.3.0/7.3.1) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@electron-forge%2fmaker-base/7.3.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@electron-forge%2fmaker-base/7.3.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@electron-forge%2fmaker-base/7.3.0/7.3.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@electron-forge%2fmaker-base/7.3.0/7.3.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@napi-rs/cli](https://togithub.com/napi-rs/napi-rs) | [`3.0.0-alpha.43` -> `3.0.0-alpha.46`](https://renovatebot.com/diffs/npm/@napi-rs%2fcli/3.0.0-alpha.43/3.0.0-alpha.46) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@napi-rs%2fcli/3.0.0-alpha.46?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@napi-rs%2fcli/3.0.0-alpha.46?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@napi-rs%2fcli/3.0.0-alpha.43/3.0.0-alpha.46?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@napi-rs%2fcli/3.0.0-alpha.43/3.0.0-alpha.46?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@nx/vite](https://nx.dev) ([source](https://togithub.com/nrwl/nx/tree/HEAD/packages/vite)) | [`18.1.2` -> `18.2.4`](https://renovatebot.com/diffs/npm/@nx%2fvite/18.1.2/18.2.4) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@nx%2fvite/18.2.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@nx%2fvite/18.2.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@nx%2fvite/18.1.2/18.2.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@nx%2fvite/18.1.2/18.2.4?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@opentelemetry/exporter-prometheus](https://togithub.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-exporter-prometheus) ([source](https://togithub.com/open-telemetry/opentelemetry-js)) | [`^0.49.0` -> `^0.50.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2fexporter-prometheus/0.49.1/0.50.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2fexporter-prometheus/0.50.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@opentelemetry%2fexporter-prometheus/0.50.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@opentelemetry%2fexporter-prometheus/0.49.1/0.50.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2fexporter-prometheus/0.49.1/0.50.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@opentelemetry/instrumentation](https://togithub.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation) ([source](https://togithub.com/open-telemetry/opentelemetry-js)) | [`^0.49.0` -> `^0.50.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation/0.49.1/0.50.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation/0.50.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@opentelemetry%2finstrumentation/0.50.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@opentelemetry%2finstrumentation/0.49.1/0.50.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation/0.49.1/0.50.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@opentelemetry/instrumentation-graphql](https://togithub.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-instrumentation-graphql#readme) ([source](https://togithub.com/open-telemetry/opentelemetry-js-contrib)) | [`^0.38.0` -> `^0.39.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-graphql/0.38.0/0.39.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-graphql/0.39.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@opentelemetry%2finstrumentation-graphql/0.39.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@opentelemetry%2finstrumentation-graphql/0.38.0/0.39.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation-graphql/0.38.0/0.39.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@opentelemetry/instrumentation-http](https://togithub.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-instrumentation-http) ([source](https://togithub.com/open-telemetry/opentelemetry-js)) | [`^0.49.0` -> `^0.50.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-http/0.49.1/0.50.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-http/0.50.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@opentelemetry%2finstrumentation-http/0.50.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@opentelemetry%2finstrumentation-http/0.49.1/0.50.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation-http/0.49.1/0.50.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@opentelemetry/instrumentation-ioredis](https://togithub.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-instrumentation-ioredis#readme) ([source](https://togithub.com/open-telemetry/opentelemetry-js-contrib)) | [`^0.38.0` -> `^0.39.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-ioredis/0.38.0/0.39.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-ioredis/0.39.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@opentelemetry%2finstrumentation-ioredis/0.39.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@opentelemetry%2finstrumentation-ioredis/0.38.0/0.39.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation-ioredis/0.38.0/0.39.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@opentelemetry/instrumentation-nestjs-core](https://togithub.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/opentelemetry-instrumentation-nestjs-core#readme) ([source](https://togithub.com/open-telemetry/opentelemetry-js-contrib)) | [`^0.35.0` -> `^0.36.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-nestjs-core/0.35.0/0.36.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-nestjs-core/0.36.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@opentelemetry%2finstrumentation-nestjs-core/0.36.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@opentelemetry%2finstrumentation-nestjs-core/0.35.0/0.36.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation-nestjs-core/0.35.0/0.36.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@opentelemetry/instrumentation-socket.io](https://togithub.com/open-telemetry/opentelemetry-js-contrib/tree/main/plugins/node/instrumentation-socket.io#readme) ([source](https://togithub.com/open-telemetry/opentelemetry-js-contrib)) | [`^0.37.0` -> `^0.38.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-socket.io/0.37.0/0.38.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-socket.io/0.38.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@opentelemetry%2finstrumentation-socket.io/0.38.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@opentelemetry%2finstrumentation-socket.io/0.37.0/0.38.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation-socket.io/0.37.0/0.38.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@opentelemetry/sdk-node](https://togithub.com/open-telemetry/opentelemetry-js/tree/main/experimental/packages/opentelemetry-sdk-node) ([source](https://togithub.com/open-telemetry/opentelemetry-js)) | [`^0.49.0` -> `^0.50.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2fsdk-node/0.49.1/0.50.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2fsdk-node/0.50.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@opentelemetry%2fsdk-node/0.50.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@opentelemetry%2fsdk-node/0.49.1/0.50.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2fsdk-node/0.49.1/0.50.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [eslint-plugin-import-x](https://togithub.com/un-ts/eslint-plugin-import-x) | [`^0.4.1` -> `^0.5.0`](https://renovatebot.com/diffs/npm/eslint-plugin-import-x/0.4.4/0.5.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/eslint-plugin-import-x/0.5.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/eslint-plugin-import-x/0.5.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/eslint-plugin-import-x/0.4.4/0.5.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/eslint-plugin-import-x/0.4.4/0.5.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [eslint-plugin-sonarjs](https://togithub.com/SonarSource/eslint-plugin-sonarjs) | [`^0.24.0` -> `^0.25.0`](https://renovatebot.com/diffs/npm/eslint-plugin-sonarjs/0.24.0/0.25.1) | [![age](https://developer.mend.io/api/mc/badges/age/npm/eslint-plugin-sonarjs/0.25.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/eslint-plugin-sonarjs/0.25.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/eslint-plugin-sonarjs/0.24.0/0.25.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/eslint-plugin-sonarjs/0.24.0/0.25.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [vite-plugin-dts](https://togithub.com/qmhc/vite-plugin-dts) | [`3.7.3` -> `3.8.1`](https://renovatebot.com/diffs/npm/vite-plugin-dts/3.7.3/3.8.1) | [![age](https://developer.mend.io/api/mc/badges/age/npm/vite-plugin-dts/3.8.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/vite-plugin-dts/3.8.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/vite-plugin-dts/3.7.3/3.8.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/vite-plugin-dts/3.7.3/3.8.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>aws/aws-sdk-js-v3 (@&#8203;aws-sdk/client-s3)</summary>

### [`v3.552.0`](https://togithub.com/aws/aws-sdk-js-v3/blob/HEAD/clients/client-s3/CHANGELOG.md#35520-2024-04-09)

[Compare Source](https://togithub.com/aws/aws-sdk-js-v3/compare/v3.550.0...v3.552.0)

**Note:** Version bump only for package [@&#8203;aws-sdk/client-s3](https://togithub.com/aws-sdk/client-s3)

### [`v3.550.0`](https://togithub.com/aws/aws-sdk-js-v3/blob/HEAD/clients/client-s3/CHANGELOG.md#35500-2024-04-05)

[Compare Source](https://togithub.com/aws/aws-sdk-js-v3/compare/v3.549.0...v3.550.0)

**Note:** Version bump only for package [@&#8203;aws-sdk/client-s3](https://togithub.com/aws-sdk/client-s3)

### [`v3.549.0`](https://togithub.com/aws/aws-sdk-js-v3/blob/HEAD/clients/client-s3/CHANGELOG.md#35490-2024-04-04)

[Compare Source](https://togithub.com/aws/aws-sdk-js-v3/compare/v3.540.0...v3.549.0)

**Note:** Version bump only for package [@&#8203;aws-sdk/client-s3](https://togithub.com/aws-sdk/client-s3)

### [`v3.540.0`](https://togithub.com/aws/aws-sdk-js-v3/blob/HEAD/clients/client-s3/CHANGELOG.md#35400-2024-03-22)

[Compare Source](https://togithub.com/aws/aws-sdk-js-v3/compare/v3.537.0...v3.540.0)

##### Bug Fixes

-   **util-endpoints:** augment endpointFunctions inline in endpointResolver functions ([#&#8203;5933](https://togithub.com/aws/aws-sdk-js-v3/issues/5933)) ([42a791d](42a791defb))

</details>

<details>
<summary>electron/forge (@&#8203;electron-forge/maker-base)</summary>

### [`v7.3.1`](https://togithub.com/electron/forge/releases/tag/v7.3.1)

[Compare Source](https://togithub.com/electron/forge/compare/v7.3.0...v7.3.1)

##### What's Changed

##### Highlighted Feature:

-   build: Bump [@&#8203;electron/windows-sign](https://togithub.com/electron/windows-sign) deps by [@&#8203;felixrieseberg](https://togithub.com/felixrieseberg) in [https://github.com/electron/forge/pull/3524](https://togithub.com/electron/forge/pull/3524)

This change enables advanced code signing with \[[@&#8203;electron/windows-sign](https://togithub.com/electron/windows-sign)]\[[@&#8203;electron/windows-sign](https://togithub.com/electron/windows-sign)], supporting two different ways to codesign your application and the installer:

1.  Modern: By passing a `windowsSign` option, which will be passed to \[[@&#8203;electron/windows-sign](https://togithub.com/electron/windows-sign)]. This method allows full customization of the code-signing process - and supports more complicated scenarios like cloud-hosted EV certificates, custom sign pipelines, and per-file overrides. It also supports all existing "simple" codesigning scenarios, including just passing a certificate file and password. Please see https://github.com/[@&#8203;electron/windows-sign](https://togithub.com/electron/windows-sign) for all possible configuration options.

    When passing `windowsSign`, do not pass any other available parameters at the top level (like `certificateFile`, `certificatePassword`, or `signWithParams`).

2.  Legacy: By passing the top-level settings (`certificateFile`, `certificatePassword`, and `signWithParams`). For simple codesigning scenarios, there's no reason not to use this method - it'll work just as fine as the modern method.

##### Bug Fixes and Improvements

-   fix(publisher-github): don't sanitize asset names before upload by [@&#8203;dsanders11](https://togithub.com/dsanders11) in [https://github.com/electron/forge/pull/3485](https://togithub.com/electron/forge/pull/3485)

-   build: bump memory limit for docs:generate script by [@&#8203;dsanders11](https://togithub.com/dsanders11) in [https://github.com/electron/forge/pull/3500](https://togithub.com/electron/forge/pull/3500)

-   build: fix keyv type resolution warning during dev by [@&#8203;MarshallOfSound](https://togithub.com/MarshallOfSound) in [https://github.com/electron/forge/pull/3507](https://togithub.com/electron/forge/pull/3507)

-   build(deps): bump actions/setup-node from 4.0.1 to 4.0.2 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/electron/forge/pull/3522](https://togithub.com/electron/forge/pull/3522)

-   build(deps): bump follow-redirects from 1.15.4 to 1.15.6 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/electron/forge/pull/3537](https://togithub.com/electron/forge/pull/3537)

-   ci(windows): pin version of wix toolset to v3.14.0 by [@&#8203;erickzhao](https://togithub.com/erickzhao) in [https://github.com/electron/forge/pull/3525](https://togithub.com/electron/forge/pull/3525)

-   docs: update forge create-electron-app template to match tutorial by [@&#8203;alicelovescake](https://togithub.com/alicelovescake) in [https://github.com/electron/forge/pull/3528](https://togithub.com/electron/forge/pull/3528)

##### New Contributors

-   [@&#8203;alicelovescake](https://togithub.com/alicelovescake) made their first contribution in [https://github.com/electron/forge/pull/3528](https://togithub.com/electron/forge/pull/3528)

**Full Changelog**: https://github.com/electron/forge/compare/v7.3.0...v7.3.1

![image](https://togithub.com/electron/forge/assets/33054982/3ebd6e16-0db8-4c66-bdb0-dc4461b8226d)

</details>

<details>
<summary>napi-rs/napi-rs (@&#8203;napi-rs/cli)</summary>

### [`v3.0.0-alpha.46`](https://togithub.com/napi-rs/napi-rs/releases/tag/%40napi-rs/cli%403.0.0-alpha.46)

[Compare Source](https://togithub.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.0.0-alpha.45...@napi-rs/cli@3.0.0-alpha.46)

#### What's Changed

-   fix(deps): update dependency emnapi to v1.1.1 by [@&#8203;renovate](https://togithub.com/renovate) in [https://github.com/napi-rs/napi-rs/pull/2017](https://togithub.com/napi-rs/napi-rs/pull/2017)
-   feat(cli): add support for armv7-unknown-linux-muslebihf by [@&#8203;sapphi-red](https://togithub.com/sapphi-red) in [https://github.com/napi-rs/napi-rs/pull/2019](https://togithub.com/napi-rs/napi-rs/pull/2019)
-   feat(cli): add support for powerpc64le-unknown-linux-gnu by [@&#8203;sapphi-red](https://togithub.com/sapphi-red) in [https://github.com/napi-rs/napi-rs/pull/2023](https://togithub.com/napi-rs/napi-rs/pull/2023)
-   feat(cli): add support for s390x-unknown-linux-gnu by [@&#8203;sapphi-red](https://togithub.com/sapphi-red) in [https://github.com/napi-rs/napi-rs/pull/2028](https://togithub.com/napi-rs/napi-rs/pull/2028)
-   feat(cli): support wasm32-wasipxx targets by [@&#8203;Brooooooklyn](https://togithub.com/Brooooooklyn) in [https://github.com/napi-rs/napi-rs/pull/2030](https://togithub.com/napi-rs/napi-rs/pull/2030)

**Full Changelog**: https://github.com/napi-rs/napi-rs/compare/[@&#8203;napi-rs/cli](https://togithub.com/napi-rs/cli)[@&#8203;3](https://togithub.com/3).0.0-alpha.45...[@&#8203;napi-rs/cli](https://togithub.com/napi-rs/cli)[@&#8203;3](https://togithub.com/3).0.0-alpha.46

### [`v3.0.0-alpha.45`](https://togithub.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.0.0-alpha.44...@napi-rs/cli@3.0.0-alpha.45)

[Compare Source](https://togithub.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.0.0-alpha.44...@napi-rs/cli@3.0.0-alpha.45)

### [`v3.0.0-alpha.44`](https://togithub.com/napi-rs/napi-rs/releases/tag/%40napi-rs/cli%403.0.0-alpha.44)

[Compare Source](https://togithub.com/napi-rs/napi-rs/compare/@napi-rs/cli@3.0.0-alpha.43...@napi-rs/cli@3.0.0-alpha.44)

#### What's Changed

-   fix(cli): cleanup js binding template by [@&#8203;Brooooooklyn](https://togithub.com/Brooooooklyn) in [https://github.com/napi-rs/napi-rs/pull/1984](https://togithub.com/napi-rs/napi-rs/pull/1984)
-   chore(deps): lock file maintenance by [@&#8203;renovate](https://togithub.com/renovate) in [https://github.com/napi-rs/napi-rs/pull/1987](https://togithub.com/napi-rs/napi-rs/pull/1987)
-   chore(deps): update yarn to v4.1.1 by [@&#8203;renovate](https://togithub.com/renovate) in [https://github.com/napi-rs/napi-rs/pull/1989](https://togithub.com/napi-rs/napi-rs/pull/1989)
-   ci: fix arm64 job by [@&#8203;Brooooooklyn](https://togithub.com/Brooooooklyn) in [https://github.com/napi-rs/napi-rs/pull/1998](https://togithub.com/napi-rs/napi-rs/pull/1998)
-   chore(deps): bump follow-redirects from 1.15.5 to 1.15.6 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/napi-rs/napi-rs/pull/2004](https://togithub.com/napi-rs/napi-rs/pull/2004)
-   fix(deps): update dependency emnapi to v1.1.0 by [@&#8203;renovate](https://togithub.com/renovate) in [https://github.com/napi-rs/napi-rs/pull/2006](https://togithub.com/napi-rs/napi-rs/pull/2006)
-   chore(wasm-runtime): upgrade emnapi by [@&#8203;Brooooooklyn](https://togithub.com/Brooooooklyn) in [https://github.com/napi-rs/napi-rs/pull/2008](https://togithub.com/napi-rs/napi-rs/pull/2008)

**Full Changelog**: https://github.com/napi-rs/napi-rs/compare/napi@2.16.0...[@&#8203;napi-rs/cli](https://togithub.com/napi-rs/cli)[@&#8203;3](https://togithub.com/3).0.0-alpha.44

</details>

<details>
<summary>nrwl/nx (@&#8203;nx/vite)</summary>

### [`v18.2.4`](https://togithub.com/nrwl/nx/releases/tag/18.2.4)

[Compare Source](https://togithub.com/nrwl/nx/compare/18.2.3...18.2.4)

##### 18.2.4 (2024-04-09)

##### 🩹 Fixes

-   **angular:** fix dynamic module federation generation ([#&#8203;22724](https://togithub.com/nrwl/nx/pull/22724))
-   **core:** update pty version to add windows specific flags ([#&#8203;22711](https://togithub.com/nrwl/nx/pull/22711))
-   **nextjs:** Adding tailwind should work when creating an app OOTB ([#&#8203;22709](https://togithub.com/nrwl/nx/pull/22709))

##### ❤️  Thank You

-   Craigory Coppola [@&#8203;AgentEnder](https://togithub.com/AgentEnder)
-   Leosvel Pérez Espinosa [@&#8203;leosvelperez](https://togithub.com/leosvelperez)
-   Nicholas Cunningham [@&#8203;ndcunningham](https://togithub.com/ndcunningham)

### [`v18.2.3`](https://togithub.com/nrwl/nx/releases/tag/18.2.3)

[Compare Source](https://togithub.com/nrwl/nx/compare/18.2.2...18.2.3)

#### 18.2.3 (2024-04-05)

##### 🚀 Features

-   **core:** list crystal plugins with nx report ([#&#8203;22649](https://togithub.com/nrwl/nx/pull/22649))

##### 🩹 Fixes

-   **bundling:** print errors from rollup build ([#&#8203;22707](https://togithub.com/nrwl/nx/pull/22707))
-   **core:** write terminal output to cache folder ([#&#8203;22673](https://togithub.com/nrwl/nx/pull/22673))
-   **core:** errors from create dependencies should show properly ([#&#8203;22695](https://togithub.com/nrwl/nx/pull/22695))
-   **core:** not passing props of run-commands to underlying command ([#&#8203;22595](https://togithub.com/nrwl/nx/pull/22595))
-   **js:** update jest snapshot after vite-plugin-dts bump ([#&#8203;22621](https://togithub.com/nrwl/nx/pull/22621))
-   **js:** append target when generating tmp tsconfig to prevent conflicts [#&#8203;21396](https://togithub.com/nrwl/nx/issues/21396) ([#&#8203;22671](https://togithub.com/nrwl/nx/pull/22671), [#&#8203;21396](https://togithub.com/nrwl/nx/issues/21396))
-   **js:** propagate error from child process to [@&#8203;nx/js](https://togithub.com/nx/js):node executor ([#&#8203;22705](https://togithub.com/nrwl/nx/pull/22705))
-   **misc:** fix optional branch tracking on ci pipeline ([#&#8203;22652](https://togithub.com/nrwl/nx/pull/22652))
-   **module-federation:** serve dynamic remotes statically in their own processes ([#&#8203;22688](https://togithub.com/nrwl/nx/pull/22688))
-   **nx-dev:** Update urls that are 404 ([#&#8203;22653](https://togithub.com/nrwl/nx/pull/22653))
-   **release:** respect root .npmrc registry settings for publishing ([9dd97c43a1](https://togithub.com/nrwl/nx/commit/9dd97c43a1))
-   **testing:** fix playwright executor uiPort option schema ([#&#8203;22610](https://togithub.com/nrwl/nx/pull/22610))
-   **testing:** app generators should create correct e2e config at generation time ([#&#8203;22565](https://togithub.com/nrwl/nx/pull/22565))
-   **vite:** ensure cache is created correctly for separate vite and vitest config files [#&#8203;22244](https://togithub.com/nrwl/nx/issues/22244) ([#&#8203;22618](https://togithub.com/nrwl/nx/pull/22618), [#&#8203;22244](https://togithub.com/nrwl/nx/issues/22244))
-   **webpack:** bring back previous SVG and SVGR behavior for React projects ([#&#8203;22628](https://togithub.com/nrwl/nx/pull/22628))
-   **webpack:** support standard webpack config with [@&#8203;nx/webpack](https://togithub.com/nx/webpack):dev-server ([#&#8203;22660](https://togithub.com/nrwl/nx/pull/22660))
-   **webpack:** remove url-loader from dependencies since it is replaced by asset modules ([#&#8203;22698](https://togithub.com/nrwl/nx/pull/22698))

##### ❤️  Thank You

-   Altan Stalker
-   Austin Fahsl [@&#8203;fahslaj](https://togithub.com/fahslaj)
-   Colum Ferry [@&#8203;Coly010](https://togithub.com/Coly010)
-   Craigory Coppola [@&#8203;AgentEnder](https://togithub.com/AgentEnder)
-   Emily Xiong [@&#8203;xiongemi](https://togithub.com/xiongemi)
-   Jack Hsu [@&#8203;jaysoo](https://togithub.com/jaysoo)
-   Leosvel Pérez Espinosa [@&#8203;leosvelperez](https://togithub.com/leosvelperez)
-   Miroslav Jonaš [@&#8203;meeroslav](https://togithub.com/meeroslav)
-   Nicholas Cunningham [@&#8203;ndcunningham](https://togithub.com/ndcunningham)

### [`v18.2.2`](https://togithub.com/nrwl/nx/releases/tag/18.2.2)

[Compare Source](https://togithub.com/nrwl/nx/compare/18.2.1...18.2.2)

#### 18.2.2 (2024-04-02)

##### 🚀 Features

-   **vite:** migrate to latest vite-plugin-dts ([#&#8203;22614](https://togithub.com/nrwl/nx/pull/22614))

##### 🩹 Fixes

-   **angular:** prevent false positive validation due to option default value in dev-server executor ([#&#8203;22606](https://togithub.com/nrwl/nx/pull/22606))
-   **angular:** respect skipPackageJson correctly in library generator ([#&#8203;22608](https://togithub.com/nrwl/nx/pull/22608))
-   **angular:** fix @&#8203;nx/angular/src/utils entry point ([#&#8203;22609](https://togithub.com/nrwl/nx/pull/22609))
-   **core:** do not assume workspace inputs cause all projects to be af… ([#&#8203;22573](https://togithub.com/nrwl/nx/pull/22573))
-   **react-native:** storybook relative paths ([#&#8203;22031](https://togithub.com/nrwl/nx/pull/22031))

##### ❤️  Thank You

-   arekkubaczkowski [@&#8203;arekkubaczkowski](https://togithub.com/arekkubaczkowski)
-   Colum Ferry [@&#8203;Coly010](https://togithub.com/Coly010)
-   Jason Jean [@&#8203;FrozenPandaz](https://togithub.com/FrozenPandaz)
-   Leosvel Pérez Espinosa [@&#8203;leosvelperez](https://togithub.com/leosvelperez)

### [`v18.2.1`](https://togithub.com/nrwl/nx/releases/tag/18.2.1)

[Compare Source](https://togithub.com/nrwl/nx/compare/18.2.0...18.2.1)

##### 18.2.1 (2024-03-28)

##### 🩹 Fixes

-   **nuxt:** use loadConfigFile from devkit rather than [@&#8203;nuxt/kit](https://togithub.com/nuxt/kit) ([#&#8203;22571](https://togithub.com/nrwl/nx/pull/22571))

##### ❤️  Thank You

-   Jack Hsu [@&#8203;jaysoo](https://togithub.com/jaysoo)

### [`v18.2.0`](https://togithub.com/nrwl/nx/releases/tag/18.2.0)

[Compare Source](https://togithub.com/nrwl/nx/compare/18.1.3...18.2.0)

##### 18.2.0 (2024-03-28)

##### 🚀 Features

-   **angular:** support angular 17.3.0 ([#&#8203;22202](https://togithub.com/nrwl/nx/pull/22202))
-   **bundling:** refactor rollup executor to perform single build for all formats ([#&#8203;22436](https://togithub.com/nrwl/nx/pull/22436))
-   **core:** add ability to add metadata to projects ([#&#8203;22299](https://togithub.com/nrwl/nx/pull/22299))
-   **core:** create structured project graph errors with all plugin er… ([#&#8203;22404](https://togithub.com/nrwl/nx/pull/22404))
-   **core:** add ability to scope plugins ([#&#8203;22379](https://togithub.com/nrwl/nx/pull/22379))
-   **gradle:** add gradle init generator ([#&#8203;22245](https://togithub.com/nrwl/nx/pull/22245))
-   **gradle:** make gradle public ([#&#8203;22399](https://togithub.com/nrwl/nx/pull/22399))
-   **gradle:** add gradle init generator ([#&#8203;22245](https://togithub.com/nrwl/nx/pull/22245))
-   **gradle:** add technology ([#&#8203;22528](https://togithub.com/nrwl/nx/pull/22528))
-   **graph:** remove polyfills from graph client ([#&#8203;22494](https://togithub.com/nrwl/nx/pull/22494))
-   **testing:** infer open-cypress task ([#&#8203;22556](https://togithub.com/nrwl/nx/pull/22556))
-   **webpack:** create build log for remotes to help debug errors ([#&#8203;22539](https://togithub.com/nrwl/nx/pull/22539))

##### 🩹 Fixes

-   **angular:** target correct versions of ng-packagr to create stylesheet worker synchronously ([#&#8203;22485](https://togithub.com/nrwl/nx/pull/22485))
-   **angular:** prevent creating stylesheet worker multiple times in ng-packagr executors ([#&#8203;22491](https://togithub.com/nrwl/nx/pull/22491))
-   **bundling:** prevent sensitive keys from being bundled ([#&#8203;22413](https://togithub.com/nrwl/nx/pull/22413))
-   **bundling:** prevent sensitive keys from being bundled ([#&#8203;22413](https://togithub.com/nrwl/nx/pull/22413))
-   **core:** override Path env variable on Windows platform ([#&#8203;22382](https://togithub.com/nrwl/nx/pull/22382))
-   **core:** Should work if extends is a string ([a00f6438b9](https://togithub.com/nrwl/nx/commit/a00f6438b9))
-   **core:** fix no plugins found for nx init without packge.json ([#&#8203;22434](https://togithub.com/nrwl/nx/pull/22434))
-   **core:** exponential backoff retry on cache put fail ([#&#8203;21926](https://togithub.com/nrwl/nx/pull/21926))
-   **core:** override Path env variable on Windows platform ([#&#8203;22382](https://togithub.com/nrwl/nx/pull/22382))
-   **core:** Should work if extends is a string ([66ae83e569](https://togithub.com/nrwl/nx/commit/66ae83e569))
-   **core:** fix no plugins found for nx init without packge.json ([#&#8203;22434](https://togithub.com/nrwl/nx/pull/22434))
-   **core:** exponential backoff retry on cache put fail ([#&#8203;21926](https://togithub.com/nrwl/nx/pull/21926))
-   **core:** cannot read property kind of undefined ([#&#8203;21715](https://togithub.com/nrwl/nx/pull/21715))
-   **core:** rethrow unknown errors during incremental graph calculation ([#&#8203;22522](https://togithub.com/nrwl/nx/pull/22522))
-   **core:** ignore yarn/cache when watching with the daemon ([#&#8203;22516](https://togithub.com/nrwl/nx/pull/22516))
-   **core:** handle undefined properties in schemas with additionalProperties ([#&#8203;22426](https://togithub.com/nrwl/nx/pull/22426))
-   **core:** fix caching outputs which have symlinks ([#&#8203;22548](https://togithub.com/nrwl/nx/pull/22548))
-   **gradle:** fix missing tasks ([#&#8203;22400](https://togithub.com/nrwl/nx/pull/22400))
-   **gradle:** fix gradle plugin path ([#&#8203;22405](https://togithub.com/nrwl/nx/pull/22405))
-   **gradle:** fix missing tasks ([#&#8203;22400](https://togithub.com/nrwl/nx/pull/22400))
-   **gradle:** fix gradle plugin path ([#&#8203;22405](https://togithub.com/nrwl/nx/pull/22405))
-   **gradle:** fix gradle to work on windows ([#&#8203;22470](https://togithub.com/nrwl/nx/pull/22470))
-   **gradle:** fix gradle icon ([#&#8203;22553](https://togithub.com/nrwl/nx/pull/22553))
-   **gradle:** only allow certain types of task to be cached ([#&#8203;22559](https://togithub.com/nrwl/nx/pull/22559))
-   **gradle:** add [@&#8203;nx/gradle](https://togithub.com/nx/gradle) to nx migrations ([#&#8203;22567](https://togithub.com/nrwl/nx/pull/22567))
-   **js:** update babel preset to specify minor version of core-js for better optimization ([#&#8203;22433](https://togithub.com/nrwl/nx/pull/22433))
-   **js:** match core-js version with babel options ([#&#8203;22493](https://togithub.com/nrwl/nx/pull/22493))
-   **js:** migrate core-js to 3.36 for workspaces that use it ([#&#8203;22495](https://togithub.com/nrwl/nx/pull/22495))
-   **js:** handle case where tslib or [@&#8203;swc/helpers](https://togithub.com/swc/helpers) are missing from externalNodes ([#&#8203;22523](https://togithub.com/nrwl/nx/pull/22523))
-   **js:** do not write cached lockfile parsed results when an error is… ([#&#8203;22526](https://togithub.com/nrwl/nx/pull/22526))
-   **linter:** convert parser options to flat config even is parser is missing ([#&#8203;22388](https://togithub.com/nrwl/nx/pull/22388))
-   **linter:** convert parser options to flat config even is parser is missing ([#&#8203;22388](https://togithub.com/nrwl/nx/pull/22388))
-   **misc:** handle cwd correctly when generating artifacts with as-provided ([#&#8203;22411](https://togithub.com/nrwl/nx/pull/22411))
-   **misc:** handle cwd correctly when generating artifacts with as-provided ([#&#8203;22411](https://togithub.com/nrwl/nx/pull/22411))
-   **misc:** align nx init package.json scripts handling when deselecting all plugins ([#&#8203;22490](https://togithub.com/nrwl/nx/pull/22490))
-   **react:** HMR for withModuleFederation [#&#8203;22300](https://togithub.com/nrwl/nx/issues/22300) ([#&#8203;22562](https://togithub.com/nrwl/nx/pull/22562), [#&#8203;22300](https://togithub.com/nrwl/nx/issues/22300))
-   **remix:** generate correct e2e config if Crystal is used ([#&#8203;22558](https://togithub.com/nrwl/nx/pull/22558))
-   **rollup:** remove exports field from @&#8203;nx/rollup/package.json since is a breaking change ([#&#8203;22545](https://togithub.com/nrwl/nx/pull/22545))
-   **storybook:** do not set cacheableOperations if not previously set ([#&#8203;22535](https://togithub.com/nrwl/nx/pull/22535))
-   **testing:** remove root from the cypress ci-e2e group ([#&#8203;22468](https://togithub.com/nrwl/nx/pull/22468))
-   **testing:** name group of e2e ci tasks distinctly from target name ([#&#8203;22525](https://togithub.com/nrwl/nx/pull/22525))
-   **web:** spa flag should correctly define redirect ([#&#8203;22487](https://togithub.com/nrwl/nx/pull/22487))
-   **webpack:** Stylus loader path ([#&#8203;22373](https://togithub.com/nrwl/nx/pull/22373))
-   **webpack:** Stylus loader path ([#&#8203;22373](https://togithub.com/nrwl/nx/pull/22373))
-   **webpack:** pass options from executor to NxWebpackPlugin correctly ([#&#8203;22529](https://togithub.com/nrwl/nx/pull/22529))
-   **webpack:** resolve assets from executor options as relative to workspace root ([#&#8203;22544](https://togithub.com/nrwl/nx/pull/22544))

##### ❤️  Thank You

-   Colum Ferry [@&#8203;Coly010](https://togithub.com/Coly010)
-   Emily Xiong [@&#8203;xiongemi](https://togithub.com/xiongemi)
-   Jack Hsu [@&#8203;jaysoo](https://togithub.com/jaysoo)
-   Jason Jean [@&#8203;FrozenPandaz](https://togithub.com/FrozenPandaz)
-   Joel Pelaez Jorge
-   Leosvel Pérez Espinosa [@&#8203;leosvelperez](https://togithub.com/leosvelperez)
-   Maxence LEFEBVRE
-   MaxKless [@&#8203;MaxKless](https://togithub.com/MaxKless)
-   Mike Pham
-   Miroslav Jonaš [@&#8203;meeroslav](https://togithub.com/meeroslav)
-   Nicholas Cunningham [@&#8203;ndcunningham](https://togithub.com/ndcunningham)

### [`v18.1.3`](https://togithub.com/nrwl/nx/releases/tag/18.1.3)

[Compare Source](https://togithub.com/nrwl/nx/compare/18.1.2...18.1.3)

##### 18.1.3 (2024-03-25)

##### 🚀 Features

-   **gradle:** add gradle init generator ([#&#8203;22245](https://togithub.com/nrwl/nx/pull/22245))

##### 🩹 Fixes

-   **angular:** prevent creating stylesheet worker multiple times in ng-packagr executors ([#&#8203;22491](https://togithub.com/nrwl/nx/pull/22491))
-   **bundling:** prevent sensitive keys from being bundled ([#&#8203;22413](https://togithub.com/nrwl/nx/pull/22413))
-   **core:** override Path env variable on Windows platform ([#&#8203;22382](https://togithub.com/nrwl/nx/pull/22382))
-   **core:** Should work if extends is a string ([66ae83e569](https://togithub.com/nrwl/nx/commit/66ae83e569))
-   **core:** fix no plugins found for nx init without packge.json ([#&#8203;22434](https://togithub.com/nrwl/nx/pull/22434))
-   **core:** exponential backoff retry on cache put fail ([#&#8203;21926](https://togithub.com/nrwl/nx/pull/21926))
-   **gradle:** fix missing tasks ([#&#8203;22400](https://togithub.com/nrwl/nx/pull/22400))
-   **gradle:** fix gradle plugin path ([#&#8203;22405](https://togithub.com/nrwl/nx/pull/22405))
-   **linter:** convert parser options to flat config even is parser is missing ([#&#8203;22388](https://togithub.com/nrwl/nx/pull/22388))
-   **misc:** handle cwd correctly when generating artifacts with as-provided ([#&#8203;22411](https://togithub.com/nrwl/nx/pull/22411))
-   **webpack:** Stylus loader path ([#&#8203;22373](https://togithub.com/nrwl/nx/pull/22373))

##### ❤️  Thank You

-   Emily Xiong
-   Jack Hsu
-   Jason Jean
-   Joel Pelaez Jorge
-   Leosvel Pérez Espinosa
-   Mike Pham
-   Miroslav Jonaš
-   Nicholas Cunningham

</details>

<details>
<summary>open-telemetry/opentelemetry-js (@&#8203;opentelemetry/exporter-prometheus)</summary>

### [`v0.50.0`](3920b158d0...5231aa2550)

[Compare Source](3920b158d0...5231aa2550)

</details>

<details>
<summary>open-telemetry/opentelemetry-js-contrib (@&#8203;opentelemetry/instrumentation-graphql)</summary>

### [`v0.39.0`](9d19ca4103...17a0bc1da3)

[Compare Source](9d19ca4103...17a0bc1da3)

### [`v0.38.1`](32204a362d...39c34df61f)

[Compare Source](fcea8ca0c8...9d19ca4103)

</details>

<details>
<summary>un-ts/eslint-plugin-import-x (eslint-plugin-import-x)</summary>

### [`v0.5.0`](https://togithub.com/un-ts/eslint-plugin-import-x/blob/HEAD/CHANGELOG.md#050)

[Compare Source](https://togithub.com/un-ts/eslint-plugin-import-x/compare/v0.4.4...v0.5.0)

##### Minor Changes

-   [#&#8203;66](https://togithub.com/un-ts/eslint-plugin-import-x/pull/66) [`49418a0`](49418a0352) Thanks [@&#8203;JounQin](https://togithub.com/JounQin)! - chore(dep)!: drop eslint <8.56 support

-   [#&#8203;66](https://togithub.com/un-ts/eslint-plugin-import-x/pull/66) [`49418a0`](49418a0352) Thanks [@&#8203;JounQin](https://togithub.com/JounQin)! - feat!: upgrade [@&#8203;typescript-eslint/utils](https://togithub.com/typescript-eslint/utils) to v7

</details>

<details>
<summary>SonarSource/eslint-plugin-sonarjs (eslint-plugin-sonarjs)</summary>

### [`v0.25.1`](https://togithub.com/SonarSource/eslint-plugin-sonarjs/releases/tag/0.25.1)

[Compare Source](https://togithub.com/SonarSource/eslint-plugin-sonarjs/compare/0.25.0...0.25.1)

##### What's Changed

-   Resolve issue [#&#8203;456](https://togithub.com/SonarSource/eslint-plugin-sonarjs/issues/456) - [@&#8203;typescript-eslint/utils](https://togithub.com/typescript-eslint/utils) dependency is missing by [@&#8203;ericmorand-sonarsource](https://togithub.com/ericmorand-sonarsource) in [https://github.com/SonarSource/eslint-plugin-sonarjs/pull/457](https://togithub.com/SonarSource/eslint-plugin-sonarjs/pull/457)
-   Bump the project manifest version to 0.25.1 by [@&#8203;ericmorand-sonarsource](https://togithub.com/ericmorand-sonarsource) in [https://github.com/SonarSource/eslint-plugin-sonarjs/pull/458](https://togithub.com/SonarSource/eslint-plugin-sonarjs/pull/458)

##### New Contributors

-   [@&#8203;ericmorand-sonarsource](https://togithub.com/ericmorand-sonarsource) made their first contribution in [https://github.com/SonarSource/eslint-plugin-sonarjs/pull/457](https://togithub.com/SonarSource/eslint-plugin-sonarjs/pull/457)

**Full Changelog**: https://github.com/SonarSource/eslint-plugin-sonarjs/compare/0.25.0...0.25.1

### [`v0.25.0`](https://togithub.com/SonarSource/eslint-plugin-sonarjs/releases/tag/0.25.0)

[Compare Source](https://togithub.com/SonarSource/eslint-plugin-sonarjs/compare/0.24.0...0.25.0)

#### What's Changed

-   Prepare for next development iteration by [@&#8203;yassin-kammoun-sonarsource](https://togithub.com/yassin-kammoun-sonarsource) in [https://github.com/SonarSource/eslint-plugin-sonarjs/pull/446](https://togithub.com/SonarSource/eslint-plugin-sonarjs/pull/446)
-   Fix FP 3699 (no-use-of-empty-return-values) to handle ambient functions by [@&#8203;zglicz](https://togithub.com/zglicz) in [https://github.com/SonarSource/eslint-plugin-sonarjs/pull/451](https://togithub.com/SonarSource/eslint-plugin-sonarjs/pull/451)
-   Make no-unused-collection not trigger if writing to elements of said collection by [@&#8203;zglicz](https://togithub.com/zglicz) in [https://github.com/SonarSource/eslint-plugin-sonarjs/pull/452](https://togithub.com/SonarSource/eslint-plugin-sonarjs/pull/452)
-   Replace devDependency `@typescript-eslint/experimental-utils` with `@typescript-eslint/utils` by [@&#8203;yassin-kammoun-sonarsource](https://togithub.com/yassin-kammoun-sonarsource) in [https://github.com/SonarSource/eslint-plugin-sonarjs/pull/453](https://togithub.com/SonarSource/eslint-plugin-sonarjs/pull/453)

#### New Contributors

-   [@&#8203;zglicz](https://togithub.com/zglicz) made their first contribution in [https://github.com/SonarSource/eslint-plugin-sonarjs/pull/451](https://togithub.com/SonarSource/eslint-plugin-sonarjs/pull/451)

**Full Changelog**: https://github.com/SonarSource/eslint-plugin-sonarjs/compare/0.24.0...0.25.0

</details>

<details>
<summary>qmhc/vite-plugin-dts (vite-plugin-dts)</summary>

### [`v3.8.1`](https://togithub.com/qmhc/vite-plugin-dts/blob/HEAD/CHANGELOG.md#381-2024-03-28)

[Compare Source](https://togithub.com/qmhc/vite-plugin-dts/compare/v3.8.0...v3.8.1)

##### Bug Fixes

-   correct process property names in imports ([fb320fb](fb320fb814)), closes [#&#8203;316](https://togithub.com/qmhc/vite-plugin-dts/issues/316)

### [`v3.8.0`](https://togithub.com/qmhc/vite-plugin-dts/blob/HEAD/CHANGELOG.md#380-2024-03-27)

[Compare Source](https://togithub.com/qmhc/vite-plugin-dts/compare/v3.7.3...v3.8.0)

##### Bug Fixes

-   add syntactic and semantic diagnostics ([#&#8203;310](https://togithub.com/qmhc/vite-plugin-dts/issues/310)) ([7c10782](7c10782284))

##### Features

-   collect declared modules when rollup types ([39606bd](39606bd345)), closes [#&#8203;240](https://togithub.com/qmhc/vite-plugin-dts/issues/240)

#### [3.7.3](https://togithub.com/qmhc/vite-plugin-dts/compare/v3.7.2...v3.7.3) (2024-02-21)

##### Bug Fixes

-   incorrect process for removing pure import ([d0c0c86](d0c0c867d6)), closes [#&#8203;301](https://togithub.com/qmhc/vite-plugin-dts/issues/301)

#### [3.7.2](https://togithub.com/qmhc/vite-plugin-dts/compare/v3.7.1...v3.7.2) (2024-01-24)

##### Bug Fixes

-   correct match result for alias form tsconfig ([88469d0](88469d0e6a)), closes [#&#8203;298](https://togithub.com/qmhc/vite-plugin-dts/issues/298)

#### [3.7.1](https://togithub.com/qmhc/vite-plugin-dts/compare/v3.7.0...v3.7.1) (2024-01-15)

##### Bug Fixes

-   improve aliases replacement logic ([e8827cb](e8827cb6c8)), closes [#&#8203;294](https://togithub.com/qmhc/vite-plugin-dts/issues/294)
-   manually collect compiler options for types rollup ([0d0b255](0d0b255ad2)), closes [#&#8203;297](https://togithub.com/qmhc/vite-plugin-dts/issues/297)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://togithub.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4yNjEuMCIsInVwZGF0ZWRJblZlciI6IjM3LjI2OS4yIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-04-11 10:17:32 +00:00
JimmFly
8579ac63a2 feat(core): set the right sidebar to display TOC by default (#6503)
https://github.com/toeverything/AFFiNE/assets/102217452/e37ee798-0b22-4ad4-8bf7-ed32eafc89d0
2024-04-11 10:04:39 +00:00
JimmFly
9dd4c74115 chore(core): update description of the workspace member (#6492)
close AFF-805
2024-04-11 09:53:03 +00:00
DarkSky
db1206dbd5 fix: re-create session should skip rewrite messages (#6513) 2024-04-11 12:22:45 +08:00
renovate
5cd4c051fd chore: bump up @testing-library/react version to v15 (#6508)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [@testing-library/react](https://togithub.com/testing-library/react-testing-library) | [`^14.2.1` -> `^15.0.0`](https://renovatebot.com/diffs/npm/@testing-library%2freact/14.2.1/15.0.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@testing-library%2freact/15.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@testing-library%2freact/15.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@testing-library%2freact/14.2.1/15.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@testing-library%2freact/14.2.1/15.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>testing-library/react-testing-library (@&#8203;testing-library/react)</summary>

### [`v15.0.0`](https://togithub.com/testing-library/react-testing-library/compare/v14.3.0...787cb85f8baa3d2e2a9916b7dad12c0a76d787a4)

[Compare Source](https://togithub.com/testing-library/react-testing-library/compare/v14.3.0...v15.0.0)

### [`v14.3.0`](https://togithub.com/testing-library/react-testing-library/compare/v14.2.2...9c4a46d5b9923c21c936d206614a8febcc939fc2)

[Compare Source](https://togithub.com/testing-library/react-testing-library/compare/v14.2.2...v14.3.0)

### [`v14.2.2`](https://togithub.com/testing-library/react-testing-library/compare/v14.2.1...3da62fd9741ca74bcd0d2bc668ba76a2d8f3751f)

[Compare Source](https://togithub.com/testing-library/react-testing-library/compare/v14.2.1...v14.2.2)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4yNjkuMiIsInVwZGF0ZWRJblZlciI6IjM3LjI2OS4yIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-04-11 03:23:27 +00:00
renovate
aaeae8ebcd chore: bump up stripe version to v15 (#6512)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [stripe](https://togithub.com/stripe/stripe-node) | [`^14.18.0` -> `^15.0.0`](https://renovatebot.com/diffs/npm/stripe/14.18.0/15.0.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/stripe/15.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/stripe/15.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/stripe/14.18.0/15.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/stripe/14.18.0/15.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>stripe/stripe-node (stripe)</summary>

### [`v15.0.0`](https://togithub.com/stripe/stripe-node/blob/HEAD/CHANGELOG.md#1500---2024-04-10)

[Compare Source](https://togithub.com/stripe/stripe-node/compare/v14.25.0...v15.0.0)

-   [#&#8203;2057](https://togithub.com/stripe/stripe-node/pull/2057)

    -   This release changes the pinned API version to `2024-04-10`. Please read the [API Upgrade Guide](https://stripe.com/docs/upgrades#2024-04-10) and carefully review the API changes before upgrading.

##### ⚠️ Breaking changes

-   Rename event type `InvoiceitemCreatedEvent` to `InvoiceItemCreatedEvent`
-   Rename event type `InvoiceitemDeletedEvent` to `InvoiceItemDeletedEvent`
-   Rename `features` to `marketing_features` on `ProductCreateOptions`, `ProductUpdateOptions`, and `Product`.

##### ⚠️ Removal of enum values, properties and events that are no longer part of the publicly documented Stripe API

-   Remove `subscription_pause` from the below as the feature to pause subscription on the portal has been deprecated.
    -   `BillingPortal.Configuration.Features`
    -   `BillingPortal.ConfigurationCreateParams.Features`
    -   `BillingPortal.ConfigurationUpdateParams.Features`
-   Remove the below deprecated values for the type `BalanceTransaction.Type`
    -   `obligation_inbound`
    -   `obligation_payout`
    -   `obligation_payout_failure`
    -   `'obligation_reversal_outbound'`
-   Remove deprecated value `various` for the type `Climate.Supplier.RemovalPathway`
-   Remove deprecated events
    -   `invoiceitem.updated`
    -   `order.created`
    -   `recipient.created`
    -   `recipient.deleted`
    -   `recipient.updated`
    -   `sku.created`
    -   `sku.deleted`
    -   `sku.updated`
-   Remove types for the deprecated events
    -   `InvoiceItemUpdatedEvent`
    -   `OrderCreatedEvent`
    -   `RecipientCreatedEvent`
    -   `RecipientDeletedEvent`
    -   `RecipientUpdatedEvent`
    -   `SKUCreatedEvent`
    -   `SKUDeletedEvent`
-   Remove the deprecated value `include_and_require` for the type`InvoiceCreateParams.PendingInvoiceItemsBehavior`
-   Remove the deprecated value `service_tax` for the types `TaxRate.TaxType`, `TaxRateCreateParams.TaxType`, `TaxRateUpdateParams.TaxType`, and `InvoiceUpdateLineItemParams.TaxAmount.TaxRateData`
-   Remove `request_incremental_authorization` from `PaymentIntentCreateParams.PaymentMethodOptions.CardPresent`, `PaymentIntentUpdateParams.PaymentMethodOptions.CardPresent` and `PaymentIntentConfirmParams.PaymentMethodOptions.CardPresent`
-   Remove support for `id_bank_transfer`, `multibanco`, `netbanking`, `pay_by_bank`, and `upi` on `PaymentMethodConfiguration`
-   Remove the deprecated value `obligation` for the type `Reporting.ReportRunCreateParams.Parameters.ReportingCategory`
-   Remove the deprecated value `challenge_only` from the type `SetupIntent.PaymentMethodOptions.Card.RequestThreeDSecure`
-   Remove the legacy field `rendering_options` in `Invoice`, `InvoiceCreateOptions` and `InvoiceUpdateOptions`. Use `rendering` instead.

### [`v14.25.0`](https://togithub.com/stripe/stripe-node/blob/HEAD/CHANGELOG.md#14250---2024-04-09)

[Compare Source](https://togithub.com/stripe/stripe-node/compare/v14.24.0...v14.25.0)

-   [#&#8203;2059](https://togithub.com/stripe/stripe-node/pull/2059) Update generated code
    -   Add support for new resources `Entitlements.ActiveEntitlement` and `Entitlements.Feature`
    -   Add support for `list` and `retrieve` methods on resource `ActiveEntitlement`
    -   Add support for `create`, `list`, `retrieve`, and `update` methods on resource `Feature`
    -   Add support for `controller` on `AccountCreateParams`
    -   Add support for `fees`, `losses`, `requirement_collection`, and `stripe_dashboard` on `Account.controller`
    -   Add support for new value `none` on enum `Account.type`
    -   Add support for `event_name` on `Billing.MeterEventAdjustmentCreateParams` and `Billing.MeterEventAdjustment`
    -   Add support for `cancel` and `type` on `Billing.MeterEventAdjustment`

### [`v14.24.0`](https://togithub.com/stripe/stripe-node/blob/HEAD/CHANGELOG.md#14240---2024-04-04)

[Compare Source](https://togithub.com/stripe/stripe-node/compare/v14.23.0...v14.24.0)

-   [#&#8203;2053](https://togithub.com/stripe/stripe-node/pull/2053) Update generated code
    -   Change `Charge.payment_method_details.us_bank_account.payment_reference`, `PaymentIntent.next_action.swish_handle_redirect_or_display_qr_code.hosted_instructions_url`, `PaymentIntent.next_action.swish_handle_redirect_or_display_qr_code.mobile_auth_url`, `PaymentIntent.next_action.swish_handle_redirect_or_display_qr_code.qr_code.data`, `PaymentIntent.next_action.swish_handle_redirect_or_display_qr_code.qr_code.image_url_png`, `PaymentIntent.next_action.swish_handle_redirect_or_display_qr_code.qr_code.image_url_svg`, `PaymentIntent.next_action.swish_handle_redirect_or_display_qr_code.qr_code`, and `PaymentIntent.payment_method_options.swish.reference` to be required
    -   Change type of `Checkout.SessionCreateParams.payment_method_options.swish.reference` from `emptyable(string)` to `string`
    -   Add support for `subscription_item` on `Discount`
    -   Add support for `email` and `phone` on `Identity.VerificationReport`, `Identity.VerificationSession.options`, `Identity.VerificationSession.verified_outputs`, `Identity.VerificationSessionCreateParams.options`, and `Identity.VerificationSessionUpdateParams.options`
    -   Add support for `verification_flow` on `Identity.VerificationReport`, `Identity.VerificationSessionCreateParams`, and `Identity.VerificationSession`
    -   Add support for new value `verification_flow` on enums `Identity.VerificationReport.type` and `Identity.VerificationSession.type`
    -   Add support for `provided_details` on `Identity.VerificationSessionCreateParams`, `Identity.VerificationSessionUpdateParams`, and `Identity.VerificationSession`
    -   Change `Identity.VerificationSessionCreateParams.type` to be optional
    -   Add support for new values `email_unverified_other`, `email_verification_declined`, `phone_unverified_other`, and `phone_verification_declined` on enum `Identity.VerificationSession.last_error.code`
    -   Add support for `promotion_code` on `InvoiceCreateParams.discounts[]`, `InvoiceItemCreateParams.discounts[]`, `InvoiceItemUpdateParams.discounts[]`, `InvoiceUpdateParams.discounts[]`, `QuoteCreateParams.discounts[]`, and `QuoteUpdateParams.discounts[]`
    -   Add support for `discounts` on `InvoiceUpcomingLinesParams.subscription_items[]`, `InvoiceUpcomingParams.subscription_items[]`, `QuoteCreateParams.line_items[]`, `QuoteUpdateParams.line_items[]`, `SubscriptionCreateParams.add_invoice_items[]`, `SubscriptionCreateParams.items[]`, `SubscriptionCreateParams`, `SubscriptionItemCreateParams`, `SubscriptionItemUpdateParams`, `SubscriptionItem`, `SubscriptionSchedule.phases[].add_invoice_items[]`, `SubscriptionSchedule.phases[].items[]`, `SubscriptionSchedule.phases[]`, `SubscriptionScheduleCreateParams.phases[].add_invoice_items[]`, `SubscriptionScheduleCreateParams.phases[].items[]`, `SubscriptionScheduleCreateParams.phases[]`, `SubscriptionScheduleUpdateParams.phases[].add_invoice_items[]`, `SubscriptionScheduleUpdateParams.phases[].items[]`, `SubscriptionScheduleUpdateParams.phases[]`, `SubscriptionUpdateParams.add_invoice_items[]`, `SubscriptionUpdateParams.items[]`, `SubscriptionUpdateParams`, and `Subscription`
    -   Change type of `Invoice.discounts` from `array(expandable(deletable($Discount))) | null` to `array(expandable(deletable($Discount)))`
    -   Add support for `allowed_merchant_countries` and `blocked_merchant_countries` on `Issuing.Card.spending_controls`, `Issuing.CardCreateParams.spending_controls`, `Issuing.CardUpdateParams.spending_controls`, `Issuing.Cardholder.spending_controls`, `Issuing.CardholderCreateParams.spending_controls`, and `Issuing.CardholderUpdateParams.spending_controls`
    -   Add support for `zip` on `PaymentMethodConfigurationCreateParams`, `PaymentMethodConfigurationUpdateParams`, and `PaymentMethodConfiguration`
    -   Add support for `offline` on `SetupAttempt.payment_method_details.card_present`
    -   Add support for `card_present` on `SetupIntent.payment_method_options`, `SetupIntentConfirmParams.payment_method_options`, `SetupIntentCreateParams.payment_method_options`, and `SetupIntentUpdateParams.payment_method_options`
    -   Add support for new value `mobile_phone_reader` on enums `Terminal.Reader.device_type` and `Terminal.ReaderListParams.device_type`

### [`v14.23.0`](https://togithub.com/stripe/stripe-node/blob/HEAD/CHANGELOG.md#14230---2024-03-28)

[Compare Source](https://togithub.com/stripe/stripe-node/compare/v14.22.0...v14.23.0)

-   [#&#8203;2046](https://togithub.com/stripe/stripe-node/pull/2046) Update generated code
    -   Add support for new resources `Billing.MeterEventAdjustment`, `Billing.MeterEvent`, and `Billing.Meter`
    -   Add support for `create`, `deactivate`, `list`, `reactivate`, `retrieve`, and `update` methods on resource `Meter`
    -   Add support for `create` method on resources `MeterEventAdjustment` and `MeterEvent`
    -   Add support for `amazon_pay_payments` on `Account.capabilities`, `AccountCreateParams.capabilities`, and `AccountUpdateParams.capabilities`
    -   Add support for new value `verification_failed_representative_authority` on enums `Account.future_requirements.errors[].code`, `Account.requirements.errors[].code`, `BankAccount.future_requirements.errors[].code`, and `BankAccount.requirements.errors[].code`
    -   Add support for `destination_on_behalf_of_charge_management` on `AccountSession.components.payment_details.features`, `AccountSession.components.payments.features`, `AccountSessionCreateParams.components.payment_details.features`, and `AccountSessionCreateParams.components.payments.features`
    -   Add support for `mandate` on `Charge.payment_method_details.us_bank_account`, `Treasury.InboundTransfer.origin_payment_method_details.us_bank_account`, `Treasury.OutboundPayment.destination_payment_method_details.us_bank_account`, and `Treasury.OutboundTransfer.destination_payment_method_details.us_bank_account`
    -   Add support for `second_line` on `Issuing.CardCreateParams`
    -   Add support for `meter` on `PlanCreateParams`, `Plan`, `Price.recurring`, `PriceCreateParams.recurring`, and `PriceListParams.recurring`
-   [#&#8203;2045](https://togithub.com/stripe/stripe-node/pull/2045) esbuild test project fixes

### [`v14.22.0`](https://togithub.com/stripe/stripe-node/blob/HEAD/CHANGELOG.md#14220---2024-03-21)

[Compare Source](https://togithub.com/stripe/stripe-node/compare/v14.21.0...v14.22.0)

-   [#&#8203;2040](https://togithub.com/stripe/stripe-node/pull/2040) Update generated code
    -   Add support for new resources `ConfirmationToken` and `Forwarding.Request`
    -   Add support for `retrieve` method on resource `ConfirmationToken`
    -   Add support for `create`, `list`, and `retrieve` methods on resource `Request`
    -   Add support for `mobilepay_payments` on `Account.capabilities`, `AccountCreateParams.capabilities`, and `AccountUpdateParams.capabilities`
    -   Add support for new values `forwarding_api_inactive`, `forwarding_api_invalid_parameter`, `forwarding_api_upstream_connection_error`, and `forwarding_api_upstream_connection_timeout` on enums `Invoice.last_finalization_error.code`, `PaymentIntent.last_payment_error.code`, `SetupAttempt.setup_error.code`, `SetupIntent.last_setup_error.code`, and `StripeError.code`
    -   Add support for `mobilepay` on `Charge.payment_method_details`, `PaymentIntent.payment_method_options`, `PaymentIntentConfirmParams.payment_method_data`, `PaymentIntentConfirmParams.payment_method_options`, `PaymentIntentCreateParams.payment_method_data`, `PaymentIntentCreateParams.payment_method_options`, `PaymentIntentUpdateParams.payment_method_data`, `PaymentIntentUpdateParams.payment_method_options`, `PaymentMethodCreateParams`, `PaymentMethod`, `SetupIntentConfirmParams.payment_method_data`, `SetupIntentCreateParams.payment_method_data`, and `SetupIntentUpdateParams.payment_method_data`
    -   Add support for `payment_reference` on `Charge.payment_method_details.us_bank_account`
    -   Add support for new value `mobilepay` on enums `CustomerListPaymentMethodsParams.type`, `PaymentMethodCreateParams.type`, and `PaymentMethodListParams.type`
    -   Add support for `confirmation_token` on `PaymentIntentConfirmParams`, `PaymentIntentCreateParams`, `SetupIntentConfirmParams`, and `SetupIntentCreateParams`
    -   Add support for new value `mobilepay` on enums `PaymentIntentConfirmParams.payment_method_data.type`, `PaymentIntentCreateParams.payment_method_data.type`, `PaymentIntentUpdateParams.payment_method_data.type`, `SetupIntentConfirmParams.payment_method_data.type`, `SetupIntentCreateParams.payment_method_data.type`, and `SetupIntentUpdateParams.payment_method_data.type`
    -   Add support for new value `mobilepay` on enum `PaymentMethod.type`
    -   Add support for `name` on `Terminal.ConfigurationCreateParams`, `Terminal.ConfigurationUpdateParams`, and `Terminal.Configuration`
    -   Add support for `payout` on `Treasury.ReceivedDebit.linked_flows`
-   [#&#8203;2043](https://togithub.com/stripe/stripe-node/pull/2043) Don't mutate error.type during minification

### [`v14.21.0`](https://togithub.com/stripe/stripe-node/blob/HEAD/CHANGELOG.md#14210---2024-03-14)

[Compare Source](https://togithub.com/stripe/stripe-node/compare/v14.20.0...v14.21.0)

-   [#&#8203;2035](https://togithub.com/stripe/stripe-node/pull/2035) Update generated code
    -   Add support for new resources `Issuing.PersonalizationDesign` and `Issuing.PhysicalBundle`
    -   Add support for `create`, `list`, `retrieve`, and `update` methods on resource `PersonalizationDesign`
    -   Add support for `list` and `retrieve` methods on resource `PhysicalBundle`
    -   Add support for `personalization_design` on `Issuing.CardCreateParams`, `Issuing.CardListParams`, `Issuing.CardUpdateParams`, and `Issuing.Card`
    -   Change type of `SubscriptionCreateParams.application_fee_percent` and `SubscriptionUpdateParams.application_fee_percent` from `number` to `emptyStringable(number)`
    -   Add support for `sepa_debit` on `Subscription.payment_settings.payment_method_options`, `SubscriptionCreateParams.payment_settings.payment_method_options`, and `SubscriptionUpdateParams.payment_settings.payment_method_options`

### [`v14.20.0`](https://togithub.com/stripe/stripe-node/blob/HEAD/CHANGELOG.md#14200---2024-03-07)

[Compare Source](https://togithub.com/stripe/stripe-node/compare/v14.19.0...v14.20.0)

-   [#&#8203;2033](https://togithub.com/stripe/stripe-node/pull/2033) Update generated code
    -   Add support for `documents` on `AccountSession.components` and `AccountSessionCreateParams.components`
    -   Add support for `request_three_d_secure` on `Checkout.Session.payment_method_options.card` and `Checkout.SessionCreateParams.payment_method_options.card`
    -   Add support for `created` on `CreditNoteListParams`
    -   Add support for `sepa_debit` on `Invoice.payment_settings.payment_method_options`, `InvoiceCreateParams.payment_settings.payment_method_options`, and `InvoiceUpdateParams.payment_settings.payment_method_options`

### [`v14.19.0`](https://togithub.com/stripe/stripe-node/blob/HEAD/CHANGELOG.md#14190---2024-02-29)

[Compare Source](https://togithub.com/stripe/stripe-node/compare/v14.18.0...v14.19.0)

-   [#&#8203;2029](https://togithub.com/stripe/stripe-node/pull/2029) Update generated code
    -   Change `Identity.VerificationReport.type`, `SubscriptionSchedule.default_settings.invoice_settings.account_tax_ids`, `SubscriptionSchedule.phases[].invoice_settings.account_tax_ids`, and `TaxId.owner` to be required
    -   Change type of `Identity.VerificationSession.type` from `enum('document'|'id_number') | null` to `enum('document'|'id_number')`
    -   Add support for `number` on `InvoiceCreateParams` and `InvoiceUpdateParams`
    -   Add support for `enable_customer_cancellation` on `Terminal.Reader.action.process_payment_intent.process_config`, `Terminal.Reader.action.process_setup_intent.process_config`, `Terminal.ReaderProcessPaymentIntentParams.process_config`, and `Terminal.ReaderProcessSetupIntentParams.process_config`
    -   Add support for `refund_payment_config` on `Terminal.Reader.action.refund_payment` and `Terminal.ReaderRefundPaymentParams`
    -   Add support for `payment_method` on `TokenCreateParams.bank_account`
-   [#&#8203;2027](https://togithub.com/stripe/stripe-node/pull/2027) vscode settings: true -> "explicit"

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4yNjkuMiIsInVwZGF0ZWRJblZlciI6IjM3LjI2OS4yIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-04-11 03:12:46 +00:00
EYHN
c92bec0ebb chore: bump blocksuite (#6448)
## Features
- https://github.com/toeverything/BlockSuite/pull/6679 @pengx17
- https://github.com/toeverything/BlockSuite/pull/6620 @doouding
- https://github.com/toeverything/BlockSuite/pull/6602 @golok727
- https://github.com/toeverything/BlockSuite/pull/6661 @Flrande
- https://github.com/toeverything/BlockSuite/pull/6591 @fundon
- https://github.com/toeverything/BlockSuite/pull/6634 @golok727
- https://github.com/toeverything/BlockSuite/pull/6626 @regischen
- https://github.com/toeverything/BlockSuite/pull/6630 @Saul-Mirone
- https://github.com/toeverything/BlockSuite/pull/6605 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6614 @zzj3720
- https://github.com/toeverything/BlockSuite/pull/6508 @doouding
- https://github.com/toeverything/BlockSuite/pull/6578 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6572 @zzj3720
- https://github.com/toeverything/BlockSuite/pull/6571 @golok727
- https://github.com/toeverything/BlockSuite/pull/6573 @fundon
- https://github.com/toeverything/BlockSuite/pull/6580 @Flrande
- https://github.com/toeverything/BlockSuite/pull/6540 @golok727
- https://github.com/toeverything/BlockSuite/pull/6567 @Flrande
- https://github.com/toeverything/BlockSuite/pull/6565 @zzj3720
- https://github.com/toeverything/BlockSuite/pull/6561 @zzj3720

## Bugfix
- https://github.com/toeverything/BlockSuite/pull/6677 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6681 @fundon
- https://github.com/toeverything/BlockSuite/pull/6673 @fundon
- https://github.com/toeverything/BlockSuite/pull/6652 @fundon
- https://github.com/toeverything/BlockSuite/pull/6641 @fourdim
- https://github.com/toeverything/BlockSuite/pull/6649 @golok727
- https://github.com/toeverything/BlockSuite/pull/6648 @doouding
- https://github.com/toeverything/BlockSuite/pull/6662 @Flrande
- https://github.com/toeverything/BlockSuite/pull/6659 @zkwolf
- https://github.com/toeverything/BlockSuite/pull/6644 @doouding
- https://github.com/toeverything/BlockSuite/pull/6633 @golok727
- https://github.com/toeverything/BlockSuite/pull/6632 @golok727
- https://github.com/toeverything/BlockSuite/pull/6622 @RubaXa
- https://github.com/toeverything/BlockSuite/pull/6622 @RubaXa
- https://github.com/toeverything/BlockSuite/pull/6622 @RubaXa
- https://github.com/toeverything/BlockSuite/pull/6622 @RubaXa
- https://github.com/toeverything/BlockSuite/pull/6622 @RubaXa
- https://github.com/toeverything/BlockSuite/pull/6600 @fourdim
- https://github.com/toeverything/BlockSuite/pull/6617 @zzj3720
- https://github.com/toeverything/BlockSuite/pull/6618 @Flrande
- https://github.com/toeverything/BlockSuite/pull/6576 @fundon
- https://github.com/toeverything/BlockSuite/pull/6595 @fundon
- https://github.com/toeverything/BlockSuite/pull/6596 @zzj3720
- https://github.com/toeverything/BlockSuite/pull/6574 @fundon
- https://github.com/toeverything/BlockSuite/pull/6593 @Saul-Mirone
- https://github.com/toeverything/BlockSuite/pull/6588 @zzj3720
- https://github.com/toeverything/BlockSuite/pull/6590 @zzj3720
- https://github.com/toeverything/BlockSuite/pull/6589 @zzj3720
- https://github.com/toeverything/BlockSuite/pull/6584 @lawvs
- https://github.com/toeverything/BlockSuite/pull/6579 @fundon
- https://github.com/toeverything/BlockSuite/pull/6538 @golok727
- https://github.com/toeverything/BlockSuite/pull/6570 @zzj3720
- https://github.com/toeverything/BlockSuite/pull/6569 @zzj3720
- https://github.com/toeverything/BlockSuite/pull/6564 @zzj3720
- https://github.com/toeverything/BlockSuite/pull/6563 @zzj3720

## Refactor
- https://github.com/toeverything/BlockSuite/pull/6683 @doodlewind
- https://github.com/toeverything/BlockSuite/pull/6668 @Flrande
- https://github.com/toeverything/BlockSuite/pull/6635 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6653 @fundon
- https://github.com/toeverything/BlockSuite/pull/6615 @Flrande
- https://github.com/toeverything/BlockSuite/pull/6534 @Saul-Mirone
- https://github.com/toeverything/BlockSuite/pull/6553 @golok727

## Misc
- chore(examples): cleanup types
- https://github.com/toeverything/BlockSuite/pull/6664 @fourdim
- https://github.com/toeverything/BlockSuite/pull/6657 @fourdim
- https://github.com/toeverything/BlockSuite/pull/6654 @fourdim
- https://github.com/toeverything/BlockSuite/pull/6587 @Saul-Mirone
- https://github.com/toeverything/BlockSuite/pull/6562 @donteatfriedrice
2024-04-10 12:25:37 +00:00
darkskygit
9f349a2300 feat: text to image impl (#6437)
fix CLOUD-18
fix CLOUD-28
fix CLOUD-29
2024-04-10 12:13:39 +00:00
darkskygit
7c38a54f81 feat: copilot controller (#6272)
fix CLOUD-27
2024-04-10 11:58:40 +00:00
darkskygit
e6a576551a feat: add copilot impl (#6230)
fix CLOUD-22
fix CLOUD-24
2024-04-10 11:15:32 +00:00
darkskygit
46a368d7f1 feat: add session impl (#6254) 2024-04-10 11:15:25 +00:00
CatsJuice
8a02c81745 feat(core): remove toggle workspace onboarding dialog (#6501) 2024-04-10 09:07:59 +00:00
CatsJuice
a0c92b9966 feat(core): i18n for pricing plans (#6499)
feat(core): adjust pricing plans style

feat(core): i18n for pricing plans
2024-04-10 08:09:14 +00:00
JimmFly
6ea20e477b feat(core): add sign in to not found page (#6496)
close AFF-211
2024-04-10 07:27:02 +00:00
L-Sun
7d131ee9fc fix(core): viewport element not found in share page (#6453) 2024-04-10 15:26:22 +08:00
EYHN
939fa9cef0 fix(component): sign up password max length notify (#6467)
The input `maxLength` causes password to be automatically truncated without notice user.
2024-04-10 06:20:21 +00:00
JimmFly
a83f49e700 fix(core): unexpected horizontal scrolling after jump to block (#6475)
close TOV-788
2024-04-09 15:18:15 +00:00
EYHN
1c93f8e70b fix(infra): fix sync issues on old ids (#6474) 2024-04-09 18:37:53 +08:00
CatsJuice
4a93582799 feat(core): ai subscription in billing page (#6476) 2024-04-09 08:54:54 +00:00
EYHN
97c4ae48b5 fix(infra): fix sync issues on old ids (#6474) 2024-04-09 04:40:15 +00:00
JimmFly
142896c41a fix(core): remove unexpected duplicate notification (#6477)
close TOV-775
2024-04-09 04:28:31 +00:00
JimmFly
5064ef2a94 fix(core): remove duplicate windows controls when right sidebar is expanded (#6480)
close TOV-786
2024-04-09 04:17:32 +00:00
JimmFly
8bcc89f8fc feat(core): add responsive styles to collection filter (#6486)
https://github.com/toeverything/AFFiNE/assets/102217452/caa4c09b-e1fd-4bd0-a504-535925607e2f
2024-04-09 04:05:18 +00:00
renovate
891ab6ab51 chore: bump up style-loader version to v4 (#6490)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [style-loader](https://togithub.com/webpack-contrib/style-loader) | [`^3.3.4` -> `^4.0.0`](https://renovatebot.com/diffs/npm/style-loader/3.3.4/4.0.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/style-loader/4.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/style-loader/4.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/style-loader/3.3.4/4.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/style-loader/3.3.4/4.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>webpack-contrib/style-loader (style-loader)</summary>

### [`v4.0.0`](https://togithub.com/webpack-contrib/style-loader/blob/HEAD/CHANGELOG.md#400-2024-04-08)

[Compare Source](https://togithub.com/webpack-contrib/style-loader/compare/v3.3.4...v4.0.0)

##### ⚠ BREAKING CHANGES

-   minimum supported webpack version is `5.27.0`
-   minimum support Node.js version is `18.12.0`
-   the `insert` option can only be a selector or the path to the module

Migration:

Before:

**webpack.config.js**

```js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          {
            loader: "style-loader",
            options: {
              injectType: "styleTag",
              styleTagTransform: function (css, style) {
                // Do something ...
                style.innerHTML = `${css}.modify{}\n`;

                document.head.appendChild(style);
              },
            },
          },
          "css-loader",
        ],
      },
    ],
  },
};
```

After:

**insert-function.js**

```js
function insert(css, style) {
  var parent = options.target || document.head;

  parent.appendChild(element);
}

module.exports = insert;
```

**webpack.config.js**

```js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          {
            loader: "style-loader",
            options: {
              insert: require.resolve("./insert.js"),
            },
          },
          "css-loader",
        ],
      },
    ],
  },
};
```

-   the `styleTagTransform` option can only be the path to the module

Migration:

Before:

**webpack.config.js**

```js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          {
            loader: "style-loader",
            options: {
              injectType: "styleTag",
              styleTagTransform: function (css, style) {
                // Do something ...
                style.innerHTML = `${css}.modify{}\n`;

                document.head.appendChild(style);
              },
            },
          },
          "css-loader",
        ],
      },
    ],
  },
};
```

After:

**style-tag-transform-function.js**

```js
function styleTagTransform(css, style) {
  // Do something ...
  style.innerHTML = `${css}.modify{}\n`;

  document.head.appendChild(style);
}

module.exports = styleTagTransform;
```

**webpack.config.js**

```js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: [
          {
            loader: "style-loader",
            options: {
              styleTagTransform: require.resolve("./style-tag-transform-function.js"),
            },
          },
          "css-loader",
        ],
      },
    ],
  },
};
```

##### Bug Fixes

-   css experiments logic ([#&#8203;617](https://togithub.com/webpack-contrib/style-loader/issues/617)) ([8b9fc97](8b9fc97662))

##### [3.3.3](https://togithub.com/webpack-contrib/style-loader/compare/v3.3.2...v3.3.3) (2023-05-19)

##### Bug Fixes

-   compatibility with built-in CSS support ([#&#8203;605](https://togithub.com/webpack-contrib/style-loader/issues/605)) ([9636f58](9636f58054))

##### [3.3.2](https://togithub.com/webpack-contrib/style-loader/compare/v3.3.1...v3.3.2) (2023-03-13)

##### Bug Fixes

-   noop in environment without DOM API ([#&#8203;597](https://togithub.com/webpack-contrib/style-loader/issues/597)) ([03d3df3](03d3df3c36))

##### [3.3.1](https://togithub.com/webpack-contrib/style-loader/compare/v3.3.0...v3.3.1) (2021-10-21)

##### Bug Fixes

-   small perf improvement ([#&#8203;544](https://togithub.com/webpack-contrib/style-loader/issues/544)) ([610524e](610524ef62))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4yNjkuMiIsInVwZGF0ZWRJblZlciI6IjM3LjI2OS4yIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-04-09 03:52:32 +00:00
CatsJuice
803b17ae78 style(component): remove button's shadow (#6481) 2024-04-09 03:06:28 +00:00
CatsJuice
d9504e4eb7 fix(core): avoid flicking when click sidebar user avatar (#6482) 2024-04-09 02:52:51 +00:00
renovate
f590d84711 chore: bump up vite version to v5.0.13 [SECURITY] (#6455)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [vite](https://vitejs.dev) ([source](https://togithub.com/vitejs/vite/tree/HEAD/packages/vite)) | [`5.0.12` -> `5.0.13`](https://renovatebot.com/diffs/npm/vite/5.0.12/5.0.13) | [![age](https://developer.mend.io/api/mc/badges/age/npm/vite/5.0.13?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/vite/5.0.13?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/vite/5.0.12/5.0.13?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/vite/5.0.12/5.0.13?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

### GitHub Vulnerability Alerts

#### [CVE-2024-31207](https://togithub.com/vitejs/vite/security/advisories/GHSA-8jhw-289h-jh2g)

### Summary
[Vite dev server option](https://vitejs.dev/config/server-options.html#server-fs-deny) `server.fs.deny` did not deny requests for patterns with directories. An example of such a pattern is `/foo/**/*`.

### Impact
Only apps setting a custom `server.fs.deny` that includes a pattern with directories, and explicitly exposing the Vite dev server to the network (using `--host` or [`server.host` config option](https://vitejs.dev/config/server-options.html#server-host)) are affected.

### Patches
Fixed in vite@5.2.6, vite@5.1.7, vite@5.0.13, vite@4.5.3, vite@3.2.10, vite@2.9.18

### Details
`server.fs.deny` uses picomatch with the config of `{ matchBase: true }`. [matchBase](https://togithub.com/micromatch/picomatch/blob/master/README.md#options:~:text=Description-,basename,-boolean) only matches the basename of the file, not the path due to a bug ([https://github.com/micromatch/picomatch/issues/89](https://togithub.com/micromatch/picomatch/issues/89)). The vite config docs read like you should be able to set fs.deny to glob with picomatch. Vite also does not set `{ dot: true }` and that causes [dotfiles not to be denied](https://togithub.com/micromatch/picomatch/blob/master/README.md#options:~:text=error%20is%20thrown.-,dot,-boolean) unless they are explicitly defined.

**Reproduction**

Set fs.deny to `['**/.git/**']` and then curl for `/.git/config`.

* with `matchBase: true`, you can get any file under  `.git/` (config, HEAD, etc).
* with `matchBase: false`, you cannot get any file under  `.git/` (config, HEAD, etc).

---

### Release Notes

<details>
<summary>vitejs/vite (vite)</summary>

### [`v5.0.13`](https://togithub.com/vitejs/vite/releases/tag/v5.0.13)

[Compare Source](https://togithub.com/vitejs/vite/compare/v5.0.12...v5.0.13)

Please refer to [CHANGELOG.md](https://togithub.com/vitejs/vite/blob/v5.0.13/packages/vite/CHANGELOG.md) for details.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "" (UTC), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4yNjkuMiIsInVwZGF0ZWRJblZlciI6IjM3LjI2OS4yIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-04-08 13:20:31 +00:00
forehalo
b01a8bb2ab fix(server): give s3 client a default request timeout (#6483) 2024-04-08 08:41:55 +00:00
dependabot
9f66b810a6 build(deps): bump whoami from 1.4.1 to 1.5.1 (#6471)
Bumps [whoami](https://github.com/ardaku/whoami) from 1.4.1 to 1.5.1.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/ardaku/whoami/blob/v1/CHANGELOG.md">whoami's changelog</a>.</em></p>
<blockquote>
<h2>[1.5.1] - 2024-03-09</h2>
<h3>Fixed</h3>
<ul>
<li>Broken link in docs</li>
</ul>
<h2>[1.5.0] - 2024-03-03</h2>
<h3>Added</h3>
<ul>
<li>WASI support</li>
<li>Redox support</li>
<li>Fallible functions
<ul>
<li><code>whoami::fallible::devicename()</code></li>
<li><code>whoami::fallible::devicename_os()</code></li>
<li><code>whoami::fallible::distro()</code></li>
<li><code>whoami::fallible::hostname()</code> - notably doesn't normalize to lowercase</li>
<li><code>whoami::fallible::realname()</code></li>
<li><code>whoami::fallible::realname_os()</code></li>
<li><code>whoami::fallible::username()</code></li>
<li><code>whoami::fallible::username_os()</code></li>
</ul>
</li>
<li><code>whoami::Language</code></li>
<li><code>whoami::Country</code></li>
<li><code>whoami::langs()</code></li>
<li><code>whoami::fallible::account()</code></li>
<li><code>whoami::fallible::account_os()</code></li>
<li><code>whoami::DesktopEnv::is_gtk()</code></li>
<li><code>whoami::DesktopEnv::is_kde()</code></li>
</ul>
<h3>Removed</h3>
<ul>
<li>Generated device names that infer casing based on the hostname when the
device name is not available - now returns the hostname unchanged</li>
<li>Partial (potentially unsound) support for Android, iOS, watchOS, tvOS,
Fuchsia, Haiku, Solaris, and a few others.  These targets now use the &quot;fake&quot;
implementation.</li>
</ul>
<h3>Changed</h3>
<ul>
<li>Deprecated <code>whoami::distro_os()</code></li>
<li>Deprecated <code>whoami::hostname()</code></li>
<li>Deprecated <code>whoami::hostname_os()</code></li>
<li>Deprecated <code>whoami::lang()</code></li>
<li>illumos and Redox are no longer untested targets</li>
<li>Documented that illumos and Redox have a higher MSRV (Rust 1.65) than other
targets</li>
<li>Display implementation on <code>Platform::Illumos</code> now displays in lowercase:
illumos</li>
</ul>
<h3>Fixed</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="74a0c3b8b8"><code>74a0c3b</code></a> Prepare to release whoami 1.5.1 (<a href="https://redirect.github.com/ardaku/whoami/issues/109">#109</a>)</li>
<li><a href="7789b3f9dd"><code>7789b3f</code></a> Backport v2 -&gt; v1: Test docs in CI (<a href="https://redirect.github.com/ardaku/whoami/issues/108">#108</a>)</li>
<li><a href="4bbaf5201b"><code>4bbaf52</code></a> Prevent future potential UB in unix wrapper for <code>getpwuid()</code> (<a href="https://redirect.github.com/ardaku/whoami/issues/104">#104</a>)</li>
<li><a href="358dc0ef16"><code>358dc0e</code></a> WhoAmI 1.5.0 Release (<a href="https://redirect.github.com/ardaku/whoami/issues/94">#94</a>)</li>
<li><a href="d6ee13ed9e"><code>d6ee13e</code></a> Fix Instances of Memory Corruption on Illumos (<a href="https://redirect.github.com/ardaku/whoami/issues/93">#93</a>)</li>
<li><a href="953e702c0b"><code>953e702</code></a> Support Redox (<a href="https://redirect.github.com/ardaku/whoami/issues/92">#92</a>)</li>
<li><a href="5bc73e4e63"><code>5bc73e4</code></a> Preserve OS case for hostnames (<a href="https://redirect.github.com/ardaku/whoami/issues/86">#86</a>)</li>
<li><a href="29d5f22103"><code>29d5f22</code></a> Support WASI (<a href="https://redirect.github.com/ardaku/whoami/issues/84">#84</a>)</li>
<li><a href="d7885e7c75"><code>d7885e7</code></a> Add <code>Target</code> trait for implementing new targets (<a href="https://redirect.github.com/ardaku/whoami/issues/80">#80</a>)</li>
<li><a href="e3da4c56f9"><code>e3da4c5</code></a> Add <code>langs()</code> function, deprecate <code>lang()</code> (<a href="https://redirect.github.com/ardaku/whoami/issues/78">#78</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/ardaku/whoami/compare/v1.4.1...v1.5.1">compare view</a></li>
</ul>
</details>
<br />

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=whoami&package-manager=cargo&previous-version=1.4.1&new-version=1.5.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/toeverything/AFFiNE/network/alerts).

</details>
2024-04-08 02:46:13 +00:00
renovate
4736776ae9 chore: bump up jotai-effect version to v1 (#6472)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [jotai-effect](https://togithub.com/jotaijs/jotai-effect) | [`^0.6.0` -> `^1.0.0`](https://renovatebot.com/diffs/npm/jotai-effect/0.6.0/1.0.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/jotai-effect/1.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/jotai-effect/1.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/jotai-effect/0.6.0/1.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/jotai-effect/0.6.0/1.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>jotaijs/jotai-effect (jotai-effect)</summary>

### [`v1.0.0`](https://togithub.com/jotaijs/jotai-effect/releases/tag/v1.0.0)

[Compare Source](https://togithub.com/jotaijs/jotai-effect/compare/v0.6.0...v1.0.0)

I am thrilled to announce the release of Jotai Effect version 1.0! This milestone represents a significant achievement marking its readiness for production use.

Jotai Effect was started last October with the aim of providing a utility package for reactive side effects within the Jotai ecosystem. Over the past six months, I have been closely monitoring the API's stability and its effectiveness in real-world applications.

For those eagerly waiting for Jotai to be considered production-ready, the moment has finally arrived. The feedback from our community has been overwhelmingly positive, and today, I'm confident that Jotai Effect is ready for its prime time.

I couldn't have reached this point without the help of our amazing contributors, Daishi Kato ([@&#8203;dai-shi](https://togithub.com/dai-shi)) and Alex Yang ([@&#8203;himself65](https://togithub.com/himself65)). Their early contributions were pivotal in honing the library to what it is today. Additionally, I want to extend my gratitude to our vibrant community on [Discord](https://discord.com/invite/poimandres) for their continuous support and feedback.
Thank you.

As we celebrate this release, I encourage you to explore [Jotai Effect](https://jotai.org/docs/extensions/effect) and discover how it can streamline your reactive programming workflows. Happy Coding!

Best regards,
David Maskasky

*The full discussion can be viewed [here](https://togithub.com/jotaijs/jotai-effect/discussions/34).*

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4yNjkuMiIsInVwZGF0ZWRJblZlciI6IjM3LjI2OS4yIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-04-08 02:12:52 +00:00
renovate
4351dc8541 chore: bump up css-loader version to v7 (#6458)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [css-loader](https://togithub.com/webpack-contrib/css-loader) | [`^6.10.0` -> `^7.0.0`](https://renovatebot.com/diffs/npm/css-loader/6.10.0/7.0.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/css-loader/7.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/css-loader/7.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/css-loader/6.10.0/7.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/css-loader/6.10.0/7.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>webpack-contrib/css-loader (css-loader)</summary>

### [`v7.0.0`](https://togithub.com/webpack-contrib/css-loader/blob/HEAD/CHANGELOG.md#700-2024-04-04)

[Compare Source](https://togithub.com/webpack-contrib/css-loader/compare/v6.11.0...v7.0.0)

##### ⚠ BREAKING CHANGES

-   The `modules.namedExport` option is `true` by default if you enable the `esModule` option

Migration guide:

Before:

```js
import style from "./style.css";

console.log(style.myClass);
```

After:

```js
import * as style from "./style.css";

console.log(style.myClass);
```

-   The `modules.exportLocalsConvention` has the value `as-is` when the `modules.namedExport` option is `true` and you don't specify a value
-   Minimum supported webpack version is `5.27.0`
-   Minimum supported Node.js version is `18.12.0`

##### Features

-   The `modules.namedExports` option works fine with any `modules.exportLocalsConvention` values ([f96a110](f96a11007d))
-   Added dashed variants for the `modules.exportLocalsConvention` options ([40e1668](40e1668b83))

### [`v6.11.0`](https://togithub.com/webpack-contrib/css-loader/blob/HEAD/CHANGELOG.md#6110-2024-04-03)

[Compare Source](https://togithub.com/webpack-contrib/css-loader/compare/v6.10.0...v6.11.0)

##### Features

-   supports multiple composes ([#&#8203;1582](https://togithub.com/webpack-contrib/css-loader/issues/1582)) ([bbca614](bbca61411d))

##### Bug Fixes

-   do not break `@scope` at-rule without params ([#&#8203;1581](https://togithub.com/webpack-contrib/css-loader/issues/1581)) ([e022e3b](e022e3bb40))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4yNjkuMiIsInVwZGF0ZWRJblZlciI6IjM3LjI2OS4yIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-04-08 01:55:38 +00:00
renovate
39c90a4428 chore: bump up undici version to v6.11.1 [SECURITY] (#6457)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [undici](https://undici.nodejs.org) ([source](https://togithub.com/nodejs/undici)) | [`6.6.2` -> `6.11.1`](https://renovatebot.com/diffs/npm/undici/6.6.2/6.11.1) | [![age](https://developer.mend.io/api/mc/badges/age/npm/undici/6.11.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/undici/6.11.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/undici/6.6.2/6.11.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/undici/6.6.2/6.11.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

### GitHub Vulnerability Alerts

#### [CVE-2024-30260](https://togithub.com/nodejs/undici/security/advisories/GHSA-m4v8-wqvr-p9f7)

### Impact

Undici cleared Authorization and Proxy-Authorization headers for `fetch()`, but did not clear them for `undici.request()`.

### Patches

This has been patched in 6805746680.
Fixes has been released in v5.28.4 and v6.11.1.

### Workarounds

use `fetch()` or disable `maxRedirections`.

### References

Linzi Shang reported this.

* https://hackerone.com/reports/2408074
* https://github.com/nodejs/undici/security/advisories/GHSA-3787-6prv-h9w3

#### [CVE-2024-30261](https://togithub.com/nodejs/undici/security/advisories/GHSA-9qxr-qj54-h672)

### Impact

If an attacker can alter the `integrity` option passed to `fetch()`, they can let `fetch()` accept requests as valid even if they have been tampered.

### Patches

Fixed in d542b8cd39.
Fixes has been released in v5.28.4 and v6.11.1.

### Workarounds

Ensure that `integrity` cannot be tampered with.

### References

https://hackerone.com/reports/2377760

---

### Release Notes

<details>
<summary>nodejs/undici (undici)</summary>

### [`v6.11.1`](https://togithub.com/nodejs/undici/compare/v6.11.0...6df3c738d03dc4014a26640316bf699950d62024)

[Compare Source](https://togithub.com/nodejs/undici/compare/v6.11.0...v6.11.1)

### [`v6.11.0`](https://togithub.com/nodejs/undici/compare/v6.10.2...ee5f892f3955eaca37730ed30349153ba203e9cd)

[Compare Source](https://togithub.com/nodejs/undici/compare/v6.10.2...v6.11.0)

### [`v6.10.2`](https://togithub.com/nodejs/undici/releases/tag/v6.10.2)

[Compare Source](https://togithub.com/nodejs/undici/compare/v6.10.1...v6.10.2)

##### What's Changed

-   Do not fail test if streams support typed arrays by [@&#8203;mcollina](https://togithub.com/mcollina) in [https://github.com/nodejs/undici/pull/2978](https://togithub.com/nodejs/undici/pull/2978)
-   fix(fetch): properly redirect non-ascii location header url by [@&#8203;Xvezda](https://togithub.com/Xvezda) in [https://github.com/nodejs/undici/pull/2971](https://togithub.com/nodejs/undici/pull/2971)
-   perf: Remove double-stringify in setCookie by [@&#8203;peterver](https://togithub.com/peterver) in [https://github.com/nodejs/undici/pull/2980](https://togithub.com/nodejs/undici/pull/2980)
-   \[fix [#&#8203;2982](https://togithub.com/nodejs/undici/issues/2982)] use DispatcherInterceptor type for Dispatcher#Compose by [@&#8203;clovis-guillemot](https://togithub.com/clovis-guillemot) in [https://github.com/nodejs/undici/pull/2983](https://togithub.com/nodejs/undici/pull/2983)
-   fix: make EventSource properties enumerable by [@&#8203;MattBidewell](https://togithub.com/MattBidewell) in [https://github.com/nodejs/undici/pull/2987](https://togithub.com/nodejs/undici/pull/2987)
-   docs: ✏️ fixed benchmark links by [@&#8203;benhalverson](https://togithub.com/benhalverson) in [https://github.com/nodejs/undici/pull/2991](https://togithub.com/nodejs/undici/pull/2991)
-   fix([#&#8203;2986](https://togithub.com/nodejs/undici/issues/2986)): bad start check by [@&#8203;metcoder95](https://togithub.com/metcoder95) in [https://github.com/nodejs/undici/pull/2992](https://togithub.com/nodejs/undici/pull/2992)
-   fix(H2 Client): bind stream 'data' listener only after received 'response' event by [@&#8203;St3ffGv4](https://togithub.com/St3ffGv4) in [https://github.com/nodejs/undici/pull/2985](https://togithub.com/nodejs/undici/pull/2985)
-   feat:  added search input by [@&#8203;benhalverson](https://togithub.com/benhalverson) in [https://github.com/nodejs/undici/pull/2993](https://togithub.com/nodejs/undici/pull/2993)
-   chore: validate responses can be consumed without a Content-Length or… by [@&#8203;jacob-ebey](https://togithub.com/jacob-ebey) in [https://github.com/nodejs/undici/pull/2995](https://togithub.com/nodejs/undici/pull/2995)
-   fix error message by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2998](https://togithub.com/nodejs/undici/pull/2998)
-   Revert "perf: reuse TextDecoder instance ([#&#8203;2863](https://togithub.com/nodejs/undici/issues/2863))" by [@&#8203;panva](https://togithub.com/panva) in [https://github.com/nodejs/undici/pull/2999](https://togithub.com/nodejs/undici/pull/2999)
-   test: remove only by [@&#8203;metcoder95](https://togithub.com/metcoder95) in [https://github.com/nodejs/undici/pull/3001](https://togithub.com/nodejs/undici/pull/3001)

##### New Contributors

-   [@&#8203;Xvezda](https://togithub.com/Xvezda) made their first contribution in [https://github.com/nodejs/undici/pull/2971](https://togithub.com/nodejs/undici/pull/2971)
-   [@&#8203;peterver](https://togithub.com/peterver) made their first contribution in [https://github.com/nodejs/undici/pull/2980](https://togithub.com/nodejs/undici/pull/2980)
-   [@&#8203;clovis-guillemot](https://togithub.com/clovis-guillemot) made their first contribution in [https://github.com/nodejs/undici/pull/2983](https://togithub.com/nodejs/undici/pull/2983)
-   [@&#8203;MattBidewell](https://togithub.com/MattBidewell) made their first contribution in [https://github.com/nodejs/undici/pull/2987](https://togithub.com/nodejs/undici/pull/2987)
-   [@&#8203;benhalverson](https://togithub.com/benhalverson) made their first contribution in [https://github.com/nodejs/undici/pull/2991](https://togithub.com/nodejs/undici/pull/2991)
-   [@&#8203;St3ffGv4](https://togithub.com/St3ffGv4) made their first contribution in [https://github.com/nodejs/undici/pull/2985](https://togithub.com/nodejs/undici/pull/2985)
-   [@&#8203;jacob-ebey](https://togithub.com/jacob-ebey) made their first contribution in [https://github.com/nodejs/undici/pull/2995](https://togithub.com/nodejs/undici/pull/2995)

**Full Changelog**: https://github.com/nodejs/undici/compare/v6.10.0...v6.10.2

### [`v6.10.1`](https://togithub.com/nodejs/undici/compare/v6.10.0...dd3918fee4f90e02fb93ff1bc04e707144041938)

[Compare Source](https://togithub.com/nodejs/undici/compare/v6.10.0...v6.10.1)

### [`v6.10.0`](https://togithub.com/nodejs/undici/releases/tag/v6.10.0)

[Compare Source](https://togithub.com/nodejs/undici/compare/v6.9.0...v6.10.0)

#### What's Changed

-   test: fix flakyness of issue-803 test by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2960](https://togithub.com/nodejs/undici/pull/2960)
-   Cleanup format by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2959](https://togithub.com/nodejs/undici/pull/2959)
-   Chore: run tests daily against node nightly by [@&#8203;mweberxyz](https://togithub.com/mweberxyz) in [https://github.com/nodejs/undici/pull/2969](https://togithub.com/nodejs/undici/pull/2969)
-   fix: fix retry handler option by [@&#8203;acommodari](https://togithub.com/acommodari) in [https://github.com/nodejs/undici/pull/2962](https://togithub.com/nodejs/undici/pull/2962)
-   build(deps): bump node from `4999fa1` to `577f8eb` in /build by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/nodejs/undici/pull/2974](https://togithub.com/nodejs/undici/pull/2974)
-   feat(TS): add types for composed dispatchers by [@&#8203;metcoder95](https://togithub.com/metcoder95) in [https://github.com/nodejs/undici/pull/2967](https://togithub.com/nodejs/undici/pull/2967)
-   fix: count for error response and network errors by [@&#8203;metcoder95](https://togithub.com/metcoder95) in [https://github.com/nodejs/undici/pull/2966](https://togithub.com/nodejs/undici/pull/2966)

#### New Contributors

-   [@&#8203;mweberxyz](https://togithub.com/mweberxyz) made their first contribution in [https://github.com/nodejs/undici/pull/2969](https://togithub.com/nodejs/undici/pull/2969)
-   [@&#8203;acommodari](https://togithub.com/acommodari) made their first contribution in [https://github.com/nodejs/undici/pull/2962](https://togithub.com/nodejs/undici/pull/2962)

**Full Changelog**: https://github.com/nodejs/undici/compare/v6.9.0...v6.10.0

### [`v6.9.0`](https://togithub.com/nodejs/undici/releases/tag/v6.9.0)

[Compare Source](https://togithub.com/nodejs/undici/compare/v6.8.0...v6.9.0)

#### What's Changed

-   feat: add new dispatch compose by [@&#8203;metcoder95](https://togithub.com/metcoder95) in [https://github.com/nodejs/undici/pull/2826](https://togithub.com/nodejs/undici/pull/2826)
-   ci: add macos-latest to test-matrix by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2952](https://togithub.com/nodejs/undici/pull/2952)
-   types: align RequestInit.body type with lib.dom.ts by [@&#8203;jdufresne](https://togithub.com/jdufresne) in [https://github.com/nodejs/undici/pull/2956](https://togithub.com/nodejs/undici/pull/2956)
-   ci: pin versions of github actions by [@&#8203;UlisesGascon](https://togithub.com/UlisesGascon) in [https://github.com/nodejs/undici/pull/2957](https://togithub.com/nodejs/undici/pull/2957)
-   fetch: improve output for FormData, Response, Request by [@&#8203;mertcanaltin](https://togithub.com/mertcanaltin) in [https://github.com/nodejs/undici/pull/2955](https://togithub.com/nodejs/undici/pull/2955)
-   perf: optimize collectASequenceOfBytes by [@&#8203;tsctx](https://togithub.com/tsctx) in [https://github.com/nodejs/undici/pull/2958](https://togithub.com/nodejs/undici/pull/2958)

#### New Contributors

-   [@&#8203;jdufresne](https://togithub.com/jdufresne) made their first contribution in [https://github.com/nodejs/undici/pull/2956](https://togithub.com/nodejs/undici/pull/2956)
-   [@&#8203;UlisesGascon](https://togithub.com/UlisesGascon) made their first contribution in [https://github.com/nodejs/undici/pull/2957](https://togithub.com/nodejs/undici/pull/2957)

**Full Changelog**: https://github.com/nodejs/undici/compare/v6.8.0...v6.9.0

### [`v6.8.0`](https://togithub.com/nodejs/undici/releases/tag/v6.8.0)

[Compare Source](https://togithub.com/nodejs/undici/compare/v6.7.1...v6.8.0)

#### What's Changed

-   fix: send correct SNI for proxy connections by [@&#8203;chrros95](https://togithub.com/chrros95) in [https://github.com/nodejs/undici/pull/2939](https://togithub.com/nodejs/undici/pull/2939)
-   build(deps): bump node from `8bf9240` to `7bfef1d` in /build by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/nodejs/undici/pull/2937](https://togithub.com/nodejs/undici/pull/2937)
-   fetch: improve util.inspect output for web specifications by [@&#8203;mertcanaltin](https://togithub.com/mertcanaltin) in [https://github.com/nodejs/undici/pull/2938](https://togithub.com/nodejs/undici/pull/2938)
-   ci: fix broken ci on windows and node v21 because of libuv bug by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2941](https://togithub.com/nodejs/undici/pull/2941)
-   perf: improve getResolveErrorBodyCallback by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2940](https://togithub.com/nodejs/undici/pull/2940)
-   fix: don't assign kAgent twice by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2942](https://togithub.com/nodejs/undici/pull/2942)
-   perf: dump immediatly if known size exceeds limit by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2882](https://togithub.com/nodejs/undici/pull/2882)
-   build(deps): bump node from `7bfef1d` to `4999fa1` in /build by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/nodejs/undici/pull/2946](https://togithub.com/nodejs/undici/pull/2946)
-   try to fix windows failure by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2950](https://togithub.com/nodejs/undici/pull/2950)
-   perf: improve parsing form-data by [@&#8203;tsctx](https://togithub.com/tsctx) in [https://github.com/nodejs/undici/pull/2944](https://togithub.com/nodejs/undici/pull/2944)

#### New Contributors

-   [@&#8203;chrros95](https://togithub.com/chrros95) made their first contribution in [https://github.com/nodejs/undici/pull/2939](https://togithub.com/nodejs/undici/pull/2939)

**Full Changelog**: https://github.com/nodejs/undici/compare/v6.7.1...v6.8.0

### [`v6.7.1`](https://togithub.com/nodejs/undici/releases/tag/v6.7.1)

[Compare Source](https://togithub.com/nodejs/undici/compare/v6.7.0...v6.7.1)

#### What's Changed

-   fetch: use EOL of os-module by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2915](https://togithub.com/nodejs/undici/pull/2915)
-   ci: only send codecov from ubuntu and node by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2914](https://togithub.com/nodejs/undici/pull/2914)
-   tests: improve skip for unix.js tests, remove skipped tests by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2916](https://togithub.com/nodejs/undici/pull/2916)
-   chore: fix typo in isHistoryNavigation comments by [@&#8203;kachick](https://togithub.com/kachick) in [https://github.com/nodejs/undici/pull/2920](https://togithub.com/nodejs/undici/pull/2920)
-   fix(benchmark): set body correctly by [@&#8203;tsctx](https://togithub.com/tsctx) in [https://github.com/nodejs/undici/pull/2918](https://togithub.com/nodejs/undici/pull/2918)
-   chore: increase test coverage to 100% for /lib/api/api-request.js by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2912](https://togithub.com/nodejs/undici/pull/2912)
-   fix: chunksDecode cuts off 3 characters at the end if having BOM by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2922](https://togithub.com/nodejs/undici/pull/2922)
-   docs: clarify URI parsing behavior of ProxyAgent constructor by [@&#8203;rossilor95](https://togithub.com/rossilor95) in [https://github.com/nodejs/undici/pull/2893](https://togithub.com/nodejs/undici/pull/2893)
-   implement sync formdata parser by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2911](https://togithub.com/nodejs/undici/pull/2911)
-   Fix docs links and add examples to sidebar by [@&#8203;tastypackets](https://togithub.com/tastypackets) in [https://github.com/nodejs/undici/pull/2895](https://togithub.com/nodejs/undici/pull/2895)
-   doc: update diagnostics channel request headers type change by [@&#8203;jessezhang91](https://togithub.com/jessezhang91) in [https://github.com/nodejs/undici/pull/2925](https://togithub.com/nodejs/undici/pull/2925)
-   perf: optimize getResolveErrorBodyCallback by [@&#8203;tsctx](https://togithub.com/tsctx) in [https://github.com/nodejs/undici/pull/2921](https://togithub.com/nodejs/undici/pull/2921)
-   override request dispatcher from init by [@&#8203;matthieusieben](https://togithub.com/matthieusieben) in [https://github.com/nodejs/undici/pull/2928](https://togithub.com/nodejs/undici/pull/2928)
-   add busboy tests by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2924](https://togithub.com/nodejs/undici/pull/2924)
-   fix(benchmark): make it fair by [@&#8203;tsctx](https://togithub.com/tsctx) in [https://github.com/nodejs/undici/pull/2929](https://togithub.com/nodejs/undici/pull/2929)
-   Revert "chore: remove no-simd wasm" by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2935](https://togithub.com/nodejs/undici/pull/2935)
-   build(deps): bump node from `d3271e4` to `8bf9240` in /build by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/nodejs/undici/pull/2936](https://togithub.com/nodejs/undici/pull/2936)
-   Flip link between docs and README by [@&#8203;mcollina](https://togithub.com/mcollina) in [https://github.com/nodejs/undici/pull/2933](https://togithub.com/nodejs/undici/pull/2933)

#### New Contributors

-   [@&#8203;kachick](https://togithub.com/kachick) made their first contribution in [https://github.com/nodejs/undici/pull/2920](https://togithub.com/nodejs/undici/pull/2920)
-   [@&#8203;tastypackets](https://togithub.com/tastypackets) made their first contribution in [https://github.com/nodejs/undici/pull/2895](https://togithub.com/nodejs/undici/pull/2895)
-   [@&#8203;jessezhang91](https://togithub.com/jessezhang91) made their first contribution in [https://github.com/nodejs/undici/pull/2925](https://togithub.com/nodejs/undici/pull/2925)
-   [@&#8203;matthieusieben](https://togithub.com/matthieusieben) made their first contribution in [https://github.com/nodejs/undici/pull/2928](https://togithub.com/nodejs/undici/pull/2928)

**Full Changelog**: https://github.com/nodejs/undici/compare/v6.7.0...v6.7.1

### [`v6.7.0`](https://togithub.com/nodejs/undici/releases/tag/v6.7.0)

[Compare Source](https://togithub.com/nodejs/undici/compare/v6.6.2...v6.7.0)

#### What's Changed

-   test: remove t.diagnostics() calls in push-dont-push.js test by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2715](https://togithub.com/nodejs/undici/pull/2715)
-   fix: fix flaky debug test by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2714](https://togithub.com/nodejs/undici/pull/2714)
-   fix: HTTP2 tweaks by [@&#8203;metcoder95](https://togithub.com/metcoder95) in [https://github.com/nodejs/undici/pull/2711](https://togithub.com/nodejs/undici/pull/2711)
-   test: improve cookie tests by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2693](https://togithub.com/nodejs/undici/pull/2693)
-   test: response.url after redirect is set to target url by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2716](https://togithub.com/nodejs/undici/pull/2716)
-   chore: remove mocha and chai by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2696](https://togithub.com/nodejs/undici/pull/2696)
-   test: replace t.pass with t.ok by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2721](https://togithub.com/nodejs/undici/pull/2721)
-   perf: remove redundant operation in FormData by [@&#8203;tsctx](https://togithub.com/tsctx) in [https://github.com/nodejs/undici/pull/2726](https://togithub.com/nodejs/undici/pull/2726)
-   Add support for passing iterable objects as headers by [@&#8203;JaoodxD](https://togithub.com/JaoodxD) in [https://github.com/nodejs/undici/pull/2708](https://togithub.com/nodejs/undici/pull/2708)
-   chore: refine esbuild & node detection by [@&#8203;mochaaP](https://togithub.com/mochaaP) in [https://github.com/nodejs/undici/pull/2677](https://togithub.com/nodejs/undici/pull/2677)
-   chore: rephrase some comments by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2717](https://togithub.com/nodejs/undici/pull/2717)
-   test: replace t.type with t.ok and instanceof by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2720](https://togithub.com/nodejs/undici/pull/2720)
-   remove useless options in web streams by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2729](https://togithub.com/nodejs/undici/pull/2729)
-   Let's add superagent to the benchmark. closes [#&#8203;2730](https://togithub.com/nodejs/undici/issues/2730) by [@&#8203;eddienubes](https://togithub.com/eddienubes) in [https://github.com/nodejs/undici/pull/2731](https://togithub.com/nodejs/undici/pull/2731)
-   convert node build to latin1 by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2673](https://togithub.com/nodejs/undici/pull/2673)
-   simplify formData body parsing by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2735](https://togithub.com/nodejs/undici/pull/2735)
-   chore: migrate a batch of tests to node test runner no. 1 by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2719](https://togithub.com/nodejs/undici/pull/2719)
-   chore: migrate a batch of tests to node test runner no. 2 by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2737](https://togithub.com/nodejs/undici/pull/2737)
-   chore: migrate a batch of tests to node test runner no. 4 by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2739](https://togithub.com/nodejs/undici/pull/2739)
-   chore: migrate a batch of tests to node test runner no. 5 by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2740](https://togithub.com/nodejs/undici/pull/2740)
-   chore: migrate a batch of tests to node test runner no. 3 by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2738](https://togithub.com/nodejs/undici/pull/2738)
-   chore: migrate a batch of tests to node test runner no. 6 by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2741](https://togithub.com/nodejs/undici/pull/2741)
-   chore: migrate a batch of tests to node test runner no. 8 by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2744](https://togithub.com/nodejs/undici/pull/2744)
-   chore: migrate a batch of tests to node test runner no. 7 by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2742](https://togithub.com/nodejs/undici/pull/2742)
-   build(deps-dev): bump cronometro from 2.0.2 to 3.0.1 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/nodejs/undici/pull/2749](https://togithub.com/nodejs/undici/pull/2749)
-   perf: always use the same prototype Iterator by [@&#8203;tsctx](https://togithub.com/tsctx) in [https://github.com/nodejs/undici/pull/2743](https://togithub.com/nodejs/undici/pull/2743)
-   chore: migrate a batch of tests to node test runner no. 9, remove tap by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2746](https://togithub.com/nodejs/undici/pull/2746)
-   chore: remove usage of http-errors in proxy example by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2753](https://togithub.com/nodejs/undici/pull/2753)
-   fix: dont ship wasm files of llhttp via npm by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2752](https://togithub.com/nodejs/undici/pull/2752)
-   fix: handle request body as late as possible by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2734](https://togithub.com/nodejs/undici/pull/2734)
-   perf(tree): avoid recursive calls by [@&#8203;tsctx](https://togithub.com/tsctx) in [https://github.com/nodejs/undici/pull/2755](https://togithub.com/nodejs/undici/pull/2755)
-   docs: fix favicon by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2758](https://togithub.com/nodejs/undici/pull/2758)
-   chore: use mermaid engine and mermaid in markdown by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2759](https://togithub.com/nodejs/undici/pull/2759)
-   chore: remove sinon dev dependency by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2767](https://togithub.com/nodejs/undici/pull/2767)
-   tests: skip test/node-test/debug on node 21.6.2 and windows by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2765](https://togithub.com/nodejs/undici/pull/2765)
-   chore: improve usage of skip in tests by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2761](https://togithub.com/nodejs/undici/pull/2761)
-   feat: improve mock error breadcrumbs by [@&#8203;rossilor95](https://togithub.com/rossilor95) in [https://github.com/nodejs/undici/pull/2774](https://togithub.com/nodejs/undici/pull/2774)
-   expose MessageEvent in fetch bundle by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2770](https://togithub.com/nodejs/undici/pull/2770)
-   test: always exit with 0 when running in Node's Daily WPT Report CI job by [@&#8203;panva](https://togithub.com/panva) in [https://github.com/nodejs/undici/pull/2778](https://togithub.com/nodejs/undici/pull/2778)
-   fix: add node prefix for util to fix issue in env with min version node 18 by [@&#8203;riderx](https://togithub.com/riderx) in [https://github.com/nodejs/undici/pull/2775](https://togithub.com/nodejs/undici/pull/2775)
-   perf: improve perf of parseRawHeaders by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2781](https://togithub.com/nodejs/undici/pull/2781)
-   fix: make mock-agent.js test more resilient by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2780](https://togithub.com/nodejs/undici/pull/2780)
-   chore: make some test run even without internet connection by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2786](https://togithub.com/nodejs/undici/pull/2786)
-   mock: improve validateReplyParameters by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2783](https://togithub.com/nodejs/undici/pull/2783)
-   perf: improve TernarySearchTree by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2782](https://togithub.com/nodejs/undici/pull/2782)
-   fix: convert HeadersInit to sequence/dictionary correctly by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2784](https://togithub.com/nodejs/undici/pull/2784)
-   chore: improve getFieldValue by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2785](https://togithub.com/nodejs/undici/pull/2785)
-   Add RetryHandler to sidebar by [@&#8203;mcollina](https://togithub.com/mcollina) in [https://github.com/nodejs/undici/pull/2797](https://togithub.com/nodejs/undici/pull/2797)
-   Add RetryAgent by [@&#8203;mcollina](https://togithub.com/mcollina) in [https://github.com/nodejs/undici/pull/2798](https://togithub.com/nodejs/undici/pull/2798)
-   build(deps): bump step-security/harden-runner from 2.6.0 to 2.7.0 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/nodejs/undici/pull/2690](https://togithub.com/nodejs/undici/pull/2690)
-   build(deps): bump actions/checkout from 4.1.0 to 4.1.1 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/nodejs/undici/pull/2393](https://togithub.com/nodejs/undici/pull/2393)
-   build(deps): bump actions/upload-artifact from 3.1.3 to 4.3.1 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/nodejs/undici/pull/2799](https://togithub.com/nodejs/undici/pull/2799)
-   build(deps): bump node from 20-alpine to 21-alpine in /build by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/nodejs/undici/pull/2803](https://togithub.com/nodejs/undici/pull/2803)
-   perf: improve sort algorithm by [@&#8203;tsctx](https://togithub.com/tsctx) in [https://github.com/nodejs/undici/pull/2756](https://togithub.com/nodejs/undici/pull/2756)
-   refactor: move web stuff into their own folder by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2793](https://togithub.com/nodejs/undici/pull/2793)
-   `s/ dispactgher/dispatcher/` by [@&#8203;steveluscher](https://togithub.com/steveluscher) in [https://github.com/nodejs/undici/pull/2807](https://togithub.com/nodejs/undici/pull/2807)
-   Use paralellelRequests instead of connections to calculate req/sec in benchmarks by [@&#8203;mcollina](https://togithub.com/mcollina) in [https://github.com/nodejs/undici/pull/2800](https://togithub.com/nodejs/undici/pull/2800)
-   Split out documentation into separate directory by [@&#8203;Ethan-Arrowood](https://togithub.com/Ethan-Arrowood) in [https://github.com/nodejs/undici/pull/2788](https://togithub.com/nodejs/undici/pull/2788)
-   build(deps): bump fastify/github-action-merge-dependabot from 3.9.1 to 3.10.1 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/nodejs/undici/pull/2820](https://togithub.com/nodejs/undici/pull/2820)
-   build(deps): bump actions/dependency-review-action from 4.0.0 to 4.1.3 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/nodejs/undici/pull/2821](https://togithub.com/nodejs/undici/pull/2821)
-   build(deps): bump github/codeql-action from 3.23.2 to 3.24.4 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/nodejs/undici/pull/2818](https://togithub.com/nodejs/undici/pull/2818)
-   build(deps): bump actions/setup-node from 4.0.1 to 4.0.2 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/nodejs/undici/pull/2819](https://togithub.com/nodejs/undici/pull/2819)
-   fix: move CNAME and .nojekyll to root by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2822](https://togithub.com/nodejs/undici/pull/2822)
-   remove all fetchParam event handlers by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2823](https://togithub.com/nodejs/undici/pull/2823)
-   feat: refactor ProxyAgent constructor to also accept single URL argument by [@&#8203;rossilor95](https://togithub.com/rossilor95) in [https://github.com/nodejs/undici/pull/2810](https://togithub.com/nodejs/undici/pull/2810)
-   fix: isCTLExcludingHtab by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2790](https://togithub.com/nodejs/undici/pull/2790)
-   refactor: move files into logical folders by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2813](https://togithub.com/nodejs/undici/pull/2813)
-   refactor: move fixed-queeu to dispatcher and rm node folder by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2827](https://togithub.com/nodejs/undici/pull/2827)
-   chore: create package.json in benchmarks by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2766](https://togithub.com/nodejs/undici/pull/2766)
-   build(deps): bump github/codeql-action from 3.24.4 to 3.24.5 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/nodejs/undici/pull/2829](https://togithub.com/nodejs/undici/pull/2829)
-   chore: use lts for pubish types workflow by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2830](https://togithub.com/nodejs/undici/pull/2830)
-   add dispatcher option to Request by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2831](https://togithub.com/nodejs/undici/pull/2831)
-   fix url referrer wpt by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2832](https://togithub.com/nodejs/undici/pull/2832)
-   refactor: remove own sort logic by [@&#8203;tsctx](https://togithub.com/tsctx) in [https://github.com/nodejs/undici/pull/2834](https://togithub.com/nodejs/undici/pull/2834)
-   fix(fetch): prevent crash when `fetch` is aborted with `null` as the `AbortSignal's` `reason` by [@&#8203;steveluscher](https://togithub.com/steveluscher) in [https://github.com/nodejs/undici/pull/2833](https://togithub.com/nodejs/undici/pull/2833)
-   refactor: avoid http2 dynamic dispatch in socket handlers by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2839](https://togithub.com/nodejs/undici/pull/2839)
-   build(deps-dev): bump proxy from 1.0.2 to 2.1.1 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/nodejs/undici/pull/2137](https://togithub.com/nodejs/undici/pull/2137)
-   perf(tree): reduce overhead of build TernarySearchTree by [@&#8203;tsctx](https://togithub.com/tsctx) in [https://github.com/nodejs/undici/pull/2840](https://togithub.com/nodejs/undici/pull/2840)
-   webidl: implement resizable arraybuffer checks by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2094](https://togithub.com/nodejs/undici/pull/2094)
-   websocket server only needs to reply with a single subprotocol by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2845](https://togithub.com/nodejs/undici/pull/2845)
-   unite webidl stringification by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2843](https://togithub.com/nodejs/undici/pull/2843)
-   fix: deflake connect-timeout test by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2851](https://togithub.com/nodejs/undici/pull/2851)
-   fix: coverage reporting by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2763](https://togithub.com/nodejs/undici/pull/2763)
-   fix: pipelining logic is not relevant for h2 by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2850](https://togithub.com/nodejs/undici/pull/2850)
-   processBody doesn't need to return a promise by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2858](https://togithub.com/nodejs/undici/pull/2858)
-   refactor: split client into client-h1/h2 by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2848](https://togithub.com/nodejs/undici/pull/2848)
-   ci: fix concurrency by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2862](https://togithub.com/nodejs/undici/pull/2862)
-   perf: improve performance of isValidSubprotocol by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2861](https://togithub.com/nodejs/undici/pull/2861)
-   perf: reuse TextDecoder instance by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2863](https://togithub.com/nodejs/undici/pull/2863)
-   chore: restructure benchmarks, use kebab-case by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2864](https://togithub.com/nodejs/undici/pull/2864)
-   cookies: improve perf of toIMFDate by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2867](https://togithub.com/nodejs/undici/pull/2867)
-   cookies: fix validateCookiePath by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2866](https://togithub.com/nodejs/undici/pull/2866)
-   refactor: move out more h2 from core client by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2860](https://togithub.com/nodejs/undici/pull/2860)
-   mock: improve test coverage of buildHeadersFromArray by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2872](https://togithub.com/nodejs/undici/pull/2872)
-   fix: remove broken build request hack by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2874](https://togithub.com/nodejs/undici/pull/2874)
-   chore: filenames should use kebab-case by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2873](https://togithub.com/nodejs/undici/pull/2873)
-   refactor: split out last h1 specific code from core by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2876](https://togithub.com/nodejs/undici/pull/2876)
-   fix: make pipelining limit work for h2 by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2875](https://togithub.com/nodejs/undici/pull/2875)
-   fix: http2 doesn't have pipelining queue by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2878](https://togithub.com/nodejs/undici/pull/2878)
-   fix: minor connect cleanup by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2877](https://togithub.com/nodejs/undici/pull/2877)
-   Request headers types by [@&#8203;JaoodxD](https://togithub.com/JaoodxD) in [https://github.com/nodejs/undici/pull/2879](https://togithub.com/nodejs/undici/pull/2879)
-   ci: remove concurrency by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2880](https://togithub.com/nodejs/undici/pull/2880)
-   fix: prefer queueMicrotask by [@&#8203;ronag](https://togithub.com/ronag) in [https://github.com/nodejs/undici/pull/2881](https://togithub.com/nodejs/undici/pull/2881)
-   chore: remove no-simd wasm by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2871](https://togithub.com/nodejs/undici/pull/2871)
-   cookies: improve validateCookieValue by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2883](https://togithub.com/nodejs/undici/pull/2883)
-   cookies: improve validateCookieName by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2884](https://togithub.com/nodejs/undici/pull/2884)
-   Properly parse set-cookie header using http2 by [@&#8203;jeanp413](https://togithub.com/jeanp413) in [https://github.com/nodejs/undici/pull/2886](https://togithub.com/nodejs/undici/pull/2886)
-   doc deprecate bodymixin.formData by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2892](https://togithub.com/nodejs/undici/pull/2892)
-   perf: optimize check invalid field-vchar by [@&#8203;tsctx](https://togithub.com/tsctx) in [https://github.com/nodejs/undici/pull/2889](https://togithub.com/nodejs/undici/pull/2889)
-   build(deps): bump github/codeql-action from 3.24.5 to 3.24.6 by [@&#8203;dependabot](https://togithub.com/dependabot) in [https://github.com/nodejs/undici/pull/2897](https://togithub.com/nodejs/undici/pull/2897)
-   fix issue 2898 by [@&#8203;KhafraDev](https://togithub.com/KhafraDev) in [https://github.com/nodejs/undici/pull/2900](https://togithub.com/nodejs/undici/pull/2900)
-   tests: ignore catch block when requiring crypto module by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2901](https://togithub.com/nodejs/undici/pull/2901)
-   websocket: remove dead code in parseCloseBody by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2902](https://togithub.com/nodejs/undici/pull/2902)
-   fix: tests dont need process.exit by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2909](https://togithub.com/nodejs/undici/pull/2909)
-   chore: remove proxyquire by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2906](https://togithub.com/nodejs/undici/pull/2906)
-   chore: remove import-fresh as devDependency by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2908](https://togithub.com/nodejs/undici/pull/2908)
-   perf(headers): a single set-cookie by [@&#8203;tsctx](https://togithub.com/tsctx) in [https://github.com/nodejs/undici/pull/2903](https://togithub.com/nodejs/undici/pull/2903)
-   websocket: improve .close() by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2865](https://togithub.com/nodejs/undici/pull/2865)
-   feat: add sending data benchmark by [@&#8203;tsctx](https://togithub.com/tsctx) in [https://github.com/nodejs/undici/pull/2905](https://togithub.com/nodejs/undici/pull/2905)
-   ci: integrate workflows into nodejs.yml by [@&#8203;Uzlopak](https://togithub.com/Uzlopak) in [https://github.com/nodejs/undici/pull/2899](https://togithub.com/nodejs/undici/pull/2899)

#### New Contributors

-   [@&#8203;JaoodxD](https://togithub.com/JaoodxD) made their first contribution in [https://github.com/nodejs/undici/pull/2708](https://togithub.com/nodejs/undici/pull/2708)
-   [@&#8203;eddienubes](https://togithub.com/eddienubes) made their first contribution in [https://github.com/nodejs/undici/pull/2731](https://togithub.com/nodejs/undici/pull/2731)
-   [@&#8203;riderx](https://togithub.com/riderx) made their first contribution in [https://github.com/nodejs/undici/pull/2775](https://togithub.com/nodejs/undici/pull/2775)
-   [@&#8203;steveluscher](https://togithub.com/steveluscher) made their first contribution in [https://github.com/nodejs/undici/pull/2807](https://togithub.com/nodejs/undici/pull/2807)
-   [@&#8203;jeanp413](https://togithub.com/jeanp413) made their first contribution in [https://github.com/nodejs/undici/pull/2886](https://togithub.com/nodejs/undici/pull/2886)

**Full Changelog**: https://github.com/nodejs/undici/compare/v6.6.2...v6.7.0

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "" (UTC), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://developer.mend.io/github/toeverything/AFFiNE).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy4yNjkuMiIsInVwZGF0ZWRJblZlciI6IjM3LjI2OS4yIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-04-08 01:42:52 +00:00
liuyi
edef9b2735 fix(server): only auto apply coupon in pro subscription (#6452) 2024-04-03 17:13:18 +08:00
liuyi
6fa4b7da54 feat(core): avoid popup window being blocked (#6451) 2024-04-03 16:50:09 +08:00
CatsJuice
3e9e2ce93b feat(core): pricing plans ai subscription ui (#6449) 2024-04-03 08:04:30 +00:00
JimmFly
e7de20f648 fix(core): add max-height to tag filter (#6442)
close TOV-778
close #6334
2024-04-03 04:04:37 +00:00
forehalo
2dc628eca5 fix(server): doc cache makes publish mode updating failed (#6444) 2024-04-03 03:34:01 +00:00
JimmFly
dde4e165c6 feat(core): add confirm modal to add new doc button (#6436) 2024-04-03 03:14:06 +00:00
JimmFly
f93ffbeb6f fix(core): unexpected collection item drag event (#6443)
close TOV-785

before:

https://github.com/toeverything/AFFiNE/assets/102217452/71b563a5-7a1a-4ec5-871f-8de373742bad
2024-04-02 14:24:19 +00:00
JimmFly
4624a4923d feat(core): add favorite operation to all collection (#6428)
https://github.com/toeverything/AFFiNE/assets/102217452/d90553aa-6076-4ecc-996b-a8398991982a
2024-04-02 08:35:26 +00:00
CatsJuice
381be8a982 fix(component): useConfirmModal can't be closed automatically when 'onConfirm' is non-async (#6439) 2024-04-02 08:23:27 +00:00
darkskygit
3c01d944fb feat: add prompt service (#6241)
fix CLOUD-19
2024-04-02 07:04:54 +00:00
darkskygit
593161dccb feat: basic copilot plugin implement (#6229)
fix CLOUD-25
2024-04-02 07:04:49 +00:00
EYHN
366e0a4b60 feat(server): adjust telemetry config (#6424) 2024-04-02 03:44:48 +00:00
EYHN
8bd2408b0c fix(core): selfhost should not use favicon from affine.pro (#6425) 2024-04-02 03:31:34 +00:00
CatsJuice
9127bfae67 refactor(core): replace all notification relies on jotai (#6417)
- remove all notification that implemented with jotai and replaced with new `notify`
- Add some notify presets:
  - `notify.error`
  - `notify.success`
  - `notify.warning`
2024-04-02 03:19:07 +00:00
CatsJuice
a4cd51e503 refactor(component): new notification center implemented with sonner (#6416)
The Notification has been reimplemented using sooner, no longer relies on jotai, and new story has been added.

- Before
  ```ts
  import { pushNotificationAtom } from '@affine/component/notification-center';
  import { useSetAtom } from 'jotai';

  export const Component = () => {
    const pushNotification = useSetAtom(pushNotificationAtom);
    pushNotification({ ... });
  }
  ```

- After
  ```ts
  import { notify } from "@affine/component";

  export const Component = () => {
    notify({ ... });
  }
  ```
2024-04-02 03:18:57 +00:00
EYHN
80c7750f4a fix(core): selfhost redirect url (#6426) 2024-04-01 13:26:39 +00:00
Taylor Hoffmann
94ea6a07b3 fix: broken links in README.md (#6418) 2024-04-01 17:12:15 +08:00
JimmFly
62d9b0c959 fix(core): unexpected editable state after trash page refresh (#6433)
close TOV-772
2024-04-01 08:16:17 +00:00
JimmFly
2631b41e6d feat(core): add new doc button to collection page (#6423)
close TOV-64

You can add this document to the collection while creating a new document on the collection page.

<img width="790" alt="image" src="https://github.com/toeverything/AFFiNE/assets/102217452/828f80af-a1a5-47c2-98c2-a574cf79052c">
2024-04-01 08:05:23 +00:00
EYHN
9c6eba8971 fix(electron): fix build script (#6422) 2024-04-01 07:51:35 +00:00
CatsJuice
f4e1e23120 feat(core): add cloud usage in sidebar avatar menu (#6400)
- Extract logic of getting cloud storage usage information into new hook
- Move `<StorageProgress />`: `component` → `core`
- Set minimum progress `0.5%`
- Add cloud usage progress bar in sidebar user avatar's dropdown

![CleanShot 2024-03-29 at 17.10.04@2x.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/LakojjjzZNf6ogjOVwKE/1fe9371a-a870-49a1-b4bb-b923c2fa4fe6.png)
2024-04-01 07:41:20 +00:00
CatsJuice
af2158cb0c feat(core): compatible with multiple subscriptions (#6277) 2024-04-01 07:28:33 +00:00
pengx17
9381757982 fix(electron): wayland build (#6404)
![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/4a064576-fcda-4a7c-b646-48d55cd118c0.png)
2024-04-01 07:14:49 +00:00
pengx17
23cb309714 fix: onboarding app should also setup app storage config (#6427) 2024-04-01 07:04:39 +00:00
JimmFly
e33ab170d7 fix(core): edit button missing in shared page (#6398) 2024-04-01 06:50:21 +00:00
pengx17
b15ae21c45 fix(electron): optimize bundle size by removing unused dependencies (#6415)
Should greatly reduce the size of helper.js and could speed up the time on starting the client app.

fix https://github.com/toeverything/AFFiNE/issues/6312

Before:

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/681d6766-7d48-4574-b791-49e2c0ae6e1b.png)

After:
![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/ab13e624-7e03-407d-9538-3b9452694402.png)
2024-03-30 09:06:10 +00:00
CatsJuice
506efdf02c feat(core): make the synchronous animation trigger less frequently (#6412) 2024-03-30 08:11:05 +00:00
pengx17
f41d587d65 chore: upgrade cmdk to 1.0.0 (#6401)
Also include the command score into our own repo for some tweaks.

Might fix https://github.com/toeverything/AFFiNE/issues/6322
2024-03-30 08:00:21 +00:00
Brooooooklyn
822bbb54a4 chore(core): add more telemetry (#6402) 2024-03-30 07:46:23 +00:00
EYHN
1c648c2425 test(infra): add test for livedata with react (#6397) 2024-03-29 07:10:40 +00:00
Brooooooklyn
c1eb7b657a fix(templates): missing deps (#6396) 2024-03-29 06:56:52 +00:00
JimmFly
c517a71361 fix(core): unexpected response style of shared page (#6383) 2024-03-29 14:35:32 +08:00
JimmFly
2576a69eb6 fix(core): unexpected response style of shared page (#6383) 2024-03-29 05:29:54 +00:00
LongYinan
848ca3a0c4 Merge branch 'canary' into beta 2024-03-29 13:15:39 +08:00
pengx17
c7e10c2283 feat(core): fav item reordering (#6302) 2024-03-29 04:04:27 +00:00
pengx17
35af526eb2 feat: allow collections to be added to favorites (#6288) 2024-03-29 04:04:17 +00:00
pengx17
5490944d04 refactor(core): favorite adapter (#6285)
1. abstraction over favourites that supports different type of resources
2. sorting abstraction
2024-03-29 04:04:08 +00:00
LongYinan
296362ced1 ci: fix build frontend image step 2024-03-29 12:02:23 +08:00
CatsJuice
f1c70d5df0 fix(core): reload user session after signing out (#6381) 2024-03-29 03:47:18 +00:00
Brooooooklyn
c078f32a83 chore: bump Rust to 1.77 (#6316) 2024-03-29 03:45:59 +00:00
CatsJuice
605a5abee8 fix(core): user info should be updated when session reloaded (#6382) 2024-03-29 03:25:13 +00:00
pengx17
0abd24654d fix(electron): appimage wayland support (#6371)
Turns out the only solution for now is to patch the AppRun script in the AppImage using https://github.com/toeverything/electron-forge-maker-appimage/blob/master/scripts/patch-apprun.sh

fix #6340, fix #6252

Some references:
- https://github.com/audacity/audacity/issues/2498
- https://github.com/develar/app-builder/issues/104
2024-03-29 02:47:20 +00:00
CatsJuice
35715ab1d8 feat(core): sidebar local workspace enable cloud directly (#6366)
- Add a new hook `useEnableClould`, remove `<EnableAffineClouldModal />`
- Sidebar local workspace list item support enable AFFiNE Cloud
2024-03-29 02:08:09 +00:00
JimmFly
1a873ecf3c feat(core): add shortcut for multi select docs (#6318)
close TOV-701
In All Doc, `Shift + Click` has been added to activate the multiple selection state.
2024-03-29 01:40:23 +00:00
darkskygit
0ce6401a6f feat: update crawler rules (#6369) 2024-03-29 01:33:12 +00:00
CatsJuice
d14552b5af fix(core): sidebar workspace list can't scroll on mobile (#6370) 2024-03-29 01:25:13 +00:00
Brooooooklyn
7cd75824a4 fix(core): use ResizeObserver polyfill (#6360)
- Close https://github.com/toeverything/AFFiNE/issues/6359
2024-03-28 10:48:22 +00:00
Brooooooklyn
c2847e2082 chore(core): split sentry and setup into web/electron (#6357) 2024-03-28 10:24:25 +00:00
JimmFly
88d04e23e9 feat(core): update i18n resources (#6362)
close TOV-769

<img width="712" alt="image" src="https://github.com/toeverything/AFFiNE/assets/102217452/902a0771-e8df-46e2-8d7c-1f453a7099e6">
2024-03-28 10:11:25 +00:00
Jo Kroese
7526dea705 docs: correct typos and improve grammar in README (#6358) 2024-03-28 17:14:44 +08:00
fundon
ef354f1643 fix(core): prevent frequent sign-in (#6339) 2024-03-28 07:35:32 +00:00
CatsJuice
8ed38d7c0d fix(component): uppercase avatar letter fallback (#6354) 2024-03-28 07:24:11 +00:00
CatsJuice
fbe7a346c6 fix(core): adjust sidebar workspace card syncing tooltip style (#6356) 2024-03-28 07:13:27 +00:00
forehalo
f69649c922 ci: separate image build to a standalone workflow (#6167) 2024-03-28 04:02:13 +00:00
pengx17
e53744b740 fix: should use fullscreen to control where to place macos window controls (#6351) 2024-03-28 03:40:46 +00:00
EYHN
4e7652f108 fix(infra): fix white screen issue (#6350) 2024-03-28 02:12:25 +00:00
darkskygit
e24b6e4ddc docs: update supported version (#6344) 2024-03-27 15:35:21 +00:00
EYHN
ba9dad95b4 fix(core): improve performance (#6345) 2024-03-27 14:01:54 +00:00
CatsJuice
710edd28db feat(core): open restore history confirm modal with hook (#6343) 2024-03-27 13:30:36 +00:00
CatsJuice
d412635f6b feat(component): new hook to open confirm modal (#6342)
new exports from `@affine/component`:
```ts
import { ConfirmModalProvider, useConfirmModal } from "@affine/component"
```

Open confirm modal with hook:

```ts
const Component = () => {
  const { openConfirmModal } = useConformModal();

  const open = () => {
    openConfirmModal({
      // props of ConfirmModal
      /**
       * will show loading state when confirm clicked, and close after onConfirm finished
       */
      onConfirm: async () => {
        await new Promise((r) => setTimeout(r, 2000));
      },
    });
  }
  return <Button onClick={open}>Open</Button>
}
```
2024-03-27 13:30:30 +00:00
LongYinan
44c6ee6274 Merge remote-tracking branch 'origin/canary' into beta 2024-03-27 17:08:47 +08:00
Brooooooklyn
39facba92e fix(core): opt out telemetry condition (#6341) 2024-03-27 08:37:15 +00:00
997 changed files with 85969 additions and 22198 deletions

View File

@@ -12,4 +12,4 @@ static
web-static
public
packages/frontend/i18n/src/i18n-generated.ts
packages/frontend/templates/edgeless-templates.gen.ts
packages/frontend/templates/*.gen.ts

View File

@@ -48,14 +48,12 @@ const allPackages = [
'packages/frontend/i18n',
'packages/frontend/native',
'packages/frontend/templates',
'packages/frontend/workspace-impl',
'packages/common/debug',
'packages/common/env',
'packages/common/infra',
'packages/common/theme',
'packages/common/y-indexeddb',
'tools/cli',
'tests/storybook',
];
/**
@@ -234,7 +232,7 @@ const config = {
},
},
...allPackages.map(pkg => ({
files: [`${pkg}/src/**/*.ts`, `${pkg}/src/**/*.tsx`],
files: [`${pkg}/src/**/*.ts`, `${pkg}/src/**/*.tsx`, `${pkg}/**/*.mjs`],
rules: {
'@typescript-eslint/no-restricted-imports': [
'error',

View File

@@ -13,8 +13,10 @@ const {
R2_ACCOUNT_ID,
R2_ACCESS_KEY_ID,
R2_SECRET_ACCESS_KEY,
ENABLE_CAPTCHA,
CAPTCHA_TURNSTILE_SECRET,
COPILOT_OPENAI_API_KEY,
COPILOT_FAL_API_KEY,
COPILOT_UNSPLASH_API_KEY,
MAILER_SENDER,
MAILER_USER,
MAILER_PASSWORD,
@@ -97,8 +99,12 @@ const createHelmCommand = ({ isDryRun }) => {
`--set graphql.replicaCount=${graphqlReplicaCount}`,
`--set-string graphql.image.tag="${imageTag}"`,
`--set graphql.app.host=${host}`,
`--set graphql.app.captcha.enabled=${ENABLE_CAPTCHA}`,
`--set graphql.app.captcha.enabled=true`,
`--set-string graphql.app.captcha.turnstile.secret="${CAPTCHA_TURNSTILE_SECRET}"`,
`--set graphql.app.copilot.enabled=true`,
`--set-string graphql.app.copilot.openai.key="${COPILOT_OPENAI_API_KEY}"`,
`--set-string graphql.app.copilot.fal.key="${COPILOT_FAL_API_KEY}"`,
`--set-string graphql.app.copilot.unsplash.key="${COPILOT_UNSPLASH_API_KEY}"`,
`--set graphql.app.objectStorage.r2.enabled=true`,
`--set-string graphql.app.objectStorage.r2.accountId="${R2_ACCOUNT_ID}"`,
`--set-string graphql.app.objectStorage.r2.accessKeyId="${R2_ACCESS_KEY_ID}"`,

View File

@@ -30,6 +30,9 @@ services:
- NODE_ENV=production
- AFFINE_ADMIN_EMAIL=${AFFINE_ADMIN_EMAIL}
- AFFINE_ADMIN_PASSWORD=${AFFINE_ADMIN_PASSWORD}
# Telemetry allows us to collect data on how you use the affine. This data will helps us improve the app and provide better features.
# Uncomment next line if you wish to quit telemetry.
# - TELEMETRY_ENABLE=false
redis:
image: redis
container_name: affine_redis

View File

@@ -0,0 +1,11 @@
{{- if .Values.app.copilot.enabled -}}
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.copilot.secretName }}"
type: Opaque
data:
openaiSecret: {{ .Values.app.copilot.openai.key | b64enc }}
falSecret: {{ .Values.app.copilot.fal.key | b64enc }}
unsplashSecret: {{ .Values.app.copilot.unsplash.key | b64enc }}
{{- end }}

View File

@@ -148,6 +148,23 @@ spec:
name: "{{ .Values.app.captcha.secretName }}"
key: turnstileSecret
{{ end }}
{{ if .Values.app.copilot.enabled }}
- name: COPILOT_OPENAI_API_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.app.copilot.secretName }}"
key: openaiSecret
- name: COPILOT_FAL_API_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.app.copilot.secretName }}"
key: falSecret
- name: COPILOT_UNSPLASH_API_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.app.copilot.secretName }}"
key: unsplashSecret
{{ end }}
{{ if .Values.app.oauth.google.enabled }}
- name: OAUTH_GOOGLE_ENABLED
value: "true"

View File

@@ -24,6 +24,11 @@ app:
secretName: captcha
turnstile:
secret: ''
copilot:
enable: false
secretName: copilot
openai:
key: ''
objectStorage:
r2:
enabled: false

View File

@@ -35,6 +35,8 @@ graphql:
service:
type: ClusterIP
port: 3000
annotations:
cloud.google.com/backend-config: '{"default": "affine-backendconfig"}'
sync:
service:

5
.github/labeler.yml vendored
View File

@@ -29,11 +29,6 @@ mod:plugin-cli:
- any-glob-to-any-file:
- 'tools/plugin-cli/**/*'
mod:workspace-impl:
- changed-files:
- any-glob-to-any-file:
- 'packages/frontend/workspace-impl/**/*'
mod:i18n:
- changed-files:
- any-glob-to-any-file:

View File

@@ -0,0 +1,25 @@
name: Build Selfhost Image
on:
workflow_dispatch:
inputs:
flavor:
description: 'Select distribution to build'
type: choice
default: canary
options:
- canary
- beta
- stable
permissions:
contents: 'write'
id-token: 'write'
packages: 'write'
jobs:
build-image:
name: Build Image
uses: ./.github/workflows/build-server-image.yml
with:
flavor: ${{ github.event.inputs.flavor }}

192
.github/workflows/build-server-image.yml vendored Normal file
View File

@@ -0,0 +1,192 @@
name: Build Images
on:
workflow_call:
inputs:
flavor:
type: string
required: true
env:
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
permissions:
contents: 'write'
id-token: 'write'
packages: 'write'
jobs:
build-server:
name: Build Server
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
extra-flags: workspaces focus @affine/server
- name: Build Server
run: yarn workspace @affine/server build
- name: Upload server dist
uses: actions/upload-artifact@v4
with:
name: server-dist
path: ./packages/backend/server/dist
if-no-files-found: error
build-web-selfhost:
name: Build @affine/web selfhost
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.flavor }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build Core
run: yarn nx build @affine/web --skip-nx-cache
env:
BUILD_TYPE: ${{ github.event.inputs.flavor }}
SHOULD_REPORT_TRACE: false
PUBLIC_PATH: '/'
SELF_HOSTED: true
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
- name: Download selfhost fonts
run: node ./scripts/download-blocksuite-fonts.mjs
- name: Upload web artifact
uses: actions/upload-artifact@v4
with:
name: selfhost-web
path: ./packages/frontend/web/dist
if-no-files-found: error
build-storage:
name: Build Storage - ${{ matrix.targets.name }}
runs-on: ubuntu-latest
strategy:
matrix:
targets:
- name: x86_64-unknown-linux-gnu
file: storage.node
- name: aarch64-unknown-linux-gnu
file: storage.arm64.node
- name: armv7-unknown-linux-gnueabihf
file: storage.armv7.node
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
extra-flags: workspaces focus @affine/storage
- name: Build Rust
uses: ./.github/actions/build-rust
with:
target: ${{ matrix.targets.name }}
package: '@affine/storage'
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
- name: Upload ${{ matrix.targets.file }}
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.targets.file }}
path: ./packages/backend/storage/storage.node
if-no-files-found: error
build-docker:
name: Build Docker
runs-on: ubuntu-latest
needs:
- build-server
- build-web-selfhost
- build-storage
steps:
- uses: actions/checkout@v4
- name: Download server dist
uses: actions/download-artifact@v4
with:
name: server-dist
path: ./packages/backend/server/dist
- name: Download storage.node
uses: actions/download-artifact@v4
with:
name: storage.node
path: ./packages/backend/server
- name: Download storage.node arm64
uses: actions/download-artifact@v4
with:
name: storage.arm64.node
path: ./packages/backend/storage
- name: Download storage.node arm64
uses: actions/download-artifact@v4
with:
name: storage.armv7.node
path: .
- name: move storage files
run: |
mv ./packages/backend/storage/storage.node ./packages/backend/server/storage.arm64.node
mv storage.node ./packages/backend/server/storage.armv7.node
- name: Setup env
run: |
echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
if [ -z "${{ inputs.flavor }}" ]
then
echo "RELEASE_FLAVOR=canary" >> "$GITHUB_ENV"
else
echo "RELEASE_FLAVOR=${{ inputs.flavor }}" >> "$GITHUB_ENV"
fi
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
logout: false
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
# setup node without cache configuration
# Prisma cache is not compatible with docker build cache
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
registry-url: https://npm.pkg.github.com
scope: '@toeverything'
- name: Download selfhost web artifact
uses: actions/download-artifact@v4
with:
name: selfhost-web
path: ./packages/frontend/web/dist
- name: Install Node.js dependencies
run: |
yarn config set --json supportedArchitectures.cpu '["x64", "arm64", "arm"]'
yarn config set --json supportedArchitectures.libc '["glibc"]'
yarn workspaces focus @affine/server --production
- name: Generate Prisma client
run: yarn workspace @affine/server prisma generate
- name: Build graphql Dockerfile
uses: docker/build-push-action@v5
with:
context: .
push: true
pull: true
platforms: linux/amd64,linux/arm64,linux/arm/v7
provenance: true
file: .github/deployment/node/Dockerfile
tags: ghcr.io/toeverything/affine-graphql:${{env.RELEASE_FLAVOR}}-${{ env.GIT_SHORT_HASH }},ghcr.io/toeverything/affine-graphql:${{env.RELEASE_FLAVOR}}

View File

@@ -280,8 +280,10 @@ jobs:
- name: Build Web
# always skip cache because its fast, and cache configuration is always changing
run: yarn nx build @affine/web --skip-nx-cache
env:
DISTRIBUTION: 'desktop'
- name: zip web
run: tar -czf dist.tar.gz --directory=packages/frontend/web/dist .
run: tar -czf dist.tar.gz --directory=packages/frontend/electron/renderer/dist .
- name: Upload web artifact
uses: actions/upload-artifact@v4
with:

View File

@@ -13,32 +13,20 @@ on:
- stable
- internal
env:
APP_NAME: affine
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
MIXPANEL_TOKEN: '389c0615a69b57cca7d3fa0a4824c930'
permissions:
contents: 'write'
id-token: 'write'
packages: 'write'
jobs:
build-server:
name: Build Server
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
extra-flags: workspaces focus @affine/server
- name: Build Server
run: yarn workspace @affine/server build
- name: Upload server dist
uses: actions/upload-artifact@v4
with:
name: server-dist
path: ./packages/backend/server/dist
if-no-files-found: error
build-server-image:
name: Build Server Image
uses: ./.github/workflows/build-server-image.yml
with:
flavor: ${{ github.event.inputs.flavor }}
build-web:
name: Build @affine/web
runs-on: ubuntu-latest
@@ -61,10 +49,11 @@ jobs:
TRACE_REPORT_ENDPOINT: ${{ secrets.TRACE_REPORT_ENDPOINT }}
CAPTCHA_SITE_KEY: ${{ secrets.CAPTCHA_SITE_KEY }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_PROJECT: 'affine-web'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
- name: Upload web artifact
uses: actions/upload-artifact@v4
with:
@@ -72,112 +61,18 @@ jobs:
path: ./packages/frontend/web/dist
if-no-files-found: error
build-web-selfhost:
name: Build @affine/web selfhost
build-frontend-image:
name: Build Frontend Image
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.flavor }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build Core
run: yarn nx build @affine/web --skip-nx-cache
env:
BUILD_TYPE: ${{ github.event.inputs.flavor }}
SHOULD_REPORT_TRACE: false
PUBLIC_PATH: '/'
SELF_HOSTED: true
- name: Download selfhost fonts
run: node ./scripts/download-blocksuite-fonts.mjs
- name: Upload web artifact
uses: actions/upload-artifact@v4
with:
name: selfhost-web
path: ./packages/frontend/web/dist
if-no-files-found: error
build-storage:
name: Build Storage - ${{ matrix.targets.name }}
runs-on: ubuntu-latest
strategy:
matrix:
targets:
- name: x86_64-unknown-linux-gnu
file: storage.node
- name: aarch64-unknown-linux-gnu
file: storage.arm64.node
- name: armv7-unknown-linux-gnueabihf
file: storage.armv7.node
steps:
- uses: actions/checkout@v4
- name: Setup Version
id: version
uses: ./.github/actions/setup-version
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
extra-flags: workspaces focus @affine/storage
- name: Build Rust
uses: ./.github/actions/build-rust
with:
target: ${{ matrix.targets.name }}
package: '@affine/storage'
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
- name: Upload ${{ matrix.targets.file }}
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.targets.file }}
path: ./packages/backend/storage/storage.node
if-no-files-found: error
build-docker:
name: Build Docker
runs-on: ubuntu-latest
permissions:
contents: 'write'
id-token: 'write'
packages: 'write'
needs:
- build-server
- build-web
- build-web-selfhost
- build-storage
steps:
- uses: actions/checkout@v4
- name: Download core artifact
- name: Download web artifact
uses: actions/download-artifact@v4
with:
name: web
path: ./packages/frontend/web/dist
- name: Download server dist
uses: actions/download-artifact@v4
with:
name: server-dist
path: ./packages/backend/server/dist
- name: Download storage.node
uses: actions/download-artifact@v4
with:
name: storage.node
path: ./packages/backend/server
- name: Download storage.node arm64
uses: actions/download-artifact@v4
with:
name: storage.arm64.node
path: ./packages/backend/storage
- name: Download storage.node arm64
uses: actions/download-artifact@v4
with:
name: storage.armv7.node
path: .
- name: move storage files
run: |
mv ./packages/backend/storage/storage.node ./packages/backend/server/storage.arm64.node
mv storage.node ./packages/backend/server/storage.armv7.node
- name: Setup env
run: |
echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
@@ -187,7 +82,6 @@ jobs:
else
echo "RELEASE_FLAVOR=${{ inputs.flavor }}" >> "$GITHUB_ENV"
fi
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
@@ -210,53 +104,13 @@ jobs:
file: .github/deployment/front/Dockerfile
tags: ghcr.io/toeverything/affine-front:${{env.RELEASE_FLAVOR}}-${{ env.GIT_SHORT_HASH }},ghcr.io/toeverything/affine-front:${{env.RELEASE_FLAVOR}}
# setup node without cache configuration
# Prisma cache is not compatible with docker build cache
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
registry-url: https://npm.pkg.github.com
scope: '@toeverything'
- name: Remove web dist
run: rm -rf ./packages/frontend/web/dist
- name: Download selfhost web artifact
uses: actions/download-artifact@v4
with:
name: selfhost-web
path: ./packages/frontend/web/dist
- name: Install Node.js dependencies
run: |
yarn config set --json supportedArchitectures.cpu '["x64", "arm64", "arm"]'
yarn config set --json supportedArchitectures.libc '["glibc"]'
yarn workspaces focus @affine/server --production
- name: Generate Prisma client
run: yarn workspace @affine/server prisma generate
- name: Build graphql Dockerfile
uses: docker/build-push-action@v5
with:
context: .
push: true
pull: true
platforms: linux/amd64,linux/arm64,linux/arm/v7
provenance: true
file: .github/deployment/node/Dockerfile
tags: ghcr.io/toeverything/affine-graphql:${{env.RELEASE_FLAVOR}}-${{ env.GIT_SHORT_HASH }},ghcr.io/toeverything/affine-graphql:${{env.RELEASE_FLAVOR}}
deploy:
name: Deploy to cluster
if: ${{ github.event_name == 'workflow_dispatch' }}
environment: ${{ github.event.inputs.flavor }}
permissions:
contents: 'write'
id-token: 'write'
needs:
- build-docker
- build-frontend-image
- build-server-image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -279,8 +133,10 @@ jobs:
R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
ENABLE_CAPTCHA: true
CAPTCHA_TURNSTILE_SECRET: ${{ secrets.CAPTCHA_TURNSTILE_SECRET }}
COPILOT_OPENAI_API_KEY: ${{ secrets.COPILOT_OPENAI_API_KEY }}
COPILOT_FAL_API_KEY: ${{ secrets.COPILOT_FAL_API_KEY }}
COPILOT_UNSPLASH_API_KEY: ${{ secrets.COPILOT_UNSPLASH_API_KEY }}
MAILER_SENDER: ${{ secrets.OAUTH_EMAIL_SENDER }}
MAILER_USER: ${{ secrets.OAUTH_EMAIL_LOGIN }}
MAILER_PASSWORD: ${{ secrets.OAUTH_EMAIL_PASSWORD }}

View File

@@ -25,4 +25,7 @@ jobs:
node-version-file: '.nvmrc'
- name: Install dependencies
run: yarn workspaces focus @affine/commitlint-config
- run: echo "${{ github.event.pull_request.title }}" | yarn workspace @affine/commitlint-config commitlint -g ./.commitlintrc.json
- name: Check PR title
env:
TITLE: ${{ github.event.pull_request.title }}
run: echo "$TITLE" | yarn workspace @affine/commitlint-config commitlint -g ./.commitlintrc.json

View File

@@ -1,51 +0,0 @@
name: Publish Storybook
env:
NODE_OPTIONS: --max-old-space-size=4096
on:
workflow_dispatch:
push:
branches:
- canary
pull_request:
branches:
- canary
paths-ignore:
- README.md
- .github/**
- packages/backend/server
- packages/frontend/electron
- '!.github/workflows/publish-storybook.yml'
jobs:
publish-storybook:
name: Publish Storybook
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
# This is required to fetch all commits for chromatic
fetch-depth: 0
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
- uses: chromaui/action-next@v1
with:
workingDir: tests/storybook
buildScriptName: build
exitOnceUploaded: true
onlyChanged: false
diagnostics: true
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
NODE_OPTIONS: ${{ env.NODE_OPTIONS }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: chromatic-build-artifacts-${{ github.run_id }}
path: |
chromatic-diagnostics.json
**/build-storybook.log

View File

@@ -1,51 +0,0 @@
name: Publish UI Storybook
env:
NODE_OPTIONS: --max-old-space-size=4096
on:
workflow_dispatch:
push:
branches:
- canary
pull_request:
branches:
- canary
paths-ignore:
- README.md
- .github/**
- packages/backend/server
- packages/frontend/electron
- '!.github/workflows/publish-storybook.yml'
jobs:
publish-ui-storybook:
name: Publish UI Storybook
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.merge_commit_sha }}
# This is required to fetch all commits for chromatic
fetch-depth: 0
- name: Setup Node.js
uses: ./.github/actions/setup-node
with:
electron-install: false
- uses: chromaui/action-next@v1
with:
workingDir: packages/frontend/component
buildScriptName: build:storybook
exitOnceUploaded: true
onlyChanged: false
diagnostics: true
env:
CHROMATIC_PROJECT_TOKEN: ${{ secrets.CHROMATIC_UI_PROJECT_TOKEN }}
NODE_OPTIONS: ${{ env.NODE_OPTIONS }}
- uses: actions/upload-artifact@v4
if: always()
with:
name: chromatic-build-artifacts-${{ github.run_id }}
path: |
chromatic-diagnostics.json
**/build-storybook.log

View File

@@ -33,7 +33,6 @@ env:
DEBUG: napi:*
APP_NAME: affine
MACOSX_DEPLOYMENT_TARGET: '10.13'
MIXPANEL_TOKEN: '389c0615a69b57cca7d3fa0a4824c930'
jobs:
before-make:
@@ -54,12 +53,13 @@ jobs:
run: yarn workspace @affine/electron generate-assets
env:
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
SENTRY_PROJECT: 'affine'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
RELEASE_VERSION: ${{ steps.version.outputs.APP_VERSION }}
SKIP_PLUGIN_BUILD: 'true'
SKIP_NX_CACHE: 'true'
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
- name: Upload web artifact
uses: actions/upload-artifact@v4
@@ -90,6 +90,11 @@ jobs:
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
SKIP_GENERATE_ASSETS: 1
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
@@ -124,6 +129,12 @@ jobs:
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
- name: Install fuse on Linux (for patching AppImage)
if: ${{ matrix.spec.platform == 'linux' }}
run: |
sudo add-apt-repository universe
sudo apt install libfuse2 -y
- name: make
run: yarn workspace @affine/electron make --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
env:
@@ -169,6 +180,11 @@ jobs:
FILES_TO_BE_SIGNED: ${{ steps.get_files_to_be_signed.outputs.FILES_TO_BE_SIGNED }}
env:
SKIP_GENERATE_ASSETS: 1
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: 'affine'
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: Setup Version
@@ -244,6 +260,10 @@ jobs:
- name: Setup Node.js
timeout-minutes: 10
uses: ./.github/actions/setup-node
with:
extra-flags: workspaces focus @affine/electron @affine/monorepo
hard-link-nm: false
nmHoistingLimits: workspaces
- name: Download and overwrite packaged artifacts
uses: actions/download-artifact@v4
with:
@@ -255,6 +275,9 @@ jobs:
- name: Make squirrel.windows installer
run: yarn workspace @affine/electron make-squirrel --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
- name: Make nsis.windows installer
run: yarn workspace @affine/electron make-nsis --platform=${{ matrix.spec.platform }} --arch=${{ matrix.spec.arch }}
- name: Zip artifacts for faster upload
run: Compress-Archive -CompressionLevel Fastest -Path packages/frontend/electron/out/${{ env.BUILD_TYPE }}/make/* -DestinationPath archive.zip
@@ -302,7 +325,7 @@ jobs:
mkdir -p builds
mv packages/frontend/electron/out/*/make/zip/win32/x64/AFFiNE*-win32-x64-*.zip ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-windows-x64.zip
mv packages/frontend/electron/out/*/make/squirrel.windows/x64/*.exe ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-windows-x64.exe
mv packages/frontend/electron/out/*/make/squirrel.windows/x64/*.msi ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-windows-x64.msi
mv packages/frontend/electron/out/*/make/nsis.windows/x64/*.exe ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-windows-x64.nsis.exe
- name: Upload Artifact
uses: actions/upload-artifact@v4

2
.nvmrc
View File

@@ -1 +1 @@
20
20.12.1

View File

@@ -16,8 +16,7 @@ packages/frontend/i18n/src/i18n-generated.ts
packages/frontend/graphql/src/graphql/index.ts
tests/affine-legacy/**/static
.yarnrc.yml
packages/frontend/templates/edgeless-templates.gen.ts
packages/frontend/templates/templates.gen.ts
packages/frontend/templates/*.gen.ts
packages/frontend/templates/onboarding
# auto-generated by NAPI-RS

File diff suppressed because one or more lines are too long

749
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@
</a>
<br/>
<p align="center">
A privacy-focussed, local-first, open-source, and ready-to-use alternative for Notion & Miro. <br />
A privacy-focused, local-first, open-source, and ready-to-use alternative for Notion & Miro. <br />
One hyper-fused platform for wildly creative minds.
</p>
@@ -49,7 +49,7 @@
## Getting started & staying tuned with us.
Star us, and you will receive all releases notifications from GitHub without any delay!
Star us, and you will receive all release notifications from GitHub without any delay!
<img src="https://user-images.githubusercontent.com/79301703/230891830-0110681e-8c7e-483b-b6d9-9e42b291b9ef.gif" style="width: 100%"/>
@@ -65,7 +65,7 @@ AFFiNE is an open-source, all-in-one workspace and an operating system for all t
**Multimodal AI partner ready to kick in any work**
- Write up professional work report? Turn an outline into expressive and presentable slides? Summary an article into a well-structured mindmap? Sorting your job plan and backlog for tasks? Or....draw and code prototype apps and web pages directly all with one prompt? With you, AFFiNE AI pushes your creativity to the edge of your imagination.
- Write up professional work report? Turn an outline into expressive and presentable slides? Summary an article into a well-structured mindmap? Sorting your job plan and backlog for tasks? Or... draw and code prototype apps and web pages directly all with one prompt? With you, AFFiNE AI pushes your creativity to the edge of your imagination.
**Local-first & Real-time collaborative**
@@ -73,7 +73,7 @@ AFFiNE is an open-source, all-in-one workspace and an operating system for all t
**Self-host & Shape your own AFFiNE**
- You have the freedom to manage, self-host, fork and build your own AFFiNE. Plugin community and third-party blocks is coming soon. More tractions on [Blocksuite](block-suite.com). Check there to learn how to [self-host AFFiNE](https://docs.affine.pro/docs/self-host-affine-).
- You have the freedom to manage, self-host, fork and build your own AFFiNE. Plugin community and third-party blocks are coming soon. More tractions on [Blocksuite](https://blocksuite.io). Check there to learn how to [self-host AFFiNE](https://docs.affine.pro/docs/self-host-affine).
## Acknowledgement
@@ -83,7 +83,7 @@ AFFiNE is an open-source, all-in-one workspace and an operating system for all t
- Trello with their Kanban
- Airtable & Miro with their no-code programable datasheets
- Miro & Whimiscal with their edgeless visual whiteboard
- Remnote & Capacities with their object-based tag system
- Remote & Capacities with their object-based tag system
There is a large overlap of their atomic “building blocks” between these apps. They are not open source, nor do they have a plugin system like Vscode for contributors to customize. We want to have something that contains all the features we love and also goes one step even further.
@@ -104,7 +104,7 @@ For **bug reports**, **feature requests** and other **suggestions** you can also
For **translation** and **language support** you can visit our [i18n General Space](https://community.affine.pro/c/i18n-general).
Looking for **others ways to contribute** and wondering where to start? Check out the [AFFiNE Ambassador program](https://community.affine.pro/c/start-here/affine-ambassador), we work closely with passionate community members and provide them with a wide-range of support and resources.
Looking for **other ways to contribute** and wondering where to start? Check out the [AFFiNE Ambassador program](https://community.affine.pro/c/start-here/affine-ambassador), we work closely with passionate community members and provide them with a wide range of support and resources.
If you have questions, you are welcome to contact us. One of the best places to get more info and learn more is in the [AFFiNE Community](https://community.affine.pro) where you can engage with other like-minded individuals.
@@ -112,7 +112,7 @@ If you have questions, you are welcome to contact us. One of the best places to
| Name | | |
| -------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| [@affine/component](packages/frontend/component) | AFFiNE Component Resources | [![](https://img.shields.io/codecov/c/github/toeverything/affine?style=flat-square)](https://affine-storybook.vercel.app/) |
| [@affine/component](packages/frontend/component) | AFFiNE Component Resources | ![](https://img.shields.io/codecov/c/github/toeverything/affine?style=flat-square) |
| [@toeverything/y-indexeddb](packages/common/y-indexeddb) | IndexedDB database adapter for Yjs | [![](https://img.shields.io/npm/dm/@toeverything/y-indexeddb?style=flat-square&color=eee)](https://www.npmjs.com/package/@toeverything/y-indexeddb) |
| [@toeverything/theme](packages/common/theme) | AFFiNE theme | [![](https://img.shields.io/npm/dm/@toeverything/theme?style=flat-square&color=eee)](https://www.npmjs.com/package/@toeverything/theme) |
@@ -143,15 +143,15 @@ We would like to express our gratitude to all the individuals who have already c
## Self-Host
Begin with Docker to deploy your own feature-rich, unrestricted version of AFFiNE. Our team is diligently updating to the latest version. For more information on how to self-host AFFiNE, please refer to our [documentation](https://docs.affine.pro/docs/self-host-affine-).
Begin with Docker to deploy your own feature-rich, unrestricted version of AFFiNE. Our team is diligently updating to the latest version. For more information on how to self-host AFFiNE, please refer to our [documentation](https://docs.affine.pro/docs/self-host-affine).
## Hiring
Some amazing companies including AFFiNE are looking for developers! Are you interesgo to iour discord channel AFFiNE and/or its partners? Check out some of the latest [jobs available].
Some amazing companies, including AFFiNE, are looking for developers! Are you interested in joining AFFiNE or its partners? Check out our Discord channel for some of the latest jobs available.
## Feature Request
For feature request, please see [community.affine.pro](https://community.affine.pro/c/feature-requests/).
For feature requests, please see [community.affine.pro](https://community.affine.pro/c/feature-requests/).
## Building
@@ -186,7 +186,7 @@ See [LICENSE] for details.
[jobs available]: ./docs/jobs.md
[latest packages]: https://github.com/toeverything/AFFiNE/pkgs/container/affine-self-hosted
[contributor license agreement]: https://github.com/toeverything/affine/edit/canary/.github/CLA.md
[rust-version-icon]: https://img.shields.io/badge/Rust-1.76.0-dea584
[rust-version-icon]: https://img.shields.io/badge/Rust-1.77.0-dea584
[stars-icon]: https://img.shields.io/github/stars/toeverything/AFFiNE.svg?style=flat&logo=github&colorB=red&label=stars
[codecov]: https://codecov.io/gh/toeverything/affine/branch/canary/graphs/badge.svg?branch=canary
[node-version-icon]: https://img.shields.io/badge/node-%3E=18.16.1-success

View File

@@ -6,8 +6,8 @@ We recommend users to always use the latest major version. Security updates will
| Version | Supported |
| --------------- | ------------------ |
| 0.12.x (stable) | :white_check_mark: |
| < 0.12.x | :x: |
| 0.13.x (stable) | :white_check_mark: |
| < 0.13.x | :x: |
## Reporting a Vulnerability

View File

@@ -29,13 +29,6 @@ It includes the global constants, browser and system check.
This package should be imported at the very beginning of the entry point.
### `@affine/workspace-impl`
Current we have two workspace plugin:
- `local` for local workspace, which is the default workspace type.
- `affine` for cloud workspace, which is the workspace type for AFFiNE Cloud with OctoBase backend.
#### Design principles
- Each workspace plugin has its state and is isolated from other workspace plugins.
@@ -61,12 +54,6 @@ yarn dev
See [building desktop client app](../building-desktop-client-app.md).
### `@affine/storybook`
```shell
yarn workspace @affine/storybook storybook
```
## What's next?
- [Behind the code](./behind-the-code.md)

View File

@@ -9,7 +9,7 @@
"devDependencies": {
"nodemon": "^3.1.0",
"serve": "^14.2.1",
"typedoc": "^0.25.8"
"typedoc": "^0.25.13"
},
"nodemonConfig": {
"watch": [

10
oxlint.json Normal file
View File

@@ -0,0 +1,10 @@
{
"rules": {
"import/no-cycle": [
"error",
{
"ignoreTypes": true
}
]
}
}

View File

@@ -22,15 +22,13 @@
"build": "yarn nx build @affine/web",
"build:electron": "yarn nx build @affine/electron",
"build:storage": "yarn nx run-many -t build -p @affine/storage",
"build:storybook": "yarn nx build @affine/storybook",
"start:web-static": "yarn workspace @affine/web static-server",
"start:storybook": "yarn exec serve tests/storybook/storybook-static -l 6006",
"serve:test-static": "yarn exec serve tests/fixtures --cors -p 8081",
"lint:eslint": "cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" eslint . --ext .js,mjs,.ts,.tsx --cache",
"lint:eslint:fix": "yarn lint:eslint --fix",
"lint:prettier": "prettier --ignore-unknown --cache --check .",
"lint:prettier:fix": "prettier --ignore-unknown --cache --write .",
"lint:ox": "oxlint --import-plugin --deny-warnings -D correctness -D nursery -D prefer-array-some -D no-useless-promise-resolve-reject -D perf -A no-undef -A consistent-type-exports -A default -A named -A ban-ts-comment -A export -A no-unresolved -A no-default-export -A no-duplicates -A no-side-effects-in-initialization -A no-named-as-default -A getter-return",
"lint:ox": "oxlint -c oxlint.json --import-plugin --deny-warnings -D correctness -D nursery -D prefer-array-some -D no-useless-promise-resolve-reject -D perf -A no-undef -A consistent-type-exports -A default -A named -A ban-ts-comment -A export -A no-unresolved -A no-default-export -A no-duplicates -A no-side-effects-in-initialization -A no-named-as-default -A getter-return",
"lint": "yarn lint:eslint && yarn lint:prettier",
"lint:fix": "yarn lint:eslint:fix && yarn lint:prettier:fix",
"test": "vitest --run",
@@ -56,64 +54,63 @@
"devDependencies": {
"@affine-test/kit": "workspace:*",
"@affine/cli": "workspace:*",
"@commitlint/cli": "^19.0.0",
"@commitlint/config-conventional": "^19.0.0",
"@commitlint/cli": "^19.2.1",
"@commitlint/config-conventional": "^19.1.0",
"@faker-js/faker": "^8.4.1",
"@istanbuljs/schema": "^0.1.3",
"@magic-works/i18n-codegen": "^0.5.0",
"@nx/vite": "18.1.2",
"@playwright/test": "^1.41.2",
"@nx/vite": "18.2.4",
"@playwright/test": "^1.43.0",
"@taplo/cli": "^0.7.0",
"@testing-library/react": "^14.2.1",
"@testing-library/react": "^15.0.0",
"@toeverything/infra": "workspace:*",
"@types/affine__env": "workspace:*",
"@types/eslint": "^8.56.3",
"@types/node": "^20.11.20",
"@typescript-eslint/eslint-plugin": "^7.0.2",
"@typescript-eslint/parser": "^7.0.2",
"@vanilla-extract/vite-plugin": "^4.0.4",
"@vanilla-extract/webpack-plugin": "^2.3.6",
"@types/eslint": "^8.56.7",
"@types/node": "^20.12.7",
"@typescript-eslint/eslint-plugin": "^7.6.0",
"@typescript-eslint/parser": "^7.6.0",
"@vanilla-extract/vite-plugin": "^4.0.7",
"@vanilla-extract/webpack-plugin": "^2.3.7",
"@vitejs/plugin-react-swc": "^3.6.0",
"@vitest/coverage-istanbul": "1.4.0",
"@vitest/ui": "1.4.0",
"cross-env": "^7.0.3",
"electron": "^29.0.1",
"eslint": "^8.56.0",
"electron": "^30.0.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import-x": "^0.4.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-import-x": "^0.5.0",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-rxjs": "^5.0.3",
"eslint-plugin-simple-import-sort": "^12.0.0",
"eslint-plugin-sonarjs": "^0.24.0",
"eslint-plugin-unicorn": "^51.0.1",
"eslint-plugin-sonarjs": "^0.25.1",
"eslint-plugin-unicorn": "^52.0.0",
"eslint-plugin-unused-imports": "^3.1.0",
"eslint-plugin-vue": "^9.22.0",
"eslint-plugin-vue": "^9.24.1",
"fake-indexeddb": "5.0.2",
"happy-dom": "^14.0.0",
"happy-dom": "^14.7.1",
"husky": "^9.0.11",
"lint-staged": "^15.2.2",
"msw": "^2.2.1",
"nanoid": "^5.0.6",
"nx": "^18.0.4",
"msw": "^2.2.13",
"nanoid": "^5.0.7",
"nx": "^18.2.4",
"nyc": "^15.1.0",
"oxlint": "0.2.14",
"oxlint": "0.3.1",
"prettier": "^3.2.5",
"semver": "^7.6.0",
"serve": "^14.2.1",
"string-width": "^7.1.0",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"vite": "^5.1.4",
"typescript": "^5.4.5",
"vite": "^5.2.8",
"vite-plugin-istanbul": "^6.0.0",
"vite-plugin-static-copy": "^1.0.1",
"vite-plugin-static-copy": "^1.0.2",
"vitest": "1.4.0",
"vitest-fetch-mock": "^0.2.2",
"vitest-mock-extended": "^1.3.1"
},
"packageManager": "yarn@4.1.1",
"resolutions": {
"vite": "^5.0.6",
"array-buffer-byte-length": "npm:@nolyfill/array-buffer-byte-length@latest",
"array-includes": "npm:@nolyfill/array-includes@latest",
"array.prototype.flat": "npm:@nolyfill/array.prototype.flat@latest",
@@ -169,7 +166,7 @@
"unbox-primitive": "npm:@nolyfill/unbox-primitive@latest",
"which-boxed-primitive": "npm:@nolyfill/which-boxed-primitive@latest",
"which-typed-array": "npm:@nolyfill/which-typed-array@latest",
"@reforged/maker-appimage/@electron-forge/maker-base": "7.3.0",
"@reforged/maker-appimage/@electron-forge/maker-base": "7.3.1",
"macos-alias": "npm:@napi-rs/macos-alias@0.0.4",
"fs-xattr": "npm:@napi-rs/xattr@latest",
"@radix-ui/react-dialog": "npm:@radix-ui/react-dialog@latest"

View File

@@ -0,0 +1,16 @@
-- CreateEnum
CREATE TYPE "AiPromptRole" AS ENUM ('system', 'assistant', 'user');
-- CreateTable
CREATE TABLE "ai_prompts" (
"id" VARCHAR NOT NULL,
"name" VARCHAR(20) NOT NULL,
"idx" INTEGER NOT NULL,
"role" "AiPromptRole" NOT NULL,
"content" TEXT NOT NULL,
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ai_prompts_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "ai_prompts_name_idx_key" ON "ai_prompts"("name", "idx");

View File

@@ -0,0 +1,25 @@
-- CreateTable
CREATE TABLE "ai_sessions" (
"id" VARCHAR(36) NOT NULL,
"user_id" VARCHAR NOT NULL,
"workspace_id" VARCHAR NOT NULL,
"doc_id" VARCHAR NOT NULL,
"prompt_name" VARCHAR NOT NULL,
"action" BOOLEAN NOT NULL,
"flavor" VARCHAR NOT NULL,
"model" VARCHAR NOT NULL,
"messages" JSON NOT NULL,
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(6) NOT NULL,
CONSTRAINT "ai_sessions_pkey" PRIMARY KEY ("id")
);
-- AddForeignKey
ALTER TABLE "ai_sessions" ADD CONSTRAINT "ai_sessions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ai_sessions" ADD CONSTRAINT "ai_sessions_workspace_id_fkey" FOREIGN KEY ("workspace_id") REFERENCES "workspaces"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ai_sessions" ADD CONSTRAINT "ai_sessions_doc_id_workspace_id_fkey" FOREIGN KEY ("doc_id", "workspace_id") REFERENCES "snapshots"("guid", "workspace_id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,87 @@
/*
Warnings:
- You are about to drop the `ai_prompts` table. If the table is not empty, all the data it contains will be lost.
- You are about to drop the `ai_sessions` table. If the table is not empty, all the data it contains will be lost.
*/
-- DropForeignKey
ALTER TABLE "ai_sessions" DROP CONSTRAINT "ai_sessions_doc_id_workspace_id_fkey";
-- DropForeignKey
ALTER TABLE "ai_sessions" DROP CONSTRAINT "ai_sessions_user_id_fkey";
-- DropForeignKey
ALTER TABLE "ai_sessions" DROP CONSTRAINT "ai_sessions_workspace_id_fkey";
-- DropTable
DROP TABLE "ai_prompts";
-- DropTable
DROP TABLE "ai_sessions";
-- CreateTable
CREATE TABLE "ai_prompts_messages" (
"prompt_id" INTEGER NOT NULL,
"idx" INTEGER NOT NULL,
"role" "AiPromptRole" NOT NULL,
"content" TEXT NOT NULL,
"attachments" JSON,
"params" JSON,
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- CreateTable
CREATE TABLE "ai_prompts_metadata" (
"id" SERIAL NOT NULL,
"name" VARCHAR(32) NOT NULL,
"action" VARCHAR,
"model" VARCHAR,
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ai_prompts_metadata_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ai_sessions_messages" (
"id" VARCHAR(36) NOT NULL,
"session_id" VARCHAR(36) NOT NULL,
"role" "AiPromptRole" NOT NULL,
"content" TEXT NOT NULL,
"attachments" JSON,
"params" JSON,
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(6) NOT NULL,
CONSTRAINT "ai_sessions_messages_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ai_sessions_metadata" (
"id" VARCHAR(36) NOT NULL,
"user_id" VARCHAR(36) NOT NULL,
"workspace_id" VARCHAR(36) NOT NULL,
"doc_id" VARCHAR(36) NOT NULL,
"prompt_name" VARCHAR(32) NOT NULL,
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "ai_sessions_metadata_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "ai_prompts_messages_prompt_id_idx_key" ON "ai_prompts_messages"("prompt_id", "idx");
-- CreateIndex
CREATE UNIQUE INDEX "ai_prompts_metadata_name_key" ON "ai_prompts_metadata"("name");
-- AddForeignKey
ALTER TABLE "ai_prompts_messages" ADD CONSTRAINT "ai_prompts_messages_prompt_id_fkey" FOREIGN KEY ("prompt_id") REFERENCES "ai_prompts_metadata"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ai_sessions_messages" ADD CONSTRAINT "ai_sessions_messages_session_id_fkey" FOREIGN KEY ("session_id") REFERENCES "ai_sessions_metadata"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ai_sessions_metadata" ADD CONSTRAINT "ai_sessions_metadata_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ai_sessions_metadata" ADD CONSTRAINT "ai_sessions_metadata_prompt_name_fkey" FOREIGN KEY ("prompt_name") REFERENCES "ai_prompts_metadata"("name") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -18,104 +18,107 @@
"predeploy": "yarn prisma migrate deploy && node --import ./scripts/register.js ./dist/data/index.js run"
},
"dependencies": {
"@apollo/server": "^4.10.0",
"@auth/prisma-adapter": "^1.4.0",
"@aws-sdk/client-s3": "^3.536.0",
"@apollo/server": "^4.10.2",
"@aws-sdk/client-s3": "^3.552.0",
"@google-cloud/opentelemetry-cloud-monitoring-exporter": "^0.17.0",
"@google-cloud/opentelemetry-cloud-trace-exporter": "^2.1.0",
"@google-cloud/opentelemetry-resource-util": "^2.1.0",
"@keyv/redis": "^2.8.4",
"@nestjs/apollo": "^12.1.0",
"@nestjs/common": "^10.3.3",
"@nestjs/core": "^10.3.3",
"@nestjs/common": "^10.3.7",
"@nestjs/core": "^10.3.7",
"@nestjs/event-emitter": "^2.0.4",
"@nestjs/graphql": "^12.1.1",
"@nestjs/platform-express": "^10.3.3",
"@nestjs/platform-socket.io": "^10.3.3",
"@nestjs/platform-express": "^10.3.7",
"@nestjs/platform-socket.io": "^10.3.7",
"@nestjs/schedule": "^4.0.1",
"@nestjs/serve-static": "^4.0.1",
"@nestjs/throttler": "^5.0.1",
"@nestjs/websockets": "^10.3.3",
"@node-rs/argon2": "^1.7.2",
"@node-rs/crc32": "^1.9.2",
"@node-rs/jsonwebtoken": "^0.5.0",
"@opentelemetry/api": "^1.7.0",
"@opentelemetry/core": "^1.21.0",
"@opentelemetry/exporter-prometheus": "^0.49.0",
"@opentelemetry/exporter-zipkin": "^1.21.0",
"@nestjs/serve-static": "^4.0.2",
"@nestjs/throttler": "5.0.1",
"@nestjs/websockets": "^10.3.7",
"@node-rs/argon2": "^1.8.0",
"@node-rs/crc32": "^1.10.0",
"@node-rs/jsonwebtoken": "^0.5.2",
"@opentelemetry/api": "^1.8.0",
"@opentelemetry/core": "^1.23.0",
"@opentelemetry/exporter-prometheus": "^0.50.0",
"@opentelemetry/exporter-zipkin": "^1.23.0",
"@opentelemetry/host-metrics": "^0.35.0",
"@opentelemetry/instrumentation": "^0.49.0",
"@opentelemetry/instrumentation-graphql": "^0.38.0",
"@opentelemetry/instrumentation-http": "^0.49.0",
"@opentelemetry/instrumentation-ioredis": "^0.38.0",
"@opentelemetry/instrumentation-nestjs-core": "^0.35.0",
"@opentelemetry/instrumentation-socket.io": "^0.37.0",
"@opentelemetry/resources": "^1.21.0",
"@opentelemetry/sdk-metrics": "^1.21.0",
"@opentelemetry/sdk-node": "^0.49.0",
"@opentelemetry/sdk-trace-node": "^1.21.0",
"@opentelemetry/semantic-conventions": "^1.21.0",
"@prisma/client": "^5.10.2",
"@prisma/instrumentation": "^5.10.2",
"@socket.io/redis-adapter": "^8.2.1",
"@opentelemetry/instrumentation": "^0.50.0",
"@opentelemetry/instrumentation-graphql": "^0.39.0",
"@opentelemetry/instrumentation-http": "^0.50.0",
"@opentelemetry/instrumentation-ioredis": "^0.39.0",
"@opentelemetry/instrumentation-nestjs-core": "^0.36.0",
"@opentelemetry/instrumentation-socket.io": "^0.38.0",
"@opentelemetry/resources": "^1.23.0",
"@opentelemetry/sdk-metrics": "^1.23.0",
"@opentelemetry/sdk-node": "^0.50.0",
"@opentelemetry/sdk-trace-node": "^1.23.0",
"@opentelemetry/semantic-conventions": "^1.23.0",
"@prisma/client": "^5.12.1",
"@prisma/instrumentation": "^5.12.1",
"@socket.io/redis-adapter": "^8.3.0",
"cookie-parser": "^1.4.6",
"dotenv": "^16.4.5",
"dotenv-cli": "^7.3.0",
"express": "^4.18.2",
"dotenv-cli": "^7.4.1",
"express": "^4.19.2",
"file-type": "^19.0.0",
"get-stream": "^9.0.0",
"get-stream": "^9.0.1",
"graphql": "^16.8.1",
"graphql-scalars": "^1.22.4",
"graphql-scalars": "^1.23.0",
"graphql-type-json": "^0.3.2",
"graphql-upload": "^16.0.2",
"ioredis": "^5.3.2",
"keyv": "^4.5.4",
"lodash-es": "^4.17.21",
"mixpanel": "^0.18.0",
"nanoid": "^5.0.6",
"mustache": "^4.2.0",
"nanoid": "^5.0.7",
"nest-commander": "^3.12.5",
"nestjs-throttler-storage-redis": "^0.4.1",
"nodemailer": "^6.9.10",
"nodemailer": "^6.9.13",
"on-headers": "^1.0.2",
"openai": "^4.33.0",
"parse-duration": "^1.1.0",
"pretty-time": "^1.1.0",
"prisma": "^5.10.2",
"prom-client": "^15.1.0",
"reflect-metadata": "^0.2.1",
"prisma": "^5.12.1",
"prom-client": "^15.1.1",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"semver": "^7.6.0",
"socket.io": "^4.7.4",
"stripe": "^14.18.0",
"socket.io": "^4.7.5",
"stripe": "^15.0.0",
"tiktoken": "^1.0.13",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"typescript": "^5.4.5",
"ws": "^8.16.0",
"yjs": "^13.6.12",
"yjs": "^13.6.14",
"zod": "^3.22.4"
},
"devDependencies": {
"@affine-test/kit": "workspace:*",
"@affine/storage": "workspace:*",
"@napi-rs/image": "^1.9.1",
"@nestjs/testing": "^10.3.3",
"@types/cookie-parser": "^1.4.6",
"@nestjs/testing": "^10.3.7",
"@types/cookie-parser": "^1.4.7",
"@types/engine.io": "^3.1.10",
"@types/express": "^4.17.21",
"@types/graphql-upload": "^16.0.7",
"@types/keyv": "^4.2.0",
"@types/lodash-es": "^4.17.12",
"@types/mixpanel": "^2.14.8",
"@types/node": "^20.11.20",
"@types/mustache": "^4.2.5",
"@types/node": "^20.12.7",
"@types/nodemailer": "^6.4.14",
"@types/on-headers": "^1.0.3",
"@types/pretty-time": "^1.1.5",
"@types/sinon": "^17.0.3",
"@types/supertest": "^6.0.2",
"@types/ws": "^8.5.10",
"ava": "^6.1.1",
"ava": "^6.1.2",
"c8": "^9.1.0",
"nodemon": "^3.1.0",
"sinon": "^17.0.1",
"supertest": "^6.3.4"
"supertest": "^7.0.0"
},
"ava": {
"timeout": "1m",

View File

@@ -30,6 +30,7 @@ model User {
pagePermissions WorkspacePageUserPermission[]
connectedAccounts ConnectedAccount[]
sessions UserSession[]
aiSessions AiSession[]
@@map("users")
}
@@ -422,6 +423,75 @@ model UserInvoice {
@@map("user_invoices")
}
enum AiPromptRole {
system
assistant
user
}
model AiPromptMessage {
promptId Int @map("prompt_id") @db.Integer
// if a group of prompts contains multiple sentences, idx specifies the order of each sentence
idx Int @db.Integer
// system/assistant/user
role AiPromptRole
// prompt content
content String @db.Text
attachments Json? @db.Json
params Json? @db.Json
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
prompt AiPrompt @relation(fields: [promptId], references: [id], onDelete: Cascade)
@@unique([promptId, idx])
@@map("ai_prompts_messages")
}
model AiPrompt {
id Int @id @default(autoincrement()) @db.Integer
name String @unique @db.VarChar(32)
// an mark identifying which view to use to display the session
// it is only used in the frontend and does not affect the backend
action String? @db.VarChar
model String? @db.VarChar
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
messages AiPromptMessage[]
sessions AiSession[]
@@map("ai_prompts_metadata")
}
model AiSessionMessage {
id String @id @default(uuid()) @db.VarChar(36)
sessionId String @map("session_id") @db.VarChar(36)
role AiPromptRole
content String @db.Text
attachments Json? @db.Json
params Json? @db.Json
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(6)
session AiSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
@@map("ai_sessions_messages")
}
model AiSession {
id String @id @default(uuid()) @db.VarChar(36)
userId String @map("user_id") @db.VarChar(36)
workspaceId String @map("workspace_id") @db.VarChar(36)
docId String @map("doc_id") @db.VarChar(36)
promptName String @map("prompt_name") @db.VarChar(32)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
prompt AiPrompt @relation(fields: [promptName], references: [name], onDelete: Cascade)
messages AiSessionMessage[]
@@map("ai_sessions_metadata")
}
model DataMigration {
id String @id @default(uuid()) @db.VarChar(36)
name String @db.VarChar

View File

@@ -1,13 +1,12 @@
import { join } from 'node:path';
import { Logger, Module } from '@nestjs/common';
import { APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core';
import { ScheduleModule } from '@nestjs/schedule';
import { ServeStaticModule } from '@nestjs/serve-static';
import { get } from 'lodash-es';
import { AppController } from './app.controller';
import { AuthGuard, AuthModule } from './core/auth';
import { AuthModule } from './core/auth';
import { ADD_ENABLED_FEATURES, ServerConfigModule } from './core/config';
import { DocModule } from './core/doc';
import { FeatureModule } from './core/features';
@@ -17,7 +16,7 @@ import { SyncModule } from './core/sync';
import { UserModule } from './core/user';
import { WorkspaceModule } from './core/workspaces';
import { getOptionalModuleMetadata } from './fundamentals';
import { CacheInterceptor, CacheModule } from './fundamentals/cache';
import { CacheModule } from './fundamentals/cache';
import type { AvailablePlugins } from './fundamentals/config';
import { Config, ConfigModule } from './fundamentals/config';
import { EventModule } from './fundamentals/event';
@@ -103,16 +102,6 @@ export class AppModuleBuilder {
compile() {
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: CacheInterceptor,
},
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
imports: this.modules,
controllers: this.config.isSelfhosted ? [] : [AppController],
})
@@ -135,13 +124,12 @@ function buildAppModule() {
.use(DocModule)
// sync server only
.useIf(config => config.flavor.sync, SyncModule)
.useIf(config => config.flavor.sync, WebSocketModule, SyncModule)
// graphql server only
.useIf(
config => config.flavor.graphql,
ServerConfigModule,
WebSocketModule,
GqlModule,
StorageModule,
UserModule,

View File

@@ -4,7 +4,12 @@ import type { NestExpressApplication } from '@nestjs/platform-express';
import cookieParser from 'cookie-parser';
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
import { GlobalExceptionFilter } from './fundamentals';
import { AuthGuard } from './core/auth';
import {
CacheInterceptor,
CloudThrottlerGuard,
GlobalExceptionFilter,
} from './fundamentals';
import { SocketIoAdapter, SocketIoAdapterImpl } from './fundamentals/websocket';
import { serverTimingAndCache } from './middleware/timing';
@@ -28,6 +33,8 @@ export async function createApp() {
})
);
app.useGlobalGuards(app.get(AuthGuard), app.get(CloudThrottlerGuard));
app.useGlobalInterceptors(app.get(CacheInterceptor));
app.useGlobalFilters(new GlobalExceptionFilter(app.getHttpAdapter()));
app.use(cookieParser());

View File

@@ -19,6 +19,9 @@ AFFiNE.ENV_MAP = {
MAILER_SECURE: ['mailer.secure', 'boolean'],
THROTTLE_TTL: ['rateLimiter.ttl', 'int'],
THROTTLE_LIMIT: ['rateLimiter.limit', 'int'],
COPILOT_OPENAI_API_KEY: 'plugins.copilot.openai.apiKey',
COPILOT_FAL_API_KEY: 'plugins.copilot.fal.apiKey',
COPILOT_UNSPLASH_API_KEY: 'plugins.copilot.unsplashKey',
REDIS_SERVER_HOST: 'plugins.redis.host',
REDIS_SERVER_PORT: ['plugins.redis.port', 'int'],
REDIS_SERVER_USER: 'plugins.redis.username',
@@ -36,4 +39,5 @@ AFFiNE.ENV_MAP = {
'featureFlags.syncClientVersionCheck',
'boolean',
],
TELEMETRY_ENABLE: ['telemetry.enabled', 'boolean'],
};

View File

@@ -36,8 +36,16 @@ if (env.R2_OBJECT_STORAGE_ACCOUNT_ID) {
AFFiNE.storage.storages.blob.bucket = `workspace-blobs-${
AFFiNE.affine.canary ? 'canary' : 'prod'
}`;
AFFiNE.storage.storages.copilot.provider = 'cloudflare-r2';
AFFiNE.storage.storages.copilot.bucket = `workspace-copilot-${
AFFiNE.affine.canary ? 'canary' : 'prod'
}`;
}
AFFiNE.plugins.use('copilot', {
openai: {},
});
AFFiNE.plugins.use('redis');
AFFiNE.plugins.use('payment', {
stripe: {

View File

@@ -14,7 +14,11 @@ import {
} from '@nestjs/common';
import type { Request, Response } from 'express';
import { PaymentRequiredException, URLHelper } from '../../fundamentals';
import {
PaymentRequiredException,
Throttle,
URLHelper,
} from '../../fundamentals';
import { UserService } from '../user';
import { validators } from '../utils/validators';
import { CurrentUser } from './current-user';
@@ -27,6 +31,12 @@ class SignInCredential {
password?: string;
}
class MagicLinkCredential {
email!: string;
token!: string;
}
@Throttle('strict')
@Controller('/api/auth')
export class AuthController {
constructor(
@@ -85,7 +95,7 @@ export class AuthController {
) {
const token = await this.token.createToken(TokenType.SignIn, email);
const magicLink = this.url.link('/api/auth/magic-link', {
const magicLink = this.url.link('/magic-link', {
token,
email,
redirect_uri: redirectUri,
@@ -124,20 +134,16 @@ export class AuthController {
}
@Public()
@Get('/magic-link')
@Post('/magic-link')
async magicLinkSignIn(
@Req() req: Request,
@Res() res: Response,
@Query('token') token?: string,
@Query('email') email?: string,
@Query('redirect_uri') redirectUri = this.url.home
@Body() { email, token }: MagicLinkCredential
) {
if (!token || !email) {
throw new BadRequestException('Invalid Sign-in mail Token');
throw new BadRequestException('Missing sign-in mail token');
}
email = decodeURIComponent(email);
token = decodeURIComponent(token);
validators.assertValidEmail(email);
const valid = await this.token.verifyToken(TokenType.SignIn, token, {
@@ -145,7 +151,7 @@ export class AuthController {
});
if (!valid) {
throw new BadRequestException('Invalid Sign-in mail Token');
throw new BadRequestException('Invalid sign-in mail token');
}
const user = await this.user.fulfillUser(email, {
@@ -155,9 +161,10 @@ export class AuthController {
await this.auth.setCookie(req, res, user);
return this.url.safeRedirect(res, redirectUri);
res.send({ id: user.id, email: user.email, name: user.name });
}
@Throttle('default', { limit: 1200 })
@Public()
@Get('/session')
async currentSessionUser(@CurrentUser() user?: CurrentUser) {
@@ -166,6 +173,7 @@ export class AuthController {
};
}
@Throttle('default', { limit: 1200 })
@Public()
@Get('/sessions')
async currentSessionUsers(@Req() req: Request) {

View File

@@ -36,7 +36,7 @@ export class AuthGuard implements CanActivate, OnModuleInit {
}
async canActivate(context: ExecutionContext) {
const { req } = getRequestResponseFromContext(context);
const { req, res } = getRequestResponseFromContext(context);
// check cookie
let sessionToken: string | undefined =
@@ -51,9 +51,22 @@ export class AuthGuard implements CanActivate, OnModuleInit {
req.headers[AuthService.authUserSeqHeaderName]
);
const user = await this.auth.getUser(sessionToken, userSeq);
const { user, expiresAt } = await this.auth.getUser(
sessionToken,
userSeq
);
if (res && user && expiresAt) {
await this.auth.refreshUserSessionIfNeeded(
req,
res,
sessionToken,
user.id,
expiresAt
);
}
if (user) {
req.sid = sessionToken;
req.user = user;
}
}

View File

@@ -1,16 +1,18 @@
import { Module } from '@nestjs/common';
import { FeatureModule } from '../features';
import { QuotaModule } from '../quota';
import { UserModule } from '../user';
import { AuthController } from './controller';
import { AuthGuard } from './guard';
import { AuthResolver } from './resolver';
import { AuthService } from './service';
import { TokenService, TokenType } from './token';
@Module({
imports: [FeatureModule, UserModule],
providers: [AuthService, AuthResolver, TokenService],
exports: [AuthService],
imports: [FeatureModule, UserModule, QuotaModule],
providers: [AuthService, AuthResolver, TokenService, AuthGuard],
exports: [AuthService, AuthGuard],
controllers: [AuthController],
})
export class AuthModule {}

View File

@@ -1,8 +1,4 @@
import {
BadRequestException,
ForbiddenException,
UseGuards,
} from '@nestjs/common';
import { BadRequestException, ForbiddenException } from '@nestjs/common';
import {
Args,
Context,
@@ -16,7 +12,7 @@ import {
} from '@nestjs/graphql';
import type { Request, Response } from 'express';
import { CloudThrottlerGuard, Config, Throttle } from '../../fundamentals';
import { Config, SkipThrottle, Throttle } from '../../fundamentals';
import { UserService } from '../user';
import { UserType } from '../user/types';
import { validators } from '../utils/validators';
@@ -37,13 +33,7 @@ export class ClientTokenType {
sessionToken?: string;
}
/**
* Auth resolver
* Token rate limit: 20 req/m
* Sign up/in rate limit: 10 req/m
* Other rate limit: 5 req/m
*/
@UseGuards(CloudThrottlerGuard)
@Throttle('strict')
@Resolver(() => UserType)
export class AuthResolver {
constructor(
@@ -53,12 +43,7 @@ export class AuthResolver {
private readonly token: TokenService
) {}
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@SkipThrottle()
@Public()
@Query(() => UserType, {
name: 'currentUser',
@@ -69,12 +54,6 @@ export class AuthResolver {
return user;
}
@Throttle({
default: {
limit: 20,
ttl: 60,
},
})
@ResolveField(() => ClientTokenType, {
name: 'token',
deprecationReason: 'use [/api/auth/authorize]',
@@ -101,12 +80,6 @@ export class AuthResolver {
}
@Public()
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@Mutation(() => UserType)
async signUp(
@Context() ctx: { req: Request; res: Response },
@@ -122,12 +95,6 @@ export class AuthResolver {
}
@Public()
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@Mutation(() => UserType)
async signIn(
@Context() ctx: { req: Request; res: Response },
@@ -141,12 +108,6 @@ export class AuthResolver {
return user;
}
@Throttle({
default: {
limit: 5,
ttl: 60,
},
})
@Mutation(() => UserType)
async changePassword(
@CurrentUser() user: CurrentUser,
@@ -172,12 +133,6 @@ export class AuthResolver {
return user;
}
@Throttle({
default: {
limit: 5,
ttl: 60,
},
})
@Mutation(() => UserType)
async changeEmail(
@CurrentUser() user: CurrentUser,
@@ -202,12 +157,6 @@ export class AuthResolver {
return user;
}
@Throttle({
default: {
limit: 5,
ttl: 60,
},
})
@Mutation(() => Boolean)
async sendChangePasswordEmail(
@CurrentUser() user: CurrentUser,
@@ -235,12 +184,6 @@ export class AuthResolver {
return !res.rejected.length;
}
@Throttle({
default: {
limit: 5,
ttl: 60,
},
})
@Mutation(() => Boolean)
async sendSetPasswordEmail(
@CurrentUser() user: CurrentUser,
@@ -273,12 +216,6 @@ export class AuthResolver {
// 4. user open confirm email page from new email
// 5. user click confirm button
// 6. send notification email
@Throttle({
default: {
limit: 5,
ttl: 60,
},
})
@Mutation(() => Boolean)
async sendChangeEmail(
@CurrentUser() user: CurrentUser,
@@ -299,12 +236,6 @@ export class AuthResolver {
return !res.rejected.length;
}
@Throttle({
default: {
limit: 5,
ttl: 60,
},
})
@Mutation(() => Boolean)
async sendVerifyChangeEmail(
@CurrentUser() user: CurrentUser,
@@ -347,12 +278,6 @@ export class AuthResolver {
return !res.rejected.length;
}
@Throttle({
default: {
limit: 5,
ttl: 60,
},
})
@Mutation(() => Boolean)
async sendVerifyEmail(
@CurrentUser() user: CurrentUser,
@@ -367,12 +292,6 @@ export class AuthResolver {
return !res.rejected.length;
}
@Throttle({
default: {
limit: 5,
ttl: 60,
},
})
@Mutation(() => Boolean)
async verifyEmail(
@CurrentUser() user: CurrentUser,

View File

@@ -11,6 +11,8 @@ import { assign, omit } from 'lodash-es';
import { Config, CryptoHelper, MailService } from '../../fundamentals';
import { FeatureManagementService } from '../features/management';
import { QuotaService } from '../quota/service';
import { QuotaType } from '../quota/types';
import { UserService } from '../user/service';
import type { CurrentUser } from './current-user';
@@ -68,15 +70,28 @@ export class AuthService implements OnApplicationBootstrap {
private readonly db: PrismaClient,
private readonly mailer: MailService,
private readonly feature: FeatureManagementService,
private readonly quota: QuotaService,
private readonly user: UserService,
private readonly crypto: CryptoHelper
) {}
async onApplicationBootstrap() {
if (this.config.node.dev) {
await this.signUp('Dev User', 'dev@affine.pro', 'dev').catch(() => {
try {
const [email, name, pwd] = ['dev@affine.pro', 'Dev User', 'dev'];
let devUser = await this.user.findUserByEmail(email);
if (!devUser) {
devUser = await this.user.createUser({
email,
name,
password: await this.crypto.encryptPassword(pwd),
});
}
await this.quota.switchUserQuota(devUser.id, QuotaType.ProPlanV1);
await this.feature.addCopilot(devUser.id);
} catch (e) {
// ignore
});
}
}
}
@@ -131,24 +146,27 @@ export class AuthService implements OnApplicationBootstrap {
return sessionUser(user);
}
async getUser(token: string, seq = 0): Promise<CurrentUser | null> {
async getUser(
token: string,
seq = 0
): Promise<{ user: CurrentUser | null; expiresAt: Date | null }> {
const session = await this.getSession(token);
// no such session
if (!session) {
return null;
return { user: null, expiresAt: null };
}
const userSession = session.userSessions.at(seq);
// no such user session
if (!userSession) {
return null;
return { user: null, expiresAt: null };
}
// user session expired
if (userSession.expiresAt && userSession.expiresAt <= new Date()) {
return null;
return { user: null, expiresAt: null };
}
const user = await this.db.user.findUnique({
@@ -156,10 +174,10 @@ export class AuthService implements OnApplicationBootstrap {
});
if (!user) {
return null;
return { user: null, expiresAt: null };
}
return sessionUser(user);
return { user: sessionUser(user), expiresAt: userSession.expiresAt };
}
async getUserList(token: string) {
@@ -255,6 +273,43 @@ export class AuthService implements OnApplicationBootstrap {
});
}
async refreshUserSessionIfNeeded(
_req: Request,
res: Response,
sessionId: string,
userId: string,
expiresAt: Date,
ttr = this.config.auth.session.ttr
): Promise<boolean> {
if (expiresAt && expiresAt.getTime() - Date.now() > ttr * 1000) {
// no need to refresh
return false;
}
const newExpiresAt = new Date(
Date.now() + this.config.auth.session.ttl * 1000
);
await this.db.userSession.update({
where: {
sessionId_userId: {
sessionId,
userId,
},
},
data: {
expiresAt: newExpiresAt,
},
});
res.cookie(AuthService.sessionCookieName, sessionId, {
expires: newExpiresAt,
...this.cookieOptions,
});
return true;
}
async createUserSession(
user: { id: string },
existingSession?: string,

View File

@@ -70,14 +70,17 @@ export class TokenService {
!expired && (!record.credential || record.credential === credential);
if ((expired || valid) && !keep) {
await this.db.verificationToken.delete({
const deleted = await this.db.verificationToken.deleteMany({
where: {
type_token: {
token,
type,
},
token,
type,
},
});
// already deleted, means token has been used
if (!deleted.count) {
return null;
}
}
return valid ? record : null;

View File

@@ -5,6 +5,7 @@ import { DeploymentType } from '../fundamentals';
import { Public } from './auth';
export enum ServerFeature {
Copilot = 'copilot',
Payment = 'payment',
OAuth = 'oauth',
}
@@ -66,6 +67,9 @@ export class ServerConfigType {
description: 'credentials requirement',
})
credentialsRequirement!: CredentialsRequirementType;
@Field({ description: 'enable telemetry' })
enableTelemetry!: boolean;
}
export class ServerConfigResolver {
@@ -87,6 +91,7 @@ export class ServerConfigResolver {
credentialsRequirement: {
password: AFFiNE.auth.password,
},
enableTelemetry: AFFiNE.telemetry.enabled,
};
}
}

View File

@@ -54,10 +54,35 @@ export class UnlimitedWorkspaceFeatureConfig extends FeatureConfig {
}
}
export class UnlimitedCopilotFeatureConfig extends FeatureConfig {
override config!: Feature & { feature: FeatureType.UnlimitedCopilot };
constructor(data: any) {
super(data);
if (this.config.feature !== FeatureType.UnlimitedCopilot) {
throw new Error('Invalid feature config: type is not AIEarlyAccess');
}
}
}
export class AIEarlyAccessFeatureConfig extends FeatureConfig {
override config!: Feature & { feature: FeatureType.AIEarlyAccess };
constructor(data: any) {
super(data);
if (this.config.feature !== FeatureType.AIEarlyAccess) {
throw new Error('Invalid feature config: type is not AIEarlyAccess');
}
}
}
const FeatureConfigMap = {
[FeatureType.Copilot]: CopilotFeatureConfig,
[FeatureType.EarlyAccess]: EarlyAccessFeatureConfig,
[FeatureType.AIEarlyAccess]: AIEarlyAccessFeatureConfig,
[FeatureType.UnlimitedWorkspace]: UnlimitedWorkspaceFeatureConfig,
[FeatureType.UnlimitedCopilot]: UnlimitedCopilotFeatureConfig,
};
export type FeatureConfigType<F extends FeatureType> = InstanceType<

View File

@@ -1,6 +1,6 @@
import { Module } from '@nestjs/common';
import { FeatureManagementService } from './management';
import { EarlyAccessType, FeatureManagementService } from './management';
import { FeatureService } from './service';
/**
@@ -15,6 +15,11 @@ import { FeatureService } from './service';
})
export class FeatureModule {}
export { type CommonFeature, commonFeatureSchema } from './types';
export { FeatureKind, Features, FeatureType } from './types';
export { FeatureManagementService, FeatureService };
export {
type CommonFeature,
commonFeatureSchema,
FeatureKind,
Features,
FeatureType,
} from './types';
export { EarlyAccessType, FeatureManagementService, FeatureService };

View File

@@ -7,6 +7,11 @@ import { FeatureType } from './types';
const STAFF = ['@toeverything.info'];
export enum EarlyAccessType {
App = 'app',
AI = 'ai',
}
@Injectable()
export class FeatureManagementService {
protected logger = new Logger(FeatureManagementService.name);
@@ -30,25 +35,43 @@ export class FeatureManagementService {
}
// ======== Early Access ========
async addEarlyAccess(userId: string) {
async addEarlyAccess(
userId: string,
type: EarlyAccessType = EarlyAccessType.App
) {
return this.feature.addUserFeature(
userId,
FeatureType.EarlyAccess,
2,
type === EarlyAccessType.App
? FeatureType.EarlyAccess
: FeatureType.AIEarlyAccess,
'Early access user'
);
}
async removeEarlyAccess(userId: string) {
return this.feature.removeUserFeature(userId, FeatureType.EarlyAccess);
async removeEarlyAccess(
userId: string,
type: EarlyAccessType = EarlyAccessType.App
) {
return this.feature.removeUserFeature(
userId,
type === EarlyAccessType.App
? FeatureType.EarlyAccess
: FeatureType.AIEarlyAccess
);
}
async listEarlyAccess() {
return this.feature.listFeatureUsers(FeatureType.EarlyAccess);
async listEarlyAccess(type: EarlyAccessType = EarlyAccessType.App) {
return this.feature.listFeatureUsers(
type === EarlyAccessType.App
? FeatureType.EarlyAccess
: FeatureType.AIEarlyAccess
);
}
async isEarlyAccessUser(email: string) {
async isEarlyAccessUser(
email: string,
type: EarlyAccessType = EarlyAccessType.App
) {
const user = await this.prisma.user.findFirst({
where: {
email: {
@@ -57,9 +80,15 @@ export class FeatureManagementService {
},
},
});
if (user) {
const canEarlyAccess = await this.feature
.hasUserFeature(user.id, FeatureType.EarlyAccess)
.hasUserFeature(
user.id,
type === EarlyAccessType.App
? FeatureType.EarlyAccess
: FeatureType.AIEarlyAccess
)
.catch(() => false);
return canEarlyAccess;
@@ -68,14 +97,43 @@ export class FeatureManagementService {
}
/// check early access by email
async canEarlyAccess(email: string) {
async canEarlyAccess(
email: string,
type: EarlyAccessType = EarlyAccessType.App
) {
if (this.config.featureFlags.earlyAccessPreview && !this.isStaff(email)) {
return this.isEarlyAccessUser(email);
return this.isEarlyAccessUser(email, type);
} else {
return true;
}
}
// ======== CopilotFeature ========
async addCopilot(userId: string, reason = 'Copilot plan user') {
return this.feature.addUserFeature(
userId,
FeatureType.UnlimitedCopilot,
reason
);
}
async removeCopilot(userId: string) {
return this.feature.removeUserFeature(userId, FeatureType.UnlimitedCopilot);
}
async isCopilotUser(userId: string) {
return await this.feature.hasUserFeature(
userId,
FeatureType.UnlimitedCopilot
);
}
// ======== User Feature ========
async getActivatedUserFeatures(userId: string): Promise<FeatureType[]> {
const features = await this.feature.getActivatedUserFeatures(userId);
return features.map(f => f.feature.name);
}
// ======== Workspace Feature ========
async addWorkspaceFeatures(
workspaceId: string,
@@ -115,10 +173,4 @@ export class FeatureManagementService {
async listFeatureWorkspaces(feature: FeatureType) {
return this.feature.listFeatureWorkspaces(feature);
}
async getUserFeatures(userId: string): Promise<FeatureType[]> {
return (await this.feature.getUserFeatures(userId)).map(
f => f.feature.name
);
}
}

View File

@@ -59,7 +59,6 @@ export class FeatureService {
async addUserFeature(
userId: string,
feature: FeatureType,
version: number,
reason: string,
expiredAt?: Date | string
) {
@@ -77,9 +76,21 @@ export class FeatureService {
createdAt: 'desc',
},
});
if (latestFlag) {
return latestFlag.id;
} else {
const latestVersion = await tx.features
.aggregate({
where: { feature },
_max: { version: true },
})
.then(r => r._max.version);
if (!latestVersion) {
throw new Error(`Feature ${feature} not found`);
}
return tx.userFeatures
.create({
data: {
@@ -95,7 +106,7 @@ export class FeatureService {
connect: {
feature_version: {
feature,
version,
version: latestVersion,
},
type: FeatureKind.Feature,
},
@@ -157,6 +168,33 @@ export class FeatureService {
return configs.filter(feature => !!feature.feature);
}
async getActivatedUserFeatures(userId: string) {
const features = await this.prisma.userFeatures.findMany({
where: {
user: { id: userId },
feature: { type: FeatureKind.Feature },
activated: true,
OR: [{ expiredAt: null }, { expiredAt: { gt: new Date() } }],
},
select: {
activated: true,
reason: true,
createdAt: true,
expiredAt: true,
featureId: true,
},
});
const configs = await Promise.all(
features.map(async feature => ({
...feature,
feature: await getFeature(this.prisma, feature.featureId),
}))
);
return configs.filter(feature => !!feature.feature);
}
async listFeatureUsers(feature: FeatureType) {
return this.prisma.userFeatures
.findMany({
@@ -193,6 +231,7 @@ export class FeatureService {
feature,
type: FeatureKind.Feature,
},
OR: [{ expiredAt: null }, { expiredAt: { gt: new Date() } }],
},
})
.then(count => count > 0);

View File

@@ -1,8 +1,12 @@
import { registerEnumType } from '@nestjs/graphql';
export enum FeatureType {
Copilot = 'copilot',
// user feature
EarlyAccess = 'early_access',
AIEarlyAccess = 'ai_early_access',
UnlimitedCopilot = 'unlimited_copilot',
// workspace feature
Copilot = 'copilot',
UnlimitedWorkspace = 'unlimited_workspace',
}

View File

@@ -9,3 +9,8 @@ export const featureEarlyAccess = z.object({
whitelist: z.string().array(),
}),
});
export const featureAIEarlyAccess = z.object({
feature: z.literal(FeatureType.AIEarlyAccess),
configs: z.object({}),
});

View File

@@ -2,7 +2,8 @@ import { z } from 'zod';
import { FeatureType } from './common';
import { featureCopilot } from './copilot';
import { featureEarlyAccess } from './early-access';
import { featureAIEarlyAccess, featureEarlyAccess } from './early-access';
import { featureUnlimitedCopilot } from './unlimited-copilot';
import { featureUnlimitedWorkspace } from './unlimited-workspace';
/// ======== common schema ========
@@ -52,6 +53,18 @@ export const Features: Feature[] = [
version: 1,
configs: {},
},
{
feature: FeatureType.UnlimitedCopilot,
type: FeatureKind.Feature,
version: 1,
configs: {},
},
{
feature: FeatureType.AIEarlyAccess,
type: FeatureKind.Feature,
version: 1,
configs: {},
},
];
/// ======== schema infer ========
@@ -64,7 +77,9 @@ export const FeatureSchema = commonFeatureSchema
z.discriminatedUnion('feature', [
featureCopilot,
featureEarlyAccess,
featureAIEarlyAccess,
featureUnlimitedWorkspace,
featureUnlimitedCopilot,
])
);

View File

@@ -0,0 +1,8 @@
import { z } from 'zod';
import { FeatureType } from './common';
export const featureUnlimitedCopilot = z.object({
feature: z.literal(FeatureType.UnlimitedCopilot),
configs: z.object({}),
});

View File

@@ -20,5 +20,5 @@ import { QuotaManagementService } from './storage';
export class QuotaModule {}
export { QuotaManagementService, QuotaService };
export { Quota_FreePlanV1_1, Quota_ProPlanV1, Quotas } from './schema';
export { Quota_FreePlanV1_1, Quota_ProPlanV1 } from './schema';
export { QuotaQueryType, QuotaType } from './types';

View File

@@ -79,6 +79,10 @@ export class QuotaConfig {
return this.config.configs.memberLimit;
}
get copilotActionLimit() {
return this.config.configs.copilotActionLimit || undefined;
}
get humanReadable() {
return {
name: this.config.configs.name,
@@ -86,6 +90,9 @@ export class QuotaConfig {
storageQuota: formatSize(this.storageQuota),
historyPeriod: formatDate(this.historyPeriod),
memberLimit: this.memberLimit.toString(),
copilotActionLimit: this.copilotActionLimit
? `${this.copilotActionLimit} times`
: 'Unlimited',
};
}
}

View File

@@ -93,14 +93,85 @@ export const Quotas: Quota[] = [
memberLimit: 3,
},
},
{
feature: QuotaType.FreePlanV1,
type: FeatureKind.Quota,
version: 4,
configs: {
// quota name
name: 'Free',
// single blob limit 10MB
blobLimit: 10 * OneMB,
// server limit will larger then client to handle a edge case:
// when a user downgrades from pro to free, he can still continue
// to upload previously added files that exceed the free limit
// NOTE: this is a product decision, may change in future
businessBlobLimit: 100 * OneMB,
// total blob limit 10GB
storageQuota: 10 * OneGB,
// history period of validity 7 days
historyPeriod: 7 * OneDay,
// member limit 3
memberLimit: 3,
// copilot action limit 10
copilotActionLimit: 10,
},
},
{
feature: QuotaType.ProPlanV1,
type: FeatureKind.Quota,
version: 2,
configs: {
// quota name
name: 'Pro',
// single blob limit 100MB
blobLimit: 100 * OneMB,
// total blob limit 100GB
storageQuota: 100 * OneGB,
// history period of validity 30 days
historyPeriod: 30 * OneDay,
// member limit 10
memberLimit: 10,
// copilot action limit 10
copilotActionLimit: 10,
},
},
{
feature: QuotaType.RestrictedPlanV1,
type: FeatureKind.Quota,
version: 2,
configs: {
// quota name
name: 'Restricted',
// single blob limit 10MB
blobLimit: OneMB,
// total blob limit 1GB
storageQuota: 10 * OneMB,
// history period of validity 30 days
historyPeriod: 30 * OneDay,
// member limit 10
memberLimit: 10,
// copilot action limit 10
copilotActionLimit: 10,
},
},
];
export function getLatestQuota(type: QuotaType) {
const quota = Quotas.filter(f => f.feature === type);
quota.sort((a, b) => b.version - a.version);
return quota[0];
}
export const FreePlan = getLatestQuota(QuotaType.FreePlanV1);
export const ProPlan = getLatestQuota(QuotaType.ProPlanV1);
export const Quota_FreePlanV1_1 = {
feature: Quotas[4].feature,
version: Quotas[4].version,
feature: Quotas[5].feature,
version: Quotas[5].version,
};
export const Quota_ProPlanV1 = {
feature: Quotas[1].feature,
version: Quotas[1].version,
feature: Quotas[6].feature,
version: Quotas[6].version,
};

View File

@@ -3,13 +3,17 @@ import { PrismaClient } from '@prisma/client';
import type { EventPayload } from '../../fundamentals';
import { OnEvent, PrismaTransaction } from '../../fundamentals';
import { FeatureKind } from '../features';
import { SubscriptionPlan } from '../../plugins/payment/types';
import { FeatureKind, FeatureManagementService } from '../features';
import { QuotaConfig } from './quota';
import { QuotaType } from './types';
@Injectable()
export class QuotaService {
constructor(private readonly prisma: PrismaClient) {}
constructor(
private readonly prisma: PrismaClient,
private readonly feature: FeatureManagementService
) {}
// get activated user quota
async getUserQuota(userId: string) {
@@ -159,22 +163,42 @@ export class QuotaService {
@OnEvent('user.subscription.activated')
async onSubscriptionUpdated({
userId,
plan,
}: EventPayload<'user.subscription.activated'>) {
await this.switchUserQuota(
userId,
QuotaType.ProPlanV1,
'subscription activated'
);
switch (plan) {
case SubscriptionPlan.AI:
await this.feature.addCopilot(userId, 'subscription activated');
break;
case SubscriptionPlan.Pro:
await this.switchUserQuota(
userId,
QuotaType.ProPlanV1,
'subscription activated'
);
break;
default:
break;
}
}
@OnEvent('user.subscription.canceled')
async onSubscriptionCanceled(
userId: EventPayload<'user.subscription.canceled'>
) {
await this.switchUserQuota(
userId,
QuotaType.FreePlanV1,
'subscription canceled'
);
async onSubscriptionCanceled({
userId,
plan,
}: EventPayload<'user.subscription.canceled'>) {
switch (plan) {
case SubscriptionPlan.AI:
await this.feature.removeCopilot(userId);
break;
case SubscriptionPlan.Pro:
await this.switchUserQuota(
userId,
QuotaType.FreePlanV1,
'subscription canceled'
);
break;
default:
break;
}
}
}

View File

@@ -7,7 +7,10 @@ import { OneGB } from './constant';
import { QuotaService } from './service';
import { formatSize, QuotaQueryType } from './types';
type QuotaBusinessType = QuotaQueryType & { businessBlobLimit: number };
type QuotaBusinessType = QuotaQueryType & {
businessBlobLimit: number;
unlimited: boolean;
};
@Injectable()
export class QuotaManagementService {
@@ -33,6 +36,7 @@ export class QuotaManagementService {
storageQuota: quota.feature.storageQuota,
historyPeriod: quota.feature.historyPeriod,
memberLimit: quota.feature.memberLimit,
copilotActionLimit: quota.feature.copilotActionLimit,
};
}
@@ -58,6 +62,52 @@ export class QuotaManagementService {
}, 0);
}
private generateQuotaCalculator(
quota: number,
blobLimit: number,
usedSize: number,
unlimited = false
) {
const checkExceeded = (recvSize: number) => {
const total = usedSize + recvSize;
// only skip total storage check if workspace has unlimited feature
if (total > quota && !unlimited) {
this.logger.log(`storage size limit exceeded: ${total} > ${quota}`);
return true;
} else if (recvSize > blobLimit) {
this.logger.log(`blob size limit exceeded: ${recvSize} > ${blobLimit}`);
return true;
} else {
return false;
}
};
return checkExceeded;
}
async getQuotaCalculator(userId: string) {
const quota = await this.getUserQuota(userId);
const { storageQuota, businessBlobLimit } = quota;
const usedSize = await this.getUserUsage(userId);
return this.generateQuotaCalculator(
storageQuota,
businessBlobLimit,
usedSize
);
}
async getQuotaCalculatorByWorkspace(workspaceId: string) {
const { storageQuota, usedSize, businessBlobLimit, unlimited } =
await this.getWorkspaceUsage(workspaceId);
return this.generateQuotaCalculator(
storageQuota,
businessBlobLimit,
usedSize,
unlimited
);
}
// get workspace's owner quota and total size of used
// quota was apply to owner's account
async getWorkspaceUsage(workspaceId: string): Promise<QuotaBusinessType> {
@@ -72,11 +122,18 @@ export class QuotaManagementService {
historyPeriod,
memberLimit,
storageQuota,
copilotActionLimit,
humanReadable,
},
} = await this.quota.getUserQuota(owner.id);
// get all workspaces size of owner used
const usedSize = await this.getUserUsage(owner.id);
// relax restrictions if workspace has unlimited feature
// todo(@darkskygit): need a mechanism to allow feature as a middleware to edit quota
const unlimited = await this.feature.hasWorkspaceFeature(
workspaceId,
FeatureType.UnlimitedWorkspace
);
const quota = {
name,
@@ -85,17 +142,13 @@ export class QuotaManagementService {
historyPeriod,
memberLimit,
storageQuota,
copilotActionLimit,
humanReadable,
usedSize,
unlimited,
};
// relax restrictions if workspace has unlimited feature
// todo(@darkskygit): need a mechanism to allow feature as a middleware to edit quota
const unlimited = await this.feature.hasWorkspaceFeature(
workspaceId,
FeatureType.UnlimitedWorkspace
);
if (unlimited) {
if (quota.unlimited) {
return this.mergeUnlimitedQuota(quota);
}

View File

@@ -34,6 +34,7 @@ const quotaPlan = z.object({
historyPeriod: z.number().positive().int(),
memberLimit: z.number().positive().int(),
businessBlobLimit: z.number().positive().int().nullish(),
copilotActionLimit: z.number().positive().int().nullish(),
}),
});
@@ -65,6 +66,9 @@ export class HumanReadableQuotaType {
@Field(() => String)
memberLimit!: string;
@Field(() => String, { nullable: true })
copilotActionLimit?: string;
}
@ObjectType()
@@ -84,6 +88,9 @@ export class QuotaQueryType {
@Field(() => SafeIntResolver)
storageQuota!: number;
@Field(() => SafeIntResolver, { nullable: true })
copilotActionLimit?: number;
@Field(() => HumanReadableQuotaType)
humanReadable!: HumanReadableQuotaType;

View File

@@ -1,22 +1,24 @@
import { BadRequestException, ForbiddenException } from '@nestjs/common';
import {
BadRequestException,
ForbiddenException,
UseGuards,
} from '@nestjs/common';
import { Args, Context, Int, Mutation, Query, Resolver } from '@nestjs/graphql';
Args,
Context,
Int,
Mutation,
Query,
registerEnumType,
Resolver,
} from '@nestjs/graphql';
import { CloudThrottlerGuard, Throttle } from '../../fundamentals';
import { CurrentUser } from '../auth/current-user';
import { sessionUser } from '../auth/service';
import { FeatureManagementService } from '../features';
import { EarlyAccessType, FeatureManagementService } from '../features';
import { UserService } from './service';
import { UserType } from './types';
/**
* User resolver
* All op rate limit: 10 req/m
*/
@UseGuards(CloudThrottlerGuard)
registerEnumType(EarlyAccessType, {
name: 'EarlyAccessType',
});
@Resolver(() => UserType)
export class UserManagementResolver {
constructor(
@@ -24,37 +26,26 @@ export class UserManagementResolver {
private readonly feature: FeatureManagementService
) {}
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@Mutation(() => Int)
async addToEarlyAccess(
@CurrentUser() currentUser: CurrentUser,
@Args('email') email: string
@Args('email') email: string,
@Args({ name: 'type', type: () => EarlyAccessType }) type: EarlyAccessType
): Promise<number> {
if (!this.feature.isStaff(currentUser.email)) {
throw new ForbiddenException('You are not allowed to do this');
}
const user = await this.users.findUserByEmail(email);
if (user) {
return this.feature.addEarlyAccess(user.id);
return this.feature.addEarlyAccess(user.id, type);
} else {
const user = await this.users.createAnonymousUser(email, {
registered: false,
});
return this.feature.addEarlyAccess(user.id);
return this.feature.addEarlyAccess(user.id, type);
}
}
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@Mutation(() => Int)
async removeEarlyAccess(
@CurrentUser() currentUser: CurrentUser,
@@ -70,12 +61,6 @@ export class UserManagementResolver {
return this.feature.removeEarlyAccess(user.id);
}
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@Query(() => [UserType])
async earlyAccessUsers(
@Context() ctx: { isAdminQuery: boolean },

View File

@@ -1,4 +1,4 @@
import { BadRequestException, UseGuards } from '@nestjs/common';
import { BadRequestException } from '@nestjs/common';
import {
Args,
Int,
@@ -14,7 +14,6 @@ import { isNil, omitBy } from 'lodash-es';
import type { FileUpload } from '../../fundamentals';
import {
CloudThrottlerGuard,
EventEmitter,
PaymentRequiredException,
Throttle,
@@ -35,11 +34,6 @@ import {
UserType,
} from './types';
/**
* User resolver
* All op rate limit: 10 req/m
*/
@UseGuards(CloudThrottlerGuard)
@Resolver(() => UserType)
export class UserResolver {
constructor(
@@ -51,12 +45,7 @@ export class UserResolver {
private readonly event: EventEmitter
) {}
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@Throttle('strict')
@Query(() => UserOrLimitedUser, {
name: 'user',
description: 'Get user by email',
@@ -90,7 +79,6 @@ export class UserResolver {
};
}
@Throttle({ default: { limit: 10, ttl: 60 } })
@ResolveField(() => UserQuotaType, { name: 'quota', nullable: true })
async getQuota(@CurrentUser() me: User) {
const quota = await this.quota.getUserQuota(me.id);
@@ -98,7 +86,6 @@ export class UserResolver {
return quota.feature;
}
@Throttle({ default: { limit: 10, ttl: 60 } })
@ResolveField(() => Int, {
name: 'invoiceCount',
description: 'Get user invoice count',
@@ -109,21 +96,14 @@ export class UserResolver {
});
}
@Throttle({ default: { limit: 10, ttl: 60 } })
@ResolveField(() => [FeatureType], {
name: 'features',
description: 'Enabled features of a user',
})
async userFeatures(@CurrentUser() user: CurrentUser) {
return this.feature.getUserFeatures(user.id);
return this.feature.getActivatedUserFeatures(user.id);
}
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@Mutation(() => UserType, {
name: 'uploadAvatar',
description: 'Upload user avatar',
@@ -153,12 +133,6 @@ export class UserResolver {
});
}
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@Mutation(() => UserType, {
name: 'updateProfile',
})
@@ -180,12 +154,6 @@ export class UserResolver {
);
}
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@Mutation(() => RemoveAvatar, {
name: 'removeAvatar',
description: 'Remove user avatar',
@@ -201,12 +169,6 @@ export class UserResolver {
return { success: true };
}
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@Mutation(() => DeleteAccount)
async deleteAccount(
@CurrentUser() user: CurrentUser

View File

@@ -36,10 +36,17 @@ export class WorkspacesController {
@Get('/:id/blobs/:name')
@CallTimer('controllers', 'workspace_get_blob')
async blob(
@CurrentUser() user: CurrentUser | undefined,
@Param('id') workspaceId: string,
@Param('name') name: string,
@Res() res: Response
) {
// if workspace is public or have any public page, then allow to access
// otherwise, check permission
if (!(await this.permission.tryCheckWorkspace(workspaceId, user?.id))) {
throw new ForbiddenException('Permission denied');
}
const { body, metadata } = await this.storage.get(workspaceId, name);
if (!body) {
@@ -109,11 +116,6 @@ export class WorkspacesController {
}
res.setHeader('content-type', 'application/octet-stream');
res.setHeader(
'last-modified',
new Date(binResponse.timestamp).toUTCString()
);
res.setHeader('cache-control', 'private, max-age=2592000');
res.send(binResponse.binary);
}

View File

@@ -1,4 +1,4 @@
import { ForbiddenException, UseGuards } from '@nestjs/common';
import { ForbiddenException } from '@nestjs/common';
import {
Args,
Int,
@@ -9,13 +9,11 @@ import {
Resolver,
} from '@nestjs/graphql';
import { CloudThrottlerGuard, Throttle } from '../../fundamentals';
import { CurrentUser } from '../auth';
import { FeatureManagementService, FeatureType } from '../features';
import { PermissionService } from './permission';
import { WorkspaceType } from './types';
@UseGuards(CloudThrottlerGuard)
@Resolver(() => WorkspaceType)
export class WorkspaceManagementResolver {
constructor(
@@ -23,12 +21,6 @@ export class WorkspaceManagementResolver {
private readonly permission: PermissionService
) {}
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@Mutation(() => Int)
async addWorkspaceFeature(
@CurrentUser() currentUser: CurrentUser,
@@ -42,12 +34,6 @@ export class WorkspaceManagementResolver {
return this.feature.addWorkspaceFeatures(workspaceId, feature);
}
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@Mutation(() => Int)
async removeWorkspaceFeature(
@CurrentUser() currentUser: CurrentUser,
@@ -61,12 +47,6 @@ export class WorkspaceManagementResolver {
return this.feature.removeWorkspaceFeature(workspaceId, feature);
}
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@Query(() => [WorkspaceType])
async listWorkspaceFeatures(
@CurrentUser() user: CurrentUser,
@@ -117,12 +97,7 @@ export class WorkspaceManagementResolver {
async availableFeatures(
@CurrentUser() user: CurrentUser
): Promise<FeatureType[]> {
const isEarlyAccessUser = await this.feature.isEarlyAccessUser(user.email);
if (isEarlyAccessUser) {
return [FeatureType.Copilot];
} else {
return [];
}
return await this.feature.getActivatedUserFeatures(user.id);
}
@ResolveField(() => [FeatureType], {

View File

@@ -26,6 +26,22 @@ export class PermissionService {
return data?.type as Permission;
}
/**
* check whether a workspace exists and has any one can access it
* @param workspaceId workspace id
* @returns
*/
async hasWorkspace(workspaceId: string) {
return await this.prisma.workspaceUserPermission
.count({
where: {
workspaceId,
accepted: true,
},
})
.then(count => count > 0);
}
async getOwnedWorkspaces(userId: string) {
return this.prisma.workspaceUserPermission
.findMany({
@@ -65,10 +81,22 @@ export class PermissionService {
});
}
/**
* check if a doc binary is accessible by a user
*/
async isAccessible(ws: string, id: string, user?: string): Promise<boolean> {
// workspace
if (ws === id) {
return this.tryCheckWorkspace(ws, user, Permission.Read);
// if workspace is public or have any public page, then allow to access
const [isPublicWorkspace, publicPages] = await Promise.all([
this.tryCheckWorkspace(ws, user, Permission.Read),
await this.prisma.workspacePage.count({
where: {
workspaceId: ws,
public: true,
},
}),
]);
return isPublicWorkspace || publicPages > 0;
}
return this.tryCheckPage(ws, id, user);
@@ -96,6 +124,23 @@ export class PermissionService {
return count !== 0;
}
/**
* only check permission if the workspace is a cloud workspace
* @param workspaceId workspace id
* @param userId user id, check if is a public workspace if not provided
* @param permission default is read
*/
async checkCloudWorkspace(
workspaceId: string,
userId?: string,
permission: Permission = Permission.Read
) {
const hasWorkspace = await this.hasWorkspace(workspaceId);
if (hasWorkspace) {
await this.checkWorkspace(workspaceId, userId, permission);
}
}
async checkWorkspace(
ws: string,
user?: string,
@@ -122,21 +167,6 @@ export class PermissionService {
if (count > 0) {
return true;
}
const publicPage = await this.prisma.workspacePage.findFirst({
select: {
pageId: true,
},
where: {
workspaceId: ws,
public: true,
},
});
// has any public pages
if (publicPage) {
return true;
}
}
if (user) {
@@ -263,6 +293,25 @@ export class PermissionService {
/// End regin: workspace permission
/// Start regin: page permission
/**
* only check permission if the workspace is a cloud workspace
* @param workspaceId workspace id
* @param pageId page id aka doc id
* @param userId user id, check if is a public page if not provided
* @param permission default is read
*/
async checkCloudPagePermission(
workspaceId: string,
pageId: string,
userId?: string,
permission = Permission.Read
) {
const hasWorkspace = await this.hasWorkspace(workspaceId);
if (hasWorkspace) {
await this.checkPagePermission(workspaceId, pageId, userId, permission);
}
}
async checkPagePermission(
ws: string,
page: string,

View File

@@ -1,9 +1,4 @@
import {
ForbiddenException,
Logger,
PayloadTooLargeException,
UseGuards,
} from '@nestjs/common';
import { Logger, PayloadTooLargeException, UseGuards } from '@nestjs/common';
import {
Args,
Int,
@@ -23,7 +18,6 @@ import {
PreventCache,
} from '../../../fundamentals';
import { CurrentUser } from '../../auth';
import { FeatureManagementService, FeatureType } from '../../features';
import { QuotaManagementService } from '../../quota';
import { WorkspaceBlobStorage } from '../../storage';
import { PermissionService } from '../permission';
@@ -35,7 +29,6 @@ export class WorkspaceBlobResolver {
logger = new Logger(WorkspaceBlobResolver.name);
constructor(
private readonly permissions: PermissionService,
private readonly feature: FeatureManagementService,
private readonly quota: QuotaManagementService,
private readonly storage: WorkspaceBlobStorage
) {}
@@ -130,34 +123,8 @@ export class WorkspaceBlobResolver {
Permission.Write
);
const { storageQuota, usedSize, businessBlobLimit } =
await this.quota.getWorkspaceUsage(workspaceId);
const unlimited = await this.feature.hasWorkspaceFeature(
workspaceId,
FeatureType.UnlimitedWorkspace
);
const checkExceeded = (recvSize: number) => {
if (!storageQuota) {
throw new ForbiddenException('Cannot find user quota.');
}
const total = usedSize + recvSize;
// only skip total storage check if workspace has unlimited feature
if (total > storageQuota && !unlimited) {
this.logger.log(
`storage size limit exceeded: ${total} > ${storageQuota}`
);
return true;
} else if (recvSize > businessBlobLimit) {
this.logger.log(
`blob size limit exceeded: ${recvSize} > ${businessBlobLimit}`
);
return true;
} else {
return false;
}
};
const checkExceeded =
await this.quota.getQuotaCalculatorByWorkspace(workspaceId);
if (checkExceeded(0)) {
throw new PayloadTooLargeException(

View File

@@ -1,4 +1,3 @@
import { UseGuards } from '@nestjs/common';
import {
Args,
Field,
@@ -12,7 +11,6 @@ import {
} from '@nestjs/graphql';
import type { SnapshotHistory } from '@prisma/client';
import { CloudThrottlerGuard } from '../../../fundamentals';
import { CurrentUser } from '../../auth';
import { DocHistoryManager } from '../../doc';
import { DocID } from '../../utils/doc';
@@ -31,7 +29,6 @@ class DocHistoryType implements Partial<SnapshotHistory> {
timestamp!: Date;
}
@UseGuards(CloudThrottlerGuard)
@Resolver(() => WorkspaceType)
export class DocHistoryResolver {
constructor(

View File

@@ -1,4 +1,4 @@
import { BadRequestException, UseGuards } from '@nestjs/common';
import { BadRequestException } from '@nestjs/common';
import {
Args,
Field,
@@ -12,7 +12,6 @@ import {
import type { WorkspacePage as PrismaWorkspacePage } from '@prisma/client';
import { PrismaClient } from '@prisma/client';
import { CloudThrottlerGuard } from '../../../fundamentals';
import { CurrentUser } from '../../auth';
import { DocID } from '../../utils/doc';
import { PermissionService, PublicPageMode } from '../permission';
@@ -38,7 +37,6 @@ class WorkspacePage implements Partial<PrismaWorkspacePage> {
public!: boolean;
}
@UseGuards(CloudThrottlerGuard)
@Resolver(() => WorkspaceType)
export class PagePermissionResolver {
constructor(
@@ -78,12 +76,30 @@ export class PagePermissionResolver {
});
}
@ResolveField(() => WorkspacePage, {
description: 'Get public page of a workspace by page id.',
complexity: 2,
nullable: true,
})
async publicPage(
@Parent() workspace: WorkspaceType,
@Args('pageId') pageId: string
) {
return this.prisma.workspacePage.findFirst({
where: {
workspaceId: workspace.id,
pageId,
public: true,
},
});
}
/**
* @deprecated
*/
@Mutation(() => Boolean, {
name: 'sharePage',
deprecationReason: 'renamed to publicPage',
deprecationReason: 'renamed to publishPage',
})
async deprecatedSharePage(
@CurrentUser() user: CurrentUser,

View File

@@ -4,7 +4,6 @@ import {
Logger,
NotFoundException,
PayloadTooLargeException,
UseGuards,
} from '@nestjs/common';
import {
Args,
@@ -22,7 +21,6 @@ import { applyUpdate, Doc } from 'yjs';
import type { FileUpload } from '../../../fundamentals';
import {
CloudThrottlerGuard,
EventEmitter,
MailService,
MutexService,
@@ -48,7 +46,6 @@ import { defaultWorkspaceAvatar } from '../utils';
* Public apis rate limit: 10 req/m
* Other rate limit: 120 req/m
*/
@UseGuards(CloudThrottlerGuard)
@Resolver(() => WorkspaceType)
export class WorkspaceResolver {
private readonly logger = new Logger(WorkspaceResolver.name);
@@ -191,28 +188,6 @@ export class WorkspaceResolver {
});
}
@Throttle({
default: {
limit: 10,
ttl: 30,
},
})
@Public()
@Query(() => WorkspaceType, {
description: 'Get public workspace by id',
})
async publicWorkspace(@Args('id') id: string) {
const workspace = await this.prisma.workspace.findUnique({
where: { id },
});
if (workspace?.public) {
return workspace;
}
throw new NotFoundException("Workspace doesn't exist");
}
@Query(() => WorkspaceType, {
description: 'Get workspace by id',
})
@@ -422,15 +397,10 @@ export class WorkspaceResolver {
}
}
@Throttle({
default: {
limit: 10,
ttl: 30,
},
})
@Throttle('strict')
@Public()
@Query(() => InvitationType, {
description: 'Update workspace',
description: 'send workspace invitation',
})
async getInviteInfo(@Args('inviteId') inviteId: string) {
const workspaceId = await this.prisma.workspaceUserPermission

View File

@@ -1,6 +1,6 @@
import { PrismaClient } from '@prisma/client';
import { Quotas } from '../../core/quota';
import { Quotas } from '../../core/quota/schema';
import { upgradeQuotaVersion } from './utils/user-quotas';
export class NewFreePlan1705395933447 {

View File

@@ -1,6 +1,6 @@
import { PrismaClient } from '@prisma/client';
import { Quotas } from '../../core/quota';
import { Quotas } from '../../core/quota/schema';
import { upgradeQuotaVersion } from './utils/user-quotas';
export class BusinessBlobLimit1706513866287 {

View File

@@ -0,0 +1,13 @@
import { PrismaClient } from '@prisma/client';
import { refreshPrompts } from './utils/prompts';
export class Prompts1712068777394 {
// do the migration
static async up(db: PrismaClient) {
await refreshPrompts(db);
}
// revert the migration
static async down(_db: PrismaClient) {}
}

View File

@@ -0,0 +1,19 @@
import { PrismaClient } from '@prisma/client';
import { QuotaType } from '../../core/quota/types';
import { upgradeLatestQuotaVersion } from './utils/user-quotas';
export class RefreshFreePlan1712224382221 {
// do the migration
static async up(db: PrismaClient) {
// free plan 1.1
await upgradeLatestQuotaVersion(
db,
QuotaType.FreePlanV1,
'free plan 1.1 migration'
);
}
// revert the migration
static async down(_db: PrismaClient) {}
}

View File

@@ -0,0 +1,23 @@
import { PrismaClient } from '@prisma/client';
import { QuotaType } from '../../core/quota/types';
import { upgradeLatestQuotaVersion } from './utils/user-quotas';
export class CopilotFeature1713164714634 {
// do the migration
static async up(db: PrismaClient) {
await upgradeLatestQuotaVersion(
db,
QuotaType.ProPlanV1,
'pro plan 1.1 migration'
);
await upgradeLatestQuotaVersion(
db,
QuotaType.RestrictedPlanV1,
'restricted plan 1.1 migration'
);
}
// revert the migration
static async down(_db: PrismaClient) {}
}

View File

@@ -0,0 +1,14 @@
import { PrismaClient } from '@prisma/client';
import { FeatureType } from '../../core/features';
import { upsertLatestFeatureVersion } from './utils/user-features';
export class AiEarlyAccess1713176777814 {
// do the migration
static async up(db: PrismaClient) {
await upsertLatestFeatureVersion(db, FeatureType.AIEarlyAccess);
}
// revert the migration
static async down(_db: PrismaClient) {}
}

View File

@@ -0,0 +1,13 @@
import { PrismaClient } from '@prisma/client';
import { refreshPrompts } from './utils/prompts';
export class RefreshPrompt1713185798895 {
// do the migration
static async up(db: PrismaClient) {
await refreshPrompts(db);
}
// revert the migration
static async down(_db: PrismaClient) {}
}

View File

@@ -0,0 +1,14 @@
import { PrismaClient } from '@prisma/client';
import { FeatureType } from '../../core/features';
import { upsertLatestFeatureVersion } from './utils/user-features';
export class UnlimitedCopilot1713285638427 {
// do the migration
static async up(db: PrismaClient) {
await upsertLatestFeatureVersion(db, FeatureType.UnlimitedCopilot);
}
// revert the migration
static async down(_db: PrismaClient) {}
}

View File

@@ -0,0 +1,13 @@
import { PrismaClient } from '@prisma/client';
import { refreshPrompts } from './utils/prompts';
export class UpdatePrompt1713522040090 {
// do the migration
static async up(db: PrismaClient) {
await refreshPrompts(db);
}
// revert the migration
static async down(_db: PrismaClient) {}
}

View File

@@ -0,0 +1,13 @@
import { PrismaClient } from '@prisma/client';
import { refreshPrompts } from './utils/prompts';
export class UpdatePrompts1713777617122 {
// do the migration
static async up(db: PrismaClient) {
await refreshPrompts(db);
}
// revert the migration
static async down(_db: PrismaClient) {}
}

View File

@@ -0,0 +1,13 @@
import { PrismaClient } from '@prisma/client';
import { refreshPrompts } from './utils/prompts';
export class UpdatePrompt1713864641056 {
// do the migration
static async up(db: PrismaClient) {
await refreshPrompts(db);
}
// revert the migration
static async down(_db: PrismaClient) {}
}

View File

@@ -0,0 +1,13 @@
import { PrismaClient } from '@prisma/client';
import { refreshPrompts } from './utils/prompts';
export class UpdatePrompts1714021969665 {
// do the migration
static async up(db: PrismaClient) {
await refreshPrompts(db);
}
// revert the migration
static async down(_db: PrismaClient) {}
}

View File

@@ -0,0 +1,627 @@
import { AiPromptRole, PrismaClient } from '@prisma/client';
type PromptMessage = {
role: AiPromptRole;
content: string;
params?: Record<string, string | string[]>;
};
type Prompt = {
name: string;
action?: string;
model: string;
messages: PromptMessage[];
};
export const prompts: Prompt[] = [
{
name: 'debug:chat:gpt4',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'system',
content:
'You are AFFiNE AI, a professional and humor copilot within AFFiNE. You are powered by latest GPT model from OpenAI and AFFiNE. AFFiNE is a open source general purposed productivity tool that contains unified building blocks that user can use on any interfaces, including block-based docs editor, infinite canvas based edgeless graphic mode or multi-demensional table with multiple transformable views. Your mission is always try the very best to assist user to use AFFiNE to write docs, draw diagrams or plan things with these abilities. You always think step-by-step and describe your plan for what to build with well-structured clear markdown, written out in great detail. Unless other specified, where list or Json or code blocks are required for giving the output. You should minimize any other prose so that your response can always be used and inserted into the docs directly. You are able to access to API of AFFiNE to finish your job. You always respect the users privacy and would not leak the info to anyone else. AFFiNE is made by Toeverything .Ltd, a company registered in Singapore with a diversed and international team. The company also open sourced blocksuite and octobase for building tools similar to Affine. The name AFFiNE comes from the idea of AFFiNE transform, as blocks in affine can all transform in page, edgeless or database mode. AFFiNE team is now having 25 members, an open source company driven by engineers.',
},
],
},
{
name: 'chat:gpt4',
model: 'gpt-4-vision-preview',
messages: [
{
role: 'system',
content:
'You are AFFiNE AI, a professional and humor copilot within AFFiNE. You are powered by latest GPT model from OpenAI and AFFiNE. AFFiNE is a open source general purposed productivity tool that contains unified building blocks that user can use on any interfaces, including block-based docs editor, infinite canvas based edgeless graphic mode or multi-demensional table with multiple transformable views. Your mission is always try the very best to assist user to use AFFiNE to write docs, draw diagrams or plan things with these abilities. You always think step-by-step and describe your plan for what to build with well-structured clear markdown, written out in great detail. Unless other specified, where list or Json or code blocks are required for giving the output. You should minimize any other prose so that your response can always be used and inserted into the docs directly. You are able to access to API of AFFiNE to finish your job. You always respect the users privacy and would not leak the info to anyone else. AFFiNE is made by Toeverything .Ltd, a company registered in Singapore with a diversed and international team. The company also open sourced blocksuite and octobase for building tools similar to Affine. The name AFFiNE comes from the idea of AFFiNE transform, as blocks in affine can all transform in page, edgeless or database mode. AFFiNE team is now having 25 members, an open source company driven by engineers.',
},
],
},
{
name: 'debug:action:gpt4',
action: 'text',
model: 'gpt-4-turbo-preview',
messages: [],
},
{
name: 'debug:action:vision4',
action: 'text',
model: 'gpt-4-vision-preview',
messages: [],
},
{
name: 'debug:action:dalle3',
action: 'image',
model: 'dall-e-3',
messages: [],
},
{
name: 'debug:action:fal-sd15',
action: 'image',
model: 'lcm-sd15-i2i',
messages: [],
},
{
name: 'debug:action:fal-sdturbo',
action: 'image',
model: 'fast-turbo-diffusion',
messages: [],
},
{
name: 'Summary',
action: 'Summary',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `Summarize the key points from the following content in a clear and concise manner, suitable for a reader who is seeking a quick understanding of the original content. Ensure to capture the main ideas and any significant details without unnecessary elaboration:
""""
{{content}}
""""`,
},
],
},
{
name: 'Summary the webpage',
action: 'Summary the webpage',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content:
'Summarize the insights from the following webpage content:\n\nFirst, provide a brief summary of the webpage content below. Then, list the insights derived from it, one by one.\n\n{{#links}}\n- {{.}}\n{{/links}}',
},
],
},
{
name: 'Explain this',
action: 'Explain this',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `Please analyze the following content and provide a brief summary and more detailed insights, with the insights listed in the form of an outline:
""""
{{content}}
""""
You can refer to this template:
""""
### Summary
your summary content here
### Insights
- Insight 1
- Insight 2
- Insight 3
""""`,
},
],
},
{
name: 'Explain this image',
action: 'Explain this image',
model: 'gpt-4-vision-preview',
messages: [
{
role: 'assistant',
content:
'Describe the scene captured in this image, focusing on the details, colors, emotions, and any interactions between subjects or objects present.\n\n{{image}}',
},
],
},
{
name: 'Explain this code',
action: 'Explain this code',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content:
'Analyze and explain the functionality of the following code snippet, highlighting its purpose, the logic behind its operations, and its potential output:\n\n{{code}}',
},
],
},
{
name: 'Translate to',
action: 'Translate',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `You are a translation expert, please translate the following content into {{language}}, and only perform the translation action, keeping the translated content in the same format as the original content:
""""
{{content}}
""""`,
params: {
language: [
'English',
'Spanish',
'German',
'French',
'Italian',
'Simplified Chinese',
'Traditional Chinese',
'Japanese',
'Russian',
'Korean',
],
},
},
],
},
{
name: 'Write an article about this',
action: 'Write an article about this',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `You are a good editor.
Please write an article based on the following content with reference to the rules given, and finally send only the written article to us
""""
{{content}}
""""
Rules to follow
1. Title: Craft an engaging and relevant title for the article that encapsulates the main theme.
2. Introduction: Start with an introductory paragraph that provides an overview of the topic and piques the readers interest.
3. Main Content:
• Include at least three key points about the subject matter that are informative and backed by credible sources.
• For each key point, provide analysis or insights that contribute to a deeper understanding of the topic.
• Make sure to maintain a flow and connection between the points to ensure the article is cohesive.
4. Conclusion: Write a concluding paragraph that summarizes the main points and offers a final thought or call to action for the readers.
5. Tone: The article should be written in a professional yet accessible tone, appropriate for an educated audience interested in the topic.`,
},
],
},
{
name: 'Write a twitter about this',
action: 'Write a twitter about this',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `You are a social media strategist with a flair for crafting engaging tweets. Please write a tweet based on the following content. The tweet must be concise, not exceeding 280 characters, and should be designed to capture attention and encourage sharing. Make sure it includes relevant hashtags and, if applicable, a call-to-action:
""""
{{content}}
""""`,
},
],
},
{
name: 'Write a poem about this',
action: 'Write a poem about this',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `You are an accomplished poet tasked with the creation of vivid and evocative verse. Please write a poem incorporating the following content into its narrative. Your poem should have a clear theme, employ rich imagery, and convey deep emotions. Make sure to structure the poem with attention to rhythm, meter, and where appropriate, rhyme scheme. Provide a title that encapsulates the essence of your poem:
""""
{{content}}
""""`,
},
],
},
{
name: 'Write a blog post about this',
action: 'Write a blog post about this',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `You are a creative blog writer specializing in producing captivating and informative content. Your task is to write a blog post based on the following content. The blog post should be between 500-700 words, engaging, and well-structured, with an inviting introduction that hooks the reader, concise and informative body paragraphs, and a compelling conclusion that encourages readers to engage with the content, whether it's through commenting, sharing, or exploring the topics further.
Please ensure the blog post is optimized for SEO with relevant keywords, includes at least 2-3 subheadings for better readability, and whenever possible, provides actionable insights or takeaways for the reader. Integrate a friendly and approachable tone throughout the post that reflects the voice of someone knowledgeable yet relatable.
Here is the content you need to base your blog post on:
""""
{{content}}
""""`,
},
],
},
{
name: 'Write outline',
action: 'Write outline',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content:
'Write an outline based on the following content, organizing the main points, subtopics, and structure:\n\n{{content}}',
},
],
},
{
name: 'Change tone to',
action: 'Change tone',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `You are an editor, please rewrite the following content in a {{tone}} tone. It is essential to retain the core meaning of the original content and send us only the rewritten version.
""""
{{content}}
""""`,
params: {
tone: [
'professional',
'informal',
'friendly',
'critical',
'humorous',
],
},
},
],
},
{
name: 'Brainstorm ideas about this',
action: 'Brainstorm ideas about this',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `You are an innovative thinker and brainstorming expert skilled at generating creative ideas. Your task is to help brainstorm various concepts, strategies, and approaches based on the following content. I am looking for original and actionable ideas that can be implemented. Please present your suggestions in a bulleted points format to clearly outline the different ideas. Ensure that each point is focused on potential development or implementation of the concept presented in the content provided. Heres the content for your brainstorming session:
""""
{{content}}
""""
Based on the information above, please provide a list of brainstormed ideas in the following format:
""""
- Idea 1: [Brief explanation]
- Idea 2: [Brief explanation]
- Idea 3: [Brief explanation]
- […]
""""
Remember, the focus is on creativity and practicality. Submit a range of diverse ideas that explore different angles and aspects of the content. `,
},
],
},
{
name: 'Brainstorm mindmap',
action: 'Brainstorm mindmap',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content:
'Use the nested unordered list syntax without other extra text style in Markdown to create a structure similar to a mind map without any unnecessary plain text description. Analyze the following questions or topics: \n\n{{content}}',
},
],
},
{
name: 'Expand mind map',
action: 'Expand mind map',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `An existing mind map is displayed as a markdown list:
{{mindmap}}.
Please expand the node “{{content}}", adding more essential details and subtopics to the existing mind map in the same markdown list format. Only output the expand part without the original mind map. No need to include any additional text or explanation`,
},
],
},
{
name: 'Improve writing for it',
action: 'Improve writing for it',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `You are an editor
Please rewrite the following content to enhance its clarity, coherence, and overall quality, ensuring that the message is effectively communicated and free of any grammatical errors. Provide a refined version that maintains the original intent but exhibits improved structure and readability
""""
{{content}}
""""`,
},
],
},
{
name: 'Improve grammar for it',
action: 'Improve grammar for it',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content:
'Please correct the grammar in the following content to ensure that it is free from any grammatical errors, maintaining proper sentence structure, correct tense usage, and accurate punctuation. Ensure that the final content is grammatically sound while preserving the original message:\n\n{{content}}',
},
],
},
{
name: 'Fix spelling for it',
action: 'Fix spelling for it',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `Please carefully check the following content, and correct all the spelling errors found, only carry out this operation. The standard for correcting errors is, Ensure that each word is spelled correctly, adhering to standard {{language}} spelling conventions, The content's meaning should remain unchanged, and retain the original format of the content. Finally, return the corrected content
""""
{{content}}
""""`,
params: {
language: [
'English',
'Spanish',
'German',
'French',
'Italian',
'Simplified Chinese',
'Traditional Chinese',
'Japanese',
'Russian',
'Korean',
],
},
},
],
},
{
name: 'Find action items from it',
action: 'Find action items from it',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `Please extract the items that can be used as tasks from the following content, and send them to me in the format provided by the template. The extracted items should cover as much of this content as possible:
""""
{{content}}
""""
If there are no items that can be used as to-do tasks, please reply with the following message:
""""
The current content does not have any items that can be listed as to-dos, please check again.
""""
If there are items in the content that can be used as to-do tasks, please refer to the template below:
""""
[] Todo 1
[] Todo 2
[] Todo 3
""""`,
},
],
},
{
name: 'Check code error',
action: 'Check code error',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content:
'Review the following code snippet for any syntax errors and list them individually:\n\n{{content}}',
},
],
},
{
name: 'Create a presentation',
action: 'Create a presentation',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content:
'I want to write a PPT, that has many pages, each page has 1 to 4 sections,\neach section has a title of no more than 30 words and no more than 500 words of content,\nbut also need some keywords that match the content of the paragraph used to generate images,\nTry to have a different number of section per page\nThe first page is the cover, which generates a general title (no more than 4 words) and description based on the topic\nthis is a template:\n- page name\n - title\n - keywords\n - description\n- page name\n - section name\n - keywords\n - content\n - section name\n - keywords\n - content\n- page name\n - section name\n - keywords\n - content\n - section name\n - keywords\n - content\n - section name\n - keywords\n - content\n- page name\n - section name\n - keywords\n - content\n - section name\n - keywords\n - content\n - section name\n - keywords\n - content\n - section name\n - keywords\n - content\n- page name\n - section name\n - keywords\n - content\n\n\nplease help me to write this ppt, do not output any content that does not belong to the ppt content itself outside of the content, Directly output the title content keywords without prefix like Title:xxx, Content: xxx, Keywords: xxx\nThe PPT is based on the following topics:\n\n{{content}}',
},
],
},
{
name: 'Create headings',
action: 'Create headings',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `You are an editor.
Please generate a title for the following content, no more than 20 words, and output in H1 format
""""
{{content}}
""""
The output format can refer to this template
""""
# Title content
""""`,
},
],
},
{
name: 'Make it real',
action: 'Make it real',
model: 'gpt-4-vision-preview',
messages: [
{
role: 'system',
content: `You are an expert web developer who specializes in building working website prototypes from low-fidelity wireframes.
Your job is to accept low-fidelity wireframes, then create a working prototype using HTML, CSS, and JavaScript, and finally send back the results.
The results should be a single HTML file.
Use tailwind to style the website.
Put any additional CSS styles in a style tag and any JavaScript in a script tag.
Use unpkg or skypack to import any required dependencies.
Use Google fonts to pull in any open source fonts you require.
If you have any images, load them from Unsplash or use solid colored rectangles.
The wireframes may include flow charts, diagrams, labels, arrows, sticky notes, and other features that should inform your work.
If there are screenshots or images, use them to inform the colors, fonts, and layout of your website.
Use your best judgement to determine whether what you see should be part of the user interface, or else is just an annotation.
Use what you know about applications and user experience to fill in any implicit business logic in the wireframes. Flesh it out, make it real!
The user may also provide you with the html of a previous design that they want you to iterate from.
In the wireframe, the previous design's html will appear as a white rectangle.
Use their notes, together with the previous design, to inform your next result.
Sometimes it's hard for you to read the writing in the wireframes.
For this reason, all text from the wireframes will be provided to you as a list of strings, separated by newlines.
Use the provided list of text from the wireframes as a reference if any text is hard to read.
You love your designers and want them to be happy. Incorporating their feedback and notes and producing working websites makes them happy.
When sent new wireframes, respond ONLY with the contents of the html file.
`,
},
],
},
{
name: 'Make it longer',
action: 'Make it longer',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `You are an editor, skilled in elaborating and adding detail to given texts without altering their core meaning.
Commands:
1. Carefully read the following content.
2. Maintain the original message or story.
3. Enhance the content by adding descriptive language, relevant details, and any necessary explanations to make it longer.
4. Ensure that the content remains coherent and the flow is natural.
5. Avoid repetitive or redundant information that does not contribute meaningful content or insight.
6. Use creative and engaging language to enrich the content and capture the readers interest.
7. Keep the expansion within a reasonable length to avoid over-elaboration.
Following content
""""
{{content}}
""""
Output: Generate a new version of the provided content that is longer in length due to the added details and descriptions. The expanded content should convey the same message as the original, but with more depth and richness to give the reader a fuller understanding or a more vivid picture of the topic discussed.`,
},
],
},
{
name: 'Make it shorter',
action: 'Make it shorter',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `You are a skilled editor with a talent for conciseness. Your task is to shorten the provided text without sacrificing its core meaning, ensuring the essence of the message remains clear and strong.
Commands:
1. Read the Following content carefully.
2. Identify the key points and main message within the content.
3. Rewrite the content in a more concise form, ensuring you preserve its essential meaning and main points.
4. Avoid using unnecessary words or phrases that do not contribute to the core message.
5. Ensure readability is maintained, with proper grammar and punctuation.
6. Present the shortened version as the final polished content.
Following content
""""
{{content}}
""""
Finally, you should present the final, shortened content as your response. Make sure it is a clear, well-structured version of the original, maintaining the integrity of the main ideas and information.`,
},
],
},
{
name: 'Continue writing',
action: 'Continue writing',
model: 'gpt-4-turbo-preview',
messages: [
{
role: 'assistant',
content: `You are an accomplished ghostwriter known for your ability to seamlessly continue narratives in the voice and style of the original author. You are tasked with extending a given story, maintaining the established tone, characters, and plot direction. Please read the following content carefully and continue writing the story. Your continuation should feel like an uninterrupted extension of the provided text. Aim for a smooth narrative flow and authenticity to the original context. Heres the content you need to continue:
""""
{{content}}
""""
When you craft your continuation, remember to:
- Immerse yourself in the role of the characters, ensuring their actions and dialogue remain true to their established personalities.
- Adhere to the pre-existing plot points, building upon them in a way that feels organic and plausible within the storys universe.
- Maintain the voice and style of the original text, making your writing indistinguishable from the initial content.
- Provide a natural progression of the story that adds depth and interest, guiding the reader to the next phase of the plot.
- Ensure your writing is compelling and keeps the reader eager to read on.
Finally, please only send us the content of your continuation.`,
},
],
},
];
export async function refreshPrompts(db: PrismaClient) {
for (const prompt of prompts) {
await db.aiPrompt.upsert({
create: {
name: prompt.name,
action: prompt.action,
model: prompt.model,
messages: {
create: prompt.messages.map((message, idx) => ({
idx,
role: message.role,
content: message.content,
params: message.params,
})),
},
},
where: { name: prompt.name },
update: {
action: prompt.action,
model: prompt.model,
messages: {
deleteMany: {},
create: prompt.messages.map((message, idx) => ({
idx,
role: message.role,
content: message.content,
params: message.params,
})),
},
},
});
}
}

View File

@@ -1,7 +1,8 @@
import { PrismaClient } from '@prisma/client';
import { FeatureKind } from '../../../core/features';
import { Quota } from '../../../core/quota/types';
import { getLatestQuota } from '../../../core/quota/schema';
import { Quota, QuotaType } from '../../../core/quota/types';
import { upsertFeature } from './user-features';
export async function upgradeQuotaVersion(
@@ -12,54 +13,77 @@ export async function upgradeQuotaVersion(
// add new quota
await upsertFeature(db, quota);
// migrate all users that using old quota to new quota
await db.$transaction(async tx => {
const latestQuotaVersion = await tx.features.findFirstOrThrow({
where: { feature: quota.feature },
orderBy: { version: 'desc' },
select: { id: true },
});
await db.$transaction(
async tx => {
const latestQuotaVersion = await tx.features.findFirstOrThrow({
where: { feature: quota.feature },
orderBy: { version: 'desc' },
select: { id: true },
});
// find all users that have old free plan
const userIds = await db.user.findMany({
where: {
features: {
every: {
feature: {
type: FeatureKind.Quota,
feature: quota.feature,
version: { lt: quota.version },
// find all users that have old free plan
const userIds = await tx.user.findMany({
where: {
features: {
some: {
feature: {
type: FeatureKind.Quota,
feature: quota.feature,
version: { lt: quota.version },
},
activated: true,
},
activated: true,
},
},
},
select: { id: true },
});
select: { id: true },
});
// deactivate all old quota for the user
await tx.userFeatures.updateMany({
where: {
id: undefined,
userId: {
in: userIds.map(({ id }) => id),
// deactivate all old quota for the user
await tx.userFeatures.updateMany({
where: {
id: undefined,
userId: {
in: userIds.map(({ id }) => id),
},
feature: {
type: FeatureKind.Quota,
},
activated: true,
},
feature: {
type: FeatureKind.Quota,
data: {
activated: false,
},
activated: true,
},
data: {
activated: false,
},
});
});
await tx.userFeatures.createMany({
data: userIds.map(({ id: userId }) => ({
userId,
featureId: latestQuotaVersion.id,
reason,
activated: true,
})),
});
});
await tx.userFeatures.createMany({
data: userIds.map(({ id: userId }) => ({
userId,
featureId: latestQuotaVersion.id,
reason,
activated: true,
})),
});
},
{
maxWait: 10000,
timeout: 20000,
}
);
}
export async function upsertLatestQuotaVersion(
db: PrismaClient,
type: QuotaType
) {
const latestQuota = getLatestQuota(type);
await upsertFeature(db, latestQuota);
}
export async function upgradeLatestQuotaVersion(
db: PrismaClient,
type: QuotaType,
reason: string
) {
const latestQuota = getLatestQuota(type);
await upgradeQuotaVersion(db, latestQuota, reason);
}

View File

@@ -1,11 +1,12 @@
import { Global, Module } from '@nestjs/common';
import { Cache, SessionCache } from './instances';
import { CacheInterceptor } from './interceptor';
@Global()
@Module({
providers: [Cache, SessionCache],
exports: [Cache, SessionCache],
providers: [Cache, SessionCache, CacheInterceptor],
exports: [Cache, SessionCache, CacheInterceptor],
})
export class CacheModule {}
export { Cache, SessionCache };

View File

@@ -2,7 +2,6 @@ import type { ApolloDriverConfig } from '@nestjs/apollo';
import SMTPTransport from 'nodemailer/lib/smtp-transport';
import type { LeafPaths } from '../utils/types';
import { EnvConfigType } from './env';
import type { AFFiNEStorageConfig } from './storage';
declare global {
@@ -13,6 +12,7 @@ declare global {
}
}
export type EnvConfigType = 'string' | 'int' | 'float' | 'boolean';
export type ServerFlavor = 'allinone' | 'graphql' | 'sync';
export type AFFINE_ENV = 'dev' | 'beta' | 'production';
export type NODE_ENV = 'development' | 'test' | 'production';
@@ -240,6 +240,13 @@ export interface AFFiNEConfig {
* @default 15 days
*/
ttl: number;
/**
* Application auth time to refresh in seconds
*
* @default 7 days
*/
ttr: number;
};
/**

View File

@@ -153,6 +153,7 @@ export const getDefaultAFFiNEConfig: () => AFFiNEConfig = () => {
},
session: {
ttl: 15 * ONE_DAY_IN_SEC,
ttr: 7 * ONE_DAY_IN_SEC,
},
accessToken: {
ttl: 7 * ONE_DAY_IN_SEC,
@@ -188,7 +189,7 @@ export const getDefaultAFFiNEConfig: () => AFFiNEConfig = () => {
enabled: false,
},
telemetry: {
enabled: isSelfhosted && !process.env.DISABLE_SERVER_TELEMETRY,
enabled: isSelfhosted,
token: '389c0615a69b57cca7d3fa0a4824c930',
},
plugins: {

View File

@@ -1,8 +1,7 @@
import { set } from 'lodash-es';
import type { AFFiNEConfig } from './def';
import type { AFFiNEConfig, EnvConfigType } from './def';
export type EnvConfigType = 'string' | 'int' | 'float' | 'boolean';
/**
* parse number value from environment variables
*/

View File

@@ -19,6 +19,7 @@ export type StorageConfig<Ext = unknown> = {
export interface StoragesConfig {
avatar: StorageConfig<{ publicLinkFactory: (key: string) => string }>;
blob: StorageConfig;
copilot: StorageConfig;
}
export interface AFFiNEStorageConfig {
@@ -51,6 +52,10 @@ export function getDefaultAFFiNEStorageConfig(): AFFiNEStorageConfig {
provider: 'fs',
bucket: 'blobs',
},
copilot: {
provider: 'fs',
bucket: 'copilot',
},
},
};
}

View File

@@ -27,7 +27,7 @@ export {
export type { PrismaTransaction } from './prisma';
export * from './storage';
export { type StorageProvider, StorageProviderFactory } from './storage';
export { AuthThrottlerGuard, CloudThrottlerGuard, Throttle } from './throttler';
export { CloudThrottlerGuard, SkipThrottle, Throttle } from './throttler';
export {
getRequestFromHost,
getRequestResponseFromContext,

View File

@@ -0,0 +1,39 @@
import { applyDecorators, SetMetadata } from '@nestjs/common';
import { SkipThrottle, Throttle as RawThrottle } from '@nestjs/throttler';
export type Throttlers = 'default' | 'strict' | 'authenticated';
export const THROTTLER_PROTECTED = 'affine_throttler:protected';
/**
* Choose what throttler to use
*
* If a Controller or Query do not protected behind a Throttler,
* it will never be rate limited.
*
* - default: 120 calls within 60 seconds
* - strict: 10 calls within 60 seconds
* - authenticated: no rate limit for authenticated users, apply [default] throttler for unauthenticated users
*
* @example
*
* \@Throttle()
* \@Throttle('strict')
*
* // the config call be override by the second parameter,
* // and the call count will be calculated separately
* \@Throttle('default', { limit: 10, ttl: 10 })
*
*/
export function Throttle(
type: Throttlers = 'default',
override: { limit?: number; ttl?: number } = {}
): MethodDecorator & ClassDecorator {
return applyDecorators(
SetMetadata(THROTTLER_PROTECTED, type),
RawThrottle({
[type]: override,
})
);
}
export { SkipThrottle };

View File

@@ -1,15 +1,20 @@
import { ExecutionContext, Global, Injectable, Module } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import {
Throttle,
InjectThrottlerOptions,
InjectThrottlerStorage,
ThrottlerGuard,
ThrottlerModule,
ThrottlerModuleOptions,
type ThrottlerModuleOptions,
ThrottlerOptions,
ThrottlerOptionsFactory,
ThrottlerStorageService,
} from '@nestjs/throttler';
import type { Request } from 'express';
import { Config } from '../config';
import { getRequestResponseFromContext } from '../utils/request';
import { THROTTLER_PROTECTED, Throttlers } from './decorators';
@Injectable()
export class ThrottlerStorage extends ThrottlerStorageService {}
@@ -25,13 +30,16 @@ class CustomOptionsFactory implements ThrottlerOptionsFactory {
const options: ThrottlerModuleOptions = {
throttlers: [
{
name: 'default',
ttl: this.config.rateLimiter.ttl * 1000,
limit: this.config.rateLimiter.limit,
},
{
name: 'strict',
ttl: this.config.rateLimiter.ttl * 1000,
limit: 20,
},
],
skipIf: () => {
return !this.config.node.prod || this.config.affine.canary;
},
storage: this.storage,
};
@@ -39,6 +47,134 @@ class CustomOptionsFactory implements ThrottlerOptionsFactory {
}
}
@Injectable()
export class CloudThrottlerGuard extends ThrottlerGuard {
constructor(
@InjectThrottlerOptions() options: ThrottlerModuleOptions,
@InjectThrottlerStorage() storageService: ThrottlerStorage,
reflector: Reflector,
private readonly config: Config
) {
super(options, storageService, reflector);
}
override getRequestResponse(context: ExecutionContext) {
return getRequestResponseFromContext(context) as any;
}
override getTracker(req: Request): Promise<string> {
return Promise.resolve(
// ↓ prefer session id if available
`throttler:${req.sid ?? req.get('CF-Connecting-IP') ?? req.get('CF-ray') ?? req.ip}`
// ^ throttler prefix make the key in store recognizable
);
}
override generateKey(
context: ExecutionContext,
tracker: string,
throttler: string
) {
if (tracker.endsWith(';custom')) {
return `${tracker};${throttler}:${context.getClass().name}.${context.getHandler().name}`;
}
return `${tracker};${throttler}`;
}
override async handleRequest(
context: ExecutionContext,
limit: number,
ttl: number,
throttlerOptions: ThrottlerOptions
) {
// give it 'default' if no throttler is specified,
// so the unauthenticated users visits will always hit default throttler
// authenticated users will directly bypass unprotected APIs in [CloudThrottlerGuard.canActivate]
const throttler = this.getSpecifiedThrottler(context) ?? 'default';
// by pass unmatched throttlers
if (throttlerOptions.name !== throttler) {
return true;
}
const { req, res } = this.getRequestResponse(context);
const ignoreUserAgents =
throttlerOptions.ignoreUserAgents ?? this.commonOptions.ignoreUserAgents;
if (Array.isArray(ignoreUserAgents)) {
for (const pattern of ignoreUserAgents) {
const ua = req.headers['user-agent'];
if (ua && pattern.test(ua)) {
return true;
}
}
}
let tracker = await this.getTracker(req);
if (this.config.node.dev) {
limit = Number.MAX_SAFE_INTEGER;
} else {
// custom limit or ttl APIs will be treated standalone
if (limit !== throttlerOptions.limit || ttl !== throttlerOptions.ttl) {
tracker += ';custom';
}
}
const key = this.generateKey(
context,
tracker,
throttlerOptions.name ?? 'default'
);
const { timeToExpire, totalHits } = await this.storageService.increment(
key,
ttl
);
if (totalHits > limit) {
res.header('Retry-After', timeToExpire.toString());
await this.throwThrottlingException(context, {
limit,
ttl,
key,
tracker,
totalHits,
timeToExpire,
});
}
res.header(`${this.headerPrefix}-Limit`, limit.toString());
res.header(
`${this.headerPrefix}-Remaining`,
(limit - totalHits).toString()
);
res.header(`${this.headerPrefix}-Reset`, timeToExpire.toString());
return true;
}
override async canActivate(context: ExecutionContext): Promise<boolean> {
const { req } = this.getRequestResponse(context);
const throttler = this.getSpecifiedThrottler(context);
// if user is logged in, bypass non-protected handlers
if (!throttler && req.user) {
return true;
}
return super.canActivate(context);
}
getSpecifiedThrottler(context: ExecutionContext) {
const throttler = this.reflector.getAllAndOverride<Throttlers | undefined>(
THROTTLER_PROTECTED,
[context.getHandler(), context.getClass()]
);
return throttler === 'authenticated' ? undefined : throttler;
}
}
@Global()
@Module({
imports: [
@@ -46,46 +182,9 @@ class CustomOptionsFactory implements ThrottlerOptionsFactory {
useClass: CustomOptionsFactory,
}),
],
providers: [ThrottlerStorage],
exports: [ThrottlerStorage],
providers: [ThrottlerStorage, CloudThrottlerGuard],
exports: [ThrottlerStorage, CloudThrottlerGuard],
})
export class RateLimiterModule {}
@Injectable()
export class CloudThrottlerGuard extends ThrottlerGuard {
override getRequestResponse(context: ExecutionContext) {
return getRequestResponseFromContext(context) as any;
}
protected override getTracker(req: Record<string, any>): Promise<string> {
return Promise.resolve(
req?.get('CF-Connecting-IP') ?? req?.get('CF-ray') ?? req?.ip
);
}
}
@Injectable()
export class AuthThrottlerGuard extends CloudThrottlerGuard {
override async handleRequest(
context: ExecutionContext,
limit: number,
ttl: number
): Promise<boolean> {
const { req } = this.getRequestResponse(context);
if (req?.url === '/api/auth/session') {
// relax throttle for session auto renew
return super.handleRequest(context, limit * 20, ttl, {
ttl: ttl * 20,
limit: limit * 20,
});
}
return super.handleRequest(context, limit, ttl, {
ttl,
limit,
});
}
}
export { Throttle };
export * from './decorators';

View File

@@ -26,7 +26,7 @@ export function getRequestResponseFromHost(host: ArgumentsHost) {
const ws = host.switchToWs();
const req = ws.getClient<Socket>().client.conn.request as Request;
const cookieStr = req?.headers?.cookie;
const cookieStr = req?.headers?.cookie ?? '';
// patch cookies to match auth guard logic
if (typeof cookieStr === 'string') {
req.cookies = cookieStr.split(';').reduce(

View File

@@ -1,6 +1,7 @@
declare namespace Express {
interface Request {
user?: import('./core/auth/current-user').CurrentUser;
sid?: string;
}
}

View File

@@ -1,3 +1,4 @@
import { CopilotConfig } from './copilot';
import { GCloudConfig } from './gcloud/config';
import { OAuthConfig } from './oauth';
import { PaymentConfig } from './payment';
@@ -6,6 +7,7 @@ import { R2StorageConfig, S3StorageConfig } from './storage';
declare module '../fundamentals/config' {
interface PluginsConfig {
readonly copilot: CopilotConfig;
readonly payment: PaymentConfig;
readonly redis: RedisOptions;
readonly gcloud: GCloudConfig;

View File

@@ -0,0 +1,352 @@
import {
BadRequestException,
Controller,
Get,
HttpException,
InternalServerErrorException,
Logger,
NotFoundException,
Param,
Query,
Req,
Res,
Sse,
} from '@nestjs/common';
import type { Request, Response } from 'express';
import {
catchError,
concatMap,
connect,
EMPTY,
from,
map,
merge,
mergeMap,
Observable,
of,
switchMap,
toArray,
} from 'rxjs';
import { Public } from '../../core/auth';
import { CurrentUser } from '../../core/auth/current-user';
import { Config } from '../../fundamentals';
import { CopilotProviderService } from './providers';
import { ChatSession, ChatSessionService } from './session';
import { CopilotStorage } from './storage';
import { CopilotCapability } from './types';
export interface ChatEvent {
type: 'attachment' | 'message' | 'error';
id?: string;
data: string;
}
@Controller('/api/copilot')
export class CopilotController {
private readonly logger = new Logger(CopilotController.name);
constructor(
private readonly config: Config,
private readonly chatSession: ChatSessionService,
private readonly provider: CopilotProviderService,
private readonly storage: CopilotStorage
) {}
private async hasAttachment(sessionId: string, messageId: string) {
const session = await this.chatSession.get(sessionId);
if (!session) {
throw new BadRequestException('Session not found');
}
const message = await session.getMessageById(messageId);
if (Array.isArray(message.attachments) && message.attachments.length) {
return true;
}
return false;
}
private async appendSessionMessage(
sessionId: string,
messageId: string
): Promise<ChatSession> {
const session = await this.chatSession.get(sessionId);
if (!session) {
throw new BadRequestException('Session not found');
}
await session.pushByMessageId(messageId);
return session;
}
private getSignal(req: Request) {
const controller = new AbortController();
req.on('close', () => controller.abort());
return controller.signal;
}
private handleError(err: any) {
if (err instanceof Error) {
const ret = {
message: err.message,
status: (err as any).status,
};
if (err instanceof HttpException) {
ret.status = err.getStatus();
}
}
return err;
}
@Get('/chat/:sessionId')
async chat(
@CurrentUser() user: CurrentUser,
@Req() req: Request,
@Param('sessionId') sessionId: string,
@Query('messageId') messageId: string,
@Query() params: Record<string, string | string[]>
): Promise<string> {
await this.chatSession.checkQuota(user.id);
const model = await this.chatSession.get(sessionId).then(s => s?.model);
const provider = this.provider.getProviderByCapability(
CopilotCapability.TextToText,
model
);
if (!provider) {
throw new InternalServerErrorException('No provider available');
}
const session = await this.appendSessionMessage(sessionId, messageId);
try {
delete params.messageId;
const content = await provider.generateText(
session.finish(params),
session.model,
{
signal: this.getSignal(req),
user: user.id,
}
);
session.push({
role: 'assistant',
content,
createdAt: new Date(),
});
await session.save();
return content;
} catch (e: any) {
throw new InternalServerErrorException(
e.message || "Couldn't generate text"
);
}
}
@Sse('/chat/:sessionId/stream')
async chatStream(
@CurrentUser() user: CurrentUser,
@Req() req: Request,
@Param('sessionId') sessionId: string,
@Query('messageId') messageId: string,
@Query() params: Record<string, string>
): Promise<Observable<ChatEvent>> {
try {
await this.chatSession.checkQuota(user.id);
} catch (err) {
return of({
type: 'error' as const,
data: this.handleError(err),
});
}
const model = await this.chatSession.get(sessionId).then(s => s?.model);
const provider = this.provider.getProviderByCapability(
CopilotCapability.TextToText,
model
);
if (!provider) {
throw new InternalServerErrorException('No provider available');
}
const session = await this.appendSessionMessage(sessionId, messageId);
delete params.messageId;
return from(
provider.generateTextStream(session.finish(params), session.model, {
signal: this.getSignal(req),
user: user.id,
})
).pipe(
connect(shared$ =>
merge(
// actual chat event stream
shared$.pipe(
map(data => ({ type: 'message' as const, id: sessionId, data }))
),
// save the generated text to the session
shared$.pipe(
toArray(),
concatMap(values => {
session.push({
role: 'assistant',
content: values.join(''),
createdAt: new Date(),
});
return from(session.save());
}),
switchMap(() => EMPTY)
)
)
),
catchError(err =>
of({
type: 'error' as const,
data: this.handleError(err),
})
)
);
}
@Sse('/chat/:sessionId/images')
async chatImagesStream(
@CurrentUser() user: CurrentUser,
@Req() req: Request,
@Param('sessionId') sessionId: string,
@Query('messageId') messageId: string,
@Query() params: Record<string, string>
): Promise<Observable<ChatEvent>> {
try {
await this.chatSession.checkQuota(user.id);
} catch (err) {
return of({
type: 'error' as const,
data: this.handleError(err),
});
}
const hasAttachment = await this.hasAttachment(sessionId, messageId);
const model = await this.chatSession.get(sessionId).then(s => s?.model);
const provider = this.provider.getProviderByCapability(
hasAttachment
? CopilotCapability.ImageToImage
: CopilotCapability.TextToImage,
model
);
if (!provider) {
throw new InternalServerErrorException('No provider available');
}
const session = await this.appendSessionMessage(sessionId, messageId);
delete params.messageId;
const handleRemoteLink = this.storage.handleRemoteLink.bind(
this.storage,
user.id,
sessionId
);
return from(
provider.generateImagesStream(session.finish(params), session.model, {
signal: this.getSignal(req),
user: user.id,
})
).pipe(
mergeMap(handleRemoteLink),
connect(shared$ =>
merge(
// actual chat event stream
shared$.pipe(
map(attachment => ({
type: 'attachment' as const,
id: sessionId,
data: attachment,
}))
),
// save the generated text to the session
shared$.pipe(
toArray(),
concatMap(attachments => {
session.push({
role: 'assistant',
content: '',
attachments: attachments,
createdAt: new Date(),
});
return from(session.save());
}),
switchMap(() => EMPTY)
)
)
),
catchError(err =>
of({
type: 'error' as const,
data: this.handleError(err),
})
)
);
}
@Get('/unsplash/photos')
async unsplashPhotos(
@Req() req: Request,
@Res() res: Response,
@Query() params: Record<string, string>
) {
const { unsplashKey } = this.config.plugins.copilot || {};
if (!unsplashKey) {
throw new InternalServerErrorException('Unsplash key is not configured');
}
const query = new URLSearchParams(params);
const response = await fetch(
`https://api.unsplash.com/search/photos?${query}`,
{
headers: { Authorization: `Client-ID ${unsplashKey}` },
signal: this.getSignal(req),
}
);
res.set({
'Content-Type': response.headers.get('Content-Type'),
'Content-Length': response.headers.get('Content-Length'),
'X-Ratelimit-Limit': response.headers.get('X-Ratelimit-Limit'),
'X-Ratelimit-Remaining': response.headers.get('X-Ratelimit-Remaining'),
});
res.status(response.status).send(await response.json());
}
@Public()
@Get('/blob/:userId/:workspaceId/:key')
async getBlob(
@Res() res: Response,
@Param('userId') userId: string,
@Param('workspaceId') workspaceId: string,
@Param('key') key: string
) {
const { body, metadata } = await this.storage.get(userId, workspaceId, key);
if (!body) {
throw new NotFoundException(
`Blob not found in ${userId}'s workspace ${workspaceId}: ${key}`
);
}
// metadata should always exists if body is not null
if (metadata) {
res.setHeader('content-type', metadata.contentType);
res.setHeader('last-modified', metadata.lastModified.toUTCString());
res.setHeader('content-length', metadata.contentLength);
} else {
this.logger.warn(`Blob ${workspaceId}/${key} has no metadata`);
}
res.setHeader('cache-control', 'public, max-age=2592000, immutable');
body.pipe(res);
}
}

View File

@@ -0,0 +1,47 @@
import { ServerFeature } from '../../core/config';
import { FeatureModule } from '../../core/features';
import { QuotaModule } from '../../core/quota';
import { PermissionService } from '../../core/workspaces/permission';
import { Plugin } from '../registry';
import { CopilotController } from './controller';
import { ChatMessageCache } from './message';
import { PromptService } from './prompt';
import {
assertProvidersConfigs,
CopilotProviderService,
FalProvider,
OpenAIProvider,
registerCopilotProvider,
} from './providers';
import { CopilotResolver, UserCopilotResolver } from './resolver';
import { ChatSessionService } from './session';
import { CopilotStorage } from './storage';
registerCopilotProvider(FalProvider);
registerCopilotProvider(OpenAIProvider);
@Plugin({
name: 'copilot',
imports: [FeatureModule, QuotaModule],
providers: [
PermissionService,
ChatSessionService,
CopilotResolver,
ChatMessageCache,
UserCopilotResolver,
PromptService,
CopilotProviderService,
CopilotStorage,
],
controllers: [CopilotController],
contributesTo: ServerFeature.Copilot,
if: config => {
if (config.flavor.graphql) {
return assertProvidersConfigs(config);
}
return false;
},
})
export class CopilotModule {}
export type { CopilotConfig } from './types';

View File

@@ -0,0 +1,35 @@
import { randomUUID } from 'node:crypto';
import { Injectable, Logger } from '@nestjs/common';
import { SessionCache } from '../../fundamentals';
import { SubmittedMessage, SubmittedMessageSchema } from './types';
const CHAT_MESSAGE_KEY = 'chat-message';
const CHAT_MESSAGE_TTL = 3600 * 1 * 1000; // 1 hours
@Injectable()
export class ChatMessageCache {
private readonly logger = new Logger(ChatMessageCache.name);
constructor(private readonly cache: SessionCache) {}
async get(id: string): Promise<SubmittedMessage | undefined> {
return await this.cache.get(`${CHAT_MESSAGE_KEY}:${id}`);
}
async set(message: SubmittedMessage): Promise<string | undefined> {
try {
const parsed = SubmittedMessageSchema.safeParse(message);
if (parsed.success) {
const id = randomUUID();
await this.cache.set(`${CHAT_MESSAGE_KEY}:${id}`, parsed.data, {
ttl: CHAT_MESSAGE_TTL,
});
return id;
}
} catch (e: any) {
this.logger.error(`Failed to get chat message from cache: ${e.message}`);
}
return undefined;
}
}

View File

@@ -0,0 +1,240 @@
import { Injectable, Logger } from '@nestjs/common';
import { AiPrompt, PrismaClient } from '@prisma/client';
import Mustache from 'mustache';
import { Tiktoken } from 'tiktoken';
import {
getTokenEncoder,
PromptMessage,
PromptMessageSchema,
PromptParams,
} from './types';
// disable escaping
Mustache.escape = (text: string) => text;
function extractMustacheParams(template: string) {
const regex = /\{\{\s*([^{}]+)\s*\}\}/g;
const params = [];
let match;
while ((match = regex.exec(template)) !== null) {
params.push(match[1]);
}
return Array.from(new Set(params));
}
export class ChatPrompt {
private readonly logger = new Logger(ChatPrompt.name);
public readonly encoder?: Tiktoken;
private readonly promptTokenSize: number;
private readonly templateParamKeys: string[] = [];
private readonly templateParams: PromptParams = {};
static createFromPrompt(
options: Omit<AiPrompt, 'id' | 'createdAt'> & {
messages: PromptMessage[];
}
) {
return new ChatPrompt(
options.name,
options.action || undefined,
options.model || undefined,
options.messages
);
}
constructor(
public readonly name: string,
public readonly action: string | undefined,
public readonly model: string | undefined,
private readonly messages: PromptMessage[]
) {
this.encoder = getTokenEncoder(model);
this.promptTokenSize =
this.encoder?.encode_ordinary(messages.map(m => m.content).join('') || '')
.length || 0;
this.templateParamKeys = extractMustacheParams(
messages.map(m => m.content).join('')
);
this.templateParams = messages.reduce(
(acc, m) => Object.assign(acc, m.params),
{} as PromptParams
);
}
/**
* get prompt token size
*/
get tokens() {
return this.promptTokenSize;
}
/**
* get prompt param keys in template
*/
get paramKeys() {
return this.templateParamKeys.slice();
}
/**
* get prompt params
*/
get params() {
return { ...this.templateParams };
}
encode(message: string) {
return this.encoder?.encode_ordinary(message).length || 0;
}
private checkParams(params: PromptParams, sessionId?: string) {
const selfParams = this.templateParams;
for (const key of Object.keys(selfParams)) {
const options = selfParams[key];
const income = params[key];
if (
typeof income !== 'string' ||
(Array.isArray(options) && !options.includes(income))
) {
if (sessionId) {
const prefix = income
? `Invalid param value: ${key}=${income}`
: `Missing param value: ${key}`;
this.logger.warn(
`${prefix} in session ${sessionId}, use default options: ${options[0]}`
);
}
if (Array.isArray(options)) {
// use the first option if income is not in options
params[key] = options[0];
} else {
params[key] = options;
}
}
}
}
/**
* render prompt messages with params
* @param params record of params, e.g. { name: 'Alice' }
* @returns e.g. [{ role: 'system', content: 'Hello, {{name}}' }] => [{ role: 'system', content: 'Hello, Alice' }]
*/
finish(params: PromptParams, sessionId?: string): PromptMessage[] {
this.checkParams(params, sessionId);
return this.messages.map(({ content, params: _, ...rest }) => ({
...rest,
params,
content: Mustache.render(content, params),
}));
}
free() {
this.encoder?.free();
}
}
@Injectable()
export class PromptService {
private readonly cache = new Map<string, ChatPrompt>();
constructor(private readonly db: PrismaClient) {}
/**
* list prompt names
* @returns prompt names
*/
async list() {
return this.db.aiPrompt
.findMany({ select: { name: true } })
.then(prompts => Array.from(new Set(prompts.map(p => p.name))));
}
/**
* get prompt messages by prompt name
* @param name prompt name
* @returns prompt messages
*/
async get(name: string): Promise<ChatPrompt | null> {
const cached = this.cache.get(name);
if (cached) return cached;
const prompt = await this.db.aiPrompt.findUnique({
where: {
name,
},
select: {
name: true,
action: true,
model: true,
messages: {
select: {
role: true,
content: true,
params: true,
},
orderBy: {
idx: 'asc',
},
},
},
});
const messages = PromptMessageSchema.array().safeParse(prompt?.messages);
if (prompt && messages.success) {
const chatPrompt = ChatPrompt.createFromPrompt({
...prompt,
messages: messages.data,
});
this.cache.set(name, chatPrompt);
return chatPrompt;
}
return null;
}
async set(name: string, messages: PromptMessage[]) {
return await this.db.aiPrompt
.create({
data: {
name,
messages: {
create: messages.map((m, idx) => ({
idx,
...m,
attachments: m.attachments || undefined,
params: m.params || undefined,
})),
},
},
})
.then(ret => ret.id);
}
async update(name: string, messages: PromptMessage[]) {
const { id } = await this.db.aiPrompt.update({
where: { name },
data: {
messages: {
// cleanup old messages
deleteMany: {},
create: messages.map((m, idx) => ({
idx,
...m,
attachments: m.attachments || undefined,
params: m.params || undefined,
})),
},
},
});
this.cache.delete(name);
return id;
}
async delete(name: string) {
const { id } = await this.db.aiPrompt.delete({ where: { name } });
this.cache.delete(name);
return id;
}
}

View File

@@ -0,0 +1,109 @@
import assert from 'node:assert';
import {
CopilotCapability,
CopilotImageToImageProvider,
CopilotProviderType,
CopilotTextToImageProvider,
PromptMessage,
} from '../types';
export type FalConfig = {
apiKey: string;
};
export type FalResponse = {
detail: Array<{ msg: string }>;
images: Array<{ url: string }>;
};
export class FalProvider
implements CopilotTextToImageProvider, CopilotImageToImageProvider
{
static readonly type = CopilotProviderType.FAL;
static readonly capabilities = [
CopilotCapability.TextToImage,
CopilotCapability.ImageToImage,
];
readonly availableModels = [
// text to image
'fast-turbo-diffusion',
// image to image
'lcm-sd15-i2i',
];
constructor(private readonly config: FalConfig) {
assert(FalProvider.assetsConfig(config));
}
static assetsConfig(config: FalConfig) {
return !!config.apiKey;
}
getCapabilities(): CopilotCapability[] {
return FalProvider.capabilities;
}
isModelAvailable(model: string): boolean {
return this.availableModels.includes(model);
}
// ====== image to image ======
async generateImages(
messages: PromptMessage[],
model: string = this.availableModels[0],
options: {
signal?: AbortSignal;
user?: string;
} = {}
): Promise<Array<string>> {
const { content, attachments } = messages.pop() || {};
if (!this.availableModels.includes(model)) {
throw new Error(`Invalid model: ${model}`);
}
// prompt attachments require at least one
if (!content && (!Array.isArray(attachments) || !attachments.length)) {
throw new Error('Prompt or Attachments is empty');
}
const data = (await fetch(`https://fal.run/fal-ai/${model}`, {
method: 'POST',
headers: {
Authorization: `key ${this.config.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
image_url: attachments?.[0],
prompt: content,
sync_mode: true,
seed: 42,
enable_safety_checks: false,
}),
signal: options.signal,
}).then(res => res.json())) as FalResponse;
if (!data.images?.length) {
const error = data.detail?.[0]?.msg;
throw new Error(
error ? `Invalid message: ${error}` : 'No images generated'
);
}
return data.images?.map(image => image.url) || [];
}
async *generateImagesStream(
messages: PromptMessage[],
model: string = this.availableModels[0],
options: {
signal?: AbortSignal;
user?: string;
} = {}
): AsyncIterable<string> {
const ret = await this.generateImages(messages, model, options);
for (const url of ret) {
yield url;
}
}
}

View File

@@ -0,0 +1,157 @@
import assert from 'node:assert';
import { Injectable, Logger } from '@nestjs/common';
import { Config } from '../../../fundamentals';
import {
CapabilityToCopilotProvider,
CopilotCapability,
CopilotConfig,
CopilotProvider,
CopilotProviderType,
} from '../types';
type CopilotProviderConfig = CopilotConfig[keyof CopilotConfig];
interface CopilotProviderDefinition<C extends CopilotProviderConfig> {
// constructor signature
new (config: C): CopilotProvider;
// type of the provider
readonly type: CopilotProviderType;
// capabilities of the provider, like text to text, text to image, etc.
readonly capabilities: CopilotCapability[];
// asserts that the config is valid for this provider
assetsConfig(config: C): boolean;
}
// registered provider factory
const COPILOT_PROVIDER = new Map<
CopilotProviderType,
(config: Config, logger: Logger) => CopilotProvider
>();
// map of capabilities to providers
const PROVIDER_CAPABILITY_MAP = new Map<
CopilotCapability,
CopilotProviderType[]
>();
// config assertions for providers
const ASSERT_CONFIG = new Map<CopilotProviderType, (config: Config) => void>();
export function registerCopilotProvider<
C extends CopilotProviderConfig = CopilotProviderConfig,
>(provider: CopilotProviderDefinition<C>) {
const type = provider.type;
const factory = (config: Config, logger: Logger) => {
const providerConfig = config.plugins.copilot?.[type];
if (!provider.assetsConfig(providerConfig as C)) {
throw new Error(
`Invalid configuration for copilot provider ${type}: ${providerConfig}`
);
}
const instance = new provider(providerConfig as C);
logger.log(
`Copilot provider ${type} registered, capabilities: ${provider.capabilities.join(', ')}`
);
return instance;
};
// register the provider
COPILOT_PROVIDER.set(type, factory);
// register the provider capabilities
for (const capability of provider.capabilities) {
const providers = PROVIDER_CAPABILITY_MAP.get(capability) || [];
if (!providers.includes(type)) {
providers.push(type);
}
PROVIDER_CAPABILITY_MAP.set(capability, providers);
}
// register the provider config assertion
ASSERT_CONFIG.set(type, (config: Config) => {
assert(config.plugins.copilot);
const providerConfig = config.plugins.copilot[type];
if (!providerConfig) return false;
return provider.assetsConfig(providerConfig as C);
});
}
/// Asserts that the config is valid for any registered providers
export function assertProvidersConfigs(config: Config) {
return (
Array.from(ASSERT_CONFIG.values()).findIndex(assertConfig =>
assertConfig(config)
) !== -1
);
}
@Injectable()
export class CopilotProviderService {
private readonly logger = new Logger(CopilotProviderService.name);
constructor(private readonly config: Config) {}
private readonly cachedProviders = new Map<
CopilotProviderType,
CopilotProvider
>();
private create(provider: CopilotProviderType): CopilotProvider {
assert(this.config.plugins.copilot);
const providerFactory = COPILOT_PROVIDER.get(provider);
if (!providerFactory) {
throw new Error(`Unknown copilot provider type: ${provider}`);
}
return providerFactory(this.config, this.logger);
}
getProvider(provider: CopilotProviderType): CopilotProvider {
if (!this.cachedProviders.has(provider)) {
this.cachedProviders.set(provider, this.create(provider));
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.cachedProviders.get(provider)!;
}
getProviderByCapability<C extends CopilotCapability>(
capability: C,
model?: string,
prefer?: CopilotProviderType
): CapabilityToCopilotProvider[C] | null {
const providers = PROVIDER_CAPABILITY_MAP.get(capability);
if (Array.isArray(providers) && providers.length) {
let selectedProvider: CopilotProviderType | undefined = prefer;
let currentIndex = -1;
if (!selectedProvider) {
currentIndex = 0;
selectedProvider = providers[currentIndex];
}
while (selectedProvider) {
// find first provider that supports the capability and model
if (providers.includes(selectedProvider)) {
const provider = this.getProvider(selectedProvider);
if (provider.getCapabilities().includes(capability)) {
if (model) {
if (provider.isModelAvailable(model)) {
return provider as CapabilityToCopilotProvider[C];
}
} else {
return provider as CapabilityToCopilotProvider[C];
}
}
}
currentIndex += 1;
selectedProvider = providers[currentIndex];
}
}
return null;
}
}
export { FalProvider } from './fal';
export { OpenAIProvider } from './openai';

View File

@@ -0,0 +1,270 @@
import assert from 'node:assert';
import { ClientOptions, OpenAI } from 'openai';
import {
ChatMessageRole,
CopilotCapability,
CopilotImageToTextProvider,
CopilotProviderType,
CopilotTextToEmbeddingProvider,
CopilotTextToImageProvider,
CopilotTextToTextProvider,
PromptMessage,
} from '../types';
const DEFAULT_DIMENSIONS = 256;
const SIMPLE_IMAGE_URL_REGEX = /^(https?:\/\/|data:image\/)/;
export class OpenAIProvider
implements
CopilotTextToTextProvider,
CopilotTextToEmbeddingProvider,
CopilotTextToImageProvider,
CopilotImageToTextProvider
{
static readonly type = CopilotProviderType.OpenAI;
static readonly capabilities = [
CopilotCapability.TextToText,
CopilotCapability.TextToEmbedding,
CopilotCapability.TextToImage,
CopilotCapability.ImageToText,
];
readonly availableModels = [
// text to text
'gpt-4-vision-preview',
'gpt-4-turbo-preview',
'gpt-3.5-turbo',
// embeddings
'text-embedding-3-large',
'text-embedding-3-small',
'text-embedding-ada-002',
// moderation
'text-moderation-latest',
'text-moderation-stable',
// text to image
'dall-e-3',
];
private readonly instance: OpenAI;
constructor(config: ClientOptions) {
assert(OpenAIProvider.assetsConfig(config));
this.instance = new OpenAI(config);
}
static assetsConfig(config: ClientOptions) {
return !!config.apiKey;
}
getCapabilities(): CopilotCapability[] {
return OpenAIProvider.capabilities;
}
isModelAvailable(model: string): boolean {
return this.availableModels.includes(model);
}
private chatToGPTMessage(
messages: PromptMessage[]
): OpenAI.Chat.Completions.ChatCompletionMessageParam[] {
// filter redundant fields
return messages.map(({ role, content, attachments }) => {
if (Array.isArray(attachments)) {
const contents = [
{ type: 'text', text: content },
...attachments
.filter(url => SIMPLE_IMAGE_URL_REGEX.test(url))
.map(url => ({
type: 'image_url',
image_url: { url, detail: 'high' },
})),
];
return {
role,
content: contents,
} as OpenAI.Chat.Completions.ChatCompletionMessageParam;
} else {
return { role, content };
}
});
}
private checkParams({
messages,
embeddings,
model,
}: {
messages?: PromptMessage[];
embeddings?: string[];
model: string;
}) {
if (!this.availableModels.includes(model)) {
throw new Error(`Invalid model: ${model}`);
}
if (Array.isArray(messages) && messages.length > 0) {
if (
messages.some(
m =>
// check non-object
typeof m !== 'object' ||
!m ||
// check content
typeof m.content !== 'string' ||
// content and attachments must exist at least one
((!m.content || !m.content.trim()) &&
(!Array.isArray(m.attachments) || !m.attachments.length))
)
) {
throw new Error('Empty message content');
}
if (
messages.some(
m =>
typeof m.role !== 'string' ||
!m.role ||
!ChatMessageRole.includes(m.role)
)
) {
throw new Error('Invalid message role');
}
} else if (
Array.isArray(embeddings) &&
embeddings.some(e => typeof e !== 'string' || !e || !e.trim())
) {
throw new Error('Invalid embedding');
}
}
// ====== text to text ======
async generateText(
messages: PromptMessage[],
model: string = 'gpt-3.5-turbo',
options: {
temperature?: number;
maxTokens?: number;
signal?: AbortSignal;
user?: string;
} = {}
): Promise<string> {
this.checkParams({ messages, model });
const result = await this.instance.chat.completions.create(
{
messages: this.chatToGPTMessage(messages),
model: model,
temperature: options.temperature || 0,
max_tokens: options.maxTokens || 4096,
user: options.user,
},
{ signal: options.signal }
);
const { content } = result.choices[0].message;
if (!content) {
throw new Error('Failed to generate text');
}
return content;
}
async *generateTextStream(
messages: PromptMessage[],
model: string = 'gpt-3.5-turbo',
options: {
temperature?: number;
maxTokens?: number;
signal?: AbortSignal;
user?: string;
} = {}
): AsyncIterable<string> {
this.checkParams({ messages, model });
const result = await this.instance.chat.completions.create(
{
stream: true,
messages: this.chatToGPTMessage(messages),
model: model,
temperature: options.temperature || 0,
max_tokens: options.maxTokens || 4096,
user: options.user,
},
{
signal: options.signal,
}
);
for await (const message of result) {
const content = message.choices[0].delta.content;
if (content) {
yield content;
if (options.signal?.aborted) {
result.controller.abort();
break;
}
}
}
}
// ====== text to embedding ======
async generateEmbedding(
messages: string | string[],
model: string,
options: {
dimensions: number;
signal?: AbortSignal;
user?: string;
} = { dimensions: DEFAULT_DIMENSIONS }
): Promise<number[][]> {
messages = Array.isArray(messages) ? messages : [messages];
this.checkParams({ embeddings: messages, model });
const result = await this.instance.embeddings.create({
model: model,
input: messages,
dimensions: options.dimensions || DEFAULT_DIMENSIONS,
user: options.user,
});
return result.data.map(e => e.embedding);
}
// ====== text to image ======
async generateImages(
messages: PromptMessage[],
model: string = 'dall-e-3',
options: {
signal?: AbortSignal;
user?: string;
} = {}
): Promise<Array<string>> {
const { content: prompt } = messages.pop() || {};
if (!prompt) {
throw new Error('Prompt is required');
}
const result = await this.instance.images.generate(
{
prompt,
model,
response_format: 'url',
user: options.user,
},
{ signal: options.signal }
);
return result.data.map(image => image.url).filter((v): v is string => !!v);
}
async *generateImagesStream(
messages: PromptMessage[],
model: string = 'dall-e-3',
options: {
signal?: AbortSignal;
user?: string;
} = {}
): AsyncIterable<string> {
const ret = await this.generateImages(messages, model, options);
for (const url of ret) {
yield url;
}
}
}

View File

@@ -0,0 +1,329 @@
import { createHash } from 'node:crypto';
import { BadRequestException, Logger } from '@nestjs/common';
import {
Args,
Field,
ID,
InputType,
Mutation,
ObjectType,
Parent,
registerEnumType,
ResolveField,
Resolver,
} from '@nestjs/graphql';
import { GraphQLJSON, SafeIntResolver } from 'graphql-scalars';
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
import { CurrentUser } from '../../core/auth';
import { UserType } from '../../core/user';
import { PermissionService } from '../../core/workspaces/permission';
import {
FileUpload,
MutexService,
Throttle,
TooManyRequestsException,
} from '../../fundamentals';
import { ChatSessionService } from './session';
import { CopilotStorage } from './storage';
import {
AvailableModels,
type ChatHistory,
type ChatMessage,
type ListHistoriesOptions,
SubmittedMessage,
} from './types';
registerEnumType(AvailableModels, { name: 'CopilotModel' });
export const COPILOT_LOCKER = 'copilot';
// ================== Input Types ==================
@InputType()
class CreateChatSessionInput {
@Field(() => String)
workspaceId!: string;
@Field(() => String)
docId!: string;
@Field(() => String, {
description: 'The prompt name to use for the session',
})
promptName!: string;
}
@InputType()
class CreateChatMessageInput implements Omit<SubmittedMessage, 'content'> {
@Field(() => String)
sessionId!: string;
@Field(() => String, { nullable: true })
content!: string | undefined;
@Field(() => [String], { nullable: true })
attachments!: string[] | undefined;
@Field(() => [GraphQLUpload], { nullable: true })
blobs!: Promise<FileUpload>[] | undefined;
@Field(() => GraphQLJSON, { nullable: true })
params!: Record<string, string> | undefined;
}
@InputType()
class QueryChatHistoriesInput implements Partial<ListHistoriesOptions> {
@Field(() => Boolean, { nullable: true })
action: boolean | undefined;
@Field(() => Number, { nullable: true })
limit: number | undefined;
@Field(() => Number, { nullable: true })
skip: number | undefined;
@Field(() => String, { nullable: true })
sessionId: string | undefined;
}
// ================== Return Types ==================
@ObjectType('ChatMessage')
class ChatMessageType implements Partial<ChatMessage> {
@Field(() => String)
role!: 'system' | 'assistant' | 'user';
@Field(() => String)
content!: string;
@Field(() => [String], { nullable: true })
attachments!: string[];
@Field(() => GraphQLJSON, { nullable: true })
params!: Record<string, string> | undefined;
@Field(() => Date)
createdAt!: Date;
}
@ObjectType('CopilotHistories')
class CopilotHistoriesType implements Partial<ChatHistory> {
@Field(() => String)
sessionId!: string;
@Field(() => String, {
description: 'An mark identifying which view to use to display the session',
nullable: true,
})
action!: string | undefined;
@Field(() => Number, {
description: 'The number of tokens used in the session',
})
tokens!: number;
@Field(() => [ChatMessageType])
messages!: ChatMessageType[];
@Field(() => Date)
createdAt!: Date;
}
@ObjectType('CopilotQuota')
class CopilotQuotaType {
@Field(() => SafeIntResolver, { nullable: true })
limit?: number;
@Field(() => SafeIntResolver)
used!: number;
}
// ================== Resolver ==================
@ObjectType('Copilot')
export class CopilotType {
@Field(() => ID, { nullable: true })
workspaceId!: string | undefined;
}
@Throttle()
@Resolver(() => CopilotType)
export class CopilotResolver {
private readonly logger = new Logger(CopilotResolver.name);
constructor(
private readonly permissions: PermissionService,
private readonly mutex: MutexService,
private readonly chatSession: ChatSessionService,
private readonly storage: CopilotStorage
) {}
@ResolveField(() => CopilotQuotaType, {
name: 'quota',
description: 'Get the quota of the user in the workspace',
complexity: 2,
})
async getQuota(@CurrentUser() user: CurrentUser) {
return await this.chatSession.getQuota(user.id);
}
@ResolveField(() => [String], {
description: 'Get the session list of chats in the workspace',
complexity: 2,
})
async chats(
@Parent() copilot: CopilotType,
@CurrentUser() user: CurrentUser
) {
if (!copilot.workspaceId) return [];
await this.permissions.checkCloudWorkspace(copilot.workspaceId, user.id);
return await this.chatSession.listSessions(user.id, copilot.workspaceId);
}
@ResolveField(() => [String], {
description: 'Get the session list of actions in the workspace',
complexity: 2,
})
async actions(
@Parent() copilot: CopilotType,
@CurrentUser() user: CurrentUser
) {
if (!copilot.workspaceId) return [];
await this.permissions.checkCloudWorkspace(copilot.workspaceId, user.id);
return await this.chatSession.listSessions(user.id, copilot.workspaceId, {
action: true,
});
}
@ResolveField(() => [CopilotHistoriesType], {})
async histories(
@Parent() copilot: CopilotType,
@CurrentUser() user: CurrentUser,
@Args('docId', { nullable: true }) docId?: string,
@Args({
name: 'options',
type: () => QueryChatHistoriesInput,
nullable: true,
})
options?: QueryChatHistoriesInput
) {
const workspaceId = copilot.workspaceId;
if (!workspaceId) {
return [];
} else if (docId) {
await this.permissions.checkCloudPagePermission(
workspaceId,
docId,
user.id
);
} else {
await this.permissions.checkCloudWorkspace(workspaceId, user.id);
}
const histories = await this.chatSession.listHistories(
user.id,
workspaceId,
docId,
options,
true
);
return histories.map(h => ({
...h,
// filter out empty messages
messages: h.messages.filter(m => m.content || m.attachments?.length),
}));
}
@Mutation(() => String, {
description: 'Create a chat session',
})
async createCopilotSession(
@CurrentUser() user: CurrentUser,
@Args({ name: 'options', type: () => CreateChatSessionInput })
options: CreateChatSessionInput
) {
await this.permissions.checkCloudPagePermission(
options.workspaceId,
options.docId,
user.id
);
const lockFlag = `${COPILOT_LOCKER}:session:${user.id}:${options.workspaceId}`;
await using lock = await this.mutex.lock(lockFlag);
if (!lock) {
return new TooManyRequestsException('Server is busy');
}
await this.chatSession.checkQuota(user.id);
const session = await this.chatSession.create({
...options,
userId: user.id,
});
return session;
}
@Mutation(() => String, {
description: 'Create a chat message',
})
async createCopilotMessage(
@CurrentUser() user: CurrentUser,
@Args({ name: 'options', type: () => CreateChatMessageInput })
options: CreateChatMessageInput
) {
const lockFlag = `${COPILOT_LOCKER}:message:${user?.id}:${options.sessionId}`;
await using lock = await this.mutex.lock(lockFlag);
if (!lock) {
return new TooManyRequestsException('Server is busy');
}
const session = await this.chatSession.get(options.sessionId);
if (!session) return new BadRequestException('Session not found');
if (options.blobs) {
options.attachments = options.attachments || [];
const { workspaceId } = session.config;
const blobs = await Promise.all(options.blobs);
delete options.blobs;
for (const blob of blobs) {
const uploaded = await this.storage.handleUpload(user.id, blob);
const filename = createHash('sha256')
.update(uploaded.buffer)
.digest('base64url');
const link = await this.storage.put(
user.id,
workspaceId,
filename,
uploaded.buffer
);
options.attachments.push(link);
}
}
try {
return await this.chatSession.createMessage(options);
} catch (e: any) {
this.logger.error(`Failed to create chat message: ${e.message}`);
throw new Error('Failed to create chat message');
}
}
}
@Throttle()
@Resolver(() => UserType)
export class UserCopilotResolver {
constructor(private readonly permissions: PermissionService) {}
@ResolveField(() => CopilotType)
async copilot(
@CurrentUser() user: CurrentUser,
@Args('workspaceId', { nullable: true }) workspaceId?: string
) {
if (workspaceId) {
await this.permissions.checkCloudWorkspace(workspaceId, user.id);
}
return { workspaceId };
}
}

View File

@@ -0,0 +1,458 @@
import { randomUUID } from 'node:crypto';
import { Injectable, Logger } from '@nestjs/common';
import { AiPromptRole, PrismaClient } from '@prisma/client';
import { FeatureManagementService } from '../../core/features';
import { QuotaService } from '../../core/quota';
import { PaymentRequiredException } from '../../fundamentals';
import { ChatMessageCache } from './message';
import { ChatPrompt, PromptService } from './prompt';
import {
AvailableModel,
ChatHistory,
ChatMessage,
ChatMessageSchema,
ChatSessionOptions,
ChatSessionState,
getTokenEncoder,
ListHistoriesOptions,
PromptMessage,
PromptParams,
SubmittedMessage,
} from './types';
export class ChatSession implements AsyncDisposable {
constructor(
private readonly messageCache: ChatMessageCache,
private readonly state: ChatSessionState,
private readonly dispose?: (state: ChatSessionState) => Promise<void>,
private readonly maxTokenSize = 3840
) {}
get model() {
return this.state.prompt.model;
}
get config() {
const {
sessionId,
userId,
workspaceId,
docId,
prompt: { name: promptName },
} = this.state;
return { sessionId, userId, workspaceId, docId, promptName };
}
push(message: ChatMessage) {
if (
this.state.prompt.action &&
this.state.messages.length > 0 &&
message.role === 'user'
) {
throw new Error('Action has been taken, no more messages allowed');
}
this.state.messages.push(message);
}
async getMessageById(messageId: string) {
const message = await this.messageCache.get(messageId);
if (!message || message.sessionId !== this.state.sessionId) {
throw new Error(`Message not found: ${messageId}`);
}
return message;
}
async pushByMessageId(messageId: string) {
const message = await this.messageCache.get(messageId);
if (!message || message.sessionId !== this.state.sessionId) {
throw new Error(`Message not found: ${messageId}`);
}
this.push({
role: 'user',
content: message.content || '',
attachments: message.attachments,
params: message.params,
createdAt: new Date(),
});
}
pop() {
this.state.messages.pop();
}
private takeMessages(): ChatMessage[] {
if (this.state.prompt.action) {
const messages = this.state.messages;
return messages.slice(messages.length - 1);
}
const ret = [];
const messages = this.state.messages.slice();
let size = this.state.prompt.tokens;
while (messages.length) {
const message = messages.pop();
if (!message) break;
size += this.state.prompt.encode(message.content);
if (size > this.maxTokenSize) {
break;
}
ret.push(message);
}
ret.reverse();
return ret;
}
finish(params: PromptParams): PromptMessage[] {
const messages = this.takeMessages();
return [
...this.state.prompt.finish(
Object.keys(params).length ? params : messages[0]?.params || {},
this.config.sessionId
),
...messages.filter(m => m.content || m.attachments?.length),
];
}
async save() {
await this.dispose?.(this.state);
}
async [Symbol.asyncDispose]() {
this.state.prompt.free();
await this.save?.();
}
}
@Injectable()
export class ChatSessionService {
private readonly logger = new Logger(ChatSessionService.name);
constructor(
private readonly db: PrismaClient,
private readonly feature: FeatureManagementService,
private readonly quota: QuotaService,
private readonly messageCache: ChatMessageCache,
private readonly prompt: PromptService
) {}
private async setSession(state: ChatSessionState): Promise<string> {
return await this.db.$transaction(async tx => {
let sessionId = state.sessionId;
// find existing session if session is chat session
if (!state.prompt.action) {
const { id } =
(await tx.aiSession.findFirst({
where: {
userId: state.userId,
workspaceId: state.workspaceId,
docId: state.docId,
prompt: { action: { equals: null } },
},
select: { id: true },
})) || {};
if (id) sessionId = id;
}
const messages = state.messages.map(m => ({
...m,
attachments: m.attachments || undefined,
params: m.params || undefined,
}));
await tx.aiSession.upsert({
where: {
id: sessionId,
userId: state.userId,
},
update: {
messages: {
// skip delete old messages if no new messages
deleteMany: messages.length ? {} : undefined,
create: messages,
},
},
create: {
id: sessionId,
workspaceId: state.workspaceId,
docId: state.docId,
messages: {
create: messages,
},
// connect
user: { connect: { id: state.userId } },
prompt: { connect: { name: state.prompt.name } },
},
});
return sessionId;
});
}
private async getSession(
sessionId: string
): Promise<ChatSessionState | undefined> {
return await this.db.aiSession
.findUnique({
where: { id: sessionId },
select: {
id: true,
userId: true,
workspaceId: true,
docId: true,
messages: {
select: {
role: true,
content: true,
createdAt: true,
},
orderBy: {
createdAt: 'asc',
},
},
prompt: {
select: {
name: true,
action: true,
model: true,
messages: {
select: {
role: true,
content: true,
createdAt: true,
},
orderBy: {
idx: 'asc',
},
},
},
},
},
})
.then(async session => {
if (!session) return;
const messages = ChatMessageSchema.array().safeParse(session.messages);
return {
sessionId: session.id,
userId: session.userId,
workspaceId: session.workspaceId,
docId: session.docId,
prompt: ChatPrompt.createFromPrompt(session.prompt),
messages: messages.success ? messages.data : [],
};
});
}
private calculateTokenSize(
messages: PromptMessage[],
model: AvailableModel
): number {
const encoder = getTokenEncoder(model);
return messages
.map(m => encoder?.encode_ordinary(m.content).length || 0)
.reduce((total, length) => total + length, 0);
}
private async countUserActions(userId: string): Promise<number> {
return await this.db.aiSession.count({
where: { userId, prompt: { action: { not: null } } },
});
}
private async countUserChats(userId: string): Promise<number> {
const chats = await this.db.aiSession.findMany({
where: { userId, prompt: { action: null } },
select: {
_count: {
select: { messages: { where: { role: AiPromptRole.user } } },
},
},
});
return chats.reduce((prev, chat) => prev + chat._count.messages, 0);
}
async listSessions(
userId: string,
workspaceId: string,
options?: { docId?: string; action?: boolean }
): Promise<string[]> {
return await this.db.aiSession
.findMany({
where: {
userId,
workspaceId,
docId: workspaceId === options?.docId ? undefined : options?.docId,
prompt: {
action: options?.action ? { not: null } : null,
},
},
select: { id: true },
})
.then(sessions => sessions.map(({ id }) => id));
}
async listHistories(
userId: string,
workspaceId?: string,
docId?: string,
options?: ListHistoriesOptions,
withPrompt = false
): Promise<ChatHistory[]> {
return await this.db.aiSession
.findMany({
where: {
userId,
workspaceId: workspaceId,
docId: workspaceId === docId ? undefined : docId,
prompt: {
action: options?.action ? { not: null } : null,
},
id: options?.sessionId ? { equals: options.sessionId } : undefined,
},
select: {
id: true,
promptName: true,
createdAt: true,
messages: {
select: {
role: true,
content: true,
attachments: true,
params: true,
createdAt: true,
},
orderBy: {
createdAt: 'asc',
},
},
},
take: options?.limit,
skip: options?.skip,
orderBy: { createdAt: 'desc' },
})
.then(sessions =>
Promise.all(
sessions.map(async ({ id, promptName, messages, createdAt }) => {
try {
const ret = ChatMessageSchema.array().safeParse(messages);
if (ret.success) {
const prompt = await this.prompt.get(promptName);
if (!prompt) {
throw new Error(`Prompt not found: ${promptName}`);
}
const tokens = this.calculateTokenSize(
ret.data,
prompt.model as AvailableModel
);
// render system prompt
const preload = withPrompt
? prompt
.finish(ret.data[0]?.params || {}, id)
.filter(({ role }) => role !== 'system')
: [];
// `createdAt` is required for history sorting in frontend, let's fake the creating time of prompt messages
(preload as ChatMessage[]).forEach((msg, i) => {
msg.createdAt = new Date(
createdAt.getTime() - preload.length - i - 1
);
});
return {
sessionId: id,
action: prompt.action || undefined,
tokens,
createdAt,
messages: preload.concat(ret.data),
};
} else {
this.logger.error(
`Unexpected message schema: ${JSON.stringify(ret.error)}`
);
}
} catch (e) {
this.logger.error('Unexpected error in listHistories', e);
}
return undefined;
})
)
)
.then(histories =>
histories.filter((v): v is NonNullable<typeof v> => !!v)
);
}
async getQuota(userId: string) {
const isCopilotUser = await this.feature.isCopilotUser(userId);
let limit: number | undefined;
if (!isCopilotUser) {
const quota = await this.quota.getUserQuota(userId);
limit = quota.feature.copilotActionLimit;
}
const actions = await this.countUserActions(userId);
const chats = await this.countUserChats(userId);
return { limit, used: actions + chats };
}
async checkQuota(userId: string) {
const { limit, used } = await this.getQuota(userId);
if (limit && Number.isFinite(limit) && used >= limit) {
throw new PaymentRequiredException(
`You have reached the limit of actions in this workspace, please upgrade your plan.`
);
}
}
async create(options: ChatSessionOptions): Promise<string> {
const sessionId = randomUUID();
const prompt = await this.prompt.get(options.promptName);
if (!prompt) {
this.logger.error(`Prompt not found: ${options.promptName}`);
throw new Error('Prompt not found');
}
return await this.setSession({
...options,
sessionId,
prompt,
messages: [],
});
}
async createMessage(message: SubmittedMessage): Promise<string | undefined> {
return await this.messageCache.set(message);
}
/**
* usage:
* ``` typescript
* {
* // allocate a session, can be reused chat in about 12 hours with same session
* await using session = await session.get(sessionId);
* session.push(message);
* copilot.generateText(session.finish(), model);
* }
* // session will be disposed after the block
* @param sessionId session id
* @returns
*/
async get(sessionId: string): Promise<ChatSession | null> {
const state = await this.getSession(sessionId);
if (state) {
return new ChatSession(this.messageCache, state, async state => {
await this.setSession(state);
});
}
return null;
}
}

Some files were not shown because too many files have changed in this diff Show More