Compare commits

..

230 Commits

Author SHA1 Message Date
pengx17
095f8c2359 fix: button should have its font-family inherited (#6311) 2024-03-26 03:57:59 +00:00
Brooooooklyn
ffbfdb65a2 fix(core): add env info to tracks (#6313) 2024-03-26 03:41:41 +00:00
pengx17
e9bc24bf37 fix(electron): possible issue on openning two main windows (#6307)
fix https://github.com/toeverything/AFFiNE/issues/6303

fetching `getWindowAdditionalArguments` requires forking a new process & handshake, which could be time consuming
2024-03-26 03:29:37 +00:00
renovate
2662ba763c chore: bump up express version to v4.19.2 [SECURITY] (#6308)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

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

### GitHub Vulnerability Alerts

#### [CVE-2024-29041](https://togithub.com/expressjs/express/security/advisories/GHSA-rv95-896h-c2vc)

### Impact

Versions of Express.js prior to 4.19.2 and pre-release alpha and beta versions before 5.0.0-beta.3 are affected by an open redirect vulnerability using malformed URLs.

When a user of Express performs a redirect using a user-provided URL Express performs an encode [using `encodeurl`](https://togithub.com/pillarjs/encodeurl) on the contents before passing it to the `location` header. This can cause malformed URLs to be evaluated in unexpected ways by common redirect allow list implementations in Express applications, leading to an Open Redirect via bypass of a properly implemented allow list.

The main method impacted is `res.location()` but this is also called from within `res.redirect()`.

### Patches

0867302ddb
0b746953c4

An initial fix went out with `express@4.19.0`, we then patched a feature regression in `4.19.1` and added improved handling for the bypass in `4.19.2`.

### Workarounds

The fix for this involves pre-parsing the url string with either `require('node:url').parse` or `new URL`. These are steps you can take on your own before passing the user input string to `res.location` or `res.redirect`.

### References

[https://github.com/expressjs/express/pull/5539](https://togithub.com/expressjs/express/pull/5539)
[https://github.com/koajs/koa/issues/1800](https://togithub.com/koajs/koa/issues/1800)
https://expressjs.com/en/4x/api.html#res.location

---

### Release Notes

<details>
<summary>expressjs/express (express)</summary>

### [`v4.19.2`](https://togithub.com/expressjs/express/blob/HEAD/History.md#4192--2024-03-25)

[Compare Source](https://togithub.com/expressjs/express/compare/4.19.1...4.19.2)

\==========

-   Improved fix for open redirect allow list bypass

### [`v4.19.1`](https://togithub.com/expressjs/express/blob/HEAD/History.md#4191--2024-03-20)

[Compare Source](https://togithub.com/expressjs/express/compare/4.19.0...4.19.1)

\==========

-   Allow passing non-strings to res.location with new encoding handling checks

### [`v4.19.0`](https://togithub.com/expressjs/express/compare/4.18.3...83e77aff6a3859d58206f3ff9501277023c03f87)

[Compare Source](https://togithub.com/expressjs/express/compare/4.18.3...4.19.0)

### [`v4.18.3`](https://togithub.com/expressjs/express/blob/HEAD/History.md#4183--2024-02-26)

[Compare Source](https://togithub.com/expressjs/express/compare/4.18.2...4.18.3)

\==========

-   Fix routing requests without method
-   deps: body-parser@1.20.2
    -   Fix strict json error message on Node.js 19+
    -   deps: content-type@~1.0.5
    -   deps: raw-body@2.5.2

</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-03-26 03:17:49 +00:00
forehalo
1a1af83375 test(server): auth tests (#6135) 2024-03-26 02:24:17 +00:00
pengx17
1c9d899831 fix: runtime issue for electron app (#6306)
Looks like we need to be careful to share common libraries between electron (nodejs) & web

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/7e568e47-2d61-45c8-8a1e-b933b63fd1a9.png)
2024-03-26 02:04:13 +00:00
pengx17
00092c9955 fix(electron): fix electron build (#6305) 2024-03-25 15:57:22 +00:00
EYHN
3e547ce4cc fix(core): hidden modals when workspace fallback (#6301) 2024-03-25 13:52:08 +00:00
EYHN
da12a0e48e fix(core): fix error when switch to local workspace (#6144) 2024-03-25 21:35:10 +08:00
CatsJuice
b2f34d17a2 feat(core): adjust app sidebar's style (#6162) 2024-03-25 10:25:48 +00:00
pengx17
2a019d4fae fix(core): storybook stability for date (#6300) 2024-03-25 09:50:48 +00:00
donteatfriedrice
48abc52e85 feat: bump blocksuite (#6294)
## Features
- https://github.com/toeverything/BlockSuite/pull/6544 @golok727
- https://github.com/toeverything/BlockSuite/pull/6543 @golok727
- https://github.com/toeverything/BlockSuite/pull/6536 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6497 @doouding
- https://github.com/toeverything/BlockSuite/pull/6514 @regischen
- https://github.com/toeverything/BlockSuite/pull/6523 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6530 @zzj3720
- https://github.com/toeverything/BlockSuite/pull/6526 @fourdim
- https://github.com/toeverything/BlockSuite/pull/6532 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6493 @golok727
- https://github.com/toeverything/BlockSuite/pull/6529 @zzj3720
- https://github.com/toeverything/BlockSuite/pull/6528 @zzj3720
- https://github.com/toeverything/BlockSuite/pull/6509 @zzj3720
- https://github.com/toeverything/BlockSuite/pull/6525 @doodlewind
- https://github.com/toeverything/BlockSuite/pull/6502 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6489 @Flrande

## Bugfix
- https://github.com/toeverything/BlockSuite/pull/6558 @fourdim
- https://github.com/toeverything/BlockSuite/pull/6556 @fourdim
- https://github.com/toeverything/BlockSuite/pull/6547 @fundon
- https://github.com/toeverything/BlockSuite/pull/6537 @golok727
- https://github.com/toeverything/BlockSuite/pull/6531 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6524 @doodlewind
- https://github.com/toeverything/BlockSuite/pull/6519 @regischen
- https://github.com/toeverything/BlockSuite/pull/6517 @doodlewind
- https://github.com/toeverything/BlockSuite/pull/6516 @doodlewind
- https://github.com/toeverything/BlockSuite/pull/6510 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6511 @congzhou09
- https://github.com/toeverything/BlockSuite/pull/6507 @doouding
- https://github.com/toeverything/BlockSuite/pull/6500 @fourdim
- https://github.com/toeverything/BlockSuite/pull/6486 @congzhou09
- https://github.com/toeverything/BlockSuite/pull/6495 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6488 @Saul-Mirone
- https://github.com/toeverything/BlockSuite/pull/6482 @Flrande
- https://github.com/toeverything/BlockSuite/pull/6558 @fourdim

## Refactor
- https://github.com/toeverything/BlockSuite/pull/6548 @doodlewind
- https://github.com/toeverything/BlockSuite/pull/6522 @doodlewind
- https://github.com/toeverything/BlockSuite/pull/6518 @regischen
- https://github.com/toeverything/BlockSuite/pull/6521 @Saul-Mirone

## Misc
- https://github.com/toeverything/BlockSuite/pull/6557 @fourdim
- https://github.com/toeverything/BlockSuite/pull/6546 @Flrande
- docs: update package desc
- https://github.com/toeverything/BlockSuite/pull/6527 @fourdim
- https://github.com/toeverything/BlockSuite/pull/6505 @Brooooooklyn
- https://github.com/toeverything/BlockSuite/pull/6503 @fourdim
- v0.13.0
- https://github.com/toeverything/BlockSuite/pull/6496 @doodlewind
- https://github.com/toeverything/BlockSuite/pull/6562 @donteatfriedrice
2024-03-25 09:20:45 +00:00
JimmFly
09a27b6c25 feat(core): add remove from collection to collection page list (#6265)
close AFF-246
2024-03-25 08:31:38 +00:00
JimmFly
03c01a9693 fix(core): edit tag input autofocus (#6296)
close TOV-724
2024-03-25 08:05:25 +00:00
JimmFly
1ff6af85f5 feat(core): add page group and display properties (#6228)
close TOV-23

https://github.com/toeverything/AFFiNE/assets/102217452/c05474de-b73c-40ab-9f18-cc43bb9fd828
2024-03-25 07:53:33 +00:00
Brooooooklyn
6467e10690 ci: fix lint oom (#6295) 2024-03-25 07:11:49 +00:00
EYHN
a8cd1579f5 feat(infra): livedata effect (#6281) 2024-03-25 06:09:45 +00:00
EYHN
f2adbdaba4 style: enable import-x/no-duplicates (#6279) 2024-03-25 03:55:33 +00:00
EYHN
7ce2bfbf0b style: no import infra submodule (#6278) 2024-03-25 03:55:29 +00:00
EYHN
b93871f045 feat(electron): define runtimeConfig in esbuild (#6287) 2024-03-25 03:55:26 +00:00
EYHN
d59e1389ec chore(electron): config vitest swc (#6282) 2024-03-25 03:55:23 +00:00
EYHN
82cacd09d6 fix(core): fix flaky e2e (#6293) 2024-03-25 02:58:52 +00:00
pengx17
578d4c9775 fix(core): image preview flaky (#6292) 2024-03-25 02:46:31 +00:00
pengx17
64c011c72f fix(electron): set referer and origin headers for electron (#6289) 2024-03-25 01:23:18 +00:00
EYHN
2b42a75e5a style: enable rxjs/finnish (#6276)
chore(infra): use finnish notation for observables

do rename
2024-03-24 17:04:51 +00:00
dependabot
c6676fd074 build(deps): bump webpack-dev-middleware from 7.0.0 to 7.1.1 (#6275)
Bumps [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) from 7.0.0 to 7.1.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/webpack/webpack-dev-middleware/releases">webpack-dev-middleware's releases</a>.</em></p>
<blockquote>
<h2>v7.1.1</h2>
<h3><a href="https://github.com/webpack/webpack-dev-middleware/compare/v7.1.0...v7.1.1">7.1.1</a> (2024-03-21)</h3>
<h3>Bug Fixes</h3>
<ul>
<li><code>ContentLength</code> incorrectly set for empty files (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1785">#1785</a>) (<a href="0f3e25e2b0">0f3e25e</a>)</li>
<li>improve perf (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1777">#1777</a>) (<a href="5b47c9294e">5b47c92</a>)</li>
<li><strong>types:</strong> make types better (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1786">#1786</a>) (<a href="e4d183ea6d">e4d183e</a>)</li>
</ul>
<h2>v7.1.0</h2>
<h2><a href="https://github.com/webpack/webpack-dev-middleware/compare/v7.0.0...v7.1.0">7.1.0</a> (2024-03-19)</h2>
<h3>Features</h3>
<ul>
<li>prefer to use <code>fs.createReadStream</code> over <code>fs.readFileSync</code> to read files (<a href="ab533de933">ab533de</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>cleaup stream and handle errors (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1769">#1769</a>) (<a href="1258fdd3d9">1258fdd</a>)</li>
<li><strong>security:</strong> do not allow to read files above (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1771">#1771</a>) (<a href="e10008c762">e10008c</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/webpack/webpack-dev-middleware/blob/master/CHANGELOG.md">webpack-dev-middleware's changelog</a>.</em></p>
<blockquote>
<h3><a href="https://github.com/webpack/webpack-dev-middleware/compare/v7.1.0...v7.1.1">7.1.1</a> (2024-03-21)</h3>
<h3>Bug Fixes</h3>
<ul>
<li><code>ContentLength</code> incorrectly set for empty files (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1785">#1785</a>) (<a href="0f3e25e2b0">0f3e25e</a>)</li>
<li>improve perf (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1777">#1777</a>) (<a href="5b47c9294e">5b47c92</a>)</li>
<li><strong>types:</strong> make types better (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1786">#1786</a>) (<a href="e4d183ea6d">e4d183e</a>)</li>
</ul>
<h2><a href="https://github.com/webpack/webpack-dev-middleware/compare/v7.0.0...v7.1.0">7.1.0</a> (2024-03-19)</h2>
<h3>Features</h3>
<ul>
<li>prefer to use <code>fs.createReadStream</code> over <code>fs.readFileSync</code> to read files (<a href="ab533de933">ab533de</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li>cleaup stream and handle errors (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1769">#1769</a>) (<a href="1258fdd3d9">1258fdd</a>)</li>
<li><strong>security:</strong> do not allow to read files above (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1771">#1771</a>) (<a href="e10008c762">e10008c</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="7c6164a82f"><code>7c6164a</code></a> chore(release): 7.1.1</li>
<li><a href="e4d183ea6d"><code>e4d183e</code></a> fix(types): make types better (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1786">#1786</a>)</li>
<li><a href="f23ed7ccd8"><code>f23ed7c</code></a> chore(deps-dev): bump <code>@​babel/core</code> from 7.24.1 to 7.24.3 (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1782">#1782</a>)</li>
<li><a href="0f3e25e2b0"><code>0f3e25e</code></a> fix: <code>ContentLength</code> incorrectly set for empty files (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1785">#1785</a>)</li>
<li><a href="d45f033ea7"><code>d45f033</code></a> chore(deps-dev): bump <code>@​babel/preset-env</code> from 7.24.1 to 7.24.3 (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1783">#1783</a>)</li>
<li><a href="c0c2eea2e7"><code>c0c2eea</code></a> chore(deps-dev): bump express from 4.18.3 to 4.19.1 (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1781">#1781</a>)</li>
<li><a href="5b47c9294e"><code>5b47c92</code></a> fix: improve perf (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1777">#1777</a>)</li>
<li><a href="1a34bc4bce"><code>1a34bc4</code></a> chore(deps-dev): bump <code>@​types/node</code> from 20.11.29 to 20.11.30 (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1774">#1774</a>)</li>
<li><a href="d618f1f126"><code>d618f1f</code></a> chore(deps-dev): bump <code>@​babel/preset-env</code> from 7.24.0 to 7.24.1 (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1772">#1772</a>)</li>
<li><a href="40daa4bb71"><code>40daa4b</code></a> chore(deps-dev): bump <code>@​babel/core</code> from 7.24.0 to 7.24.1 (<a href="https://redirect.github.com/webpack/webpack-dev-middleware/issues/1776">#1776</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/webpack/webpack-dev-middleware/compare/v7.0.0...v7.1.1">compare view</a></li>
</ul>
</details>
<br />

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=webpack-dev-middleware&package-manager=npm_and_yarn&previous-version=7.0.0&new-version=7.1.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-03-24 11:02:10 +00:00
BABA
6a02d0bc96 feat: open about page in setting modal when click about menu (#6245)
Co-authored-by: EYHN <cneyhn@gmail.com>
2024-03-23 13:27:05 +00:00
Fangdun Tsai
6c9db367e2 chore(core): add oauth connecting state (#6225) 2024-03-23 21:18:48 +08:00
BABA
a1532d4df2 chore: fix renderer entry path not found in desktop development (#6270)
Co-authored-by: LongYinan <lynweklm@gmail.com>
2024-03-23 21:12:23 +08:00
fundon
7e161682f0 fix(core): creating multiple workspaces with consecutive clicks (#6259)
Closes #6213
2024-03-23 12:29:46 +00:00
pengx17
62a6075675 fix(core): do not ensure properties on read (#6263) 2024-03-23 12:15:06 +00:00
JimmFly
532d655ffb feat(core): add confirm modal for delete tag action (#6268) 2024-03-23 12:03:14 +00:00
pengx17
3c6983ee49 fix(core): storybook build issue (#6274)
1. es2022 is required and should be set separately in storybook.
2. @blocksuite/icons versions are not consistent across packages.
2024-03-23 06:33:25 +00:00
EYHN
34703a3b7d feat(infra): new doc sync engine (#6205)
https://github.com/toeverything/AFFiNE/blob/eyhn/feat/new-sync/packages/common/infra/src/workspace/engine/doc/README.md
2024-03-22 16:43:26 +00:00
Brooooooklyn
05c44db5a9 chore(core): remove unused dependencies (#6203) 2024-03-22 10:39:39 +00:00
Brooooooklyn
622e90f176 chore(core): add telemetry switch (#6267) 2024-03-22 10:28:55 +00:00
EYHN
a0b97f948c fix(core): fix stuttering when change doc title (#6269) 2024-03-22 10:06:37 +00:00
Fangdun Tsai
69cb8b0f60 chore(core): disable onborading on the web (#6222) 2024-03-22 18:05:36 +08:00
Brooooooklyn
150c22936d chore(core): add mixpanel track (#6202) 2024-03-22 09:24:41 +00:00
Brooooooklyn
10af0ab48d feat(server): support ai plan (#6216) 2024-03-22 08:39:18 +00:00
Brooooooklyn
aecc523663 fix(server): avoid error when other prices added but logic is not released (#6191) 2024-03-22 08:39:12 +00:00
EYHN
75355867c7 feat(core): save user habits in right sidebar (#6262)
Closes #6237
2024-03-22 07:32:59 +00:00
Brooooooklyn
85ee22329c fix(electron): add icon for AppImage build (#6257)
1. the icon is fixed in `/Applications`: 128b8c22f9 (diff-a694a3e854f53b066e34ec310e05bd18b4944c016455f6963f54a351784d5fa6L91)
2. the App's icon MUST be 64x64 png and set via `setIcon`

![image](https://github.com/toeverything/AFFiNE/assets/584378/bbce0007-066b-413f-a85a-193acbbe5c13)
2024-03-21 14:29:02 +00:00
forehalo
540e456704 ci: set private key from env (#6239) 2024-03-21 10:09:26 +00:00
EYHN
d03c72a0a8 fix(electron): linux crash on exiting presentation mode (#6253) 2024-03-21 09:54:48 +00:00
Brooooooklyn
6a0ab54e25 ci: fix isSelfHosted does not take effect (#6249) 2024-03-21 08:52:14 +00:00
Brooooooklyn
18224a83d1 chore(electron): bump @napi-rs/macos-alias (#6240)
fix dmg background missing issue https://github.com/Brooooooklyn/macos-alias/pull/3
2024-03-21 06:53:14 +00:00
pengx17
f4ede22b93 fix(core): change cursor when hovering the area blow editor (#6226) 2024-03-21 02:00:37 +00:00
pengx17
8b2b2646bc fix: move traffic lights based on zoom level (#6201)
<div class='graphite__hidden'>
          <div>🎥 Video uploaded on Graphite:</div>
            <a href="https://app.graphite.dev/media/video/T2klNLEk0wxLh4NRDzhk/f75d1f6f-18f4-4dff-8174-67223f5f9807.mp4">
              <img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/T2klNLEk0wxLh4NRDzhk/f75d1f6f-18f4-4dff-8174-67223f5f9807.mp4">
            </a>
          </div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/f75d1f6f-18f4-4dff-8174-67223f5f9807.mp4">Kapture 2024-03-19 at 18.05.20.mp4</video>
2024-03-21 02:00:35 +00:00
EYHN
e1cfa1071e chore(core): align sidebar icons (#6219) 2024-03-20 17:15:13 +00:00
EYHN
e4e4a54d90 fix(core): resize-handle remains interactive when dragging split-view (#6217) 2024-03-20 16:56:17 +00:00
EYHN
08b610bbad fix(electron): menu item position on Mac when fullscreen (#6200)
fix https://github.com/toeverything/AFFiNE/issues/6155
2024-03-20 16:45:14 +00:00
EYHN
3edf32b1df build: add sourceMaps and inlineSourcesContent option to swc (#6234) 2024-03-20 16:33:15 +00:00
EYHN
39cde560d1 fix(templates): fix typo in onboarding template (#6221) 2024-03-20 16:19:28 +00:00
Brooooooklyn
483f957583 chore: bump up @aws-sdk/client-s3 version to v3.537.0 (#6210)
[![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.536.0` -> `3.537.0`](https://renovatebot.com/diffs/npm/@aws-sdk%2fclient-s3/3.536.0/3.537.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@aws-sdk%2fclient-s3/3.537.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@aws-sdk%2fclient-s3/3.537.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@aws-sdk%2fclient-s3/3.536.0/3.537.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@aws-sdk%2fclient-s3/3.536.0/3.537.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

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

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

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

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

</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:eyJjcmVhdGVkSW5WZXIiOiIzNy4yNDUuMCIsInVwZGF0ZWRJblZlciI6IjM3LjI0NS4wIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-03-20 14:44:24 +00:00
Brooooooklyn
32ab0693e2 feat(core): update split view icons and texts (#6193) 2024-03-20 14:30:35 +00:00
pengx17
a8a1074a8a feat(electron): add isMaximized flag to html (#6199)
to make some special ui rules for desktop
2024-03-20 13:20:19 +00:00
pengx17
65ab6c89bf fix(electron): optimize electron open/close on mac (#6224)
1. never close main window on mac to allow it to be quickly open
1. make the browser show a bit faster
2. brought up app window when clicking some menu items
2024-03-20 11:02:22 +00:00
liuyi
4f5907766f fix(server): decode uri component before verify token (#6231) 2024-03-20 18:17:11 +08:00
liuyi
06a5b2e5a5 fix(server): wrong google oauth param (#6227) 2024-03-20 17:45:22 +08:00
pengx17
7adb89f134 feat(core): open new page on meta-clicking a page link (#6220) 2024-03-20 05:38:39 +00:00
EYHN
5623c0967c feat(electron): enable css text autospace (#6218)
before

![CleanShot 2024-03-20 at 10.38.50@2x.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/g3jz87HxbjOJpXV3FPT7/80a59a2b-ede7-453a-889a-6c54a967c27d.png)

after

![CleanShot 2024-03-20 at 10.39.08@2x.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/g3jz87HxbjOJpXV3FPT7/5e98d9e1-ec8e-4cc7-804b-e4347c62ee6e.png)
2024-03-20 02:47:49 +00:00
Peng Xiao
fce4484a85 fix(core): tag size in docs view (#6197) 2024-03-19 08:53:08 +00:00
Peng Xiao
0695544073 fix(core): page info should use sans font (inter) (#6196) 2024-03-19 08:53:00 +00:00
JimmFly
9030ca511e refactor(core): refactor tag to use di (#6079)
use case
```
const tagService = useService(TagService);
const tags = useLiveData(tagService.tags);
const currentTagLiveData = tagService.tagByTagId(tagId);
const currentTag = useLiveData(currentTagLiveData);

```
2024-03-19 08:39:15 +00:00
LongYinan
332cd3b380 refactor(core): split web entry from core (#6082)
This pr is trying to split `web` and `electron` entries from `core`. It allows more platform-related optimization to be addressed in each entry.
We should remove all browser/electron only codes from `core` eventually, this is the very first step for that.
2024-03-19 07:48:56 +00:00
LongYinan
26925c96e4 chore: bump up happy-dom version to v14 (#6187)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

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

---

### Release Notes

<details>
<summary>capricorn86/happy-dom (happy-dom)</summary>

### [`v14.0.0`](https://togithub.com/capricorn86/happy-dom/releases/tag/v14.0.0)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.10.1...v14.0.0)

##### 💣 Breaking Changes

-   Removes interfaces for Node's, as they are no longer needed as newer versions of Typescript can handle circular dependencies - By **[@&#8203;capricorn86](https://togithub.com/capricorn86)** in task [#&#8203;1330](https://togithub.com/capricorn86/happy-dom/issues/1330)

### [`v13.10.1`](https://togithub.com/capricorn86/happy-dom/compare/v13.10.0...a6debf50e909766e0e5442b9e4c5ebe8dadb1cd1)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.10.0...v13.10.1)

### [`v13.10.0`](https://togithub.com/capricorn86/happy-dom/releases/tag/v13.10.0)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.9.0...v13.10.0)

##### 🎨 Features

-   Adds support for the Headers.getSetCookie - By **[@&#8203;betterqualityassuranceuser](https://togithub.com/betterqualityassuranceuser)** in task [#&#8203;1315](https://togithub.com/capricorn86/happy-dom/issues/1315)

### [`v13.9.0`](https://togithub.com/capricorn86/happy-dom/compare/v13.8.6...9d6d1f39aeb2cbfce914277ce22264ee88290582)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.8.6...v13.9.0)

### [`v13.8.6`](https://togithub.com/capricorn86/happy-dom/releases/tag/v13.8.6)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.8.5...v13.8.6)

##### 👷‍♂️ Patch fixes

-   Fixes bug related to multiple fallbacks to CSS variables being set incorrectly - By **[@&#8203;odanado](https://togithub.com/odanado)** in task [#&#8203;1308](https://togithub.com/capricorn86/happy-dom/issues/1308)

### [`v13.8.5`](https://togithub.com/capricorn86/happy-dom/releases/tag/v13.8.5)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.8.4...v13.8.5)

##### 👷‍♂️ Patch fixes

-   Fixes problem related to invalid pseudo query selectors matching elements (e.g. ":before" should only match the pseudo element and not the actual element) - By **[@&#8203;capricorn86](https://togithub.com/capricorn86)** in task [#&#8203;1122](https://togithub.com/capricorn86/happy-dom/issues/1122)
-   Adds support for using multiple pseudo query selectors (e.g. ":first-of-type:last-of-type") - By **[@&#8203;capricorn86](https://togithub.com/capricorn86)** in task [#&#8203;1122](https://togithub.com/capricorn86/happy-dom/issues/1122)
-   Fixes minor typo in `HTMLElementConfig` - By **[@&#8203;danbentley](https://togithub.com/danbentley)** in task [#&#8203;1306](https://togithub.com/capricorn86/happy-dom/issues/1306)

### [`v13.8.4`](https://togithub.com/capricorn86/happy-dom/releases/tag/v13.8.4)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.8.3...v13.8.4)

##### 👷‍♂️ Patch fixes

-   Adds support for returning URL relative to window location in `HTMLLinkElement.href`, `HTMLImageElement.src` and `HTMLScriptElement.src` - By **[@&#8203;capricorn86](https://togithub.com/capricorn86)** in task [#&#8203;1135](https://togithub.com/capricorn86/happy-dom/issues/1135)

### [`v13.8.3`](https://togithub.com/capricorn86/happy-dom/releases/tag/v13.8.3)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.8.2...v13.8.3)

##### 👷‍♂️ Patch fixes

-   Fixes problem where some elements (e.g. `<li>`, `<h1>` or `<table>`) doesn't allow itself as direct descendant when parsing HTML, but should allow itself as descendant when it is not at first level - By **[@&#8203;capricorn86](https://togithub.com/capricorn86)** in task [#&#8203;1039](https://togithub.com/capricorn86/happy-dom/issues/1039)

### [`v13.8.2`](https://togithub.com/capricorn86/happy-dom/compare/v13.8.1...4970c699d07d97c4a9839e25c831eef230445abf)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.8.1...v13.8.2)

### [`v13.8.1`](https://togithub.com/capricorn86/happy-dom/compare/v13.8.0...08cd42601d62f39d42d01d902a56d2441f7128e0)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.8.0...v13.8.1)

### [`v13.8.0`](https://togithub.com/capricorn86/happy-dom/releases/tag/v13.8.0)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.7.8...v13.8.0)

##### 🎨 Features

-   Adds support for Element.scrollIntoView - By **[@&#8203;capricorn86](https://togithub.com/capricorn86)** in task [#&#8203;1051](https://togithub.com/capricorn86/happy-dom/issues/1051)

### [`v13.7.8`](https://togithub.com/capricorn86/happy-dom/compare/v13.7.7...0dfe51d6006c09b2f12ec2ec4f15858ae6450060)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.7.7...v13.7.8)

### [`v13.7.7`](https://togithub.com/capricorn86/happy-dom/compare/v13.7.6...v13.7.7)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.7.6...v13.7.7)

### [`v13.7.6`](https://togithub.com/capricorn86/happy-dom/compare/v13.7.5...54d1ae080f4e91ae09bb586ad01f82050cf5db15)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.7.5...v13.7.6)

### [`v13.7.5`](https://togithub.com/capricorn86/happy-dom/releases/tag/v13.7.5)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.7.4...v13.7.5)

##### 👷‍♂️ Patch fixes

-   Modify option node to return empty string even if the value is empty string - In task [#&#8203;1138](https://togithub.com/capricorn86/happy-dom/issues/1138)

### [`v13.7.4`](https://togithub.com/capricorn86/happy-dom/compare/v13.7.3...16396f9d1f114ad70c926f56da40a31382aeabcb)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.7.3...v13.7.4)

### [`v13.7.3`](https://togithub.com/capricorn86/happy-dom/compare/v13.7.2...1bd90205d67aa78de52ea5d1ebb3c8f8db2364af)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.7.2...v13.7.3)

### [`v13.7.2`](https://togithub.com/capricorn86/happy-dom/compare/v13.7.1...3b4339d709bb9b097a8302996dc4af356f496e1a)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.7.1...v13.7.2)

### [`v13.7.1`](https://togithub.com/capricorn86/happy-dom/releases/tag/v13.7.1)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.7.0...v13.7.1)

##### 👷‍♂️ Patch fixes

-   Adds support for cloning body in `Response.clone()` - By **[@&#8203;cprecioso](https://togithub.com/cprecioso)** in task [#&#8203;1216](https://togithub.com/capricorn86/happy-dom/issues/1216)

### [`v13.7.0`](https://togithub.com/capricorn86/happy-dom/compare/v13.6.2...4c808b62f8dcfb5c85d4ac4e94b8e2ba58195e86)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.6.2...v13.7.0)

### [`v13.6.2`](https://togithub.com/capricorn86/happy-dom/releases/tag/v13.6.2)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.6.1...v13.6.2)

##### 🎨 Features

-   Add support for the ":target" pseudo query selector - By **[@&#8203;Schleuse](https://togithub.com/Schleuse)** in task [#&#8203;1221](https://togithub.com/capricorn86/happy-dom/issues/1221)

##### 👷‍♂️ Patch fixes

-   The Event listener method `handleEvent()` should be called within the listener scope - By **[@&#8203;titouanmathis](https://togithub.com/titouanmathis)** in task [#&#8203;1182](https://togithub.com/capricorn86/happy-dom/issues/1182)

### [`v13.6.1`](https://togithub.com/capricorn86/happy-dom/releases/tag/v13.6.1)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.6.0...v13.6.1)

##### 👷‍♂️ Patch fixes

-   Improves validation for the options argument in `MutationsObserver.observe()` - By **[@&#8203;romansp](https://togithub.com/romansp)** in task [#&#8203;1223](https://togithub.com/capricorn86/happy-dom/issues/1223)

### [`v13.6.0`](https://togithub.com/capricorn86/happy-dom/releases/tag/v13.6.0)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.5.3...v13.6.0)

##### 🎨 Features

-   Adds support for `Node.isEqualNode()` - By **[@&#8203;aralroca](https://togithub.com/aralroca)** in task [#&#8203;1263](https://togithub.com/capricorn86/happy-dom/issues/1263)

##### 👷‍♂️ Patch fixes

-   Adds support for the property `Document.forms` - By **[@&#8203;juandiegombr](https://togithub.com/juandiegombr)** in task [#&#8203;1260](https://togithub.com/capricorn86/happy-dom/issues/1260)
-   Adds check for if `MutationObserver` options are null, which most likely happens for code that is executed after the Window instance has been closed - By **[@&#8203;zachlankton](https://togithub.com/zachlankton)** in task [#&#8203;1217](https://togithub.com/capricorn86/happy-dom/issues/1217)

### [`v13.5.3`](https://togithub.com/capricorn86/happy-dom/releases/tag/v13.5.3)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.5.2...v13.5.3)

##### 👷‍♂️ Patch fixes

-   Improves check for invalid query selectors - By **[@&#8203;btea](https://togithub.com/btea)** in task #&#8203;0

### [`v13.5.2`](https://togithub.com/capricorn86/happy-dom/releases/tag/v13.5.2)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.5.1...v13.5.2)

##### 👷‍♂️ Patch fixes

-   Adds unit test for Vue component with SVG - By **[@&#8203;capricorn86](https://togithub.com/capricorn86)** in task [#&#8203;1271](https://togithub.com/capricorn86/happy-dom/issues/1271)

### [`v13.5.1`](https://togithub.com/capricorn86/happy-dom/releases/tag/v13.5.1)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.5.0...v13.5.1)

##### 👷‍♂️ Patch fixes

-   Fixes problem with query selectors not finding SVG elements after the v13.4.0 release - By **[@&#8203;capricorn86](https://togithub.com/capricorn86)** in task [#&#8203;1274](https://togithub.com/capricorn86/happy-dom/issues/1274)

### [`v13.5.0`](https://togithub.com/capricorn86/happy-dom/releases/tag/v13.5.0)

[Compare Source](https://togithub.com/capricorn86/happy-dom/compare/v13.4.1...v13.5.0)

##### 🎨 Features

-   Use the Node.js `ReadableStream` class  for the properties `Response.body` and `Request.body` - By **[@&#8203;diego-toro](https://togithub.com/diego-toro)** and **[@&#8203;capricorn86](https://togithub.com/capricorn86)** in task [#&#8203;1180](https://togithub.com/capricorn86/happy-dom/issues/1180)
    -   The previous implementation used the Node.js `Stream.Readable` class, which is not fully spec compliant

</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:eyJjcmVhdGVkSW5WZXIiOiIzNy4yNDUuMCIsInVwZGF0ZWRJblZlciI6IjM3LjI0NS4wIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-03-19 07:34:32 +00:00
LongYinan
398d66fac1 chore: bump up all non-major dependencies (#6107)
[![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.529.1` -> `3.536.0`](https://renovatebot.com/diffs/npm/@aws-sdk%2fclient-s3/3.529.1/3.536.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@aws-sdk%2fclient-s3/3.536.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@aws-sdk%2fclient-s3/3.536.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@aws-sdk%2fclient-s3/3.529.1/3.536.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@aws-sdk%2fclient-s3/3.529.1/3.536.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@nx/vite](https://nx.dev) ([source](https://togithub.com/nrwl/nx/tree/HEAD/packages/vite)) | [`18.0.8` -> `18.1.2`](https://renovatebot.com/diffs/npm/@nx%2fvite/18.0.8/18.1.2) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@nx%2fvite/18.1.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@nx%2fvite/18.1.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@nx%2fvite/18.0.8/18.1.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@nx%2fvite/18.0.8/18.1.2?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@vitest/coverage-istanbul](https://togithub.com/vitest-dev/vitest/tree/main/packages/coverage-istanbul#readme) ([source](https://togithub.com/vitest-dev/vitest/tree/HEAD/packages/coverage-istanbul)) | [`1.3.1` -> `1.4.0`](https://renovatebot.com/diffs/npm/@vitest%2fcoverage-istanbul/1.3.1/1.4.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@vitest%2fcoverage-istanbul/1.4.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@vitest%2fcoverage-istanbul/1.4.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@vitest%2fcoverage-istanbul/1.3.1/1.4.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@vitest%2fcoverage-istanbul/1.3.1/1.4.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@vitest/ui](https://togithub.com/vitest-dev/vitest/tree/main/packages/ui#readme) ([source](https://togithub.com/vitest-dev/vitest/tree/HEAD/packages/ui)) | [`1.3.1` -> `1.4.0`](https://renovatebot.com/diffs/npm/@vitest%2fui/1.3.1/1.4.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@vitest%2fui/1.4.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@vitest%2fui/1.4.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@vitest%2fui/1.3.1/1.4.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@vitest%2fui/1.3.1/1.4.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [vitest](https://togithub.com/vitest-dev/vitest) ([source](https://togithub.com/vitest-dev/vitest/tree/HEAD/packages/vitest)) | [`1.3.1` -> `1.4.0`](https://renovatebot.com/diffs/npm/vitest/1.3.1/1.4.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/vitest/1.4.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/vitest/1.4.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/vitest/1.3.1/1.4.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/vitest/1.3.1/1.4.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

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

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

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

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

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

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

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

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

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

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

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

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

##### Features

-   **client-s3:** This release makes the default option for S3 on Outposts request signing to use the SigV4A algorithm when using AWS Common Runtime (CRT). ([2ddd8ec](2ddd8ec13e))

#### [3.529.1](https://togithub.com/aws/aws-sdk-js-v3/compare/v3.529.0...v3.529.1) (2024-03-08)

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

</details>

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

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

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

##### 18.1.2 (2024-03-18)

##### 🚀 Features

-   **nx-dev:** add apollo.io tracking script to `_app.tsx` ([#&#8203;22339](https://togithub.com/nrwl/nx/pull/22339))

##### 🩹 Fixes

-   **core:** do not use pseudo terminal if platform is unsuported and f… ([#&#8203;22341](https://togithub.com/nrwl/nx/pull/22341))
-   **js:** read lockfile from the workspace root ([#&#8203;22340](https://togithub.com/nrwl/nx/pull/22340))

##### ❤️  Thank You

-   Benjamin Cabanes [@&#8203;bcabanes](https://togithub.com/bcabanes)
-   Jason Jean [@&#8203;FrozenPandaz](https://togithub.com/FrozenPandaz)

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

[Compare Source](5e64e7dcb0...18.1.1)

##### 18.1.1 (2024-03-15)

##### 🚀 Features

-   **angular:** update jest-preset-angular version ([#&#8203;21776](https://togithub.com/nrwl/nx/pull/21776))
-   **angular:** add the extract-i18n executor ([#&#8203;21802](https://togithub.com/nrwl/nx/pull/21802))
-   **angular:** ensure all targets are generated for application and libraries ([#&#8203;21826](https://togithub.com/nrwl/nx/pull/21826))
-   **angular:** support angular 17.2.0 ([#&#8203;21671](https://togithub.com/nrwl/nx/pull/21671))
-   **angular:** force explicit targets when NX_ADD_PLUGINS is not explicitly true ([#&#8203;21852](https://togithub.com/nrwl/nx/pull/21852))
-   **angular:** update jest-preset-angular dependency to 14.0.3 ([#&#8203;21912](https://togithub.com/nrwl/nx/pull/21912))
-   **angular:** remove optional [@&#8203;nx/cypress](https://togithub.com/nx/cypress) and [@&#8203;nx/jest](https://togithub.com/nx/jest) from dependencies ([#&#8203;22162](https://togithub.com/nrwl/nx/pull/22162))
-   **bundling:** bump rollup-plugin-typescript2 version ([#&#8203;20609](https://togithub.com/nrwl/nx/pull/20609))
-   **bundling:** rollup should support ESM config files ([#&#8203;21999](https://togithub.com/nrwl/nx/pull/21999))
-   **bundling:** crystalize rollup ([#&#8203;22045](https://togithub.com/nrwl/nx/pull/22045))
-   **core:** update swc/register ([#&#8203;21755](https://togithub.com/nrwl/nx/pull/21755))
-   **core:** add option to disable log grouping on CI ([#&#8203;21782](https://togithub.com/nrwl/nx/pull/21782))
-   **core:** remove leading arrow from output headlines ([#&#8203;21359](https://togithub.com/nrwl/nx/pull/21359))
-   **core:** remove leading arrow from output headlines" ([#&#8203;21800](https://togithub.com/nrwl/nx/pull/21800))
-   **core:** support migrating to canary versions of nx for testing ([#&#8203;21812](https://togithub.com/nrwl/nx/pull/21812))
-   **core:** flatten default base config to base ([#&#8203;19964](https://togithub.com/nrwl/nx/pull/19964))
-   **core:** execute plugins in isolated processes ([#&#8203;21760](https://togithub.com/nrwl/nx/pull/21760))
-   **core:** provide a hint when project.json has empty targets ([#&#8203;22028](https://togithub.com/nrwl/nx/pull/22028))
-   **core:** add gradle plugin ([#&#8203;21055](https://togithub.com/nrwl/nx/pull/21055))
-   **core:** use flag in nx.json for toggling crystal ([#&#8203;21980](https://togithub.com/nrwl/nx/pull/21980))
-   **core:** forward options for run command ([#&#8203;22064](https://togithub.com/nrwl/nx/pull/22064))
-   **core:** revert running plugins in isolation ([#&#8203;22246](https://togithub.com/nrwl/nx/pull/22246))
-   **core:** run commands directly ([#&#8203;21918](https://togithub.com/nrwl/nx/pull/21918))
-   **detox:** upgrade [@&#8203;config-plugins/detox](https://togithub.com/config-plugins/detox) to 7 ([#&#8203;21959](https://togithub.com/nrwl/nx/pull/21959))
-   **expo:** support cjs and mjs ([#&#8203;21408](https://togithub.com/nrwl/nx/pull/21408))
-   **graph:** add error boundary error page for project details ([#&#8203;22007](https://togithub.com/nrwl/nx/pull/22007))
-   **graph:** add spinner on the projects page ([#&#8203;22149](https://togithub.com/nrwl/nx/pull/22149))
-   **js:** replace publish script with nx release config ([#&#8203;21474](https://togithub.com/nrwl/nx/pull/21474))
-   **misc:** log message in nx init when detecting plugins ([#&#8203;21932](https://togithub.com/nrwl/nx/pull/21932))
-   **nextjs:** use global NX_GRAPH_CREATION in withNx plugin to guard against graph creation during create nodes ([#&#8203;22026](https://togithub.com/nrwl/nx/pull/22026))
-   **nuxt:** export storybook generator ([#&#8203;21969](https://togithub.com/nrwl/nx/pull/21969))
-   **nx-dev:** update launch conf timings ([a0e4cf747d](https://togithub.com/nrwl/nx/commit/a0e4cf747d))
-   **nx-dev:** update launch page link text ([#&#8203;21747](https://togithub.com/nrwl/nx/pull/21747))
-   **nx-dev:** update website header components ([#&#8203;21833](https://togithub.com/nrwl/nx/pull/21833))
-   **nx-dev:** change color for nx-agents & nx-ai buttons on home ([#&#8203;22142](https://togithub.com/nrwl/nx/pull/22142))
-   **react:** add tailwind as style prompt option for app gen ([#&#8203;21784](https://togithub.com/nrwl/nx/pull/21784))
-   **release:** prompt to create github release when no file changes ([#&#8203;21819](https://togithub.com/nrwl/nx/pull/21819))
-   **release:** interpolate workspaceRoot in changelog path ([#&#8203;22058](https://togithub.com/nrwl/nx/pull/22058))
-   **release:** add conventional commits configurability for version and changelog ([#&#8203;22004](https://togithub.com/nrwl/nx/pull/22004))
-   **remix:** add playwright option for e2eTestRunner ([#&#8203;21603](https://togithub.com/nrwl/nx/pull/21603))
-   **remix:** upgrade to latest remix 2.6.0 ([#&#8203;21843](https://togithub.com/nrwl/nx/pull/21843))
-   **remix:** use Remix CLI directly with Remix Crystal Plugin ([#&#8203;22234](https://togithub.com/nrwl/nx/pull/22234))
-   **remix:** support version 2.8.0 ([#&#8203;22326](https://togithub.com/nrwl/nx/pull/22326))
-   **remix:** add option to create-nx-workspace ([#&#8203;22334](https://togithub.com/nrwl/nx/pull/22334))
-   **repo:** use latest pnpm for CI runs ([#&#8203;22207](https://togithub.com/nrwl/nx/pull/22207))
-   **testing:** update cypress version ([#&#8203;21961](https://togithub.com/nrwl/nx/pull/21961))
-   **testing:** add getJestProjectsAsync to support inferred targets ([#&#8203;21897](https://togithub.com/nrwl/nx/pull/21897))
-   **vite:** add vitest.workspace.ts at root ([#&#8203;21915](https://togithub.com/nrwl/nx/pull/21915))

##### 🩹 Fixes

-   **angular:** fix wrong trailing comma in mf bootstrap code generation ([#&#8203;21600](https://togithub.com/nrwl/nx/pull/21600))
-   **angular:** support inferred cypress targets in setup-mf generator ([#&#8203;21619](https://togithub.com/nrwl/nx/pull/21619))
-   **angular:** ajv hoisting issue ([#&#8203;21641](https://togithub.com/nrwl/nx/pull/21641))
-   **angular:** resolve the index html transformer correctly for esbuild based build targets in dev-server ([#&#8203;21679](https://togithub.com/nrwl/nx/pull/21679))
-   **angular:** generate app server module setup correctly in setup-ssr generator ([#&#8203;21702](https://togithub.com/nrwl/nx/pull/21702))
-   **angular:** add missing forceEsbuild option to dev-server executor ([#&#8203;21753](https://togithub.com/nrwl/nx/pull/21753))
-   **angular:** do not force explicit targets for separate e2e projects ([#&#8203;21865](https://togithub.com/nrwl/nx/pull/21865))
-   **angular:** stop using npmScope as a prefix for component and directive selectors ([#&#8203;21828](https://togithub.com/nrwl/nx/pull/21828))
-   **angular:** do not add target defaults for the ng-packagr-lite executor when generating non-buildable library ([#&#8203;21935](https://togithub.com/nrwl/nx/pull/21935))
-   **angular:** ensure generated editor tsconfig in apps only include runtime files ([#&#8203;21945](https://togithub.com/nrwl/nx/pull/21945))
-   **angular:** log message about unsupported ng cache command ([#&#8203;22154](https://togithub.com/nrwl/nx/pull/22154))
-   **angular:** fix message logged for unsupported ng cache ([#&#8203;22211](https://togithub.com/nrwl/nx/pull/22211))
-   **angular:** Module federation with Crystal enabled. ([#&#8203;22224](https://togithub.com/nrwl/nx/pull/22224))
-   **angular:** install jsonc-eslint-parser only when [@&#8203;nx/dependency-checks](https://togithub.com/nx/dependency-checks) is used ([#&#8203;22231](https://togithub.com/nrwl/nx/pull/22231))
-   **core:** nx cloud prompt during migrate doesn't skip connection ([#&#8203;21588](https://togithub.com/nrwl/nx/pull/21588))
-   **core:** pass the full resolved path of ts-node/esm when reloading the CLI ([#&#8203;21607](https://togithub.com/nrwl/nx/pull/21607))
-   **core:** remove logic to reload process with esm loader for Node 18 ([#&#8203;21623](https://togithub.com/nrwl/nx/pull/21623))
-   **core:** prevent target defaults from being discarded during merge process ([#&#8203;21624](https://togithub.com/nrwl/nx/pull/21624))
-   **core:** add missing parts to ci workflws and update docs ([ab76d6291a](https://togithub.com/nrwl/nx/commit/ab76d6291a))
-   **core:** temporary use forked portable_pty to inherit cursor position for windows ([#&#8203;21683](https://togithub.com/nrwl/nx/pull/21683))
-   **core:** handle blocking stdin ([#&#8203;21672](https://togithub.com/nrwl/nx/pull/21672))
-   **core:** remove implementation detail from warning ([18efd62003](https://togithub.com/nrwl/nx/commit/18efd62003))
-   **core:** static run one lifecycle should always print dependent task status, and output when verbose ([#&#8203;21720](https://togithub.com/nrwl/nx/pull/21720))
-   **core:** run migrations ordered by their target version ([#&#8203;21799](https://togithub.com/nrwl/nx/pull/21799))
-   **core:** Update NxWelcome connect to cloud ([#&#8203;21830](https://togithub.com/nrwl/nx/pull/21830))
-   **core:** propagate `verbose` flag when running `init` generator dur… ([#&#8203;21868](https://togithub.com/nrwl/nx/pull/21868))
-   **core:** ensure migrate works with yarn PnP ([#&#8203;21824](https://togithub.com/nrwl/nx/pull/21824))
-   **core:** align terminal output padding and remove leading arrow ([#&#8203;21809](https://togithub.com/nrwl/nx/pull/21809))
-   **core:** read all targets from package json when defining target defaults ([#&#8203;21719](https://togithub.com/nrwl/nx/pull/21719))
-   **core:** include nx/nuxt in migrations ([#&#8203;21885](https://togithub.com/nrwl/nx/pull/21885))
-   **core:** do not use the new pty function for older versions of windows ([#&#8203;21854](https://togithub.com/nrwl/nx/pull/21854))
-   **core:** normalize migration target versions when sorting migrations ([#&#8203;21967](https://togithub.com/nrwl/nx/pull/21967))
-   **core:** target defaults application shouldn't include extra scripts ([#&#8203;21970](https://togithub.com/nrwl/nx/pull/21970))
-   **core:** update generated README pages with more useful instructions ([#&#8203;21976](https://togithub.com/nrwl/nx/pull/21976))
-   **core:** plugin pool should not clobber promises when called multiple times ([#&#8203;21977](https://togithub.com/nrwl/nx/pull/21977))
-   **core:** plugins should not be registered twice and should respect shutdown queue ([#&#8203;22057](https://togithub.com/nrwl/nx/pull/22057))
-   **core:** nextjs-standalone generates package scripts consistent with create-next-app ([#&#8203;21996](https://togithub.com/nrwl/nx/pull/21996))
-   **core:** target defaults should represent nx.json in source info ([#&#8203;22080](https://togithub.com/nrwl/nx/pull/22080))
-   **core:** setting up .nx inside gradle shouldn't throw ([#&#8203;21957](https://togithub.com/nrwl/nx/pull/21957))
-   **core:** add outputs to nx.json for nx init in monorepo ([#&#8203;22061](https://togithub.com/nrwl/nx/pull/22061))
-   **core:** fix no such file or directory, open 'package-lock.json' ([#&#8203;21835](https://togithub.com/nrwl/nx/pull/21835))
-   **core:** reject all promises in pool during shutdown ([#&#8203;22188](https://togithub.com/nrwl/nx/pull/22188))
-   **core:** fix terminal message alignment on errors ([#&#8203;22189](https://togithub.com/nrwl/nx/pull/22189))
-   **core:** only start plugin workers once ([#&#8203;22222](https://togithub.com/nrwl/nx/pull/22222))
-   **core:** properly cleanup when the project-graph creation fails ([#&#8203;22243](https://togithub.com/nrwl/nx/pull/22243))
-   **core:** make windows runtime input hashing windowless ([#&#8203;22197](https://togithub.com/nrwl/nx/pull/22197))
-   **core:** fix gh group success icon ([#&#8203;22281](https://togithub.com/nrwl/nx/pull/22281))
-   **core:** fix pty for multiple commands in 1 process ([#&#8203;22294](https://togithub.com/nrwl/nx/pull/22294))
-   **devkit:** respect expectComments when parsing json ([#&#8203;21584](https://togithub.com/nrwl/nx/pull/21584))
-   **graph:** fix open project with / in name ([#&#8203;21722](https://togithub.com/nrwl/nx/pull/21722))
-   **graph:** show command property as monospace ([#&#8203;21997](https://togithub.com/nrwl/nx/pull/21997))
-   **js:** babel preset should also check for JEST_WORKER_ID to transpile to CJS ([#&#8203;21754](https://togithub.com/nrwl/nx/pull/21754))
-   **js:** nx release-version resolve-version-spec should normalize fetchSpec ([#&#8203;21710](https://togithub.com/nrwl/nx/pull/21710))
-   **js:** swc executor should support inlining on windows ([#&#8203;21801](https://togithub.com/nrwl/nx/pull/21801))
-   **js:** set moduleResolution to Node10 so it is compatible with CommonJS module ([#&#8203;21979](https://togithub.com/nrwl/nx/pull/21979))
-   **js:** use NodeJs moduleResolution with ts-node to support CommonJS module and TS 4.x ([#&#8203;22258](https://togithub.com/nrwl/nx/pull/22258))
-   **linter:** adjust terminal run check for crystal ([#&#8203;21638](https://togithub.com/nrwl/nx/pull/21638))
-   **linter:** fix eslint-plugin migration target version ([#&#8203;21966](https://togithub.com/nrwl/nx/pull/21966))
-   **linter:** add v7 of typescript-eslint to peerDeps ([#&#8203;21853](https://togithub.com/nrwl/nx/pull/21853))
-   **linter:** refactor pcv3 plugin, expose configFiles on context ([#&#8203;21677](https://togithub.com/nrwl/nx/pull/21677))
-   **misc:** handle workspaces if no plugin selected in nx init and only generate files after prompts ([#&#8203;21606](https://togithub.com/nrwl/nx/pull/21606))
-   **misc:** ensure swc transpiler process required files ([#&#8203;21674](https://togithub.com/nrwl/nx/pull/21674))
-   **misc:** pin generated vite version to ~5.0.0 to avoid issues with storybook ([#&#8203;21740](https://togithub.com/nrwl/nx/pull/21740))
-   **misc:** logs from rm-default-collection should render properly ([#&#8203;21953](https://togithub.com/nrwl/nx/pull/21953))
-   **misc:** set nx property in root package.json when no replacing script in nx init ([#&#8203;21974](https://togithub.com/nrwl/nx/pull/21974))
-   **misc:** migration should shutdown plugin workers if it starts them ([#&#8203;22048](https://togithub.com/nrwl/nx/pull/22048))
-   **misc:** make sure to add e2e crystal plugin ([#&#8203;22041](https://togithub.com/nrwl/nx/pull/22041))
-   **misc:** fix buildable libs utils calculating dependent projects from task graph ([#&#8203;22015](https://togithub.com/nrwl/nx/pull/22015))
-   **misc:** add missing format files call ([#&#8203;22137](https://togithub.com/nrwl/nx/pull/22137))
-   **misc:** improve package.json scripts handling when running "nx init" and "nx add" ([#&#8203;22168](https://togithub.com/nrwl/nx/pull/22168))
-   **misc:** do not add includedScripts unless really needed when running nx add ([#&#8203;22180](https://togithub.com/nrwl/nx/pull/22180))
-   **module-federation:** map static remote locations correctly ([#&#8203;21709](https://togithub.com/nrwl/nx/pull/21709))
-   **module-federation:** ensure targetDefaults for module federation executors are setup correctly ([#&#8203;22282](https://togithub.com/nrwl/nx/pull/22282))
-   **nextjs:** move `next/constants` from top-level import to when it is needed ([#&#8203;21612](https://togithub.com/nrwl/nx/pull/21612))
-   **nextjs:** Enable next e2e test ([#&#8203;21625](https://togithub.com/nrwl/nx/pull/21625))
-   **nextjs:** src package.json should not be copied to output folder ([aa622bab5a](https://togithub.com/nrwl/nx/commit/aa622bab5a))
-   **nextjs:** Custom server should work with Crystal ([#&#8203;21736](https://togithub.com/nrwl/nx/pull/21736))
-   **nextjs:** Svg should work when svgr is true in next config ([#&#8203;21761](https://togithub.com/nrwl/nx/pull/21761))
-   **nextjs:** Add missing e2e-ci target for cypress ([#&#8203;21805](https://togithub.com/nrwl/nx/pull/21805))
-   **nextjs:** Add spec files when creating a next app ([#&#8203;22079](https://togithub.com/nrwl/nx/pull/22079))
-   **nextjs:** avoid path error on dev  server creation ([#&#8203;21998](https://togithub.com/nrwl/nx/pull/21998))
-   **nextjs:** Adding styles to nextjs cypress should not fail. ([#&#8203;22170](https://togithub.com/nrwl/nx/pull/22170))
-   **nextjs:** Surface error codes when build is interrupted by signals SIGINT, SIGTERM etc... ([#&#8203;22190](https://togithub.com/nrwl/nx/pull/22190))
-   **nextjs:** runCLI stdio ([#&#8203;22267](https://togithub.com/nrwl/nx/pull/22267))
-   **node:** Broken E2E tests ([#&#8203;21569](https://togithub.com/nrwl/nx/pull/21569))
-   **node:** Increase timeout for CI ([#&#8203;22003](https://togithub.com/nrwl/nx/pull/22003))
-   **nuxt:** init generator should add [@&#8203;nx/vite](https://togithub.com/nx/vite) to dependencies ([#&#8203;21911](https://togithub.com/nrwl/nx/pull/21911))
-   **nuxt:** turn on autoimport ([#&#8203;21894](https://togithub.com/nrwl/nx/pull/21894))
-   **nuxt:** tsconfig types and output dir ([#&#8203;21934](https://togithub.com/nrwl/nx/pull/21934))
-   **nuxt:** fix storybook preview config path ([#&#8203;22020](https://togithub.com/nrwl/nx/pull/22020))
-   **nuxt:** Add e2e-ci and serve-static targets ([#&#8203;22056](https://togithub.com/nrwl/nx/pull/22056))
-   **nx-dev:** redirect core-features page ([#&#8203;21616](https://togithub.com/nrwl/nx/pull/21616))
-   **nx-dev:** launch page mobile experience ([de676e207f](https://togithub.com/nrwl/nx/commit/de676e207f))
-   **nx-dev:** redirect on remote caching page ([#&#8203;21669](https://togithub.com/nrwl/nx/pull/21669))
-   **nx-dev:** remove fence from new packages and "nx add" commands ([#&#8203;21705](https://togithub.com/nrwl/nx/pull/21705))
-   **nx-dev:** add colors to ms logo ([#&#8203;21790](https://togithub.com/nrwl/nx/pull/21790))
-   **nx-plugin:** do not print duplicated warning about derived format when generating plugin ([#&#8203;22230](https://togithub.com/nrwl/nx/pull/22230))
-   **nx-plugin:** support root tsconfig.json in nx-plugin-checks eslint rule ([4850bdb6aa](https://togithub.com/nrwl/nx/commit/4850bdb6aa))
-   **playwright:** fix include in tsconfig.json ([#&#8203;21730](https://togithub.com/nrwl/nx/pull/21730))
-   **react:** generate correctly when --js is used for module federation host/remote ([#&#8203;20119](https://togithub.com/nrwl/nx/pull/20119))
-   **react:** full support custom secure host for module federation ([#&#8203;21777](https://togithub.com/nrwl/nx/pull/21777))
-   **react:** ensure playwright configuration is using correct port in app gen ([#&#8203;21941](https://togithub.com/nrwl/nx/pull/21941))
-   **react:** pass correct argument to rspack configuration generator ([#&#8203;22241](https://togithub.com/nrwl/nx/pull/22241))
-   **react-native:** change gradlew to absolute path ([#&#8203;21725](https://togithub.com/nrwl/nx/pull/21725))
-   **react-native:** add all flag to sync-deps ([#&#8203;21821](https://togithub.com/nrwl/nx/pull/21821))
-   **react-native:** pin ajv version to 8.12.0 ([#&#8203;22002](https://togithub.com/nrwl/nx/pull/22002))
-   **release:** logging improvements ([#&#8203;21692](https://togithub.com/nrwl/nx/pull/21692))
-   **release:** ensure `nx release publish --graph` only includes projects with target ([#&#8203;21726](https://togithub.com/nrwl/nx/pull/21726))
-   **release:** do not stop daemon in dry-run ([#&#8203;21743](https://togithub.com/nrwl/nx/pull/21743))
-   **release:** skip prompt for publish when no version created ([#&#8203;21769](https://togithub.com/nrwl/nx/pull/21769))
-   **release:** use --first-parent to support merged repos ([#&#8203;21686](https://togithub.com/nrwl/nx/pull/21686))
-   **release:** move github release creation to git tasks ([#&#8203;21510](https://togithub.com/nrwl/nx/pull/21510))
-   **release:** currentVersionResolver git-tag should prefer merged tags ([#&#8203;22082](https://togithub.com/nrwl/nx/pull/22082))
-   **release:** skip lock file update if workspaces are not enabled ([#&#8203;22055](https://togithub.com/nrwl/nx/pull/22055))
-   **release:** store rawVersionSpec on versionData ([#&#8203;22071](https://togithub.com/nrwl/nx/pull/22071))
-   **release:** fix default renderer resolution to be relative within t… ([#&#8203;22331](https://togithub.com/nrwl/nx/pull/22331))
-   **remix:** do not rename root jest.preset.js ([#&#8203;21703](https://togithub.com/nrwl/nx/pull/21703))
-   **remix:** should add remix plugin to nx.json on init correctly ([#&#8203;21827](https://togithub.com/nrwl/nx/pull/21827))
-   **remix:** the output path should respect the remix.config.js in crystal ([#&#8203;21842](https://togithub.com/nrwl/nx/pull/21842))
-   **remix:** adjust remix start script when building ([#&#8203;21883](https://togithub.com/nrwl/nx/pull/21883))
-   **remix:** typo in tsconfig.spec.json update led to invalid tsconfig ([#&#8203;21886](https://togithub.com/nrwl/nx/pull/21886))
-   **remix:** ensure component-testing is exported correctly [#&#8203;22091](https://togithub.com/nrwl/nx/issues/22091) ([#&#8203;22095](https://togithub.com/nrwl/nx/pull/22095), [#&#8203;22091](https://togithub.com/nrwl/nx/issues/22091))
-   **repo:** update browser tools to fix ci ([#&#8203;21955](https://togithub.com/nrwl/nx/pull/21955))
-   **storybook:** handle main.js file correctly in storybook plugin ([#&#8203;22081](https://togithub.com/nrwl/nx/pull/22081))
-   **testing:** cleanup e2e atomization plugins ([#&#8203;21688](https://togithub.com/nrwl/nx/pull/21688))
-   **testing:** increase the default timeout to 15s for the dev server to start ([#&#8203;21716](https://togithub.com/nrwl/nx/pull/21716))
-   **testing:** ensure cypress closes the web dev server ([#&#8203;21759](https://togithub.com/nrwl/nx/pull/21759))
-   **testing:** jest should handle root jest.preset.cjs ([#&#8203;21746](https://togithub.com/nrwl/nx/pull/21746))
-   **testing:** fix cypress project targets does not exist ([#&#8203;21785](https://togithub.com/nrwl/nx/pull/21785))
-   **testing:** pin cypress version to avoid issue with verifying cypress ([#&#8203;21917](https://togithub.com/nrwl/nx/pull/21917))
-   **testing:** ensure baseUrl is not passed to playwright cli ([#&#8203;21943](https://togithub.com/nrwl/nx/pull/21943))
-   **testing:** playwright plugin enoent error ([#&#8203;21951](https://togithub.com/nrwl/nx/pull/21951))
-   **testing:** add null checks when reading targets ([#&#8203;21952](https://togithub.com/nrwl/nx/pull/21952))
-   **testing:** calculate correct support file path in cypress e2e preset ([#&#8203;22096](https://togithub.com/nrwl/nx/pull/22096))
-   **testing:** increase the default timeout to 60s for the cypress web dev server to start ([#&#8203;22132](https://togithub.com/nrwl/nx/pull/22132))
-   **testing:** close cypress web server correctly on windows ([#&#8203;22125](https://togithub.com/nrwl/nx/pull/22125))
-   **testing:** resolve cypress config glob pattern correctly to handle root projects ([#&#8203;22165](https://togithub.com/nrwl/nx/pull/22165))
-   **testing:** minor adjustment to the config generation template ([#&#8203;22175](https://togithub.com/nrwl/nx/pull/22175))
-   **testing:** fix project config might not be defined ([#&#8203;22174](https://togithub.com/nrwl/nx/pull/22174))
-   **vite:** import esbuild before loading config to keep it in cache ([#&#8203;21685](https://togithub.com/nrwl/nx/pull/21685))
-   **vite:** normalize vitest cli args in executor ([#&#8203;21870](https://togithub.com/nrwl/nx/pull/21870))
-   **vite:** project conversion generator ([#&#8203;21646](https://togithub.com/nrwl/nx/pull/21646))
-   **vite:** update vitest and use parseCLI ([#&#8203;21890](https://togithub.com/nrwl/nx/pull/21890))
-   **vite:** Storing nxjson details too early ([#&#8203;22285](https://togithub.com/nrwl/nx/pull/22285))
-   **vue:** fixing vue and nuxt welcome templates ([#&#8203;21792](https://togithub.com/nrwl/nx/pull/21792))
-   **vue:** tailwind generator ignoring styleSheet option ([#&#8203;21840](https://togithub.com/nrwl/nx/pull/21840))
-   **vue:** small typo in CNW description ([#&#8203;21888](https://togithub.com/nrwl/nx/pull/21888))
-   **webpack:** require ForkTsCheckerWebpackPlugin only as required ([#&#8203;21629](https://togithub.com/nrwl/nx/pull/21629))
-   **webpack:** resolve relative path for assets inputs ([#&#8203;21822](https://togithub.com/nrwl/nx/pull/21822))
-   **webpack:** correctly handle paranthesis in PostCSS in url ([#&#8203;21884](https://togithub.com/nrwl/nx/pull/21884))
-   **webpack:** surface original error when remotes fail to start ([#&#8203;21919](https://togithub.com/nrwl/nx/pull/21919))

##### ❤️  Thank You

-   Alex Swindler
-   Alon Valadji [@&#8203;alonronin](https://togithub.com/alonronin)
-   Austin Fahsl [@&#8203;fahslaj](https://togithub.com/fahslaj)
-   Benjamin Cabanes [@&#8203;bcabanes](https://togithub.com/bcabanes)
-   Colum Ferry [@&#8203;Coly010](https://togithub.com/Coly010)
-   Craigory Coppola [@&#8203;AgentEnder](https://togithub.com/AgentEnder)
-   Dan Roujinsky
-   Edouard Bozon [@&#8203;edbzn](https://togithub.com/edbzn)
-   Emily Xiong [@&#8203;xiongemi](https://togithub.com/xiongemi)
-   Jack Hsu [@&#8203;jaysoo](https://togithub.com/jaysoo)
-   James Henry [@&#8203;JamesHenry](https://togithub.com/JamesHenry)
-   Jason Jean [@&#8203;FrozenPandaz](https://togithub.com/FrozenPandaz)
-   Javier Abia [@&#8203;weberjavi](https://togithub.com/weberjavi)
-   Jonathan Cammisuli
-   Julian Martin
-   Juri [@&#8203;juristr](https://togithub.com/juristr)
-   Juri Strumpflohner [@&#8203;juristr](https://togithub.com/juristr)
-   Katerina Skroumpelou [@&#8203;mandarini](https://togithub.com/mandarini)
-   Leosvel Pérez Espinosa [@&#8203;leosvelperez](https://togithub.com/leosvelperez)
-   MaxKless [@&#8203;MaxKless](https://togithub.com/MaxKless)
-   Miroslav Jonas [@&#8203;meeroslav](https://togithub.com/meeroslav)
-   Miroslav Jonaš [@&#8203;meeroslav](https://togithub.com/meeroslav)
-   Nicholas Cunningham [@&#8203;ndcunningham](https://togithub.com/ndcunningham)
-   Nikita Barsukov [@&#8203;nsbarsukov](https://togithub.com/nsbarsukov)
-   Philip Fulcher
-   Remco Krams
-   Steven Nance [@&#8203;llwt](https://togithub.com/llwt)
-   Tine Kondo [@&#8203;tinesoft](https://togithub.com/tinesoft)
-   Vadim Goy
-   Victor Login [@&#8203;batazor](https://togithub.com/batazor)
-   Viktor Pöntinen
-   Yu Zheng
-   Zachary DeRose [@&#8203;ZackDeRose](https://togithub.com/ZackDeRose)

### [`v18.1.0`](https://togithub.com/nrwl/nx/compare/18.0.8...5e64e7dcb011fd164e22f87a9f2a6358a7c2cd32)

[Compare Source](https://togithub.com/nrwl/nx/compare/18.0.8...5e64e7dcb011fd164e22f87a9f2a6358a7c2cd32)

</details>

<details>
<summary>vitest-dev/vitest (@&#8203;vitest/coverage-istanbul)</summary>

### [`v1.4.0`](https://togithub.com/vitest-dev/vitest/releases/tag/v1.4.0)

[Compare Source](https://togithub.com/vitest-dev/vitest/compare/v1.3.1...v1.4.0)

#####    🚀 Features

-   Throw error when using snapshot assertion with `not`  -  by [@&#8203;fenghan34](https://togithub.com/fenghan34) in [https://github.com/vitest-dev/vitest/issues/5294](https://togithub.com/vitest-dev/vitest/issues/5294) [<samp>(b9d37)</samp>](https://togithub.com/vitest-dev/vitest/commit/b9d378f5)
-   Add a flag to include test location in tasks  -  by [@&#8203;sheremet-va](https://togithub.com/sheremet-va) in [https://github.com/vitest-dev/vitest/issues/5342](https://togithub.com/vitest-dev/vitest/issues/5342) [<samp>(d627e)</samp>](https://togithub.com/vitest-dev/vitest/commit/d627e209)
-   **cli**:
    -   Support wildcards in `--project` option  -  by [@&#8203;fenghan34](https://togithub.com/fenghan34) in [https://github.com/vitest-dev/vitest/issues/5295](https://togithub.com/vitest-dev/vitest/issues/5295) [<samp>(201bd)</samp>](https://togithub.com/vitest-dev/vitest/commit/201bd067)
-   **config**:
    -   Add `shuffle.files` and `shuffle.tests` options  -  by [@&#8203;fenghan34](https://togithub.com/fenghan34) in [https://github.com/vitest-dev/vitest/issues/5281](https://togithub.com/vitest-dev/vitest/issues/5281) [<samp>(356db)</samp>](https://togithub.com/vitest-dev/vitest/commit/356db87b)
    -   Deprecate `cache.dir` option  -  by [@&#8203;fenghan34](https://togithub.com/fenghan34) in [https://github.com/vitest-dev/vitest/issues/5229](https://togithub.com/vitest-dev/vitest/issues/5229) [<samp>(d7e8b)</samp>](https://togithub.com/vitest-dev/vitest/commit/d7e8b53e)
-   **coverage**:
    -   Support `--changed` option  -  by [@&#8203;AriPerkkio](https://togithub.com/AriPerkkio) in [https://github.com/vitest-dev/vitest/issues/5314](https://togithub.com/vitest-dev/vitest/issues/5314) [<samp>(600b4)</samp>](https://togithub.com/vitest-dev/vitest/commit/600b44d6)
-   **vitest**:
    -   Support `clearScreen` cli flag  -  by [@&#8203;hi-ogawa](https://togithub.com/hi-ogawa) in [https://github.com/vitest-dev/vitest/issues/5241](https://togithub.com/vitest-dev/vitest/issues/5241) [<samp>(e1735)</samp>](https://togithub.com/vitest-dev/vitest/commit/e1735fb6)

#####    🐞 Bug Fixes

-   Repeatable `--project` option  -  by [@&#8203;fenghan34](https://togithub.com/fenghan34) in [https://github.com/vitest-dev/vitest/issues/5265](https://togithub.com/vitest-dev/vitest/issues/5265) [<samp>(d1a06)</samp>](https://togithub.com/vitest-dev/vitest/commit/d1a06730)
-   `--inspect-brk` to pause before execution  -  by [@&#8203;AriPerkkio](https://togithub.com/AriPerkkio) in [https://github.com/vitest-dev/vitest/issues/5355](https://togithub.com/vitest-dev/vitest/issues/5355) [<samp>(e77c5)</samp>](https://togithub.com/vitest-dev/vitest/commit/e77c553f)
-   Correct locations in test.each tasks  -  by [@&#8203;sheremet-va](https://togithub.com/sheremet-va) [<samp>(4f6e3)</samp>](https://togithub.com/vitest-dev/vitest/commit/4f6e39c1)
-   **api**:
    -   Use resolvedUrls from devserver  -  by [@&#8203;saitonakamura](https://togithub.com/saitonakamura) and [@&#8203;hi-ogawa](https://togithub.com/hi-ogawa) in [https://github.com/vitest-dev/vitest/issues/5289](https://togithub.com/vitest-dev/vitest/issues/5289) [<samp>(2fef5)</samp>](https://togithub.com/vitest-dev/vitest/commit/2fef5a7e)
-   **browser**:
    -   Add `magic-string` to `optimizeDeps.include`  -  by [@&#8203;hi-ogawa](https://togithub.com/hi-ogawa) in [https://github.com/vitest-dev/vitest/issues/5278](https://togithub.com/vitest-dev/vitest/issues/5278) [<samp>(8f04e)</samp>](https://togithub.com/vitest-dev/vitest/commit/8f04e798)
-   **coverage**:
    -   Expensive regexp hangs v8 report generation  -  by [@&#8203;AriPerkkio](https://togithub.com/AriPerkkio) in [https://github.com/vitest-dev/vitest/issues/5259](https://togithub.com/vitest-dev/vitest/issues/5259) [<samp>(d68a7)</samp>](https://togithub.com/vitest-dev/vitest/commit/d68a7390)
    -   V8 to ignore type-only files  -  by [@&#8203;AriPerkkio](https://togithub.com/AriPerkkio) in [https://github.com/vitest-dev/vitest/issues/5328](https://togithub.com/vitest-dev/vitest/issues/5328) [<samp>(c3eb8)</samp>](https://togithub.com/vitest-dev/vitest/commit/c3eb8deb)
    -   Respect source maps of pre-transpiled sources  -  by [@&#8203;AriPerkkio](https://togithub.com/AriPerkkio) in [https://github.com/vitest-dev/vitest/issues/5367](https://togithub.com/vitest-dev/vitest/issues/5367) [<samp>(6eda4)</samp>](https://togithub.com/vitest-dev/vitest/commit/6eda473f)
    -   Prevent `reportsDirectory` from removing user's project  -  by [@&#8203;AriPerkkio](https://togithub.com/AriPerkkio) in [https://github.com/vitest-dev/vitest/issues/5376](https://togithub.com/vitest-dev/vitest/issues/5376) [<samp>(07ec3)</samp>](https://togithub.com/vitest-dev/vitest/commit/07ec3779)
-   **expect**:
    -   Show diff on `toContain/toMatch` assertion error  -  by [@&#8203;hi-ogawa](https://togithub.com/hi-ogawa) in [https://github.com/vitest-dev/vitest/issues/5267](https://togithub.com/vitest-dev/vitest/issues/5267) [<samp>(8ee59)</samp>](https://togithub.com/vitest-dev/vitest/commit/8ee59f0d)
-   **forks**:
    -   Wrap `defines` to support `undefined` values  -  by [@&#8203;AriPerkkio](https://togithub.com/AriPerkkio) in [https://github.com/vitest-dev/vitest/issues/5284](https://togithub.com/vitest-dev/vitest/issues/5284) [<samp>(5b58b)</samp>](https://togithub.com/vitest-dev/vitest/commit/5b58b399)
-   **typecheck**:
    -   Update get-tsconfig 4.7.3 to fix false circularity error  -  by [@&#8203;hi-ogawa](https://togithub.com/hi-ogawa) in [https://github.com/vitest-dev/vitest/issues/5384](https://togithub.com/vitest-dev/vitest/issues/5384) [<samp>(bdc37)</samp>](https://togithub.com/vitest-dev/vitest/commit/bdc371ee)
-   **ui**:
    -   Escape html in error diff  -  by [@&#8203;hi-ogawa](https://togithub.com/hi-ogawa) in [https://github.com/vitest-dev/vitest/issues/5325](https://togithub.com/vitest-dev/vitest/issues/5325) [<samp>(ab60b)</samp>](https://togithub.com/vitest-dev/vitest/commit/ab60bf8d)
-   **vitest**:
    -   Loosen `onConsoleLog` return type  -  by [@&#8203;hi-ogawa](https://togithub.com/hi-ogawa) in [https://github.com/vitest-dev/vitest/issues/5337](https://togithub.com/vitest-dev/vitest/issues/5337) [<samp>(6d1b1)</samp>](https://togithub.com/vitest-dev/vitest/commit/6d1b1451)
    -   Ensure restoring terminal cursor on close  -  by [@&#8203;hi-ogawa](https://togithub.com/hi-ogawa) in [https://github.com/vitest-dev/vitest/issues/5292](https://togithub.com/vitest-dev/vitest/issues/5292) [<samp>(0bea2)</samp>](https://togithub.com/vitest-dev/vitest/commit/0bea2247)
    -   Ignore timeout on websocket reporter rpc  -  by [@&#8203;sheremet-va](https://togithub.com/sheremet-va) [<samp>(38119)</samp>](https://togithub.com/vitest-dev/vitest/commit/38119b75)
    -   Correctly override api with --no-api flag  -  by [@&#8203;sheremet-va](https://togithub.com/sheremet-va) in [https://github.com/vitest-dev/vitest/issues/5386](https://togithub.com/vitest-dev/vitest/issues/5386) [<samp>(51d1d)</samp>](https://togithub.com/vitest-dev/vitest/commit/51d1d472)
    -   Logs in `beforeAll` and `afterAll`  -  by [@&#8203;fenghan34](https://togithub.com/fenghan34) in [https://github.com/vitest-dev/vitest/issues/5288](https://togithub.com/vitest-dev/vitest/issues/5288) [<samp>(ce5ca)</samp>](https://togithub.com/vitest-dev/vitest/commit/ce5ca6bf)
-   **workspace**:
    -   Throw error when browser mode and `@vitest/coverage-v8` are used  -  by [@&#8203;AriPerkkio](https://togithub.com/AriPerkkio) in [https://github.com/vitest-dev/vitest/issues/5250](https://togithub.com/vitest-dev/vitest/issues/5250) [<samp>(29f98)</samp>](https://togithub.com/vitest-dev/vitest/commit/29f98cd3)

#####     [View changes on GitHub](https://togithub.com/vitest-dev/vitest/compare/v1.3.1...v1.4.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.

👻 **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:eyJjcmVhdGVkSW5WZXIiOiIzNy4yMzguMSIsInVwZGF0ZWRJblZlciI6IjM3LjI0NS4wIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-03-19 03:01:03 +00:00
liuyi
797e3c5b35 fix(server): do not force sign in password length (#6188)
hotfix
2024-03-19 10:58:34 +08:00
liuyi
bba1a95f9c chore: bump base version to 0.14.0 (#6170) 2024-03-19 02:34:18 +00:00
DarkSky
da32682afb fix(server): handle expired lock re-release & external locker injection (#6145) 2024-03-19 02:16:47 +00:00
liuyi
4702c1a9ca fix(server): inject correct locker to request scope mutex (#6140) 2024-03-19 02:16:35 +00:00
DarkSky
f18133af82 fix(server): wrap read-modify-write apis with distributed lock (#6142) 2024-03-19 02:16:24 +00:00
liuyi
a4cd8d6ca3 chore(server): organize server configs (#6169) 2024-03-19 02:05:56 +00:00
liuyi
a721b3887b fix(server): hotfix auth & doc push (#6168) 2024-03-18 16:32:35 +08:00
Peng Xiao
5693d90451 e2e(core): add test for split view (#6133) 2024-03-18 07:04:06 +00:00
Peng Xiao
dc8f351051 refactor(component): render react element into lit (#6124)
See docs in https://insider.affine.pro/share/af3478a2-9c9c-4d16-864d-bffa1eb10eb6/oL1ifjA4rKv7HRn5nYzIF

This PR also enables opening split view by ctrl-click a page link in a doc.
2024-03-18 07:04:02 +00:00
Peng Xiao
e896f19f1a fix(electron): disable mica for windows for now (#6165)
Upstream https://github.com/electron/electron/issues/41073
2024-03-18 06:52:40 +00:00
Peng Xiao
386bd033af fix(electron): add dedicated api for opening external links in the default browser (#6166) 2024-03-18 06:41:48 +00:00
Fangdun Tsai
8301d82548 fix(core): sync list titles in sidebar (#6157) 2024-03-18 14:39:28 +08:00
LongYinan
0b0b3e0ae9 build(deps): bump follow-redirects from 1.15.5 to 1.15.6 (#6164)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.5 to 1.15.6.
<details>
<summary>Commits</summary>
<ul>
<li><a href="35a517c586"><code>35a517c</code></a> Release version 1.15.6 of the npm package.</li>
<li><a href="c4f847f851"><code>c4f847f</code></a> Drop Proxy-Authorization across hosts.</li>
<li><a href="8526b4a1b2"><code>8526b4a</code></a> Use GitHub for disclosure.</li>
<li>See full diff in <a href="https://github.com/follow-redirects/follow-redirects/compare/v1.15.5...v1.15.6">compare view</a></li>
</ul>
</details>
<br />

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=follow-redirects&package-manager=npm_and_yarn&previous-version=1.15.5&new-version=1.15.6)](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-03-18 03:53:11 +00:00
liuyi
268ca03f62 fix(server): ensure selfhost admin created after all data migrated (#6163)
fix #6154 

cp to canary
2024-03-18 11:43:12 +08:00
LongYinan
58c81dd1ac chore: bump up get-stream version to v9 (#6139)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

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

---

### Release Notes

<details>
<summary>sindresorhus/get-stream (get-stream)</summary>

### [`v9.0.0`](https://togithub.com/sindresorhus/get-stream/releases/tag/v9.0.0)

[Compare Source](https://togithub.com/sindresorhus/get-stream/compare/v8.0.1...v9.0.0)

##### Breaking

-   Require Node.js 18 ([#&#8203;111](https://togithub.com/sindresorhus/get-stream/issues/111))  [`7ccab70`](https://togithub.com/sindresorhus/get-stream/commit/7ccab70)

##### Improvements

-   Fix browser support ([#&#8203;122](https://togithub.com/sindresorhus/get-stream/issues/122))  [`4d233d3`](https://togithub.com/sindresorhus/get-stream/commit/4d233d3)
-   Allow multiple readers at once ([#&#8203;121](https://togithub.com/sindresorhus/get-stream/issues/121))  [`a51d085`](https://togithub.com/sindresorhus/get-stream/commit/a51d085)

</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:eyJjcmVhdGVkSW5WZXIiOiIzNy4yNDUuMCIsInVwZGF0ZWRJblZlciI6IjM3LjI0NS4wIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-03-18 03:08:03 +00:00
liuyi
e94be8968b fix(server): hotfix (#6161) 2024-03-18 10:41:11 +08:00
Fangdun Tsai
636fa503b8 feat: support esc shortcut on input-edit (#6143) 2024-03-16 13:13:37 +00:00
Fangdun Tsai
eec24db1a1 fix: use the esc shortcut to exit in create collection dialog (#6138) 2024-03-16 19:18:06 +08:00
LongYinan
7ed86a66e8 build(core): add source-map-loader for blocksuite codes (#6137) 2024-03-15 09:37:48 +00:00
LongYinan
bc465f9704 Revert "fix(server): wrap read-modify-write apis with distributed lock (#5979)"
This reverts commit 34f892b05b.
2024-03-15 16:55:48 +08:00
Chen
a24320da68 feat: bump blocksuite (#6123) 2024-03-15 16:54:24 +08:00
EYHN
78f7e3a45e fix(core): catch page load error (#6134) 2024-03-15 08:44:36 +00:00
DarkSky
34f892b05b fix(server): wrap read-modify-write apis with distributed lock (#5979) 2024-03-15 13:31:11 +08:00
Chen
fd4e4123f5 fix: note added with template should be edgeless only (#6122) 2024-03-15 12:51:03 +08:00
EYHN
9ba47f43bb fix(core): fix active view undefined (#6131)
close https://github.com/toeverything/AFFiNE/issues/6127, #6132
2024-03-15 03:45:41 +00:00
EYHN
12c04a8575 feat(core): allow switch workspace in loading fallback (#6129) 2024-03-15 03:45:32 +00:00
EYHN
96c67afc11 fix(core): catch auth error (#6128) 2024-03-15 03:45:23 +00:00
Cats Juice
ee75b468e6 fix(core): shared page's present button not working (#6117) 2024-03-15 03:32:37 +00:00
Peng Xiao
e35f6dca0e fix: update docs (#6094) 2024-03-15 02:33:55 +00:00
LongYinan
5a0e207a8c ci: fix canary backend auto release job (#6121) 2024-03-15 02:21:57 +00:00
liuyi
533c181640 feat(server): cleanup gateway code (#6118) 2024-03-15 02:00:40 +00:00
liuyi
79ffca314d feat(server): allow prefetch doc stats before sync (#6115) 2024-03-14 17:34:33 +00:00
Cats Juice
7fdb1f2d97 feat(core): adjust split view ui (#6076) 2024-03-14 06:41:29 +00:00
EYHN
b9fc848824 fix(infra): avoid data loss (#6111) 2024-03-14 06:27:49 +00:00
Peng Xiao
d2bad68b74 feat(core): move blocksuite flags to experimental features (#6113) 2024-03-14 05:24:47 +00:00
Peng Xiao
dd9a253772 feat(core): add split view to experimental features settings (#6093) 2024-03-14 05:13:04 +00:00
Lye Hongtao
05583dbe98 feat: bump blocksuite (#6112) 2024-03-14 13:05:52 +08:00
liuyi
c5d8c6cc8c fix(server): blob api should be public (#6109) 2024-03-14 02:36:13 +00:00
regischen
c1f5e848b4 feat: blocksuite bump (#6104) 2024-03-13 23:50:10 +08:00
Peng Xiao
e116a82c24 fix(component): an input display issue in confirm dialog (#6102)
fix the following issue of `input`

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/8a439fd1-3f96-46c3-9e8d-0449779b5c36.png)
2024-03-13 11:40:34 +00:00
DarkSky
282b788258 fix: allow empty mailer password (#6066)
fix #6046
smtp relay may allow empty password, although this is usually not safe
2024-03-13 10:07:18 +00:00
liuyi
f2ec81b2d0 feat(server): user connected accounts migration (#6103) 2024-03-13 09:28:52 +00:00
regischen
fddbb426a6 feat: bump blocksuite (#6078) 2024-03-13 17:04:21 +08:00
liuyi
573528be41 fix(server): user can not signup through oauth if ever invited (#6101) 2024-03-13 07:50:11 +00:00
EYHN
fd9084ea6a feat(infra): computed livedata (#6091) 2024-03-13 06:26:05 +00:00
EYHN
cacc2d311e feat(infra): livedata flatten (#6083) 2024-03-13 06:06:18 +00:00
Peng Xiao
e8f83a237d fix(core): vitest on windows (#6100) 2024-03-13 04:32:39 +00:00
LongYinan
9e1adfed81 chore: bump up all non-major dependencies (#6069)
[![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/test-runner](https://togithub.com/storybookjs/test-runner) | [`^0.16.0` -> `^0.17.0`](https://renovatebot.com/diffs/npm/@storybook%2ftest-runner/0.16.0/0.17.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@storybook%2ftest-runner/0.17.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@storybook%2ftest-runner/0.17.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@storybook%2ftest-runner/0.16.0/0.17.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@storybook%2ftest-runner/0.16.0/0.17.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [next-themes](https://togithub.com/pacocoursey/next-themes) | [`^0.2.1` -> `^0.3.0`](https://renovatebot.com/diffs/npm/next-themes/0.2.1/0.3.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/next-themes/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/next-themes/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/next-themes/0.2.1/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/next-themes/0.2.1/0.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>storybookjs/test-runner (@&#8203;storybook/test-runner)</summary>

### [`v0.17.0`](https://togithub.com/storybookjs/test-runner/releases/tag/v0.17.0)

[Compare Source](https://togithub.com/storybookjs/test-runner/compare/v0.16.0...v0.17.0)

##### 🚀 Enhancement

-   Release 0.17.0 [#&#8203;438](https://togithub.com/storybookjs/test-runner/pull/438) ([@&#8203;JReinhold](https://togithub.com/JReinhold) [@&#8203;shilman](https://togithub.com/shilman) [@&#8203;valentinpalkovic](https://togithub.com/valentinpalkovic) [@&#8203;yannbf](https://togithub.com/yannbf) [@&#8203;ndelangen](https://togithub.com/ndelangen))
-   Support Storybook 8 [#&#8203;429](https://togithub.com/storybookjs/test-runner/pull/429) ([@&#8203;yannbf](https://togithub.com/yannbf))
-   Support unhandled rendering errors [#&#8203;421](https://togithub.com/storybookjs/test-runner/pull/421) ([@&#8203;yannbf](https://togithub.com/yannbf))

##### 🐛 Bug Fix

-   Prebundle dependencies [#&#8203;431](https://togithub.com/storybookjs/test-runner/pull/431) ([@&#8203;yannbf](https://togithub.com/yannbf))
-   Update internal example to Storybook 8 [#&#8203;430](https://togithub.com/storybookjs/test-runner/pull/430) ([@&#8203;yannbf](https://togithub.com/yannbf))
-   swap storybook/jest to storybook/test [#&#8203;427](https://togithub.com/storybookjs/test-runner/pull/427) ([@&#8203;ndelangen](https://togithub.com/ndelangen))
-   Add PR template [#&#8203;428](https://togithub.com/storybookjs/test-runner/pull/428) ([@&#8203;yannbf](https://togithub.com/yannbf))
-   Fix build step [#&#8203;425](https://togithub.com/storybookjs/test-runner/pull/425) ([@&#8203;valentinpalkovic](https://togithub.com/valentinpalkovic))
-   Remove --prerelease from sb upgrade CI [#&#8203;423](https://togithub.com/storybookjs/test-runner/pull/423) ([@&#8203;JReinhold](https://togithub.com/JReinhold))
-   Support stories with meta id for permalinking [#&#8203;419](https://togithub.com/storybookjs/test-runner/pull/419) ([@&#8203;yannbf](https://togithub.com/yannbf))

##### 📝 Documentation

-   Docs: Add remark regarding pnp support [#&#8203;432](https://togithub.com/storybookjs/test-runner/pull/432) ([@&#8203;yannbf](https://togithub.com/yannbf))
-   docs: replace postRender(deprecated) with postVisit [#&#8203;418](https://togithub.com/storybookjs/test-runner/pull/418) ([@&#8203;junkisai](https://togithub.com/junkisai))

##### Authors: 6

-   Jeppe Reinhold ([@&#8203;JReinhold](https://togithub.com/JReinhold))
-   Junki Saito ([@&#8203;junkisai](https://togithub.com/junkisai))
-   Michael Shilman ([@&#8203;shilman](https://togithub.com/shilman))
-   Norbert de Langen ([@&#8203;ndelangen](https://togithub.com/ndelangen))
-   Valentin Palkovic ([@&#8203;valentinpalkovic](https://togithub.com/valentinpalkovic))
-   Yann Braga ([@&#8203;yannbf](https://togithub.com/yannbf))

</details>

<details>
<summary>pacocoursey/next-themes (next-themes)</summary>

### [`v0.3.0`](https://togithub.com/pacocoursey/next-themes/releases/tag/v0.3.0)

#### What's

-   `"use client"` was added to the library output so that you can use it as a React Client component without creating a wrapper.

#### New Contributors

-   [@&#8203;MaxLeiter](https://togithub.com/MaxLeiter) made their first contribution in [https://github.com/pacocoursey/next-themes/pull/120](https://togithub.com/pacocoursey/next-themes/pull/120)
-   [@&#8203;gnoff](https://togithub.com/gnoff) made their first contribution in [https://github.com/pacocoursey/next-themes/pull/135](https://togithub.com/pacocoursey/next-themes/pull/135)
-   [@&#8203;WITS](https://togithub.com/WITS) made their first contribution in [https://github.com/pacocoursey/next-themes/pull/168](https://togithub.com/pacocoursey/next-themes/pull/168)
-   [@&#8203;dimaMachina](https://togithub.com/dimaMachina) made their first contribution in [https://github.com/pacocoursey/next-themes/pull/186](https://togithub.com/pacocoursey/next-themes/pull/186)
-   [@&#8203;amrhassab](https://togithub.com/amrhassab) made their first contribution in [https://github.com/pacocoursey/next-themes/pull/192](https://togithub.com/pacocoursey/next-themes/pull/192)
-   [@&#8203;BekzodIsakov](https://togithub.com/BekzodIsakov) made their first contribution in [https://github.com/pacocoursey/next-themes/pull/241](https://togithub.com/pacocoursey/next-themes/pull/241)
-   [@&#8203;BlankParticle](https://togithub.com/BlankParticle) made their first contribution in [https://github.com/pacocoursey/next-themes/pull/253](https://togithub.com/pacocoursey/next-themes/pull/253)

**Full Changelog**: https://github.com/pacocoursey/next-themes/compare/v0.2.0...v0.3.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.

👻 **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:eyJjcmVhdGVkSW5WZXIiOiIzNy4yMzAuMCIsInVwZGF0ZWRJblZlciI6IjM3LjIzOC4xIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-03-13 04:22:43 +00:00
liuyi
73801ce864 fix(server): gql schema is outdated (#6097) 2024-03-13 04:11:44 +00:00
liuyi
495855cc07 fix(server): server info api should be public (#6098) 2024-03-13 03:59:26 +00:00
EYHN
05b79aae89 fix(core): fix tags includes missing error (#6096)
fix https://toeverything.sentry.io/issues/5059796018/events/0899e77a3f6842088568b4cc42b814d7/
2024-03-13 02:43:28 +00:00
liuyi
d8f9e357d0 ci: proxy /oauth to server (#6095) 2024-03-13 02:33:58 +00:00
Peng Xiao
20bce48132 fix: experimental settings sometimes not show (#6090) 2024-03-13 02:07:51 +00:00
Peng Xiao
3f27b7e5f7 fix(core): adjust suspense loading for some components (#6088)
Adjust setting & user info popover suspense to make them a little bit more responsive
2024-03-12 15:18:43 +00:00
Peng Xiao
9f74cb32ea fix(component): input focus implementation (#6086) 2024-03-12 15:07:48 +00:00
Peng Xiao
e333b4d348 fix(core): make sidebar switch transition smooth (#6085) 2024-03-12 14:56:45 +00:00
liuyi
fb3a0e7b8f refactor(server): auth (#5895)
Remove `next-auth` and implement our own Authorization/Authentication system from scratch.

## Server

- [x] tokens
  - [x] function
  - [x] encryption

- [x] AuthController
  - [x] /api/auth/sign-in
  - [x] /api/auth/sign-out
  - [x] /api/auth/session
  - [x] /api/auth/session (WE SUPPORT MULTI-ACCOUNT!)

- [x] OAuthPlugin
  - [x] OAuthController
  - [x] /oauth/login
  - [x] /oauth/callback
  - [x] Providers
    - [x] Google
    - [x] GitHub

## Client

- [x] useSession
- [x] cloudSignIn
- [x] cloudSignOut

## NOTE:

Tests will be adding in the future
2024-03-12 10:00:09 +00:00
liuyi
af49e8cc41 ci: only enable jwst codec in canary (#6081) 2024-03-12 09:44:39 +00:00
Peng Xiao
4e79d720df docs(server): update docs for running server locally (#6080) 2024-03-12 09:33:52 +00:00
Peng Xiao
1c5279747b fix(server): migration script running on windows (#6077) 2024-03-12 16:19:48 +08:00
regischen
5c1e904335 fix: linked doc jump (#6074) 2024-03-12 16:02:56 +08:00
Lye Hongtao
d5386bee41 fix: std might not be ready when onLoad invoked (#6075) 2024-03-12 16:02:42 +08:00
Peng Xiao
83e1217f61 fix(server): server startup on windows (#6071) 2024-03-12 06:41:42 +00:00
JimmFly
f832ada98c fix(core): cannot scroll when dragging (#6070)
close TOV-671
2024-03-12 05:43:47 +00:00
EYHN
eca35ded2f fix(core): fix page priority load (#6072)
When refactoring, I planned to put the logic of loading current pages priority in `PageManager.open`, but I forgot, so the current page will not be loaded priority.

But currently `PageManager.open` has been used in many places, but only one priority page can be set, so the logic of setting the priority page is still placed in the detail page.
2024-03-12 05:09:14 +00:00
EYHN
3672366fea fix(core): fix missing windows controls (#6067)
fix #6064
2024-03-12 02:43:45 +00:00
JimmFly
33501d5fdc fix(electron): invalid isLinux function (#6065) 2024-03-12 10:17:53 +08:00
Peng Xiao
716f679914 feat(electron): mica styles for windows (#5802)
![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/1b59f076-fc43-42a0-899d-007aeb16a4d6.png)
2024-03-11 09:41:09 +00:00
JimmFly
b26efa4940 feat(core): add tag operation to tag list (#5998)
https://github.com/toeverything/AFFiNE/assets/102217452/11745733-0d7b-494b-97fd-33e40a240a02
2024-03-11 08:36:07 +00:00
LongYinan
cb96d7de43 chore: bump up @blocksuite/icons version to v2.1.45 (#6061)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [@blocksuite/icons](https://togithub.com/toeverything/icons) | [`2.1.44` -> `2.1.45`](https://renovatebot.com/diffs/npm/@blocksuite%2ficons/2.1.44/2.1.45) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@blocksuite%2ficons/2.1.45?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@blocksuite%2ficons/2.1.45?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@blocksuite%2ficons/2.1.44/2.1.45?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@blocksuite%2ficons/2.1.44/2.1.45?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>toeverything/icons (@&#8203;blocksuite/icons)</summary>

### [`v2.1.45`](bc31d70961...cc4113532a)

[Compare Source](bc31d70961...cc4113532a)

</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 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:eyJjcmVhdGVkSW5WZXIiOiIzNy4yMzAuMCIsInVwZGF0ZWRJblZlciI6IjM3LjIzMC4wIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-03-11 07:35:56 +00:00
EYHN
ba4637a6bd fix(core): add view error boundary (#6036) 2024-03-11 07:09:47 +00:00
Peng Xiao
c93077f643 fix: divider styles issue (#6058)
Using height: 1 as divider may have some display issues on Chrome. No idea why yet.
![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/42b342fb-d109-4bf7-b458-e870099710bc.png)
2024-03-11 06:04:55 +00:00
Jason
fc9bf9cd8a chore(component): simplify code (#6056) 2024-03-11 14:04:16 +08:00
LongYinan
d81c017224 chore: bump up all non-major dependencies (#6037)
[![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.525.0` -> `3.529.1`](https://renovatebot.com/diffs/npm/@aws-sdk%2fclient-s3/3.525.0/3.529.1) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@aws-sdk%2fclient-s3/3.529.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@aws-sdk%2fclient-s3/3.529.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@aws-sdk%2fclient-s3/3.525.0/3.529.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@aws-sdk%2fclient-s3/3.525.0/3.529.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@nx/vite](https://nx.dev) ([source](https://togithub.com/nrwl/nx/tree/HEAD/packages/vite)) | [`18.0.7` -> `18.0.8`](https://renovatebot.com/diffs/npm/@nx%2fvite/18.0.7/18.0.8) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@nx%2fvite/18.0.8?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@nx%2fvite/18.0.8?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@nx%2fvite/18.0.7/18.0.8?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@nx%2fvite/18.0.7/18.0.8?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.37.0` -> `^0.38.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-graphql/0.37.0/0.38.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-graphql/0.38.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@opentelemetry%2finstrumentation-graphql/0.38.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@opentelemetry%2finstrumentation-graphql/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-graphql/0.37.0/0.38.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.37.0` -> `^0.38.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-ioredis/0.37.0/0.38.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-ioredis/0.38.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@opentelemetry%2finstrumentation-ioredis/0.38.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@opentelemetry%2finstrumentation-ioredis/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-ioredis/0.37.0/0.38.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.34.0` -> `^0.35.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-nestjs-core/0.34.0/0.35.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-nestjs-core/0.35.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@opentelemetry%2finstrumentation-nestjs-core/0.35.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@opentelemetry%2finstrumentation-nestjs-core/0.34.0/0.35.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation-nestjs-core/0.34.0/0.35.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.36.0` -> `^0.37.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-socket.io/0.36.0/0.37.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-socket.io/0.37.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@opentelemetry%2finstrumentation-socket.io/0.37.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@opentelemetry%2finstrumentation-socket.io/0.36.0/0.37.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation-socket.io/0.36.0/0.37.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

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

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

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

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

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

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

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

</details>

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

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

[Compare Source](https://togithub.com/nrwl/nx/compare/18.0.7...18.0.8)

##### 18.0.8 (2024-03-08)

##### 🩹 Fixes

-   **angular:** log message about unsupported ng cache command ([#&#8203;22154](https://togithub.com/nrwl/nx/pull/22154))
-   **angular:** fix message logged for unsupported ng cache ([#&#8203;22211](https://togithub.com/nrwl/nx/pull/22211))
-   **angular:** Module federation with Crystal enabled. ([#&#8203;22224](https://togithub.com/nrwl/nx/pull/22224))
-   **angular:** install jsonc-eslint-parser only when [@&#8203;nx/dependency-checks](https://togithub.com/nx/dependency-checks) is used ([#&#8203;22231](https://togithub.com/nrwl/nx/pull/22231))
-   **core:** setting up .nx inside gradle shouldn't throw ([#&#8203;21957](https://togithub.com/nrwl/nx/pull/21957))
-   **core:** add outputs to nx.json for nx init in monorepo ([#&#8203;22061](https://togithub.com/nrwl/nx/pull/22061))
-   **core:** fix no such file or directory, open 'package-lock.json' ([#&#8203;21835](https://togithub.com/nrwl/nx/pull/21835))
-   **core:** fix terminal message alignment on errors ([#&#8203;22189](https://togithub.com/nrwl/nx/pull/22189))
-   **graph:** show command property as monospace ([#&#8203;21997](https://togithub.com/nrwl/nx/pull/21997))
-   **linter:** add v7 of typescript-eslint to peerDeps ([#&#8203;21853](https://togithub.com/nrwl/nx/pull/21853))
-   **misc:** improve package.json scripts handling when running "nx init" and "nx add" ([#&#8203;22168](https://togithub.com/nrwl/nx/pull/22168))
-   **misc:** do not add includedScripts unless really needed when running nx add ([#&#8203;22180](https://togithub.com/nrwl/nx/pull/22180))
-   **nextjs:** avoid path error on dev  server creation ([#&#8203;21998](https://togithub.com/nrwl/nx/pull/21998))
-   **nextjs:** Surface error codes when build is interrupted by signals SIGINT, SIGTERM etc... ([#&#8203;22190](https://togithub.com/nrwl/nx/pull/22190))
-   **nx-plugin:** do not print duplicated warning about derived format when generating plugin ([#&#8203;22230](https://togithub.com/nrwl/nx/pull/22230))
-   **react:** pass correct argument to rspack configuration generator ([#&#8203;22241](https://togithub.com/nrwl/nx/pull/22241))
-   **release:** store rawVersionSpec on versionData ([#&#8203;22071](https://togithub.com/nrwl/nx/pull/22071))
-   **testing:** close cypress web server correctly on windows ([#&#8203;22125](https://togithub.com/nrwl/nx/pull/22125))
-   **testing:** resolve cypress config glob pattern correctly to handle root projects ([#&#8203;22165](https://togithub.com/nrwl/nx/pull/22165))
-   **testing:** fix project config might not be defined ([#&#8203;22174](https://togithub.com/nrwl/nx/pull/22174))
-   **vue:** small typo in CNW description ([#&#8203;21888](https://togithub.com/nrwl/nx/pull/21888))

##### ❤️  Thank You

-   Alon Valadji [@&#8203;alonronin](https://togithub.com/alonronin)
-   Colum Ferry [@&#8203;Coly010](https://togithub.com/Coly010)
-   Craigory Coppola [@&#8203;AgentEnder](https://togithub.com/AgentEnder)
-   Emily Xiong [@&#8203;xiongemi](https://togithub.com/xiongemi)
-   James Henry [@&#8203;JamesHenry](https://togithub.com/JamesHenry)
-   Javier Abia [@&#8203;weberjavi](https://togithub.com/weberjavi)
-   Juri Strumpflohner [@&#8203;juristr](https://togithub.com/juristr)
-   Leosvel Pérez Espinosa [@&#8203;leosvelperez](https://togithub.com/leosvelperez)
-   MaxKless [@&#8203;MaxKless](https://togithub.com/MaxKless)
-   Miroslav Jonaš [@&#8203;meeroslav](https://togithub.com/meeroslav)
-   Nicholas Cunningham [@&#8203;ndcunningham](https://togithub.com/ndcunningham)
-   Victor Login [@&#8203;batazor](https://togithub.com/batazor)

</details>

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

### [`v0.38.0`](efdfc727a4...f81f8a76a8)

[Compare Source](32204a362d...fcea8ca0c8)

</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:eyJjcmVhdGVkSW5WZXIiOiIzNy4yMjcuMiIsInVwZGF0ZWRJblZlciI6IjM3LjIzMC4wIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-03-11 03:53:00 +00:00
LongYinan
c37f15cf97 chore: bump up softprops/action-gh-release action to v2 (#6051)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [softprops/action-gh-release](https://togithub.com/softprops/action-gh-release) | action | major | `v1` -> `v2` |

---

### Release Notes

<details>
<summary>softprops/action-gh-release (softprops/action-gh-release)</summary>

### [`v2`](https://togithub.com/softprops/action-gh-release/compare/v1...v2)

[Compare Source](https://togithub.com/softprops/action-gh-release/compare/v1...v2)

</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:eyJjcmVhdGVkSW5WZXIiOiIzNy4yMzAuMCIsInVwZGF0ZWRJblZlciI6IjM3LjIzMC4wIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-03-11 02:25:33 +00:00
LongYinan
08fa157ce4 chore: bump up vite-plugin-istanbul version to v6 (#6054)
[![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-plugin-istanbul](https://togithub.com/ifaxity/vite-plugin-istanbul) | [`^5.0.0` -> `^6.0.0`](https://renovatebot.com/diffs/npm/vite-plugin-istanbul/5.0.0/6.0.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/vite-plugin-istanbul/6.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/vite-plugin-istanbul/6.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/vite-plugin-istanbul/5.0.0/6.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/vite-plugin-istanbul/5.0.0/6.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>ifaxity/vite-plugin-istanbul (vite-plugin-istanbul)</summary>

### [`v6.0.0`](https://togithub.com/iFaxity/vite-plugin-istanbul/releases/tag/v6.0.0)

[Compare Source](https://togithub.com/ifaxity/vite-plugin-istanbul/compare/v5.0.0...v6.0.0)

##### Bug Fixes

-   add regex for vue SFC to prevent instrumenting style and templates ([#&#8203;180](https://togithub.com/ifaxity/vite-plugin-istanbul/issues/180)) ([c8a7ca7](c8a7ca771e)), closes [#&#8203;96](https://togithub.com/ifaxity/vite-plugin-istanbul/issues/96) [#&#8203;178](https://togithub.com/ifaxity/vite-plugin-istanbul/issues/178) [#&#8203;89](https://togithub.com/ifaxity/vite-plugin-istanbul/issues/89)

-   feat!: update to support vite v5  ([#&#8203;183](https://togithub.com/ifaxity/vite-plugin-istanbul/issues/183)) ([614f8bd](614f8bd365)), closes [#&#8203;183](https://togithub.com/ifaxity/vite-plugin-istanbul/issues/183)

##### BREAKING CHANGES

-   updating from espree v9 to v10 deprecates older node versions

-   feat!: update to support vite v5

-   least supported version changed from v2.9.1 to v4

</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:eyJjcmVhdGVkSW5WZXIiOiIzNy4yMzAuMCIsInVwZGF0ZWRJblZlciI6IjM3LjIzMC4wIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-03-11 02:15:20 +00:00
EYHN
5091174da2 fix(core): remove unexpected loading (#6035) 2024-03-09 15:01:18 +00:00
EYHN
ae22d6469d fix(core): fix unit test (#6040) 2024-03-09 15:01:16 +00:00
Fangdun Tsai
c99f0249d9 fix(native): needless borrows for generic args (#6039) 2024-03-06 14:52:16 +00:00
LongYinan
3aba2d220b fix: backlinks jump (#6034)
Fix [AFF-656](https://linear.app/affine-design/issue/AFF-656/back-links-看起来都坏了)
2024-03-06 11:06:02 +00:00
LongYinan
e1ec90dcc5 ci: add write packages permission to release workflow 2024-03-06 18:25:37 +08:00
LongYinan
665fc69f67 ci: add write permission to release workflow 2024-03-06 18:25:26 +08:00
JimmFly
c7dd6778ac fix(core): disable doc info and backlinks in readonly mode (#6033)
close AFF-671

<img width="1422" alt="image" src="https://github.com/toeverything/AFFiNE/assets/102217452/ef80b195-3d67-4751-82d9-12973f331798">
2024-03-06 10:07:20 +00:00
LongYinan
e96b86ec71 ci: add write permission to release workflow 2024-03-06 15:01:21 +08:00
EYHN
633a5bab53 fix(core): fix cmdk not show current page (#6031) 2024-03-06 06:31:38 +00:00
Fangdun Tsai
5d63ca0bab fix(core): canvas font loading in desktop (#6025) 2024-03-06 13:42:56 +08:00
fourdim
0ff1e8d2f7 fix: breaking function signature on showImportModal onSuceessHandler (#6011)
This will fix the breaking change introduced in
da781df473
This should be merged after the next BlockSuite bump.
2024-03-05 18:51:39 +00:00
EYHN
dce3b59c52 fix(core): fix falsy e2e (#6027) 2024-03-05 15:25:19 +00:00
EYHN
5d8dea084c fix(core): fix journal missing after refresh (#6022) 2024-03-05 11:42:09 +00:00
JimmFly
c1afdb9bd6 fix(core): unexpected line breaks (#6019) 2024-03-05 10:02:22 +00:00
EYHN
ceec54b423 fix(docs): fix bug-report template (#6020) 2024-03-05 09:52:57 +00:00
EYHN
e9d51f6c96 fix(component): journal header button no responce (#6021) 2024-03-05 09:40:19 +00:00
Peng Xiao
65b32156a8 build: do not fail build for a non-fatal error (#6017) 2024-03-05 08:27:47 +00:00
EYHN
7c55472b78 docs: update bug-report.yml (#6018) 2024-03-05 16:27:14 +08:00
Peng Xiao
55ecd92722 fix(electron): windows distribution name (#6014) 2024-03-05 07:53:30 +00:00
Peng Xiao
63e82cca78 build: remove incorrect project dep (#6016) 2024-03-05 07:44:13 +00:00
Lye Hongtao
5bebcec2eb feat: bump blocksuite (#6015)
## Features
- https://github.com/toeverything/BlockSuite/pull/6379 @doodlewind
- https://github.com/toeverything/BlockSuite/pull/6375 @doodlewind
- https://github.com/toeverything/BlockSuite/pull/6370 @donteatfriedrice

## Bugfix
- https://github.com/toeverything/BlockSuite/pull/6380 @donteatfriedrice
- https://github.com/toeverything/BlockSuite/pull/6377 @doouding
- https://github.com/toeverything/BlockSuite/pull/6376 @fourdim
- https://github.com/toeverything/BlockSuite/pull/6374 @regischen

## Misc
- docs: update examples desc
- ci: start publishing 0.13 canary
2024-03-05 07:35:05 +00:00
EYHN
7c76c25a9c refactor(core): new back&forward button base on workbench (#6012)
# feature:

## In Browser:
- hidden back&forward button in sidebar.
- back and forward is equal with `window.history.back()` `window.history.forward()`

## In Desktop:
- Back and forward can be controlled through the sidebar, cmdk, and shortcut keys.
- back and forward act on the currently **active** view.
- buttons change disable&enable style based on current active view history

# Refactor:

Move app-sidebar and app-container from @affine/component to @affine/core
2024-03-05 07:01:24 +00:00
Lye Hongtao
b06aeb22dd fix: view in edgeless button of surface block (#6013)
Co-authored-by: donteatfriedrice <huisheng.chen7788@outlook.com>
Co-authored-by: Chen <99816898+donteatfriedrice@users.noreply.github.com>
2024-03-05 14:35:53 +08:00
Chen
ebf7a74387 feat: bump blocksuite (#6010) 2024-03-05 14:19:11 +08:00
LongYinan
43813d7f86 build(deps): bump mio from 0.8.9 to 0.8.11 (#6009)
Bumps [mio](https://github.com/tokio-rs/mio) from 0.8.9 to 0.8.11.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/tokio-rs/mio/blob/master/CHANGELOG.md">mio's changelog</a>.</em></p>
<blockquote>
<h1>0.8.11</h1>
<ul>
<li>Fix receiving IOCP events after deregistering a Windows named pipe
(<a href="https://redirect.github.com/tokio-rs/mio/pull/1760">tokio-rs/mio#1760</a>, backport pr:
<a href="https://redirect.github.com/tokio-rs/mio/pull/1761">tokio-rs/mio#1761</a>).</li>
</ul>
<h1>0.8.10</h1>
<h2>Added</h2>
<ul>
<li>Solaris support
(<a href="https://redirect.github.com/tokio-rs/mio/pull/1724">tokio-rs/mio#1724</a>).</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="0328bdef90"><code>0328bde</code></a> Release v0.8.11</li>
<li><a href="7084498512"><code>7084498</code></a> Fix warnings</li>
<li><a href="90d4fe00df"><code>90d4fe0</code></a> named-pipes: fix receiving IOCP events after deregister</li>
<li><a href="c710a307f8"><code>c710a30</code></a> Add v0.8.x to the CI</li>
<li><a href="c29e21c244"><code>c29e21c</code></a> Release v0.8.10</li>
<li><a href="f6a20da1c8"><code>f6a20da</code></a> Add Solaris operating system support (<a href="https://redirect.github.com/tokio-rs/mio/issues/1724">#1724</a>)</li>
<li>See full diff in <a href="https://github.com/tokio-rs/mio/compare/v0.8.9...v0.8.11">compare view</a></li>
</ul>
</details>
<br />

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=mio&package-manager=cargo&previous-version=0.8.9&new-version=0.8.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-03-05 04:02:36 +00:00
LongYinan
9a3ae9302e chore: bump up all non-major dependencies (#5964)
[![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.523.0` -> `3.525.0`](https://renovatebot.com/diffs/npm/@aws-sdk%2fclient-s3/3.523.0/3.525.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@aws-sdk%2fclient-s3/3.525.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@aws-sdk%2fclient-s3/3.525.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@aws-sdk%2fclient-s3/3.523.0/3.525.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@aws-sdk%2fclient-s3/3.523.0/3.525.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@node-rs/jsonwebtoken](https://togithub.com/napi-rs/node-rs) | [`^0.4.0` -> `^0.5.0`](https://renovatebot.com/diffs/npm/@node-rs%2fjsonwebtoken/0.4.0/0.5.1) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@node-rs%2fjsonwebtoken/0.5.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@node-rs%2fjsonwebtoken/0.5.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@node-rs%2fjsonwebtoken/0.4.0/0.5.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@node-rs%2fjsonwebtoken/0.4.0/0.5.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@nx/vite](https://nx.dev) ([source](https://togithub.com/nrwl/nx/tree/HEAD/packages/vite)) | [`18.0.5` -> `18.0.7`](https://renovatebot.com/diffs/npm/@nx%2fvite/18.0.5/18.0.7) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@nx%2fvite/18.0.7?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@nx%2fvite/18.0.7?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@nx%2fvite/18.0.5/18.0.7?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@nx%2fvite/18.0.5/18.0.7?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.48.0` -> `^0.49.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2fexporter-prometheus/0.48.0/0.49.1) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2fexporter-prometheus/0.49.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@opentelemetry%2fexporter-prometheus/0.49.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@opentelemetry%2fexporter-prometheus/0.48.0/0.49.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2fexporter-prometheus/0.48.0/0.49.1?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.48.0` -> `^0.49.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation/0.48.0/0.49.1) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation/0.49.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@opentelemetry%2finstrumentation/0.49.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@opentelemetry%2finstrumentation/0.48.0/0.49.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation/0.48.0/0.49.1?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.48.0` -> `^0.49.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2finstrumentation-http/0.48.0/0.49.1) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2finstrumentation-http/0.49.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@opentelemetry%2finstrumentation-http/0.49.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@opentelemetry%2finstrumentation-http/0.48.0/0.49.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2finstrumentation-http/0.48.0/0.49.1?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.48.0` -> `^0.49.0`](https://renovatebot.com/diffs/npm/@opentelemetry%2fsdk-node/0.48.0/0.49.1) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@opentelemetry%2fsdk-node/0.49.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@opentelemetry%2fsdk-node/0.49.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@opentelemetry%2fsdk-node/0.48.0/0.49.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@opentelemetry%2fsdk-node/0.48.0/0.49.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [jotai-effect](https://togithub.com/jotaijs/jotai-effect) | [`^0.5.0` -> `^0.6.0`](https://renovatebot.com/diffs/npm/jotai-effect/0.5.0/0.6.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/jotai-effect/0.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/jotai-effect/0.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/jotai-effect/0.5.0/0.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/jotai-effect/0.5.0/0.6.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [yarn](https://togithub.com/yarnpkg/berry) ([source](https://togithub.com/yarnpkg/berry/tree/HEAD/packages/yarnpkg-cli)) | [`4.1.0` -> `4.1.1`](https://renovatebot.com/diffs/npm/yarn/4.1.0/4.1.1) | [![age](https://developer.mend.io/api/mc/badges/age/npm/yarn/4.1.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/yarn/4.1.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/yarn/4.1.0/4.1.1?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/yarn/4.1.0/4.1.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.525.0`](https://togithub.com/aws/aws-sdk-js-v3/blob/HEAD/clients/client-s3/CHANGELOG.md#35250-2024-02-29)

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

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

</details>

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

### [`v0.5.1`](https://togithub.com/napi-rs/node-rs/releases/tag/%40node-rs/jsonwebtoken%400.5.1)

[Compare Source](https://togithub.com/napi-rs/node-rs/compare/@node-rs/jsonwebtoken@0.5.0...@node-rs/jsonwebtoken@0.5.1)

#### What's Changed

-   chore(deps): lock file maintenance by [@&#8203;renovate](https://togithub.com/renovate) in [https://github.com/napi-rs/node-rs/pull/800](https://togithub.com/napi-rs/node-rs/pull/800)
-   fix(jsonwebtoken): handle error in decodeHeader by [@&#8203;Brooooooklyn](https://togithub.com/Brooooooklyn) in [https://github.com/napi-rs/node-rs/pull/801](https://togithub.com/napi-rs/node-rs/pull/801)

**Full Changelog**: https://github.com/napi-rs/node-rs/compare/[@&#8203;node-rs/crc32](https://togithub.com/node-rs/crc32)[@&#8203;1](https://togithub.com/1).10.0...[@&#8203;node-rs/jsonwebtoken](https://togithub.com/node-rs/jsonwebtoken)[@&#8203;0](https://togithub.com/0).5.1

### [`v0.5.0`](https://togithub.com/napi-rs/node-rs/releases/tag/%40node-rs/jsonwebtoken%400.5.0)

[Compare Source](https://togithub.com/napi-rs/node-rs/compare/@node-rs/jsonwebtoken@0.4.0...@node-rs/jsonwebtoken@0.5.0)

#### What's Changed

-   fix(jsonwebtoken): num typings by [@&#8203;tada5hi](https://togithub.com/tada5hi) in [https://github.com/napi-rs/node-rs/pull/777](https://togithub.com/napi-rs/node-rs/pull/777)
-   feat(jsonwebtoken): flatten claims to align with rfc by [@&#8203;tada5hi](https://togithub.com/tada5hi) in [https://github.com/napi-rs/node-rs/pull/780](https://togithub.com/napi-rs/node-rs/pull/780)
-   feat(jsonwebtoken): expose decode header utility by [@&#8203;tada5hi](https://togithub.com/tada5hi) in [https://github.com/napi-rs/node-rs/pull/795](https://togithub.com/napi-rs/node-rs/pull/795)
-   chore(\*): upgrade NAPI-RS cli and bindings by [@&#8203;Brooooooklyn](https://togithub.com/Brooooooklyn) in [https://github.com/napi-rs/node-rs/pull/797](https://togithub.com/napi-rs/node-rs/pull/797)
-   feat(jsonwebtoken): support wasm32-wasi target by [@&#8203;Brooooooklyn](https://togithub.com/Brooooooklyn) in [https://github.com/napi-rs/node-rs/pull/798](https://togithub.com/napi-rs/node-rs/pull/798)

#### New Contributors

-   [@&#8203;tada5hi](https://togithub.com/tada5hi) made their first contribution in [https://github.com/napi-rs/node-rs/pull/777](https://togithub.com/napi-rs/node-rs/pull/777)

**Full Changelog**: https://github.com/napi-rs/node-rs/compare/[@&#8203;node-rs/argon2](https://togithub.com/node-rs/argon2)[@&#8203;1](https://togithub.com/1).7.0...[@&#8203;node-rs/jsonwebtoken](https://togithub.com/node-rs/jsonwebtoken)[@&#8203;0](https://togithub.com/0).5.0

</details>

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

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

[Compare Source](https://togithub.com/nrwl/nx/compare/18.0.6...18.0.7)

#### 18.0.7 (2024-03-04)

##### 🩹 Fixes

-   **core:** target defaults should represent nx.json in source info ([#&#8203;22080](https://togithub.com/nrwl/nx/pull/22080))
-   **nextjs:** Add spec files when creating a next app ([#&#8203;22079](https://togithub.com/nrwl/nx/pull/22079))
-   **release:** skip lock file update if workspaces are not enabled ([#&#8203;22055](https://togithub.com/nrwl/nx/pull/22055))
-   **remix:** ensure component-testing is exported correctly [#&#8203;22091](https://togithub.com/nrwl/nx/issues/22091) ([#&#8203;22095](https://togithub.com/nrwl/nx/pull/22095), [#&#8203;22091](https://togithub.com/nrwl/nx/issues/22091))
-   **storybook:** handle main.js file correctly in storybook plugin ([#&#8203;22081](https://togithub.com/nrwl/nx/pull/22081))
-   **testing:** calculate correct support file path in cypress e2e preset ([#&#8203;22096](https://togithub.com/nrwl/nx/pull/22096))

##### ❤️  Thank You

-   Austin Fahsl [@&#8203;fahslaj](https://togithub.com/fahslaj)
-   Colum Ferry [@&#8203;Coly010](https://togithub.com/Coly010)
-   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.0.6`](https://togithub.com/nrwl/nx/releases/tag/18.0.6)

[Compare Source](https://togithub.com/nrwl/nx/compare/18.0.5...18.0.6)

##### 18.0.6 (2024-02-29)

##### 🩹 Fixes

-   **core:** nextjs-standalone generates package scripts consistent with create-next-app ([#&#8203;21996](https://togithub.com/nrwl/nx/pull/21996))
-   **misc:** make sure to add e2e crystal plugin ([#&#8203;22041](https://togithub.com/nrwl/nx/pull/22041))
-   **misc:** fix buildable libs utils calculating dependent projects from task graph ([#&#8203;22015](https://togithub.com/nrwl/nx/pull/22015))
-   **node:** Increase timeout for CI ([#&#8203;22003](https://togithub.com/nrwl/nx/pull/22003))
-   **nuxt:** fix storybook preview config path ([#&#8203;22020](https://togithub.com/nrwl/nx/pull/22020))
-   **nuxt:** Add e2e-ci and serve-static targets ([#&#8203;22056](https://togithub.com/nrwl/nx/pull/22056))
-   **release:** currentVersionResolver git-tag should prefer merged tags ([#&#8203;22082](https://togithub.com/nrwl/nx/pull/22082))

##### ❤️  Thank You

-   Austin Fahsl [@&#8203;fahslaj](https://togithub.com/fahslaj)
-   Jack Hsu [@&#8203;jaysoo](https://togithub.com/jaysoo)
-   Katerina Skroumpelou [@&#8203;mandarini](https://togithub.com/mandarini)
-   Leosvel Pérez Espinosa [@&#8203;leosvelperez](https://togithub.com/leosvelperez)
-   Nicholas Cunningham [@&#8203;ndcunningham](https://togithub.com/ndcunningham)

</details>

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

### [`v0.49.1`](7be35c7845...3920b158d0)

[Compare Source](7be35c7845...3920b158d0)

### [`v0.49.0`](828f2ed730...7be35c7845)

[Compare Source](828f2ed730...7be35c7845)

</details>

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

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

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

#### What's Changed

-   rethrow errors thrown during effectFn and cleanup by [@&#8203;dmaskasky](https://togithub.com/dmaskasky) in [https://github.com/jotaijs/jotai-effect/pull/33](https://togithub.com/jotaijs/jotai-effect/pull/33)

**Full Changelog**: https://github.com/jotaijs/jotai-effect/compare/v0.5.0...v0.6.0

</details>

<details>
<summary>yarnpkg/berry (yarn)</summary>

### [`v4.1.1`](52909a5e0d...6b7621f073)

[Compare Source](52909a5e0d...6b7621f073)

</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:eyJjcmVhdGVkSW5WZXIiOiIzNy4yMjAuMiIsInVwZGF0ZWRJblZlciI6IjM3LjIyMC4yIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-03-05 03:46:56 +00:00
Cats Juice
358e1e99c5 feat(core): set journal's really created time (#5968)
closes #5958;

This makes the creation date of the journal correspond to the date, and the creation time is the real creation time.
2024-03-05 03:37:23 +00:00
Peng Xiao
3fcbfe4d30 fix(electron): electron updater issues (#6005)
- generate-yml.js does not filter files correctly
- add version to generated bundles
- change `.AppImage` to `.appimage`. See https://github.com/electron-userland/electron-builder/blob/master/packages/electron-updater/src/providers/Provider.ts#L88
2024-03-05 03:27:52 +00:00
EYHN
a9be19ce6c fix(core): fix style break when scroll to block (#6001) 2024-03-04 11:43:10 +00:00
EYHN
d935cf9d4d fix(core): dont break workspace when page load failed (#5988) 2024-03-04 11:32:56 +00:00
tzyoo
ee54e00a03 fix(core): upgrade dialog close button layout level (#6004) 2024-03-04 11:30:25 +00:00
Cats Juice
2275eee5b2 feat(core): resize and reorder split-view (#5994) 2024-03-04 11:19:39 +00:00
Peng Xiao
7b31363c51 fix(electron): do not open external links in the browser for internal links in electron (#5997) 2024-03-04 09:39:03 +00:00
EYHN
c599715963 feat(core): split right sidebar (#5971)
https://github.com/toeverything/AFFiNE/assets/13579374/c846c069-aa32-445d-b59b-b773a9b05ced

Now each view has a general container, the yellow area is the general container part, and the green part is the routing specific part.

![CleanShot 2024-03-01 at 11.47.35@2x.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/g3jz87HxbjOJpXV3FPT7/9a9f6ad6-2207-42e5-ae66-f7426bc9f3fc.png)
2024-03-04 06:42:12 +00:00
Peng Xiao
e2a31ea1fc fix(core): some ui style issues (#5995)
Increase page info toggle click area:

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/7f712008-c2db-4522-81b0-6c18d2aea421.png)

fix history modal page height
2024-03-04 03:57:45 +00:00
JimmFly
ffda9a0567 feat(core): separate runtimeConfig for e2e tests and deployment (#5976)
Prior to modification, it will function for both `deploy` and `e2e-test`. Consequently, `allowLocalWorkspace` is also set to `true` in the production environment.
https://github.com/toeverything/AFFiNE/actions/runs/8093473180/job/22116199541#step:5:120
2024-03-03 12:00:11 +00:00
Peng Xiao
a1ea19fcb7 fix: optimize history animation (#5973)
- adjust timing
- make sure card do not show the bottom border on transitioning

<div class='graphite__hidden'>
          <div>🎥 Video uploaded on Graphite:</div>
            <a href="https://app.graphite.dev/media/video/T2klNLEk0wxLh4NRDzhk/7a225ec1-eb33-45ab-bf27-646bb2519a83.mp4">
              <img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/T2klNLEk0wxLh4NRDzhk/7a225ec1-eb33-45ab-bf27-646bb2519a83.mp4">
            </a>
          </div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/7a225ec1-eb33-45ab-bf27-646bb2519a83.mp4">Kapture 2024-03-01 at 10.56.09.mp4</video>
2024-03-01 06:08:08 +00:00
EYHN
de939bb6f6 fix(core): fix page mode scroll bar (#5955) 2024-03-01 04:46:15 +00:00
JimmFly
ff7e7f237f fix(core): update src in video tag to correct URL (#5974)
In app.affine.pro the video is not displayed correctly.

<img width="1026" alt="image" src="https://github.com/toeverything/AFFiNE/assets/102217452/8e9d9bb2-f973-433e-88c9-bedfe0f55935">
2024-03-01 04:36:37 +00:00
EYHN
a231884d9e fix(storybook): fix storybook test (#5970) 2024-03-01 02:30:04 +00:00
Peng Xiao
d4e78dd3d0 feat: add animation for history preview (#5966)
<div class='graphite__hidden'>
          <div>🎥 Video uploaded on Graphite:</div>
            <a href="https://app.graphite.dev/media/video/T2klNLEk0wxLh4NRDzhk/5ce45d13-1117-4853-a066-e8ab1446eb4f.mp4">
              <img src="https://app.graphite.dev/api/v1/graphite/video/thumbnail/T2klNLEk0wxLh4NRDzhk/5ce45d13-1117-4853-a066-e8ab1446eb4f.mp4">
            </a>
          </div>
<video src="https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/5ce45d13-1117-4853-a066-e8ab1446eb4f.mp4">Kapture 2024-02-29 at 18.54.15.mp4</video>
2024-02-29 14:08:21 +00:00
EYHN
78ce30db69 fix(core): fix editor sidebar (#5965) 2024-02-29 13:59:20 +00:00
Cats Juice
aaf048fcb8 chore(core): update journal's daily count label (#5967)
[tov-568](https://linear.app/affine-design/issue/TOV-568/journal-聚合的部分的名称需要修改一下)
2024-02-29 13:50:09 +00:00
Peng Xiao
752c8580ae fix: remove vite-tsconfig-paths (#5960)
It will cause storybook from running.
related to https://github.com/aleclarson/vite-tsconfig-paths/issues/132

We are not actually using `vite-tsconfig-paths` now because we rely on package.json's export field to do path mapping.
2024-02-29 07:58:41 +00:00
LongYinan
6f8a4d7bd5 chore: bump up all non-major dependencies (#5897)
[![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.515.0` -> `3.523.0`](https://renovatebot.com/diffs/npm/@aws-sdk%2fclient-s3/3.515.0/3.523.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@aws-sdk%2fclient-s3/3.523.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@aws-sdk%2fclient-s3/3.523.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@aws-sdk%2fclient-s3/3.515.0/3.523.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@aws-sdk%2fclient-s3/3.515.0/3.523.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@napi-rs/cli](https://togithub.com/napi-rs/napi-rs) | [`3.0.0-alpha.41` -> `3.0.0-alpha.43`](https://renovatebot.com/diffs/npm/@napi-rs%2fcli/3.0.0-alpha.41/3.0.0-alpha.43) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@napi-rs%2fcli/3.0.0-alpha.43?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.43?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.41/3.0.0-alpha.43?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.41/3.0.0-alpha.43?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@nx/vite](https://nx.dev) ([source](https://togithub.com/nrwl/nx/tree/HEAD/packages/vite)) | [`18.0.4` -> `18.0.5`](https://renovatebot.com/diffs/npm/@nx%2fvite/18.0.4/18.0.5) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@nx%2fvite/18.0.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@nx%2fvite/18.0.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@nx%2fvite/18.0.4/18.0.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@nx%2fvite/18.0.4/18.0.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

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

### [`v3.523.0`](https://togithub.com/aws/aws-sdk-js-v3/blob/HEAD/clients/client-s3/CHANGELOG.md#35230-2024-02-27)

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

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

### [`v3.521.0`](https://togithub.com/aws/aws-sdk-js-v3/blob/HEAD/clients/client-s3/CHANGELOG.md#35210-2024-02-23)

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

##### Features

-   requestHandler ctor param pass-through ([#&#8203;5820](https://togithub.com/aws/aws-sdk-js-v3/issues/5820)) ([9fec71d](9fec71d193))

</details>

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

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

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

##### 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)

**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.42...[@&#8203;napi-rs/cli](https://togithub.com/napi-rs/cli)[@&#8203;3](https://togithub.com/3).0.0-alpha.43

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

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

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

##### What's Changed

-   chore: fix renovate path and dedupe electron versions by [@&#8203;Brooooooklyn](https://togithub.com/Brooooooklyn) in [https://github.com/napi-rs/napi-rs/pull/1974](https://togithub.com/napi-rs/napi-rs/pull/1974)
-   refactor(cli): refactor js-binding to support easier bundling. by [@&#8203;everett1992](https://togithub.com/everett1992) in [https://github.com/napi-rs/napi-rs/pull/1957](https://togithub.com/napi-rs/napi-rs/pull/1957)

##### New Contributors

-   [@&#8203;everett1992](https://togithub.com/everett1992) made their first contribution in [https://github.com/napi-rs/napi-rs/pull/1957](https://togithub.com/napi-rs/napi-rs/pull/1957)

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

</details>

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

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

[Compare Source](https://togithub.com/nrwl/nx/compare/18.0.4...18.0.5)

#### 18.0.5 (2024-02-24)

##### 🩹 Fixes

-   **angular:** do not add target defaults for the ng-packagr-lite executor when generating non-buildable library ([#&#8203;21935](https://togithub.com/nrwl/nx/pull/21935))
-   **angular:** ensure generated editor tsconfig in apps only include runtime files ([#&#8203;21945](https://togithub.com/nrwl/nx/pull/21945))
-   **core:** run migrations ordered by their target version ([#&#8203;21799](https://togithub.com/nrwl/nx/pull/21799))
-   **core:** Update NxWelcome connect to cloud ([#&#8203;21830](https://togithub.com/nrwl/nx/pull/21830))
-   **core:** propagate `verbose` flag when running `init` generator dur… ([#&#8203;21868](https://togithub.com/nrwl/nx/pull/21868))
-   **core:** ensure migrate works with yarn PnP ([#&#8203;21824](https://togithub.com/nrwl/nx/pull/21824))
-   **core:** align terminal output padding and remove leading arrow ([#&#8203;21809](https://togithub.com/nrwl/nx/pull/21809))
-   **core:** read all targets from package json when defining target defaults ([#&#8203;21719](https://togithub.com/nrwl/nx/pull/21719))
-   **core:** include nx/nuxt in migrations ([#&#8203;21885](https://togithub.com/nrwl/nx/pull/21885))
-   **core:** do not use the new pty function for older versions of windows ([#&#8203;21854](https://togithub.com/nrwl/nx/pull/21854))
-   **core:** normalize migration target versions when sorting migrations ([#&#8203;21967](https://togithub.com/nrwl/nx/pull/21967))
-   **core:** target defaults application shouldn't include extra scripts ([#&#8203;21970](https://togithub.com/nrwl/nx/pull/21970))
-   **core:** update generated README pages with more useful instructions ([#&#8203;21976](https://togithub.com/nrwl/nx/pull/21976))
-   **devkit:** respect expectComments when parsing json ([#&#8203;21584](https://togithub.com/nrwl/nx/pull/21584))
-   **graph:** fix open project with / in name ([#&#8203;21722](https://togithub.com/nrwl/nx/pull/21722))
-   **js:** nx release-version resolve-version-spec should normalize fetchSpec ([#&#8203;21710](https://togithub.com/nrwl/nx/pull/21710))
-   **js:** swc executor should support inlining on windows ([#&#8203;21801](https://togithub.com/nrwl/nx/pull/21801))
-   **js:** set moduleResolution to Node10 so it is compatible with CommonJS module ([2dac233cf](https://togithub.com/nrwl/nx/commit/2dac233cf))
-   **linter:** fix eslint-plugin migration target version ([#&#8203;21966](https://togithub.com/nrwl/nx/pull/21966))
-   **misc:** logs from rm-default-collection should render properly ([#&#8203;21953](https://togithub.com/nrwl/nx/pull/21953))
-   **misc:** set nx property in root package.json when no replacing script in nx init ([#&#8203;21974](https://togithub.com/nrwl/nx/pull/21974))
-   **nextjs:** Svg should work when svgr is true in next config ([#&#8203;21761](https://togithub.com/nrwl/nx/pull/21761))
-   **nextjs:** Add missing e2e-ci target for cypress ([#&#8203;21805](https://togithub.com/nrwl/nx/pull/21805))
-   **nuxt:** init generator should add [@&#8203;nx/vite](https://togithub.com/nx/vite) to dependencies ([#&#8203;21911](https://togithub.com/nrwl/nx/pull/21911))
-   **nuxt:** turn on autoimport ([#&#8203;21894](https://togithub.com/nrwl/nx/pull/21894))
-   **nuxt:** tsconfig types and output dir ([#&#8203;21934](https://togithub.com/nrwl/nx/pull/21934))
-   **react:** generate correctly when --js is used for module federation host/remote ([#&#8203;20119](https://togithub.com/nrwl/nx/pull/20119))
-   **react:** full support custom secure host for module federation ([#&#8203;21777](https://togithub.com/nrwl/nx/pull/21777))
-   **react:** ensure playwright configuration is using correct port in app gen ([#&#8203;21941](https://togithub.com/nrwl/nx/pull/21941))
-   **react-native:** change gradlew to absolute path ([#&#8203;21725](https://togithub.com/nrwl/nx/pull/21725))
-   **react-native:** add all flag to sync-deps ([#&#8203;21821](https://togithub.com/nrwl/nx/pull/21821))
-   **release:** skip prompt for publish when no version created ([#&#8203;21769](https://togithub.com/nrwl/nx/pull/21769))
-   **release:** use --first-parent to support merged repos ([#&#8203;21686](https://togithub.com/nrwl/nx/pull/21686))
-   **release:** move github release creation to git tasks ([#&#8203;21510](https://togithub.com/nrwl/nx/pull/21510))
-   **remix:** should add remix plugin to nx.json on init correctly ([#&#8203;21827](https://togithub.com/nrwl/nx/pull/21827))
-   **remix:** the output path should respect the remix.config.js in crystal ([#&#8203;21842](https://togithub.com/nrwl/nx/pull/21842))
-   **remix:** adjust remix start script when building ([#&#8203;21883](https://togithub.com/nrwl/nx/pull/21883))
-   **remix:** typo in tsconfig.spec.json update led to invalid tsconfig ([#&#8203;21886](https://togithub.com/nrwl/nx/pull/21886))
-   **repo:** update browser tools to fix ci ([#&#8203;21955](https://togithub.com/nrwl/nx/pull/21955))
-   **testing:** jest should handle root jest.preset.cjs ([#&#8203;21746](https://togithub.com/nrwl/nx/pull/21746))
-   **testing:** fix cypress project targets does not exist ([#&#8203;21785](https://togithub.com/nrwl/nx/pull/21785))
-   **testing:** pin cypress version to avoid issue with verifying cypress ([#&#8203;21917](https://togithub.com/nrwl/nx/pull/21917))
-   **testing:** ensure baseUrl is not passed to playwright cli ([#&#8203;21943](https://togithub.com/nrwl/nx/pull/21943))
-   **testing:** playwright plugin enoent error ([#&#8203;21951](https://togithub.com/nrwl/nx/pull/21951))
-   **testing:** add null checks when reading targets ([#&#8203;21952](https://togithub.com/nrwl/nx/pull/21952))
-   **vite:** normalize vitest cli args in executor ([#&#8203;21870](https://togithub.com/nrwl/nx/pull/21870))
-   **vite:** project conversion generator ([#&#8203;21646](https://togithub.com/nrwl/nx/pull/21646))
-   **vite:** update vitest and use parseCLI ([#&#8203;21890](https://togithub.com/nrwl/nx/pull/21890))
-   **vue:** fixing vue and nuxt welcome templates ([#&#8203;21792](https://togithub.com/nrwl/nx/pull/21792))
-   **vue:** tailwind generator ignoring styleSheet option ([#&#8203;21840](https://togithub.com/nrwl/nx/pull/21840))
-   **webpack:** resolve relative path for assets inputs ([#&#8203;21822](https://togithub.com/nrwl/nx/pull/21822))
-   **webpack:** correctly handle paranthesis in PostCSS in url ([#&#8203;21884](https://togithub.com/nrwl/nx/pull/21884))
-   **webpack:** surface original error when remotes fail to start ([#&#8203;21919](https://togithub.com/nrwl/nx/pull/21919))

##### ❤️  Thank You

-   Austin Fahsl [@&#8203;fahslaj](https://togithub.com/fahslaj)
-   Colum Ferry [@&#8203;Coly010](https://togithub.com/Coly010)
-   Craigory Coppola [@&#8203;AgentEnder](https://togithub.com/AgentEnder)
-   Dan Roujinsky
-   Edouard Bozon [@&#8203;edbzn](https://togithub.com/edbzn)
-   Emily Xiong [@&#8203;xiongemi](https://togithub.com/xiongemi)
-   Jack Hsu [@&#8203;jaysoo](https://togithub.com/jaysoo)
-   Jonathan Cammisuli
-   Julian Martin
-   Katerina Skroumpelou [@&#8203;mandarini](https://togithub.com/mandarini)
-   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)
-   Remco Krams
-   Steven Nance [@&#8203;llwt](https://togithub.com/llwt)
-   Tine Kondo [@&#8203;tinesoft](https://togithub.com/tinesoft)
-   Vadim Goy
-   Viktor Pöntinen
-   Zachary DeRose [@&#8203;ZackDeRose](https://togithub.com/ZackDeRose)

</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:eyJjcmVhdGVkSW5WZXIiOiIzNy4yMDAuMCIsInVwZGF0ZWRJblZlciI6IjM3LjIxMi4wIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-02-29 06:51:59 +00:00
regischen
d1e6b23e1a feat: bump blocksuite (#5953) 2024-02-29 14:33:50 +08:00
EYHN
fa534869e1 feat(core): new onboarding template (#5951) 2024-02-29 06:02:32 +00:00
LongYinan
931c29fd31 chore: bump up commitlint monorepo to v19 (major) (#5928)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [@commitlint/config-conventional](https://commitlint.js.org/) ([source](https://togithub.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/config-conventional)) | [`^18.6.2` -> `^19.0.0`](https://renovatebot.com/diffs/npm/@commitlint%2fconfig-conventional/18.6.2/19.0.3) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@commitlint%2fconfig-conventional/19.0.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@commitlint%2fconfig-conventional/19.0.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@commitlint%2fconfig-conventional/18.6.2/19.0.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@commitlint%2fconfig-conventional/18.6.2/19.0.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [commitlint](https://commitlint.js.org/) ([source](https://togithub.com/conventional-changelog/commitlint/tree/HEAD/@alias/commitlint)) | [`^18.6.1` -> `^19.0.0`](https://renovatebot.com/diffs/npm/commitlint/18.6.1/19.0.3) | [![age](https://developer.mend.io/api/mc/badges/age/npm/commitlint/19.0.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/commitlint/19.0.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/commitlint/18.6.1/19.0.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/commitlint/18.6.1/19.0.3?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>conventional-changelog/commitlint (@&#8203;commitlint/config-conventional)</summary>

### [`v19.0.3`](https://togithub.com/conventional-changelog/commitlint/blob/HEAD/@&#8203;commitlint/config-conventional/CHANGELOG.md#1903-2024-02-28)

[Compare Source](https://togithub.com/conventional-changelog/commitlint/compare/v19.0.0...v19.0.3)

**Note:** Version bump only for package [@&#8203;commitlint/config-conventional](https://togithub.com/commitlint/config-conventional)

### [`v19.0.0`](https://togithub.com/conventional-changelog/commitlint/blob/HEAD/@&#8203;commitlint/config-conventional/CHANGELOG.md#1900-2024-02-27)

[Compare Source](https://togithub.com/conventional-changelog/commitlint/compare/v18.6.2...v19.0.0)

-   feat!: migrate to pure ESM ([#&#8203;3850](https://togithub.com/conventional-changelog/commitlint/issues/3850)) ([3423735](342373559b)), closes [#&#8203;3850](https://togithub.com/conventional-changelog/commitlint/issues/3850)

##### Reverts

-   Revert "chore!: minimum node version v20" ([2816783](2816783d00))

##### BREAKING CHANGES

-   migrate to pure ESM

-   feat: migrate to pure ESM

-   chore: update snapshot

-   fix: load `parserPreset` with another `await`

-   test: migrate to vitest

-   test: remove no replacement `--runInBand` test-ci script

-   chore: fix code reviews

-   refactor(load): rewrite resolve logic

-   fix(config-nx-scopes): fix syntax error

-   feat(resolve-extends): add resolveFrom and loadParserPreset

-   feat(load): use resolveFrom and loadParserPreset from resolve-extends

-   test: include only @&#8203;commitlint/\* packages src in coverage

-   test: explicit import vitest utilities

-   test: remove [@&#8203;jest/globals](https://togithub.com/jest/globals) from dependencies

-   fix(resolve-extends): `resolveFrom` output should be platform aware

-   test: restore NO_COLOR to test script

-   chore: fix linting issues

-   fix: should use fileURLToPath instead of pathname for Windows compatibility

-   Apply suggestions from code review

-   fix: should reuse `cli` instead call `yargs()`

-   feat(cli): set terminalWidth as wrap to avoid work break on help

-   Update .eslintrc.cjs

-   feat: migrate [@&#8203;commitlint/config-conventional](https://togithub.com/commitlint/config-conventional) to pure ESM

#### [18.6.2](https://togithub.com/conventional-changelog/commitlint/compare/v18.6.1...v18.6.2) (2024-02-14)

##### Bug Fixes

-   **config-conventional:** use default export ([#&#8203;3911](https://togithub.com/conventional-changelog/commitlint/issues/3911)) ([bc48408](bc4840832f))

#### [18.6.1](https://togithub.com/conventional-changelog/commitlint/compare/v18.6.0...v18.6.1) (2024-02-13)

**Note:** Version bump only for package [@&#8203;commitlint/config-conventional](https://togithub.com/commitlint/config-conventional)

</details>

<details>
<summary>conventional-changelog/commitlint (commitlint)</summary>

### [`v19.0.3`](https://togithub.com/conventional-changelog/commitlint/blob/HEAD/@&#8203;alias/commitlint/CHANGELOG.md#1903-2024-02-28)

[Compare Source](https://togithub.com/conventional-changelog/commitlint/compare/v19.0.2...v19.0.3)

**Note:** Version bump only for package commitlint

### [`v19.0.2`](https://togithub.com/conventional-changelog/commitlint/blob/HEAD/@&#8203;alias/commitlint/CHANGELOG.md#1902-2024-02-28)

[Compare Source](https://togithub.com/conventional-changelog/commitlint/compare/v19.0.1...v19.0.2)

**Note:** Version bump only for package commitlint

### [`v19.0.1`](https://togithub.com/conventional-changelog/commitlint/blob/HEAD/@&#8203;alias/commitlint/CHANGELOG.md#1901-2024-02-27)

[Compare Source](https://togithub.com/conventional-changelog/commitlint/compare/v19.0.0...v19.0.1)

**Note:** Version bump only for package commitlint

### [`v19.0.0`](https://togithub.com/conventional-changelog/commitlint/blob/HEAD/@&#8203;alias/commitlint/CHANGELOG.md#1900-2024-02-27)

[Compare Source](https://togithub.com/conventional-changelog/commitlint/compare/v18.6.1...v19.0.0)

-   feat!: migrate to pure ESM ([#&#8203;3850](https://togithub.com/conventional-changelog/commitlint/issues/3850)) ([3423735](342373559b)), closes [#&#8203;3850](https://togithub.com/conventional-changelog/commitlint/issues/3850)

##### Reverts

-   Revert "chore!: minimum node version v20" ([2816783](2816783d00))

##### BREAKING CHANGES

-   migrate to pure ESM

-   feat: migrate to pure ESM

-   chore: update snapshot

-   fix: load `parserPreset` with another `await`

-   test: migrate to vitest

-   test: remove no replacement `--runInBand` test-ci script

-   chore: fix code reviews

-   refactor(load): rewrite resolve logic

-   fix(config-nx-scopes): fix syntax error

-   feat(resolve-extends): add resolveFrom and loadParserPreset

-   feat(load): use resolveFrom and loadParserPreset from resolve-extends

-   test: include only @&#8203;commitlint/\* packages src in coverage

-   test: explicit import vitest utilities

-   test: remove [@&#8203;jest/globals](https://togithub.com/jest/globals) from dependencies

-   fix(resolve-extends): `resolveFrom` output should be platform aware

-   test: restore NO_COLOR to test script

-   chore: fix linting issues

-   fix: should use fileURLToPath instead of pathname for Windows compatibility

-   Apply suggestions from code review

-   fix: should reuse `cli` instead call `yargs()`

-   feat(cli): set terminalWidth as wrap to avoid work break on help

-   Update .eslintrc.cjs

-   feat: migrate [@&#8203;commitlint/config-conventional](https://togithub.com/commitlint/config-conventional) to pure ESM

#### [18.6.1](https://togithub.com/conventional-changelog/commitlint/compare/v18.6.0...v18.6.1) (2024-02-13)

**Note:** Version bump only for package commitlint

</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 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:eyJjcmVhdGVkSW5WZXIiOiIzNy4yMTIuMCIsInVwZGF0ZWRJblZlciI6IjM3LjIxMi4wIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-02-29 03:44:13 +00:00
LongYinan
29fc4d141f chore: bump up azure/setup-helm action to v4 (#5950)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [azure/setup-helm](https://togithub.com/azure/setup-helm) | action | major | `v3` -> `v4` |

---

### Release Notes

<details>
<summary>azure/setup-helm (azure/setup-helm)</summary>

### [`v4`](https://togithub.com/Azure/setup-helm/releases/tag/v4)

[Compare Source](https://togithub.com/azure/setup-helm/compare/v3...v4)

Latest v4 release

</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:eyJjcmVhdGVkSW5WZXIiOiIzNy4yMTIuMCIsInVwZGF0ZWRJblZlciI6IjM3LjIxMi4wIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-02-29 03:34:02 +00:00
DarkSky
3432f355b0 feat: backend module awareness & optional request (#5909) 2024-02-28 08:29:37 +00:00
Peng Xiao
35454c3bfc fix(core): polyfill resize-observer for development to avoid error overlay (#5943)
Brought a temporary solution from https://github.com/petyosi/react-virtuoso/issues/875#issuecomment-1962897033
2024-02-28 08:18:33 +00:00
DarkSky
82f21ac60b feat: udpate security policy docs (#5927) 2024-02-28 08:07:49 +00:00
wumo
d7ff7a3d95 docs: update contributed tutorial documentation (#5939) 2024-02-28 06:31:28 +00:00
liuyi
a38f7ee252 fix(server): sender passed to nextauth is never used (#5938) 2024-02-28 05:44:25 +00:00
Peng Xiao
27f2209e87 fix(core): date formatter timezone issue (#5936)
date seems not hornoring the locale browser's locale when parsing date string like "2024-02-28".
fixed by using dayjs instead.

Fix incorrect journal title issue
2024-02-28 04:27:32 +00:00
LongYinan
9e09166452 build: fix selfhost config (#5930) 2024-02-28 04:17:29 +00:00
Peng Xiao
a0364dd4ea fix(core): minor page info style issues (#5935) 2024-02-28 03:45:51 +00:00
Peng Xiao
148807879b refactor(component): use react-transition-state to simplify exit animation (#5923)
## **User description**
use react-transition-state to simplify exit animation

___

## **Type**
enhancement

___

## **Description**
- Integrated `react-transition-state` for managing animations in `ResizePanel` and `CMDKModal` components, simplifying the code and improving maintainability.
- Introduced a shared animation timeout variable to standardize animation durations across components.
- Added `react-transition-state` to dependencies to enable the new animation handling.

___

## **Changes walkthrough**
<table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>resize-panel.tsx</strong><dd><code>Integrate `react-transition-state` for Resize Panel Animations</code></dd></summary>
<hr>

packages/frontend/component/src/components/resize-panel/resize-panel.tsx

<li>Introduced <code>useTransition</code> hook from <code>react-transition-state</code> for managing <br>animations.<br> <li> Added a constant for animation timeout and applied it to the <br>transition.<br> <li> Utilized the transition state to toggle the open state of the resize <br>panel.<br>

</details>

  </td>
  <td><a href="https:/toeverything/AFFiNE/pull/5923/files#diff-a4d6e633862f63f97c167ff41ba62aff8aebf3e3b2f6e7ce13d5a0e22e8ff287">+12/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
      <summary><strong>modal.css.ts</strong><dd><code>Use Variable for Animation Timeout in Modal CSS</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

packages/frontend/core/src/components/pure/cmdk/modal.css.ts

<li>Created a variable for animation timeout.<br> <li> Updated animation durations to use the new timeout variable.<br>

</details>

  </td>
  <td><a href="https:/toeverything/AFFiNE/pull/5923/files#diff-ba8935611b9c1695153d92d08ecb0f7dac73a6197f54ccda5a6e791902cd651d">+6/-3</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
      <summary><strong>modal.tsx</strong><dd><code>Simplify CMDK Modal Animation with `react-transition-state`</code></dd></summary>
<hr>

packages/frontend/core/src/components/pure/cmdk/modal.tsx

<li>Replaced custom animation state management with <code>useTransition</code> hook.<br> <li> Removed old animation state logic and simplified the component.<br> <li> Added animation timeout variable and applied it to the transition.<br>

</details>

  </td>
  <td><a href="https:/toeverything/AFFiNE/pull/5923/files#diff-ab3fe66c9b1d3a691fafd1aebc9988e840fedfcd09b5b89570838a5a2a9469c9">+13/-33</a>&nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Dependencies</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>package.json</strong><dd><code>Add `react-transition-state` Dependency</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

packages/frontend/core/package.json

- Added `react-transition-state` as a dependency.

</details>

  </td>
  <td><a href="https:/toeverything/AFFiNE/pull/5923/files#diff-23e0e5dc0ceb004a0a5d3d13e7d00545de7487535ca0e5eab4c5047f1e24eff0">+1/-0</a>&nbsp; &nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

>  **PR-Agent usage**:
>Comment `/help` on the PR to get a list of all available PR-Agent tools and their descriptions
2024-02-27 14:55:33 +00:00
LongYinan
c724b3fece build(electron): fix dmg maker (#5922)
## **Type**
enhancement

___

## **Description**
- Introduced a custom DMG packaging solution `@affine/appdmg-patch` for Electron applications.
- Implemented a new TypeScript class `MakerDmg` to handle the custom packaging logic, including support for custom backgrounds, icons, and app positioning within the DMG.
- Updated Electron Forge configuration to use the new `@affine/appdmg-patch` package instead of `@electron-forge/maker-dmg`.
- Adjusted package dependencies in `packages/frontend/electron/package.json`, adding `@affine/appdmg-patch` and removing `@electron-forge/maker-dmg`.

___

## **Changes walkthrough**
<table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement
</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>index.ts</strong><dd><code>Implement Custom DMG Packaging Logic</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

tools/appdmg-patch/index.ts

<li>Introduced a new class <code>MakerDmg</code> extending <code>MakerBase</code> with support for <br>custom DMG packaging.<br> <li> Implemented custom DMG packaging logic including background, icon, and <br>app positioning.<br> <li> Added support for custom <code>contents</code> configuration to define the DMG <br>layout.<br>

</details>

  </td>
  <td><a href="https:/toeverything/AFFiNE/pull/5922/files#diff-96a537c04a2a740dca542688b3e6a65c67e6cf339b146bf0a9c49008ad58ea77">+80/-0</a>&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Configuration changes
</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>forge.config.mjs</strong><dd><code>Update Electron Forge Configuration for Custom DMG Packaging</code></dd></summary>
<hr>

packages/frontend/electron/forge.config.mjs

<li>Replaced <code>@electron-forge/maker-dmg</code> with <code>@affine/appdmg-patch</code> in the <br>makers configuration.<br> <li> Adjusted DMG icon size and background configuration.<br>

</details>

  </td>
  <td><a href="https:/toeverything/AFFiNE/pull/5922/files#diff-bd3343a702deabb8f5f6f77e34fbc215615538875f59b68d82612c0f5ed532a3">+2/-3</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
      <summary><strong>package.json</strong><dd><code>Introduce Package Configuration for Custom DMG Packaging</code>&nbsp; </dd></summary>
<hr>

tools/appdmg-patch/package.json

<li>Created a new package <code>@affine/appdmg-patch</code> with necessary dependencies <br>for custom DMG packaging.<br>

</details>

  </td>
  <td><a href="https:/toeverything/AFFiNE/pull/5922/files#diff-395b189951512e67d55b0abc55a167f014f239f608ec5b180f9c7d48e1ea9e8b">+16/-0</a>&nbsp; &nbsp; </td>

</tr>
</table></td></tr><tr><td><strong>Dependencies
</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>package.json</strong><dd><code>Update Package Dependencies for Electron Frontend</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

packages/frontend/electron/package.json

<li>Added <code>@affine/appdmg-patch</code> to <code>devDependencies</code>.<br> <li> Removed <code>@electron-forge/maker-dmg</code> from <code>devDependencies</code>.<br>

</details>

  </td>
  <td><a href="https:/toeverything/AFFiNE/pull/5922/files#diff-37323e9da5e4b1606abb6eb8e5ff1516ae5fce925bceaafeeade61bd80c0b62d">+1/-1</a>&nbsp; &nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

>  **PR-Agent usage**:
>Comment `/help` on the PR to get a list of all available PR-Agent tools and their descriptions
2024-02-27 14:41:39 +00:00
LongYinan
571b20d082 chore: bump up @commitlint/cli version to v19 (#5926)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [@commitlint/cli](https://commitlint.js.org/) ([source](https://togithub.com/conventional-changelog/commitlint/tree/HEAD/@commitlint/cli)) | [`^18.6.1` -> `^19.0.0`](https://renovatebot.com/diffs/npm/@commitlint%2fcli/18.6.1/19.0.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@commitlint%2fcli/19.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@commitlint%2fcli/19.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@commitlint%2fcli/18.6.1/19.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@commitlint%2fcli/18.6.1/19.0.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>conventional-changelog/commitlint (@&#8203;commitlint/cli)</summary>

### [`v19.0.0`](https://togithub.com/conventional-changelog/commitlint/blob/HEAD/@&#8203;commitlint/cli/CHANGELOG.md#1900-2024-02-27)

[Compare Source](https://togithub.com/conventional-changelog/commitlint/compare/v18.6.1...f1ff12159d627ee63bf8982ab02e6cca8f10b09f)

-   feat!: migrate to pure ESM ([#&#8203;3850](https://togithub.com/conventional-changelog/commitlint/issues/3850)) ([3423735](342373559b)), closes [#&#8203;3850](https://togithub.com/conventional-changelog/commitlint/issues/3850)

##### Reverts

-   Revert "chore!: minimum node version v20" ([2816783](2816783d00))

##### BREAKING CHANGES

-   migrate to pure ESM

-   feat: migrate to pure ESM

-   chore: update snapshot

-   fix: load `parserPreset` with another `await`

-   test: migrate to vitest

-   test: remove no replacement `--runInBand` test-ci script

-   chore: fix code reviews

-   refactor(load): rewrite resolve logic

-   fix(config-nx-scopes): fix syntax error

-   feat(resolve-extends): add resolveFrom and loadParserPreset

-   feat(load): use resolveFrom and loadParserPreset from resolve-extends

-   test: include only @&#8203;commitlint/\* packages src in coverage

-   test: explicit import vitest utilities

-   test: remove [@&#8203;jest/globals](https://togithub.com/jest/globals) from dependencies

-   fix(resolve-extends): `resolveFrom` output should be platform aware

-   test: restore NO_COLOR to test script

-   chore: fix linting issues

-   fix: should use fileURLToPath instead of pathname for Windows compatibility

-   Apply suggestions from code review

-   fix: should reuse `cli` instead call `yargs()`

-   feat(cli): set terminalWidth as wrap to avoid work break on help

-   Update .eslintrc.cjs

-   feat: migrate [@&#8203;commitlint/config-conventional](https://togithub.com/commitlint/config-conventional) to pure ESM

#### [18.6.1](https://togithub.com/conventional-changelog/commitlint/compare/v18.6.0...v18.6.1) (2024-02-13)

**Note:** Version bump only for package [@&#8203;commitlint/cli](https://togithub.com/commitlint/cli)

</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:eyJjcmVhdGVkSW5WZXIiOiIzNy4yMTIuMCIsInVwZGF0ZWRJblZlciI6IjM3LjIxMi4wIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-02-27 13:09:18 +00:00
liuyi
540d079308 ci: fix selfhost (#5920)
## **Type**
enhancement

___

## **Description**
- Introduced a new ESM module resolution setup using `ts-node` to enhance the development and deployment process.
- Implemented a dynamic loader script registration mechanism to facilitate ESM module loading.
- Simplified the predeploy script execution by refining environment variable handling and stdout configuration.
- Updated `package.json` to reflect changes in script commands for better ESM support and added necessary dependencies for `ts-node` and `typescript`.

___

## **Changes walkthrough**
<table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>loader.js</strong><dd><code>Introduce ESM Module Resolution via ts-node</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

packages/backend/server/scripts/loader.js

<li>Introduced <code>ts-node</code> configuration for ESM module resolution.<br> <li> Exported a <code>resolve</code> function for module resolution.<br>

</details>

  </td>
  <td><a href="https:/toeverything/AFFiNE/pull/5920/files#diff-9ed793897a493633028d510db0742ff38d2d86471c54b17513d4354c51597ef8">+11/-0</a>&nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
      <summary><strong>register.js</strong><dd><code>Implement Dynamic Loader Script Registration</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

packages/backend/server/scripts/register.js

<li>Implemented dynamic registration of the loader script.<br> <li> Utilized <code>node:module</code> and <code>node:url</code> for script registration.<br>

</details>

  </td>
  <td><a href="https:/toeverything/AFFiNE/pull/5920/files#diff-64831012a09f2bc4bc5a611ddb8e0871b0e83588de6c5d4f2f5cb1dae8fff244">+4/-0</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
      <summary><strong>self-host-predeploy.js</strong><dd><code>Simplify Predeploy Script Execution</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

packages/backend/server/scripts/self-host-predeploy.js

<li>Simplified environment variable passing to <code>execSync</code>.<br> <li> Changed stdout handling to inherit from the parent process.<br>

</details>

  </td>
  <td><a href="https:/toeverything/AFFiNE/pull/5920/files#diff-bd7b0be14c198018c21dadda6945a779c57d13e4c8584ee62da4baa99d370664">+3/-5</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
      <summary><strong>package.json</strong><dd><code>Update Scripts and Dependencies for ESM Support</code>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </dd></summary>
<hr>

packages/backend/server/package.json

<li>Updated script commands for ESM compatibility.<br> <li> Added <code>ts-node</code> and <code>typescript</code> dependencies.<br> <li> Removed redundant <code>--es-module-specifier-resolution=node</code> flags.<br>

</details>

  </td>
  <td><a href="https:/toeverything/AFFiNE/pull/5920/files#diff-a6530c6fe539aaa49ff0a7a80bc4362c1d95c419fdd19125415dcc869b31a443">+6/-6</a>&nbsp; &nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

>  **PR-Agent usage**:
>Comment `/help` on the PR to get a list of all available PR-Agent tools and their descriptions
2024-02-27 07:22:21 +00:00
Peng Xiao
39d177c507 build: codesigning dmg (#5921)
fix https://github.com/toeverything/AFFiNE/issues/5898
2024-02-27 06:35:46 +00:00
EYHN
606397e319 feat(core): workbench system (#5837) 2024-02-27 04:14:07 +00:00
EYHN
5cd488fe1d refactor(core): remove collection atom (#5832) 2024-02-27 03:50:56 +00:00
EYHN
ad9b0303c4 refactor(core): refactor atom to use di (#5831)
To support multiple instances, this PR removes some atoms and implements them using the new DI system.

removed atom

- `pageSettingsAtom`
- `currentPageIdAtom`
- `currentModeAtom`
2024-02-27 03:50:53 +00:00
EYHN
0dabb08217 chore: create default user for devcontainer (#5917)
## **Type**
enhancement

___

## **Description**
- Added a command to `.devcontainer/build.sh` to create a default user (`affine`) in the database for development purposes.
- Introduced error handling in `.devcontainer/setup-user.sh` to ensure the script stops on the first error.
- Modified the git fetch command in `.devcontainer/setup-user.sh` to fetch the `canary` branch from `origin` with a depth of 1, optimizing the setup process.

___

## **Changes walkthrough**
<table><thead><tr><th></th><th align="left">Relevant files</th></tr></thead><tbody><tr><td><strong>Enhancement
</strong></td><td><table>
<tr>
  <td>
    <details>
      <summary><strong>build.sh</strong><dd><code>Add Default User Creation to Dev Container Build Script</code>&nbsp; &nbsp; </dd></summary>
<hr>

.devcontainer/build.sh

<li>Added command to create a default user with username and password <br>'affine'.<br> <li> Ensured database push command is followed by a newline.<br>

</details>

  </td>
  <td><a href="https:/toeverything/AFFiNE/pull/5917/files#diff-165b66803397b501866b23a0532418e9ad3eb2ab642274f9679383c76d18136f">+4/-1</a>&nbsp; &nbsp; &nbsp; </td>

</tr>

<tr>
  <td>
    <details>
      <summary><strong>setup-user.sh</strong><dd><code>Enhance User Setup Script with Error Handling and Specific Git Fetch</code></dd></summary>
<hr>

.devcontainer/setup-user.sh

<li>Introduced error handling with 'set -e' to stop on first error.<br> <li> Modified 'git fetch' to specifically fetch 'canary' branch from <br>'origin' with depth of 1.<br>

</details>

  </td>
  <td><a href="https:/toeverything/AFFiNE/pull/5917/files#diff-8c5e76719e2ff0a3bf2b59d108dc9cf88bdae00c317728454cfa5108f2fc76af">+3/-1</a>&nbsp; &nbsp; &nbsp; </td>

</tr>
</table></td></tr></tr></tbody></table>

___

>  **PR-Agent usage**:
>Comment `/help` on the PR to get a list of all available PR-Agent tools and their descriptions
2024-02-27 03:32:03 +00:00
Peng Xiao
2295685590 fix: page info styles (#5910)
fix inconsistent styles compared to the one defined in figma
fix https://github.com/toeverything/AFFiNE/issues/5904
fix https://github.com/toeverything/AFFiNE/issues/5903
2024-02-26 14:11:24 +00:00
EYHN
6ccc4f1501 chore: start dev server on devcontainer (#5914) 2024-02-26 13:47:06 +00:00
EYHN
ee4918b3b8 chore: update devcontainer build.sh (#5912) 2024-02-26 13:47:00 +00:00
Hwang
8e5ab2bfbf docs(docs): update readmd.md (#5911)
Co-authored-by: EYHN <cneyhn@gmail.com>
2024-02-26 10:57:11 +00:00
HeJiachen-PM
d602cbb53c ci: update compose.yaml (#5905) 2024-02-26 18:47:37 +08:00
Peng Xiao
298afc7d74 feat(core): enable right sidepanel animation (#5902) 2024-02-26 09:04:04 +00:00
JimmFly
0be62d892d refactor(core): use custom scrollbar for editor and adjust shared page style (#5752)
Close TOV-481
- Use a custom scrollbar component for editor
- Modified the header of the share page and added a new footer
2024-02-26 08:54:52 +00:00
JimmFly
e9f9eea80c fix(core): handling error message when sending verify change email (#5894) 2024-02-26 08:32:48 +00:00
JimmFly
d3360f655e chore: update i18n resources (#5906) 2024-02-26 07:53:45 +00:00
Peng Xiao
cc2d97e3b8 fix: right sidepanel open on focus (#5901)
fix TOV-614
Should not show sidepanel on focusing inner elemenets
2024-02-26 03:43:37 +00:00
Peng Xiao
7d73145a69 fix: page info -> info (#5892)
fix AFF-607
2024-02-23 14:14:43 +00:00
Peng Xiao
c16e4d4464 fix: page properties flaky tests (#5893) 2024-02-23 14:14:41 +00:00
Peng Xiao
9e7406e0a1 chore: downgrade react-virtuoso to 4.6.3 (#5896)
temp workaround for https://github.com/petyosi/react-virtuoso/issues/1039

![image.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/T2klNLEk0wxLh4NRDzhk/79901ff6-fe30-4794-ab93-c81f217022d0.png)
2024-02-23 13:55:11 +00:00
LongYinan
ae41c6c544 chore: bump all dependencies (#5891) 2024-02-23 09:37:14 +00:00
Peng Xiao
db1d0dcf52 fix(electron): autohide menu bar on linux (#5890) 2024-02-23 09:27:29 +00:00
LongYinan
ccd3190cec ci: run CI on beta/stable push (#5889) 2024-02-23 09:17:13 +00:00
LongYinan
062526d452 chore: bump up node version to v20 (#5201)
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [node](https://nodejs.org) ([source](https://togithub.com/nodejs/node)) |  | major | `18` -> `20` |
| [node](https://nodejs.org) ([source](https://togithub.com/nodejs/node)) | engines | major | [`>=18.16.1 <19.0.0` -> `<21.0.0`](https://renovatebot.com/diffs/npm/node/v18.19.1/v20.11.1) |
| [node](https://togithub.com/nodejs/node) | final | major | `18-bookworm-slim` -> `20-bookworm-slim` |

---

### Release Notes

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

### [`v20.11.1`](https://togithub.com/nodejs/node/compare/v20.11.0...v20.11.1)

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

### [`v20.11.0`](https://togithub.com/nodejs/node/compare/v20.10.0...v20.11.0)

[Compare Source](https://togithub.com/nodejs/node/compare/v20.10.0...v20.11.0)

### [`v20.10.0`](https://togithub.com/nodejs/node/compare/v20.9.0...v20.10.0)

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

### [`v20.9.0`](https://togithub.com/nodejs/node/compare/v20.8.1...v20.9.0)

[Compare Source](https://togithub.com/nodejs/node/compare/v20.8.1...v20.9.0)

### [`v20.8.1`](https://togithub.com/nodejs/node/releases/tag/v20.8.1): 2023-10-13, Version 20.8.1 (Current), @&#8203;RafaelGSS

[Compare Source](https://togithub.com/nodejs/node/compare/v20.8.0...v20.8.1)

This is a security release.

##### Notable Changes

The following CVEs are fixed in this release:

-   [CVE-2023-44487](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-44487): `nghttp2` Security Release (High)
-   [CVE-2023-45143](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-45143): `undici` Security Release (High)
-   [CVE-2023-39332](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-39332): Path traversal through path stored in Uint8Array (High)
-   [CVE-2023-39331](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-39331): Permission model improperly protects against path traversal (High)
-   [CVE-2023-38552](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-38552):  Integrity checks according to policies can be circumvented (Medium)
-   [CVE-2023-39333](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-39333): Code injection via WebAssembly export names (Low)

More detailed information on each of the vulnerabilities can be found in [October 2023 Security Releases](https://nodejs.org/en/blog/vulnerability/october-2023-security-releases/) blog post.

##### Commits

-   \[[`c86883e844`](https://togithub.com/nodejs/node/commit/c86883e844)] - **deps**: update nghttp2 to 1.57.0 (James M Snell) [#&#8203;50121](https://togithub.com/nodejs/node/pull/50121)
-   \[[`2860631359`](https://togithub.com/nodejs/node/commit/2860631359)] - **deps**: update undici to v5.26.3 (Matteo Collina) [#&#8203;50153](https://togithub.com/nodejs/node/pull/50153)
-   \[[`cd37838bf8`](https://togithub.com/nodejs/node/commit/cd37838bf8)] - **lib**: let deps require `node` prefixed modules (Matthew Aitken) [#&#8203;50047](https://togithub.com/nodejs/node/pull/50047)
-   \[[`f5c90b2951`](https://togithub.com/nodejs/node/commit/f5c90b2951)] - **module**: fix code injection through export names (Tobias Nießen) [nodejs-private/node-private#461](https://togithub.com/nodejs-private/node-private/pull/461)
-   \[[`fa5dae1944`](https://togithub.com/nodejs/node/commit/fa5dae1944)] - **permission**: fix Uint8Array path traversal (Tobias Nießen) [nodejs-private/node-private#456](https://togithub.com/nodejs-private/node-private/pull/456)
-   \[[`cd35275111`](https://togithub.com/nodejs/node/commit/cd35275111)] - **permission**: improve path traversal protection (Tobias Nießen) [nodejs-private/node-private#456](https://togithub.com/nodejs-private/node-private/pull/456)
-   \[[`a4cb7fc7c0`](https://togithub.com/nodejs/node/commit/a4cb7fc7c0)] - **policy**: use tamper-proof integrity check function (Tobias Nießen) [nodejs-private/node-private#462](https://togithub.com/nodejs-private/node-private/pull/462)

### [`v20.8.0`](https://togithub.com/nodejs/node/releases/tag/v20.8.0): 2023-09-28, Version 20.8.0 (Current), @&#8203;ruyadorno

[Compare Source](https://togithub.com/nodejs/node/compare/v20.7.0...v20.8.0)

##### Notable Changes

##### Stream performance improvements

Performance improvements to writable and readable streams, improving the creation and destruction by ±15% and reducing the memory overhead each stream takes in Node.js

Contributed by Benjamin Gruenbaum in [#&#8203;49745](https://togithub.com/nodejs/node/pull/49745) and Raz Luvaton in [#&#8203;49834](https://togithub.com/nodejs/node/pull/49834).

Performance improvements for readable webstream, improving readable stream async iterator consumption by ±140% and improving readable stream `pipeTo` consumption by ±60%

Contributed by Raz Luvaton in [#&#8203;49662](https://togithub.com/nodejs/node/pull/49662) and [#&#8203;49690](https://togithub.com/nodejs/node/pull/49690).

##### Rework of memory management in `vm` APIs with the `importModuleDynamically` option

This rework addressed a series of long-standing memory leaks and use-after-free issues in the following APIs that support `importModuleDynamically`:

-   `vm.Script`
-   `vm.compileFunction`
-   `vm.SyntheticModule`
-   `vm.SourceTextModule`

This should enable affected users (in particular Jest users) to upgrade from older versions of Node.js.

Contributed by Joyee Cheung in [#&#8203;48510](https://togithub.com/nodejs/node/pull/48510).

##### Other notable changes

-   \[[`32d4d29d02`](https://togithub.com/nodejs/node/commit/32d4d29d02)] - **deps**: add v8::Object::SetInternalFieldForNodeCore() (Joyee Cheung) [#&#8203;49874](https://togithub.com/nodejs/node/pull/49874)
-   \[[`0e686d096b`](https://togithub.com/nodejs/node/commit/0e686d096b)] - **doc**: deprecate `fs.F_OK`, `fs.R_OK`, `fs.W_OK`, `fs.X_OK` (Livia Medeiros) [#&#8203;49683](https://togithub.com/nodejs/node/pull/49683)
-   \[[`a5dd057540`](https://togithub.com/nodejs/node/commit/a5dd057540)] - **doc**: deprecate `util.toUSVString` (Yagiz Nizipli) [#&#8203;49725](https://togithub.com/nodejs/node/pull/49725)
-   \[[`7b6a73172f`](https://togithub.com/nodejs/node/commit/7b6a73172f)] - **doc**: deprecate calling `promisify` on a function that returns a promise (Antoine du Hamel) [#&#8203;49647](https://togithub.com/nodejs/node/pull/49647)
-   \[[`1beefd5f16`](https://togithub.com/nodejs/node/commit/1beefd5f16)] - **esm**: set all hooks as release candidate (Geoffrey Booth) [#&#8203;49597](https://togithub.com/nodejs/node/pull/49597)
-   \[[`b0ce78a75b`](https://togithub.com/nodejs/node/commit/b0ce78a75b)] - **module**: fix the leak in SourceTextModule and ContextifySript (Joyee Cheung) [#&#8203;48510](https://togithub.com/nodejs/node/pull/48510)
-   \[[`4e578f8ab1`](https://togithub.com/nodejs/node/commit/4e578f8ab1)] - **module**: fix leak of vm.SyntheticModule (Joyee Cheung) [#&#8203;48510](https://togithub.com/nodejs/node/pull/48510)
-   \[[`69e4218772`](https://togithub.com/nodejs/node/commit/69e4218772)] - **module**: use symbol in WeakMap to manage host defined options (Joyee Cheung) [#&#8203;48510](https://togithub.com/nodejs/node/pull/48510)
-   \[[`14ece0aa76`](https://togithub.com/nodejs/node/commit/14ece0aa76)] - **(SEMVER-MINOR)** **src**: allow embedders to override NODE_MODULE_VERSION (Cheng Zhao) [#&#8203;49279](https://togithub.com/nodejs/node/pull/49279)
-   \[[`9fd67fbff0`](https://togithub.com/nodejs/node/commit/9fd67fbff0)] - **stream**: use bitmap in writable state (Raz Luvaton) [#&#8203;49834](https://togithub.com/nodejs/node/pull/49834)
-   \[[`0ccd4638ac`](https://togithub.com/nodejs/node/commit/0ccd4638ac)] - **stream**: use bitmap in readable state (Benjamin Gruenbaum) [#&#8203;49745](https://togithub.com/nodejs/node/pull/49745)
-   \[[`7c5e322346`](https://togithub.com/nodejs/node/commit/7c5e322346)] - **stream**: improve webstream readable async iterator performance (Raz Luvaton) [#&#8203;49662](https://togithub.com/nodejs/node/pull/49662)
-   \[[`80b342cc38`](https://togithub.com/nodejs/node/commit/80b342cc38)] - **(SEMVER-MINOR)** **test_runner**: accept `testOnly` in `run` (Moshe Atlow) [#&#8203;49753](https://togithub.com/nodejs/node/pull/49753)
-   \[[`17a05b141d`](https://togithub.com/nodejs/node/commit/17a05b141d)] - **(SEMVER-MINOR)** **test_runner**: add junit reporter (Moshe Atlow) [#&#8203;49614](https://togithub.com/nodejs/node/pull/49614)

##### Commits

-   \[[`4879e3fbbe`](https://togithub.com/nodejs/node/commit/4879e3fbbe)] - **benchmark**: add a benchmark for read() of ReadableStreams (Debadree Chatterjee) [#&#8203;49622](https://togithub.com/nodejs/node/pull/49622)
-   \[[`78a6c73157`](https://togithub.com/nodejs/node/commit/78a6c73157)] - **benchmark**: shorten pipe-to by reducing number of chunks (Raz Luvaton) [#&#8203;49577](https://togithub.com/nodejs/node/pull/49577)
-   \[[`4126a6e4c9`](https://togithub.com/nodejs/node/commit/4126a6e4c9)] - **benchmark**: fix webstream pipe-to (Raz Luvaton) [#&#8203;49552](https://togithub.com/nodejs/node/pull/49552)
-   \[[`6010a91825`](https://togithub.com/nodejs/node/commit/6010a91825)] - **bootstrap**: do not expand argv1 for snapshots (Joyee Cheung) [#&#8203;49506](https://togithub.com/nodejs/node/pull/49506)
-   \[[`8480280c4b`](https://togithub.com/nodejs/node/commit/8480280c4b)] - **bootstrap**: only use the isolate snapshot when compiling code cache (Joyee Cheung) [#&#8203;49288](https://togithub.com/nodejs/node/pull/49288)
-   \[[`b30754aa87`](https://togithub.com/nodejs/node/commit/b30754aa87)] - **build**: run embedtest using node executable (Joyee Cheung) [#&#8203;49506](https://togithub.com/nodejs/node/pull/49506)
-   \[[`31db0b8e2b`](https://togithub.com/nodejs/node/commit/31db0b8e2b)] - **build**: add --write-snapshot-as-array-literals to configure.py (Joyee Cheung) [#&#8203;49312](https://togithub.com/nodejs/node/pull/49312)
-   \[[`6fcb51d3ba`](https://togithub.com/nodejs/node/commit/6fcb51d3ba)] - **debugger**: use `internal/url.URL` instead of `url.parse` (LiviaMedeiros) [#&#8203;49590](https://togithub.com/nodejs/node/pull/49590)
-   \[[`32d4d29d02`](https://togithub.com/nodejs/node/commit/32d4d29d02)] - **deps**: add v8::Object::SetInternalFieldForNodeCore() (Joyee Cheung) [#&#8203;49874](https://togithub.com/nodejs/node/pull/49874)
-   \[[`ad37cadc3f`](https://togithub.com/nodejs/node/commit/ad37cadc3f)] - **deps**: V8: backport [`de9a5de`](https://togithub.com/nodejs/node/commit/de9a5de2274f) (Joyee Cheung) [#&#8203;49703](https://togithub.com/nodejs/node/pull/49703)
-   \[[`cdd1c66222`](https://togithub.com/nodejs/node/commit/cdd1c66222)] - **deps**: V8: cherry-pick [`b33bf2d`](https://togithub.com/nodejs/node/commit/b33bf2dfd261) (Joyee Cheung) [#&#8203;49703](https://togithub.com/nodejs/node/pull/49703)
-   \[[`61d18d6473`](https://togithub.com/nodejs/node/commit/61d18d6473)] - **deps**: update undici to 5.24.0 (Node.js GitHub Bot) [#&#8203;49559](https://togithub.com/nodejs/node/pull/49559)
-   \[[`b8a4fef393`](https://togithub.com/nodejs/node/commit/b8a4fef393)] - **deps**: remove pthread-fixes.c from uv.gyp (Ben Noordhuis) [#&#8203;49744](https://togithub.com/nodejs/node/pull/49744)
-   \[[`6c86c0683c`](https://togithub.com/nodejs/node/commit/6c86c0683c)] - **deps**: update googletest to [`d1467f5`](https://togithub.com/nodejs/node/commit/d1467f5) (Node.js GitHub Bot) [#&#8203;49676](https://togithub.com/nodejs/node/pull/49676)
-   \[[`1424404742`](https://togithub.com/nodejs/node/commit/1424404742)] - **deps**: update nghttp2 to 1.56.0 (Node.js GitHub Bot) [#&#8203;49582](https://togithub.com/nodejs/node/pull/49582)
-   \[[`15b54ff95d`](https://togithub.com/nodejs/node/commit/15b54ff95d)] - **deps**: update googletest to [`8a6feab`](https://togithub.com/nodejs/node/commit/8a6feab) (Node.js GitHub Bot) [#&#8203;49463](https://togithub.com/nodejs/node/pull/49463)
-   \[[`2ceab877c2`](https://togithub.com/nodejs/node/commit/2ceab877c2)] - **deps**: update corepack to 0.20.0 (Node.js GitHub Bot) [#&#8203;49464](https://togithub.com/nodejs/node/pull/49464)
-   \[[`4814872ddc`](https://togithub.com/nodejs/node/commit/4814872ddc)] - **doc**: fix `DEP0176` number (LiviaMedeiros) [#&#8203;49858](https://togithub.com/nodejs/node/pull/49858)
-   \[[`0e686d096b`](https://togithub.com/nodejs/node/commit/0e686d096b)] - **doc**: deprecate `fs.F_OK`, `fs.R_OK`, `fs.W_OK`, `fs.X_OK` (Livia Medeiros) [#&#8203;49683](https://togithub.com/nodejs/node/pull/49683)
-   \[[`5877c403a2`](https://togithub.com/nodejs/node/commit/5877c403a2)] - **doc**: add mertcanaltin as a triager (mert.altin) [#&#8203;49826](https://togithub.com/nodejs/node/pull/49826)
-   \[[`864fe56432`](https://togithub.com/nodejs/node/commit/864fe56432)] - **doc**: add `git node backport` way to the backporting guide (Raz Luvaton) [#&#8203;49760](https://togithub.com/nodejs/node/pull/49760)
-   \[[`e0f93492d5`](https://togithub.com/nodejs/node/commit/e0f93492d5)] - **doc**: improve documentation about ICU data fallback (Joyee Cheung) [#&#8203;49666](https://togithub.com/nodejs/node/pull/49666)
-   \[[`a5dd057540`](https://togithub.com/nodejs/node/commit/a5dd057540)] - **doc**: deprecate `util.toUSVString` (Yagiz Nizipli) [#&#8203;49725](https://togithub.com/nodejs/node/pull/49725)
-   \[[`774c1cfd52`](https://togithub.com/nodejs/node/commit/774c1cfd52)] - **doc**: add missing function call to example for `util.promisify` (Jungku Lee) [#&#8203;49719](https://togithub.com/nodejs/node/pull/49719)
-   \[[`fe78a34845`](https://togithub.com/nodejs/node/commit/fe78a34845)] - **doc**: update output of example in `mimeParams.set()` (Deokjin Kim) [#&#8203;49718](https://togithub.com/nodejs/node/pull/49718)
-   \[[`4175ea33bd`](https://togithub.com/nodejs/node/commit/4175ea33bd)] - **doc**: add missed `inspect` with numericSeparator to example (Deokjin Kim) [#&#8203;49717](https://togithub.com/nodejs/node/pull/49717)
-   \[[`3a88571972`](https://togithub.com/nodejs/node/commit/3a88571972)] - **doc**: fix history comments (Antoine du Hamel) [#&#8203;49701](https://togithub.com/nodejs/node/pull/49701)
-   \[[`db4ab1ccbb`](https://togithub.com/nodejs/node/commit/db4ab1ccbb)] - **doc**: add missing history info for `import.meta.resolve` (Antoine du Hamel) [#&#8203;49700](https://togithub.com/nodejs/node/pull/49700)
-   \[[`a304d1ee19`](https://togithub.com/nodejs/node/commit/a304d1ee19)] - **doc**: link maintaining deps to pull-request.md (Marco Ippolito) [#&#8203;49716](https://togithub.com/nodejs/node/pull/49716)
-   \[[`35294486ad`](https://togithub.com/nodejs/node/commit/35294486ad)] - **doc**: fix print results in `events` (Jungku Lee) [#&#8203;49548](https://togithub.com/nodejs/node/pull/49548)
-   \[[`9f0b0e15c9`](https://togithub.com/nodejs/node/commit/9f0b0e15c9)] - **doc**: alphabetize cli.md sections (Geoffrey Booth) [#&#8203;49668](https://togithub.com/nodejs/node/pull/49668)
-   \[[`7b6a73172f`](https://togithub.com/nodejs/node/commit/7b6a73172f)] - **doc**: deprecate calling `promisify` on a function that returns a promise (Antoine du Hamel) [#&#8203;49647](https://togithub.com/nodejs/node/pull/49647)
-   \[[`d316b32fff`](https://togithub.com/nodejs/node/commit/d316b32fff)] - **doc**: update `corepack.md` to account for 0.20.0 changes (Antoine du Hamel) [#&#8203;49486](https://togithub.com/nodejs/node/pull/49486)
-   \[[`c2eac7dc7c`](https://togithub.com/nodejs/node/commit/c2eac7dc7c)] - **doc**: remove `@anonrig` from performance initiative (Yagiz Nizipli) [#&#8203;49641](https://togithub.com/nodejs/node/pull/49641)
-   \[[`3d839fbf87`](https://togithub.com/nodejs/node/commit/3d839fbf87)] - **doc**: mark Node.js 16 as End-of-Life (Richard Lau) [#&#8203;49651](https://togithub.com/nodejs/node/pull/49651)
-   \[[`53fb5aead8`](https://togithub.com/nodejs/node/commit/53fb5aead8)] - **doc**: save user preference for JS flavor (Vidar Eldøy) [#&#8203;49526](https://togithub.com/nodejs/node/pull/49526)
-   \[[`e3594d5658`](https://togithub.com/nodejs/node/commit/e3594d5658)] - **doc**: update documentation for node:process warning (Shubham Pandey) [#&#8203;49517](https://togithub.com/nodejs/node/pull/49517)
-   \[[`8e033c3963`](https://togithub.com/nodejs/node/commit/8e033c3963)] - **doc**: rename possibly confusing variable and CSS class (Antoine du Hamel) [#&#8203;49536](https://togithub.com/nodejs/node/pull/49536)
-   \[[`d0e0eb4bb3`](https://togithub.com/nodejs/node/commit/d0e0eb4bb3)] - **doc**: update outdated history info (Antoine du Hamel) [#&#8203;49530](https://togithub.com/nodejs/node/pull/49530)
-   \[[`b4724e2e3a`](https://togithub.com/nodejs/node/commit/b4724e2e3a)] - **doc**: close a parenthesis (Sébastien Règne) [#&#8203;49525](https://togithub.com/nodejs/node/pull/49525)
-   \[[`0471c5798e`](https://togithub.com/nodejs/node/commit/0471c5798e)] - **doc**: cast GetInternalField() return type to v8::Value in addons.md (Joyee Cheung) [#&#8203;49439](https://togithub.com/nodejs/node/pull/49439)
-   \[[`9f8bea3dda`](https://togithub.com/nodejs/node/commit/9f8bea3dda)] - **doc**: fix documentation for input option in child_process (Ariel Weiss) [#&#8203;49481](https://togithub.com/nodejs/node/pull/49481)
-   \[[`f3fea92f8a`](https://togithub.com/nodejs/node/commit/f3fea92f8a)] - **doc**: fix missing imports in `test.run` code examples (Oshri Asulin) [#&#8203;49489](https://togithub.com/nodejs/node/pull/49489)
-   \[[`e426b77b67`](https://togithub.com/nodejs/node/commit/e426b77b67)] - **doc**: fix documentation for fs.createWriteStream highWaterMark option (Mert Can Altın) [#&#8203;49456](https://togithub.com/nodejs/node/pull/49456)
-   \[[`2b119108ff`](https://togithub.com/nodejs/node/commit/2b119108ff)] - **doc**: updated releasers instructions for node.js website (Claudio W) [#&#8203;49427](https://togithub.com/nodejs/node/pull/49427)
-   \[[`b9d4a80183`](https://togithub.com/nodejs/node/commit/b9d4a80183)] - **doc**: edit `import.meta.resolve` documentation (Antoine du Hamel) [#&#8203;49247](https://togithub.com/nodejs/node/pull/49247)
-   \[[`f67433f666`](https://togithub.com/nodejs/node/commit/f67433f666)] - **doc,tools**: switch to `@node-core/utils` (Michaël Zasso) [#&#8203;49851](https://togithub.com/nodejs/node/pull/49851)
-   \[[`142e256fc5`](https://togithub.com/nodejs/node/commit/142e256fc5)] - **errors**: improve classRegExp in errors.js (Uzlopak) [#&#8203;49643](https://togithub.com/nodejs/node/pull/49643)
-   \[[`6377f1bce2`](https://togithub.com/nodejs/node/commit/6377f1bce2)] - **errors**: use `determineSpecificType` in more error messages (Antoine du Hamel) [#&#8203;49580](https://togithub.com/nodejs/node/pull/49580)
-   \[[`05f0fcb4c4`](https://togithub.com/nodejs/node/commit/05f0fcb4c4)] - **esm**: identify parent importing a url with invalid host (Jacob Smith) [#&#8203;49736](https://togithub.com/nodejs/node/pull/49736)
-   \[[`8a6f5fb8f3`](https://togithub.com/nodejs/node/commit/8a6f5fb8f3)] - **esm**: fix return type of `import.meta.resolve` (Antoine du Hamel) [#&#8203;49698](https://togithub.com/nodejs/node/pull/49698)
-   \[[`a6140f1b8c`](https://togithub.com/nodejs/node/commit/a6140f1b8c)] - **esm**: update loaders warning (Geoffrey Booth) [#&#8203;49633](https://togithub.com/nodejs/node/pull/49633)
-   \[[`521a9327e0`](https://togithub.com/nodejs/node/commit/521a9327e0)] - **esm**: fix support for `URL` instances in `register` (Antoine du Hamel) [#&#8203;49655](https://togithub.com/nodejs/node/pull/49655)
-   \[[`3a9ea0925a`](https://togithub.com/nodejs/node/commit/3a9ea0925a)] - **esm**: clarify ERR_REQUIRE_ESM errors (Daniel Compton) [#&#8203;49521](https://togithub.com/nodejs/node/pull/49521)
-   \[[`1beefd5f16`](https://togithub.com/nodejs/node/commit/1beefd5f16)] - **esm**: set all hooks as release candidate (Geoffrey Booth) [#&#8203;49597](https://togithub.com/nodejs/node/pull/49597)
-   \[[`be48267888`](https://togithub.com/nodejs/node/commit/be48267888)] - **esm**: remove return value for `Module.register` (Antoine du Hamel) [#&#8203;49529](https://togithub.com/nodejs/node/pull/49529)
-   \[[`e74a075124`](https://togithub.com/nodejs/node/commit/e74a075124)] - **esm**: refactor test-esm-loader-resolve-type (Geoffrey Booth) [#&#8203;49493](https://togithub.com/nodejs/node/pull/49493)
-   \[[`17823b3533`](https://togithub.com/nodejs/node/commit/17823b3533)] - **esm**: refactor test-esm-named-exports (Geoffrey Booth) [#&#8203;49493](https://togithub.com/nodejs/node/pull/49493)
-   \[[`f34bd15ac1`](https://togithub.com/nodejs/node/commit/f34bd15ac1)] - **esm**: refactor mocking test (Geoffrey Booth) [#&#8203;49465](https://togithub.com/nodejs/node/pull/49465)
-   \[[`ec323bbd99`](https://togithub.com/nodejs/node/commit/ec323bbd99)] - **fs**: replace `SetMethodNoSideEffect` in node_file (CanadaHonk) [#&#8203;49857](https://togithub.com/nodejs/node/pull/49857)
-   \[[`6acf800123`](https://togithub.com/nodejs/node/commit/6acf800123)] - **fs**: improve error performance for `unlinkSync` (CanadaHonk) [#&#8203;49856](https://togithub.com/nodejs/node/pull/49856)
-   \[[`31702c9403`](https://togithub.com/nodejs/node/commit/31702c9403)] - **fs**: improve `readFileSync` with file descriptors (Yagiz Nizipli) [#&#8203;49691](https://togithub.com/nodejs/node/pull/49691)
-   \[[`835f9fe7b9`](https://togithub.com/nodejs/node/commit/835f9fe7b9)] - **fs**: fix file descriptor validator (Yagiz Nizipli) [#&#8203;49752](https://togithub.com/nodejs/node/pull/49752)
-   \[[`b618fe262f`](https://togithub.com/nodejs/node/commit/b618fe262f)] - **fs**: improve error performance of `opendirSync` (Yagiz Nizipli) [#&#8203;49705](https://togithub.com/nodejs/node/pull/49705)
-   \[[`938471ef55`](https://togithub.com/nodejs/node/commit/938471ef55)] - **fs**: improve error performance of sync methods (Yagiz Nizipli) [#&#8203;49593](https://togithub.com/nodejs/node/pull/49593)
-   \[[`db3fc6d087`](https://togithub.com/nodejs/node/commit/db3fc6d087)] - **fs**: fix readdir and opendir recursive with unknown file types (William Marlow) [#&#8203;49603](https://togithub.com/nodejs/node/pull/49603)
-   \[[`0f020ed22d`](https://togithub.com/nodejs/node/commit/0f020ed22d)] - **gyp**: put cctest filenames in variables (Cheng Zhao) [#&#8203;49178](https://togithub.com/nodejs/node/pull/49178)
-   \[[`0ce1e94d12`](https://togithub.com/nodejs/node/commit/0ce1e94d12)] - **lib**: update encoding sets in `WHATWG API` (Jungku Lee) [#&#8203;49610](https://togithub.com/nodejs/node/pull/49610)
-   \[[`efd6815a7a`](https://togithub.com/nodejs/node/commit/efd6815a7a)] - **lib**: fix `internalBinding` typings (Yagiz Nizipli) [#&#8203;49742](https://togithub.com/nodejs/node/pull/49742)
-   \[[`1287d5b74e`](https://togithub.com/nodejs/node/commit/1287d5b74e)] - **lib**: allow byob reader for 'blob.stream()' (Debadree Chatterjee) [#&#8203;49713](https://togithub.com/nodejs/node/pull/49713)
-   \[[`bbc710522d`](https://togithub.com/nodejs/node/commit/bbc710522d)] - **lib**: reset the cwd cache before execution (Maël Nison) [#&#8203;49684](https://togithub.com/nodejs/node/pull/49684)
-   \[[`f62d649e4d`](https://togithub.com/nodejs/node/commit/f62d649e4d)] - **lib**: use internal `fileURLToPath` (Deokjin Kim) [#&#8203;49558](https://togithub.com/nodejs/node/pull/49558)
-   \[[`e515046941`](https://togithub.com/nodejs/node/commit/e515046941)] - **lib**: use internal `pathToFileURL` (Livia Medeiros) [#&#8203;49553](https://togithub.com/nodejs/node/pull/49553)
-   \[[`00608e8070`](https://togithub.com/nodejs/node/commit/00608e8070)] - **lib**: check SharedArrayBuffer availability in freeze_intrinsics.js (Milan Burda) [#&#8203;49482](https://togithub.com/nodejs/node/pull/49482)
-   \[[`8bfbe7079c`](https://togithub.com/nodejs/node/commit/8bfbe7079c)] - **meta**: fix linter error (Antoine du Hamel) [#&#8203;49755](https://togithub.com/nodejs/node/pull/49755)
-   \[[`58f7a9e096`](https://togithub.com/nodejs/node/commit/58f7a9e096)] - **meta**: add primordials strategic initiative (Benjamin Gruenbaum) [#&#8203;49706](https://togithub.com/nodejs/node/pull/49706)
-   \[[`5366027756`](https://togithub.com/nodejs/node/commit/5366027756)] - **meta**: bump github/codeql-action from 2.21.2 to 2.21.5 (dependabot\[bot]) [#&#8203;49438](https://togithub.com/nodejs/node/pull/49438)
-   \[[`fe26b74082`](https://togithub.com/nodejs/node/commit/fe26b74082)] - **meta**: bump rtCamp/action-slack-notify from 2.2.0 to 2.2.1 (dependabot\[bot]) [#&#8203;49437](https://togithub.com/nodejs/node/pull/49437)
-   \[[`b0ce78a75b`](https://togithub.com/nodejs/node/commit/b0ce78a75b)] - **module**: fix the leak in SourceTextModule and ContextifySript (Joyee Cheung) [#&#8203;48510](https://togithub.com/nodejs/node/pull/48510)
-   \[[`4e578f8ab1`](https://togithub.com/nodejs/node/commit/4e578f8ab1)] - **module**: fix leak of vm.SyntheticModule (Joyee Cheung) [#&#8203;48510](https://togithub.com/nodejs/node/pull/48510)
-   \[[`69e4218772`](https://togithub.com/nodejs/node/commit/69e4218772)] - **module**: use symbol in WeakMap to manage host defined options (Joyee Cheung) [#&#8203;48510](https://togithub.com/nodejs/node/pull/48510)
-   \[[`96874e8fbc`](https://togithub.com/nodejs/node/commit/96874e8fbc)] - **node-api**: enable uncaught exceptions policy by default (Chengzhong Wu) [#&#8203;49313](https://togithub.com/nodejs/node/pull/49313)
-   \[[`b931aeadfd`](https://togithub.com/nodejs/node/commit/b931aeadfd)] - **perf_hooks**: reduce overhead of new performance_entries (Vinicius Lourenço) [#&#8203;49803](https://togithub.com/nodejs/node/pull/49803)
-   \[[`ad043bac31`](https://togithub.com/nodejs/node/commit/ad043bac31)] - **process**: add custom dir support for heapsnapshot-signal (Jithil P Ponnan) [#&#8203;47854](https://togithub.com/nodejs/node/pull/47854)
-   \[[`8a7c10194c`](https://togithub.com/nodejs/node/commit/8a7c10194c)] - **repl**: don't accumulate excess indentation in .load (Daniel X Moore) [#&#8203;49461](https://togithub.com/nodejs/node/pull/49461)
-   \[[`10a2adeed5`](https://togithub.com/nodejs/node/commit/10a2adeed5)] - **src**: improve error message when ICU data cannot be initialized (Joyee Cheung) [#&#8203;49666](https://togithub.com/nodejs/node/pull/49666)
-   \[[`ce37688bac`](https://togithub.com/nodejs/node/commit/ce37688bac)] - **src**: remove unnecessary todo (Rafael Gonzaga) [#&#8203;49227](https://togithub.com/nodejs/node/pull/49227)
-   \[[`f611583b71`](https://togithub.com/nodejs/node/commit/f611583b71)] - **src**: use SNAPSHOT_SERDES to log snapshot ser/deserialization (Joyee Cheung) [#&#8203;49637](https://togithub.com/nodejs/node/pull/49637)
-   \[[`a597cb8457`](https://togithub.com/nodejs/node/commit/a597cb8457)] - **src**: port Pipe to uv_pipe_bind2, uv_pipe_connect2 (Geoff Goodman) [#&#8203;49667](https://togithub.com/nodejs/node/pull/49667)
-   \[[`fb21062338`](https://togithub.com/nodejs/node/commit/fb21062338)] - **src**: set --rehash-snapshot explicitly (Joyee Cheung) [#&#8203;49556](https://togithub.com/nodejs/node/pull/49556)
-   \[[`14ece0aa76`](https://togithub.com/nodejs/node/commit/14ece0aa76)] - **(SEMVER-MINOR)** **src**: allow embedders to override NODE_MODULE_VERSION (Cheng Zhao) [#&#8203;49279](https://togithub.com/nodejs/node/pull/49279)
-   \[[`4b5e23c71b`](https://togithub.com/nodejs/node/commit/4b5e23c71b)] - **src**: set ModuleWrap internal fields only once (Joyee Cheung) [#&#8203;49391](https://togithub.com/nodejs/node/pull/49391)
-   \[[`2d3f5c7cab`](https://togithub.com/nodejs/node/commit/2d3f5c7cab)] - **src**: fix fs_type_to_name default value (Mustafa Ateş Uzun) [#&#8203;49239](https://togithub.com/nodejs/node/pull/49239)
-   \[[`cfbcb1059c`](https://togithub.com/nodejs/node/commit/cfbcb1059c)] - **src**: fix comment on StreamResource (rogertyang) [#&#8203;49193](https://togithub.com/nodejs/node/pull/49193)
-   \[[`39fb83ad16`](https://togithub.com/nodejs/node/commit/39fb83ad16)] - **src**: do not rely on the internal field being default to undefined (Joyee Cheung) [#&#8203;49413](https://togithub.com/nodejs/node/pull/49413)
-   \[[`9fd67fbff0`](https://togithub.com/nodejs/node/commit/9fd67fbff0)] - **stream**: use bitmap in writable state (Raz Luvaton) [#&#8203;49834](https://togithub.com/nodejs/node/pull/49834)
-   \[[`0ccd4638ac`](https://togithub.com/nodejs/node/commit/0ccd4638ac)] - **stream**: use bitmap in readable state (Benjamin Gruenbaum) [#&#8203;49745](https://togithub.com/nodejs/node/pull/49745)
-   \[[`b29d927010`](https://togithub.com/nodejs/node/commit/b29d927010)] - **stream**: improve readable webstream `pipeTo` (Raz Luvaton) [#&#8203;49690](https://togithub.com/nodejs/node/pull/49690)
-   \[[`7c5e322346`](https://togithub.com/nodejs/node/commit/7c5e322346)] - **stream**: improve webstream readable async iterator performance (Raz Luvaton) [#&#8203;49662](https://togithub.com/nodejs/node/pull/49662)
-   \[[`be211ef818`](https://togithub.com/nodejs/node/commit/be211ef818)] - **test**: deflake test-vm-contextified-script-leak (Joyee Cheung) [#&#8203;49710](https://togithub.com/nodejs/node/pull/49710)
-   \[[`355f10dab2`](https://togithub.com/nodejs/node/commit/355f10dab2)] - **test**: use checkIfCollectable in vm leak tests (Joyee Cheung) [#&#8203;49671](https://togithub.com/nodejs/node/pull/49671)
-   \[[`17cfc531aa`](https://togithub.com/nodejs/node/commit/17cfc531aa)] - **test**: add checkIfCollectable to test/common/gc.js (Joyee Cheung) [#&#8203;49671](https://togithub.com/nodejs/node/pull/49671)
-   \[[`e49a573752`](https://togithub.com/nodejs/node/commit/e49a573752)] - **test**: add os setPriority, getPriority test coverage (Wael) [#&#8203;38771](https://togithub.com/nodejs/node/pull/38771)
-   \[[`5f02711522`](https://togithub.com/nodejs/node/commit/5f02711522)] - **test**: deflake test-runner-output (Moshe Atlow) [#&#8203;49878](https://togithub.com/nodejs/node/pull/49878)
-   \[[`cd9754d6a7`](https://togithub.com/nodejs/node/commit/cd9754d6a7)] - **test**: mark test-runner-output as flaky (Joyee Cheung) [#&#8203;49854](https://togithub.com/nodejs/node/pull/49854)
-   \[[`5ad00424dd`](https://togithub.com/nodejs/node/commit/5ad00424dd)] - **test**: use mustSucceed instead of mustCall (SiddharthDevulapalli) [#&#8203;49788](https://togithub.com/nodejs/node/pull/49788)
-   \[[`3db9b40081`](https://togithub.com/nodejs/node/commit/3db9b40081)] - **test**: refactor test-readline-async-iterators into a benchmark (Shubham Pandey) [#&#8203;49237](https://togithub.com/nodejs/node/pull/49237)
-   \[[`2cc5ad7859`](https://togithub.com/nodejs/node/commit/2cc5ad7859)] - ***Revert*** "**test**: mark test-http-regr-[gh-2928](https://togithub.com/nodejs/node/issues/2928) as flaky" (Luigi Pinca) [#&#8203;49708](https://togithub.com/nodejs/node/pull/49708)
-   \[[`e5185b053c`](https://togithub.com/nodejs/node/commit/e5185b053c)] - **test**: use `fs.constants` for `fs.access` constants (Livia Medeiros) [#&#8203;49685](https://togithub.com/nodejs/node/pull/49685)
-   \[[`b9e5b43462`](https://togithub.com/nodejs/node/commit/b9e5b43462)] - **test**: deflake test-http-regr-[gh-2928](https://togithub.com/nodejs/node/issues/2928) (Luigi Pinca) [#&#8203;49574](https://togithub.com/nodejs/node/pull/49574)
-   \[[`1fffda504e`](https://togithub.com/nodejs/node/commit/1fffda504e)] - **test**: fix argument computation in embedtest (Joyee Cheung) [#&#8203;49506](https://togithub.com/nodejs/node/pull/49506)
-   \[[`6e56f2db52`](https://togithub.com/nodejs/node/commit/6e56f2db52)] - **test**: skip test-child-process-stdio-reuse-readable-stdio on Windows (Joyee Cheung) [#&#8203;49621](https://togithub.com/nodejs/node/pull/49621)
-   \[[`ab3afb330d`](https://togithub.com/nodejs/node/commit/ab3afb330d)] - **test**: mark test-runner-watch-mode as flaky (Joyee Cheung) [#&#8203;49627](https://togithub.com/nodejs/node/pull/49627)
-   \[[`185d9b50db`](https://togithub.com/nodejs/node/commit/185d9b50db)] - **test**: deflake test-tls-socket-close (Luigi Pinca) [#&#8203;49575](https://togithub.com/nodejs/node/pull/49575)
-   \[[`c70c74a9e6`](https://togithub.com/nodejs/node/commit/c70c74a9e6)] - **test**: show more info on failure in test-cli-syntax-require.js (Joyee Cheung) [#&#8203;49561](https://togithub.com/nodejs/node/pull/49561)
-   \[[`ed7c6d1114`](https://togithub.com/nodejs/node/commit/ed7c6d1114)] - **test**: mark test-http-regr-[gh-2928](https://togithub.com/nodejs/node/issues/2928) as flaky (Joyee Cheung) [#&#8203;49565](https://togithub.com/nodejs/node/pull/49565)
-   \[[`3599eebab9`](https://togithub.com/nodejs/node/commit/3599eebab9)] - **test**: use spawnSyncAndExitWithoutError in sea tests (Joyee Cheung) [#&#8203;49543](https://togithub.com/nodejs/node/pull/49543)
-   \[[`f79b153e89`](https://togithub.com/nodejs/node/commit/f79b153e89)] - **test**: use spawnSyncAndExitWithoutError in test/common/sea.js (Joyee Cheung) [#&#8203;49543](https://togithub.com/nodejs/node/pull/49543)
-   \[[`c079c73769`](https://togithub.com/nodejs/node/commit/c079c73769)] - **test**: use setImmediate() in test-heapdump-shadowrealm.js (Joyee Cheung) [#&#8203;49573](https://togithub.com/nodejs/node/pull/49573)
-   \[[`667a92493c`](https://togithub.com/nodejs/node/commit/667a92493c)] - **test**: skip test-child-process-pipe-dataflow.js on Windows (Joyee Cheung) [#&#8203;49563](https://togithub.com/nodejs/node/pull/49563)
-   \[[`91af0a9a3c`](https://togithub.com/nodejs/node/commit/91af0a9a3c)] - ***Revert*** "**test**: ignore the copied entry_point.c" (Chengzhong Wu) [#&#8203;49515](https://togithub.com/nodejs/node/pull/49515)
-   \[[`567afc71b8`](https://togithub.com/nodejs/node/commit/567afc71b8)] - **test**: avoid copying test source files (Chengzhong Wu) [#&#8203;49515](https://togithub.com/nodejs/node/pull/49515)
-   \[[`ced25a976d`](https://togithub.com/nodejs/node/commit/ced25a976d)] - **test**: increase coverage of `Module.register` and `initialize` hook (Antoine du Hamel) [#&#8203;49532](https://togithub.com/nodejs/node/pull/49532)
-   \[[`be02fbdb8a`](https://togithub.com/nodejs/node/commit/be02fbdb8a)] - **test**: isolate `globalPreload` tests (Geoffrey Booth) [#&#8203;49545](https://togithub.com/nodejs/node/pull/49545)
-   \[[`f214428845`](https://togithub.com/nodejs/node/commit/f214428845)] - **test**: split test-crypto-dh to avoid timeout on slow machines in the CI (Joyee Cheung) [#&#8203;49492](https://togithub.com/nodejs/node/pull/49492)
-   \[[`3987094569`](https://togithub.com/nodejs/node/commit/3987094569)] - **test**: make `test-dotenv-node-options` locale-independent (Livia Medeiros) [#&#8203;49470](https://togithub.com/nodejs/node/pull/49470)
-   \[[`34c1741792`](https://togithub.com/nodejs/node/commit/34c1741792)] - **test**: add test for urlstrings usage in `node:fs` (Livia Medeiros) [#&#8203;49471](https://togithub.com/nodejs/node/pull/49471)
-   \[[`c3c6c4f007`](https://togithub.com/nodejs/node/commit/c3c6c4f007)] - **test**: make test-worker-prof more robust (Joyee Cheung) [#&#8203;49274](https://togithub.com/nodejs/node/pull/49274)
-   \[[`843df1a4da`](https://togithub.com/nodejs/node/commit/843df1a4da)] - **test,crypto**: update WebCryptoAPI WPT (Filip Skokan) [#&#8203;49714](https://togithub.com/nodejs/node/pull/49714)
-   \[[`80b342cc38`](https://togithub.com/nodejs/node/commit/80b342cc38)] - **(SEMVER-MINOR)** **test_runner**: accept `testOnly` in `run` (Moshe Atlow) [#&#8203;49753](https://togithub.com/nodejs/node/pull/49753)
-   \[[`76865515b9`](https://togithub.com/nodejs/node/commit/76865515b9)] - **test_runner**: fix test runner watch mode when no positional arguments (Moshe Atlow) [#&#8203;49578](https://togithub.com/nodejs/node/pull/49578)
-   \[[`17a05b141d`](https://togithub.com/nodejs/node/commit/17a05b141d)] - **(SEMVER-MINOR)** **test_runner**: add junit reporter (Moshe Atlow) [#&#8203;49614](https://togithub.com/nodejs/node/pull/49614)
-   \[[`5672e38457`](https://togithub.com/nodejs/node/commit/5672e38457)] - **test_runner**: add jsdocs to mock.js (Caio Borghi) [#&#8203;49555](https://togithub.com/nodejs/node/pull/49555)
-   \[[`b4d42a8f2b`](https://togithub.com/nodejs/node/commit/b4d42a8f2b)] - **test_runner**: fix invalid timer call (Erick Wendel) [#&#8203;49477](https://togithub.com/nodejs/node/pull/49477)
-   \[[`f755e6786b`](https://togithub.com/nodejs/node/commit/f755e6786b)] - **test_runner**: add jsdocs to MockTimers (Erick Wendel) [#&#8203;49476](https://togithub.com/nodejs/node/pull/49476)
-   \[[`e7285d4bf0`](https://togithub.com/nodejs/node/commit/e7285d4bf0)] - **test_runner**: fix typescript coverage (Moshe Atlow) [#&#8203;49406](https://togithub.com/nodejs/node/pull/49406)
-   \[[`07a2e29bf3`](https://togithub.com/nodejs/node/commit/07a2e29bf3)] - **tools**: support updating [@&#8203;reporters/github](https://togithub.com/reporters/github) manually (Moshe Atlow) [#&#8203;49871](https://togithub.com/nodejs/node/pull/49871)
-   \[[`5ac6722031`](https://togithub.com/nodejs/node/commit/5ac6722031)] - **tools**: skip ruff on tools/node_modules (Moshe Atlow) [#&#8203;49838](https://togithub.com/nodejs/node/pull/49838)
-   \[[`462228bd24`](https://togithub.com/nodejs/node/commit/462228bd24)] - **tools**: fix uvwasi updater (Michael Dawson) [#&#8203;49682](https://togithub.com/nodejs/node/pull/49682)
-   \[[`ff81bfb958`](https://togithub.com/nodejs/node/commit/ff81bfb958)] - **tools**: update lint-md-dependencies to rollup@3.29.2 (Node.js GitHub Bot) [#&#8203;49679](https://togithub.com/nodejs/node/pull/49679)
-   \[[`08ffc6344c`](https://togithub.com/nodejs/node/commit/08ffc6344c)] - **tools**: restrict internal code from using public `url` module (LiviaMedeiros) [#&#8203;49590](https://togithub.com/nodejs/node/pull/49590)
-   \[[`728ebf6c97`](https://togithub.com/nodejs/node/commit/728ebf6c97)] - **tools**: update eslint to 8.49.0 (Node.js GitHub Bot) [#&#8203;49586](https://togithub.com/nodejs/node/pull/49586)
-   \[[`20d038ffb1`](https://togithub.com/nodejs/node/commit/20d038ffb1)] - **tools**: update lint-md-dependencies to rollup@3.29.0 unified@11.0.3 (Node.js GitHub Bot) [#&#8203;49584](https://togithub.com/nodejs/node/pull/49584)
-   \[[`210c15bd12`](https://togithub.com/nodejs/node/commit/210c15bd12)] - **tools**: allow passing absolute path of config.gypi in js2c (Cheng Zhao) [#&#8203;49162](https://togithub.com/nodejs/node/pull/49162)
-   \[[`e341efe173`](https://togithub.com/nodejs/node/commit/e341efe173)] - **tools**: configure never-stale label correctly (Michaël Zasso) [#&#8203;49498](https://togithub.com/nodejs/node/pull/49498)
-   \[[`a8a8a498ce`](https://togithub.com/nodejs/node/commit/a8a8a498ce)] - **tools**: update doc dependencies (Node.js GitHub Bot) [#&#8203;49467](https://togithub.com/nodejs/node/pull/49467)
-   \[[`ac06607f9e`](https://togithub.com/nodejs/node/commit/ac06607f9e)] - **typings**: fix missing property in `ExportedHooks` (Antoine du Hamel) [#&#8203;49567](https://togithub.com/nodejs/node/pull/49567)
-   \[[`097b59807a`](https://togithub.com/nodejs/node/commit/097b59807a)] - **url**: improve invalid url performance (Yagiz Nizipli) [#&#8203;49692](https://togithub.com/nodejs/node/pull/49692)
-   \[[`7c2060cfac`](https://togithub.com/nodejs/node/commit/7c2060cfac)] - **util**: add `getCwdSafe` internal util fn (João Lenon) [#&#8203;48434](https://togithub.com/nodejs/node/pull/48434)
-   \[[`c23c60f545`](https://togithub.com/nodejs/node/commit/c23c60f545)] - **zlib**: disable CRC32 SIMD optimization (Luigi Pinca) [#&#8203;49511](https://togithub.com/nodejs/node/pull/49511)

### [`v20.7.0`](https://togithub.com/nodejs/node/releases/tag/v20.7.0): 2023-09-18, Version 20.7.0 (Current), @&#8203;UlisesGascon

[Compare Source](https://togithub.com/nodejs/node/compare/v20.6.1...v20.7.0)

##### Notable Changes

-   \[[`022f1b70c1`](https://togithub.com/nodejs/node/commit/022f1b70c1)] - **src**: support multiple `--env-file` declarations (Yagiz Nizipli) [#&#8203;49542](https://togithub.com/nodejs/node/pull/49542)
-   \[[`4a1d1cad61`](https://togithub.com/nodejs/node/commit/4a1d1cad61)] - **crypto**: update root certificates to NSS 3.93 (Node.js GitHub Bot) [#&#8203;49341](https://togithub.com/nodejs/node/pull/49341)
-   \[[`a1a65f593c`](https://togithub.com/nodejs/node/commit/a1a65f593c)] - **deps**: upgrade npm to 10.1.0 (npm team) [#&#8203;49570](https://togithub.com/nodejs/node/pull/49570)
-   \[[`6c2480cad9`](https://togithub.com/nodejs/node/commit/6c2480cad9)] - **(SEMVER-MINOR)** **deps**: upgrade npm to 10.0.0 (npm team) [#&#8203;49423](https://togithub.com/nodejs/node/pull/49423)
-   \[[`bef900e56b`](https://togithub.com/nodejs/node/commit/bef900e56b)] - **doc**: move and rename loaders section (Geoffrey Booth) [#&#8203;49261](https://togithub.com/nodejs/node/pull/49261)
-   \[[`db4ce8a593`](https://togithub.com/nodejs/node/commit/db4ce8a593)] - **doc**: add release key for Ulises Gascon (Ulises Gascón) [#&#8203;49196](https://togithub.com/nodejs/node/pull/49196)
-   \[[`11c85ffa98`](https://togithub.com/nodejs/node/commit/11c85ffa98)] - **(SEMVER-MINOR)** **lib**: add api to detect whether source-maps are enabled (翠 / green) [#&#8203;46391](https://togithub.com/nodejs/node/pull/46391)
-   \[[`ec51e25ed7`](https://togithub.com/nodejs/node/commit/ec51e25ed7)] - **src,permission**: add multiple allow-fs-\* flags (Carlos Espa) [#&#8203;49047](https://togithub.com/nodejs/node/pull/49047)
-   \[[`efdc95fbc0`](https://togithub.com/nodejs/node/commit/efdc95fbc0)] - **(SEMVER-MINOR)** **test_runner**: expose location of tests (Colin Ihrig) [#&#8203;48975](https://togithub.com/nodejs/node/pull/48975)

##### Commits

-   \[[`e84515594e`](https://togithub.com/nodejs/node/commit/e84515594e)] - **benchmark**: use `tmpdir.resolve()` (Livia Medeiros) [#&#8203;49137](https://togithub.com/nodejs/node/pull/49137)
-   \[[`f37444e896`](https://togithub.com/nodejs/node/commit/f37444e896)] - **bootstrap**: build code cache from deserialized isolate (Joyee Cheung) [#&#8203;49099](https://togithub.com/nodejs/node/pull/49099)
-   \[[`af6dc1754d`](https://togithub.com/nodejs/node/commit/af6dc1754d)] - **bootstrap**: do not generate code cache in an unfinalized isolate (Joyee Cheung) [#&#8203;49108](https://togithub.com/nodejs/node/pull/49108)
-   \[[`cade5716df`](https://togithub.com/nodejs/node/commit/cade5716df)] - **build**: add symlink to `compile_commands.json` file if needed (Juan José) [#&#8203;49260](https://togithub.com/nodejs/node/pull/49260)
-   \[[`34a2590b05`](https://togithub.com/nodejs/node/commit/34a2590b05)] - **build**: expand when we run internet tests (Michael Dawson) [#&#8203;49218](https://togithub.com/nodejs/node/pull/49218)
-   \[[`f637fd46ab`](https://togithub.com/nodejs/node/commit/f637fd46ab)] - **build**: fix typo `libray` -> `library` (configure.py) (michalbiesek) [#&#8203;49106](https://togithub.com/nodejs/node/pull/49106)
-   \[[`ef3d8dd493`](https://togithub.com/nodejs/node/commit/ef3d8dd493)] - **crypto**: remove webcrypto EdDSA key checks and properties (Filip Skokan) [#&#8203;49408](https://togithub.com/nodejs/node/pull/49408)
-   \[[`4a1d1cad61`](https://togithub.com/nodejs/node/commit/4a1d1cad61)] - **crypto**: update root certificates to NSS 3.93 (Node.js GitHub Bot) [#&#8203;49341](https://togithub.com/nodejs/node/pull/49341)
-   \[[`7eb10a38ea`](https://togithub.com/nodejs/node/commit/7eb10a38ea)] - **crypto**: remove getDefaultEncoding() (Tobias Nießen) [#&#8203;49170](https://togithub.com/nodejs/node/pull/49170)
-   \[[`772496c030`](https://togithub.com/nodejs/node/commit/772496c030)] - **crypto**: remove default encoding from DiffieHellman (Tobias Nießen) [#&#8203;49169](https://togithub.com/nodejs/node/pull/49169)
-   \[[`c795083232`](https://togithub.com/nodejs/node/commit/c795083232)] - **crypto**: remove default encoding from Hash/Hmac (Tobias Nießen) [#&#8203;49167](https://togithub.com/nodejs/node/pull/49167)
-   \[[`08197aa010`](https://togithub.com/nodejs/node/commit/08197aa010)] - **crypto**: remove default encoding from sign/verify (Tobias Nießen) [#&#8203;49145](https://togithub.com/nodejs/node/pull/49145)
-   \[[`a1a65f593c`](https://togithub.com/nodejs/node/commit/a1a65f593c)] - **deps**: upgrade npm to 10.1.0 (npm team) [#&#8203;49570](https://togithub.com/nodejs/node/pull/49570)
-   \[[`6c2480cad9`](https://togithub.com/nodejs/node/commit/6c2480cad9)] - **(SEMVER-MINOR)** **deps**: upgrade npm to 10.0.0 (npm team) [#&#8203;49423](https://togithub.com/nodejs/node/pull/49423)
-   \[[`84195d9584`](https://togithub.com/nodejs/node/commit/84195d9584)] - **deps**: add missing thread-common.c in uv.gyp (Santiago Gimeno) [#&#8203;49410](https://togithub.com/nodejs/node/pull/49410)
-   \[[`5b70b68b3d`](https://togithub.com/nodejs/node/commit/5b70b68b3d)] - **deps**: V8: cherry-pick [`eadaef5`](https://togithub.com/nodejs/node/commit/eadaef581c29) (Adam Majer) [#&#8203;49401](https://togithub.com/nodejs/node/pull/49401)
-   \[[`fe34d632e8`](https://togithub.com/nodejs/node/commit/fe34d632e8)] - **deps**: update zlib to 1.2.13.1-motley-f5fd0ad (Node.js GitHub Bot) [#&#8203;49252](https://togithub.com/nodejs/node/pull/49252)
-   \[[`db4ce8a593`](https://togithub.com/nodejs/node/commit/db4ce8a593)] - **doc**: add release key for Ulises Gascon (Ulises Gascón) [#&#8203;49196](https://togithub.com/nodejs/node/pull/49196)
-   \[[`e5f3a694cf`](https://togithub.com/nodejs/node/commit/e5f3a694cf)] - **doc**: fix node-api call example (Chengzhong Wu) [#&#8203;49395](https://togithub.com/nodejs/node/pull/49395)
-   \[[`021345a724`](https://togithub.com/nodejs/node/commit/021345a724)] - **doc**: add news issue for Diagnostics WG (Michael Dawson) [#&#8203;49306](https://togithub.com/nodejs/node/pull/49306)
-   \[[`f82347266b`](https://togithub.com/nodejs/node/commit/f82347266b)] - **doc**: clarify policy expectations (Rafael Gonzaga) [#&#8203;48947](https://togithub.com/nodejs/node/pull/48947)
-   \[[`73cfd9c895`](https://togithub.com/nodejs/node/commit/73cfd9c895)] - **doc**: add print results for examples in `StringDecoder` (Jungku Lee) [#&#8203;49326](https://togithub.com/nodejs/node/pull/49326)
-   \[[`63ab591416`](https://togithub.com/nodejs/node/commit/63ab591416)] - **doc**: update outdated reference to NIST SP 800-131A (Tobias Nießen) [#&#8203;49316](https://togithub.com/nodejs/node/pull/49316)
-   \[[`935dfe2afd`](https://togithub.com/nodejs/node/commit/935dfe2afd)] - **doc**: use `cjs` as block code's type in `MockTimers` (Deokjin Kim) [#&#8203;49309](https://togithub.com/nodejs/node/pull/49309)
-   \[[`7c0cd2fb87`](https://togithub.com/nodejs/node/commit/7c0cd2fb87)] - **doc**: update `options.filter` description for `fs.cp` (Shubham Pandey) [#&#8203;49289](https://togithub.com/nodejs/node/pull/49289)
-   \[[`f72e79ea67`](https://togithub.com/nodejs/node/commit/f72e79ea67)] - **doc**: add riscv64 to list of architectures (Stewart X Addison) [#&#8203;49284](https://togithub.com/nodejs/node/pull/49284)
-   \[[`d19c710064`](https://togithub.com/nodejs/node/commit/d19c710064)] - **doc**: avoid "not currently recommended" (Tobias Nießen) [#&#8203;49300](https://togithub.com/nodejs/node/pull/49300)
-   \[[`ae656101c0`](https://togithub.com/nodejs/node/commit/ae656101c0)] - **doc**: update module hooks docs (Geoffrey Booth) [#&#8203;49265](https://togithub.com/nodejs/node/pull/49265)
-   \[[`fefbdb92f2`](https://togithub.com/nodejs/node/commit/fefbdb92f2)] - **doc**: modify param description for end(),write() in `StringDecoder` (Jungku Lee) [#&#8203;49285](https://togithub.com/nodejs/node/pull/49285)
-   \[[`59e66a1ebe`](https://togithub.com/nodejs/node/commit/59e66a1ebe)] - **doc**: use NODE_API_SUPPORTED_VERSION_MAX in release doc (Cheng Zhao) [#&#8203;49268](https://togithub.com/nodejs/node/pull/49268)
-   \[[`ac3b88449b`](https://togithub.com/nodejs/node/commit/ac3b88449b)] - **doc**: fix typo in `stream.finished` documentation (Antoine du Hamel) [#&#8203;49271](https://togithub.com/nodejs/node/pull/49271)
-   \[[`7428ebf6c3`](https://togithub.com/nodejs/node/commit/7428ebf6c3)] - **doc**: update description for `percent_encode` sets in `WHATWG API` (Jungku Lee) [#&#8203;49258](https://togithub.com/nodejs/node/pull/49258)
-   \[[`bef900e56b`](https://togithub.com/nodejs/node/commit/bef900e56b)] - **doc**: move and rename loaders section (Geoffrey Booth) [#&#8203;49261](https://togithub.com/nodejs/node/pull/49261)
-   \[[`a22e0d9696`](https://togithub.com/nodejs/node/commit/a22e0d9696)] - **doc**: clarify use of Uint8Array for n-api (Fedor Indutny) [#&#8203;48742](https://togithub.com/nodejs/node/pull/48742)
-   \[[`1704f24cb9`](https://togithub.com/nodejs/node/commit/1704f24cb9)] - **doc**: add signature for `module.register` (Geoffrey Booth) [#&#8203;49251](https://togithub.com/nodejs/node/pull/49251)
-   \[[`5a363bb01b`](https://togithub.com/nodejs/node/commit/5a363bb01b)] - **doc**: caveat unavailability of `import.meta.resolve` in custom loaders (Jacob Smith) [#&#8203;49242](https://togithub.com/nodejs/node/pull/49242)
-   \[[`8101f2b259`](https://togithub.com/nodejs/node/commit/8101f2b259)] - **doc**: use same name in the doc as in the code (Hyunjin Kim) [#&#8203;49216](https://togithub.com/nodejs/node/pull/49216)
-   \[[`edf278d60d`](https://togithub.com/nodejs/node/commit/edf278d60d)] - **doc**: add notable-change label mention to PR template (Rafael Gonzaga) [#&#8203;49188](https://togithub.com/nodejs/node/pull/49188)
-   \[[`3df2251a6a`](https://togithub.com/nodejs/node/commit/3df2251a6a)] - **doc**: add h1 summary to security release process (Rafael Gonzaga) [#&#8203;49112](https://togithub.com/nodejs/node/pull/49112)
-   \[[`9fcd99a744`](https://togithub.com/nodejs/node/commit/9fcd99a744)] - **doc**: update to semver-minor releases by default (Rafael Gonzaga) [#&#8203;49175](https://togithub.com/nodejs/node/pull/49175)
-   \[[`777931f499`](https://togithub.com/nodejs/node/commit/777931f499)] - **doc**: fix wording in napi_async_init (Tobias Nießen) [#&#8203;49180](https://togithub.com/nodejs/node/pull/49180)
-   \[[`f45c8e10c0`](https://togithub.com/nodejs/node/commit/f45c8e10c0)] - **doc,test**: add known path resolution issue in permission model (Tobias Nießen) [#&#8203;49155](https://togithub.com/nodejs/node/pull/49155)
-   \[[`a6cfea3f74`](https://togithub.com/nodejs/node/commit/a6cfea3f74)] - **esm**: align sync and async load implementations (Antoine du Hamel) [#&#8203;49152](https://togithub.com/nodejs/node/pull/49152)
-   \[[`9fac310b33`](https://togithub.com/nodejs/node/commit/9fac310b33)] - **fs**: add the options param description in openAsBlob() (Yeseul Lee) [#&#8203;49308](https://togithub.com/nodejs/node/pull/49308)
-   \[[`92772a8175`](https://togithub.com/nodejs/node/commit/92772a8175)] - **fs**: remove redundant code in readableWebStream() (Deokjin Kim) [#&#8203;49298](https://togithub.com/nodejs/node/pull/49298)
-   \[[`88ba79b083`](https://togithub.com/nodejs/node/commit/88ba79b083)] - **fs**: make sure to write entire buffer (Robert Nagy) [#&#8203;49211](https://togithub.com/nodejs/node/pull/49211)
-   \[[`11c85ffa98`](https://togithub.com/nodejs/node/commit/11c85ffa98)] - **(SEMVER-MINOR)** **lib**: add api to detect whether source-maps are enabled (翠 / green) [#&#8203;46391](https://togithub.com/nodejs/node/pull/46391)
-   \[[`c12711ebfe`](https://togithub.com/nodejs/node/commit/c12711ebfe)] - **lib**: implement WeakReference on top of JS WeakRef (Joyee Cheung) [#&#8203;49053](https://togithub.com/nodejs/node/pull/49053)
-   \[[`9a0891f88d`](https://togithub.com/nodejs/node/commit/9a0891f88d)] - **meta**: bump step-security/harden-runner from 2.5.0 to 2.5.1 (dependabot\[bot]) [#&#8203;49435](https://togithub.com/nodejs/node/pull/49435)
-   \[[`ae67f41ef1`](https://togithub.com/nodejs/node/commit/ae67f41ef1)] - **meta**: bump actions/checkout from 3.5.3 to 3.6.0 (dependabot\[bot]) [#&#8203;49436](https://togithub.com/nodejs/node/pull/49436)
-   \[[`71b4411fb2`](https://togithub.com/nodejs/node/commit/71b4411fb2)] - **meta**: bump actions/setup-node from 3.7.0 to 3.8.1 (dependabot\[bot]) [#&#8203;49434](https://togithub.com/nodejs/node/pull/49434)
-   \[[`83b7d3a395`](https://togithub.com/nodejs/node/commit/83b7d3a395)] - **meta**: remove modules team from CODEOWNERS (Benjamin Gruenbaum) [#&#8203;49412](https://togithub.com/nodejs/node/pull/49412)
-   \[[`81ff68c45c`](https://togithub.com/nodejs/node/commit/81ff68c45c)] - **meta**: move one or more collaborators to emeritus (Node.js GitHub Bot) [#&#8203;49264](https://togithub.com/nodejs/node/pull/49264)
-   \[[`ab975233cc`](https://togithub.com/nodejs/node/commit/ab975233cc)] - **meta**: mention nodejs/tsc when changing GH templates (Rafael Gonzaga) [#&#8203;49189](https://togithub.com/nodejs/node/pull/49189)
-   \[[`ceaa5494de`](https://togithub.com/nodejs/node/commit/ceaa5494de)] - **meta**: add test/reporters to codeowners (Chemi Atlow) [#&#8203;49186](https://togithub.com/nodejs/node/pull/49186)
-   \[[`de0a51b7cf`](https://togithub.com/nodejs/node/commit/de0a51b7cf)] - **net**: improve performance of isIPv4 and isIPv6 (Uzlopak) [#&#8203;49568](https://togithub.com/nodejs/node/pull/49568)
-   \[[`8d0913bf95`](https://togithub.com/nodejs/node/commit/8d0913bf95)] - **net**: use asserts in JS Socket Stream to catch races in future (Tim Perry) [#&#8203;49400](https://togithub.com/nodejs/node/pull/49400)
-   \[[`2486836a7d`](https://togithub.com/nodejs/node/commit/2486836a7d)] - **net**: fix crash due to simultaneous close/shutdown on JS Stream Sockets (Tim Perry) [#&#8203;49400](https://togithub.com/nodejs/node/pull/49400)
-   \[[`7a808340cd`](https://togithub.com/nodejs/node/commit/7a808340cd)] - **node-api**: fix compiler warning in node_api.h (Michael Graeb) [#&#8203;49103](https://togithub.com/nodejs/node/pull/49103)
-   \[[`30f26a99f4`](https://togithub.com/nodejs/node/commit/30f26a99f4)] - **permission**: ensure to resolve path when calling mkdtemp (RafaelGSS) [nodejs-private/node-private#440](https://togithub.com/nodejs-private/node-private/pull/440)
-   \[[`5051c75a5b`](https://togithub.com/nodejs/node/commit/5051c75a5b)] - **policy**: fix path to URL conversion (Antoine du Hamel) [#&#8203;49133](https://togithub.com/nodejs/node/pull/49133)
-   \[[`173aed4757`](https://togithub.com/nodejs/node/commit/173aed4757)] - **report**: fix recent coverity warning (Michael Dawson) [#&#8203;48954](https://togithub.com/nodejs/node/pull/48954)
-   \[[`d7ff78b442`](https://togithub.com/nodejs/node/commit/d7ff78b442)] - **sea**: generate code cache with deserialized isolate (Joyee Cheung) [#&#8203;49226](https://togithub.com/nodejs/node/pull/49226)
-   \[[`022f1b70c1`](https://togithub.com/nodejs/node/commit/022f1b70c1)] - **src**: support multiple `--env-file` declarations (Yagiz Nizipli) [#&#8203;49542](https://togithub.com/nodejs/node/pull/49542)
-   \[[`154b1c2115`](https://togithub.com/nodejs/node/commit/154b1c2115)] - **src**: don't overwrite environment from .env file (Phil Nash) [#&#8203;49424](https://togithub.com/nodejs/node/pull/49424)
-   \[[`dc4de1c69b`](https://togithub.com/nodejs/node/commit/dc4de1c69b)] - **src**: modify code for empty string (pluris) [#&#8203;49336](https://togithub.com/nodejs/node/pull/49336)
-   \[[`701c46f967`](https://togithub.com/nodejs/node/commit/701c46f967)] - **src**: remove unused PromiseWrap-related code (Joyee Cheung) [#&#8203;49335](https://togithub.com/nodejs/node/pull/49335)
-   \[[`4a094dc7af`](https://togithub.com/nodejs/node/commit/4a094dc7af)] - **src**: rename IsAnyByteSource to IsAnyBufferSource (Tobias Nießen) [#&#8203;49346](https://togithub.com/nodejs/node/pull/49346)
-   \[[`55d6649175`](https://togithub.com/nodejs/node/commit/55d6649175)] - **src**: support snapshot deserialization in RAIIIsolate (Joyee Cheung) [#&#8203;49226](https://togithub.com/nodejs/node/pull/49226)
-   \[[`dc092864ef`](https://togithub.com/nodejs/node/commit/dc092864ef)] - **src**: remove unused function `GetName()` in node_perf (Jungku Lee) [#&#8203;49244](https://togithub.com/nodejs/node/pull/49244)
-   \[[`f2552a410e`](https://togithub.com/nodejs/node/commit/f2552a410e)] - **src**: use ARES_SUCCESS instead of 0 (Jungku Lee) [#&#8203;49048](https://togithub.com/nodejs/node/pull/49048)
-   \[[`4a9ae31519`](https://togithub.com/nodejs/node/commit/4a9ae31519)] - **src**: add a condition if the argument of `DomainToUnicode` is empty (Jungku Lee) [#&#8203;49097](https://togithub.com/nodejs/node/pull/49097)
-   \[[`f460362cdf`](https://togithub.com/nodejs/node/commit/f460362cdf)] - **src**: remove C++ WeakReference implementation (Joyee Cheung) [#&#8203;49053](https://togithub.com/nodejs/node/pull/49053)
-   \[[`2a35383b3e`](https://togithub.com/nodejs/node/commit/2a35383b3e)] - **src**: use per-realm GetBindingData() wherever applicable (Joyee Cheung) [#&#8203;49007](https://togithub.com/nodejs/node/pull/49007)
-   \[[`184bbddcf5`](https://togithub.com/nodejs/node/commit/184bbddcf5)] - **src**: add per-realm GetBindingData() method (Joyee Cheung) [#&#8203;49007](https://togithub.com/nodejs/node/pull/49007)
-   \[[`e9946885f9`](https://togithub.com/nodejs/node/commit/e9946885f9)] - **src**: serialize both BaseObject slots (Joyee Cheung) [#&#8203;48996](https://togithub.com/nodejs/node/pull/48996)
-   \[[`ec51e25ed7`](https://togithub.com/nodejs/node/commit/ec51e25ed7)] - **src,permission**: add multiple allow-fs-\* flags (Carlos Espa) [#&#8203;49047](https://togithub.com/nodejs/node/pull/49047)
-   \[[`8aac95de4b`](https://togithub.com/nodejs/node/commit/8aac95de4b)] - **stream**: improve tee perf by reduce `ReflectConstruct` usages (Raz Luvaton) [#&#8203;49546](https://togithub.com/nodejs/node/pull/49546)
-   \[[`0eea7fd8fb`](https://togithub.com/nodejs/node/commit/0eea7fd8fb)] - **stream**: use Buffer.from when constructor is a Buffer (Matthew Aitken) [#&#8203;49250](https://togithub.com/nodejs/node/pull/49250)
-   \[[`b961d9bd52`](https://togithub.com/nodejs/node/commit/b961d9bd52)] - **stream**: add `highWaterMark` for the map operator (Raz Luvaton) [#&#8203;49249](https://togithub.com/nodejs/node/pull/49249)
-   \[[`ca1384166d`](https://togithub.com/nodejs/node/commit/ca1384166d)] - **test**: fix warning for comment in embedtest (Jungku Lee) [#&#8203;49416](https://togithub.com/nodejs/node/pull/49416)
-   \[[`2a35782809`](https://togithub.com/nodejs/node/commit/2a35782809)] - **test**: simplify test-crypto-dh-group-setters (Tobias Nießen) [#&#8203;49404](https://togithub.com/nodejs/node/pull/49404)
-   \[[`6740f3c209`](https://togithub.com/nodejs/node/commit/6740f3c209)] - **test**: verify dynamic import call with absolute path strings (Chengzhong Wu) [#&#8203;49275](https://togithub.com/nodejs/node/pull/49275)
-   \[[`6ed47bd8fb`](https://togithub.com/nodejs/node/commit/6ed47bd8fb)] - **test**: reduce length in crypto keygen tests (Joyee Cheung) [#&#8203;49221](https://togithub.com/nodejs/node/pull/49221)
-   \[[`4faa30c553`](https://togithub.com/nodejs/node/commit/4faa30c553)] - **test**: split JWK async elliptic curve keygen tests (Joyee Cheung) [#&#8203;49221](https://togithub.com/nodejs/node/pull/49221)
-   \[[`e04a2603d8`](https://togithub.com/

</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 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:eyJjcmVhdGVkSW5WZXIiOiIzNy44MS4zIiwidXBkYXRlZEluVmVyIjoiMzcuMjAwLjAiLCJ0YXJnZXRCcmFuY2giOiJjYW5hcnkifQ==-->
2024-02-23 09:03:57 +00:00
DarkSky
d9fee81b10 fix(server): handle unexpected error from storage provider (#5888)
![image](https://github.com/toeverything/AFFiNE/assets/25152247/5a914c7d-0cc2-44ad-97a9-3d39199d8131)

handle unexpected error in workspace usage calc
2024-02-23 08:53:32 +00:00
LongYinan
f003fa0968 chore: bump up all non-major dependencies (#5858)
[![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-forge/maker-base](https://togithub.com/electron/forge) | [`7.2.0` -> `7.3.0`](https://renovatebot.com/diffs/npm/@electron-forge%2fmaker-base/7.2.0/7.3.0) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@electron-forge%2fmaker-base/7.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://developer.mend.io/api/mc/badges/adoption/npm/@electron-forge%2fmaker-base/7.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://developer.mend.io/api/mc/badges/compatibility/npm/@electron-forge%2fmaker-base/7.2.0/7.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@electron-forge%2fmaker-base/7.2.0/7.3.0?slim=true)](https://docs.renovatebot.com/merge-confidence/) |
| [@napi-rs/cli](https://togithub.com/napi-rs/napi-rs) | [`3.0.0-alpha.40` -> `3.0.0-alpha.41`](https://renovatebot.com/diffs/npm/@napi-rs%2fcli/3.0.0-alpha.40/3.0.0-alpha.41) | [![age](https://developer.mend.io/api/mc/badges/age/npm/@napi-rs%2fcli/3.0.0-alpha.41?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.41?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.40/3.0.0-alpha.41?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.40/3.0.0-alpha.41?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

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

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

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

##### What's Changed

##### Features

-   feat(plugin-vite): upgrade to vite@5 by [@&#8203;caoxiemeihao](https://togithub.com/caoxiemeihao) in [https://github.com/electron/forge/pull/3468](https://togithub.com/electron/forge/pull/3468)
-   feat: allow a custom out dir from forge config by [@&#8203;lutzroeder](https://togithub.com/lutzroeder) in [https://github.com/electron/forge/pull/3458](https://togithub.com/electron/forge/pull/3458)
-   feat(template-vite): patch types by [@&#8203;caoxiemeihao](https://togithub.com/caoxiemeihao) in [https://github.com/electron/forge/pull/3494](https://togithub.com/electron/forge/pull/3494)
-   feat: adds default fuses to templates by [@&#8203;yangannyx](https://togithub.com/yangannyx) in [https://github.com/electron/forge/pull/3480](https://togithub.com/electron/forge/pull/3480)
-   feat(publisher-github): option to automatically generate release notes by [@&#8203;dsanders11](https://togithub.com/dsanders11) in [https://github.com/electron/forge/pull/3484](https://togithub.com/electron/forge/pull/3484)

##### Fixes

-   fix(electron-release-publisher): change api/version endpoint in PublisherERS to use versions/sorted by [@&#8203;kgallagher52](https://togithub.com/kgallagher52) in [https://github.com/electron/forge/pull/3431](https://togithub.com/electron/forge/pull/3431)
-   fix(core): packageJSON won't be found when programmatic usage instead of CLI by [@&#8203;ianho](https://togithub.com/ianho) in [https://github.com/electron/forge/pull/3455](https://togithub.com/electron/forge/pull/3455)
-   fix: actually depend on preceeding groups by [@&#8203;MarshallOfSound](https://togithub.com/MarshallOfSound) in [https://github.com/electron/forge/pull/3438](https://togithub.com/electron/forge/pull/3438)
-   fix: normalize windows version with build part correctly by [@&#8203;rickymohk](https://togithub.com/rickymohk) in [https://github.com/electron/forge/pull/3461](https://togithub.com/electron/forge/pull/3461)
-   fix: .vscode settings.json changes on open by [@&#8203;lutzroeder](https://togithub.com/lutzroeder) in [https://github.com/electron/forge/pull/3460](https://togithub.com/electron/forge/pull/3460)
-   fix(plugin-vite): package volume size to large by [@&#8203;caoxiemeihao](https://togithub.com/caoxiemeihao) in [https://github.com/electron/forge/pull/3336](https://togithub.com/electron/forge/pull/3336)

##### Performance

-   refactor: only run webpack once for multi-arch packages by [@&#8203;MarshallOfSound](https://togithub.com/MarshallOfSound) in [https://github.com/electron/forge/pull/3437](https://togithub.com/electron/forge/pull/3437)

##### Other Changes

-   chore: update Packager by [@&#8203;erikian](https://togithub.com/erikian) in [https://github.com/electron/forge/pull/3419](https://togithub.com/electron/forge/pull/3419)
-   chore: bump electronjs/node to 2.2.0 (main) by [@&#8203;electron-roller](https://togithub.com/electron-roller) in [https://github.com/electron/forge/pull/3469](https://togithub.com/electron/forge/pull/3469)
-   chore(plugins/electronegativity): correct some config types  by [@&#8203;Dogdriip](https://togithub.com/Dogdriip) in [https://github.com/electron/forge/pull/3482](https://togithub.com/electron/forge/pull/3482)
-   chore: use Dependabot to update GitHub Actions deps by [@&#8203;dsanders11](https://togithub.com/dsanders11) in [https://github.com/electron/forge/pull/3487](https://togithub.com/electron/forge/pull/3487)
-   chore: bump electronjs/node to 2.2.1 (main) by [@&#8203;electron-roller](https://togithub.com/electron-roller) in [https://github.com/electron/forge/pull/3496](https://togithub.com/electron/forge/pull/3496)

##### New Contributors

-   [@&#8203;kgallagher52](https://togithub.com/kgallagher52) made their first contribution in [https://github.com/electron/forge/pull/3431](https://togithub.com/electron/forge/pull/3431)
-   [@&#8203;rickymohk](https://togithub.com/rickymohk) made their first contribution in [https://github.com/electron/forge/pull/3461](https://togithub.com/electron/forge/pull/3461)
-   [@&#8203;lutzroeder](https://togithub.com/lutzroeder) made their first contribution in [https://github.com/electron/forge/pull/3460](https://togithub.com/electron/forge/pull/3460)
-   [@&#8203;ianho](https://togithub.com/ianho) made their first contribution in [https://github.com/electron/forge/pull/3455](https://togithub.com/electron/forge/pull/3455)
-   [@&#8203;yangannyx](https://togithub.com/yangannyx) made their first contribution in [https://github.com/electron/forge/pull/3480](https://togithub.com/electron/forge/pull/3480)
-   [@&#8203;Dogdriip](https://togithub.com/Dogdriip) made their first contribution in [https://github.com/electron/forge/pull/3482](https://togithub.com/electron/forge/pull/3482)

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

</details>

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

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

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

##### What's Changed

-   fix(cli): fallback to wasm32 if platform is not support by [@&#8203;Brooooooklyn](https://togithub.com/Brooooooklyn) in [https://github.com/napi-rs/napi-rs/pull/1967](https://togithub.com/napi-rs/napi-rs/pull/1967)
-   fix(cli): allow more platform & arch fallback to wasm by [@&#8203;Brooooooklyn](https://togithub.com/Brooooooklyn) in [https://github.com/napi-rs/napi-rs/pull/1969](https://togithub.com/napi-rs/napi-rs/pull/1969)

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

</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:eyJjcmVhdGVkSW5WZXIiOiIzNy4yMDAuMCIsInVwZGF0ZWRJblZlciI6IjM3LjIwMC4wIiwidGFyZ2V0QnJhbmNoIjoiY2FuYXJ5In0=-->
2024-02-23 08:39:35 +00:00
JimmFly
815ddd3778 chore: replace pricing plan link (#5886)
close TOV-609
2024-02-23 08:28:24 +00:00
983 changed files with 29823 additions and 37813 deletions

View File

@@ -8,5 +8,11 @@ corepack prepare yarn@stable --activate
# install dependencies
yarn install
# Build Server Dependencies
yarn workspace @affine/storage build
# Create database
yarn workspace @affine/server prisma db push
# Create user username: affine, password: affine
echo "INSERT INTO \"users\"(\"id\",\"name\",\"email\",\"email_verified\",\"created_at\",\"password\") VALUES('99f3ad04-7c9b-441e-a6db-79f73aa64db9','affine','affine@affine.pro','2024-02-26 15:54:16.974','2024-02-26 15:54:16.974+00','\$argon2id\$v=19\$m=19456,t=2,p=1\$esDS3QCHRH0Kmeh87YPm5Q\$9S+jf+xzw2Hicj6nkWltvaaaXX3dQIxAFwCfFa9o38A');" | yarn workspace @affine/server prisma db execute --stdin

View File

@@ -21,5 +21,6 @@
}
},
"updateContentCommand": "bash ./.devcontainer/build.sh",
"postCreateCommand": "bash ./.devcontainer/setup-user.sh"
"postCreateCommand": "bash ./.devcontainer/setup-user.sh",
"postStartCommand": ["yarn dev", "yarn workspace @affine/server dev"]
}

View File

@@ -1,7 +1,9 @@
set -e
if [ -v GRAPHITE_TOKEN ];then
gt auth --token $GRAPHITE_TOKEN
fi
git fetch
git fetch origin canary:canary --depth=1
git branch canary -t origin/canary
gt init --trunk canary

View File

@@ -1,4 +1,4 @@
const { resolve } = require('node:path');
const { join } = require('node:path');
const createPattern = packageName => [
{
@@ -31,22 +31,6 @@ const createPattern = packageName => [
message: 'Use `useNavigateHelper` instead',
importNames: ['useNavigate'],
},
{
group: ['next-auth/react'],
message: "Import hooks from 'use-current-user.tsx'",
// useSession is type unsafe
importNames: ['useSession'],
},
{
group: ['next-auth/react'],
message: "Import hooks from 'cloud-utils.ts'",
importNames: ['signIn', 'signOut'],
},
{
group: ['yjs'],
message: 'Do not use this API because it has a bug',
importNames: ['mergeUpdates'],
},
{
group: ['@affine/env/constant'],
message:
@@ -104,16 +88,17 @@ const config = {
},
ecmaVersion: 'latest',
sourceType: 'module',
project: resolve(__dirname, './tsconfig.eslint.json'),
project: join(__dirname, 'tsconfig.eslint.json'),
},
plugins: [
'react',
'@typescript-eslint',
'simple-import-sort',
'sonarjs',
'i',
'import-x',
'unused-imports',
'unicorn',
'rxjs',
],
rules: {
'array-callback-return': 'error',
@@ -146,6 +131,7 @@ const config = {
'unused-imports/no-unused-imports': 'error',
'simple-import-sort/imports': 'error',
'simple-import-sort/exports': 'error',
'import-x/no-duplicates': 'error',
'@typescript-eslint/ban-ts-comment': [
'error',
{
@@ -179,22 +165,6 @@ const config = {
message: 'Use `useNavigateHelper` instead',
importNames: ['useNavigate'],
},
{
group: ['next-auth/react'],
message: "Import hooks from 'use-current-user.tsx'",
// useSession is type unsafe
importNames: ['useSession'],
},
{
group: ['next-auth/react'],
message: "Import hooks from 'cloud-utils.ts'",
importNames: ['signIn', 'signOut'],
},
{
group: ['yjs'],
message: 'Do not use this API because it has a bug',
importNames: ['mergeUpdates'],
},
],
},
],
@@ -234,6 +204,21 @@ const config = {
'sonarjs/no-collection-size-mischeck': 'error',
'sonarjs/no-useless-catch': 'error',
'sonarjs/no-identical-functions': 'error',
'rxjs/finnish': [
'error',
{
functions: false,
methods: false,
strict: true,
types: {
'^LiveData$': true,
// some yjs classes are Observables, but they don't need to be in Finnish notation
'^Doc$': false, // yjs Doc
'^Awareness$': false, // yjs Awareness
'^UndoManager$': false, // yjs UndoManager
},
},
],
},
overrides: [
{
@@ -250,9 +235,6 @@ const config = {
},
...allPackages.map(pkg => ({
files: [`${pkg}/src/**/*.ts`, `${pkg}/src/**/*.tsx`],
parserOptions: {
project: resolve(__dirname, './tsconfig.eslint.json'),
},
rules: {
'@typescript-eslint/no-restricted-imports': [
'error',
@@ -269,7 +251,7 @@ const config = {
],
'@typescript-eslint/no-misused-promises': ['error'],
'@typescript-eslint/prefer-readonly': 'error',
'i/no-extraneous-dependencies': ['error'],
'import-x/no-extraneous-dependencies': ['error'],
'react-hooks/exhaustive-deps': [
'warn',
{

View File

@@ -7,6 +7,8 @@ body:
attributes:
value: |
Thanks for taking the time to fill out this bug report!
Check out this [link](https://github.com/toeverything/AFFiNE/blob/canary/docs/issue-triaging.md)
to learn how we manage issues and when your issue will be processed.
- type: textarea
id: what-happened
attributes:
@@ -41,6 +43,14 @@ body:
- Firefox
- Safari
- Other
- type: checkboxes
id: selfhost
attributes:
label: Are you self-hosting?
description: >
If you are self-hosting, please check the box and provide information about your setup.
options:
- label: 'Yes'
- type: textarea
id: logs
attributes:
@@ -53,11 +63,3 @@ body:
description: |
Links? References? Anything that will give us more context about the issue you are encountering!
Tip: You can attach images here
- type: checkboxes
attributes:
label: Are you willing to submit a PR?
description: >
(Optional) We encourage you to submit a [Pull Request](https://github.com/toeverything/affine/pulls) (PR) to help improve AFFiNE for everyone, especially if you have a good understanding of how to implement a fix or feature.
See the AFFiNE [Contributing Guide](https://github.com/toeverything/affine/blob/canary/CONTRIBUTING.md) to get started.
options:
- label: Yes I'd like to help by submitting a PR!

View File

@@ -24,7 +24,7 @@ runs:
shell: bash
run: |
echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
- uses: azure/setup-helm@v3
- uses: azure/setup-helm@v4
- id: auth
uses: google-github-actions/auth@v2
with:

View File

@@ -111,7 +111,7 @@ const createHelmCommand = ({ isDryRun }) => {
`--set-string graphql.app.oauth.google.clientSecret="${AFFINE_GOOGLE_CLIENT_SECRET}"`,
`--set-string graphql.app.payment.stripe.apiKey="${STRIPE_API_KEY}"`,
`--set-string graphql.app.payment.stripe.webhookKey="${STRIPE_WEBHOOK_KEY}"`,
`--set graphql.app.experimental.enableJwstCodec=true`,
`--set graphql.app.experimental.enableJwstCodec=${namespace === 'dev'}`,
`--set graphql.app.features.earlyAccessPreview=false`,
`--set graphql.app.features.syncClientVersionCheck=true`,
`--set sync.replicaCount=${syncReplicaCount}`,

View File

@@ -11,7 +11,7 @@ runs:
- name: Download tar.gz
uses: actions/download-artifact@v4
with:
name: core
name: web
path: .
- name: Extract core artifacts

View File

@@ -1,6 +1,6 @@
FROM openresty/openresty:1.25.3.1-0-buster
WORKDIR /app
COPY ./packages/frontend/core/dist ./dist
COPY ./packages/frontend/web/dist ./dist
COPY ./.github/deployment/front/nginx.conf /usr/local/openresty/nginx/conf/nginx.conf
COPY ./.github/deployment/front/affine.nginx.conf /etc/nginx/conf.d/affine.nginx.conf

View File

@@ -1,11 +1,11 @@
FROM node:18-bookworm-slim
FROM node:20-bookworm-slim
COPY ./packages/backend/server /app
COPY ./packages/frontend/core/dist /app/static
COPY ./packages/frontend/web/dist /app/static
WORKDIR /app
RUN apt-get update && \
apt-get install -y --no-install-recommends openssl && \
rm -rf /var/lib/apt/lists/*
CMD ["node", "--es-module-specifier-resolution=node", "./dist/index.js"]
CMD ["node", "--import", "./scripts/register.js", "./dist/index.js"]

View File

@@ -1,6 +1,6 @@
services:
affine:
image: ghcr.io/toeverything/affine-graphql:beta
image: ghcr.io/toeverything/affine-graphql:stable
container_name: affine_selfhosted
command:
['sh', '-c', 'node ./scripts/self-host-predeploy && node ./dist/index.js']
@@ -23,7 +23,7 @@ services:
max-size: '1000m'
restart: unless-stopped
environment:
- NODE_OPTIONS=--es-module-specifier-resolution node
- NODE_OPTIONS="--import=./scripts/register.js"
- AFFINE_CONFIG_PATH=/root/.affine/config
- REDIS_SERVER_HOST=redis
- DATABASE_URL=postgres://affine:affine@postgres:5432/affine

View File

@@ -3,4 +3,4 @@ name: affine
description: AFFiNE cloud chart
type: application
version: 0.0.0
appVersion: "0.12.0"
appVersion: "0.14.0"

View File

@@ -3,7 +3,7 @@ name: graphql
description: AFFiNE GraphQL server
type: application
version: 0.0.0
appVersion: "0.12.0"
appVersion: "0.14.0"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -61,18 +61,3 @@ Create the name of the service account to use
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{- define "jwt.key" -}}
{{- $secret := lookup "v1" "Secret" .Release.Namespace .Values.app.jwt.secretName -}}
{{- if and $secret $secret.data.private -}}
{{/*
Reusing existing secret data
*/}}
key: {{ $secret.data.private }}
{{- else -}}
{{/*
Generate new data
*/}}
key: {{ genPrivateKey "ecdsa" | b64enc }}
{{- end -}}
{{- end -}}

View File

@@ -28,10 +28,10 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: AUTH_PRIVATE_KEY
- name: AFFINE_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.app.jwt.secretName }}"
name: "{{ .Values.global.secret.secretName }}"
key: key
- name: NODE_ENV
value: "{{ .Values.env }}"
@@ -45,8 +45,6 @@ spec:
value: "graphql"
- name: AFFINE_ENV
value: "{{ .Release.Namespace }}"
- name: NEXTAUTH_URL
value: "{{ .Values.global.ingress.host }}"
- name: DATABASE_PASSWORD
valueFrom:
secretKeyRef:

View File

@@ -1,7 +0,0 @@
apiVersion: v1
kind: Secret
metadata:
name: "{{ .Values.app.jwt.secretName }}"
type: Opaque
data:
{{- ( include "jwt.key" . ) | indent 2 -}}

View File

@@ -0,0 +1,18 @@
{{- $privateKey := default (genPrivateKey "ecdsa") .Values.global.secret.privateKey | b64enc | quote }}
{{- if not .Values.global.secret.privateKey }}
{{- $existingKey := (lookup "v1" "Secret" .Release.Namespace .Values.global.secret.secretName) }}
{{- if $existingKey }}
{{- $privateKey = index $existingKey.data "key" }}
{{- end -}}
{{- end -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ .Values.global.secret.secretName }}
annotations:
"helm.sh/resource-policy": "keep"
type: Opaque
data:
key: {{ $privateKey }}

View File

@@ -19,10 +19,6 @@ app:
https: true
doc:
mergeInterval: "3000"
jwt:
secretName: jwt-private-key
# base64 encoded ecdsa private key
privateKey: ''
captcha:
enable: false
secretName: captcha

View File

@@ -3,7 +3,7 @@ name: sync
description: AFFiNE Sync Server
type: application
version: 0.0.0
appVersion: "0.12.0"
appVersion: "0.14.0"
dependencies:
- name: gcloud-sql-proxy
version: 0.0.0

View File

@@ -32,6 +32,11 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: AFFINE_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: "{{ .Values.global.secret.secretName }}"
key: key
- name: NODE_ENV
value: "{{ .Values.env }}"
- name: NO_COLOR
@@ -40,8 +45,6 @@ spec:
value: "affine"
- name: SERVER_FLAVOR
value: "sync"
- name: NEXTAUTH_URL
value: "{{ .Values.global.ingress.host }}"
- name: AFFINE_ENV
value: "{{ .Release.Namespace }}"
- name: DATABASE_PASSWORD

View File

@@ -12,7 +12,6 @@ env: 'production'
app:
# AFFINE_SERVER_HOST
host: '0.0.0.0'
serviceAccount:
create: true
annotations: {}

View File

@@ -60,6 +60,13 @@ spec:
name: affine-graphql
port:
number: {{ .Values.graphql.service.port }}
- path: /oauth
pathType: Prefix
backend:
service:
name: affine-graphql
port:
number: {{ .Values.graphql.service.port }}
- path: /
pathType: Prefix
backend:

View File

@@ -4,6 +4,9 @@ global:
className: ''
host: affine.pro
tls: []
secret:
secretName: 'server-private-key'
privateKey: ''
database:
user: 'postgres'
url: 'pg-postgresql'

View File

@@ -4,6 +4,8 @@ on:
push:
branches:
- canary
- beta
- stable
- v[0-9]+.[0-9]+.x-staging
- v[0-9]+.[0-9]+.x
paths-ignore:
@@ -116,6 +118,7 @@ jobs:
runs-on: ubuntu-latest
env:
DISTRIBUTION: browser
IN_CI_TEST: true
strategy:
fail-fast: false
matrix:
@@ -263,8 +266,8 @@ jobs:
path: ./packages/backend/storage/storage.node
if-no-files-found: error
build-core:
name: Build @affine/core
build-web:
name: Build @affine/web
runs-on: ubuntu-latest
steps:
@@ -274,15 +277,15 @@ jobs:
with:
electron-install: false
full-cache: true
- name: Build Core
- name: Build Web
# always skip cache because its fast, and cache configuration is always changing
run: yarn nx build @affine/core --skip-nx-cache
- name: zip core
run: tar -czf dist.tar.gz --directory=packages/frontend/core/dist .
- name: Upload core artifact
run: yarn nx build @affine/web --skip-nx-cache
- name: zip web
run: tar -czf dist.tar.gz --directory=packages/frontend/web/dist .
- name: Upload web artifact
uses: actions/upload-artifact@v4
with:
name: core
name: web
path: dist.tar.gz
if-no-files-found: error
@@ -333,17 +336,11 @@ jobs:
env:
PGPASSWORD: affine
- name: Generate prisma client
- name: Run init-db script
run: |
yarn workspace @affine/server exec prisma generate
yarn workspace @affine/server exec prisma db push
env:
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
- name: Run init-db script
run: |
yarn workspace @affine/server data-migration run
yarn workspace @affine/server exec node --loader ts-node/esm/transpile-only ./scripts/init-db.ts
env:
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
@@ -368,6 +365,7 @@ jobs:
env:
DISTRIBUTION: browser
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
IN_CI_TEST: true
strategy:
fail-fast: false
matrix:
@@ -431,17 +429,11 @@ jobs:
env:
PGPASSWORD: affine
- name: Generate prisma client
- name: Run init-db script
run: |
yarn workspace @affine/server exec prisma generate
yarn workspace @affine/server exec prisma db push
env:
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
- name: Run init-db script
run: |
yarn workspace @affine/server data-migration run
yarn workspace @affine/server exec node --loader ts-node/esm/transpile-only ./scripts/init-db.ts
- name: ${{ matrix.tests.name }}
run: |
@@ -493,7 +485,7 @@ jobs:
test: true,
}
needs:
- build-core
- build-web
- build-native
steps:
- uses: actions/checkout@v4
@@ -524,8 +516,8 @@ jobs:
shell: bash
run: yarn workspace @affine/electron vitest
- name: Download core artifact
uses: ./.github/actions/download-core
- name: Download web artifact
uses: ./.github/actions/download-web
with:
path: packages/frontend/electron/resources/web-static

View File

@@ -7,6 +7,11 @@ on:
schedule:
- cron: '0 9 * * *'
permissions:
contents: write
pull-requests: write
actions: write
jobs:
dispatch-deploy:
runs-on: ubuntu-latest

View File

@@ -15,6 +15,7 @@ on:
env:
APP_NAME: affine
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
MIXPANEL_TOKEN: '389c0615a69b57cca7d3fa0a4824c930'
jobs:
build-server:
@@ -38,8 +39,8 @@ jobs:
name: server-dist
path: ./packages/backend/server/dist
if-no-files-found: error
build-core:
name: Build @affine/core
build-web:
name: Build @affine/web
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.flavor }}
steps:
@@ -50,7 +51,7 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build Core
run: yarn nx build @affine/core --skip-nx-cache
run: yarn nx build @affine/web --skip-nx-cache
env:
R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
@@ -64,15 +65,15 @@ jobs:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }}
- name: Upload core artifact
- name: Upload web artifact
uses: actions/upload-artifact@v4
with:
name: core
path: ./packages/frontend/core/dist
name: web
path: ./packages/frontend/web/dist
if-no-files-found: error
build-core-selfhost:
name: Build @affine/core selfhost
build-web-selfhost:
name: Build @affine/web selfhost
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.flavor }}
steps:
@@ -83,18 +84,19 @@ jobs:
- name: Setup Node.js
uses: ./.github/actions/setup-node
- name: Build Core
run: yarn nx build @affine/core --skip-nx-cache
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 core artifact
- name: Upload web artifact
uses: actions/upload-artifact@v4
with:
name: selfhost-core
path: ./packages/frontend/core/dist
name: selfhost-web
path: ./packages/frontend/web/dist
if-no-files-found: error
build-storage:
@@ -136,18 +138,22 @@ jobs:
build-docker:
name: Build Docker
runs-on: ubuntu-latest
permissions:
contents: 'write'
id-token: 'write'
packages: 'write'
needs:
- build-server
- build-core
- build-core-selfhost
- build-web
- build-web-selfhost
- build-storage
steps:
- uses: actions/checkout@v4
- name: Download core artifact
uses: actions/download-artifact@v4
with:
name: core
path: ./packages/frontend/core/dist
name: web
path: ./packages/frontend/web/dist
- name: Download server dist
uses: actions/download-artifact@v4
with:
@@ -213,14 +219,14 @@ jobs:
registry-url: https://npm.pkg.github.com
scope: '@toeverything'
- name: Remove core dist
run: rm -rf ./packages/frontend/core/dist
- name: Remove web dist
run: rm -rf ./packages/frontend/web/dist
- name: Download selfhost core artifact
- name: Download selfhost web artifact
uses: actions/download-artifact@v4
with:
name: selfhost-core
path: ./packages/frontend/core/dist
name: selfhost-web
path: ./packages/frontend/web/dist
- name: Install Node.js dependencies
run: |

View File

@@ -24,7 +24,7 @@ jobs:
token: ${{ secrets.HELM_RELEASER_TOKEN }}
- name: Install Helm
uses: azure/setup-helm@v3
uses: azure/setup-helm@v4
- name: Install chart releaser
run: |

View File

@@ -7,6 +7,11 @@ on:
schedule:
- cron: '0 9 * * *'
permissions:
contents: write
pull-requests: write
actions: write
jobs:
dispatch-release-desktop:
runs-on: ubuntu-latest

View File

@@ -33,6 +33,7 @@ env:
DEBUG: napi:*
APP_NAME: affine
MACOSX_DEPLOYMENT_TARGET: '10.13'
MIXPANEL_TOKEN: '389c0615a69b57cca7d3fa0a4824c930'
jobs:
before-make:
@@ -60,10 +61,10 @@ jobs:
SKIP_PLUGIN_BUILD: 'true'
SKIP_NX_CACHE: 'true'
- name: Upload core artifact
- name: Upload web artifact
uses: actions/upload-artifact@v4
with:
name: core
name: web
path: packages/frontend/electron/resources/web-static
make-distribution:
@@ -110,7 +111,7 @@ jobs:
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
- uses: actions/download-artifact@v4
with:
name: core
name: web
path: packages/frontend/electron/resources/web-static
- name: Build Desktop Layers
@@ -130,18 +131,23 @@ jobs:
SKIP_WEB_BUILD: 1
HOIST_NODE_MODULES: 1
- name: signing DMG
if: ${{ matrix.spec.platform == 'darwin' }}
run: |
codesign --force --sign "Developer ID Application: TOEVERYTHING PTE. LTD." packages/frontend/electron/out/${{ env.BUILD_TYPE }}/make/AFFiNE.dmg
- name: Save artifacts (mac)
if: ${{ matrix.spec.platform == 'darwin' }}
run: |
mkdir -p builds
mv packages/frontend/electron/out/*/make/*.dmg ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.dmg
mv packages/frontend/electron/out/*/make/zip/darwin/${{ matrix.spec.arch }}/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.zip
mv packages/frontend/electron/out/*/make/*.dmg ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.dmg
mv packages/frontend/electron/out/*/make/zip/darwin/${{ matrix.spec.arch }}/*.zip ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-macos-${{ matrix.spec.arch }}.zip
- name: Save artifacts (linux)
if: ${{ matrix.spec.platform == 'linux' }}
run: |
mkdir -p builds
mv packages/frontend/electron/out/*/make/zip/linux/x64/*.zip ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.zip
mv packages/frontend/electron/out/*/make/*.AppImage ./builds/affine-${{ env.BUILD_TYPE }}-linux-x64.AppImage
mv packages/frontend/electron/out/*/make/zip/linux/x64/*.zip ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-x64.zip
mv packages/frontend/electron/out/*/make/*.AppImage ./builds/affine-${{ needs.before-make.outputs.RELEASE_VERSION }}-${{ env.BUILD_TYPE }}-linux-x64.appimage
- name: Upload Artifact
uses: actions/upload-artifact@v4
@@ -183,7 +189,7 @@ jobs:
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
- uses: actions/download-artifact@v4
with:
name: core
name: web
path: packages/frontend/electron/resources/web-static
- name: Build Desktop Layers
@@ -273,7 +279,7 @@ jobs:
artifact-name: installer-win32-x64
finalize-installer-windows:
needs: sign-installer-artifacts-windows
needs: [sign-installer-artifacts-windows, before-make]
strategy:
matrix:
spec:
@@ -294,9 +300,9 @@ jobs:
- name: Save artifacts
run: |
mkdir -p builds
mv packages/frontend/electron/out/*/make/zip/win32/x64/AFFiNE*-win32-x64-*.zip ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.zip
mv packages/frontend/electron/out/*/make/squirrel.windows/x64/*.exe ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.exe
mv packages/frontend/electron/out/*/make/squirrel.windows/x64/*.msi ./builds/affine-${{ env.BUILD_TYPE }}-windows-x64.msi
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
- name: Upload Artifact
uses: actions/upload-artifact@v4
@@ -312,7 +318,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: core
name: web
path: web-static
- name: Zip web-static
run: zip -r web-static.zip web-static
@@ -346,7 +352,7 @@ jobs:
RELEASE_VERSION: ${{ needs.before-make.outputs.RELEASE_VERSION }}
- name: Create Release Draft
if: ${{ github.ref_type == 'tag' }}
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
name: ${{ needs.before-make.outputs.RELEASE_VERSION }}
body: ''
@@ -357,12 +363,12 @@ jobs:
./*.zip
./*.dmg
./*.exe
./*.AppImage
./*.appimage
./*.apk
./*.yml
- name: Create Nightly Release Draft
if: ${{ github.ref_type == 'branch' }}
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
with:
@@ -379,6 +385,6 @@ jobs:
./*.zip
./*.dmg
./*.exe
./*.AppImage
./*.appimage
./*.apk
./*.yml

2
.nvmrc
View File

@@ -1 +1 @@
18
20

File diff suppressed because one or more lines are too long

View File

@@ -12,4 +12,4 @@ npmPublishAccess: public
npmPublishRegistry: "https://registry.npmjs.org"
yarnPath: .yarn/releases/yarn-4.1.0.cjs
yarnPath: .yarn/releases/yarn-4.1.1.cjs

4
Cargo.lock generated
View File

@@ -949,9 +949,9 @@ dependencies = [
[[package]]
name = "mio"
version = "0.8.9"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
dependencies = [
"libc",
"log",

128
README.md
View File

@@ -5,86 +5,89 @@
Write, Draw and Plan All at Once
<br>
</h1>
<p>
One hyper-fused platform for wildly creative minds. <br />
A privacy-focussed, local-first, open-source, and ready-to-use alternative for Notion & Miro.
<a href="https://affine.pro/download">
<img alt="affine logo" src="https://cdn.affine.pro/Github_hero_image1.png" style="width: 100%">
</a>
<br/>
<p align="center">
A privacy-focussed, local-first, open-source, and ready-to-use alternative for Notion & Miro. <br />
One hyper-fused platform for wildly creative minds.
</p>
</div>
<br/>
<br/>
<a href="https://www.producthunt.com/posts/affine-3?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-affine&#0045;3" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=440671&theme=light" alt="AFFiNE - One&#0032;app&#0032;for&#0032;all&#0032;&#0045;&#0032;Where&#0032;Notion&#0032;meets&#0032;Miro | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
<br/>
<br/>
<div align="center">
[![AFFiNE Web](<https://img.shields.io/badge/-Try%20It%20Online%20%E2%86%92-rgb(84,56,255)?style=flat-square&logoColor=white&logo=affine>)](https://app.affine.pro)
[![AFFiNE macOS M1/M2 Chip](https://img.shields.io/badge/-macOS_M_Chip%20%E2%86%92-black?style=flat-square&logo=apple&logoColor=white)](https://affine.pro/download)
[![AFFiNE macOS x64](https://img.shields.io/badge/-macOS_x86%20%E2%86%92-black?style=flat-square&logo=apple&logoColor=white)](https://affine.pro/download)
[![AFFiNE Window x64](https://img.shields.io/badge/-Windows%20%E2%86%92-blue?style=flat-square&logo=windows&logoColor=white)](https://affine.pro/download)
[![AFFiNE Linux](https://img.shields.io/badge/-Linux%20%E2%86%92-yellow?style=flat-square&logo=linux&logoColor=white)](https://affine.pro/download)
<a href="https://affine.pro">Home Page</a> |
<a href="https://discord.com/invite/yz6tGVsf5p">Discord</a> |
<a href="https://app.affine.pro">Live Demo</a> |
<a href="https://affine.pro/blog/">Blog</a> |
<a href="https://docs.affine.pro/docs/">Documentation</a>
</div>
<br/>
[![Releases](https://img.shields.io/github/downloads/toeverything/AFFiNE/total)](https://github.com/toeverything/AFFiNE/releases/latest)
[![stars-icon]](https://github.com/toeverything/AFFiNE)
[![All Contributors][all-contributors-badge]](#contributors)
[![codecov]](https://codecov.io/gh/toeverything/AFFiNE)
[![Node-version-icon]](https://nodejs.org/)
[![TypeScript-version-icon]](https://www.typescriptlang.org/)
[![React-version-icon]](https://reactjs.org/)
[![blocksuite-icon]](https://github.com/toeverything/blocksuite)
[![Rust-version-icon]](https://www.rust-lang.org/)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftoeverything%2FAFFiNE.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftoeverything%2FAFFiNE?ref=badge_shield)
[![Deploy](https://github.com/toeverything/AFFiNE/actions/workflows/deploy.yml/badge.svg)](https://github.com/toeverything/AFFiNE/actions/workflows/deploy.yml)
</div>
---
<div align="center">
<a href="http://affine.pro"><img src="https://img.shields.io/badge/-AFFiNE-06449d?style=social&logo=affine" height=25></a>
&nbsp;
<a href="https://community.affine.pro"><img src="https://img.shields.io/badge/-Community-424549?style=social&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAXNJREFUWEftlitLRUEURtdVEVExWUx2qxgNVouoXYtNDP4Tw20WtftAsItZrHaTYBJREZ98MAc248wcZxi4CGfSeezHmm/23kyPAa/egPPTAXQK/FsFBP7ldVDRZoqcgO9I+2bHy3ZIJBfTCPCZM1tqAxwBmzUBrNQNbEx+5b0B5oEN4NCBrAMnMaiUAuPAs3HU82TLEZwBqwGbaJ4UgKQ8CFR6SoEl4LIWwCJwZQCegKkWBWLHVKSActvdzgG3DqitDf3/VQBskBDALrDnAKXUo3ueAF5KinAf2DKOmnzD7l214bdbA6hC1XHZNQa8hSBC0hwDa57xDHDvvvWB7ciOZoE79+8CWPbsBGc769eFxJdWIKcuyIdRoG3W7AAC1dJkHDIOo8B78+4rEBo8r4AkLFk6Jk3HaeDBBTgHVmIAfpJUz+cAFXVBreQCvQYW/lqEjV1NAMUMqpAaxQMHyDnjYtuS+0BxstwaqJooFqxToFPgB5FuPCEB6XK2AAAAAElFTkSuQmCC" height=25></a>
&nbsp;
<a href="https://discord.com/invite/yz6tGVsf5p"><img src="https://img.shields.io/badge/-Discord-424549?style=social&logo=discord" height=25></a>
&nbsp;
<a href="https://t.me/affineworkos"><img src="https://img.shields.io/badge/-Telegram-red?style=social&logo=telegram" height=25></a>
&nbsp;
<a href="https://twitter.com/AffineOfficial"><img src="https://img.shields.io/badge/-Twitter-red?style=social&logo=twitter" height=25></a>
&nbsp;
<a href="https://medium.com/@affineworkos"><img src="https://img.shields.io/badge/-Medium-red?style=social&logo=medium" height=25></a>
</div>
<br />
<div align="center">
<em>Docs, canvas and tables are hyper-merged with AFFiNE - just like the word affine (əˈɪn | a-fine).</em>
</div>
<br />
![img_v2_37a7cc04-ab3f-4405-ae9a-f84ceb4c948g](https://user-images.githubusercontent.com/79301703/230892907-5fd5c0c5-1665-4d75-8a35-744e0afc36a5.gif)
## Join our community
Before we tell you how to get started with AFFiNE, we'd like to shamelessly plug our awesome user and developer communities across [official discord server](https://discord.gg/tSNqN4S4)! Once youre familiar with using the software, maybe you will share your wisdom with others and even consider joining the [AFFiNE Ambassador program](https://community.affine.pro/c/start-here/affine-ambassador) to help spread AFFiNE to the world.
<div align="center">
<img src="https://github.com/toeverything/AFFiNE/assets/79301703/49a426bb-8d2b-4216-891a-fa5993642253" style="width: 100%"/>
</div>
## Getting started & staying tuned with us.
⚠️ Please note that AFFiNE is still under active development and is not yet ready for production use. ⚠️
[![affine.pro](https://img.shields.io/static/v1?label=Try%20it%20Online&logo=affine&message=%E2%86%92&style=for-the-badge)](https://app.affine.pro) No installation or registration required! Head over to our website and try it out now.
[![community.affine.pro](https://img.shields.io/static/v1?label=Join%20the%20community&message=%E2%86%92&style=for-the-badge)](https://community.affine.pro) Our wonderful community, where you can meet and engage with the team, developers and other like-minded enthusiastic user of AFFiNE.
Star us, and you will receive all releases notifications from GitHub without any delay!
![rbU3YmmsQT](https://user-images.githubusercontent.com/79301703/230891830-0110681e-8c7e-483b-b6d9-9e42b291b9ef.gif)
<img src="https://user-images.githubusercontent.com/79301703/230891830-0110681e-8c7e-483b-b6d9-9e42b291b9ef.gif" style="width: 100%"/>
## What is AFFiNE
AFFiNE is an open-source, all-in-one workspace and an operating system for all the building blocks that assemble your knowledge base and much more -- wiki, knowledge management, presentation and digital assets. It's a better alternative to Notion and Miro.
## Features
- **Hyper merged** — Write, draw and plan all at once. Assemble any blocks you love on any canvas you like to enjoy seamless transitions between workflows with AFFiNE.
- **Privacy focussed** — AFFiNE is built with your privacy in mind and is one of our key concerns. We want you to keep control of your data, allowing you to store it as you like, where you like while still being able to freely edit and view your data on-demand.
- **Offline-first** — With your privacy in mind we also decided to go offline-first. This means that AFFiNE can be used offline, whether you want to view or edit, with support for conflict-free merging when you are back online.
- **Clean, intuitive design** — With AFFiNE you can concentrate on editing with a clean and modern interface. Which is responsive, so it looks great on tablets too, and mobile support is coming in the future.
- **Modern Block Editor with Markdown support** — A modern block editor can help you not only for docs, but slides and tables as well. When you write in AFFiNE you can use Markdown syntax which helps create an easier editing experience, that can be experienced with just a keyboard. And this allows you to export your data cleanly into Markdown.
- **Collaboration** — Whether you want to collaborate with yourself across multiple devices, or work together with others, support for collaboration and multiplayer is out-of-the-box, which makes it easy for teams to get started with AFFiNE.
- **Choice of multiple languages** — Thanks to community contributions AFFiNE offers support for multiple languages. If you don't find your language or would like to suggest some changes we welcome your contributions.
**A true canvas for blocks in any form. Docs and whiteboard are now fully merged.**
![img_v2_3a4ee0da-6dd7-48cb-8f19-5411f86768ag](https://user-images.githubusercontent.com/79301703/230893796-dc707955-e4e5-4a42-a3c9-18d1ea754f6f.gif)
- Many editor apps claim to be a canvas for productivity, but AFFiNE is one of the very few which allows you to put any building block on an edgeless canvas -- rich text, sticky notes, any embedded web pages, multi-view databases, linked pages, shapes and even slides. We have it all.
**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.
**Local-first & Real-time collaborative**
- We love the idea of local-first that you always own your data on your disk, in spite of the cloud. Furthermore, AFFiNE supports real-time sync and collaborations on web and cross-platform clients.
**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-).
## Acknowledgement
“We shape our tools and thereafter our tools shape us”. A lot of pioneers have inspired us along the way, e.g.:
- Quip & Notion with their great concept of “everything is a block”
- 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
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.
Thanks for checking us out, we appreciate your interest and sincerely hope that AFFiNE resonates with you! 🎵 Checking https://affine.pro/ for more details ions.
## Contributing
@@ -117,7 +120,7 @@ If you have questions, you are welcome to contact us. One of the best places to
We would also like to give thanks to open-source projects that make AFFiNE possible:
- [blocksuite](https://github.com/toeverything/BlockSuite) - 💠 BlockSuite is the open-source collaborative editor project behind AFFiNE.
- [Blocksuite](https://github.com/toeverything/BlockSuite) - 💠 BlockSuite is the open-source collaborative editor project behind AFFiNE.
- [OctoBase](https://github.com/toeverything/OctoBase) - 🐙 OctoBase is the open-source database behind AFFiNE, local-first, yet collaborative. A light-weight, scalable, data engine written in Rust.
- [yjs](https://github.com/yjs/yjs) - Fundamental support of CRDTs for our implementation on state management and data sync.
- [electron](https://github.com/electron/electron) - Build cross-platform desktop apps with JavaScript, HTML, and CSS.
@@ -140,20 +143,11 @@ We would like to express our gratitude to all the individuals who have already c
## Self-Host
> We know that the self-host version has been out of date for a long time.
>
> We are working hard to get this updated to the latest version, you can try our desktop version first.
Get started with Docker and deploy your own feature-rich, restriction-free deployment of AFFiNE.
We are working hard to get this updated to the latest version, you can keep an eye on the [latest packages].
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 interested in helping build with AFFiNE and/or its partners? Check out some of the latest [jobs available].
## Upgrading
For upgrading information, please see our [update page].
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].
## Feature Request
@@ -185,8 +179,6 @@ Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testi
See [LICENSE] for details.
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Ftoeverything%2FAFFiNE.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Ftoeverything%2FAFFiNE?ref=badge_large)
[all-contributors-badge]: https://img.shields.io/github/contributors/toeverything/AFFiNE
[license]: ./LICENSE
[building.md]: ./docs/BUILDING.md

29
SECURITY.md Normal file
View File

@@ -0,0 +1,29 @@
# Security Policy
## Supported Versions
We recommend users to always use the latest major version. Security updates will be provided for the current major version until the next major version is released.
| Version | Supported |
| --------------- | ------------------ |
| 0.12.x (stable) | :white_check_mark: |
| < 0.12.x | :x: |
## Reporting a Vulnerability
We welcome you to provide us with bug reports via and email at [security@toeverything.info](mailto:security@toeverything.info). We expect your report to contain at least the following for us to evaluate and reproduce:
1. Using platform and version, for example:
- macos arm64 0.12.0-canary-202402220729-0868ac6
- app.affine.pro 0.12.0-canary-202402220729-0868ac6
2. A sets of video or screenshot containing the reproduce steps that proves you successfully exploited the vulnerability, preferably including the time and software version of the successful exploit.
3. Your classification or analysis of the vulnerability (optional)
Since we are an open source project, we also welcome you to provide corresponding fix PRs.
We will provide bounties for vulnerabilities involving user information leakage, permission leakage, and unauthorized code execution. For other types of vulnerabilities, we will determine specific rewards based on the evaluation results.
If the vulnerability is caused by a library we depend on, we encourage you to submit a security report to the corresponding dependent library at the same time to benefit more users.

View File

@@ -29,7 +29,7 @@ It includes the global constants, browser and system check.
This package should be imported at the very beginning of the entry point.
### `@affine/workspace`
### `@affine/workspace-impl`
Current we have two workspace plugin:

View File

@@ -49,28 +49,24 @@ postgres=# \du
### Set the following config to `packages/backend/server/.env`
In the following setup, we assume you have postgres server running at localhost:5432 and mailhog running at localhost:1025.
When logging in via email, you will see the mail arriving at localhost:8025 in a browser.
```
DATABASE_URL="postgresql://affine:affine@localhost:5432/affine"
NEXTAUTH_URL="http://localhost:8080/"
```
You may need additional env for auth login. You may want to put your own one if you are not part of the AFFiNE team
For email login & password, please refer to https://nodemailer.com/usage/using-gmail/
```
MAILER_SENDER=
MAILER_USER=
MAILER_PASSWORD=
OAUTH_GOOGLE_ENABLED="true"
OAUTH_GOOGLE_CLIENT_ID=
OAUTH_GOOGLE_CLIENT_SECRET=
MAILER_SENDER="noreply@toeverything.info"
MAILER_USER="auth"
MAILER_PASSWORD="auth"
MAILER_HOST="localhost"
MAILER_PORT="1025"
```
## Prepare prisma
```
yarn workspace @affine/server prisma db push
yarn workspace @affine/server data-migration run
```
Note, you may need to do it again if db schema changed.

View File

@@ -7,9 +7,9 @@
"dev": "nodemon --exec 'typedoc --options ../../typedoc.json' & serve dist/"
},
"devDependencies": {
"nodemon": "^3.0.1",
"nodemon": "^3.1.0",
"serve": "^14.2.1",
"typedoc": "^0.25.4"
"typedoc": "^0.25.8"
},
"nodemonConfig": {
"watch": [
@@ -19,5 +19,5 @@
],
"ext": "ts,md,json"
},
"version": "0.12.0"
"version": "0.14.0"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@affine/monorepo",
"version": "0.12.0",
"version": "0.14.0",
"private": true,
"author": "toeverything",
"license": "MIT",
@@ -14,19 +14,19 @@
"tests/affine-legacy/*"
],
"engines": {
"node": ">=18.16.1 <19.0.0"
"node": "<21.0.0"
},
"scripts": {
"dev": "dev-core",
"dev": "yarn workspace @affine/cli dev",
"dev:electron": "yarn workspace @affine/electron dev",
"build": "yarn nx build @affine/core",
"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/core static-server",
"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": "eslint . --ext .js,mjs,.ts,.tsx --cache",
"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 .",
@@ -36,7 +36,7 @@
"test": "vitest --run",
"test:ui": "vitest --ui",
"test:coverage": "vitest run --coverage",
"typecheck": "tsc -b tsconfig.json --diagnostics",
"typecheck": "tsc -b tsconfig.json",
"postinstall": "node ./scripts/check-version.mjs && yarn i18n-codegen gen && yarn husky install",
"prepare": "husky"
},
@@ -44,7 +44,7 @@
"*": "prettier --write --ignore-unknown --cache",
"*.{ts,tsx,mjs,js,jsx}": [
"prettier --ignore-unknown --write",
"eslint --cache --fix"
"cross-env NODE_OPTIONS=\"--max-old-space-size=8192\" eslint --cache --fix"
],
"*.toml": [
"taplo format"
@@ -56,61 +56,62 @@
"devDependencies": {
"@affine-test/kit": "workspace:*",
"@affine/cli": "workspace:*",
"@commitlint/cli": "^18.4.3",
"@commitlint/config-conventional": "^18.4.3",
"@faker-js/faker": "^8.3.1",
"@commitlint/cli": "^19.0.0",
"@commitlint/config-conventional": "^19.0.0",
"@faker-js/faker": "^8.4.1",
"@istanbuljs/schema": "^0.1.3",
"@magic-works/i18n-codegen": "^0.5.0",
"@nx/vite": "18.0.4",
"@playwright/test": "^1.41.0",
"@nx/vite": "18.1.2",
"@playwright/test": "^1.41.2",
"@taplo/cli": "^0.7.0",
"@testing-library/react": "^14.1.2",
"@testing-library/react": "^14.2.1",
"@toeverything/infra": "workspace:*",
"@types/affine__env": "workspace:*",
"@types/eslint": "^8.44.7",
"@types/node": "^20.9.3",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0",
"@vanilla-extract/vite-plugin": "^4.0.0",
"@vanilla-extract/webpack-plugin": "^2.3.1",
"@vitejs/plugin-react-swc": "^3.5.0",
"@vitest/coverage-istanbul": "1.3.1",
"@vitest/ui": "1.3.1",
"electron": "^29.0.0",
"eslint": "^8.54.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-i": "^2.29.0",
"@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",
"@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",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import-x": "^0.4.1",
"eslint-plugin-react": "^7.33.2",
"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.0",
"eslint-plugin-unused-imports": "^3.0.0",
"eslint-plugin-vue": "^9.18.1",
"eslint-plugin-unicorn": "^51.0.1",
"eslint-plugin-unused-imports": "^3.1.0",
"eslint-plugin-vue": "^9.22.0",
"fake-indexeddb": "5.0.2",
"happy-dom": "^13.0.0",
"husky": "^9.0.6",
"lint-staged": "^15.1.0",
"msw": "^2.0.8",
"nanoid": "^5.0.3",
"nx": "^18.0.0",
"happy-dom": "^14.0.0",
"husky": "^9.0.11",
"lint-staged": "^15.2.2",
"msw": "^2.2.1",
"nanoid": "^5.0.6",
"nx": "^18.0.4",
"nyc": "^15.1.0",
"oxlint": "0.0.22",
"prettier": "^3.1.0",
"semver": "^7.5.4",
"prettier": "^3.2.5",
"semver": "^7.6.0",
"serve": "^14.2.1",
"string-width": "^7.0.0",
"ts-node": "^10.9.1",
"typescript": "^5.3.2",
"vite": "^5.0.6",
"vite-plugin-istanbul": "^5.0.0",
"vite-plugin-static-copy": "^1.0.0",
"vite-tsconfig-paths": "^4.2.1",
"vitest": "1.3.1",
"string-width": "^7.1.0",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"vite": "^5.1.4",
"vite-plugin-istanbul": "^6.0.0",
"vite-plugin-static-copy": "^1.0.1",
"vitest": "1.4.0",
"vitest-fetch-mock": "^0.2.2",
"vitest-mock-extended": "^1.3.1"
},
"packageManager": "yarn@4.1.0",
"packageManager": "yarn@4.1.1",
"resolutions": {
"vite": "^5.0.6",
"array-buffer-byte-length": "npm:@nolyfill/array-buffer-byte-length@latest",
@@ -168,9 +169,8 @@
"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",
"next-auth@^4.24.5": "patch:next-auth@npm%3A4.24.5#~/.yarn/patches/next-auth-npm-4.24.5-8428e11927.patch",
"@reforged/maker-appimage/@electron-forge/maker-base": "7.2.0",
"macos-alias": "npm:macos-alias-building@latest",
"@reforged/maker-appimage/@electron-forge/maker-base": "7.3.0",
"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,70 @@
-- DropForeignKey
ALTER TABLE "accounts" DROP CONSTRAINT "accounts_user_id_fkey";
-- DropForeignKey
ALTER TABLE "sessions" DROP CONSTRAINT "sessions_user_id_fkey";
-- CreateTable
CREATE TABLE "user_connected_accounts" (
"id" VARCHAR(36) NOT NULL,
"user_id" VARCHAR(36) NOT NULL,
"provider" VARCHAR NOT NULL,
"provider_account_id" VARCHAR NOT NULL,
"scope" TEXT,
"access_token" TEXT,
"refresh_token" TEXT,
"expires_at" TIMESTAMPTZ(6),
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(6) NOT NULL,
CONSTRAINT "user_connected_accounts_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "multiple_users_sessions" (
"id" VARCHAR(36) NOT NULL,
"expires_at" TIMESTAMPTZ(6),
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "multiple_users_sessions_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "user_sessions" (
"id" VARCHAR(36) NOT NULL,
"session_id" VARCHAR(36) NOT NULL,
"user_id" VARCHAR(36) NOT NULL,
"expires_at" TIMESTAMPTZ(6),
"created_at" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "user_sessions_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "verification_tokens" (
"token" VARCHAR(36) NOT NULL,
"type" SMALLINT NOT NULL,
"credential" TEXT,
"expiresAt" TIMESTAMPTZ(6) NOT NULL
);
-- CreateIndex
CREATE INDEX "user_connected_accounts_user_id_idx" ON "user_connected_accounts"("user_id");
-- CreateIndex
CREATE INDEX "user_connected_accounts_provider_account_id_idx" ON "user_connected_accounts"("provider_account_id");
-- CreateIndex
CREATE UNIQUE INDEX "user_sessions_session_id_user_id_key" ON "user_sessions"("session_id", "user_id");
-- CreateIndex
CREATE UNIQUE INDEX "verification_tokens_type_token_key" ON "verification_tokens"("type", "token");
-- AddForeignKey
ALTER TABLE "user_connected_accounts" ADD CONSTRAINT "user_connected_accounts_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_sessions" ADD CONSTRAINT "user_sessions_session_id_fkey" FOREIGN KEY ("session_id") REFERENCES "multiple_users_sessions"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "user_sessions" ADD CONSTRAINT "user_sessions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "users" ADD COLUMN "registered" BOOLEAN NOT NULL DEFAULT true;

View File

@@ -0,0 +1,11 @@
/*
Warnings:
- A unique constraint covering the columns `[user_id,plan]` on the table `user_subscriptions` will be added. If there are existing duplicate values, this will fail.
*/
-- DropIndex
DROP INDEX "user_subscriptions_user_id_key";
-- CreateIndex
CREATE UNIQUE INDEX "user_subscriptions_user_id_plan_key" ON "user_subscriptions"("user_id", "plan");

View File

@@ -1,7 +1,7 @@
{
"name": "@affine/server",
"private": true,
"version": "0.12.0",
"version": "0.14.0",
"description": "Affine Node.js server",
"type": "module",
"bin": {
@@ -9,61 +9,61 @@
},
"scripts": {
"build": "tsc",
"start": "node --loader ts-node/esm/transpile-only.mjs --es-module-specifier-resolution node ./src/index.ts",
"start": "node --loader ts-node/esm/transpile-only.mjs ./src/index.ts",
"dev": "nodemon ./src/index.ts",
"test": "ava --concurrency 1 --serial",
"test:coverage": "c8 ava --concurrency 1 --serial",
"postinstall": "prisma generate",
"data-migration": "node --loader ts-node/esm/transpile-only.mjs --es-module-specifier-resolution node ./src/data/index.ts",
"predeploy": "yarn prisma migrate deploy && node --es-module-specifier-resolution node ./dist/data/index.js run"
"data-migration": "node --loader ts-node/esm/transpile-only.mjs ./src/data/index.ts",
"predeploy": "yarn prisma migrate deploy && node --import ./scripts/register.js ./dist/data/index.js run"
},
"dependencies": {
"@apollo/server": "^4.9.5",
"@auth/prisma-adapter": "^1.0.7",
"@aws-sdk/client-s3": "^3.499.0",
"@apollo/server": "^4.10.0",
"@auth/prisma-adapter": "^1.4.0",
"@aws-sdk/client-s3": "^3.536.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.0",
"@nestjs/apollo": "^12.0.11",
"@nestjs/common": "^10.2.10",
"@nestjs/core": "^10.2.10",
"@nestjs/event-emitter": "^2.0.3",
"@nestjs/graphql": "^12.0.11",
"@nestjs/platform-express": "^10.2.10",
"@nestjs/platform-socket.io": "^10.2.10",
"@nestjs/schedule": "^4.0.0",
"@nestjs/serve-static": "^4.0.0",
"@keyv/redis": "^2.8.4",
"@nestjs/apollo": "^12.1.0",
"@nestjs/common": "^10.3.3",
"@nestjs/core": "^10.3.3",
"@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/schedule": "^4.0.1",
"@nestjs/serve-static": "^4.0.1",
"@nestjs/throttler": "^5.0.1",
"@nestjs/websockets": "^10.2.10",
"@node-rs/argon2": "^1.5.2",
"@node-rs/crc32": "^1.7.2",
"@node-rs/jsonwebtoken": "^0.4.0",
"@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.20.0",
"@opentelemetry/exporter-prometheus": "^0.48.0",
"@opentelemetry/exporter-zipkin": "^1.20.0",
"@opentelemetry/core": "^1.21.0",
"@opentelemetry/exporter-prometheus": "^0.49.0",
"@opentelemetry/exporter-zipkin": "^1.21.0",
"@opentelemetry/host-metrics": "^0.35.0",
"@opentelemetry/instrumentation": "^0.48.0",
"@opentelemetry/instrumentation-graphql": "^0.37.0",
"@opentelemetry/instrumentation-http": "^0.48.0",
"@opentelemetry/instrumentation-ioredis": "^0.37.0",
"@opentelemetry/instrumentation-nestjs-core": "^0.34.0",
"@opentelemetry/instrumentation-socket.io": "^0.36.0",
"@opentelemetry/resources": "^1.20.0",
"@opentelemetry/sdk-metrics": "^1.20.0",
"@opentelemetry/sdk-node": "^0.48.0",
"@opentelemetry/sdk-trace-node": "^1.20.0",
"@opentelemetry/semantic-conventions": "^1.20.0",
"@prisma/client": "^5.7.1",
"@prisma/instrumentation": "^5.7.1",
"@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",
"cookie-parser": "^1.4.6",
"dotenv": "^16.3.1",
"dotenv": "^16.4.5",
"dotenv-cli": "^7.3.0",
"express": "^4.18.2",
"file-type": "^19.0.0",
"get-stream": "^8.0.1",
"get-stream": "^9.0.0",
"graphql": "^16.8.1",
"graphql-scalars": "^1.22.4",
"graphql-type-json": "^0.3.2",
@@ -71,50 +71,51 @@
"ioredis": "^5.3.2",
"keyv": "^4.5.4",
"lodash-es": "^4.17.21",
"nanoid": "^5.0.3",
"nest-commander": "^3.12.2",
"mixpanel": "^0.18.0",
"nanoid": "^5.0.6",
"nest-commander": "^3.12.5",
"nestjs-throttler-storage-redis": "^0.4.1",
"next-auth": "^4.24.5",
"nodemailer": "^6.9.7",
"nodemailer": "^6.9.10",
"on-headers": "^1.0.2",
"parse-duration": "^1.1.0",
"pretty-time": "^1.1.0",
"prisma": "^5.7.1",
"prom-client": "^15.0.0",
"reflect-metadata": "^0.2.0",
"prisma": "^5.10.2",
"prom-client": "^15.1.0",
"reflect-metadata": "^0.2.1",
"rxjs": "^7.8.1",
"semver": "^7.5.4",
"socket.io": "^4.7.2",
"stripe": "^14.5.0",
"ws": "^8.14.2",
"yjs": "^13.6.10",
"semver": "^7.6.0",
"socket.io": "^4.7.4",
"stripe": "^14.18.0",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"ws": "^8.16.0",
"yjs": "^13.6.12",
"zod": "^3.22.4"
},
"devDependencies": {
"@affine-test/kit": "workspace:*",
"@affine/storage": "workspace:*",
"@napi-rs/image": "^1.7.0",
"@nestjs/testing": "^10.2.10",
"@napi-rs/image": "^1.9.1",
"@nestjs/testing": "^10.3.3",
"@types/cookie-parser": "^1.4.6",
"@types/engine.io": "^3.1.10",
"@types/express": "^4.17.21",
"@types/graphql-upload": "^16.0.5",
"@types/graphql-upload": "^16.0.7",
"@types/keyv": "^4.2.0",
"@types/lodash-es": "^4.17.11",
"@types/node": "^20.9.3",
"@types/lodash-es": "^4.17.12",
"@types/mixpanel": "^2.14.8",
"@types/node": "^20.11.20",
"@types/nodemailer": "^6.4.14",
"@types/on-headers": "^1.0.3",
"@types/pretty-time": "^1.1.5",
"@types/sinon": "^17.0.2",
"@types/supertest": "^6.0.0",
"@types/sinon": "^17.0.3",
"@types/supertest": "^6.0.2",
"@types/ws": "^8.5.10",
"ava": "^6.0.0",
"c8": "^9.0.0",
"nodemon": "^3.0.1",
"ava": "^6.1.1",
"c8": "^9.1.0",
"nodemon": "^3.1.0",
"sinon": "^17.0.1",
"supertest": "^6.3.3",
"ts-node": "^10.9.1",
"typescript": "^5.3.2"
"supertest": "^6.3.4"
},
"ava": {
"timeout": "1m",
@@ -126,8 +127,7 @@
"--trace-sigint",
"--loader",
"ts-node/esm/transpile-only.mjs",
"--es-module-specifier-resolution",
"node"
"--es-module-specifier-resolution=node"
],
"files": [
"tests/**/*.spec.ts",
@@ -144,7 +144,8 @@
"MAILER_USER": "noreply@toeverything.info",
"MAILER_PASSWORD": "affine",
"MAILER_SENDER": "noreply@toeverything.info",
"FEATURES_EARLY_ACCESS_PREVIEW": "false"
"FEATURES_EARLY_ACCESS_PREVIEW": "false",
"DEPLOYMENT_TYPE": "affine"
}
},
"nodemonConfig": {
@@ -153,8 +154,7 @@
"nodeArgs": [
"--loader",
"ts-node/esm.mjs",
"--es-module-specifier-resolution",
"node"
"--es-module-specifier-resolution=node"
],
"ignore": [
"**/__tests__/**",

View File

@@ -13,25 +13,80 @@ model User {
id String @id @default(uuid()) @db.VarChar
name String
email String @unique
emailVerified DateTime? @map("email_verified")
// image field is for the next-auth
emailVerifiedAt DateTime? @map("email_verified")
avatarUrl String? @map("avatar_url") @db.VarChar
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
/// Not available if user signed up through OAuth providers
password String? @db.VarChar
/// Indicate whether the user finished the signup progress.
/// for example, the value will be false if user never registered and invited into a workspace by others.
registered Boolean @default(true)
accounts Account[]
sessions Session[]
features UserFeatures[]
customer UserStripeCustomer?
subscription UserSubscription?
subscriptions UserSubscription[]
invoices UserInvoice[]
workspacePermissions WorkspaceUserPermission[]
pagePermissions WorkspacePageUserPermission[]
connectedAccounts ConnectedAccount[]
sessions UserSession[]
@@map("users")
}
model ConnectedAccount {
id String @id @default(uuid()) @db.VarChar(36)
userId String @map("user_id") @db.VarChar(36)
provider String @db.VarChar
providerAccountId String @map("provider_account_id") @db.VarChar
scope String? @db.Text
accessToken String? @map("access_token") @db.Text
refreshToken String? @map("refresh_token") @db.Text
expiresAt DateTime? @map("expires_at") @db.Timestamptz(6)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(6)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([providerAccountId])
@@map("user_connected_accounts")
}
model Session {
id String @id @default(uuid()) @db.VarChar(36)
expiresAt DateTime? @map("expires_at") @db.Timestamptz(6)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
userSessions UserSession[]
@@map("multiple_users_sessions")
}
model UserSession {
id String @id @default(uuid()) @db.VarChar(36)
sessionId String @map("session_id") @db.VarChar(36)
userId String @map("user_id") @db.VarChar(36)
expiresAt DateTime? @map("expires_at") @db.Timestamptz(6)
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(6)
session Session @relation(fields: [sessionId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([sessionId, userId])
@@map("user_sessions")
}
model VerificationToken {
token String @db.VarChar(36)
type Int @db.SmallInt
credential String? @db.Text
expiresAt DateTime @db.Timestamptz(6)
@@unique([type, token])
@@map("verification_tokens")
}
model Workspace {
id String @id @default(uuid()) @db.VarChar
public Boolean
@@ -186,7 +241,7 @@ model Features {
@@map("features")
}
model Account {
model DeprecatedNextAuthAccount {
id String @id @default(cuid())
userId String @map("user_id")
type String
@@ -200,23 +255,20 @@ model Account {
id_token String? @db.Text
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
@@map("accounts")
}
model Session {
model DeprecatedNextAuthSession {
id String @id @default(cuid())
sessionToken String @unique @map("session_token")
userId String @map("user_id")
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("sessions")
}
model VerificationToken {
model DeprecatedNextAuthVerificationToken {
identifier String
token String @unique
expires DateTime
@@ -317,7 +369,7 @@ model UserStripeCustomer {
model UserSubscription {
id Int @id @default(autoincrement()) @db.Integer
userId String @unique @map("user_id") @db.VarChar(36)
userId String @map("user_id") @db.VarChar(36)
plan String @db.VarChar(20)
// yearly/monthly
recurring String @db.VarChar(20)
@@ -343,6 +395,7 @@ model UserSubscription {
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(6)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([userId, plan])
@@map("user_subscriptions")
}

View File

@@ -1,37 +0,0 @@
import userA from '@affine-test/fixtures/userA.json' assert { type: 'json' };
import { hash } from '@node-rs/argon2';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
await prisma.user.create({
data: {
...userA,
password: await hash(userA.password),
features: {
create: {
reason: 'created by api sign up',
activated: true,
feature: {
connect: {
feature_version: {
feature: 'free_plan_v1',
version: 1,
},
},
},
},
},
},
});
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async e => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});

View File

@@ -0,0 +1,11 @@
import { create, createEsmHooks } from 'ts-node';
const service = create({
experimentalSpecifierResolution: 'node',
transpileOnly: true,
logError: true,
skipProject: true,
});
const hooks = createEsmHooks(service);
export const resolve = hooks.resolve;

View File

@@ -0,0 +1,4 @@
import { register } from 'node:module';
import { pathToFileURL } from 'node:url';
register('./scripts/loader.js', pathToFileURL('./'));

View File

@@ -1,7 +1,10 @@
import { execSync } from 'node:child_process';
import { generateKeyPairSync } from 'node:crypto';
import fs from 'node:fs';
import path from 'node:path';
import { parse } from 'dotenv';
const SELF_HOST_CONFIG_DIR = '/root/.affine/config';
/**
* @type {Array<{ from: string; to?: string, modifier?: (content: string): string }>}
@@ -36,17 +39,35 @@ function prepare() {
});
}
}
// make the default .env
if (to === '.env') {
const dotenvFile = fs.readFileSync(targetFilePath, 'utf-8');
const envs = parse(dotenvFile);
// generate a new private key
if (!envs.AFFINE_PRIVATE_KEY) {
const privateKey = generateKeyPairSync('ec', {
namedCurve: 'prime256v1',
}).privateKey.export({
type: 'sec1',
format: 'pem',
});
fs.writeFileSync(
targetFilePath,
`AFFINE_PRIVATE_KEY=${privateKey}\n` + dotenvFile
);
}
}
}
}
function runPredeployScript() {
console.log('running predeploy script.');
execSync('yarn predeploy', {
env: {
...process.env,
NODE_OPTIONS:
(process.env.NODE_OPTIONS ?? '') + ' --import ./dist/prelude.js',
},
encoding: 'utf-8',
env: process.env,
stdio: 'inherit',
});
}

View File

@@ -1,11 +1,13 @@
import { Controller, Get } from '@nestjs/common';
import { Public } from './core/auth';
import { Config } from './fundamentals/config';
@Controller('/')
export class AppController {
constructor(private readonly config: Config) {}
@Public()
@Get()
info() {
return {

View File

@@ -1,50 +1,49 @@
import { join } from 'node:path';
import { Logger, Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
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 { AuthModule } from './core/auth';
import { AuthGuard, AuthModule } from './core/auth';
import { ADD_ENABLED_FEATURES, ServerConfigModule } from './core/config';
import { DocModule } from './core/doc';
import { FeatureModule } from './core/features';
import { QuotaModule } from './core/quota';
import { StorageModule } from './core/storage';
import { SyncModule } from './core/sync';
import { UsersModule } from './core/users';
import { UserModule } from './core/user';
import { WorkspaceModule } from './core/workspaces';
import { getOptionalModuleMetadata } from './fundamentals';
import { CacheInterceptor, CacheModule } from './fundamentals/cache';
import {
type AvailablePlugins,
Config,
ConfigModule,
} from './fundamentals/config';
import type { AvailablePlugins } from './fundamentals/config';
import { Config, ConfigModule } from './fundamentals/config';
import { EventModule } from './fundamentals/event';
import { GqlModule } from './fundamentals/graphql';
import { HelpersModule } from './fundamentals/helpers';
import { MailModule } from './fundamentals/mailer';
import { MetricsModule } from './fundamentals/metrics';
import { MutexModule } from './fundamentals/mutex';
import { PrismaModule } from './fundamentals/prisma';
import { SessionModule } from './fundamentals/session';
import { StorageProviderModule } from './fundamentals/storage';
import { RateLimiterModule } from './fundamentals/throttler';
import { WebSocketModule } from './fundamentals/websocket';
import { pluginsMap } from './plugins';
import { REGISTERED_PLUGINS } from './plugins';
export const FunctionalityModules = [
ConfigModule.forRoot(),
ScheduleModule.forRoot(),
EventModule,
CacheModule,
MutexModule,
PrismaModule,
MetricsModule,
RateLimiterModule,
SessionModule,
MailModule,
StorageProviderModule,
HelpersModule,
];
export class AppModuleBuilder {
@@ -109,6 +108,10 @@ export class AppModuleBuilder {
provide: APP_INTERCEPTOR,
useClass: CacheInterceptor,
},
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
imports: this.modules,
controllers: this.config.isSelfhosted ? [] : [AppController],
@@ -141,7 +144,7 @@ function buildAppModule() {
WebSocketModule,
GqlModule,
StorageModule,
UsersModule,
UserModule,
WorkspaceModule,
FeatureModule,
QuotaModule
@@ -157,7 +160,7 @@ function buildAppModule() {
// plugin modules
AFFiNE.plugins.enabled.forEach(name => {
const plugin = pluginsMap.get(name as AvailablePlugins);
const plugin = REGISTERED_PLUGINS.get(name as AvailablePlugins);
if (!plugin) {
throw new Error(`Unknown plugin ${name}`);
}

View File

@@ -43,5 +43,12 @@ export async function createApp() {
app.useWebSocketAdapter(adapter);
}
if (AFFiNE.isSelfhosted && AFFiNE.telemetry.enabled) {
const mixpanel = await import('mixpanel');
mixpanel.init(AFFiNE.telemetry.token).track('selfhost-server-started', {
version: AFFiNE.version,
});
}
return app;
}

View File

@@ -7,12 +7,10 @@ AFFiNE.ENV_MAP = {
DATABASE_URL: 'db.url',
ENABLE_CAPTCHA: ['auth.captcha.enable', 'boolean'],
CAPTCHA_TURNSTILE_SECRET: ['auth.captcha.turnstile.secret', 'string'],
OAUTH_GOOGLE_ENABLED: ['auth.oauthProviders.google.enabled', 'boolean'],
OAUTH_GOOGLE_CLIENT_ID: 'auth.oauthProviders.google.clientId',
OAUTH_GOOGLE_CLIENT_SECRET: 'auth.oauthProviders.google.clientSecret',
OAUTH_GITHUB_ENABLED: ['auth.oauthProviders.github.enabled', 'boolean'],
OAUTH_GITHUB_CLIENT_ID: 'auth.oauthProviders.github.clientId',
OAUTH_GITHUB_CLIENT_SECRET: 'auth.oauthProviders.github.clientSecret',
OAUTH_GOOGLE_CLIENT_ID: 'plugins.oauth.providers.google.clientId',
OAUTH_GOOGLE_CLIENT_SECRET: 'plugins.oauth.providers.google.clientSecret',
OAUTH_GITHUB_CLIENT_ID: 'plugins.oauth.providers.github.clientId',
OAUTH_GITHUB_CLIENT_SECRET: 'plugins.oauth.providers.github.clientSecret',
MAILER_HOST: 'mailer.host',
MAILER_PORT: ['mailer.port', 'int'],
MAILER_USER: 'mailer.auth.user',

View File

@@ -39,7 +39,16 @@ if (env.R2_OBJECT_STORAGE_ACCOUNT_ID) {
}
AFFiNE.plugins.use('redis');
AFFiNE.plugins.use('payment');
AFFiNE.plugins.use('payment', {
stripe: {
keys: {
// fake the key to ensure the server generate full GraphQL Schema even env vars are not set
APIKey: '1',
webhookKey: '1',
},
},
});
AFFiNE.plugins.use('oauth');
if (AFFiNE.deploy) {
AFFiNE.mailer = {

View File

@@ -52,6 +52,18 @@ AFFiNE.port = 3010;
// /* The metrics will be available at `http://localhost:9464/metrics` with [Prometheus] format exported */
// AFFiNE.metrics.enabled = true;
//
// /* Authentication Settings */
// /* User Signup password limitation */
// AFFiNE.auth.password = {
// minLength: 8,
// maxLength: 20,
// };
//
// /* How long the login session would last by default */
// AFFiNE.auth.session = {
// ttl: 15 * 24 * 60 * 60, // 15 days
// };
//
// /* GraphQL configurations that control the behavior of the Apollo Server behind */
// /* @see https://www.apollographql.com/docs/apollo-server/api/apollo-server */
// AFFiNE.graphql = {
@@ -84,15 +96,15 @@ AFFiNE.port = 3010;
// /* Redis Plugin */
// /* Provide caching and session storing backed by Redis. */
// /* Useful when you deploy AFFiNE server in a cluster. */
AFFiNE.plugins.use('redis', {
/* override options */
});
// AFFiNE.plugins.use('redis', {
// /* override options */
// });
//
//
// /* Payment Plugin */
AFFiNE.plugins.use('payment', {
stripe: { keys: {}, apiVersion: '2023-10-16' },
});
// AFFiNE.plugins.use('payment', {
// stripe: { keys: {}, apiVersion: '2023-10-16' },
// });
//
//
// /* Cloudflare R2 Plugin */
@@ -115,3 +127,27 @@ AFFiNE.plugins.use('payment', {
// /* Update the provider of storages */
// AFFiNE.storage.storages.blob.provider = 'r2';
// AFFiNE.storage.storages.avatar.provider = 'r2';
//
// /* OAuth Plugin */
// AFFiNE.plugins.use('oauth', {
// providers: {
// github: {
// clientId: '',
// clientSecret: '',
// // See https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps
// args: {
// scope: 'user',
// },
// },
// google: {
// clientId: '',
// clientSecret: '',
// args: {
// // See https://developers.google.com/identity/protocols/oauth2
// scope: 'openid email profile',
// promot: 'select_account',
// access_type: 'offline',
// },
// },
// },
// });

View File

@@ -0,0 +1,193 @@
import { randomUUID } from 'node:crypto';
import {
BadRequestException,
Body,
Controller,
Get,
Header,
HttpStatus,
Post,
Query,
Req,
Res,
} from '@nestjs/common';
import type { Request, Response } from 'express';
import { PaymentRequiredException, URLHelper } from '../../fundamentals';
import { UserService } from '../user';
import { validators } from '../utils/validators';
import { CurrentUser } from './current-user';
import { Public } from './guard';
import { AuthService, parseAuthUserSeqNum } from './service';
import { TokenService, TokenType } from './token';
class SignInCredential {
email!: string;
password?: string;
}
@Controller('/api/auth')
export class AuthController {
constructor(
private readonly url: URLHelper,
private readonly auth: AuthService,
private readonly user: UserService,
private readonly token: TokenService
) {}
@Public()
@Post('/sign-in')
@Header('content-type', 'application/json')
async signIn(
@Req() req: Request,
@Res() res: Response,
@Body() credential: SignInCredential,
@Query('redirect_uri') redirectUri = this.url.home
) {
validators.assertValidEmail(credential.email);
const canSignIn = await this.auth.canSignIn(credential.email);
if (!canSignIn) {
throw new PaymentRequiredException(
`You don't have early access permission\nVisit https://community.affine.pro/c/insider-general/ for more information`
);
}
if (credential.password) {
const user = await this.auth.signIn(
credential.email,
credential.password
);
await this.auth.setCookie(req, res, user);
res.status(HttpStatus.OK).send(user);
} else {
// send email magic link
const user = await this.user.findUserByEmail(credential.email);
const result = await this.sendSignInEmail(
{ email: credential.email, signUp: !user },
redirectUri
);
if (result.rejected.length) {
throw new Error('Failed to send sign-in email.');
}
res.status(HttpStatus.OK).send({
email: credential.email,
});
}
}
async sendSignInEmail(
{ email, signUp }: { email: string; signUp: boolean },
redirectUri: string
) {
const token = await this.token.createToken(TokenType.SignIn, email);
const magicLink = this.url.link('/api/auth/magic-link', {
token,
email,
redirect_uri: redirectUri,
});
const result = await this.auth.sendSignInEmail(email, magicLink, signUp);
return result;
}
@Get('/sign-out')
async signOut(
@Req() req: Request,
@Res() res: Response,
@Query('redirect_uri') redirectUri?: string
) {
const session = await this.auth.signOut(
req.cookies[AuthService.sessionCookieName],
parseAuthUserSeqNum(req.headers[AuthService.authUserSeqHeaderName])
);
if (session) {
res.cookie(AuthService.sessionCookieName, session.id, {
expires: session.expiresAt ?? void 0, // expiredAt is `string | null`
...this.auth.cookieOptions,
});
} else {
res.clearCookie(AuthService.sessionCookieName);
}
if (redirectUri) {
return this.url.safeRedirect(res, redirectUri);
} else {
return res.send(null);
}
}
@Public()
@Get('/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
) {
if (!token || !email) {
throw new BadRequestException('Invalid Sign-in mail Token');
}
email = decodeURIComponent(email);
token = decodeURIComponent(token);
validators.assertValidEmail(email);
const valid = await this.token.verifyToken(TokenType.SignIn, token, {
credential: email,
});
if (!valid) {
throw new BadRequestException('Invalid Sign-in mail Token');
}
const user = await this.user.fulfillUser(email, {
emailVerifiedAt: new Date(),
registered: true,
});
await this.auth.setCookie(req, res, user);
return this.url.safeRedirect(res, redirectUri);
}
@Public()
@Get('/session')
async currentSessionUser(@CurrentUser() user?: CurrentUser) {
return {
user,
};
}
@Public()
@Get('/sessions')
async currentSessionUsers(@Req() req: Request) {
const token = req.cookies[AuthService.sessionCookieName];
if (!token) {
return {
users: [],
};
}
return {
users: await this.auth.getUserList(token),
};
}
@Public()
@Get('/challenge')
async challenge() {
// TODO: impl in following PR
return {
challenge: randomUUID(),
resource: randomUUID(),
};
}
}

View File

@@ -0,0 +1,55 @@
import type { ExecutionContext } from '@nestjs/common';
import { createParamDecorator } from '@nestjs/common';
import { User } from '@prisma/client';
import { getRequestResponseFromContext } from '../../fundamentals';
function getUserFromContext(context: ExecutionContext) {
return getRequestResponseFromContext(context).req.user;
}
/**
* Used to fetch current user from the request context.
*
* > The user may be undefined if authorization token or session cookie is not provided.
*
* @example
*
* ```typescript
* // Graphql Query
* \@Query(() => UserType)
* user(@CurrentUser() user: CurrentUser) {
* return user;
* }
* ```
*
* ```typescript
* // HTTP Controller
* \@Get('/user')
* user(@CurrentUser() user: CurrentUser) {
* return user;
* }
* ```
*
* ```typescript
* // for public apis
* \@Public()
* \@Get('/session')
* session(@currentUser() user?: CurrentUser) {
* return user
* }
* ```
*/
// interface and variable don't conflict
// eslint-disable-next-line no-redeclare
export const CurrentUser = createParamDecorator(
(_: unknown, context: ExecutionContext) => {
return getUserFromContext(context);
}
);
export interface CurrentUser
extends Pick<User, 'id' | 'email' | 'avatarUrl' | 'name'> {
hasPassword: boolean | null;
emailVerified: boolean;
}

View File

@@ -1,67 +1,62 @@
import type { CanActivate, ExecutionContext } from '@nestjs/common';
import type {
CanActivate,
ExecutionContext,
OnModuleInit,
} from '@nestjs/common';
import {
createParamDecorator,
Inject,
Injectable,
SetMetadata,
UnauthorizedException,
UseGuards,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { PrismaClient } from '@prisma/client';
import type { NextAuthOptions } from 'next-auth';
import { AuthHandler } from 'next-auth/core';
import { ModuleRef, Reflector } from '@nestjs/core';
import { getRequestResponseFromContext } from '../../fundamentals';
import { NextAuthOptionsProvide } from './next-auth-options';
import { AuthService } from './service';
import { AuthService, parseAuthUserSeqNum } from './service';
export function getUserFromContext(context: ExecutionContext) {
return getRequestResponseFromContext(context).req.user;
function extractTokenFromHeader(authorization: string) {
if (!/^Bearer\s/i.test(authorization)) {
return;
}
/**
* Used to fetch current user from the request context.
*
* > The user may be undefined if authorization token is not provided.
*
* @example
*
* ```typescript
* // Graphql Query
* \@Query(() => UserType)
* user(@CurrentUser() user?: User) {
* return user;
* }
* ```
*
* ```typescript
* // HTTP Controller
* \@Get('/user)
* user(@CurrentUser() user?: User) {
* return user;
* }
* ```
*/
export const CurrentUser = createParamDecorator(
(_: unknown, context: ExecutionContext) => {
return getUserFromContext(context);
return authorization.substring(7);
}
);
@Injectable()
class AuthGuard implements CanActivate {
export class AuthGuard implements CanActivate, OnModuleInit {
private auth!: AuthService;
constructor(
@Inject(NextAuthOptionsProvide)
private readonly nextAuthOptions: NextAuthOptions,
private readonly auth: AuthService,
private readonly prisma: PrismaClient,
private readonly ref: ModuleRef,
private readonly reflector: Reflector
) {}
onModuleInit() {
this.auth = this.ref.get(AuthService, { strict: false });
}
async canActivate(context: ExecutionContext) {
const { req, res } = getRequestResponseFromContext(context);
const token = req.headers.authorization;
const { req } = getRequestResponseFromContext(context);
// check cookie
let sessionToken: string | undefined =
req.cookies[AuthService.sessionCookieName];
if (!sessionToken && req.headers.authorization) {
sessionToken = extractTokenFromHeader(req.headers.authorization);
}
if (sessionToken) {
const userSeq = parseAuthUserSeqNum(
req.headers[AuthService.authUserSeqHeaderName]
);
const user = await this.auth.getUser(sessionToken, userSeq);
if (user) {
req.user = user;
}
}
// api is public
const isPublic = this.reflector.get<boolean>(
@@ -69,63 +64,15 @@ class AuthGuard implements CanActivate {
context.getHandler()
);
// FIXME(@forehalo): @Publicable() is duplicated with @CurrentUser() user?: User
// ^ optional
// we can prefetch user session in each request even before this `Guard`
// api can be public, but if user is logged in, we can get user info
const isPublicable = this.reflector.get<boolean>(
'isPublicable',
context.getHandler()
);
if (isPublic) {
return true;
} else if (!token) {
if (!req.cookies) {
return isPublicable;
}
const session = await AuthHandler({
req: {
cookies: req.cookies,
action: 'session',
method: 'GET',
headers: req.headers,
},
options: this.nextAuthOptions,
});
const { body = {}, cookies, status = 200 } = session;
if (!body && !isPublicable) {
if (!req.user) {
throw new UnauthorizedException('You are not signed in.');
}
// @ts-expect-error body is user here
req.user = body.user;
if (cookies && res) {
for (const cookie of cookies) {
res.cookie(cookie.name, cookie.value, cookie.options);
}
}
return Boolean(
status === 200 &&
typeof body !== 'string' &&
// ignore body if api is publicable
(Object.keys(body).length || isPublicable)
);
} else {
const [type, jwt] = token.split(' ') ?? [];
if (type === 'Bearer') {
const claims = await this.auth.verify(jwt);
req.user = await this.prisma.user.findUnique({
where: { id: claims.id },
});
return !!req.user;
}
}
return false;
return true;
}
}
@@ -140,7 +87,7 @@ class AuthGuard implements CanActivate {
* ```typescript
* \@Auth()
* \@Query(() => UserType)
* user(@CurrentUser() user: User) {
* user(@CurrentUser() user: CurrentUser) {
* return user;
* }
* ```
@@ -151,5 +98,3 @@ export const Auth = () => {
// api is public accessible
export const Public = () => SetMetadata('isPublic', true);
// api is public accessible, but if user is logged in, we can get user info
export const Publicable = () => SetMetadata('isPublicable', true);

View File

@@ -1,18 +1,21 @@
import { Global, Module } from '@nestjs/common';
import { Module } from '@nestjs/common';
import { NextAuthController } from './next-auth.controller';
import { NextAuthOptionsProvider } from './next-auth-options';
import { FeatureModule } from '../features';
import { UserModule } from '../user';
import { AuthController } from './controller';
import { AuthResolver } from './resolver';
import { AuthService } from './service';
import { TokenService, TokenType } from './token';
@Global()
@Module({
providers: [AuthService, AuthResolver, NextAuthOptionsProvider],
exports: [AuthService, NextAuthOptionsProvider],
controllers: [NextAuthController],
imports: [FeatureModule, UserModule],
providers: [AuthService, AuthResolver, TokenService],
exports: [AuthService],
controllers: [AuthController],
})
export class AuthModule {}
export * from './guard';
export { TokenType } from './resolver';
export { AuthService };
export { ClientTokenType } from './resolver';
export { AuthService, TokenService, TokenType };
export * from './current-user';

View File

@@ -1,284 +0,0 @@
import { PrismaAdapter } from '@auth/prisma-adapter';
import { FactoryProvider, Logger } from '@nestjs/common';
import { verify } from '@node-rs/argon2';
import { PrismaClient } from '@prisma/client';
import { assign, omit } from 'lodash-es';
import { NextAuthOptions } from 'next-auth';
import Credentials from 'next-auth/providers/credentials';
import Email from 'next-auth/providers/email';
import Github from 'next-auth/providers/github';
import Google from 'next-auth/providers/google';
import { Config, MailService, SessionService } from '../../fundamentals';
import { FeatureType } from '../features';
import { Quota_FreePlanV1_1 } from '../quota';
import {
decode,
encode,
sendVerificationRequest,
SendVerificationRequestParams,
} from './utils';
export const NextAuthOptionsProvide = Symbol('NextAuthOptions');
const TrustedProviders = ['google'];
export const NextAuthOptionsProvider: FactoryProvider<NextAuthOptions> = {
provide: NextAuthOptionsProvide,
useFactory(
config: Config,
prisma: PrismaClient,
mailer: MailService,
session: SessionService
) {
const logger = new Logger('NextAuth');
const prismaAdapter = PrismaAdapter(prisma);
// createUser exists in the adapter
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const createUser = prismaAdapter.createUser!.bind(prismaAdapter);
prismaAdapter.createUser = async data => {
const userData = {
name: data.name,
email: data.email,
avatarUrl: '',
emailVerified: data.emailVerified,
features: {
create: {
reason: 'created by email sign up',
activated: true,
feature: {
connect: {
feature_version: Quota_FreePlanV1_1,
},
},
},
},
};
if (data.email && !data.name) {
userData.name = data.email.split('@')[0];
}
if (data.image) {
userData.avatarUrl = data.image;
}
return createUser(userData);
};
// linkAccount exists in the adapter
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const linkAccount = prismaAdapter.linkAccount!.bind(prismaAdapter);
prismaAdapter.linkAccount = async account => {
// google account must be a verified email
if (TrustedProviders.includes(account.provider)) {
await prisma.user.update({
where: {
id: account.userId,
},
data: {
emailVerified: new Date(),
},
});
}
return linkAccount(account) as Promise<void>;
};
// getUser exists in the adapter
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const getUser = prismaAdapter.getUser!.bind(prismaAdapter)!;
prismaAdapter.getUser = async id => {
const result = await getUser(id);
if (result) {
// @ts-expect-error Third part library type mismatch
result.image = result.avatarUrl;
// @ts-expect-error Third part library type mismatch
result.hasPassword = Boolean(result.password);
}
return result;
};
prismaAdapter.createVerificationToken = async data => {
await session.set(
`${data.identifier}:${data.token}`,
Date.now() + session.sessionTtl
);
return data;
};
prismaAdapter.useVerificationToken = async ({ identifier, token }) => {
const expires = await session.get(`${identifier}:${token}`);
if (expires) {
return { identifier, token, expires: new Date(expires) };
} else {
return null;
}
};
const nextAuthOptions: NextAuthOptions = {
providers: [],
adapter: prismaAdapter,
debug: !config.node.prod,
session: {
strategy: 'database',
},
logger: {
debug(code, metadata) {
logger.debug(`${code}: ${JSON.stringify(metadata)}`);
},
error(code, metadata) {
if (metadata instanceof Error) {
// @ts-expect-error assign code to error
metadata.code = code;
logger.error(metadata);
} else if (metadata.error instanceof Error) {
assign(metadata.error, omit(metadata, 'error'), { code });
logger.error(metadata.error);
}
},
warn(code) {
logger.warn(code);
},
},
};
if (config.mailer && mailer) {
nextAuthOptions.providers.push(
// @ts-expect-error esm interop issue
Email.default({
server: config.mailer,
from: config.mailer.from,
sendVerificationRequest: (params: SendVerificationRequestParams) =>
sendVerificationRequest(config, logger, mailer, session, params),
})
);
}
nextAuthOptions.providers.push(
// @ts-expect-error esm interop issue
Credentials.default({
name: 'Password',
credentials: {
email: {
label: 'Email',
type: 'text',
placeholder: 'torvalds@osdl.org',
},
password: { label: 'Password', type: 'password' },
},
async authorize(
credentials:
| Record<'email' | 'password' | 'hashedPassword', string>
| undefined
) {
if (!credentials) {
return null;
}
const { password, hashedPassword } = credentials;
if (!password || !hashedPassword) {
return null;
}
if (!(await verify(hashedPassword, password))) {
return null;
}
return credentials;
},
})
);
if (config.auth.oauthProviders.github) {
nextAuthOptions.providers.push(
// @ts-expect-error esm interop issue
Github.default({
clientId: config.auth.oauthProviders.github.clientId,
clientSecret: config.auth.oauthProviders.github.clientSecret,
allowDangerousEmailAccountLinking: true,
})
);
}
if (config.auth.oauthProviders.google?.enabled) {
nextAuthOptions.providers.push(
// @ts-expect-error esm interop issue
Google.default({
clientId: config.auth.oauthProviders.google.clientId,
clientSecret: config.auth.oauthProviders.google.clientSecret,
checks: 'nonce',
allowDangerousEmailAccountLinking: true,
authorization: {
params: { scope: 'openid email profile', prompt: 'select_account' },
},
})
);
}
nextAuthOptions.jwt = {
encode: async ({ token, maxAge }) =>
encode(config, prisma, token, maxAge),
decode: async ({ token }) => decode(config, token),
};
nextAuthOptions.secret ??= config.auth.nextAuthSecret;
nextAuthOptions.callbacks = {
session: async ({ session, user, token }) => {
if (session.user) {
if (user) {
// @ts-expect-error Third part library type mismatch
session.user.id = user.id;
// @ts-expect-error Third part library type mismatch
session.user.image = user.image ?? user.avatarUrl;
// @ts-expect-error Third part library type mismatch
session.user.emailVerified = user.emailVerified;
// @ts-expect-error Third part library type mismatch
session.user.hasPassword = Boolean(user.password);
} else {
// technically the sub should be the same as id
// @ts-expect-error Third part library type mismatch
session.user.id = token.sub;
// @ts-expect-error Third part library type mismatch
session.user.emailVerified = token.emailVerified;
// @ts-expect-error Third part library type mismatch
session.user.hasPassword = token.hasPassword;
}
if (token && token.picture) {
session.user.image = token.picture;
}
}
return session;
},
signIn: async ({ profile, user }) => {
if (!config.featureFlags.earlyAccessPreview) {
return true;
}
const email = profile?.email ?? user.email;
if (email) {
// FIXME: cannot inject FeatureManagementService here
// it will cause prisma.account to be undefined
// then prismaAdapter.getUserByAccount will throw error
if (email.endsWith('@toeverything.info')) return true;
return prisma.userFeatures
.count({
where: {
user: {
email: {
equals: email,
mode: 'insensitive',
},
},
feature: {
feature: FeatureType.EarlyAccess,
},
activated: true,
},
})
.then(count => count > 0);
}
return false;
},
redirect({ url }) {
return url;
},
};
nextAuthOptions.pages = {
newUser: '/auth/onboarding',
};
return nextAuthOptions;
},
inject: [Config, PrismaClient, MailService, SessionService],
};

View File

@@ -1,411 +0,0 @@
import { URLSearchParams } from 'node:url';
import {
All,
BadRequestException,
Controller,
Get,
Inject,
Logger,
Next,
NotFoundException,
Query,
Req,
Res,
UseGuards,
} from '@nestjs/common';
import { hash, verify } from '@node-rs/argon2';
import { PrismaClient, type User } from '@prisma/client';
import type { NextFunction, Request, Response } from 'express';
import { pick } from 'lodash-es';
import { nanoid } from 'nanoid';
import type { AuthAction, CookieOption, NextAuthOptions } from 'next-auth';
import { AuthHandler } from 'next-auth/core';
import {
AuthThrottlerGuard,
Config,
metrics,
SessionService,
Throttle,
} from '../../fundamentals';
import { NextAuthOptionsProvide } from './next-auth-options';
import { AuthService } from './service';
const BASE_URL = '/api/auth/';
const DEFAULT_SESSION_EXPIRE_DATE = 2592000 * 1000; // 30 days
@Controller(BASE_URL)
export class NextAuthController {
private readonly callbackSession;
private readonly logger = new Logger('NextAuthController');
constructor(
readonly config: Config,
readonly prisma: PrismaClient,
private readonly authService: AuthService,
@Inject(NextAuthOptionsProvide)
private readonly nextAuthOptions: NextAuthOptions,
private readonly session: SessionService
) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.callbackSession = nextAuthOptions.callbacks!.session;
}
@UseGuards(AuthThrottlerGuard)
@Throttle({
default: {
limit: 60,
ttl: 60,
},
})
@Get('/challenge')
async getChallenge(@Res() res: Response) {
const challenge = nanoid();
const resource = nanoid();
await this.session.set(challenge, resource, 5 * 60 * 1000);
res.json({ challenge, resource });
}
@UseGuards(AuthThrottlerGuard)
@Throttle({
default: {
limit: 60,
ttl: 60,
},
})
@All('*')
async auth(
@Req() req: Request,
@Res() res: Response,
@Query() query: Record<string, any>,
@Next() next: NextFunction
) {
if (req.path === '/api/auth/signin' && req.method === 'GET') {
const query = req.query
? // @ts-expect-error req.query is satisfy with the Record<string, any>
`?${new URLSearchParams(req.query).toString()}`
: '';
res.redirect(`/signin${query}`);
return;
}
const [action, providerId] = req.url // start with request url
.slice(BASE_URL.length) // make relative to baseUrl
.replace(/\?.*/, '') // remove query part, use only path part
.split('/') as [AuthAction, string]; // as array of strings;
metrics.auth.counter('call_counter').add(1, { action, providerId });
const credentialsSignIn =
req.method === 'POST' && providerId === 'credentials';
let userId: string | undefined;
if (credentialsSignIn) {
const { email } = req.body;
if (email) {
const user = await this.prisma.user.findFirst({
where: {
email: {
equals: email,
mode: 'insensitive',
},
},
});
if (!user) {
req.statusCode = 401;
req.statusMessage = 'User not found';
req.body = null;
throw new NotFoundException(`User not found`);
} else {
userId = user.id;
req.body = {
...req.body,
name: user.name,
email: user.email,
image: user.avatarUrl,
hashedPassword: user.password,
};
}
}
}
const options = this.nextAuthOptions;
if (req.method === 'POST' && action === 'session') {
if (typeof req.body !== 'object' || typeof req.body.data !== 'object') {
metrics.auth
.counter('call_fails_counter')
.add(1, { reason: 'invalid_session_data' });
throw new BadRequestException(`Invalid new session data`);
}
const user = await this.updateSession(req, req.body.data);
// callbacks.session existed
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
options.callbacks!.session = ({ session }) => {
return {
user: {
...pick(user, 'id', 'name', 'email'),
image: user.avatarUrl,
hasPassword: !!user.password,
},
expires: session.expires,
};
};
} else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
options.callbacks!.session = this.callbackSession;
}
if (
this.config.auth.captcha.enable &&
req.method === 'POST' &&
action === 'signin' &&
// TODO: add credentials support in frontend
['email'].includes(providerId)
) {
const isVerified = await this.verifyChallenge(req, res);
if (!isVerified) return;
}
const { status, headers, body, redirect, cookies } = await AuthHandler({
req: {
body: req.body,
query: query,
method: req.method,
action,
providerId,
error: query.error ?? providerId,
cookies: req.cookies,
},
options,
});
if (headers) {
for (const { key, value } of headers) {
res.setHeader(key, value);
}
}
if (cookies) {
for (const cookie of cookies) {
res.cookie(cookie.name, cookie.value, cookie.options);
}
}
let nextAuthTokenCookie: (CookieOption & { value: string }) | undefined;
const secureCookiePrefix = '__Secure-';
const sessionCookieName = `next-auth.session-token`;
// next-auth credentials login only support JWT strategy
// https://next-auth.js.org/configuration/providers/credentials
// let's store the session token in the database
if (
credentialsSignIn &&
(nextAuthTokenCookie = cookies?.find(
({ name }) =>
name === sessionCookieName ||
name === `${secureCookiePrefix}${sessionCookieName}`
))
) {
const cookieExpires = new Date();
cookieExpires.setTime(
cookieExpires.getTime() + DEFAULT_SESSION_EXPIRE_DATE
);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await this.nextAuthOptions.adapter!.createSession!({
sessionToken: nextAuthTokenCookie.value,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
userId: userId!,
expires: cookieExpires,
});
}
if (redirect?.endsWith('api/auth/error?error=AccessDenied')) {
this.logger.log(`Early access redirect headers: ${req.headers}`);
metrics.auth
.counter('call_fails_counter')
.add(1, { reason: 'no_early_access_permission' });
if (
!req.headers?.referer ||
checkUrlOrigin(req.headers.referer, 'https://accounts.google.com')
) {
res.redirect('https://community.affine.pro/c/insider-general/');
} else {
res.status(403);
res.json({
url: 'https://community.affine.pro/c/insider-general/',
error: `You don't have early access permission`,
});
}
return;
}
if (status) {
res.status(status);
}
if (redirect) {
if (providerId === 'credentials') {
res.send(JSON.stringify({ ok: true, url: redirect }));
} else if (
action === 'callback' ||
action === 'error' ||
(providerId !== 'credentials' &&
// login in the next-auth page, /api/auth/signin, auto redirect.
// otherwise, return the json value to allow frontend to handle the redirect.
req.headers?.referer?.includes?.('/api/auth/signin'))
) {
res.redirect(redirect);
} else {
res.json({ url: redirect });
}
} else if (typeof body === 'string') {
res.send(body);
} else if (body && typeof body === 'object') {
res.json(body);
} else {
next();
}
}
private async updateSession(
req: Request,
newSession: Partial<Omit<User, 'id'>> & { oldPassword?: string }
): Promise<User> {
const { name, email, password, oldPassword } = newSession;
if (!name && !email && !password) {
throw new BadRequestException(`Invalid new session data`);
}
if (password) {
const user = await this.verifyUserFromRequest(req);
const { password: userPassword } = user;
if (!oldPassword) {
if (userPassword) {
throw new BadRequestException(
`Old password is required to update password`
);
}
} else {
if (!userPassword) {
throw new BadRequestException(`No existed password`);
}
if (await verify(userPassword, oldPassword)) {
await this.prisma.user.update({
where: {
id: user.id,
},
data: {
...pick(newSession, 'email', 'name'),
password: await hash(password),
},
});
}
}
return user;
} else {
const user = await this.verifyUserFromRequest(req);
return this.prisma.user.update({
where: {
id: user.id,
},
data: pick(newSession, 'name', 'email'),
});
}
}
private async verifyChallenge(req: Request, res: Response): Promise<boolean> {
const challenge = req.query?.challenge;
if (typeof challenge === 'string' && challenge) {
const resource = await this.session.get(challenge);
if (!resource) {
this.rejectResponse(res, 'Invalid Challenge');
return false;
}
const isChallengeVerified =
await this.authService.verifyChallengeResponse(
req.query?.token,
resource
);
this.logger.debug(
`Challenge: ${challenge}, Resource: ${resource}, Response: ${req.query?.token}, isChallengeVerified: ${isChallengeVerified}`
);
if (!isChallengeVerified) {
this.rejectResponse(res, 'Invalid Challenge Response');
return false;
}
} else {
const isTokenVerified = await this.authService.verifyCaptchaToken(
req.query?.token,
req.headers['CF-Connecting-IP'] as string
);
if (!isTokenVerified) {
this.rejectResponse(res, 'Invalid Captcha Response');
return false;
}
}
return true;
}
private async verifyUserFromRequest(req: Request): Promise<User> {
const token = req.headers.authorization;
if (!token) {
const session = await AuthHandler({
req: {
cookies: req.cookies,
action: 'session',
method: 'GET',
headers: req.headers,
},
options: this.nextAuthOptions,
});
const { body } = session;
// @ts-expect-error check if body.user exists
if (body && body.user && body.user.id) {
const user = await this.prisma.user.findUnique({
where: {
// @ts-expect-error body.user.id exists
id: body.user.id,
},
});
if (user) {
return user;
}
}
} else {
const [type, jwt] = token.split(' ') ?? [];
if (type === 'Bearer') {
const claims = await this.authService.verify(jwt);
const user = await this.prisma.user.findUnique({
where: { id: claims.id },
});
if (user) {
return user;
}
}
}
throw new BadRequestException(`User not found`);
}
rejectResponse(res: Response, error: string, status = 400) {
res.status(status);
res.json({
url: `${this.config.baseUrl}/api/auth/error?${new URLSearchParams({
error,
}).toString()}`,
error,
});
}
}
const checkUrlOrigin = (url: string, origin: string) => {
try {
return new URL(url).origin === origin;
} catch (e) {
return false;
}
};

View File

@@ -10,24 +10,23 @@ import {
Mutation,
ObjectType,
Parent,
Query,
ResolveField,
Resolver,
} from '@nestjs/graphql';
import type { Request } from 'express';
import { nanoid } from 'nanoid';
import type { Request, Response } from 'express';
import {
CloudThrottlerGuard,
Config,
SessionService,
Throttle,
} from '../../fundamentals';
import { UserType } from '../users';
import { Auth, CurrentUser } from './guard';
import { CloudThrottlerGuard, Config, Throttle } from '../../fundamentals';
import { UserService } from '../user';
import { UserType } from '../user/types';
import { validators } from '../utils/validators';
import { CurrentUser } from './current-user';
import { Public } from './guard';
import { AuthService } from './service';
import { TokenService, TokenType } from './token';
@ObjectType()
export class TokenType {
@ObjectType('tokenType')
export class ClientTokenType {
@Field()
token!: string;
@@ -50,46 +49,58 @@ export class AuthResolver {
constructor(
private readonly config: Config,
private readonly auth: AuthService,
private readonly session: SessionService
private readonly user: UserService,
private readonly token: TokenService
) {}
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@Public()
@Query(() => UserType, {
name: 'currentUser',
description: 'Get current user',
nullable: true,
})
currentUser(@CurrentUser() user?: CurrentUser): UserType | undefined {
return user;
}
@Throttle({
default: {
limit: 20,
ttl: 60,
},
})
@ResolveField(() => TokenType)
async token(
@Context() ctx: { req: Request },
@CurrentUser() currentUser: UserType,
@ResolveField(() => ClientTokenType, {
name: 'token',
deprecationReason: 'use [/api/auth/authorize]',
})
async clientToken(
@CurrentUser() currentUser: CurrentUser,
@Parent() user: UserType
) {
): Promise<ClientTokenType> {
if (user.id !== currentUser.id) {
throw new BadRequestException('Invalid user');
throw new ForbiddenException('Invalid user');
}
let sessionToken: string | undefined;
// only return session if the request is from the same origin & path == /open-app
if (
ctx.req.headers.referer &&
ctx.req.headers.host &&
new URL(ctx.req.headers.referer).pathname.startsWith('/open-app') &&
ctx.req.headers.host === new URL(this.config.origin).host
) {
const cookiePrefix = this.config.node.prod ? '__Secure-' : '';
const sessionCookieName = `${cookiePrefix}next-auth.session-token`;
sessionToken = ctx.req.cookies?.[sessionCookieName];
}
const session = await this.auth.createUserSession(
user,
undefined,
this.config.auth.accessToken.ttl
);
return {
sessionToken,
token: this.auth.sign(user),
refresh: this.auth.refresh(user),
sessionToken: session.sessionId,
token: session.sessionId,
refresh: '',
};
}
@Public()
@Throttle({
default: {
limit: 10,
@@ -98,16 +109,19 @@ export class AuthResolver {
})
@Mutation(() => UserType)
async signUp(
@Context() ctx: { req: Request },
@Context() ctx: { req: Request; res: Response },
@Args('name') name: string,
@Args('email') email: string,
@Args('password') password: string
) {
validators.assertValidCredential({ email, password });
const user = await this.auth.signUp(name, email, password);
await this.auth.setCookie(ctx.req, ctx.res, user);
ctx.req.user = user;
return user;
}
@Public()
@Throttle({
default: {
limit: 10,
@@ -116,11 +130,13 @@ export class AuthResolver {
})
@Mutation(() => UserType)
async signIn(
@Context() ctx: { req: Request },
@Context() ctx: { req: Request; res: Response },
@Args('email') email: string,
@Args('password') password: string
) {
validators.assertValidEmail(email);
const user = await this.auth.signIn(email, password);
await this.auth.setCookie(ctx.req, ctx.res, user);
ctx.req.user = user;
return user;
}
@@ -132,28 +148,26 @@ export class AuthResolver {
},
})
@Mutation(() => UserType)
@Auth()
async changePassword(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('token') token: string,
@Args('newPassword') newPassword: string
) {
const id = await this.session.get(token);
if (!user.emailVerified) {
throw new ForbiddenException('Please verify the email first');
validators.assertValidPassword(newPassword);
// NOTE: Set & Change password are using the same token type.
const valid = await this.token.verifyToken(
TokenType.ChangePassword,
token,
{
credential: user.id,
}
if (
!id ||
(id !== user.id &&
// change password after sign in with email link
// we only create user account after user sign in with email link
id !== user.email)
) {
);
if (!valid) {
throw new ForbiddenException('Invalid token');
}
await this.auth.changePassword(user.email, newPassword);
await this.session.delete(token);
await this.auth.changePassword(user.id, newPassword);
return user;
}
@@ -165,25 +179,24 @@ export class AuthResolver {
},
})
@Mutation(() => UserType)
@Auth()
async changeEmail(
@CurrentUser() user: UserType,
@Args('token') token: string
@CurrentUser() user: CurrentUser,
@Args('token') token: string,
@Args('email') email: string
) {
const key = await this.session.get(token);
if (!key) {
validators.assertValidEmail(email);
// @see [sendChangeEmail]
const valid = await this.token.verifyToken(TokenType.VerifyEmail, token, {
credential: user.id,
});
if (!valid) {
throw new ForbiddenException('Invalid token');
}
// email has set token in `sendVerifyChangeEmail`
const [id, email] = key.split(',');
if (!id || id !== user.id || !email) {
throw new ForbiddenException('Invalid token');
}
await this.auth.changeEmail(id, email);
await this.session.delete(token);
email = decodeURIComponent(email);
await this.auth.changeEmail(user.id, email);
await this.auth.sendNotificationChangeEmail(email);
return user;
@@ -196,19 +209,29 @@ export class AuthResolver {
},
})
@Mutation(() => Boolean)
@Auth()
async sendChangePasswordEmail(
@CurrentUser() user: UserType,
@Args('email') email: string,
@Args('callbackUrl') callbackUrl: string
@CurrentUser() user: CurrentUser,
@Args('callbackUrl') callbackUrl: string,
// @deprecated
@Args('email', { nullable: true }) _email?: string
) {
const token = nanoid();
await this.session.set(token, user.id);
if (!user.emailVerified) {
throw new ForbiddenException('Please verify your email first.');
}
const token = await this.token.createToken(
TokenType.ChangePassword,
user.id
);
const url = new URL(callbackUrl, this.config.baseUrl);
url.searchParams.set('token', token);
const res = await this.auth.sendChangePasswordEmail(email, url.toString());
const res = await this.auth.sendChangePasswordEmail(
user.email,
url.toString()
);
return !res.rejected.length;
}
@@ -219,19 +242,27 @@ export class AuthResolver {
},
})
@Mutation(() => Boolean)
@Auth()
async sendSetPasswordEmail(
@CurrentUser() user: UserType,
@Args('email') email: string,
@Args('callbackUrl') callbackUrl: string
@CurrentUser() user: CurrentUser,
@Args('callbackUrl') callbackUrl: string,
@Args('email', { nullable: true }) _email?: string
) {
const token = nanoid();
await this.session.set(token, user.id);
if (!user.emailVerified) {
throw new ForbiddenException('Please verify your email first.');
}
const token = await this.token.createToken(
TokenType.ChangePassword,
user.id
);
const url = new URL(callbackUrl, this.config.baseUrl);
url.searchParams.set('token', token);
const res = await this.auth.sendSetPasswordEmail(email, url.toString());
const res = await this.auth.sendSetPasswordEmail(
user.email,
url.toString()
);
return !res.rejected.length;
}
@@ -249,19 +280,22 @@ export class AuthResolver {
},
})
@Mutation(() => Boolean)
@Auth()
async sendChangeEmail(
@CurrentUser() user: UserType,
@Args('email') email: string,
@Args('callbackUrl') callbackUrl: string
@CurrentUser() user: CurrentUser,
@Args('callbackUrl') callbackUrl: string,
// @deprecated
@Args('email', { nullable: true }) _email?: string
) {
const token = nanoid();
await this.session.set(token, user.id);
if (!user.emailVerified) {
throw new ForbiddenException('Please verify your email first.');
}
const token = await this.token.createToken(TokenType.ChangeEmail, user.id);
const url = new URL(callbackUrl, this.config.baseUrl);
url.searchParams.set('token', token);
const res = await this.auth.sendChangeEmail(email, url.toString());
const res = await this.auth.sendChangeEmail(user.email, url.toString());
return !res.rejected.length;
}
@@ -272,34 +306,92 @@ export class AuthResolver {
},
})
@Mutation(() => Boolean)
@Auth()
async sendVerifyChangeEmail(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('token') token: string,
@Args('email') email: string,
@Args('callbackUrl') callbackUrl: string
) {
const id = await this.session.get(token);
if (!id || id !== user.id) {
validators.assertValidEmail(email);
const valid = await this.token.verifyToken(TokenType.ChangeEmail, token, {
credential: user.id,
});
if (!valid) {
throw new ForbiddenException('Invalid token');
}
const hasRegistered = await this.auth.getUserByEmail(email);
const hasRegistered = await this.user.findUserByEmail(email);
if (hasRegistered) {
throw new BadRequestException(`Invalid user email`);
if (hasRegistered.id !== user.id) {
throw new BadRequestException(`The email provided has been taken.`);
} else {
throw new BadRequestException(
`The email provided is the same as the current email.`
);
}
}
const withEmailToken = nanoid();
await this.session.set(withEmailToken, `${user.id},${email}`);
const verifyEmailToken = await this.token.createToken(
TokenType.VerifyEmail,
user.id
);
const url = new URL(callbackUrl, this.config.baseUrl);
url.searchParams.set('token', withEmailToken);
url.searchParams.set('token', verifyEmailToken);
url.searchParams.set('email', email);
const res = await this.auth.sendVerifyChangeEmail(email, url.toString());
await this.session.delete(token);
return !res.rejected.length;
}
@Throttle({
default: {
limit: 5,
ttl: 60,
},
})
@Mutation(() => Boolean)
async sendVerifyEmail(
@CurrentUser() user: CurrentUser,
@Args('callbackUrl') callbackUrl: string
) {
const token = await this.token.createToken(TokenType.VerifyEmail, user.id);
const url = new URL(callbackUrl, this.config.baseUrl);
url.searchParams.set('token', token);
const res = await this.auth.sendVerifyEmail(user.email, url.toString());
return !res.rejected.length;
}
@Throttle({
default: {
limit: 5,
ttl: 60,
},
})
@Mutation(() => Boolean)
async verifyEmail(
@CurrentUser() user: CurrentUser,
@Args('token') token: string
) {
if (!token) {
throw new BadRequestException('Invalid token');
}
const valid = await this.token.verifyToken(TokenType.VerifyEmail, token, {
credential: user.id,
});
if (!valid) {
throw new ForbiddenException('Invalid token');
}
const { emailVerifiedAt } = await this.auth.setEmailVerified(user.id);
return emailVerifiedAt !== null;
}
}

View File

@@ -1,299 +1,326 @@
import { randomUUID } from 'node:crypto';
import {
BadRequestException,
Injectable,
InternalServerErrorException,
UnauthorizedException,
NotAcceptableException,
OnApplicationBootstrap,
} from '@nestjs/common';
import { hash, verify } from '@node-rs/argon2';
import { Algorithm, sign, verify as jwtVerify } from '@node-rs/jsonwebtoken';
import { PrismaClient, type User } from '@prisma/client';
import { nanoid } from 'nanoid';
import type { User } from '@prisma/client';
import { PrismaClient } from '@prisma/client';
import type { CookieOptions, Request, Response } from 'express';
import { assign, omit } from 'lodash-es';
import {
Config,
MailService,
verifyChallengeResponse,
} from '../../fundamentals';
import { Quota_FreePlanV1_1 } from '../quota';
import { Config, CryptoHelper, MailService } from '../../fundamentals';
import { FeatureManagementService } from '../features/management';
import { UserService } from '../user/service';
import type { CurrentUser } from './current-user';
export type UserClaim = Pick<
export function parseAuthUserSeqNum(value: any) {
let seq: number = 0;
switch (typeof value) {
case 'number': {
seq = value;
break;
}
case 'string': {
const result = value.match(/^([\d{0, 10}])$/);
if (result?.[1]) {
seq = Number(result[1]);
}
break;
}
default: {
seq = 0;
}
}
return Math.max(0, seq);
}
export function sessionUser(
user: Pick<
User,
'id' | 'name' | 'email' | 'emailVerified' | 'createdAt' | 'avatarUrl'
> & {
hasPassword?: boolean;
};
export const getUtcTimestamp = () => Math.floor(Date.now() / 1000);
'id' | 'email' | 'avatarUrl' | 'name' | 'emailVerifiedAt'
> & { password?: string | null }
): CurrentUser {
return assign(
omit(user, 'password', 'registered', 'emailVerifiedAt', 'createdAt'),
{
hasPassword: user.password !== null,
emailVerified: user.emailVerifiedAt !== null,
}
);
}
@Injectable()
export class AuthService {
export class AuthService implements OnApplicationBootstrap {
readonly cookieOptions: CookieOptions = {
sameSite: 'lax',
httpOnly: true,
path: '/',
secure: this.config.https,
};
static readonly sessionCookieName = 'sid';
static readonly authUserSeqHeaderName = 'x-auth-user';
constructor(
private readonly config: Config,
private readonly prisma: PrismaClient,
private readonly mailer: MailService
private readonly db: PrismaClient,
private readonly mailer: MailService,
private readonly feature: FeatureManagementService,
private readonly user: UserService,
private readonly crypto: CryptoHelper
) {}
sign(user: UserClaim) {
const now = getUtcTimestamp();
return sign(
{
data: {
id: user.id,
name: user.name,
email: user.email,
emailVerified: user.emailVerified?.toISOString(),
image: user.avatarUrl,
hasPassword: Boolean(user.hasPassword),
createdAt: user.createdAt.toISOString(),
},
iat: now,
exp: now + this.config.auth.accessTokenExpiresIn,
iss: this.config.serverId,
sub: user.id,
aud: 'https://affine.pro',
jti: randomUUID({
disableEntropyCache: true,
}),
},
this.config.auth.privateKey,
{
algorithm: Algorithm.ES256,
}
);
}
refresh(user: UserClaim) {
const now = getUtcTimestamp();
return sign(
{
data: {
id: user.id,
name: user.name,
email: user.email,
emailVerified: user.emailVerified?.toISOString(),
image: user.avatarUrl,
hasPassword: Boolean(user.hasPassword),
createdAt: user.createdAt.toISOString(),
},
exp: now + this.config.auth.refreshTokenExpiresIn,
iat: now,
iss: this.config.serverId,
sub: user.id,
aud: 'https://affine.pro',
jti: randomUUID({
disableEntropyCache: true,
}),
},
this.config.auth.privateKey,
{
algorithm: Algorithm.ES256,
}
);
}
async verify(token: string) {
try {
const data = (
await jwtVerify(token, this.config.auth.publicKey, {
algorithms: [Algorithm.ES256],
iss: [this.config.serverId],
leeway: this.config.auth.leeway,
requiredSpecClaims: ['exp', 'iat', 'iss', 'sub'],
aud: ['https://affine.pro'],
})
).data as UserClaim;
return {
...data,
emailVerified: data.emailVerified ? new Date(data.emailVerified) : null,
createdAt: new Date(data.createdAt),
};
} catch (e) {
throw new UnauthorizedException('Invalid token');
}
}
async verifyCaptchaToken(token: any, ip: string) {
if (typeof token !== 'string' || !token) return false;
const formData = new FormData();
formData.append('secret', this.config.auth.captcha.turnstile.secret);
formData.append('response', token);
formData.append('remoteip', ip);
// prevent replay attack
formData.append('idempotency_key', nanoid());
const url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
const result = await fetch(url, {
body: formData,
method: 'POST',
async onApplicationBootstrap() {
if (this.config.node.dev) {
await this.signUp('Dev User', 'dev@affine.pro', 'dev').catch(() => {
// ignore
});
const outcome = await result.json();
return (
!!outcome.success &&
// skip hostname check in dev mode
(this.config.node.dev || outcome.hostname === this.config.host)
);
}
}
async verifyChallengeResponse(response: any, resource: string) {
return verifyChallengeResponse(
response,
this.config.auth.captcha.challenge.bits,
resource
);
canSignIn(email: string) {
return this.feature.canEarlyAccess(email);
}
async signIn(email: string, password: string): Promise<User> {
const user = await this.prisma.user.findFirst({
where: {
email: {
equals: email,
mode: 'insensitive',
},
},
});
if (!user) {
throw new BadRequestException('Invalid email');
}
if (!user.password) {
throw new BadRequestException('User has no password');
}
let equal = false;
try {
equal = await verify(user.password, password);
} catch (e) {
console.error(e);
throw new InternalServerErrorException(e, 'Verify password failed');
}
if (!equal) {
throw new UnauthorizedException('Invalid password');
}
return user;
}
async signUp(name: string, email: string, password: string): Promise<User> {
const user = await this.prisma.user.findFirst({
where: {
email: {
equals: email,
mode: 'insensitive',
},
},
});
async signUp(
name: string,
email: string,
password: string
): Promise<CurrentUser> {
const user = await this.user.findUserByEmail(email);
if (user) {
throw new BadRequestException('Email already exists');
throw new BadRequestException('Email was taken');
}
const hashedPassword = await hash(password);
const hashedPassword = await this.crypto.encryptPassword(password);
return this.prisma.user.create({
data: {
return this.user
.createUser({
name,
email,
password: hashedPassword,
// TODO(@forehalo): handle in event system
features: {
create: {
reason: 'created by api sign up',
activated: true,
feature: {
connect: {
feature_version: Quota_FreePlanV1_1,
},
},
},
},
},
});
})
.then(sessionUser);
}
async createAnonymousUser(email: string): Promise<User> {
const user = await this.prisma.user.findFirst({
async signIn(email: string, password: string) {
const user = await this.user.findUserWithHashedPasswordByEmail(email);
if (!user) {
throw new NotAcceptableException('Invalid sign in credentials');
}
if (!user.password) {
throw new NotAcceptableException(
'User Password is not set. Should login through email link.'
);
}
const passwordMatches = await this.crypto.verifyPassword(
password,
user.password
);
if (!passwordMatches) {
throw new NotAcceptableException('Invalid sign in credentials');
}
return sessionUser(user);
}
async getUser(token: string, seq = 0): Promise<CurrentUser | null> {
const session = await this.getSession(token);
// no such session
if (!session) {
return null;
}
const userSession = session.userSessions.at(seq);
// no such user session
if (!userSession) {
return null;
}
// user session expired
if (userSession.expiresAt && userSession.expiresAt <= new Date()) {
return null;
}
const user = await this.db.user.findUnique({
where: { id: userSession.userId },
});
if (!user) {
return null;
}
return sessionUser(user);
}
async getUserList(token: string) {
const session = await this.getSession(token);
if (!session || !session.userSessions.length) {
return [];
}
const users = await this.db.user.findMany({
where: {
email: {
equals: email,
mode: 'insensitive',
id: {
in: session.userSessions.map(({ userId }) => userId),
},
},
});
if (user) {
throw new BadRequestException('Email already exists');
// TODO(@forehalo): need to separate expired session, same for [getUser]
// Session
// | { user: LimitedUser { email, avatarUrl }, expired: true }
// | { user: User, expired: false }
return session.userSessions
.map(userSession => {
// keep users in the same order as userSessions
const user = users.find(({ id }) => id === userSession.userId);
if (!user) {
return null;
}
return sessionUser(user);
})
.filter(Boolean) as CurrentUser[];
}
return this.prisma.user.create({
async signOut(token: string, seq = 0) {
const session = await this.getSession(token);
if (session) {
// overflow the logged in user
if (session.userSessions.length <= seq) {
return session;
}
await this.db.userSession.deleteMany({
where: { id: session.userSessions[seq].id },
});
// no more user session active, delete the whole session
if (session.userSessions.length === 1) {
await this.db.session.delete({ where: { id: session.id } });
return null;
}
return session;
}
return null;
}
async getSession(token: string) {
if (!token) {
return null;
}
return this.db.$transaction(async tx => {
const session = await tx.session.findUnique({
where: {
id: token,
},
include: {
userSessions: {
orderBy: {
createdAt: 'asc',
},
},
},
});
if (!session) {
return null;
}
if (session.expiresAt && session.expiresAt <= new Date()) {
await tx.session.delete({
where: {
id: session.id,
},
});
return null;
}
return session;
});
}
async createUserSession(
user: { id: string },
existingSession?: string,
ttl = this.config.auth.session.ttl
) {
const session = existingSession
? await this.getSession(existingSession)
: null;
const expiresAt = new Date(Date.now() + ttl * 1000);
if (session) {
return this.db.userSession.upsert({
where: {
sessionId_userId: {
sessionId: session.id,
userId: user.id,
},
},
update: {
expiresAt,
},
create: {
sessionId: session.id,
userId: user.id,
expiresAt,
},
});
} else {
return this.db.userSession.create({
data: {
name: 'Unnamed',
email,
features: {
create: {
reason: 'created by invite sign up',
activated: true,
feature: {
expiresAt,
session: {
create: {},
},
user: {
connect: {
feature_version: Quota_FreePlanV1_1,
},
},
id: user.id,
},
},
},
});
}
}
async setCookie(req: Request, res: Response, user: { id: string }) {
const session = await this.createUserSession(
user,
req.cookies[AuthService.sessionCookieName]
);
res.cookie(AuthService.sessionCookieName, session.sessionId, {
expires: session.expiresAt ?? void 0,
...this.cookieOptions,
});
}
async getUserByEmail(email: string): Promise<User | null> {
return this.prisma.user.findFirst({
where: {
email: {
equals: email,
mode: 'insensitive',
},
},
});
}
async isUserHasPassword(email: string): Promise<boolean> {
const user = await this.prisma.user.findFirst({
where: {
email: {
equals: email,
mode: 'insensitive',
},
},
});
if (!user) {
throw new BadRequestException('Invalid email');
}
return Boolean(user.password);
}
async changePassword(email: string, newPassword: string): Promise<User> {
const user = await this.prisma.user.findFirst({
where: {
email: {
equals: email,
mode: 'insensitive',
},
emailVerified: {
not: null,
},
},
});
async changePassword(id: string, newPassword: string): Promise<User> {
const user = await this.user.findUserById(id);
if (!user) {
throw new BadRequestException('Invalid email');
}
const hashedPassword = await hash(newPassword);
const hashedPassword = await this.crypto.encryptPassword(newPassword);
return this.prisma.user.update({
return this.db.user.update({
where: {
id: user.id,
},
@@ -304,22 +331,33 @@ export class AuthService {
}
async changeEmail(id: string, newEmail: string): Promise<User> {
const user = await this.prisma.user.findUnique({
where: {
id,
},
});
const user = await this.user.findUserById(id);
if (!user) {
throw new BadRequestException('Invalid email');
}
return this.prisma.user.update({
return this.db.user.update({
where: {
id,
},
data: {
email: newEmail,
emailVerifiedAt: new Date(),
},
});
}
async setEmailVerified(id: string) {
return await this.db.user.update({
where: {
id,
},
data: {
emailVerifiedAt: new Date(),
},
select: {
emailVerifiedAt: true,
},
});
}
@@ -336,7 +374,20 @@ export class AuthService {
async sendVerifyChangeEmail(email: string, callbackUrl: string) {
return this.mailer.sendVerifyChangeEmail(email, callbackUrl);
}
async sendVerifyEmail(email: string, callbackUrl: string) {
return this.mailer.sendVerifyEmail(email, callbackUrl);
}
async sendNotificationChangeEmail(email: string) {
return this.mailer.sendNotificationChangeEmail(email);
}
async sendSignInEmail(email: string, link: string, signUp: boolean) {
return signUp
? await this.mailer.sendSignUpMail(link.toString(), {
to: email,
})
: await this.mailer.sendSignInMail(link.toString(), {
to: email,
});
}
}

View File

@@ -0,0 +1,96 @@
import { randomUUID } from 'node:crypto';
import { Injectable } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { PrismaClient } from '@prisma/client';
import { CryptoHelper } from '../../fundamentals/helpers';
export enum TokenType {
SignIn,
VerifyEmail,
ChangeEmail,
ChangePassword,
Challenge,
}
@Injectable()
export class TokenService {
constructor(
private readonly db: PrismaClient,
private readonly crypto: CryptoHelper
) {}
async createToken(
type: TokenType,
credential?: string,
ttlInSec: number = 30 * 60
) {
const plaintextToken = randomUUID();
const { token } = await this.db.verificationToken.create({
data: {
type,
token: plaintextToken,
credential,
expiresAt: new Date(Date.now() + ttlInSec * 1000),
},
});
return this.crypto.encrypt(token);
}
async verifyToken(
type: TokenType,
token: string,
{
credential,
keep,
}: {
credential?: string;
keep?: boolean;
} = {}
) {
token = this.crypto.decrypt(token);
const record = await this.db.verificationToken.findUnique({
where: {
type_token: {
token,
type,
},
},
});
if (!record) {
return null;
}
const expired = record.expiresAt <= new Date();
const valid =
!expired && (!record.credential || record.credential === credential);
if ((expired || valid) && !keep) {
await this.db.verificationToken.delete({
where: {
type_token: {
token,
type,
},
},
});
}
return valid ? record : null;
}
@Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)
cleanExpiredTokens() {
return this.db.verificationToken.deleteMany({
where: {
expiresAt: {
lte: new Date(),
},
},
});
}
}

View File

@@ -1,3 +0,0 @@
export { jwtDecode as decode, jwtEncode as encode } from './jwt';
export { sendVerificationRequest } from './send-mail';
export type { SendVerificationRequestParams } from 'next-auth/providers/email';

View File

@@ -1,76 +0,0 @@
import { randomUUID } from 'node:crypto';
import { BadRequestException } from '@nestjs/common';
import { Algorithm, sign, verify as jwtVerify } from '@node-rs/jsonwebtoken';
import { PrismaClient } from '@prisma/client';
import { JWT } from 'next-auth/jwt';
import { Config } from '../../../fundamentals';
import { getUtcTimestamp, UserClaim } from '../service';
export const jwtEncode = async (
config: Config,
prisma: PrismaClient,
token: JWT | undefined,
maxAge: number | undefined
) => {
if (!token?.email) {
throw new BadRequestException('Missing email in jwt token');
}
const user = await prisma.user.findFirstOrThrow({
where: {
email: token.email,
},
});
const now = getUtcTimestamp();
return sign(
{
data: {
id: user.id,
name: user.name,
email: user.email,
emailVerified: user.emailVerified?.toISOString(),
picture: user.avatarUrl,
createdAt: user.createdAt.toISOString(),
hasPassword: Boolean(user.password),
},
iat: now,
exp: now + (maxAge ?? config.auth.accessTokenExpiresIn),
iss: config.serverId,
sub: user.id,
aud: 'https://affine.pro',
jti: randomUUID({
disableEntropyCache: true,
}),
},
config.auth.privateKey,
{
algorithm: Algorithm.ES256,
}
);
};
export const jwtDecode = async (config: Config, token: string | undefined) => {
if (!token) {
return null;
}
const { name, email, emailVerified, id, picture, hasPassword } = (
await jwtVerify(token, config.auth.publicKey, {
algorithms: [Algorithm.ES256],
iss: [config.serverId],
leeway: config.auth.leeway,
requiredSpecClaims: ['exp', 'iat', 'iss', 'sub'],
})
).data as Omit<UserClaim, 'avatarUrl'> & {
picture: string | undefined;
};
return {
name,
email,
emailVerified,
picture,
sub: id,
id,
hasPassword,
};
};

View File

@@ -1,39 +0,0 @@
import { Logger } from '@nestjs/common';
import { nanoid } from 'nanoid';
import type { SendVerificationRequestParams } from 'next-auth/providers/email';
import { Config, MailService, SessionService } from '../../../fundamentals';
export async function sendVerificationRequest(
config: Config,
logger: Logger,
mailer: MailService,
session: SessionService,
params: SendVerificationRequestParams
) {
const { identifier, url, provider } = params;
const urlWithToken = new URL(url);
const callbackUrl = urlWithToken.searchParams.get('callbackUrl') || '';
if (!callbackUrl) {
throw new Error('callbackUrl is not set');
} else {
const newCallbackUrl = new URL(callbackUrl, config.origin);
const token = nanoid();
await session.set(token, identifier);
newCallbackUrl.searchParams.set('token', token);
urlWithToken.searchParams.set('callbackUrl', newCallbackUrl.toString());
}
const result = await mailer.sendSignInEmail(urlWithToken.toString(), {
to: identifier,
from: provider.from,
});
logger.log(`send verification email success: ${result.accepted.join(', ')}`);
const failed = result.rejected.concat(result.pending).filter(Boolean);
if (failed.length) {
throw new Error(`Email (${failed.join(', ')}) could not be sent`);
}
}

View File

@@ -2,9 +2,11 @@ import { Module } from '@nestjs/common';
import { Field, ObjectType, Query, registerEnumType } from '@nestjs/graphql';
import { DeploymentType } from '../fundamentals';
import { Public } from './auth';
export enum ServerFeature {
Payment = 'payment',
OAuth = 'oauth',
}
registerEnumType(ServerFeature, {
@@ -15,9 +17,9 @@ registerEnumType(DeploymentType, {
name: 'ServerDeploymentType',
});
const ENABLED_FEATURES: ServerFeature[] = [];
const ENABLED_FEATURES: Set<ServerFeature> = new Set();
export function ADD_ENABLED_FEATURES(feature: ServerFeature) {
ENABLED_FEATURES.push(feature);
ENABLED_FEATURES.add(feature);
}
@ObjectType()
@@ -46,7 +48,9 @@ export class ServerConfigType {
@Field(() => [ServerFeature], { description: 'enabled server features' })
features!: ServerFeature[];
}
export class ServerConfigResolver {
@Public()
@Query(() => ServerConfigType, {
description: 'server config',
})
@@ -60,7 +64,7 @@ export class ServerConfigResolver {
// the old flavors contains `selfhosted` but it actually not flavor but deployment type
// this field should be removed after frontend feature flags implemented
flavor: AFFiNE.type,
features: ENABLED_FEATURES,
features: Array.from(ENABLED_FEATURES),
};
}
}

View File

@@ -4,12 +4,8 @@ import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { PrismaClient } from '@prisma/client';
import {
Config,
type EventPayload,
metrics,
OnEvent,
} from '../../fundamentals';
import type { EventPayload } from '../../fundamentals';
import { Config, metrics, OnEvent } from '../../fundamentals';
import { QuotaService } from '../quota';
import { Permission } from '../workspaces/types';
import { isEmptyBuffer } from './manager';

View File

@@ -16,12 +16,12 @@ import {
transact,
} from 'yjs';
import type { EventPayload } from '../../fundamentals';
import {
Cache,
CallTimer,
Config,
EventEmitter,
type EventPayload,
mergeUpdatesInApplyWay as jwstMergeUpdates,
metrics,
OnEvent,
@@ -55,6 +55,16 @@ export function isEmptyBuffer(buf: Buffer): boolean {
const MAX_SEQ_NUM = 0x3fffffff; // u31
const UPDATES_QUEUE_CACHE_KEY = 'doc:manager:updates';
interface DocResponse {
doc: Doc;
timestamp: number;
}
interface BinaryResponse {
binary: Buffer;
timestamp: number;
}
/**
* Since we can't directly save all client updates into database, in which way the database will overload,
* we need to buffer the updates and merge them to reduce db write.
@@ -229,12 +239,12 @@ export class DocManager implements OnModuleInit, OnModuleDestroy {
update: Buffer,
retryTimes = 10
) {
await new Promise<void>((resolve, reject) => {
const timestamp = await new Promise<number>((resolve, reject) => {
defer(async () => {
const seq = await this.getUpdateSeq(workspaceId, guid);
await this.db.update.create({
const { createdAt } = await this.db.update.create({
select: {
seq: true,
createdAt: true,
},
data: {
workspaceId,
@@ -243,23 +253,27 @@ export class DocManager implements OnModuleInit, OnModuleDestroy {
blob: update,
},
});
return createdAt.getTime();
})
.pipe(retry(retryTimes)) // retry until seq num not conflict
.subscribe({
next: () => {
next: timestamp => {
this.logger.debug(
`pushed 1 update for ${guid} in workspace ${workspaceId}`
);
resolve();
resolve(timestamp);
},
error: e => {
this.logger.error('Failed to push updates', e);
reject(new Error('Failed to push update'));
},
});
}).then(() => {
return this.updateCachedUpdatesCount(workspaceId, guid, 1);
});
await this.updateCachedUpdatesCount(workspaceId, guid, 1);
return timestamp;
}
async batchPush(
@@ -268,56 +282,124 @@ export class DocManager implements OnModuleInit, OnModuleDestroy {
updates: Buffer[],
retryTimes = 10
) {
await new Promise<void>((resolve, reject) => {
const timestamp = await new Promise<number>((resolve, reject) => {
defer(async () => {
const seq = await this.getUpdateSeq(workspaceId, guid, updates.length);
const lastSeq = await this.getUpdateSeq(
workspaceId,
guid,
updates.length
);
const now = Date.now();
let timestamp = now;
let turn = 0;
const batchCount = 10;
for (const batch of chunk(updates, batchCount)) {
await this.db.update.createMany({
data: batch.map((update, i) => ({
workspaceId,
id: guid,
data: batch.map((update, i) => {
const subSeq = turn * batchCount + i + 1;
// `seq` is the last seq num of the batch
// example for 11 batched updates, start from seq num 20
// seq for first update in the batch should be:
// 31 - 11 + 0 * 10 + 0 + 1 = 21
// 31 - 11 + subSeq(0 * 10 + 0 + 1) = 21
// ^ last seq num ^ updates.length ^ turn ^ batchCount ^i
seq: seq - updates.length + turn * batchCount + i + 1,
const seq = lastSeq - updates.length + subSeq;
const createdAt = now + subSeq;
timestamp = Math.max(timestamp, createdAt);
return {
workspaceId,
id: guid,
blob: update,
})),
seq,
createdAt: new Date(createdAt), // make sure the updates can be ordered by create time
};
}),
});
turn++;
}
return timestamp;
})
.pipe(retry(retryTimes)) // retry until seq num not conflict
.subscribe({
next: () => {
next: timestamp => {
this.logger.debug(
`pushed ${updates.length} updates for ${guid} in workspace ${workspaceId}`
);
resolve();
resolve(timestamp);
},
error: e => {
this.logger.error('Failed to push updates', e);
reject(new Error('Failed to push update'));
},
});
}).then(() => {
return this.updateCachedUpdatesCount(workspaceId, guid, updates.length);
});
await this.updateCachedUpdatesCount(workspaceId, guid, updates.length);
return timestamp;
}
/**
* Get latest timestamp of all docs in the workspace.
*/
@CallTimer('doc', 'get_doc_timestamps')
async getDocTimestamps(workspaceId: string, after: number | undefined = 0) {
const snapshots = await this.db.snapshot.findMany({
where: {
workspaceId,
updatedAt: {
gt: new Date(after),
},
},
select: {
id: true,
updatedAt: true,
},
});
const updates = await this.db.update.groupBy({
where: {
workspaceId,
createdAt: {
gt: new Date(after),
},
},
by: ['id'],
_max: {
createdAt: true,
},
});
const result: Record<string, number> = {};
snapshots.forEach(s => {
result[s.id] = s.updatedAt.getTime();
});
updates.forEach(u => {
if (u._max.createdAt) {
result[u.id] = u._max.createdAt.getTime();
}
});
return result;
}
/**
* get the latest doc with all update applied.
*/
async get(workspaceId: string, guid: string): Promise<Doc | null> {
async get(workspaceId: string, guid: string): Promise<DocResponse | null> {
const result = await this._get(workspaceId, guid);
if (result) {
if ('doc' in result) {
return result.doc;
} else if ('snapshot' in result) {
return this.recoverDoc(result.snapshot);
return result;
} else {
const doc = await this.recoverDoc(result.binary);
return {
doc,
timestamp: result.timestamp,
};
}
}
@@ -327,13 +409,19 @@ export class DocManager implements OnModuleInit, OnModuleDestroy {
/**
* get the latest doc binary with all update applied.
*/
async getBinary(workspaceId: string, guid: string): Promise<Buffer | null> {
async getBinary(
workspaceId: string,
guid: string
): Promise<BinaryResponse | null> {
const result = await this._get(workspaceId, guid);
if (result) {
if ('doc' in result) {
return Buffer.from(encodeStateAsUpdate(result.doc));
} else if ('snapshot' in result) {
return result.snapshot;
return {
binary: Buffer.from(encodeStateAsUpdate(result.doc)),
timestamp: result.timestamp,
};
} else {
return result;
}
}
@@ -343,16 +431,27 @@ export class DocManager implements OnModuleInit, OnModuleDestroy {
/**
* get the latest doc state vector with all update applied.
*/
async getState(workspaceId: string, guid: string): Promise<Buffer | null> {
async getDocState(
workspaceId: string,
guid: string
): Promise<BinaryResponse | null> {
const snapshot = await this.getSnapshot(workspaceId, guid);
const updates = await this.getUpdates(workspaceId, guid);
if (updates.length) {
const doc = await this.squash(snapshot, updates);
return Buffer.from(encodeStateVector(doc));
const { doc, timestamp } = await this.squash(snapshot, updates);
return {
binary: Buffer.from(encodeStateVector(doc)),
timestamp,
};
}
return snapshot ? snapshot.state : null;
return snapshot?.state
? {
binary: snapshot.state,
timestamp: snapshot.updatedAt.getTime(),
}
: null;
}
/**
@@ -520,17 +619,17 @@ export class DocManager implements OnModuleInit, OnModuleDestroy {
private async _get(
workspaceId: string,
guid: string
): Promise<{ doc: Doc } | { snapshot: Buffer } | null> {
): Promise<DocResponse | BinaryResponse | null> {
const snapshot = await this.getSnapshot(workspaceId, guid);
const updates = await this.getUpdates(workspaceId, guid);
if (updates.length) {
return {
doc: await this.squash(snapshot, updates),
};
return this.squash(snapshot, updates);
}
return snapshot ? { snapshot: snapshot.blob } : null;
return snapshot
? { binary: snapshot.blob, timestamp: snapshot.updatedAt.getTime() }
: null;
}
/**
@@ -538,7 +637,10 @@ export class DocManager implements OnModuleInit, OnModuleDestroy {
* and delete the updates records at the same time.
*/
@CallTimer('doc', 'squash')
private async squash(snapshot: Snapshot | null, updates: Update[]) {
private async squash(
snapshot: Snapshot | null,
updates: Update[]
): Promise<DocResponse> {
if (!updates.length) {
throw new Error('No updates to squash');
}
@@ -597,7 +699,7 @@ export class DocManager implements OnModuleInit, OnModuleDestroy {
await this.updateCachedUpdatesCount(workspaceId, id, -count);
}
return doc;
return { doc, timestamp: last.createdAt.getTime() };
}
private async getUpdateSeq(workspaceId: string, guid: string, batch = 1) {

View File

@@ -1,5 +1,4 @@
import { PrismaClient } from '@prisma/client';
import { PrismaTransaction } from '../../fundamentals';
import { Feature, FeatureSchema, FeatureType } from './types';
class FeatureConfig {
@@ -67,7 +66,7 @@ export type FeatureConfigType<F extends FeatureType> = InstanceType<
const FeatureCache = new Map<number, FeatureConfigType<FeatureType>>();
export async function getFeature(prisma: PrismaClient, featureId: number) {
export async function getFeature(prisma: PrismaTransaction, featureId: number) {
const cachedQuota = FeatureCache.get(featureId);
if (cachedQuota) {

View File

@@ -115,4 +115,10 @@ 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

@@ -1,7 +1,6 @@
import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import { UserType } from '../users/types';
import { WorkspaceType } from '../workspaces/types';
import { FeatureConfigType, getFeature } from './feature';
import { FeatureKind, FeatureType } from './types';
@@ -158,7 +157,7 @@ export class FeatureService {
return configs.filter(feature => !!feature.feature);
}
async listFeatureUsers(feature: FeatureType): Promise<UserType[]> {
async listFeatureUsers(feature: FeatureType) {
return this.prisma.userFeatures
.findMany({
where: {
@@ -175,7 +174,7 @@ export class FeatureService {
name: true,
avatarUrl: true,
email: true,
emailVerified: true,
emailVerifiedAt: true,
createdAt: true,
},
},

View File

@@ -1,5 +1,4 @@
import { PrismaClient } from '@prisma/client';
import { PrismaTransaction } from '../../fundamentals';
import { formatDate, formatSize, Quota, QuotaSchema } from './types';
const QuotaCache = new Map<number, QuotaConfig>();
@@ -7,14 +6,14 @@ const QuotaCache = new Map<number, QuotaConfig>();
export class QuotaConfig {
readonly config: Quota;
static async get(prisma: PrismaClient, featureId: number) {
static async get(tx: PrismaTransaction, featureId: number) {
const cachedQuota = QuotaCache.get(featureId);
if (cachedQuota) {
return cachedQuota;
}
const quota = await prisma.features.findFirst({
const quota = await tx.features.findFirst({
where: {
id: featureId,
},

View File

@@ -1,4 +1,4 @@
import { FeatureKind } from '../features';
import { FeatureKind } from '../features/types';
import { OneDay, OneGB, OneMB } from './constant';
import { Quota, QuotaType } from './types';

View File

@@ -1,13 +1,12 @@
import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
import { type EventPayload, OnEvent } from '../../fundamentals';
import type { EventPayload } from '../../fundamentals';
import { OnEvent, PrismaTransaction } from '../../fundamentals';
import { FeatureKind } from '../features';
import { QuotaConfig } from './quota';
import { QuotaType } from './types';
type Transaction = Parameters<Parameters<PrismaClient['$transaction']>[0]>[0];
@Injectable()
export class QuotaService {
constructor(private readonly prisma: PrismaClient) {}
@@ -140,8 +139,8 @@ export class QuotaService {
});
}
async hasQuota(userId: string, quota: QuotaType, transaction?: Transaction) {
const executor = transaction ?? this.prisma;
async hasQuota(userId: string, quota: QuotaType, tx?: PrismaTransaction) {
const executor = tx ?? this.prisma;
return executor.userFeatures
.count({

View File

@@ -1,4 +1,4 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { FeatureService, FeatureType } from '../features';
import { WorkspaceBlobStorage } from '../storage';
@@ -11,6 +11,8 @@ type QuotaBusinessType = QuotaQueryType & { businessBlobLimit: number };
@Injectable()
export class QuotaManagementService {
protected logger = new Logger(QuotaManagementService.name);
constructor(
private readonly feature: FeatureService,
private readonly quota: QuotaService,
@@ -38,11 +40,22 @@ export class QuotaManagementService {
async getUserUsage(userId: string) {
const workspaces = await this.permissions.getOwnedWorkspaces(userId);
const sizes = await Promise.all(
const sizes = await Promise.allSettled(
workspaces.map(workspace => this.storage.totalSize(workspace))
);
return sizes.reduce((total, size) => total + size, 0);
return sizes.reduce((total, size) => {
if (size.status === 'fulfilled') {
if (Number.isSafeInteger(size.value)) {
return total + size.value;
} else {
this.logger.error(`Workspace size is invalid: ${size.value}`);
}
} else {
this.logger.error(`Failed to get workspace size: ${size.reason}`);
}
return total;
}, 0);
}
// get workspace's owner quota and total size of used

View File

@@ -2,7 +2,7 @@ import { Field, ObjectType } from '@nestjs/graphql';
import { SafeIntResolver } from 'graphql-scalars';
import { z } from 'zod';
import { commonFeatureSchema, FeatureKind } from '../features';
import { commonFeatureSchema, FeatureKind } from '../features/types';
import { ByteUnit, OneDay, OneKB } from './constant';
/// ======== quota define ========

View File

@@ -14,7 +14,6 @@ import { encodeStateAsUpdate, encodeStateVector } from 'yjs';
import { CallTimer, metrics } from '../../../fundamentals';
import { Auth, CurrentUser } from '../../auth';
import { DocManager } from '../../doc';
import { UserType } from '../../users';
import { DocID } from '../../utils/doc';
import { PermissionService } from '../../workspaces/permission';
import { Permission } from '../../workspaces/types';
@@ -39,26 +38,21 @@ export const GatewayErrorWrapper = (): MethodDecorator => {
return desc;
}
desc.value = function (...args: any[]) {
let result: any;
desc.value = async function (...args: any[]) {
try {
result = originalMethod.apply(this, args);
return await originalMethod.apply(this, args);
} catch (e) {
if (e instanceof EventError) {
return {
error: e,
};
} else {
metrics.socketio.counter('unhandled_errors').add(1);
new Logger('EventsGateway').error(e, (e as Error).stack);
return {
error: new InternalError(e as Error),
};
}
if (result instanceof Promise) {
return result.catch(e => {
metrics.socketio.counter('unhandled_errors').add(1);
return {
error: new InternalError(e),
};
});
} else {
return result;
}
};
@@ -85,8 +79,16 @@ type EventResponse<Data = any> =
data: Data;
});
function Sync(workspaceId: string): `${string}:sync` {
return `${workspaceId}:sync`;
}
function Awareness(workspaceId: string): `${string}:awareness` {
return `${workspaceId}:awareness`;
}
@WebSocketGateway({
cors: process.env.NODE_ENV !== 'production',
cors: !AFFiNE.node.prod,
transports: ['websocket'],
// see: https://socket.io/docs/v4/server-options/#maxhttpbuffersize
maxHttpBufferSize: 1e8, // 100 MB
@@ -113,7 +115,7 @@ export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect {
metrics.socketio.gauge('realtime_connections').record(this.connectionCount);
}
checkVersion(client: Socket, version?: string) {
assertVersion(client: Socket, version?: string) {
if (
// @todo(@darkskygit): remove this flag after 0.12 goes stable
AFFiNE.featureFlags.syncClientVersionCheck &&
@@ -126,99 +128,94 @@ export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect {
version ? ` ${version}` : ''
} is outdated, please update to ${AFFiNE.version}`,
});
return {
error: new EventError(
throw new EventError(
EventErrorCode.VERSION_REJECTED,
`Client version ${version} is outdated, please update to ${AFFiNE.version}`
),
};
);
}
}
async joinWorkspace(
client: Socket,
room: `${string}:${'sync' | 'awareness'}`
) {
await client.join(room);
}
async leaveWorkspace(
client: Socket,
room: `${string}:${'sync' | 'awareness'}`
) {
await client.leave(room);
}
assertInWorkspace(client: Socket, room: `${string}:${'sync' | 'awareness'}`) {
if (!client.rooms.has(room)) {
throw new NotInWorkspaceError(room);
}
}
async assertWorkspaceAccessible(
workspaceId: string,
userId: string,
permission: Permission = Permission.Read
) {
if (
!(await this.permissions.isWorkspaceMember(
workspaceId,
userId,
permission
))
) {
throw new AccessDeniedError(workspaceId);
}
return null;
}
@Auth()
@SubscribeMessage('client-handshake-sync')
async handleClientHandshakeSync(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@MessageBody('workspaceId') workspaceId: string,
@MessageBody('version') version: string | undefined,
@ConnectedSocket() client: Socket
): Promise<EventResponse<{ clientId: string }>> {
const versionError = this.checkVersion(client, version);
if (versionError) {
return versionError;
}
const canWrite = await this.permissions.tryCheckWorkspace(
this.assertVersion(client, version);
await this.assertWorkspaceAccessible(
workspaceId,
user.id,
Permission.Write
);
if (canWrite) {
await client.join(`${workspaceId}:sync`);
await this.joinWorkspace(client, Sync(workspaceId));
return {
data: {
clientId: client.id,
},
};
} else {
return {
error: new AccessDeniedError(workspaceId),
};
}
}
@Auth()
@SubscribeMessage('client-handshake-awareness')
async handleClientHandshakeAwareness(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@MessageBody('workspaceId') workspaceId: string,
@MessageBody('version') version: string | undefined,
@ConnectedSocket() client: Socket
): Promise<EventResponse<{ clientId: string }>> {
const versionError = this.checkVersion(client, version);
if (versionError) {
return versionError;
}
const canWrite = await this.permissions.tryCheckWorkspace(
this.assertVersion(client, version);
await this.assertWorkspaceAccessible(
workspaceId,
user.id,
Permission.Write
);
if (canWrite) {
await client.join(`${workspaceId}:awareness`);
await this.joinWorkspace(client, Awareness(workspaceId));
return {
data: {
clientId: client.id,
},
};
} else {
return {
error: new AccessDeniedError(workspaceId),
};
}
}
/**
* @deprecated use `client-handshake-sync` and `client-handshake-awareness` instead
*/
@Auth()
@SubscribeMessage('client-handshake')
async handleClientHandShake(
@MessageBody() workspaceId: string,
@ConnectedSocket() client: Socket
): Promise<EventResponse<{ clientId: string }>> {
const versionError = this.checkVersion(client);
if (versionError) {
return versionError;
}
// should unreachable
return {
error: new AccessDeniedError(workspaceId),
};
}
@SubscribeMessage('client-leave-sync')
@@ -226,14 +223,9 @@ export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect {
@MessageBody() workspaceId: string,
@ConnectedSocket() client: Socket
): Promise<EventResponse> {
if (client.rooms.has(`${workspaceId}:sync`)) {
await client.leave(`${workspaceId}:sync`);
this.assertInWorkspace(client, Sync(workspaceId));
await this.leaveWorkspace(client, Sync(workspaceId));
return {};
} else {
return {
error: new NotInWorkspaceError(workspaceId),
};
}
}
@SubscribeMessage('client-leave-awareness')
@@ -241,14 +233,27 @@ export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect {
@MessageBody() workspaceId: string,
@ConnectedSocket() client: Socket
): Promise<EventResponse> {
if (client.rooms.has(`${workspaceId}:awareness`)) {
await client.leave(`${workspaceId}:awareness`);
this.assertInWorkspace(client, Awareness(workspaceId));
await this.leaveWorkspace(client, Awareness(workspaceId));
return {};
} else {
return {
error: new NotInWorkspaceError(workspaceId),
};
}
@SubscribeMessage('client-pre-sync')
async loadDocStats(
@ConnectedSocket() client: Socket,
@MessageBody()
{ workspaceId, timestamp }: { workspaceId: string; timestamp?: number }
): Promise<EventResponse<Record<string, number>>> {
this.assertInWorkspace(client, Sync(workspaceId));
const stats = await this.docManager.getDocTimestamps(
workspaceId,
timestamp
);
return {
data: stats,
};
}
@SubscribeMessage('client-update-v2')
@@ -264,33 +269,32 @@ export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect {
updates: string[];
},
@ConnectedSocket() client: Socket
): Promise<EventResponse<{ accepted: true }>> {
if (!client.rooms.has(`${workspaceId}:sync`)) {
return {
error: new NotInWorkspaceError(workspaceId),
};
}
): Promise<EventResponse<{ accepted: true; timestamp?: number }>> {
this.assertInWorkspace(client, Sync(workspaceId));
const docId = new DocID(guid, workspaceId);
client
.to(`${docId.workspace}:sync`)
.emit('server-updates', { workspaceId, guid, updates });
const buffers = updates.map(update => Buffer.from(update, 'base64'));
const timestamp = await this.docManager.batchPush(
docId.workspace,
docId.guid,
buffers
);
client
.to(Sync(workspaceId))
.emit('server-updates', { workspaceId, guid, updates, timestamp });
await this.docManager.batchPush(docId.workspace, docId.guid, buffers);
return {
data: {
accepted: true,
timestamp,
},
};
}
@Auth()
@SubscribeMessage('doc-load-v2')
async loadDocV2(
@ConnectedSocket() client: Socket,
@CurrentUser() user: UserType,
@MessageBody()
{
workspaceId,
@@ -301,23 +305,15 @@ export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect {
guid: string;
stateVector?: string;
}
): Promise<EventResponse<{ missing: string; state?: string }>> {
if (!client.rooms.has(`${workspaceId}:sync`)) {
const canRead = await this.permissions.tryCheckWorkspace(
workspaceId,
user.id
);
if (!canRead) {
return {
error: new AccessDeniedError(workspaceId),
};
}
}
): Promise<
EventResponse<{ missing: string; state?: string; timestamp: number }>
> {
this.assertInWorkspace(client, Sync(workspaceId));
const docId = new DocID(guid, workspaceId);
const doc = await this.docManager.get(docId.workspace, docId.guid);
const res = await this.docManager.get(docId.workspace, docId.guid);
if (!doc) {
if (!res) {
return {
error: new DocNotFoundError(workspaceId, docId.guid),
};
@@ -325,16 +321,17 @@ export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect {
const missing = Buffer.from(
encodeStateAsUpdate(
doc,
res.doc,
stateVector ? Buffer.from(stateVector, 'base64') : undefined
)
).toString('base64');
const state = Buffer.from(encodeStateVector(doc)).toString('base64');
const state = Buffer.from(encodeStateVector(res.doc)).toString('base64');
return {
data: {
missing,
state,
timestamp: res.timestamp,
},
};
}
@@ -344,34 +341,28 @@ export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect {
@MessageBody() workspaceId: string,
@ConnectedSocket() client: Socket
): Promise<EventResponse<{ clientId: string }>> {
if (client.rooms.has(`${workspaceId}:awareness`)) {
client.to(`${workspaceId}:awareness`).emit('new-client-awareness-init');
this.assertInWorkspace(client, Awareness(workspaceId));
client.to(Awareness(workspaceId)).emit('new-client-awareness-init');
return {
data: {
clientId: client.id,
},
};
} else {
return {
error: new NotInWorkspaceError(workspaceId),
};
}
}
@SubscribeMessage('awareness-update')
async handleHelpGatheringAwareness(
@MessageBody() message: { workspaceId: string; awarenessUpdate: string },
@MessageBody()
{
workspaceId,
awarenessUpdate,
}: { workspaceId: string; awarenessUpdate: string },
@ConnectedSocket() client: Socket
): Promise<EventResponse> {
if (client.rooms.has(`${message.workspaceId}:awareness`)) {
this.assertInWorkspace(client, Awareness(workspaceId));
client
.to(`${message.workspaceId}:awareness`)
.emit('server-awareness-broadcast', message);
.to(Awareness(workspaceId))
.emit('server-awareness-broadcast', { workspaceId, awarenessUpdate });
return {};
} else {
return {
error: new NotInWorkspaceError(message.workspaceId),
};
}
}
}

View File

@@ -6,15 +6,15 @@ import { StorageModule } from '../storage';
import { UserAvatarController } from './controller';
import { UserManagementResolver } from './management';
import { UserResolver } from './resolver';
import { UsersService } from './users';
import { UserService } from './service';
@Module({
imports: [StorageModule, FeatureModule, QuotaModule],
providers: [UserResolver, UserManagementResolver, UsersService],
providers: [UserResolver, UserManagementResolver, UserService],
controllers: [UserAvatarController],
exports: [UsersService],
exports: [UserService],
})
export class UsersModule {}
export class UserModule {}
export { UserService } from './service';
export { UserType } from './types';
export { UsersService } from './users';

View File

@@ -6,23 +6,21 @@ import {
import { Args, Context, Int, Mutation, Query, Resolver } from '@nestjs/graphql';
import { CloudThrottlerGuard, Throttle } from '../../fundamentals';
import { Auth, CurrentUser } from '../auth/guard';
import { AuthService } from '../auth/service';
import { CurrentUser } from '../auth/current-user';
import { sessionUser } from '../auth/service';
import { FeatureManagementService } from '../features';
import { UserService } from './service';
import { UserType } from './types';
import { UsersService } from './users';
/**
* User resolver
* All op rate limit: 10 req/m
*/
@UseGuards(CloudThrottlerGuard)
@Auth()
@Resolver(() => UserType)
export class UserManagementResolver {
constructor(
private readonly auth: AuthService,
private readonly users: UsersService,
private readonly users: UserService,
private readonly feature: FeatureManagementService
) {}
@@ -34,7 +32,7 @@ export class UserManagementResolver {
})
@Mutation(() => Int)
async addToEarlyAccess(
@CurrentUser() currentUser: UserType,
@CurrentUser() currentUser: CurrentUser,
@Args('email') email: string
): Promise<number> {
if (!this.feature.isStaff(currentUser.email)) {
@@ -44,7 +42,9 @@ export class UserManagementResolver {
if (user) {
return this.feature.addEarlyAccess(user.id);
} else {
const user = await this.auth.createAnonymousUser(email);
const user = await this.users.createAnonymousUser(email, {
registered: false,
});
return this.feature.addEarlyAccess(user.id);
}
}
@@ -57,7 +57,7 @@ export class UserManagementResolver {
})
@Mutation(() => Int)
async removeEarlyAccess(
@CurrentUser() currentUser: UserType,
@CurrentUser() currentUser: CurrentUser,
@Args('email') email: string
): Promise<number> {
if (!this.feature.isStaff(currentUser.email)) {
@@ -79,13 +79,15 @@ export class UserManagementResolver {
@Query(() => [UserType])
async earlyAccessUsers(
@Context() ctx: { isAdminQuery: boolean },
@CurrentUser() user: UserType
@CurrentUser() user: CurrentUser
): Promise<UserType[]> {
if (!this.feature.isStaff(user.email)) {
throw new ForbiddenException('You are not allowed to do this');
}
// allow query other user's subscription
ctx.isAdminQuery = true;
return this.feature.listEarlyAccess();
return this.feature.listEarlyAccess().then(users => {
return users.map(sessionUser);
});
}
}

View File

@@ -7,78 +7,50 @@ import {
ResolveField,
Resolver,
} from '@nestjs/graphql';
import { PrismaClient, type User } from '@prisma/client';
import type { User } from '@prisma/client';
import { PrismaClient } from '@prisma/client';
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
import { isNil, omitBy } from 'lodash-es';
import type { FileUpload } from '../../fundamentals';
import {
CloudThrottlerGuard,
EventEmitter,
type FileUpload,
PaymentRequiredException,
Throttle,
} from '../../fundamentals';
import { Auth, CurrentUser, Public, Publicable } from '../auth/guard';
import { FeatureManagementService } from '../features';
import { CurrentUser } from '../auth/current-user';
import { Public } from '../auth/guard';
import { sessionUser } from '../auth/service';
import { FeatureManagementService, FeatureType } from '../features';
import { QuotaService } from '../quota';
import { AvatarStorage } from '../storage';
import { UserService } from './service';
import {
DeleteAccount,
RemoveAvatar,
UpdateUserInput,
UserOrLimitedUser,
UserQuotaType,
UserType,
} from './types';
import { UsersService } from './users';
/**
* User resolver
* All op rate limit: 10 req/m
*/
@UseGuards(CloudThrottlerGuard)
@Auth()
@Resolver(() => UserType)
export class UserResolver {
constructor(
private readonly prisma: PrismaClient,
private readonly storage: AvatarStorage,
private readonly users: UsersService,
private readonly users: UserService,
private readonly feature: FeatureManagementService,
private readonly quota: QuotaService,
private readonly event: EventEmitter
) {}
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@Publicable()
@Query(() => UserType, {
name: 'currentUser',
description: 'Get current user',
nullable: true,
})
async currentUser(@CurrentUser() user?: UserType) {
if (!user) {
return null;
}
const storedUser = await this.users.findUserById(user.id);
if (!storedUser) {
throw new BadRequestException(`User ${user.id} not found in db`);
}
return {
id: storedUser.id,
name: storedUser.name,
email: storedUser.email,
emailVerified: storedUser.emailVerified,
avatarUrl: storedUser.avatarUrl,
createdAt: storedUser.createdAt,
hasPassword: !!storedUser.password,
};
}
@Throttle({
default: {
limit: 10,
@@ -92,9 +64,9 @@ export class UserResolver {
})
@Public()
async user(
@CurrentUser() currentUser?: UserType,
@CurrentUser() currentUser?: CurrentUser,
@Args('email') email?: string
) {
): Promise<typeof UserOrLimitedUser | null> {
if (!email || !(await this.feature.canEarlyAccess(email))) {
throw new PaymentRequiredException(
`You don't have early access permission\nVisit https://community.affine.pro/c/insider-general/ for more information`
@@ -102,16 +74,19 @@ export class UserResolver {
}
// TODO: need to limit a user can only get another user witch is in the same workspace
const user = await this.users.findUserByEmail(email);
if (currentUser) return user;
const user = await this.users.findUserWithHashedPasswordByEmail(email);
// return empty response when user not exists
if (!user) return null;
if (currentUser) {
return sessionUser(user);
}
// only return limited info when not logged in
return {
email: user?.email,
hasPassword: !!user?.password,
email: user.email,
hasPassword: !!user.password,
};
}
@@ -128,12 +103,21 @@ export class UserResolver {
name: 'invoiceCount',
description: 'Get user invoice count',
})
async invoiceCount(@CurrentUser() user: UserType) {
async invoiceCount(@CurrentUser() user: CurrentUser) {
return this.prisma.userInvoice.count({
where: { userId: user.id },
});
}
@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);
}
@Throttle({
default: {
limit: 10,
@@ -145,7 +129,7 @@ export class UserResolver {
description: 'Upload user avatar',
})
async uploadAvatar(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args({ name: 'avatar', type: () => GraphQLUpload })
avatar: FileUpload
) {
@@ -169,6 +153,33 @@ export class UserResolver {
});
}
@Throttle({
default: {
limit: 10,
ttl: 60,
},
})
@Mutation(() => UserType, {
name: 'updateProfile',
})
async updateUserProfile(
@CurrentUser() user: CurrentUser,
@Args('input', { type: () => UpdateUserInput }) input: UpdateUserInput
): Promise<UserType> {
input = omitBy(input, isNil);
if (Object.keys(input).length === 0) {
return user;
}
return sessionUser(
await this.prisma.user.update({
where: { id: user.id },
data: input,
})
);
}
@Throttle({
default: {
limit: 10,
@@ -179,7 +190,7 @@ export class UserResolver {
name: 'removeAvatar',
description: 'Remove user avatar',
})
async removeAvatar(@CurrentUser() user: UserType) {
async removeAvatar(@CurrentUser() user: CurrentUser) {
if (!user) {
throw new BadRequestException(`User not found`);
}
@@ -197,7 +208,9 @@ export class UserResolver {
},
})
@Mutation(() => DeleteAccount)
async deleteAccount(@CurrentUser() user: UserType): Promise<DeleteAccount> {
async deleteAccount(
@CurrentUser() user: CurrentUser
): Promise<DeleteAccount> {
const deletedUser = await this.users.deleteUser(user.id);
this.event.emit('user.deleted', deletedUser);
return { success: true };

View File

@@ -0,0 +1,133 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { Prisma, PrismaClient } from '@prisma/client';
import { Quota_FreePlanV1_1 } from '../quota/schema';
@Injectable()
export class UserService {
defaultUserSelect = {
id: true,
name: true,
email: true,
emailVerifiedAt: true,
avatarUrl: true,
registered: true,
} satisfies Prisma.UserSelect;
constructor(private readonly prisma: PrismaClient) {}
get userCreatingData() {
return {
name: 'Unnamed',
features: {
create: {
reason: 'created by invite sign up',
activated: true,
feature: {
connect: {
feature_version: Quota_FreePlanV1_1,
},
},
},
},
};
}
async createUser(data: Prisma.UserCreateInput) {
return this.prisma.user.create({
data: {
...this.userCreatingData,
...data,
},
});
}
async createAnonymousUser(
email: string,
data?: Partial<Prisma.UserCreateInput>
) {
const user = await this.findUserByEmail(email);
if (user) {
throw new BadRequestException('Email already exists');
}
return this.createUser({
email,
name: email.split('@')[0],
...data,
});
}
async findUserById(id: string) {
return this.prisma.user
.findUnique({
where: { id },
select: this.defaultUserSelect,
})
.catch(() => {
return null;
});
}
async findUserByEmail(email: string) {
return this.prisma.user.findFirst({
where: {
email: {
equals: email,
mode: 'insensitive',
},
},
select: this.defaultUserSelect,
});
}
/**
* supposed to be used only for `Credential SignIn`
*/
async findUserWithHashedPasswordByEmail(email: string) {
return this.prisma.user.findFirst({
where: {
email: {
equals: email,
mode: 'insensitive',
},
},
});
}
async findOrCreateUser(
email: string,
data?: Partial<Prisma.UserCreateInput>
) {
const user = await this.findUserByEmail(email);
if (user) {
return user;
}
return this.createAnonymousUser(email, data);
}
async fulfillUser(
email: string,
data: Partial<
Pick<Prisma.UserCreateInput, 'emailVerifiedAt' | 'registered'>
>
) {
return this.prisma.user.upsert({
select: this.defaultUserSelect,
where: {
email,
},
update: data,
create: {
email,
...this.userCreatingData,
...data,
},
});
}
async deleteUser(id: string) {
return this.prisma.user.delete({ where: { id } });
}
}

View File

@@ -1,7 +1,15 @@
import { createUnionType, Field, ID, ObjectType } from '@nestjs/graphql';
import {
createUnionType,
Field,
ID,
InputType,
ObjectType,
} from '@nestjs/graphql';
import type { User } from '@prisma/client';
import { SafeIntResolver } from 'graphql-scalars';
import { CurrentUser } from '../auth/current-user';
@ObjectType('UserQuotaHumanReadable')
export class UserQuotaHumanReadableType {
@Field({ name: 'name' })
@@ -42,7 +50,7 @@ export class UserQuotaType {
}
@ObjectType()
export class UserType implements Partial<User> {
export class UserType implements CurrentUser {
@Field(() => ID)
id!: string;
@@ -53,19 +61,25 @@ export class UserType implements Partial<User> {
email!: string;
@Field(() => String, { description: 'User avatar url', nullable: true })
avatarUrl: string | null = null;
avatarUrl!: string | null;
@Field(() => Date, { description: 'User email verified', nullable: true })
emailVerified: Date | null = null;
@Field({ description: 'User created date', nullable: true })
createdAt!: Date;
@Field(() => Boolean, {
description: 'User email verified',
})
emailVerified!: boolean;
@Field(() => Boolean, {
description: 'User password has been set',
nullable: true,
})
hasPassword?: boolean;
hasPassword!: boolean | null;
@Field(() => Date, {
deprecationReason: 'useless',
description: 'User email verified',
nullable: true,
})
createdAt?: Date | null;
}
@ObjectType()
@@ -77,7 +91,7 @@ export class LimitedUserType implements Partial<User> {
description: 'User password has been set',
nullable: true,
})
hasPassword?: boolean;
hasPassword!: boolean | null;
}
export const UserOrLimitedUser = createUnionType({
@@ -101,3 +115,9 @@ export class RemoveAvatar {
@Field()
success!: boolean;
}
@InputType()
export class UpdateUserInput implements Partial<User> {
@Field({ description: 'User name', nullable: true })
name?: string;
}

View File

@@ -1,32 +0,0 @@
import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class UsersService {
constructor(private readonly prisma: PrismaClient) {}
async findUserByEmail(email: string) {
return this.prisma.user.findFirst({
where: {
email: {
equals: email,
mode: 'insensitive',
},
},
});
}
async findUserById(id: string) {
return this.prisma.user
.findUnique({
where: { id },
})
.catch(() => {
return null;
});
}
async deleteUser(id: string) {
return this.prisma.user.delete({ where: { id } });
}
}

View File

@@ -0,0 +1,56 @@
import { BadRequestException } from '@nestjs/common';
import z from 'zod';
function getAuthCredentialValidator() {
const email = z.string().email({ message: 'Invalid email address' });
let password = z.string();
password = password
.min(AFFiNE.auth.password.minLength, {
message: `Password must be ${AFFiNE.auth.password.minLength} or more charactors long`,
})
.max(AFFiNE.auth.password.maxLength, {
message: `Password must be ${AFFiNE.auth.password.maxLength} or fewer charactors long`,
});
return z
.object({
email,
password,
})
.required();
}
function assertValid<T>(z: z.ZodType<T>, value: unknown) {
const result = z.safeParse(value);
if (!result.success) {
const firstIssue = result.error.issues.at(0);
if (firstIssue) {
throw new BadRequestException(firstIssue.message);
} else {
throw new BadRequestException('Invalid credential');
}
}
}
export function assertValidEmail(email: string) {
assertValid(getAuthCredentialValidator().shape.email, email);
}
export function assertValidPassword(password: string) {
assertValid(getAuthCredentialValidator().shape.password, password);
}
export function assertValidCredential(credential: {
email: string;
password: string;
}) {
assertValid(getAuthCredentialValidator(), credential);
}
export const validators = {
assertValidEmail,
assertValidPassword,
assertValidCredential,
};

View File

@@ -11,10 +11,9 @@ import { PrismaClient } from '@prisma/client';
import type { Response } from 'express';
import { CallTimer } from '../../fundamentals';
import { Auth, CurrentUser, Publicable } from '../auth';
import { CurrentUser, Public } from '../auth';
import { DocHistoryManager, DocManager } from '../doc';
import { WorkspaceBlobStorage } from '../storage';
import { UserType } from '../users';
import { DocID } from '../utils/doc';
import { PermissionService, PublicPageMode } from './permission';
import { Permission } from './types';
@@ -33,6 +32,7 @@ export class WorkspacesController {
// get workspace blob
//
// NOTE: because graphql can't represent a File, so we have to use REST API to get blob
@Public()
@Get('/:id/blobs/:name')
@CallTimer('controllers', 'workspace_get_blob')
async blob(
@@ -51,7 +51,7 @@ export class WorkspacesController {
// metadata should always exists if body is not null
if (metadata) {
res.setHeader('content-type', metadata.contentType);
res.setHeader('last-modified', metadata.lastModified.toISOString());
res.setHeader('last-modified', metadata.lastModified.toUTCString());
res.setHeader('content-length', metadata.contentLength);
} else {
this.logger.warn(`Blob ${workspaceId}/${name} has no metadata`);
@@ -62,12 +62,11 @@ export class WorkspacesController {
}
// get doc binary
@Public()
@Get('/:id/docs/:guid')
@Auth()
@Publicable()
@CallTimer('controllers', 'workspace_get_doc')
async doc(
@CurrentUser() user: UserType | undefined,
@CurrentUser() user: CurrentUser | undefined,
@Param('id') ws: string,
@Param('guid') guid: string,
@Res() res: Response
@@ -84,9 +83,12 @@ export class WorkspacesController {
throw new ForbiddenException('Permission denied');
}
const update = await this.docManager.getBinary(docId.workspace, docId.guid);
const binResponse = await this.docManager.getBinary(
docId.workspace,
docId.guid
);
if (!update) {
if (!binResponse) {
throw new NotFoundException('Doc not found');
}
@@ -107,15 +109,18 @@ export class WorkspacesController {
}
res.setHeader('content-type', 'application/octet-stream');
res.setHeader('cache-control', 'no-cache');
res.send(update);
res.setHeader(
'last-modified',
new Date(binResponse.timestamp).toUTCString()
);
res.setHeader('cache-control', 'private, max-age=2592000');
res.send(binResponse.binary);
}
@Get('/:id/docs/:guid/histories/:timestamp')
@Auth()
@CallTimer('controllers', 'workspace_get_history')
async history(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Param('id') ws: string,
@Param('guid') guid: string,
@Param('timestamp') timestamp: string,
@@ -144,7 +149,7 @@ export class WorkspacesController {
if (history) {
res.setHeader('content-type', 'application/octet-stream');
res.setHeader('cache-control', 'public, max-age=2592000, immutable');
res.setHeader('cache-control', 'private, max-age=2592000, immutable');
res.send(history.blob);
} else {
throw new NotFoundException('Doc history not found');

View File

@@ -4,7 +4,7 @@ import { DocModule } from '../doc';
import { FeatureModule } from '../features';
import { QuotaModule } from '../quota';
import { StorageModule } from '../storage';
import { UsersService } from '../users';
import { UserModule } from '../user';
import { WorkspacesController } from './controller';
import { WorkspaceManagementResolver } from './management';
import { PermissionService } from './permission';
@@ -16,13 +16,12 @@ import {
} from './resolvers';
@Module({
imports: [DocModule, FeatureModule, QuotaModule, StorageModule],
imports: [DocModule, FeatureModule, QuotaModule, StorageModule, UserModule],
controllers: [WorkspacesController],
providers: [
WorkspaceResolver,
WorkspaceManagementResolver,
PermissionService,
UsersService,
PagePermissionResolver,
DocHistoryResolver,
WorkspaceBlobResolver,

View File

@@ -10,14 +10,12 @@ import {
} from '@nestjs/graphql';
import { CloudThrottlerGuard, Throttle } from '../../fundamentals';
import { Auth, CurrentUser } from '../auth';
import { CurrentUser } from '../auth';
import { FeatureManagementService, FeatureType } from '../features';
import { UserType } from '../users';
import { PermissionService } from './permission';
import { WorkspaceType } from './types';
@UseGuards(CloudThrottlerGuard)
@Auth()
@Resolver(() => WorkspaceType)
export class WorkspaceManagementResolver {
constructor(
@@ -33,7 +31,7 @@ export class WorkspaceManagementResolver {
})
@Mutation(() => Int)
async addWorkspaceFeature(
@CurrentUser() currentUser: UserType,
@CurrentUser() currentUser: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('feature', { type: () => FeatureType }) feature: FeatureType
): Promise<number> {
@@ -52,7 +50,7 @@ export class WorkspaceManagementResolver {
})
@Mutation(() => Int)
async removeWorkspaceFeature(
@CurrentUser() currentUser: UserType,
@CurrentUser() currentUser: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('feature', { type: () => FeatureType }) feature: FeatureType
): Promise<boolean> {
@@ -71,7 +69,7 @@ export class WorkspaceManagementResolver {
})
@Query(() => [WorkspaceType])
async listWorkspaceFeatures(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('feature', { type: () => FeatureType }) feature: FeatureType
): Promise<WorkspaceType[]> {
if (!this.feature.isStaff(user.email)) {
@@ -83,7 +81,7 @@ export class WorkspaceManagementResolver {
@Mutation(() => Boolean)
async setWorkspaceExperimentalFeature(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('feature', { type: () => FeatureType }) feature: FeatureType,
@Args('enable') enable: boolean
@@ -117,7 +115,7 @@ export class WorkspaceManagementResolver {
complexity: 2,
})
async availableFeatures(
@CurrentUser() user: UserType
@CurrentUser() user: CurrentUser
): Promise<FeatureType[]> {
const isEarlyAccessUser = await this.feature.isEarlyAccessUser(user.email);
if (isEarlyAccessUser) {

View File

@@ -1,5 +1,6 @@
import { ForbiddenException, Injectable } from '@nestjs/common';
import { type Prisma, PrismaClient } from '@prisma/client';
import type { Prisma } from '@prisma/client';
import { PrismaClient } from '@prisma/client';
import { Permission } from './types';
@@ -73,6 +74,28 @@ export class PermissionService {
return this.tryCheckPage(ws, id, user);
}
/**
* Returns whether a given user is a member of a workspace and has the given or higher permission.
*/
async isWorkspaceMember(
ws: string,
user: string,
permission: Permission
): Promise<boolean> {
const count = await this.prisma.workspaceUserPermission.count({
where: {
workspaceId: ws,
userId: user,
accepted: true,
type: {
gte: permission,
},
},
});
return count !== 0;
}
async checkWorkspace(
ws: string,
user?: string,

View File

@@ -16,22 +16,20 @@ import {
import { SafeIntResolver } from 'graphql-scalars';
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
import type { FileUpload } from '../../../fundamentals';
import {
CloudThrottlerGuard,
type FileUpload,
MakeCache,
PreventCache,
} from '../../../fundamentals';
import { Auth, CurrentUser } from '../../auth';
import { CurrentUser } from '../../auth';
import { FeatureManagementService, FeatureType } from '../../features';
import { QuotaManagementService } from '../../quota';
import { WorkspaceBlobStorage } from '../../storage';
import { UserType } from '../../users';
import { PermissionService } from '../permission';
import { Permission, WorkspaceBlobSizes, WorkspaceType } from '../types';
@UseGuards(CloudThrottlerGuard)
@Auth()
@Resolver(() => WorkspaceType)
export class WorkspaceBlobResolver {
logger = new Logger(WorkspaceBlobResolver.name);
@@ -47,7 +45,7 @@ export class WorkspaceBlobResolver {
complexity: 2,
})
async blobs(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Parent() workspace: WorkspaceType
) {
await this.permissions.checkWorkspace(workspace.id, user.id);
@@ -74,7 +72,7 @@ export class WorkspaceBlobResolver {
})
@MakeCache(['blobs'], ['workspaceId'])
async listBlobs(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string
) {
await this.permissions.checkWorkspace(workspaceId, user.id);
@@ -90,7 +88,7 @@ export class WorkspaceBlobResolver {
@Query(() => WorkspaceBlobSizes, {
deprecationReason: 'use `user.storageUsage` instead',
})
async collectAllBlobSizes(@CurrentUser() user: UserType) {
async collectAllBlobSizes(@CurrentUser() user: CurrentUser) {
const size = await this.quota.getUserUsage(user.id);
return { size };
}
@@ -102,7 +100,7 @@ export class WorkspaceBlobResolver {
deprecationReason: 'no more needed',
})
async checkBlobSize(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('size', { type: () => SafeIntResolver }) blobSize: number
) {
@@ -121,7 +119,7 @@ export class WorkspaceBlobResolver {
@Mutation(() => String)
@PreventCache(['blobs'], ['workspaceId'])
async setBlob(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args({ name: 'blob', type: () => GraphQLUpload })
blob: FileUpload
@@ -199,7 +197,7 @@ export class WorkspaceBlobResolver {
@Mutation(() => Boolean)
@PreventCache(['blobs'], ['workspaceId'])
async deleteBlob(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('hash') name: string
) {

View File

@@ -13,9 +13,8 @@ import {
import type { SnapshotHistory } from '@prisma/client';
import { CloudThrottlerGuard } from '../../../fundamentals';
import { Auth, CurrentUser } from '../../auth';
import { CurrentUser } from '../../auth';
import { DocHistoryManager } from '../../doc';
import { UserType } from '../../users';
import { DocID } from '../../utils/doc';
import { PermissionService } from '../permission';
import { Permission, WorkspaceType } from '../types';
@@ -68,10 +67,9 @@ export class DocHistoryResolver {
);
}
@Auth()
@Mutation(() => Date)
async recoverDoc(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('guid') guid: string,
@Args({ name: 'timestamp', type: () => GraphQLISODateTime }) timestamp: Date

View File

@@ -9,14 +9,11 @@ import {
ResolveField,
Resolver,
} from '@nestjs/graphql';
import {
PrismaClient,
type WorkspacePage as PrismaWorkspacePage,
} from '@prisma/client';
import type { WorkspacePage as PrismaWorkspacePage } from '@prisma/client';
import { PrismaClient } from '@prisma/client';
import { CloudThrottlerGuard } from '../../../fundamentals';
import { Auth, CurrentUser } from '../../auth';
import { UserType } from '../../users';
import { CurrentUser } from '../../auth';
import { DocID } from '../../utils/doc';
import { PermissionService, PublicPageMode } from '../permission';
import { Permission, WorkspaceType } from '../types';
@@ -42,7 +39,6 @@ class WorkspacePage implements Partial<PrismaWorkspacePage> {
}
@UseGuards(CloudThrottlerGuard)
@Auth()
@Resolver(() => WorkspaceType)
export class PagePermissionResolver {
constructor(
@@ -90,7 +86,7 @@ export class PagePermissionResolver {
deprecationReason: 'renamed to publicPage',
})
async deprecatedSharePage(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('pageId') pageId: string
) {
@@ -100,7 +96,7 @@ export class PagePermissionResolver {
@Mutation(() => WorkspacePage)
async publishPage(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('pageId') pageId: string,
@Args({
@@ -134,7 +130,7 @@ export class PagePermissionResolver {
deprecationReason: 'use revokePublicPage',
})
async deprecatedRevokePage(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('pageId') pageId: string
) {
@@ -144,7 +140,7 @@ export class PagePermissionResolver {
@Mutation(() => WorkspacePage)
async revokePublicPage(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('pageId') pageId: string
) {

View File

@@ -15,23 +15,24 @@ import {
ResolveField,
Resolver,
} from '@nestjs/graphql';
import { PrismaClient, type User } from '@prisma/client';
import { PrismaClient } from '@prisma/client';
import { getStreamAsBuffer } from 'get-stream';
import GraphQLUpload from 'graphql-upload/GraphQLUpload.mjs';
import { applyUpdate, Doc } from 'yjs';
import type { FileUpload } from '../../../fundamentals';
import {
CloudThrottlerGuard,
EventEmitter,
type FileUpload,
MailService,
MutexService,
Throttle,
TooManyRequestsException,
} from '../../../fundamentals';
import { Auth, CurrentUser, Public } from '../../auth';
import { AuthService } from '../../auth/service';
import { CurrentUser, Public } from '../../auth';
import { QuotaManagementService, QuotaQueryType } from '../../quota';
import { WorkspaceBlobStorage } from '../../storage';
import { UsersService, UserType } from '../../users';
import { UserService, UserType } from '../../user';
import { PermissionService } from '../permission';
import {
InvitationType,
@@ -48,20 +49,19 @@ import { defaultWorkspaceAvatar } from '../utils';
* Other rate limit: 120 req/m
*/
@UseGuards(CloudThrottlerGuard)
@Auth()
@Resolver(() => WorkspaceType)
export class WorkspaceResolver {
private readonly logger = new Logger(WorkspaceResolver.name);
constructor(
private readonly auth: AuthService,
private readonly mailer: MailService,
private readonly prisma: PrismaClient,
private readonly permissions: PermissionService,
private readonly quota: QuotaManagementService,
private readonly users: UsersService,
private readonly users: UserService,
private readonly event: EventEmitter,
private readonly blobStorage: WorkspaceBlobStorage
private readonly blobStorage: WorkspaceBlobStorage,
private readonly mutex: MutexService
) {}
@ResolveField(() => Permission, {
@@ -69,7 +69,7 @@ export class WorkspaceResolver {
complexity: 2,
})
async permission(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Parent() workspace: WorkspaceType
) {
// may applied in workspaces query
@@ -160,7 +160,7 @@ export class WorkspaceResolver {
complexity: 2,
})
async isOwner(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string
) {
const data = await this.permissions.tryGetWorkspaceOwner(workspaceId);
@@ -172,7 +172,7 @@ export class WorkspaceResolver {
description: 'Get all accessible workspaces for current user',
complexity: 2,
})
async workspaces(@CurrentUser() user: User) {
async workspaces(@CurrentUser() user: CurrentUser) {
const data = await this.prisma.workspaceUserPermission.findMany({
where: {
userId: user.id,
@@ -216,7 +216,7 @@ export class WorkspaceResolver {
@Query(() => WorkspaceType, {
description: 'Get workspace by id',
})
async workspace(@CurrentUser() user: UserType, @Args('id') id: string) {
async workspace(@CurrentUser() user: CurrentUser, @Args('id') id: string) {
await this.permissions.checkWorkspace(id, user.id);
const workspace = await this.prisma.workspace.findUnique({ where: { id } });
@@ -231,7 +231,7 @@ export class WorkspaceResolver {
description: 'Create a new workspace',
})
async createWorkspace(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
// we no longer support init workspace with a preload file
// use sync system to uploading them once created
@Args({ name: 'init', type: () => GraphQLUpload, nullable: true })
@@ -289,7 +289,7 @@ export class WorkspaceResolver {
description: 'Update workspace',
})
async updateWorkspace(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args({ name: 'input', type: () => UpdateWorkspaceInput })
{ id, ...updates }: UpdateWorkspaceInput
) {
@@ -304,7 +304,10 @@ export class WorkspaceResolver {
}
@Mutation(() => Boolean)
async deleteWorkspace(@CurrentUser() user: UserType, @Args('id') id: string) {
async deleteWorkspace(
@CurrentUser() user: CurrentUser,
@Args('id') id: string
) {
await this.permissions.checkWorkspace(id, user.id, Permission.Owner);
await this.prisma.workspace.delete({
@@ -320,7 +323,7 @@ export class WorkspaceResolver {
@Mutation(() => String)
async invite(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('email') email: string,
@Args('permission', { type: () => Permission }) permission: Permission,
@@ -336,6 +339,14 @@ export class WorkspaceResolver {
throw new ForbiddenException('Cannot change owner');
}
try {
// lock to prevent concurrent invite
const lockFlag = `invite:${workspaceId}`;
await using lock = await this.mutex.lock(lockFlag);
if (!lock) {
return new TooManyRequestsException('Server is busy');
}
// member limit check
const [memberCount, quota] = await Promise.all([
this.prisma.workspaceUserPermission.count({
@@ -344,12 +355,13 @@ export class WorkspaceResolver {
this.quota.getWorkspaceUsage(workspaceId),
]);
if (memberCount >= quota.memberLimit) {
throw new PayloadTooLargeException('Workspace member limit reached.');
return new PayloadTooLargeException('Workspace member limit reached.');
}
let target = await this.users.findUserByEmail(email);
if (target) {
const originRecord = await this.prisma.workspaceUserPermission.findFirst({
const originRecord =
await this.prisma.workspaceUserPermission.findFirst({
where: {
workspaceId,
userId: target.id,
@@ -358,7 +370,9 @@ export class WorkspaceResolver {
// only invite if the user is not already in the workspace
if (originRecord) return originRecord.id;
} else {
target = await this.auth.createAnonymousUser(email);
target = await this.users.createAnonymousUser(email, {
registered: false,
});
}
const inviteId = await this.permissions.grant(
@@ -402,6 +416,10 @@ export class WorkspaceResolver {
}
}
return inviteId;
} catch (e) {
this.logger.error('failed to invite user', e);
return new TooManyRequestsException('Server is busy');
}
}
@Throttle({
@@ -470,7 +488,7 @@ export class WorkspaceResolver {
@Mutation(() => Boolean)
async revoke(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('userId') userId: string
) {
@@ -514,7 +532,7 @@ export class WorkspaceResolver {
@Mutation(() => Boolean)
async leaveWorkspace(
@CurrentUser() user: UserType,
@CurrentUser() user: CurrentUser,
@Args('workspaceId') workspaceId: string,
@Args('workspaceName') workspaceName: string,
@Args('sendLeaveMail', { nullable: true }) sendLeaveMail: boolean

View File

@@ -11,7 +11,7 @@ import {
import type { Workspace } from '@prisma/client';
import { SafeIntResolver } from 'graphql-scalars';
import { UserType } from '../users/types';
import { UserType } from '../user/types';
export enum Permission {
Read = 0,

View File

@@ -1,6 +1,6 @@
import { readdirSync } from 'node:fs';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { Logger } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
@@ -23,9 +23,11 @@ export async function collectMigrations(): Promise<Migration[]> {
)
.map(desc => join(folder, desc));
migrationFiles.sort((a, b) => a.localeCompare(b));
const migrations: Migration[] = await Promise.all(
migrationFiles.map(async file => {
return import(file).then(mod => {
return import(pathToFileURL(file).href).then(mod => {
const migration = mod[Object.keys(mod)[0]];
return {

View File

@@ -3,9 +3,8 @@ import '../prelude';
import { Logger } from '@nestjs/common';
import { CommandFactory } from 'nest-commander';
import { CliAppModule } from './app';
async function bootstrap() {
const { CliAppModule } = await import('./app');
await CommandFactory.run(CliAppModule, new Logger()).catch(e => {
console.error(e);
process.exit(1);

View File

@@ -1,4 +1,5 @@
import { PrismaClient, type User } from '@prisma/client';
import type { User } from '@prisma/client';
import { PrismaClient } from '@prisma/client';
export class UnamedAccount1703756315970 {
// do the migration

View File

@@ -0,0 +1,39 @@
import { PrismaClient } from '@prisma/client';
import { loop } from './utils/loop';
export class Oauth1710319359062 {
// do the migration
static async up(db: PrismaClient) {
await loop(async (skip, take) => {
const oldRecords = await db.deprecatedNextAuthAccount.findMany({
skip,
take,
orderBy: {
providerAccountId: 'asc',
},
});
await db.connectedAccount.createMany({
data: oldRecords.map(record => ({
userId: record.userId,
provider: record.provider,
scope: record.scope,
providerAccountId: record.providerAccountId,
accessToken: record.access_token,
refreshToken: record.refresh_token,
expiresAt: record.expires_at
? new Date(record.expires_at * 1000)
: null,
})),
});
return oldRecords.length;
}, 10);
}
// revert the migration
static async down(db: PrismaClient) {
await db.connectedAccount.deleteMany({});
}
}

View File

@@ -1,13 +1,15 @@
import { ModuleRef } from '@nestjs/core';
import { hash } from '@node-rs/argon2';
import { PrismaClient } from '@prisma/client';
import { Config } from '../../fundamentals';
import { UserService } from '../../core/user';
import { Config, CryptoHelper } from '../../fundamentals';
export class SelfHostAdmin1605053000403 {
export class SelfHostAdmin99999999 {
// do the migration
static async up(db: PrismaClient, ref: ModuleRef) {
static async up(_db: PrismaClient, ref: ModuleRef) {
const config = ref.get(Config, { strict: false });
const crypto = ref.get(CryptoHelper, { strict: false });
const user = ref.get(UserService, { strict: false });
if (config.isSelfhosted) {
if (
!process.env.AFFINE_ADMIN_EMAIL ||
@@ -17,13 +19,12 @@ export class SelfHostAdmin1605053000403 {
'You have to set AFFINE_ADMIN_EMAIL and AFFINE_ADMIN_PASSWORD environment variables to generate the initial user for self-hosted AFFiNE Server.'
);
}
await db.user.create({
data: {
await user.findOrCreateUser(process.env.AFFINE_ADMIN_EMAIL, {
name: 'AFFINE First User',
email: process.env.AFFINE_ADMIN_EMAIL,
emailVerified: new Date(),
password: await hash(process.env.AFFINE_ADMIN_PASSWORD),
},
emailVerifiedAt: new Date(),
password: await crypto.encryptPassword(
process.env.AFFINE_ADMIN_PASSWORD
),
});
}
}

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