feat(core): onenote importer (#15198)

#### PR Dependency Tree


* **PR #15198** 👈

This tree was auto-generated by
[Charcoal](https://github.com/danerwilliams/charcoal)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added OneNote import support for `.one`, `.onetoc2`, and `.onepkg`,
including OneNote-to-markdown content conversion.
* OneNote now appears in the import flow with a dedicated label and
format tooltip, and file pickers recognize the new OneNote type.

* **Bug Fixes**
* Import options that are desktop-only are now disabled when not running
in the desktop app, with clear messaging.
* Improved imported asset handling by converting non-image embedded
assets into attachments for more consistent results.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
DarkSky
2026-07-06 06:20:52 +08:00
committed by GitHub
parent 8d72e4dc29
commit 9581432d21
22 changed files with 1621 additions and 112 deletions
@@ -1,4 +1,5 @@
import ava, { TestFn } from 'ava';
import { PrismaClient } from '@prisma/client';
import ava, { type TestFn } from 'ava';
import Sinon from 'sinon';
import { EmailAlreadyUsed, EventBus } from '../../base';
@@ -9,6 +10,7 @@ import { createTestingModule, sleep, type TestingModule } from '../utils';
interface Context {
module: TestingModule;
models: Models;
db: PrismaClient;
user: UserModel;
}
@@ -19,6 +21,7 @@ test.before(async t => {
t.context.user = module.get(UserModel);
t.context.models = module.get(Models);
t.context.db = module.get(PrismaClient);
t.context.module = module;
});
@@ -262,6 +265,38 @@ test('should delete user', async t => {
t.is(user2, null);
});
test('should delete user with pending invitation missing normalized email', async t => {
const owner = await t.context.user.create({
email: 'owner@affine.pro',
});
const invitee = await t.context.user.create({
email: 'invitee@affine.pro',
registered: false,
});
const workspace = await t.context.models.workspace.create(owner.id);
const invitation = await t.context.db.workspaceInvitation.create({
data: {
workspaceId: workspace.id,
inviteeUserId: invitee.id,
inviterUserId: owner.id,
requestedRole: 'member',
status: 'pending',
kind: 'email',
},
});
await t.context.user.delete(invitee.id);
t.is(
await t.context.db.workspaceInvitation.findUnique({
where: { id: invitation.id },
}),
null
);
t.is(await t.context.user.get(invitee.id), null);
});
test('should trigger user.deleted event', async t => {
const event = t.context.module.get(EventBus);
const spy = Sinon.spy();