Compare commits

...

6 Commits

Author SHA1 Message Date
renovate[bot]
b3baf6ccd4 chore: bump up nestjs 2026-03-16 13:13:37 +00:00
DarkSky
121c0d172d feat(server): improve doc tools error handle (#14662)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Centralized sync/status messages for cloud document sync and explicit
user-facing error types.
* Frontend helpers to detect and display tool errors with friendly
names.

* **Bug Fixes**
* Consistent, actionable error reporting for document and attachment
reads instead of silent failures.
* Search and semantic tools now validate workspace sync and permissions
and return clear responses.

* **Tests**
* Added comprehensive tests covering document/blob reads, search tools,
and sync/error paths.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-03-16 02:20:35 +08:00
renovate[bot]
8f03090780 chore: bump up Lakr233/MarkdownView version to from: "3.8.2" (#14658)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
|
[Lakr233/MarkdownView](https://redirect.github.com/Lakr233/MarkdownView)
| minor | `from: "3.6.3"` → `from: "3.8.2"` |

---

### Release Notes

<details>
<summary>Lakr233/MarkdownView (Lakr233/MarkdownView)</summary>

###
[`v3.8.2`](https://redirect.github.com/Lakr233/MarkdownView/compare/3.8.1...3.8.2)

[Compare
Source](https://redirect.github.com/Lakr233/MarkdownView/compare/3.8.1...3.8.2)

###
[`v3.8.1`](https://redirect.github.com/Lakr233/MarkdownView/compare/3.8.0...3.8.1)

[Compare
Source](https://redirect.github.com/Lakr233/MarkdownView/compare/3.8.0...3.8.1)

###
[`v3.8.0`](https://redirect.github.com/Lakr233/MarkdownView/compare/3.7.0...3.8.0)

[Compare
Source](https://redirect.github.com/Lakr233/MarkdownView/compare/3.7.0...3.8.0)

###
[`v3.7.0`](https://redirect.github.com/Lakr233/MarkdownView/compare/3.6.3...3.7.0)

[Compare
Source](https://redirect.github.com/Lakr233/MarkdownView/compare/3.6.3...3.7.0)

</details>

---

### Configuration

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

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

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

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

---

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

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My42Ni40IiwidXBkYXRlZEluVmVyIjoiNDMuNjYuNCIsInRhcmdldEJyYW5jaCI6ImNhbmFyeSIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-16 00:57:48 +08:00
renovate[bot]
8125cc0e75 chore: bump up Lakr233/ListViewKit version to from: "1.2.0" (#14617)
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [Lakr233/ListViewKit](https://redirect.github.com/Lakr233/ListViewKit)
| minor | `from: "1.1.8"` → `from: "1.2.0"` |

---

### Release Notes

<details>
<summary>Lakr233/ListViewKit (Lakr233/ListViewKit)</summary>

###
[`v1.2.0`](https://redirect.github.com/Lakr233/ListViewKit/compare/1.1.8...1.2.0)

[Compare
Source](https://redirect.github.com/Lakr233/ListViewKit/compare/1.1.8...1.2.0)

</details>

---

### Configuration

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

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

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

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

---

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

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My41OS4wIiwidXBkYXRlZEluVmVyIjoiNDMuNTkuMCIsInRhcmdldEJyYW5jaCI6ImNhbmFyeSIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-14 23:45:32 +08:00
renovate[bot]
f537a75f01 chore: bump up file-type version to v21.3.2 [SECURITY] (#14655)
This PR contains the following updates:

| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [file-type](https://redirect.github.com/sindresorhus/file-type) |
[`21.3.1` →
`21.3.2`](https://renovatebot.com/diffs/npm/file-type/21.3.1/21.3.2) |
![age](https://developer.mend.io/api/mc/badges/age/npm/file-type/21.3.2?slim=true)
|
![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/file-type/21.3.1/21.3.2?slim=true)
|

### GitHub Vulnerability Alerts

####
[CVE-2026-31808](https://redirect.github.com/sindresorhus/file-type/security/advisories/GHSA-5v7r-6r5c-r473)

### Impact
A denial of service vulnerability exists in the ASF (WMV/WMA) file type
detection parser. When parsing a crafted input where an ASF sub-header
has a `size` field of zero, the parser enters an infinite loop. The
`payload` value becomes negative (-24), causing
`tokenizer.ignore(payload)` to move the read position backwards, so the
same sub-header is read repeatedly forever.

Any application that uses `file-type` to detect the type of
untrusted/attacker-controlled input is affected. An attacker can stall
the Node.js event loop with a 55-byte payload.

### Patches
Fixed in version 21.3.1. Users should upgrade to >= 21.3.1.

### Workarounds
Validate or limit the size of input buffers before passing them to
`file-type`, or run file type detection in a worker thread with a
timeout.

### References
- Fix commit: 319abf871b50ba2fa221b4a7050059f1ae096f4f

### Reporter

crnkovic@lokvica.com

####
[CVE-2026-32630](https://redirect.github.com/sindresorhus/file-type/security/advisories/GHSA-j47w-4g3g-c36v)

## Summary

A crafted ZIP file can trigger excessive memory growth during type
detection in `file-type` when using `fileTypeFromBuffer()`,
`fileTypeFromBlob()`, or `fileTypeFromFile()`.

In affected versions, the ZIP inflate output limit is enforced for
stream-based detection, but not for known-size inputs. As a result, a
small compressed ZIP can cause `file-type` to inflate and process a much
larger payload while probing ZIP-based formats such as OOXML. In testing
on `file-type` `21.3.1`, a ZIP of about `255 KB` caused about `257 MB`
of RSS growth during `fileTypeFromBuffer()`.

This is an availability issue. Applications that use these APIs on
untrusted uploads can be forced to consume large amounts of memory and
may become slow or crash.

## Root Cause

The ZIP detection logic applied different limits depending on whether
the tokenizer had a known file size.

For stream inputs, ZIP probing was bounded by
`maximumZipEntrySizeInBytes` (`1 MiB`). For known-size inputs such as
buffers, blobs, and files, the code instead used
`Number.MAX_SAFE_INTEGER` in two relevant places:

```js
const maximumContentTypesEntrySize = hasUnknownFileSize(tokenizer)
	? maximumZipEntrySizeInBytes
	: Number.MAX_SAFE_INTEGER;
```

and:

```js
const maximumLength = hasUnknownFileSize(this.tokenizer)
	? maximumZipEntrySizeInBytes
	: Number.MAX_SAFE_INTEGER;
```

Together, these checks allowed a crafted ZIP to bypass the intended
inflate limit for known-size APIs and force large decompression during
detection of entries such as `[Content_Types].xml`.

## Proof of Concept

```js
import {fileTypeFromBuffer} from 'file-type';
import archiver from 'archiver';
import {Writable} from 'node:stream';

async function createZipBomb(sizeInMegabytes) {
	return new Promise((resolve, reject) => {
		const chunks = [];
		const writable = new Writable({
			write(chunk, encoding, callback) {
				chunks.push(chunk);
				callback();
			},
		});

		const archive = archiver('zip', {zlib: {level: 9}});
		archive.pipe(writable);
		writable.on('finish', () => {
			resolve(Buffer.concat(chunks));
		});
		archive.on('error', reject);

		const xmlPrefix = '<?xml version="1.0"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">';
		const padding = Buffer.alloc(sizeInMegabytes * 1024 * 1024 - xmlPrefix.length, 0x20);
		archive.append(Buffer.concat([Buffer.from(xmlPrefix), padding]), {name: '[Content_Types].xml'});
		archive.finalize();
	});
}

const zip = await createZipBomb(256);
console.log('ZIP size (KB):', (zip.length / 1024).toFixed(0));

const before = process.memoryUsage().rss;
await fileTypeFromBuffer(zip);
const after = process.memoryUsage().rss;

console.log('RSS growth (MB):', ((after - before) / 1024 / 1024).toFixed(0));
```

Observed on `file-type` `21.3.1`:
- ZIP size: about `255 KB`
- RSS growth during detection: about `257 MB`

## Affected APIs

Affected:
- `fileTypeFromBuffer()`
- `fileTypeFromBlob()`
- `fileTypeFromFile()`

Not affected:
- `fileTypeFromStream()`, which already enforced the ZIP inflate limit
for unknown-size inputs

## Impact

Applications that inspect untrusted uploads with `fileTypeFromBuffer()`,
`fileTypeFromBlob()`, or `fileTypeFromFile()` can be forced to consume
excessive memory during ZIP-based type detection. This can degrade
service or lead to process termination in memory-constrained
environments.

## Cause

The issue was introduced in 399b0f1

---

### Release Notes

<details>
<summary>sindresorhus/file-type (file-type)</summary>

###
[`v21.3.2`](https://redirect.github.com/sindresorhus/file-type/releases/tag/v21.3.2)

[Compare
Source](https://redirect.github.com/sindresorhus/file-type/compare/v21.3.1...v21.3.2)

- Fix ZIP bomb in known-size ZIP probing (GHSA-j47w-4g3g-c36v)
[`a155cd7`](https://redirect.github.com/sindresorhus/file-type/commit/a155cd7)
- Fix bound recursive BOM and ID3 detection
[`370ed91`](https://redirect.github.com/sindresorhus/file-type/commit/370ed91)

***

</details>

---

### Configuration

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

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

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

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

---

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

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My42Ni40IiwidXBkYXRlZEluVmVyIjoiNDMuNjYuNCIsInRhcmdldEJyYW5jaCI6ImNhbmFyeSIsImxhYmVscyI6WyJkZXBlbmRlbmNpZXMiXX0=-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-14 23:44:06 +08:00
renovate[bot]
9456a07889 chore: migrate Renovate config (#14656)
The Renovate config in this repository needs migrating. Typically this
is because one or more configuration options you are using have been
renamed.

You don't need to merge this PR right away, because Renovate will
continue to migrate these fields internally each time it runs. But later
some of these fields may be fully deprecated and the migrations removed.
So it's a good idea to merge this migration PR soon.





🔕 **Ignore**: Close this PR and you won't be reminded about config
migration again, but one day your current config may no longer be valid.

 Got questions? Does something look wrong to you? Please don't hesitate
to [request help
here](https://redirect.github.com/renovatebot/renovate/discussions).


---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/toeverything/AFFiNE).

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-14 23:43:39 +08:00
15 changed files with 527 additions and 229 deletions

View File

@@ -63,7 +63,7 @@
"groupName": "opentelemetry",
"matchPackageNames": [
"/^@opentelemetry/",
"/^@google-cloud\/opentelemetry-/"
"/^@google-cloud/opentelemetry-/"
]
}
],
@@ -79,7 +79,7 @@
"customManagers": [
{
"customType": "regex",
"fileMatch": ["^rust-toolchain\\.toml?$"],
"managerFilePatterns": ["/^rust-toolchain\\.toml?$/"],
"matchStrings": [
"channel\\s*=\\s*\"(?<currentValue>\\d+\\.\\d+(\\.\\d+)?)\""
],

View File

@@ -1,12 +1,35 @@
import test from 'ava';
import { z } from 'zod';
import type { DocReader } from '../../core/doc';
import type { AccessController } from '../../core/permission';
import type { Models } from '../../models';
import { NativeLlmRequest, NativeLlmStreamEvent } from '../../native';
import {
ToolCallAccumulator,
ToolCallLoop,
ToolSchemaExtractor,
} from '../../plugins/copilot/providers/loop';
import {
buildBlobContentGetter,
createBlobReadTool,
} from '../../plugins/copilot/tools/blob-read';
import {
buildDocKeywordSearchGetter,
createDocKeywordSearchTool,
} from '../../plugins/copilot/tools/doc-keyword-search';
import {
buildDocContentGetter,
createDocReadTool,
} from '../../plugins/copilot/tools/doc-read';
import {
buildDocSearchGetter,
createDocSemanticSearchTool,
} from '../../plugins/copilot/tools/doc-semantic-search';
import {
DOCUMENT_SYNC_PENDING_MESSAGE,
LOCAL_WORKSPACE_SYNC_REQUIRED_MESSAGE,
} from '../../plugins/copilot/tools/doc-sync';
test('ToolCallAccumulator should merge deltas and complete tool call', t => {
const accumulator = new ToolCallAccumulator();
@@ -286,3 +309,210 @@ test('ToolCallLoop should surface invalid JSON as tool error without executing',
is_error: true,
});
});
test('doc_read should return specific sync errors for unavailable docs', async t => {
const cases = [
{
name: 'local workspace without cloud sync',
workspace: null,
authors: null,
markdown: null,
expected: {
type: 'error',
name: 'Workspace Sync Required',
message: LOCAL_WORKSPACE_SYNC_REQUIRED_MESSAGE,
},
docReaderCalled: false,
},
{
name: 'cloud workspace document not synced to server yet',
workspace: { id: 'ws-1' },
authors: null,
markdown: null,
expected: {
type: 'error',
name: 'Document Sync Pending',
message: DOCUMENT_SYNC_PENDING_MESSAGE('doc-1'),
},
docReaderCalled: false,
},
{
name: 'cloud workspace document markdown not ready yet',
workspace: { id: 'ws-1' },
authors: {
createdAt: new Date('2026-01-01T00:00:00.000Z'),
updatedAt: new Date('2026-01-01T00:00:00.000Z'),
createdByUser: null,
updatedByUser: null,
},
markdown: null,
expected: {
type: 'error',
name: 'Document Sync Pending',
message: DOCUMENT_SYNC_PENDING_MESSAGE('doc-1'),
},
docReaderCalled: true,
},
] as const;
const ac = {
user: () => ({
workspace: () => ({ doc: () => ({ can: async () => true }) }),
}),
} as unknown as AccessController;
for (const testCase of cases) {
let docReaderCalled = false;
const docReader = {
getDocMarkdown: async () => {
docReaderCalled = true;
return testCase.markdown;
},
} as unknown as DocReader;
const models = {
workspace: {
get: async () => testCase.workspace,
},
doc: {
getAuthors: async () => testCase.authors,
},
} as unknown as Models;
const getDoc = buildDocContentGetter(ac, docReader, models);
const tool = createDocReadTool(
getDoc.bind(null, {
user: 'user-1',
workspace: 'workspace-1',
})
);
const result = await tool.execute?.({ doc_id: 'doc-1' }, {});
t.is(docReaderCalled, testCase.docReaderCalled, testCase.name);
t.deepEqual(result, testCase.expected, testCase.name);
}
});
test('document search tools should return sync error for local workspace', async t => {
const ac = {
user: () => ({
workspace: () => ({
can: async () => true,
docs: async () => [],
}),
}),
} as unknown as AccessController;
const models = {
workspace: {
get: async () => null,
},
} as unknown as Models;
let keywordSearchCalled = false;
const indexerService = {
searchDocsByKeyword: async () => {
keywordSearchCalled = true;
return [];
},
} as unknown as Parameters<typeof buildDocKeywordSearchGetter>[1];
let semanticSearchCalled = false;
const contextService = {
matchWorkspaceAll: async () => {
semanticSearchCalled = true;
return [];
},
} as unknown as Parameters<typeof buildDocSearchGetter>[1];
const keywordTool = createDocKeywordSearchTool(
buildDocKeywordSearchGetter(ac, indexerService, models).bind(null, {
user: 'user-1',
workspace: 'workspace-1',
})
);
const semanticTool = createDocSemanticSearchTool(
buildDocSearchGetter(ac, contextService, null, models).bind(null, {
user: 'user-1',
workspace: 'workspace-1',
})
);
const keywordResult = await keywordTool.execute?.({ query: 'hello' }, {});
const semanticResult = await semanticTool.execute?.({ query: 'hello' }, {});
t.false(keywordSearchCalled);
t.false(semanticSearchCalled);
t.deepEqual(keywordResult, {
type: 'error',
name: 'Workspace Sync Required',
message: LOCAL_WORKSPACE_SYNC_REQUIRED_MESSAGE,
});
t.deepEqual(semanticResult, {
type: 'error',
name: 'Workspace Sync Required',
message: LOCAL_WORKSPACE_SYNC_REQUIRED_MESSAGE,
});
});
test('doc_semantic_search should return empty array when nothing matches', async t => {
const ac = {
user: () => ({
workspace: () => ({
can: async () => true,
docs: async () => [],
}),
}),
} as unknown as AccessController;
const models = {
workspace: {
get: async () => ({ id: 'workspace-1' }),
},
} as unknown as Models;
const contextService = {
matchWorkspaceAll: async () => [],
} as unknown as Parameters<typeof buildDocSearchGetter>[1];
const semanticTool = createDocSemanticSearchTool(
buildDocSearchGetter(ac, contextService, null, models).bind(null, {
user: 'user-1',
workspace: 'workspace-1',
})
);
const result = await semanticTool.execute?.({ query: 'hello' }, {});
t.deepEqual(result, []);
});
test('blob_read should return explicit error when attachment context is missing', async t => {
const ac = {
user: () => ({
workspace: () => ({
allowLocal: () => ({
can: async () => true,
}),
}),
}),
} as unknown as AccessController;
const blobTool = createBlobReadTool(
buildBlobContentGetter(ac, null).bind(null, {
user: 'user-1',
workspace: 'workspace-1',
})
);
const result = await blobTool.execute?.({ blob_id: 'blob-1' }, {});
t.deepEqual(result, {
type: 'error',
name: 'Blob Read Failed',
message:
'Missing workspace, user, blob id, or copilot context for blob_read.',
});
});

View File

@@ -470,7 +470,8 @@ export abstract class CopilotProvider<C = any> {
});
const searchDocs = buildDocKeywordSearchGetter(
ac,
indexerService
indexerService,
models
);
tools.doc_keyword_search = createDocKeywordSearchTool(
searchDocs.bind(null, options)

View File

@@ -18,7 +18,10 @@ export const buildBlobContentGetter = (
chunk?: number
) => {
if (!options?.user || !options?.workspace || !blobId || !context) {
return;
return toolError(
'Blob Read Failed',
'Missing workspace, user, blob id, or copilot context for blob_read.'
);
}
const canAccess = await ac
.user(options.user)
@@ -29,7 +32,10 @@ export const buildBlobContentGetter = (
logger.warn(
`User ${options.user} does not have access workspace ${options.workspace}`
);
return;
return toolError(
'Blob Read Failed',
'You do not have permission to access this workspace attachment.'
);
}
const contextFile = context.files.find(
@@ -42,7 +48,12 @@ export const buildBlobContentGetter = (
context.getBlobContent(canonicalBlobId, chunk),
]);
const content = file?.trim() || blob?.trim();
if (!content) return;
if (!content) {
return toolError(
'Blob Read Failed',
`Attachment ${canonicalBlobId} is not available for reading in the current copilot context.`
);
}
const info = contextFile
? { fileName: contextFile.name, fileType: contextFile.mimeType }
: {};
@@ -53,10 +64,7 @@ export const buildBlobContentGetter = (
};
export const createBlobReadTool = (
getBlobContent: (
targetId?: string,
chunk?: number
) => Promise<object | undefined>
getBlobContent: (targetId?: string, chunk?: number) => Promise<object>
) => {
return defineTool({
description:
@@ -73,13 +81,10 @@ export const createBlobReadTool = (
execute: async ({ blob_id, chunk }) => {
try {
const blob = await getBlobContent(blob_id, chunk);
if (!blob) {
return;
}
return { ...blob };
} catch (err: any) {
logger.error(`Failed to read the blob ${blob_id} in context`, err);
return toolError('Blob Read Failed', err.message);
return toolError('Blob Read Failed', err.message ?? String(err));
}
},
});

View File

@@ -1,27 +1,43 @@
import { z } from 'zod';
import type { AccessController } from '../../../core/permission';
import type { Models } from '../../../models';
import type { IndexerService, SearchDoc } from '../../indexer';
import { workspaceSyncRequiredError } from './doc-sync';
import { toolError } from './error';
import { defineTool } from './tool';
import type { CopilotChatOptions } from './types';
export const buildDocKeywordSearchGetter = (
ac: AccessController,
indexerService: IndexerService
indexerService: IndexerService,
models: Models
) => {
const searchDocs = async (options: CopilotChatOptions, query?: string) => {
if (!options || !query?.trim() || !options.user || !options.workspace) {
return undefined;
const queryTrimmed = query?.trim();
if (!options || !queryTrimmed || !options.user || !options.workspace) {
return toolError(
'Doc Keyword Search Failed',
'Missing workspace, user, or query for doc_keyword_search.'
);
}
const workspace = await models.workspace.get(options.workspace);
if (!workspace) {
return workspaceSyncRequiredError();
}
const canAccess = await ac
.user(options.user)
.workspace(options.workspace)
.can('Workspace.Read');
if (!canAccess) return undefined;
if (!canAccess) {
return toolError(
'Doc Keyword Search Failed',
'You do not have permission to access this workspace.'
);
}
const docs = await indexerService.searchDocsByKeyword(
options.workspace,
query
queryTrimmed
);
// filter current user readable docs
@@ -29,13 +45,15 @@ export const buildDocKeywordSearchGetter = (
.user(options.user)
.workspace(options.workspace)
.docs(docs, 'Doc.Read');
return readableDocs;
return readableDocs ?? [];
};
return searchDocs;
};
export const createDocKeywordSearchTool = (
searchDocs: (query: string) => Promise<SearchDoc[] | undefined>
searchDocs: (
query: string
) => Promise<SearchDoc[] | ReturnType<typeof toolError>>
) => {
return defineTool({
description:
@@ -50,8 +68,8 @@ export const createDocKeywordSearchTool = (
execute: async ({ query }) => {
try {
const docs = await searchDocs(query);
if (!docs) {
return;
if (!Array.isArray(docs)) {
return docs;
}
return docs.map(doc => ({
docId: doc.docId,

View File

@@ -3,13 +3,20 @@ import { z } from 'zod';
import { DocReader } from '../../../core/doc';
import { AccessController } from '../../../core/permission';
import { Models, publicUserSelect } from '../../../models';
import { toolError } from './error';
import { Models } from '../../../models';
import {
documentSyncPendingError,
workspaceSyncRequiredError,
} from './doc-sync';
import { type ToolError, toolError } from './error';
import { defineTool } from './tool';
import type { CopilotChatOptions } from './types';
const logger = new Logger('DocReadTool');
const isToolError = (result: ToolError | object): result is ToolError =>
'type' in result && result.type === 'error';
export const buildDocContentGetter = (
ac: AccessController,
docReader: DocReader,
@@ -17,8 +24,17 @@ export const buildDocContentGetter = (
) => {
const getDoc = async (options: CopilotChatOptions, docId?: string) => {
if (!options?.user || !options?.workspace || !docId) {
return;
return toolError(
'Doc Read Failed',
'Missing workspace, user, or document id for doc_read.'
);
}
const workspace = await models.workspace.get(options.workspace);
if (!workspace) {
return workspaceSyncRequiredError();
}
const canAccess = await ac
.user(options.user)
.workspace(options.workspace)
@@ -28,23 +44,15 @@ export const buildDocContentGetter = (
logger.warn(
`User ${options.user} does not have access to doc ${docId} in workspace ${options.workspace}`
);
return;
return toolError(
'Doc Read Failed',
`You do not have permission to read document ${docId} in this workspace.`
);
}
const docMeta = await models.doc.getSnapshot(options.workspace, docId, {
select: {
createdAt: true,
updatedAt: true,
createdByUser: {
select: publicUserSelect,
},
updatedByUser: {
select: publicUserSelect,
},
},
});
const docMeta = await models.doc.getAuthors(options.workspace, docId);
if (!docMeta) {
return;
return documentSyncPendingError(docId);
}
const content = await docReader.getDocMarkdown(
@@ -53,7 +61,7 @@ export const buildDocContentGetter = (
true
);
if (!content) {
return;
return documentSyncPendingError(docId);
}
return {
@@ -69,8 +77,12 @@ export const buildDocContentGetter = (
return getDoc;
};
type DocReadToolResult = Awaited<
ReturnType<ReturnType<typeof buildDocContentGetter>>
>;
export const createDocReadTool = (
getDoc: (targetId?: string) => Promise<object | undefined>
getDoc: (targetId?: string) => Promise<DocReadToolResult>
) => {
return defineTool({
description:
@@ -81,13 +93,10 @@ export const createDocReadTool = (
execute: async ({ doc_id }) => {
try {
const doc = await getDoc(doc_id);
if (!doc) {
return;
}
return { ...doc };
return isToolError(doc) ? doc : { ...doc };
} catch (err: any) {
logger.error(`Failed to read the doc ${doc_id}`, err);
return toolError('Doc Read Failed', err.message);
return toolError('Doc Read Failed', err.message ?? String(err));
}
},
});

View File

@@ -7,6 +7,7 @@ import {
clearEmbeddingChunk,
type Models,
} from '../../../models';
import { workspaceSyncRequiredError } from './doc-sync';
import { toolError } from './error';
import { defineTool } from './tool';
import type {
@@ -27,14 +28,24 @@ export const buildDocSearchGetter = (
signal?: AbortSignal
) => {
if (!options || !query?.trim() || !options.user || !options.workspace) {
return `Invalid search parameters.`;
return toolError(
'Doc Semantic Search Failed',
'Missing workspace, user, or query for doc_semantic_search.'
);
}
const workspace = await models.workspace.get(options.workspace);
if (!workspace) {
return workspaceSyncRequiredError();
}
const canAccess = await ac
.user(options.user)
.workspace(options.workspace)
.can('Workspace.Read');
if (!canAccess)
return 'You do not have permission to access this workspace.';
return toolError(
'Doc Semantic Search Failed',
'You do not have permission to access this workspace.'
);
const [chunks, contextChunks] = await Promise.all([
context.matchWorkspaceAll(options.workspace, query, 10, signal),
docContext?.matchFiles(query, 10, signal) ?? [],
@@ -53,7 +64,7 @@ export const buildDocSearchGetter = (
fileChunks.push(...contextChunks);
}
if (!blobChunks.length && !docChunks.length && !fileChunks.length) {
return `No results found for "${query}".`;
return [];
}
const docIds = docChunks.map(c => ({
@@ -101,7 +112,7 @@ export const createDocSemanticSearchTool = (
searchDocs: (
query: string,
signal?: AbortSignal
) => Promise<ChunkSimilarity[] | string | undefined>
) => Promise<ChunkSimilarity[] | ReturnType<typeof toolError>>
) => {
return defineTool({
description:

View File

@@ -0,0 +1,13 @@
import { toolError } from './error';
export const LOCAL_WORKSPACE_SYNC_REQUIRED_MESSAGE =
'This workspace is local-only and does not have AFFiNE Cloud sync enabled yet. Ask the user to enable workspace sync, then try again.';
export const DOCUMENT_SYNC_PENDING_MESSAGE = (docId: string) =>
`Document ${docId} is not available on AFFiNE Cloud yet. Ask the user to wait for workspace sync to finish, then try again.`;
export const workspaceSyncRequiredError = () =>
toolError('Workspace Sync Required', LOCAL_WORKSPACE_SYNC_REQUIRED_MESSAGE);
export const documentSyncPendingError = (docId: string) =>
toolError('Document Sync Pending', DOCUMENT_SYNC_PENDING_MESSAGE(docId));

View File

@@ -32,8 +32,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/Lakr233/ListViewKit",
"state" : {
"revision" : "5dea05a52a6c2c7bb013a5925c517d6e32940605",
"version" : "1.1.8"
"revision" : "07f7adfa0629f8647991e3c148b7d3e060fe2917",
"version" : "1.2.0"
}
},
{
@@ -59,8 +59,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/Lakr233/MarkdownView",
"state" : {
"revision" : "8b8c1eecd251051c5ec2bdd5f31a2243efd9be6c",
"version" : "3.6.2"
"revision" : "37f97345a108e95f66b6671c317b43063c7f2de1",
"version" : "3.8.2"
}
},
{

View File

@@ -21,8 +21,8 @@ let package = Package(
.package(url: "https://github.com/SnapKit/SnapKit.git", from: "5.7.1"),
.package(url: "https://github.com/SwifterSwift/SwifterSwift.git", from: "6.2.0"),
.package(url: "https://github.com/Recouse/EventSource.git", from: "0.1.7"),
.package(url: "https://github.com/Lakr233/ListViewKit.git", from: "1.1.8"),
.package(url: "https://github.com/Lakr233/MarkdownView.git", from: "3.6.3"),
.package(url: "https://github.com/Lakr233/ListViewKit.git", from: "1.2.0"),
.package(url: "https://github.com/Lakr233/MarkdownView.git", from: "3.8.2"),
],
targets: [
.target(name: "Intelligents", dependencies: [

View File

@@ -7,6 +7,8 @@ import { html, nothing } from 'lit';
import { property } from 'lit/decorators.js';
import type { ToolResult } from './tool-result-card';
import { getToolErrorDisplayName, isToolError } from './tool-result-utils';
import type { ToolError } from './type';
interface DocKeywordSearchToolCall {
type: 'tool-call';
@@ -20,10 +22,7 @@ interface DocKeywordSearchToolResult {
toolCallId: string;
toolName: string;
args: { query: string };
result: Array<{
title: string;
docId: string;
}>;
result: Array<{ title: string; docId: string }> | ToolError | null;
}
export class DocKeywordSearchResult extends WithDisposable(ShadowlessElement) {
@@ -51,9 +50,23 @@ export class DocKeywordSearchResult extends WithDisposable(ShadowlessElement) {
if (this.data.type !== 'tool-result') {
return nothing;
}
const result = this.data.result;
if (!result || isToolError(result)) {
return html`<tool-call-failed
.name=${getToolErrorDisplayName(
isToolError(result) ? result : null,
'Document search failed',
{
'Workspace Sync Required':
'Enable workspace sync to search documents',
}
)}
.icon=${SearchIcon()}
></tool-call-failed>`;
}
let results: ToolResult[] = [];
try {
results = this.data.result.map(item => ({
results = result.map(item => ({
title: item.title,
icon: PageIcon(),
onClick: () => {
@@ -69,7 +82,7 @@ export class DocKeywordSearchResult extends WithDisposable(ShadowlessElement) {
console.error('Failed to parse result', err);
}
return html`<tool-result-card
.name=${`Found ${this.data.result.length} pages for "${this.data.args.query}"`}
.name=${`Found ${result.length} pages for "${this.data.args.query}"`}
.icon=${SearchIcon()}
.width=${this.width}
.results=${results}

View File

@@ -6,6 +6,9 @@ import type { Signal } from '@preact/signals-core';
import { html, nothing } from 'lit';
import { property } from 'lit/decorators.js';
import { getToolErrorDisplayName, isToolError } from './tool-result-utils';
import type { ToolError } from './type';
interface DocReadToolCall {
type: 'tool-call';
toolCallId: string;
@@ -18,14 +21,24 @@ interface DocReadToolResult {
toolCallId: string;
toolName: string;
args: { doc_id: string };
result: {
/** Old result may not have docId */
docId?: string;
title: string;
markdown: string;
};
result:
| {
/** Old result may not have docId */
docId?: string;
title: string;
markdown: string;
}
| ToolError
| null;
}
const getFailedName = (result: ToolError | null) => {
return getToolErrorDisplayName(result, 'Document read failed', {
'Workspace Sync Required': 'Enable workspace sync to read this document',
'Document Sync Pending': 'Wait for document sync to finish',
});
};
export class DocReadResult extends WithDisposable(ShadowlessElement) {
@property({ attribute: false })
accessor data!: DocReadToolCall | DocReadToolResult;
@@ -49,18 +62,25 @@ export class DocReadResult extends WithDisposable(ShadowlessElement) {
if (this.data.type !== 'tool-result') {
return nothing;
}
const result = this.data.result;
if (!result || isToolError(result)) {
return html`<tool-call-failed
.name=${getFailedName(isToolError(result) ? result : null)}
.icon=${ViewIcon()}
></tool-call-failed>`;
}
// TODO: better markdown rendering
return html`<tool-result-card
.name=${`Read "${this.data.result.title}"`}
.name=${`Read "${result.title}"`}
.icon=${ViewIcon()}
.width=${this.width}
.results=${[
{
title: this.data.result.title,
title: result.title,
icon: PageIcon(),
content: this.data.result.markdown,
content: result.markdown,
onClick: () => {
const docId = (this.data as DocReadToolResult).result.docId;
const docId = result.docId;
if (!docId) {
return;
}

View File

@@ -7,6 +7,8 @@ import { html, nothing } from 'lit';
import { property } from 'lit/decorators.js';
import type { DocDisplayConfig } from '../ai-chat-chips';
import { getToolErrorDisplayName, isToolError } from './tool-result-utils';
import type { ToolError } from './type';
interface DocSemanticSearchToolCall {
type: 'tool-call';
@@ -20,10 +22,7 @@ interface DocSemanticSearchToolResult {
toolCallId: string;
toolName: string;
args: { query: string };
result: Array<{
content: string;
docId: string;
}>;
result: Array<{ content: string; docId: string }> | ToolError | null;
}
function parseResultContent(content: string) {
@@ -82,11 +81,25 @@ export class DocSemanticSearchResult extends WithDisposable(ShadowlessElement) {
if (this.data.type !== 'tool-result') {
return nothing;
}
const result = this.data.result;
if (!result || isToolError(result)) {
return html`<tool-call-failed
.name=${getToolErrorDisplayName(
isToolError(result) ? result : null,
'Semantic search failed',
{
'Workspace Sync Required':
'Enable workspace sync to search documents',
}
)}
.icon=${AiEmbeddingIcon()}
></tool-call-failed>`;
}
return html`<tool-result-card
.name=${`Found semantically related pages for "${this.data.args.query}"`}
.icon=${AiEmbeddingIcon()}
.width=${this.width}
.results=${this.data.result
.results=${result
.map(result => ({
...parseResultContent(result.content),
title: this.docDisplayService.getTitle(result.docId),

View File

@@ -0,0 +1,16 @@
import type { ToolError } from './type';
export const isToolError = (result: unknown): result is ToolError =>
!!result &&
typeof result === 'object' &&
'type' in result &&
(result as ToolError).type === 'error';
export const getToolErrorDisplayName = (
result: ToolError | null,
fallback: string,
overrides: Record<string, string> = {}
) => {
if (!result) return fallback;
return overrides[result.name] ?? result.name;
};

251
yarn.lock
View File

@@ -6139,15 +6139,15 @@ __metadata:
languageName: node
linkType: hard
"@graphql-tools/merge@npm:9.0.24, @graphql-tools/merge@npm:^9.0.0, @graphql-tools/merge@npm:^9.0.24":
version: 9.0.24
resolution: "@graphql-tools/merge@npm:9.0.24"
"@graphql-tools/merge@npm:9.1.7, @graphql-tools/merge@npm:^9.0.0, @graphql-tools/merge@npm:^9.1.7":
version: 9.1.7
resolution: "@graphql-tools/merge@npm:9.1.7"
dependencies:
"@graphql-tools/utils": "npm:^10.8.6"
"@graphql-tools/utils": "npm:^11.0.0"
tslib: "npm:^2.4.0"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 10/95f77ff141f10d5d726cd8d1ae1ad84ed944c84346bf20461adca9b1543bb94cb524b0347885fe61d3158ccf5ffe1dddec361787ae40bfcc3449aad51528dd77
checksum: 10/e0b77dfc16e91d7c2450df0b57a85a93e11f0f67e37e396bcf04275d1db8ed1b7257c763ebe6e7f122041d81f00d6aa954fbec531fa6c0b449d195a9aff199cc
languageName: node
linkType: hard
@@ -6213,16 +6213,16 @@ __metadata:
languageName: node
linkType: hard
"@graphql-tools/schema@npm:10.0.23, @graphql-tools/schema@npm:^10.0.0, @graphql-tools/schema@npm:^10.0.11, @graphql-tools/schema@npm:^10.0.23":
version: 10.0.23
resolution: "@graphql-tools/schema@npm:10.0.23"
"@graphql-tools/schema@npm:10.0.31, @graphql-tools/schema@npm:^10.0.0, @graphql-tools/schema@npm:^10.0.11, @graphql-tools/schema@npm:^10.0.23":
version: 10.0.31
resolution: "@graphql-tools/schema@npm:10.0.31"
dependencies:
"@graphql-tools/merge": "npm:^9.0.24"
"@graphql-tools/utils": "npm:^10.8.6"
"@graphql-tools/merge": "npm:^9.1.7"
"@graphql-tools/utils": "npm:^11.0.0"
tslib: "npm:^2.4.0"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 10/f0960dae161a478941276df1802af09844825c8135e4695b36f5f7e7384f43ff8e1288a67546023fc861951d783327f239112ccf563cb4be1f22038fc78acf21
checksum: 10/5b775736f8b8454319e07cadc7d41bc5c9cc804a393490aaffd1bb7f59afddb02b498837e870bf98db4a1a989a721d5d8e2fd2b97409d078bac14503c2d4f9cb
languageName: node
linkType: hard
@@ -6262,7 +6262,21 @@ __metadata:
languageName: node
linkType: hard
"@graphql-tools/utils@npm:10.8.6, @graphql-tools/utils@npm:^10.0.0, @graphql-tools/utils@npm:^10.5.6, @graphql-tools/utils@npm:^10.8.1, @graphql-tools/utils@npm:^10.8.6":
"@graphql-tools/utils@npm:11.0.0, @graphql-tools/utils@npm:^11.0.0":
version: 11.0.0
resolution: "@graphql-tools/utils@npm:11.0.0"
dependencies:
"@graphql-typed-document-node/core": "npm:^3.1.1"
"@whatwg-node/promise-helpers": "npm:^1.0.0"
cross-inspect: "npm:1.0.1"
tslib: "npm:^2.4.0"
peerDependencies:
graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
checksum: 10/4cc7577ab85d60908a1d5d448071b318791b798f571cd4b8e4289e0e0eeae9d7183b661a1a7d5da3cedaf5f9b62b936031e3a90d2e17a1c50acbd95d9106ba3c
languageName: node
linkType: hard
"@graphql-tools/utils@npm:^10.0.0, @graphql-tools/utils@npm:^10.5.6, @graphql-tools/utils@npm:^10.8.1, @graphql-tools/utils@npm:^10.8.6":
version: 10.8.6
resolution: "@graphql-tools/utils@npm:10.8.6"
dependencies:
@@ -7540,10 +7554,10 @@ __metadata:
languageName: node
linkType: hard
"@microsoft/tsdoc@npm:0.15.1":
version: 0.15.1
resolution: "@microsoft/tsdoc@npm:0.15.1"
checksum: 10/1a92612883088fe184dba596e7ba7a0daef0e6981caeca22bad6ad551d2247294f12e368537d0d8192525cf5743f7f15fcc2ad7b3b849f26a09a15ffdd89fd0c
"@microsoft/tsdoc@npm:0.16.0":
version: 0.16.0
resolution: "@microsoft/tsdoc@npm:0.16.0"
checksum: 10/1eaad3605234dc7e44898c15d1ba3c97fb968af1117025400cba572ce268da05afc36634d1fb9e779457af3ff7f13330aee07a962510a4d9c6612c13f71ee41e
languageName: node
linkType: hard
@@ -8797,8 +8811,8 @@ __metadata:
linkType: hard
"@nestjs/apollo@npm:^13.0.4":
version: 13.1.0
resolution: "@nestjs/apollo@npm:13.1.0"
version: 13.2.4
resolution: "@nestjs/apollo@npm:13.2.4"
dependencies:
"@apollo/server-plugin-landing-page-graphql-playground": "npm:4.0.1"
iterall: "npm:1.3.0"
@@ -8806,9 +8820,9 @@ __metadata:
tslib: "npm:2.8.1"
peerDependencies:
"@apollo/gateway": ^2.0.0
"@apollo/server": ^4.11.3
"@apollo/server": ^5.0.0
"@apollo/subgraph": ^2.0.0
"@as-integrations/fastify": ^2.1.1
"@as-integrations/fastify": ^2.1.1 || ^3.0.0
"@nestjs/common": ^11.0.1
"@nestjs/core": ^11.0.1
"@nestjs/graphql": ^13.0.0
@@ -8818,9 +8832,11 @@ __metadata:
optional: true
"@apollo/subgraph":
optional: true
"@as-integrations/express5":
optional: true
"@as-integrations/fastify":
optional: true
checksum: 10/c78e409187ef905428429bea3ebb8dff450df7cc95915f0692fec0eb531e3d54b3f7ab56492fb627a9124f347d1740b4b4f35825d45cdd3e5aa5ae0692e59089
checksum: 10/f4500f1026096dc6f98cddd6f82eb2afc8ac22b6e145388477e1391c193e4bc2fd9f05a7b32b77ebfea044f7995cf2bc99d2df189be66f7d62d62bb5e3ff13d7
languageName: node
linkType: hard
@@ -8851,10 +8867,10 @@ __metadata:
linkType: hard
"@nestjs/common@npm:^11.0.21":
version: 11.1.14
resolution: "@nestjs/common@npm:11.1.14"
version: 11.1.17
resolution: "@nestjs/common@npm:11.1.17"
dependencies:
file-type: "npm:21.3.0"
file-type: "npm:21.3.2"
iterare: "npm:1.2.1"
load-esm: "npm:1.0.3"
tslib: "npm:2.8.1"
@@ -8869,13 +8885,13 @@ __metadata:
optional: true
class-validator:
optional: true
checksum: 10/420c4f66ce4d3219196fa9cc6a6c4de63aacdabe869cc95754419a0d306f498a1e52031cf2f5f6c1737616943323309fb875708f5a4f61c960eebc62d5f60232
checksum: 10/5cf09b83373ac1090a3f81ffcf34b91237f062b7e48ff37453e7c2d0cb4373e2e8dc33ed13636ff0e944a900ab19508436a76c8a5e73702d57f930ed5a0cc9c2
languageName: node
linkType: hard
"@nestjs/core@npm:^11.1.14":
version: 11.1.14
resolution: "@nestjs/core@npm:11.1.14"
version: 11.1.17
resolution: "@nestjs/core@npm:11.1.17"
dependencies:
"@nuxt/opencollective": "npm:0.4.1"
fast-safe-stringify: "npm:2.1.1"
@@ -8897,36 +8913,36 @@ __metadata:
optional: true
"@nestjs/websockets":
optional: true
checksum: 10/e346efe760079c99ab303bf05de587f172d348c194a174a4665944ab6858b36993a64b07afe62772c4e9042c685797d02579b86f12d549eb199842b436c9045e
checksum: 10/287e14b4d4a662071c9ad41d8f864010561108241da4c2b9df2f7d41962a0a9e5be6e5bcdf9b04087955ef62b7ff56f4c892da25827566dbe5bd06a921c39f8a
languageName: node
linkType: hard
"@nestjs/graphql@npm:^13.0.4":
version: 13.1.0
resolution: "@nestjs/graphql@npm:13.1.0"
version: 13.2.4
resolution: "@nestjs/graphql@npm:13.2.4"
dependencies:
"@graphql-tools/merge": "npm:9.0.24"
"@graphql-tools/schema": "npm:10.0.23"
"@graphql-tools/utils": "npm:10.8.6"
"@graphql-tools/merge": "npm:9.1.7"
"@graphql-tools/schema": "npm:10.0.31"
"@graphql-tools/utils": "npm:11.0.0"
"@nestjs/mapped-types": "npm:2.1.0"
chokidar: "npm:4.0.3"
fast-glob: "npm:3.3.3"
graphql-tag: "npm:2.12.6"
graphql-ws: "npm:6.0.4"
lodash: "npm:4.17.21"
graphql-ws: "npm:6.0.7"
lodash: "npm:4.17.23"
normalize-path: "npm:3.0.0"
subscriptions-transport-ws: "npm:0.11.0"
tslib: "npm:2.8.1"
ws: "npm:8.18.1"
ws: "npm:8.19.0"
peerDependencies:
"@apollo/subgraph": ^2.9.3
"@nestjs/common": ^11.0.1
"@nestjs/core": ^11.0.1
class-transformer: "*"
class-validator: "*"
graphql: ^16.10.0
graphql: ^16.11.0
reflect-metadata: ^0.1.13 || ^0.2.0
ts-morph: ^20.0.0 || ^21.0.0 || ^24.0.0 || ^25.0.0
ts-morph: ^20.0.0 || ^21.0.0 || ^24.0.0 || ^25.0.0 || ^26.0.0 || ^27.0.0
peerDependenciesMeta:
"@apollo/subgraph":
optional: true
@@ -8936,7 +8952,7 @@ __metadata:
optional: true
ts-morph:
optional: true
checksum: 10/07d860f665465243d6b0486fae3fed6d33a8057f6bbe982989943a288717468cba183b31cb290eecc28dd0d8c83764a0f62215e4c4fdf5967bb3c176a62d4873
checksum: 10/72b17b34b3915fddc7c0f882d09a60850b3571aabeb19c4f811f91f393e4f04bf4c5c53133cf7ed210353d03ece7d1a615e60ea19905ca137b3e317b66094704
languageName: node
linkType: hard
@@ -8958,24 +8974,24 @@ __metadata:
linkType: hard
"@nestjs/platform-express@npm:^11.1.14":
version: 11.1.14
resolution: "@nestjs/platform-express@npm:11.1.14"
version: 11.1.17
resolution: "@nestjs/platform-express@npm:11.1.17"
dependencies:
cors: "npm:2.8.6"
express: "npm:5.2.1"
multer: "npm:2.0.2"
multer: "npm:2.1.1"
path-to-regexp: "npm:8.3.0"
tslib: "npm:2.8.1"
peerDependencies:
"@nestjs/common": ^11.0.0
"@nestjs/core": ^11.0.0
checksum: 10/748259454b89a1e8d9764a8947839a67f2a608eea47ee5e978051f57626bbedfb20fe31b47f4f152e0d72316f37ecb0196e1889940ce33836db13316ca794b4f
checksum: 10/a5ed648f6797ebd153299c71b07d0ffc83f7891bc88d8a13fca5de77d772ee83ba45bfc9c7a6ba72496f3f078025b32d7f793ea7088542ef142cd42fe12cdd9c
languageName: node
linkType: hard
"@nestjs/platform-socket.io@npm:^11.1.14":
version: 11.1.14
resolution: "@nestjs/platform-socket.io@npm:11.1.14"
version: 11.1.17
resolution: "@nestjs/platform-socket.io@npm:11.1.17"
dependencies:
socket.io: "npm:4.8.3"
tslib: "npm:2.8.1"
@@ -8983,7 +8999,7 @@ __metadata:
"@nestjs/common": ^11.0.0
"@nestjs/websockets": ^11.0.0
rxjs: ^7.1.0
checksum: 10/b9e8f28c9ef906f498a412bbcf7d47ee360bcc2d6805cebc234706211e0f6b5bb411bb7bc2676a1aafa0e705777b49779703b4d74da804ba0e0b86ca6189ba74
checksum: 10/909f81d766b1187e1353951e37f61eb4f3181d9f9df65ac1316fdca853acda62650b7c5e2d8fe228fd2586ffc6a1ee4697c172e8762371aae3315ecf0093d2c3
languageName: node
linkType: hard
@@ -9000,17 +9016,17 @@ __metadata:
linkType: hard
"@nestjs/swagger@npm:^11.2.0":
version: 11.2.0
resolution: "@nestjs/swagger@npm:11.2.0"
version: 11.2.6
resolution: "@nestjs/swagger@npm:11.2.6"
dependencies:
"@microsoft/tsdoc": "npm:0.15.1"
"@microsoft/tsdoc": "npm:0.16.0"
"@nestjs/mapped-types": "npm:2.1.0"
js-yaml: "npm:4.1.0"
lodash: "npm:4.17.21"
path-to-regexp: "npm:8.2.0"
swagger-ui-dist: "npm:5.21.0"
js-yaml: "npm:4.1.1"
lodash: "npm:4.17.23"
path-to-regexp: "npm:8.3.0"
swagger-ui-dist: "npm:5.31.0"
peerDependencies:
"@fastify/static": ^8.0.0
"@fastify/static": ^8.0.0 || ^9.0.0
"@nestjs/common": ^11.0.1
"@nestjs/core": ^11.0.1
class-transformer: "*"
@@ -9023,7 +9039,7 @@ __metadata:
optional: true
class-validator:
optional: true
checksum: 10/2f8cee1f58ea5d887939d0d6f230f272a2526900057515d95253fb6d6c96dcd2d110396d70e9b74e6342cf83f4575b4dbe58ae97ba6252270b8318dcaa5fc8cd
checksum: 10/818d249c50ed6df5e465d675b93945cfb4541c1c02fc2c442689cd19273b11991a1a2fc17d2b71a58242b8bc3e332628871a121d5bd955d306d396bab547ba35
languageName: node
linkType: hard
@@ -9077,8 +9093,8 @@ __metadata:
linkType: hard
"@nestjs/websockets@npm:^11.1.14":
version: 11.1.14
resolution: "@nestjs/websockets@npm:11.1.14"
version: 11.1.17
resolution: "@nestjs/websockets@npm:11.1.17"
dependencies:
iterare: "npm:1.2.1"
object-hash: "npm:3.0.0"
@@ -9092,7 +9108,7 @@ __metadata:
peerDependenciesMeta:
"@nestjs/platform-socket.io":
optional: true
checksum: 10/06b67521692ceb7ef41ad6e1f8850469fe305a011838e0d3a466cfc9b35678e42a1783a329e0a16d1b9e45970a5afd9c5355c4924f7c2da67111e6d5b6f54c64
checksum: 10/3d4b9db8c8c1a1172d3af54ed6e947749b52207875fdf9968367a08000dfd722867efa47c07bd2af587dbf568b222ceba98f3491cdbddbdb439a205e11d46cd1
languageName: node
linkType: hard
@@ -23582,27 +23598,15 @@ __metadata:
languageName: node
linkType: hard
"file-type@npm:21.3.0":
version: 21.3.0
resolution: "file-type@npm:21.3.0"
"file-type@npm:21.3.2, file-type@npm:^21.0.0":
version: 21.3.2
resolution: "file-type@npm:21.3.2"
dependencies:
"@tokenizer/inflate": "npm:^0.4.1"
strtok3: "npm:^10.3.4"
token-types: "npm:^6.1.1"
uint8array-extras: "npm:^1.4.0"
checksum: 10/8eb8707f34d0a0fd6c2d2b223edf1ed6235cb00a44a90216741be9e812ae08dc8497dbf4e2dd63e629728509cdf361070ed32ae2280724744463ab459da81a83
languageName: node
linkType: hard
"file-type@npm:^21.0.0":
version: 21.3.1
resolution: "file-type@npm:21.3.1"
dependencies:
"@tokenizer/inflate": "npm:^0.4.1"
strtok3: "npm:^10.3.4"
token-types: "npm:^6.1.1"
uint8array-extras: "npm:^1.4.0"
checksum: 10/0f99d4fa85184ea635cdccdfa677c7838bff790cdffde7fa9ec9f52e94fa8c0e7b6c2eeeb3f6a3d6dcc0a036192c13a8ec7008bdcef374e745ae0d64a162ad33
checksum: 10/3912271811e0c745d43ff1f6c97e66d4b0d890c68d1041de4ef0c8068ede46f725ef3ed0f92c97d0cd2a261f84c3b51881d60ab797e47fa9a15e7ed227f04c85
languageName: node
linkType: hard
@@ -24689,22 +24693,22 @@ __metadata:
languageName: node
linkType: hard
"graphql-ws@npm:6.0.4, graphql-ws@npm:^6.0.3":
version: 6.0.4
resolution: "graphql-ws@npm:6.0.4"
"graphql-ws@npm:6.0.7, graphql-ws@npm:^6.0.3":
version: 6.0.7
resolution: "graphql-ws@npm:6.0.7"
peerDependencies:
"@fastify/websocket": ^10 || ^11
crossws: ~0.3
graphql: ^15.10.1 || ^16
uWebSockets.js: ^20
ws: ^8
peerDependenciesMeta:
"@fastify/websocket":
optional: true
uWebSockets.js:
crossws:
optional: true
ws:
optional: true
checksum: 10/9cfd6bdf03a65c2b390e049b0e6ce43dd3e428eb7df74afaad45bbfe1f074687a507509f5ab3f9467fea90a7fbe468558621e8c7be06729ea9dc56e116f236da
checksum: 10/633b142a7a8683f900f1f3590a30ff696076d94d17cc8c0a42d069288cd8ca77b4967e87a9f7ac884c026106be42055b9e2bda5e7de28579bd2fc8e65d0b0424
languageName: node
linkType: hard
@@ -26401,14 +26405,14 @@ __metadata:
languageName: node
linkType: hard
"js-yaml@npm:4.1.0":
version: 4.1.0
resolution: "js-yaml@npm:4.1.0"
"js-yaml@npm:4.1.1, js-yaml@npm:^4.0.0, js-yaml@npm:^4.1.0, js-yaml@npm:^4.1.1":
version: 4.1.1
resolution: "js-yaml@npm:4.1.1"
dependencies:
argparse: "npm:^2.0.1"
bin:
js-yaml: bin/js-yaml.js
checksum: 10/c138a34a3fd0d08ebaf71273ad4465569a483b8a639e0b118ff65698d257c2791d3199e3f303631f2cb98213fa7b5f5d6a4621fd0fff819421b990d30d967140
checksum: 10/a52d0519f0f4ef5b4adc1cde466cb54c50d56e2b4a983b9d5c9c0f2f99462047007a6274d7e95617a21d3c91fde3ee6115536ed70991cd645ba8521058b78f77
languageName: node
linkType: hard
@@ -26424,17 +26428,6 @@ __metadata:
languageName: node
linkType: hard
"js-yaml@npm:^4.0.0, js-yaml@npm:^4.1.0, js-yaml@npm:^4.1.1":
version: 4.1.1
resolution: "js-yaml@npm:4.1.1"
dependencies:
argparse: "npm:^2.0.1"
bin:
js-yaml: bin/js-yaml.js
checksum: 10/a52d0519f0f4ef5b4adc1cde466cb54c50d56e2b4a983b9d5c9c0f2f99462047007a6274d7e95617a21d3c91fde3ee6115536ed70991cd645ba8521058b78f77
languageName: node
linkType: hard
"jsbn@npm:1.1.0":
version: 1.1.0
resolution: "jsbn@npm:1.1.0"
@@ -27339,14 +27332,7 @@ __metadata:
languageName: node
linkType: hard
"lodash@npm:4.17.21":
version: 4.17.21
resolution: "lodash@npm:4.17.21"
checksum: 10/c08619c038846ea6ac754abd6dd29d2568aa705feb69339e836dfa8d8b09abbb2f859371e86863eda41848221f9af43714491467b5b0299122431e202bb0c532
languageName: node
linkType: hard
"lodash@npm:^4.17.11, lodash@npm:^4.17.15, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.23, lodash@npm:^4.17.4, lodash@npm:~4.17.0":
"lodash@npm:4.17.23, lodash@npm:^4.17.11, lodash@npm:^4.17.15, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.23, lodash@npm:^4.17.4, lodash@npm:~4.17.0":
version: 4.17.23
resolution: "lodash@npm:4.17.23"
checksum: 10/82504c88250f58da7a5a4289f57a4f759c44946c005dd232821c7688b5fcfbf4a6268f6a6cdde4b792c91edd2f3b5398c1d2a0998274432cff76def48735e233
@@ -28778,7 +28764,7 @@ __metadata:
languageName: node
linkType: hard
"mkdirp@npm:^0.5.1, mkdirp@npm:^0.5.6":
"mkdirp@npm:^0.5.1":
version: 0.5.6
resolution: "mkdirp@npm:0.5.6"
dependencies:
@@ -28945,22 +28931,7 @@ __metadata:
languageName: node
linkType: hard
"multer@npm:2.0.2":
version: 2.0.2
resolution: "multer@npm:2.0.2"
dependencies:
append-field: "npm:^1.0.0"
busboy: "npm:^1.6.0"
concat-stream: "npm:^2.0.0"
mkdirp: "npm:^0.5.6"
object-assign: "npm:^4.1.1"
type-is: "npm:^1.6.18"
xtend: "npm:^4.0.2"
checksum: 10/4bdcb07138cf72f93adc08a0dc27c058faab9f6721067a58f394fa546d73d11b7f100cdd66e733d649d184f9d1b402065f6888b31ec427409f056ee92c4367a6
languageName: node
linkType: hard
"multer@npm:^2.0.2":
"multer@npm:2.1.1, multer@npm:^2.0.2":
version: 2.1.1
resolution: "multer@npm:2.1.1"
dependencies:
@@ -30365,13 +30336,6 @@ __metadata:
languageName: node
linkType: hard
"path-to-regexp@npm:8.2.0":
version: 8.2.0
resolution: "path-to-regexp@npm:8.2.0"
checksum: 10/23378276a172b8ba5f5fb824475d1818ca5ccee7bbdb4674701616470f23a14e536c1db11da9c9e6d82b82c556a817bbf4eee6e41b9ed20090ef9427cbb38e13
languageName: node
linkType: hard
"path-to-regexp@npm:8.3.0, path-to-regexp@npm:^8.0.0, path-to-regexp@npm:^8.3.0":
version: 8.3.0
resolution: "path-to-regexp@npm:8.3.0"
@@ -34284,12 +34248,12 @@ __metadata:
languageName: node
linkType: hard
"swagger-ui-dist@npm:5.21.0":
version: 5.21.0
resolution: "swagger-ui-dist@npm:5.21.0"
"swagger-ui-dist@npm:5.31.0":
version: 5.31.0
resolution: "swagger-ui-dist@npm:5.31.0"
dependencies:
"@scarf/scarf": "npm:=1.4.0"
checksum: 10/5e34f4337b06d64ee6bbd64940cc63ace7c457deb98a1de2a387833c6d919543bfed1da662827191fd6c6c6762cc2f52b7ca3f316932a9bc3b12d51b718c5424
checksum: 10/79a3fc72823c9f6340184a9702f86d5f251ebff41841d734627ad1c6f4ca3553fb888f2fa33cd911506101026fad386350095ee255e6916ebeba8fd5d8114894
languageName: node
linkType: hard
@@ -36401,9 +36365,9 @@ __metadata:
languageName: node
linkType: hard
"ws@npm:8.18.1":
version: 8.18.1
resolution: "ws@npm:8.18.1"
"ws@npm:8.19.0, ws@npm:^8.17.1, ws@npm:^8.18.0, ws@npm:^8.18.3":
version: 8.19.0
resolution: "ws@npm:8.19.0"
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ">=5.0.2"
@@ -36412,7 +36376,7 @@ __metadata:
optional: true
utf-8-validate:
optional: true
checksum: 10/3f38e9594f2af5b6324138e86b74df7d77bbb8e310bf8188679dd80bac0d1f47e51536a1923ac3365f31f3d8b25ea0b03e4ade466aa8292a86cd5defca64b19b
checksum: 10/26e4901e93abaf73af9f26a93707c95b4845e91a7a347ec8c569e6e9be7f9df066f6c2b817b2d685544e208207898a750b78461e6e8d810c11a370771450c31b
languageName: node
linkType: hard
@@ -36431,21 +36395,6 @@ __metadata:
languageName: node
linkType: hard
"ws@npm:^8.17.1, ws@npm:^8.18.0, ws@npm:^8.18.3":
version: 8.19.0
resolution: "ws@npm:8.19.0"
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: ">=5.0.2"
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
checksum: 10/26e4901e93abaf73af9f26a93707c95b4845e91a7a347ec8c569e6e9be7f9df066f6c2b817b2d685544e208207898a750b78461e6e8d810c11a370771450c31b
languageName: node
linkType: hard
"ws@npm:~8.17.1":
version: 8.17.1
resolution: "ws@npm:8.17.1"
@@ -36529,7 +36478,7 @@ __metadata:
languageName: node
linkType: hard
"xtend@npm:^4.0.0, xtend@npm:^4.0.2":
"xtend@npm:^4.0.0":
version: 4.0.2
resolution: "xtend@npm:4.0.2"
checksum: 10/ac5dfa738b21f6e7f0dd6e65e1b3155036d68104e67e5d5d1bde74892e327d7e5636a076f625599dc394330a731861e87343ff184b0047fef1360a7ec0a5a36a