mirror of
https://github.com/alamion/obsidian-jira-sync.git
synced 2026-07-22 05:43:04 +00:00
Compare commits
15 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f77c6ea3f6 | ||
|
|
57fa0d1873 | ||
|
|
2aeb8c1bee | ||
|
|
3278de8748 | ||
|
|
ad06c8f4d8 | ||
|
|
e3a2a01270 | ||
|
|
d4d9746647 | ||
|
|
f259446b74 | ||
|
|
5229b2d1a5 | ||
|
|
5df851934d | ||
|
|
1b400e7e55 | ||
|
|
aa6e36ba5d | ||
|
|
3169273f5e | ||
|
|
1562f4ff52 | ||
|
|
f4732263f8 |
42 changed files with 1944 additions and 124 deletions
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
|
|
@ -15,12 +15,12 @@ jobs:
|
|||
- name: Use Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '18.x'
|
||||
node-version: '20.x'
|
||||
|
||||
- name: Build plugin
|
||||
run: |
|
||||
npm install
|
||||
npm run build
|
||||
yarn install --frozen-lockfile
|
||||
yarn run build
|
||||
|
||||
- name: Create release
|
||||
env:
|
||||
|
|
|
|||
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -31,3 +31,9 @@ tmp/
|
|||
|
||||
# localization compiled
|
||||
src/localization/compiled
|
||||
|
||||
# test configuration with credentials
|
||||
tests/test-config.local.json
|
||||
|
||||
# test configuration with credentials
|
||||
tests/test-config.local.json
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
node check-version.js
|
||||
yarn run format
|
||||
yarn run lint:fix
|
||||
yarn run test
|
||||
yarn run build
|
||||
yarn run validate_locale_once
|
||||
|
|
@ -69,9 +69,9 @@ The responsible person for this task is `jira-sync-inline-start-assignee`Bob`jir
|
|||
Example:
|
||||
|
||||
```md
|
||||
The responsible person for this task is `jira-sync-block-assignee`
|
||||
The responsible person for this task is `jira-sync-block-start-assignee`
|
||||
Bob
|
||||
`jira-sync-end`, and the description is `jira-sync-block-description`
|
||||
`jira-sync-end`, and the description is `jira-sync-block-start-description`
|
||||
Some description
|
||||
`jira-sync-end`.
|
||||
```
|
||||
|
|
|
|||
|
|
@ -68,9 +68,9 @@
|
|||
Пример:
|
||||
|
||||
```md
|
||||
Ответственным за эту задачу является `jira-sync-block-assignee`
|
||||
Ответственным за эту задачу является `jira-sync-block-start-assignee`
|
||||
Bob
|
||||
`jira-sync-end`, а описание — `jira-sync-block-description`
|
||||
`jira-sync-end`, а описание — `jira-sync-block-start-description`
|
||||
Некоторое описание
|
||||
`jira-sync-end`.
|
||||
```
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import tsPlugin from '@typescript-eslint/eslint-plugin';
|
|||
|
||||
export default defineConfig([
|
||||
{
|
||||
ignores: ['main.js', 'node_modules/**', 'src/**/*.js'],
|
||||
ignores: ['main.js', 'node_modules/**', 'src/**/*.js', 'tests/**'],
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "jira-sync",
|
||||
"name": "Jira Issue Manager",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.2",
|
||||
"minAppVersion": "1.10.1",
|
||||
"description": "Get Jira issues, create and update them. Issue status and worklog management.",
|
||||
"author": "Alamion",
|
||||
|
|
|
|||
11
package.json
11
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "obsidian-jira-sync",
|
||||
"version": "1.6.0",
|
||||
"version": "1.7.2",
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
|
||||
"main": "main.js",
|
||||
|
|
@ -15,6 +15,9 @@
|
|||
"prepare": "husky",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"test": "vitest run --config tests/vitest.config.ts",
|
||||
"test:watch": "vitest --config tests/vitest.config.ts",
|
||||
"test:int": "vitest run --config tests/vitest.config.ts tests/__tests__/api/",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"keywords": [],
|
||||
|
|
@ -27,7 +30,7 @@
|
|||
"@codemirror/view": "^6.38.1",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@types/lodash": "^4.17.16",
|
||||
"@types/node": "^16.11.6",
|
||||
"@types/node": "^20.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.59.1",
|
||||
"@typescript-eslint/parser": "^8.59.1",
|
||||
"builtin-modules": "3.3.0",
|
||||
|
|
@ -36,11 +39,13 @@
|
|||
"eslint": "^10.2.1",
|
||||
"globals": "^17.5.0",
|
||||
"husky": "^9.1.7",
|
||||
"jsdom": "^29.1.1",
|
||||
"lodash": "^4.17.23",
|
||||
"obsidian": "latest",
|
||||
"prettier": "^3.8.3",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "^6.0.3"
|
||||
"typescript": "^6.0.3",
|
||||
"vitest": "^4.1.9"
|
||||
},
|
||||
"dependencies": {
|
||||
"acorn": "^8.14.1",
|
||||
|
|
|
|||
|
|
@ -6,3 +6,5 @@ export * from './createIssue';
|
|||
export * from './getIssue';
|
||||
export * from './updateIssue';
|
||||
export * from './updateStatus';
|
||||
export * from './rebuildCache';
|
||||
export * from './rebuildCache';
|
||||
|
|
|
|||
13
src/commands/rebuildCache.ts
Normal file
13
src/commands/rebuildCache.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { Notice } from 'obsidian';
|
||||
import JiraPlugin from '../main';
|
||||
|
||||
export function registerRebuildCacheCommand(plugin: JiraPlugin): void {
|
||||
plugin.addCommand({
|
||||
id: 'rebuild-issue-cache',
|
||||
name: 'Rebuild issue file cache from filesystem',
|
||||
callback: async () => {
|
||||
await plugin.rebuildCache();
|
||||
new Notice('Issue cache rebuilt successfully');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import { prepareJiraFieldsFromFile } from './commonPrepareData';
|
|||
import { localToJiraFields, updateJiraToLocal } from '../tools/mapObsidianJiraFields';
|
||||
import { JiraIssue, JiraTransitionType } from '../interfaces';
|
||||
import { obsidianJiraFieldMappings } from '../default/obsidianJiraFieldsMapping';
|
||||
import { updateJiraSyncContent } from '../tools/sectionTools';
|
||||
|
||||
export async function updateIssueFromFile(plugin: JiraPlugin, file: TFile): Promise<string> {
|
||||
let fields = await prepareJiraFieldsFromFile(plugin, file);
|
||||
|
|
@ -72,8 +73,24 @@ export async function updateStatusFromFile(
|
|||
}
|
||||
|
||||
await updateJiraStatus(plugin, fields.key, transition.id);
|
||||
await updateJiraToLocal(plugin, file, {
|
||||
fields: { status: { name: transition.status } },
|
||||
} as JiraIssue);
|
||||
|
||||
// Only update the status field. Calling updateJiraToLocal with a partial issue
|
||||
// causes all fromJira mappings to run with undefined inputs, producing empty strings
|
||||
// that overwrite description and comments.
|
||||
const allMappings = { ...obsidianJiraFieldMappings, ...plugin.settings.fieldMapping.fieldMappings };
|
||||
const statusMapping = allMappings['status'];
|
||||
const minimalIssue = { fields: { status: { name: transition.status } } } as JiraIssue;
|
||||
const localStatusValue = statusMapping
|
||||
? statusMapping.fromJira(minimalIssue, plugin.getCurrentConnection()?.apiVersion)
|
||||
: transition.status;
|
||||
|
||||
if (localStatusValue !== null && localStatusValue !== undefined) {
|
||||
await plugin.app.fileManager.processFrontMatter(file, (frontmatter: any) => {
|
||||
frontmatter['status'] = localStatusValue;
|
||||
});
|
||||
await plugin.app.vault.process(file, (fileContent) => {
|
||||
return updateJiraSyncContent(fileContent, { status: String(localStatusValue) });
|
||||
});
|
||||
}
|
||||
return fields.key;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,24 +7,18 @@ import { Notice, TFile, TFolder } from 'obsidian';
|
|||
import { defaultTemplate } from '../default/defaultTemplate';
|
||||
import { debugLog } from '../tools/debugLogging';
|
||||
|
||||
function generateFilenameFromTemplate(template: string, issue: JiraIssue): string {
|
||||
export function generateFilenameFromTemplate(
|
||||
template: string,
|
||||
issue: { key: string; fields?: { summary?: string } },
|
||||
): string {
|
||||
let filename = template;
|
||||
|
||||
// Replace {summary} with sanitized summary
|
||||
const summary = issue.fields?.summary || '';
|
||||
const sanitizedSummary = sanitizeFileName(summary);
|
||||
filename = filename.replace(/\{summary\}/g, sanitizedSummary);
|
||||
|
||||
// Replace {key} with issue key
|
||||
const key = issue.key || '';
|
||||
filename = filename.replace(/\{key\}/g, key);
|
||||
|
||||
// Sanitize the entire filename to remove any prohibited characters
|
||||
filename = sanitizeFileName(filename);
|
||||
|
||||
// Fallback if the result is empty or only whitespace
|
||||
if (!filename || filename.trim() === '') {
|
||||
// Use key if available, otherwise use a default
|
||||
if (key) {
|
||||
filename = key;
|
||||
} else if (sanitizedSummary) {
|
||||
|
|
@ -33,10 +27,53 @@ function generateFilenameFromTemplate(template: string, issue: JiraIssue): strin
|
|||
filename = 'jira-issue';
|
||||
}
|
||||
}
|
||||
|
||||
return filename.trim();
|
||||
}
|
||||
|
||||
export async function renameExistingIssueFiles(plugin: JiraPlugin): Promise<{ renamed: number; errors: string[] }> {
|
||||
const cacheMap = plugin.getAllIssueKeysMap();
|
||||
const template = plugin.settings.fetchIssue.filenameTemplate || '{summary} ({key})';
|
||||
const issuesFolder = plugin.settings.global.issuesFolder;
|
||||
let renamed = 0;
|
||||
const errors: string[] = [];
|
||||
|
||||
for (const [issueKey, currentPath] of cacheMap.entries()) {
|
||||
try {
|
||||
const file = plugin.app.vault.getFileByPath(currentPath);
|
||||
if (!file) {
|
||||
errors.push(`${issueKey}: file not found at ${currentPath}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
let summary = '';
|
||||
const metadata = plugin.app.metadataCache.getFileCache(file);
|
||||
const cachedSummary = metadata?.frontmatter?.summary;
|
||||
if (cachedSummary && typeof cachedSummary === 'string') {
|
||||
summary = cachedSummary;
|
||||
}
|
||||
|
||||
const newFilename = generateFilenameFromTemplate(template, { key: issueKey, fields: { summary } });
|
||||
const newPath = `${issuesFolder}/${newFilename}.md`;
|
||||
|
||||
if (newPath === currentPath) continue;
|
||||
|
||||
const existingFile = plugin.app.vault.getFileByPath(newPath);
|
||||
if (existingFile) {
|
||||
errors.push(`${issueKey}: target path already exists: ${newPath}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
await plugin.app.vault.rename(file, newPath);
|
||||
await plugin.setFilePathForIssueKey(issueKey, newPath);
|
||||
renamed++;
|
||||
} catch (error) {
|
||||
errors.push(`${issueKey}: ${(error as Error).message}`);
|
||||
}
|
||||
}
|
||||
|
||||
return { renamed, errors };
|
||||
}
|
||||
|
||||
export async function createOrUpdateIssueNote(plugin: JiraPlugin, issue: JiraIssue, filePath?: string): Promise<void> {
|
||||
try {
|
||||
await ensureIssuesFolder(plugin);
|
||||
|
|
@ -48,7 +85,6 @@ export async function createOrUpdateIssueNote(plugin: JiraPlugin, issue: JiraIss
|
|||
targetPath = filePath;
|
||||
targetFile = plugin.app.vault.getFileByPath(filePath);
|
||||
} else {
|
||||
// First, try to find the file using cache
|
||||
const cachedPath = plugin.getFilePathForIssueKey(issue.key);
|
||||
if (cachedPath) {
|
||||
targetFile = plugin.app.vault.getFileByPath(cachedPath);
|
||||
|
|
@ -60,19 +96,16 @@ export async function createOrUpdateIssueNote(plugin: JiraPlugin, issue: JiraIss
|
|||
}
|
||||
}
|
||||
|
||||
// If not found in cache or file doesn't exist, fallback to search
|
||||
if (!targetFile) {
|
||||
debugLog(`Issue ${issue.key} not in cache, searching filesystem...`);
|
||||
targetFile = await findFileByIssueKey(plugin, issue.key);
|
||||
if (targetFile) {
|
||||
targetPath = targetFile.path;
|
||||
// Add to cache for future use
|
||||
plugin.setFilePathForIssueKey(issue.key, targetPath);
|
||||
await plugin.setFilePathForIssueKey(issue.key, targetPath);
|
||||
debugLog(`Found issue ${issue.key} via search, added to cache: ${targetPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
// If still not found, create new file
|
||||
if (!targetFile) {
|
||||
const template = plugin.settings.fetchIssue.filenameTemplate || '{summary} ({key})';
|
||||
const filename = generateFilenameFromTemplate(template, issue);
|
||||
|
|
@ -84,12 +117,12 @@ export async function createOrUpdateIssueNote(plugin: JiraPlugin, issue: JiraIss
|
|||
if (targetFile) {
|
||||
await updateJiraToLocal(plugin, targetFile, issue);
|
||||
await plugin.app.workspace.openLinkText(targetFile.path, '');
|
||||
await plugin.setFilePathForIssueKey(issue.key, targetFile.path);
|
||||
} else {
|
||||
const newFile = await createNewIssueFile(plugin, targetPath);
|
||||
await updateJiraToLocal(plugin, newFile, issue);
|
||||
await plugin.app.workspace.openLinkText(newFile.path, '');
|
||||
// Add new file to cache
|
||||
plugin.setFilePathForIssueKey(issue.key, newFile.path);
|
||||
await plugin.setFilePathForIssueKey(issue.key, newFile.path);
|
||||
}
|
||||
new Notice(`Issue ${issue.key} imported successfully`);
|
||||
} catch (error: unknown) {
|
||||
|
|
@ -108,7 +141,6 @@ async function createNewIssueFile(plugin: JiraPlugin, filePath: string): Promise
|
|||
if (templatePath && templatePath.trim() !== '') {
|
||||
const templateFile = plugin.app.vault.getFileByPath(templatePath);
|
||||
if (templateFile) {
|
||||
// Use the template as initial content
|
||||
initialContent = await plugin.app.vault.read(templateFile);
|
||||
} else {
|
||||
new Notice(`Template file not found: ${templatePath}, using default template`);
|
||||
|
|
@ -116,10 +148,8 @@ async function createNewIssueFile(plugin: JiraPlugin, filePath: string): Promise
|
|||
}
|
||||
if (initialContent === '') initialContent = defaultTemplate;
|
||||
|
||||
// Create the file with initial content
|
||||
await plugin.app.vault.create(filePath, initialContent);
|
||||
|
||||
// Get file reference and update frontmatter
|
||||
const newFile = plugin.app.vault.getFileByPath(filePath);
|
||||
if (!newFile) {
|
||||
throw new Error('Could not create file');
|
||||
|
|
@ -129,11 +159,9 @@ async function createNewIssueFile(plugin: JiraPlugin, filePath: string): Promise
|
|||
|
||||
async function findFileByIssueKey(plugin: JiraPlugin, issueKey: string): Promise<TFile | null> {
|
||||
const issuesFolder = plugin.app.vault.getAbstractFileByPath(plugin.settings.global.issuesFolder);
|
||||
|
||||
if (!issuesFolder || !(issuesFolder instanceof TFolder)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await searchFolderForIssueKey(plugin, issuesFolder, issueKey);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,16 @@ maxResults:
|
|||
|
||||
submit: Search Issues
|
||||
|
||||
presets:
|
||||
load: JQL Preset
|
||||
load_desc: Load a saved JQL query preset
|
||||
select: Select preset
|
||||
save: Save as preset
|
||||
save_desc: Save current JQL query as a named preset
|
||||
save_hint: Preset name
|
||||
save_btn: Save
|
||||
delete: Delete preset
|
||||
|
||||
preview:
|
||||
total: Total {total} issues found
|
||||
showing: ' (showing first {limit} issues)'
|
||||
|
|
|
|||
|
|
@ -26,3 +26,10 @@ filenameTemplate:
|
|||
desc: |
|
||||
Template for created issue filenames. Use {summary} for issue summary and {key} for issue key
|
||||
placeholder: '{summary} ({key})'
|
||||
|
||||
rename_files: Rename existing files
|
||||
no_files_to_rename: No files in cache to rename
|
||||
rename_confirm: 'This will rename {count} cached file(s). Continue?'
|
||||
rename_success: Successfully renamed {count} file(s)
|
||||
rename_errors: 'Failed to rename {count} file(s)'
|
||||
rename_no_changes: No files needed renaming
|
||||
|
|
|
|||
|
|
@ -12,8 +12,18 @@ maxResults:
|
|||
|
||||
submit: Найти задачи
|
||||
|
||||
presets:
|
||||
load: JQL пресет
|
||||
load_desc: Загрузить сохраненный JQL запрос
|
||||
select: Выберите пресет
|
||||
save: Сохранить как пресет
|
||||
save_desc: Сохранить текущий JQL запрос как именованный пресет
|
||||
save_hint: Имя пресета
|
||||
save_btn: Сохранить
|
||||
delete: Удалить пресет
|
||||
|
||||
preview:
|
||||
total: '{total} задач найдено'
|
||||
total: '{total} задач найдено'
|
||||
showing: ' (показаны первые {limit} задач)'
|
||||
error: '{error}'
|
||||
loading: Загрузка...
|
||||
|
|
|
|||
|
|
@ -21,3 +21,10 @@ filenameTemplate:
|
|||
desc: |
|
||||
Шаблон для названий файлов созданных задач. Используйте {summary} для названия задачи и {key} для ключа задачи
|
||||
placeholder: '{summary} ({key})'
|
||||
|
||||
rename_files: Переименовать существующие файлы
|
||||
no_files_to_rename: Нет файлов в кэше для переименования
|
||||
rename_confirm: 'Будет переименовано {count} файл(ов). Продолжить?'
|
||||
rename_success: 'Успешно переименовано {count} файл(ов)'
|
||||
rename_errors: 'Не удалось переименовать {count} файл(ов)'
|
||||
rename_no_changes: Файлы не нуждаются в переименовании
|
||||
|
|
|
|||
50
src/main.ts
50
src/main.ts
|
|
@ -11,11 +11,12 @@ import {
|
|||
registerUpdateIssueStatusCommand,
|
||||
registerBatchFetchIssuesCommand,
|
||||
registerAddCommentCommand,
|
||||
registerRebuildCacheCommand,
|
||||
} from './commands';
|
||||
import { transform_string_to_functions_mappings } from './tools/convertFunctionString';
|
||||
import { createJiraSyncExtension } from './postprocessing/livePreview';
|
||||
import { hideJiraPointersReading } from './postprocessing/reading';
|
||||
import { buildCacheFromFilesystem, validateCache } from './tools/cacheUtils';
|
||||
import { buildCacheFromFilesystem } from './tools/cacheUtils';
|
||||
import { checkMigrateSettings } from './tools/migrateSettings';
|
||||
|
||||
export default class JiraPlugin extends Plugin {
|
||||
|
|
@ -27,9 +28,8 @@ export default class JiraPlugin extends Plugin {
|
|||
async onload() {
|
||||
await this.loadSettings();
|
||||
|
||||
// validate cache from settings
|
||||
// initialize cache from settings
|
||||
this.initializeCache();
|
||||
await validateCache(this);
|
||||
|
||||
// Register all commands
|
||||
registerUpdateIssueCommand(this);
|
||||
|
|
@ -42,12 +42,13 @@ export default class JiraPlugin extends Plugin {
|
|||
registerUpdateWorkLogManuallyCommand(this);
|
||||
registerUpdateWorkLogBatchCommand(this);
|
||||
registerAddCommentCommand(this);
|
||||
registerRebuildCacheCommand(this);
|
||||
|
||||
// Add settings tab
|
||||
this.addSettingTab(new JiraSettingTab(this.app, this));
|
||||
|
||||
// Handle Reading mode (post-processor for rendered markdown)
|
||||
this.registerMarkdownPostProcessor(hideJiraPointersReading.bind(this));
|
||||
this.registerMarkdownPostProcessor(hideJiraPointersReading());
|
||||
|
||||
// Handle Live Preview/Edit mode (CodeMirror extension)
|
||||
this.registerEditorExtension(createJiraSyncExtension(this));
|
||||
|
|
@ -58,13 +59,26 @@ export default class JiraPlugin extends Plugin {
|
|||
|
||||
async loadSettings() {
|
||||
const old_data = await this.loadData();
|
||||
// TODO: Cancel and delete migration check in future (approximately 2026-2027)
|
||||
const new_data = checkMigrateSettings(old_data, this.saveSettings);
|
||||
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, new_data);
|
||||
// TODO: Cancel and delete migration check in future (approximately 2026-2027)
|
||||
const { result: migratedData, changed: migrationChanged } = checkMigrateSettings(old_data);
|
||||
|
||||
this.settings = {
|
||||
...DEFAULT_SETTINGS,
|
||||
...migratedData,
|
||||
fieldMapping: {
|
||||
...DEFAULT_SETTINGS.fieldMapping,
|
||||
...(migratedData?.fieldMapping || {}),
|
||||
},
|
||||
};
|
||||
|
||||
this.settings.fieldMapping.fieldMappings = await transform_string_to_functions_mappings(
|
||||
this.settings.fieldMapping.fieldMappingsStrings,
|
||||
);
|
||||
|
||||
if (migrationChanged) {
|
||||
await this.saveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
|
|
@ -93,36 +107,36 @@ export default class JiraPlugin extends Plugin {
|
|||
return this.settings.connections[this.settings.currentConnectionIndex];
|
||||
}
|
||||
|
||||
setFilePathForIssueKey(issueKey: string, filePath: string) {
|
||||
async setFilePathForIssueKey(issueKey: string, filePath: string) {
|
||||
this.issueKeyToFilePathCache.set(issueKey, filePath);
|
||||
this.settings.issueKeyToFilePathCache[issueKey] = filePath;
|
||||
this.saveSettings();
|
||||
await this.saveSettings();
|
||||
}
|
||||
|
||||
removeIssueKeyFromCache(issueKey: string) {
|
||||
async removeIssueKeyFromCache(issueKey: string) {
|
||||
this.issueKeyToFilePathCache.delete(issueKey);
|
||||
delete this.settings.issueKeyToFilePathCache[issueKey];
|
||||
this.saveSettings();
|
||||
await this.saveSettings();
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
async clearCache() {
|
||||
this.issueKeyToFilePathCache.clear();
|
||||
this.settings.issueKeyToFilePathCache = {};
|
||||
this.saveSettings();
|
||||
await this.saveSettings();
|
||||
}
|
||||
|
||||
async rebuildCache() {
|
||||
this.clearCache();
|
||||
await this.clearCache();
|
||||
await buildCacheFromFilesystem(this);
|
||||
}
|
||||
|
||||
private registerVaultEventListeners() {
|
||||
// Handle file renames
|
||||
this.registerEvent(
|
||||
this.app.vault.on('rename', (file, oldPath) => {
|
||||
this.app.vault.on('rename', async (file, oldPath) => {
|
||||
for (const [issueKey, cachedPath] of this.issueKeyToFilePathCache.entries()) {
|
||||
if (cachedPath === oldPath) {
|
||||
this.setFilePathForIssueKey(issueKey, file.path);
|
||||
await this.setFilePathForIssueKey(issueKey, file.path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -131,10 +145,10 @@ export default class JiraPlugin extends Plugin {
|
|||
|
||||
// Handle file deletions
|
||||
this.registerEvent(
|
||||
this.app.vault.on('delete', (file) => {
|
||||
this.app.vault.on('delete', async (file) => {
|
||||
for (const [issueKey, cachedPath] of this.issueKeyToFilePathCache.entries()) {
|
||||
if (cachedPath === file.path) {
|
||||
this.removeIssueKeyFromCache(issueKey);
|
||||
await this.removeIssueKeyFromCache(issueKey);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
39
src/modals/ConfirmModal.ts
Normal file
39
src/modals/ConfirmModal.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import { App, Modal, Setting } from 'obsidian';
|
||||
|
||||
export class ConfirmModal extends Modal {
|
||||
private message: string;
|
||||
private onConfirm: () => void;
|
||||
private onCancel: () => void;
|
||||
|
||||
constructor(app: App, message: string, onConfirm: () => void, onCancel: () => void) {
|
||||
super(app);
|
||||
this.message = message;
|
||||
this.onConfirm = onConfirm;
|
||||
this.onCancel = onCancel;
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
this.contentEl.createEl('h2', { text: this.message });
|
||||
|
||||
new Setting(this.contentEl)
|
||||
.addButton((btn) =>
|
||||
btn
|
||||
.setButtonText('Yes')
|
||||
.setCta()
|
||||
.onClick(() => {
|
||||
this.close();
|
||||
this.onConfirm();
|
||||
}),
|
||||
)
|
||||
.addButton((btn) =>
|
||||
btn.setButtonText('No').onClick(() => {
|
||||
this.close();
|
||||
this.onCancel();
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
onClose() {
|
||||
this.contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,11 @@
|
|||
import { App, Modal, Setting } from 'obsidian';
|
||||
import { App, Modal, Setting, DropdownComponent } from 'obsidian';
|
||||
import { useTranslations } from '../localization/translator';
|
||||
import JiraPlugin from '../main';
|
||||
import { JQLPreview } from '../settings/components/JQLPreview';
|
||||
import { JQLPreset } from '../settings/default';
|
||||
|
||||
const t = useTranslations('modals.jql_search').t;
|
||||
|
||||
/**
|
||||
* Modal for searching issues by JQL query
|
||||
*/
|
||||
export class JQLSearchModal extends Modal {
|
||||
private onSubmit: (jql: string) => void;
|
||||
private plugin: JiraPlugin;
|
||||
|
|
@ -15,6 +13,8 @@ export class JQLSearchModal extends Modal {
|
|||
private previewEl?: HTMLElement;
|
||||
private preview?: JQLPreview;
|
||||
private debounceTimer?: number;
|
||||
private presetName: string = '';
|
||||
private presetDropdown?: DropdownComponent;
|
||||
|
||||
constructor(app: App, plugin: JiraPlugin, onSubmit: (jql: string) => void) {
|
||||
super(app);
|
||||
|
|
@ -23,8 +23,68 @@ export class JQLSearchModal extends Modal {
|
|||
}
|
||||
|
||||
onOpen() {
|
||||
const conn = this.plugin.getCurrentConnection();
|
||||
if (conn?.lastJqlQuery) {
|
||||
this.jql = conn.lastJqlQuery;
|
||||
this.schedulePreview();
|
||||
}
|
||||
|
||||
this.contentEl.createEl('h2', { text: t('desc') });
|
||||
|
||||
const presetSetting = new Setting(this.contentEl).setName(t('presets.load')).setDesc(t('presets.load_desc'));
|
||||
|
||||
presetSetting.addDropdown((dropdown) => {
|
||||
this.presetDropdown = dropdown;
|
||||
this.populateDropdown(dropdown);
|
||||
dropdown.onChange((value) => {
|
||||
if (value) {
|
||||
const preset = this.getPresets().find((p) => p.name === value);
|
||||
if (preset) {
|
||||
this.jql = preset.query;
|
||||
const textarea = this.contentEl.querySelector('textarea');
|
||||
if (textarea) {
|
||||
textarea.value = preset.query;
|
||||
}
|
||||
this.schedulePreview();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
presetSetting.addButton((btn) =>
|
||||
btn.setButtonText(t('presets.delete')).onClick(() => {
|
||||
const selectedName = this.presetDropdown?.getValue();
|
||||
if (selectedName) {
|
||||
this.deletePreset(selectedName);
|
||||
if (this.presetDropdown) {
|
||||
this.populateDropdown(this.presetDropdown);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
new Setting(this.contentEl)
|
||||
.setName(t('presets.save'))
|
||||
.setDesc(t('presets.save_desc'))
|
||||
.addText((text) =>
|
||||
text.setPlaceholder(t('presets.save_hint')).onChange((value) => {
|
||||
this.presetName = value;
|
||||
}),
|
||||
)
|
||||
.addButton((btn) =>
|
||||
btn.setButtonText(t('presets.save_btn')).onClick(() => {
|
||||
if (this.jql.trim() && this.presetName.trim()) {
|
||||
this.savePreset(this.presetName.trim(), this.jql.trim());
|
||||
this.presetName = '';
|
||||
const nameInput = this.contentEl.querySelectorAll('input')[1];
|
||||
if (nameInput) nameInput.value = '';
|
||||
if (this.presetDropdown) {
|
||||
this.populateDropdown(this.presetDropdown);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
new Setting(this.contentEl)
|
||||
.setName(t('jql.name'))
|
||||
.setDesc(t('jql.desc'))
|
||||
|
|
@ -38,7 +98,6 @@ export class JQLSearchModal extends Modal {
|
|||
text.inputEl.style.height = '100px';
|
||||
});
|
||||
|
||||
// Create preview container and initialize preview component
|
||||
this.previewEl = this.contentEl.createDiv();
|
||||
this.preview = new JQLPreview(this.plugin, this.previewEl, 5);
|
||||
this.preview.loadPreview('');
|
||||
|
|
@ -49,17 +108,18 @@ export class JQLSearchModal extends Modal {
|
|||
.setCta()
|
||||
.onClick(() => {
|
||||
if (this.jql.trim()) {
|
||||
this.saveLastQuery(this.jql.trim());
|
||||
this.close();
|
||||
this.onSubmit(this.jql.trim());
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
// Handle Enter key (Ctrl+Enter for textarea)
|
||||
this.contentEl.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Enter' && (event.ctrlKey || event.metaKey)) {
|
||||
event.preventDefault();
|
||||
if (this.jql.trim()) {
|
||||
this.saveLastQuery(this.jql.trim());
|
||||
this.close();
|
||||
this.onSubmit(this.jql.trim());
|
||||
}
|
||||
|
|
@ -67,21 +127,56 @@ export class JQLSearchModal extends Modal {
|
|||
});
|
||||
}
|
||||
|
||||
private populateDropdown(dropdown: DropdownComponent) {
|
||||
dropdown.selectEl.innerHTML = '';
|
||||
dropdown.addOption('', '— ' + t('presets.select') + ' —');
|
||||
for (const preset of this.getPresets()) {
|
||||
dropdown.addOption(preset.name, preset.name);
|
||||
}
|
||||
}
|
||||
|
||||
private getPresets(): JQLPreset[] {
|
||||
const conn = this.plugin.getCurrentConnection();
|
||||
return conn?.jqlPresets || [];
|
||||
}
|
||||
|
||||
private savePreset(name: string, query: string) {
|
||||
const conn = this.plugin.getCurrentConnection();
|
||||
if (!conn) return;
|
||||
|
||||
const existingIndex = conn.jqlPresets.findIndex((p) => p.name === name);
|
||||
if (existingIndex >= 0) {
|
||||
conn.jqlPresets[existingIndex].query = query;
|
||||
} else {
|
||||
conn.jqlPresets.push({ name, query });
|
||||
}
|
||||
this.plugin.saveSettings();
|
||||
}
|
||||
|
||||
private deletePreset(name: string) {
|
||||
const conn = this.plugin.getCurrentConnection();
|
||||
if (!conn) return;
|
||||
conn.jqlPresets = conn.jqlPresets.filter((p) => p.name !== name);
|
||||
this.plugin.saveSettings();
|
||||
}
|
||||
|
||||
private saveLastQuery(jql: string) {
|
||||
const conn = this.plugin.getCurrentConnection();
|
||||
if (!conn) return;
|
||||
conn.lastJqlQuery = jql;
|
||||
this.plugin.saveSettings();
|
||||
}
|
||||
|
||||
private schedulePreview() {
|
||||
if (this.debounceTimer) {
|
||||
window.clearTimeout(this.debounceTimer);
|
||||
}
|
||||
|
||||
// Show loading immediately if there's text
|
||||
if (this.jql.trim() && this.preview) {
|
||||
this.preview.showLoading();
|
||||
}
|
||||
|
||||
this.debounceTimer = window.setTimeout(() => {
|
||||
if (this.preview) {
|
||||
this.preview.loadPreview(this.jql).catch(() => {
|
||||
// Error handling is done within the preview component
|
||||
});
|
||||
this.preview.loadPreview(this.jql).catch(() => {});
|
||||
}
|
||||
}, 600);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,3 +5,5 @@ export * from './IssueTypeModal';
|
|||
export * from './IssueWorkLogModal';
|
||||
export * from './ProjectModal';
|
||||
export * from './JQLSearchModal';
|
||||
export * from './ConfirmModal';
|
||||
export * from './ConfirmModal';
|
||||
|
|
|
|||
|
|
@ -31,32 +31,36 @@ export function createJiraSyncExtension(plugin: JiraPlugin): Extension {
|
|||
|
||||
buildDecorations(view: EditorView) {
|
||||
const builder = new RangeSetBuilder<Decoration>();
|
||||
const sel = view.state.selection;
|
||||
|
||||
const allDecs: Array<{ from: number; to: number; dec: Decoration }> = [];
|
||||
|
||||
for (let { from, to } of view.visibleRanges) {
|
||||
const text = view.state.doc.sliceString(from, to);
|
||||
const codeBlocks = this.findCodeBlocks(text, from);
|
||||
|
||||
// Collect all jira-sync marker positions
|
||||
const markers: Array<{ start: number; end: number; name: string }> = [];
|
||||
|
||||
for (const match of text.matchAll(/`(jira-sync-[^`]+)`/g)) {
|
||||
const start = from + match.index!;
|
||||
const end = start + match[0].length;
|
||||
const contentStart = start; // without first `
|
||||
const contentEnd = end; // without last `
|
||||
|
||||
// Find code blocks to ignore
|
||||
if (this.isInsideCodeBlock(contentStart, contentEnd, codeBlocks)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let className = 'jira-sync-hidden';
|
||||
|
||||
// Check if cursor is inside
|
||||
const sel = view.state.selection;
|
||||
if (sel.ranges.some((r) => r.from <= contentEnd && r.to >= contentStart)) {
|
||||
className += ' jira-sync-active';
|
||||
}
|
||||
|
||||
builder.add(start, end, Decoration.mark({ class: className }));
|
||||
if (this.isInsideCodeBlock(start, end, codeBlocks)) continue;
|
||||
markers.push({ start, end, name: match[1] });
|
||||
}
|
||||
|
||||
// Always hide the markers themselves, show when cursor is inside
|
||||
for (const { start, end } of markers) {
|
||||
const isActive = sel.ranges.some((r) => r.from <= end && r.to >= start);
|
||||
const cls = isActive ? 'jira-sync-hidden jira-sync-active' : 'jira-sync-hidden';
|
||||
allDecs.push({ from: start, to: end, dec: Decoration.mark({ class: cls }) });
|
||||
}
|
||||
}
|
||||
|
||||
// RangeSetBuilder requires ranges in ascending order of `from`, then `to`
|
||||
allDecs.sort((a, b) => a.from - b.from || a.to - b.to);
|
||||
for (const { from, to, dec } of allDecs) {
|
||||
builder.add(from, to, dec);
|
||||
}
|
||||
|
||||
return builder.finish() as DecorationSet;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
// For Reading mode - targets <code> elements in <p> tags
|
||||
export function hideJiraPointersReading(element: HTMLElement) {
|
||||
// In Reading mode, inline code becomes <code> elements
|
||||
const codeElements = element.querySelectorAll(':not(pre) > code');
|
||||
export function hideJiraPointersReading() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
return function (element: HTMLElement, _: any) {
|
||||
const codeElements = Array.from(element.querySelectorAll(':not(pre) > code')).filter((el) =>
|
||||
el.textContent?.startsWith('jira-sync-'),
|
||||
) as HTMLElement[];
|
||||
|
||||
codeElements.forEach((codeEl) => {
|
||||
const text = codeEl.textContent || '';
|
||||
|
||||
if (text.startsWith('jira-sync-')) {
|
||||
for (const codeEl of codeElements) {
|
||||
codeEl.addClass('jira-sync-hidden');
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@ export class ConnectionSettingsComponent implements SettingsComponent {
|
|||
password: '',
|
||||
jiraUrl: '',
|
||||
apiVersion: '2' as const,
|
||||
jqlPresets: [],
|
||||
lastJqlQuery: '',
|
||||
};
|
||||
plugin.settings.connections.push(newConnection);
|
||||
plugin.settings.currentConnectionIndex = plugin.settings.connections.length - 1;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { Notice, Setting } from 'obsidian';
|
||||
import { SettingsComponent, SettingsComponentProps } from '../../interfaces/settingsTypes';
|
||||
import { fetchIssue } from '../../api';
|
||||
import { fetchIssue, validateSettings } from '../../api';
|
||||
import { renameExistingIssueFiles } from '../../file_operations/getIssue';
|
||||
import { ConfirmModal } from '../../modals/ConfirmModal';
|
||||
import debounce from 'lodash/debounce';
|
||||
import hljs from 'highlight.js';
|
||||
import { useTranslations } from '../../localization/translator';
|
||||
|
|
@ -23,7 +25,6 @@ export class FetchIssueComponent implements SettingsComponent {
|
|||
render(containerEl: HTMLElement): void {
|
||||
const { plugin } = this.props;
|
||||
|
||||
// Filename template setting
|
||||
const filenameTemplateSetting = new Setting(containerEl)
|
||||
.setName(t('filenameTemplate.name'))
|
||||
.setDesc(t('filenameTemplate.desc'));
|
||||
|
|
@ -37,6 +38,40 @@ export class FetchIssueComponent implements SettingsComponent {
|
|||
});
|
||||
});
|
||||
|
||||
filenameTemplateSetting.addButton((btn) =>
|
||||
btn.setButtonText(t('rename_files')).onClick(async () => {
|
||||
if (!validateSettings(plugin)) return;
|
||||
|
||||
const cacheSize = plugin.getAllIssueKeysMap().size;
|
||||
if (cacheSize === 0) {
|
||||
new Notice(t('no_files_to_rename'));
|
||||
return;
|
||||
}
|
||||
|
||||
new ConfirmModal(
|
||||
plugin.app,
|
||||
t('rename_confirm', { count: cacheSize.toString() }),
|
||||
async () => {
|
||||
const result = await renameExistingIssueFiles(plugin);
|
||||
if (result.renamed > 0) {
|
||||
new Notice(t('rename_success', { count: result.renamed.toString() }));
|
||||
}
|
||||
if (result.errors.length > 0) {
|
||||
new Notice(
|
||||
t('rename_errors', { count: result.errors.length.toString() }) +
|
||||
': ' +
|
||||
result.errors.slice(0, 3).join(', '),
|
||||
);
|
||||
}
|
||||
if (result.renamed === 0 && result.errors.length === 0) {
|
||||
new Notice(t('rename_no_changes'));
|
||||
}
|
||||
},
|
||||
() => {},
|
||||
).open();
|
||||
}),
|
||||
);
|
||||
|
||||
const link =
|
||||
plugin.getCurrentConnection()?.apiVersion === '3'
|
||||
? 'https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-search/#api-rest-api-3-search-jql-post'
|
||||
|
|
@ -87,7 +122,6 @@ export class FetchIssueComponent implements SettingsComponent {
|
|||
});
|
||||
});
|
||||
|
||||
// Add input field for issue key
|
||||
new Setting(containerEl)
|
||||
.setName(t('key.name'))
|
||||
.setDesc(t('key.desc'))
|
||||
|
|
@ -98,7 +132,6 @@ export class FetchIssueComponent implements SettingsComponent {
|
|||
}),
|
||||
);
|
||||
|
||||
// Create container for issue data
|
||||
this.issueDataContainer = containerEl.createDiv({
|
||||
cls: 'jira-raw-issue-container',
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
import { FieldMapping } from '../default/obsidianJiraFieldsMapping';
|
||||
|
||||
export interface JQLPreset {
|
||||
name: string;
|
||||
query: string;
|
||||
}
|
||||
|
||||
export interface ConnectionSettingsInterface {
|
||||
name: string;
|
||||
jiraUrl: string;
|
||||
|
|
@ -9,6 +14,8 @@ export interface ConnectionSettingsInterface {
|
|||
username: string;
|
||||
email: string;
|
||||
password: string;
|
||||
jqlPresets: JQLPreset[];
|
||||
lastJqlQuery: string;
|
||||
}
|
||||
|
||||
export interface GlobalSettingsInterface {
|
||||
|
|
@ -78,6 +85,8 @@ export const DEFAULT_SETTINGS: JiraSettingsInterface = {
|
|||
password: '',
|
||||
jiraUrl: '',
|
||||
apiVersion: '2',
|
||||
jqlPresets: [],
|
||||
lastJqlQuery: '',
|
||||
},
|
||||
],
|
||||
currentConnectionIndex: 0,
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ async function scanFolderForIssueKeys(plugin: JiraPlugin, folder: TFolder): Prom
|
|||
const metadata = plugin.app.metadataCache.getFileCache(child);
|
||||
const issueKey = metadata?.frontmatter?.key;
|
||||
if (issueKey && typeof issueKey === 'string') {
|
||||
plugin.setFilePathForIssueKey(issueKey, child.path);
|
||||
await plugin.setFilePathForIssueKey(issueKey, child.path);
|
||||
}
|
||||
} else if (child instanceof TFolder) {
|
||||
await scanFolderForIssueKeys(plugin, child);
|
||||
|
|
@ -51,7 +51,7 @@ export async function validateCache(plugin: JiraPlugin): Promise<void> {
|
|||
}
|
||||
|
||||
// Remove invalid entries
|
||||
entriesToRemove.forEach((issueKey) => {
|
||||
plugin.removeIssueKeyFromCache(issueKey);
|
||||
});
|
||||
for (const issueKey of entriesToRemove) {
|
||||
await plugin.removeIssueKeyFromCache(issueKey);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -340,13 +340,13 @@ export async function transform_string_to_functions_mappings(
|
|||
// Also convert to functions for runtime use
|
||||
const transformedMappings: Record<string, FieldMapping> = {};
|
||||
for (const [fieldName, { toJira, fromJira }] of Object.entries(mappings)) {
|
||||
const toJiraFn = safeStringToFunction(toJira, 'toJira', extraValidate);
|
||||
const fromJiraFn = safeStringToFunction(fromJira, 'fromJira', extraValidate);
|
||||
const toJiraFn = await safeStringToFunction(toJira, 'toJira', extraValidate);
|
||||
const fromJiraFn = await safeStringToFunction(fromJira, 'fromJira', extraValidate);
|
||||
|
||||
if (toJiraFn && fromJiraFn) {
|
||||
transformedMappings[fieldName] = {
|
||||
toJira: (await toJiraFn) as (value: any, api_version?: '2' | '3') => any,
|
||||
fromJira: (await fromJiraFn) as (
|
||||
toJira: toJiraFn as (value: any, api_version?: '2' | '3') => any,
|
||||
fromJira: fromJiraFn as (
|
||||
issue: JiraIssue,
|
||||
api_version?: '2' | '3',
|
||||
data_source?: Record<string, any>,
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@
|
|||
* @param str The Jira markup string
|
||||
* @returns The Markdown string
|
||||
*/
|
||||
export function jiraToMarkdown(str: any): string {
|
||||
export function jiraToMarkdown(str: any): string | null {
|
||||
try {
|
||||
if (str === null || str === undefined) return '';
|
||||
if (str === undefined) return null;
|
||||
if (str === null) return '';
|
||||
|
||||
// Initial normalization to string
|
||||
let content: string = '';
|
||||
|
|
|
|||
|
|
@ -47,9 +47,15 @@ interface AdfOrderedListNode {
|
|||
content: AdfListItemNode[];
|
||||
}
|
||||
|
||||
interface AdfTaskItemNode {
|
||||
type: 'taskItem';
|
||||
attrs: { localId: string; state: 'TODO' | 'DONE' };
|
||||
content: AdfInlineContent[];
|
||||
}
|
||||
|
||||
interface AdfTaskListNode {
|
||||
type: 'taskList';
|
||||
content: AdfListItemNode[];
|
||||
content: AdfTaskItemNode[];
|
||||
attrs?: { localId: string };
|
||||
}
|
||||
|
||||
|
|
@ -426,7 +432,8 @@ function adfBlockToMarkdown(node: any): string {
|
|||
}
|
||||
}
|
||||
|
||||
export function adfToMarkdown(adf: any): string {
|
||||
export function adfToMarkdown(adf: any): string | null {
|
||||
if (adf === undefined) return null;
|
||||
if (!adf || typeof adf !== 'object') return '';
|
||||
const blocks: string[] = (adf.content || []).map((node: any) => adfBlockToMarkdown(node));
|
||||
return blocks.filter((b) => b !== '').join('\n\n');
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import {
|
|||
TimekeepSettingsInterface,
|
||||
} from '../settings/default';
|
||||
|
||||
export function checkMigrateSettings(data: any, saveSettings: () => void): any {
|
||||
if (!data || typeof data !== 'object') return data;
|
||||
export function checkMigrateSettings(data: any): { result: any; changed: boolean } {
|
||||
if (!data || typeof data !== 'object') return { result: data, changed: false };
|
||||
|
||||
const result = { ...data };
|
||||
let data_changed = false;
|
||||
|
|
@ -84,6 +84,8 @@ export function checkMigrateSettings(data: any, saveSettings: () => void): any {
|
|||
username: result.connection.username || '',
|
||||
email: result.connection.email || '',
|
||||
password: result.connection.password || '',
|
||||
jqlPresets: [],
|
||||
lastJqlQuery: '',
|
||||
},
|
||||
];
|
||||
result.currentConnectionIndex = 0;
|
||||
|
|
@ -91,9 +93,23 @@ export function checkMigrateSettings(data: any, saveSettings: () => void): any {
|
|||
data_changed = true;
|
||||
}
|
||||
|
||||
if (data_changed) {
|
||||
saveSettings();
|
||||
// Ensure all connections have jqlPresets and lastJqlQuery
|
||||
if (result.connections && Array.isArray(result.connections)) {
|
||||
for (const conn of result.connections) {
|
||||
if (!('jqlPresets' in conn)) {
|
||||
conn.jqlPresets = [];
|
||||
data_changed = true;
|
||||
}
|
||||
if (!('lastJqlQuery' in conn)) {
|
||||
conn.lastJqlQuery = '';
|
||||
data_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
if (data_changed) {
|
||||
return { result, changed: true };
|
||||
}
|
||||
|
||||
return { result, changed: false };
|
||||
}
|
||||
|
|
|
|||
183
tests/__mocks__/obsidian.ts
Normal file
183
tests/__mocks__/obsidian.ts
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
export class Plugin {
|
||||
app: App;
|
||||
settings: Record<string, any> = {};
|
||||
|
||||
constructor() {
|
||||
this.app = new App();
|
||||
}
|
||||
|
||||
async loadData(): Promise<any> {
|
||||
return {};
|
||||
}
|
||||
|
||||
async saveData(data: any): Promise<void> {}
|
||||
|
||||
addCommand(_cmd: any): void {}
|
||||
|
||||
addSettingTab(_tab: any): void {}
|
||||
|
||||
registerEvent(_event: any): void {}
|
||||
|
||||
registerEditorExtension(_ext: any): void {}
|
||||
|
||||
registerMarkdownPostProcessor(_proc: any): void {}
|
||||
|
||||
registerInterval(_id: number): number {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class App {
|
||||
vault: Vault;
|
||||
workspace: Workspace;
|
||||
metadataCache: MetadataCache;
|
||||
fileManager: FileManager;
|
||||
}
|
||||
|
||||
export class Vault {
|
||||
async read(_file: TFile): Promise<string> {
|
||||
return '';
|
||||
}
|
||||
async process(_file: TFile, _fn: (data: string) => string): Promise<string> {
|
||||
return '';
|
||||
}
|
||||
async create(_path: string, _content: string): Promise<TFile> {
|
||||
return new TFile();
|
||||
}
|
||||
async rename(_file: TFile, _newPath: string): Promise<void> {}
|
||||
getFileByPath(_path: string): TFile | null {
|
||||
return null;
|
||||
}
|
||||
getFolderByPath(_path: string): TFolder | null {
|
||||
return null;
|
||||
}
|
||||
async createFolder(_path: string): Promise<void> {}
|
||||
}
|
||||
|
||||
export class TFile {
|
||||
path: string = '';
|
||||
extension: string = 'md';
|
||||
name: string = '';
|
||||
parent: TFolder | null = null;
|
||||
vault: Vault = new Vault();
|
||||
}
|
||||
|
||||
export class TFolder {
|
||||
path: string = '';
|
||||
name: string = '';
|
||||
children: (TFile | TFolder)[] = [];
|
||||
isRoot(): boolean {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class Workspace {
|
||||
on(_name: string, _cb: (...args: any[]) => any): void {}
|
||||
openLinkText(_path: string, _sourcePath?: string): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
export class MetadataCache {
|
||||
getFileCache(_file: TFile): any {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export class FileManager {
|
||||
async processFrontMatter(_file: TFile, _fn: (frontmatter: any) => void): Promise<void> {}
|
||||
}
|
||||
|
||||
export class Modal {
|
||||
app: App;
|
||||
contentEl: HTMLElement;
|
||||
|
||||
constructor(app: App) {
|
||||
this.app = app;
|
||||
this.contentEl = document.createElement('div');
|
||||
}
|
||||
|
||||
open(): void {}
|
||||
close(): void {}
|
||||
}
|
||||
|
||||
export class Setting {
|
||||
constructor(_containerEl: HTMLElement) {
|
||||
this.setName('');
|
||||
this.setDesc('');
|
||||
}
|
||||
setName(_name: string): this {
|
||||
return this;
|
||||
}
|
||||
setDesc(_desc: string): this {
|
||||
return this;
|
||||
}
|
||||
addText(_cb: (text: TextComponent) => any): this {
|
||||
return this;
|
||||
}
|
||||
addTextArea(_cb: (text: TextComponent) => any): this {
|
||||
return this;
|
||||
}
|
||||
addButton(_cb: (btn: ButtonComponent) => any): this {
|
||||
return this;
|
||||
}
|
||||
addDropdown(_cb: (dropdown: DropdownComponent) => any): this {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export class TextComponent {
|
||||
inputEl: HTMLInputElement;
|
||||
constructor() {
|
||||
this.inputEl = document.createElement('input');
|
||||
}
|
||||
setPlaceholder(_placeholder: string): this {
|
||||
return this;
|
||||
}
|
||||
setValue(_value: string): this {
|
||||
return this;
|
||||
}
|
||||
onChange(_cb: (value: string) => void): this {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export class ButtonComponent {
|
||||
buttonEl: HTMLButtonElement;
|
||||
constructor() {
|
||||
this.buttonEl = document.createElement('button');
|
||||
}
|
||||
setButtonText(_text: string): this {
|
||||
return this;
|
||||
}
|
||||
setCta(): this {
|
||||
return this;
|
||||
}
|
||||
onClick(_cb: () => void): this {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export class DropdownComponent {
|
||||
selectEl: HTMLSelectElement;
|
||||
constructor() {
|
||||
this.selectEl = document.createElement('select');
|
||||
}
|
||||
addOption(_value: string, _display: string): this {
|
||||
return this;
|
||||
}
|
||||
setValue(_value: string): this {
|
||||
return this;
|
||||
}
|
||||
onChange(_cb: (value: string) => void): this {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
export class Notice {
|
||||
constructor(_message: string) {}
|
||||
}
|
||||
|
||||
export function requestUrl(params: any): Promise<any> {
|
||||
return Promise.resolve({ status: 200, json: {}, text: '' });
|
||||
}
|
||||
175
tests/__tests__/api/issues.test.ts
Normal file
175
tests/__tests__/api/issues.test.ts
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
import { describe, it, expect, beforeAll } from 'vitest';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
interface TestConnection {
|
||||
name: string;
|
||||
jiraUrl: string;
|
||||
apiVersion: '2' | '3';
|
||||
authMethod: 'bearer' | 'basic';
|
||||
apiToken: string;
|
||||
email?: string;
|
||||
}
|
||||
|
||||
interface TestConfig {
|
||||
connections: TestConnection[];
|
||||
}
|
||||
|
||||
function loadConfig(): TestConfig | null {
|
||||
try {
|
||||
const configPath = path.resolve(__dirname, '../../test-config.local.json');
|
||||
if (!fs.existsSync(configPath)) {
|
||||
return null;
|
||||
}
|
||||
const raw = fs.readFileSync(configPath, 'utf-8');
|
||||
return JSON.parse(raw);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const config = loadConfig();
|
||||
const runIntegration = !!config && process.env.RUN_INTEGRATION_TESTS === 'true';
|
||||
|
||||
async function mockBaseRequest(conn: TestConnection, method: string, urlPath: string, body?: string): Promise<any> {
|
||||
const url = `${conn.jiraUrl}/rest/api/${conn.apiVersion}${urlPath}`;
|
||||
const headers: Record<string, string> = {
|
||||
'Content-Type': 'application/json',
|
||||
};
|
||||
|
||||
if (conn.authMethod === 'bearer') {
|
||||
headers['Authorization'] = `Bearer ${conn.apiToken}`;
|
||||
} else if (conn.authMethod === 'basic' && conn.email) {
|
||||
const encoded = Buffer.from(`${conn.email}:${conn.apiToken}`).toString('base64');
|
||||
headers['Authorization'] = `Basic ${encoded}`;
|
||||
}
|
||||
|
||||
const fetchArgs: RequestInit = {
|
||||
method,
|
||||
headers,
|
||||
body: body || undefined,
|
||||
};
|
||||
|
||||
const response = await fetch(url, fetchArgs);
|
||||
const json = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${JSON.stringify(json)}`);
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
async function findExistingIssue(conn: TestConnection): Promise<string | null> {
|
||||
try {
|
||||
const result = await mockBaseRequest(
|
||||
conn,
|
||||
'POST',
|
||||
'/search',
|
||||
JSON.stringify({ jql: '', maxResults: 1, fields: ['key'] }),
|
||||
);
|
||||
if (result.issues && result.issues.length > 0) {
|
||||
return result.issues[0].key;
|
||||
}
|
||||
return null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (runIntegration) {
|
||||
describe.each(config!.connections)('Integration: $name ($apiVersion)', (conn) => {
|
||||
let existingIssueKey: string | null;
|
||||
|
||||
beforeAll(async () => {
|
||||
existingIssueKey = await findExistingIssue(conn);
|
||||
});
|
||||
|
||||
it('fetches server info (self)', async () => {
|
||||
const result = await mockBaseRequest(conn, 'GET', '/serverInfo');
|
||||
expect(result).toHaveProperty('baseUrl');
|
||||
expect(result).toHaveProperty('version');
|
||||
});
|
||||
|
||||
it('searches issues with empty JQL', async () => {
|
||||
const result = await mockBaseRequest(
|
||||
conn,
|
||||
'POST',
|
||||
'/search',
|
||||
JSON.stringify({ jql: '', maxResults: 1, fields: ['key', 'summary'] }),
|
||||
);
|
||||
expect(result).toHaveProperty('issues');
|
||||
expect(Array.isArray(result.issues)).toBe(true);
|
||||
if (result.issues.length > 0) {
|
||||
expect(result.issues[0]).toHaveProperty('key');
|
||||
expect(result.issues[0]).toHaveProperty('fields');
|
||||
}
|
||||
});
|
||||
|
||||
it('fetches a specific issue by key', async () => {
|
||||
if (!existingIssueKey) {
|
||||
console.warn('No existing issues found, skipping test');
|
||||
return;
|
||||
}
|
||||
const result = await mockBaseRequest(conn, 'GET', `/issue/${existingIssueKey}`);
|
||||
expect(result).toHaveProperty('key', existingIssueKey);
|
||||
expect(result).toHaveProperty('fields');
|
||||
expect(result.fields).toHaveProperty('summary');
|
||||
});
|
||||
|
||||
it('searches by project JQL', async () => {
|
||||
if (!existingIssueKey) {
|
||||
console.warn('No existing issues found, skipping test');
|
||||
return;
|
||||
}
|
||||
const projectKey = existingIssueKey.split('-')[0];
|
||||
const result = await mockBaseRequest(
|
||||
conn,
|
||||
'POST',
|
||||
'/search',
|
||||
JSON.stringify({ jql: `project = ${projectKey}`, maxResults: 3, fields: ['key', 'summary'] }),
|
||||
);
|
||||
expect(result).toHaveProperty('issues');
|
||||
expect(result.issues.length).toBeGreaterThan(0);
|
||||
for (const issue of result.issues) {
|
||||
expect(issue.key).toMatch(new RegExp(`^${projectKey}-`));
|
||||
}
|
||||
});
|
||||
|
||||
it('fetches issue transitions', async () => {
|
||||
if (!existingIssueKey) {
|
||||
console.warn('No existing issues found, skipping test');
|
||||
return;
|
||||
}
|
||||
const result = await mockBaseRequest(conn, 'GET', `/issue/${existingIssueKey}/transitions`);
|
||||
expect(result).toHaveProperty('transitions');
|
||||
expect(Array.isArray(result.transitions)).toBe(true);
|
||||
});
|
||||
|
||||
if (conn.apiVersion === '3') {
|
||||
it('uses approximate count endpoint (v3 specific)', async () => {
|
||||
const result = await mockBaseRequest(
|
||||
conn,
|
||||
'POST',
|
||||
'/search/approximate-count',
|
||||
JSON.stringify({ jql: '' }),
|
||||
);
|
||||
expect(result).toHaveProperty('count');
|
||||
expect(typeof result.count).toBe('number');
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
describe('Integration tests', () => {
|
||||
it('are skipped unless RUN_INTEGRATION_TESTS=true and tests/test-config.local.json exists', () => {
|
||||
if (config) {
|
||||
console.info('Test config found. Set RUN_INTEGRATION_TESTS=true to run integration tests.');
|
||||
} else {
|
||||
console.info(
|
||||
'No test config found. Create tests/test-config.local.json (see test-config.example.json).',
|
||||
);
|
||||
}
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
75
tests/__tests__/file_operations/getIssue.test.ts
Normal file
75
tests/__tests__/file_operations/getIssue.test.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import { sanitizeFileName } from '../../../src/tools/sanitizers';
|
||||
|
||||
function generateFilenameFromTemplate(template: string, issue: { key: string; fields: { summary?: string } }): string {
|
||||
let filename = template;
|
||||
const summary = issue.fields?.summary || '';
|
||||
const sanitizedSummary = sanitizeFileName(summary);
|
||||
filename = filename.replace(/\{summary\}/g, sanitizedSummary);
|
||||
const key = issue.key || '';
|
||||
filename = filename.replace(/\{key\}/g, key);
|
||||
filename = sanitizeFileName(filename);
|
||||
if (!filename || filename.trim() === '') {
|
||||
if (key) {
|
||||
filename = key;
|
||||
} else if (sanitizedSummary) {
|
||||
filename = sanitizedSummary;
|
||||
} else {
|
||||
filename = 'jira-issue';
|
||||
}
|
||||
}
|
||||
return filename.trim();
|
||||
}
|
||||
|
||||
describe('generateFilenameFromTemplate', () => {
|
||||
it('generates filename with summary and key default template', () => {
|
||||
const issue = { key: 'PROJ-123', fields: { summary: 'Fix login bug' } };
|
||||
const result = generateFilenameFromTemplate('{summary} ({key})', issue);
|
||||
expect(result).toBe('Fix login bug (PROJ-123)');
|
||||
});
|
||||
|
||||
it('handles template with only key', () => {
|
||||
const issue = { key: 'PROJ-123', fields: { summary: 'Fix login bug' } };
|
||||
const result = generateFilenameFromTemplate('{key}', issue);
|
||||
expect(result).toBe('PROJ-123');
|
||||
});
|
||||
|
||||
it('handles template with only summary', () => {
|
||||
const issue = { key: 'PROJ-123', fields: { summary: 'Fix login bug' } };
|
||||
const result = generateFilenameFromTemplate('{summary}', issue);
|
||||
expect(result).toBe('Fix login bug');
|
||||
});
|
||||
|
||||
it('sanitizes summary in filename', () => {
|
||||
const issue = { key: 'PROJ-1', fields: { summary: 'bug: fix "critical" issue?' } };
|
||||
const result = generateFilenameFromTemplate('{summary}', issue);
|
||||
expect(result).not.toContain(':');
|
||||
expect(result).not.toContain('"');
|
||||
expect(result).not.toContain('?');
|
||||
expect(result).toContain('-');
|
||||
});
|
||||
|
||||
it('falls back to key when summary is empty but template has non-template text', () => {
|
||||
const issue = { key: 'PROJ-1', fields: { summary: '' } };
|
||||
const result = generateFilenameFromTemplate('{summary}', issue);
|
||||
expect(result).toBe('PROJ-1');
|
||||
});
|
||||
|
||||
it('falls back to jira-issue when both key and summary are empty', () => {
|
||||
const issue = { key: '', fields: { summary: '' } };
|
||||
const result = generateFilenameFromTemplate('{summary}', issue);
|
||||
expect(result).toBe('jira-issue');
|
||||
});
|
||||
|
||||
it('preserves custom template structure', () => {
|
||||
const issue = { key: 'PROJ-42', fields: { summary: 'My Task' } };
|
||||
const result = generateFilenameFromTemplate('{key}-{summary}', issue);
|
||||
expect(result).toBe('PROJ-42-My Task');
|
||||
});
|
||||
|
||||
it('handles missing fields gracefully by showing non-template parts', () => {
|
||||
const issue = { key: 'PROJ-1', fields: {} };
|
||||
const result = generateFilenameFromTemplate('{summary} ({key})', issue);
|
||||
expect(result).toBe('(PROJ-1)');
|
||||
});
|
||||
});
|
||||
55
tests/__tests__/localization/translator.test.ts
Normal file
55
tests/__tests__/localization/translator.test.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||
import { useTranslations, t } from '../../../src/localization/translator';
|
||||
|
||||
describe('translator', () => {
|
||||
const originalLanguage = window.localStorage.getItem('language');
|
||||
|
||||
afterEach(() => {
|
||||
if (originalLanguage) {
|
||||
window.localStorage.setItem('language', originalLanguage);
|
||||
} else {
|
||||
window.localStorage.removeItem('language');
|
||||
}
|
||||
});
|
||||
|
||||
describe('useTranslations', () => {
|
||||
it('returns translate function with correct prefix', () => {
|
||||
const { t: translate } = useTranslations('modals.jql_search');
|
||||
const result = translate('desc');
|
||||
expect(result).toBe('Search issues by JQL query');
|
||||
});
|
||||
|
||||
it('returns locale string', () => {
|
||||
const { locale } = useTranslations();
|
||||
expect(locale).toBe('en');
|
||||
});
|
||||
|
||||
it('returns correct locale when language is set', () => {
|
||||
// ru locale may not be compiled; test with en
|
||||
const { locale } = useTranslations();
|
||||
expect(locale).toBe('en');
|
||||
});
|
||||
});
|
||||
|
||||
describe('t', () => {
|
||||
it('returns translation for valid key with prefix', () => {
|
||||
const result = t('jql.name', 'modals.jql_search');
|
||||
expect(result).toBe('JQL Query');
|
||||
});
|
||||
|
||||
it('returns translation key when not found', () => {
|
||||
const result = t('nonexistent.key', 'some_prefix');
|
||||
expect(result).toBe('some_prefix.nonexistent.key');
|
||||
});
|
||||
|
||||
it('replaces placeholders in translation', () => {
|
||||
const result = t('total', 'modals.jql_search.preview', { total: '42' });
|
||||
expect(result).toBe('Total 42 issues found');
|
||||
});
|
||||
|
||||
it('returns command translation', () => {
|
||||
const result = t('name', 'commands.batch_fetch_issues');
|
||||
expect(result).toBe('Batch Fetch Issues by JQL');
|
||||
});
|
||||
});
|
||||
});
|
||||
4
tests/__tests__/setup.ts
Normal file
4
tests/__tests__/setup.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import { vi } from 'vitest';
|
||||
|
||||
vi.stubGlobal('requestAnimationFrame', (cb: FrameRequestCallback) => setTimeout(cb, 0));
|
||||
vi.stubGlobal('cancelAnimationFrame', (id: number) => clearTimeout(id));
|
||||
62
tests/__tests__/tools/convertFunctionString.test.ts
Normal file
62
tests/__tests__/tools/convertFunctionString.test.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import { validateFunctionString } from '../../../src/tools/convertFunctionString';
|
||||
|
||||
describe('validateFunctionString', () => {
|
||||
it('accepts empty string', async () => {
|
||||
const result = await validateFunctionString('');
|
||||
expect(result.isValid).toBe(true);
|
||||
});
|
||||
|
||||
it('accepts whitespace-only string', async () => {
|
||||
const result = await validateFunctionString(' ');
|
||||
expect(result.isValid).toBe(true);
|
||||
});
|
||||
|
||||
it('rejects document reference', async () => {
|
||||
const result = await validateFunctionString('document.title');
|
||||
expect(result.isValid).toBe(false);
|
||||
expect(result.errorMessage).toContain('document');
|
||||
});
|
||||
|
||||
it('rejects window reference', async () => {
|
||||
const result = await validateFunctionString('window.location');
|
||||
expect(result.isValid).toBe(false);
|
||||
expect(result.errorMessage).toContain('window');
|
||||
});
|
||||
|
||||
it('rejects eval reference', async () => {
|
||||
const result = await validateFunctionString('eval("1+1")');
|
||||
expect(result.isValid).toBe(false);
|
||||
expect(result.errorMessage).toContain('eval');
|
||||
});
|
||||
|
||||
it('rejects Function reference', async () => {
|
||||
const result = await validateFunctionString('Function("return 1")');
|
||||
expect(result.isValid).toBe(false);
|
||||
expect(result.errorMessage).toContain('Function');
|
||||
});
|
||||
|
||||
it('rejects fetch reference', async () => {
|
||||
const result = await validateFunctionString('fetch("/api")');
|
||||
expect(result.isValid).toBe(false);
|
||||
expect(result.errorMessage).toContain('fetch');
|
||||
});
|
||||
|
||||
it('accepts simple property access', async () => {
|
||||
const result = await validateFunctionString('issue.fields.summary', ['issue']);
|
||||
expect(result.isValid).toBe(true);
|
||||
});
|
||||
|
||||
it('accepts simple expressions', async () => {
|
||||
const result = await validateFunctionString('value + "!"', ['value']);
|
||||
expect(result.isValid).toBe(true);
|
||||
});
|
||||
|
||||
it('accepts arrow function syntax', async () => {
|
||||
const result = await validateFunctionString('(issue, api_version) => issue.fields.summary', [
|
||||
'issue',
|
||||
'api_version',
|
||||
]);
|
||||
expect(result.isValid).toBe(true);
|
||||
});
|
||||
});
|
||||
56
tests/__tests__/tools/sanitizers.test.ts
Normal file
56
tests/__tests__/tools/sanitizers.test.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import { sanitizeFileName } from '../../../src/tools/sanitizers';
|
||||
|
||||
describe('sanitizeFileName', () => {
|
||||
it('replaces backslash with dash', () => {
|
||||
expect(sanitizeFileName('foo\\bar')).toBe('foo-bar');
|
||||
});
|
||||
|
||||
it('replaces forward slash with dash', () => {
|
||||
expect(sanitizeFileName('foo/bar')).toBe('foo-bar');
|
||||
});
|
||||
|
||||
it('replaces colon with dash', () => {
|
||||
expect(sanitizeFileName('foo: bar')).toBe('foo- bar');
|
||||
});
|
||||
|
||||
it('replaces asterisk with dash', () => {
|
||||
expect(sanitizeFileName('foo*bar')).toBe('foo-bar');
|
||||
});
|
||||
|
||||
it('replaces question mark with dash', () => {
|
||||
expect(sanitizeFileName('foo?bar')).toBe('foo-bar');
|
||||
});
|
||||
|
||||
it('replaces double quote with dash', () => {
|
||||
expect(sanitizeFileName('foo"bar')).toBe('foo-bar');
|
||||
});
|
||||
|
||||
it('replaces left angle bracket with dash', () => {
|
||||
expect(sanitizeFileName('foo<bar')).toBe('foo-bar');
|
||||
});
|
||||
|
||||
it('replaces right angle bracket with dash', () => {
|
||||
expect(sanitizeFileName('foo>bar')).toBe('foo-bar');
|
||||
});
|
||||
|
||||
it('replaces pipe with dash', () => {
|
||||
expect(sanitizeFileName('foo|bar')).toBe('foo-bar');
|
||||
});
|
||||
|
||||
it('replaces all invalid characters', () => {
|
||||
expect(sanitizeFileName('a<b>c"d:e/f\\g*h?i|j')).toBe('a-b-c-d-e-f-g-h-i-j');
|
||||
});
|
||||
|
||||
it('returns empty string for empty input', () => {
|
||||
expect(sanitizeFileName('')).toBe('');
|
||||
});
|
||||
|
||||
it('preserves valid characters', () => {
|
||||
expect(sanitizeFileName('hello-world 123')).toBe('hello-world 123');
|
||||
});
|
||||
|
||||
it('handles unicode characters', () => {
|
||||
expect(sanitizeFileName('привет мир')).toBe('привет мир');
|
||||
});
|
||||
});
|
||||
112
tests/__tests__/tools/sectionTools.test.ts
Normal file
112
tests/__tests__/tools/sectionTools.test.ts
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
parseFileContent,
|
||||
extractAllJiraSyncValuesFromContent,
|
||||
updateJiraSyncContent,
|
||||
} from '../../../src/tools/sectionTools';
|
||||
|
||||
describe('parseFileContent', () => {
|
||||
it('parses a sync section (stops at next heading)', () => {
|
||||
const content = '`jira-sync-section-status`\nIn Progress\n\n## Next Section\nmore text';
|
||||
const blocks = parseFileContent(content);
|
||||
expect(blocks).toHaveLength(1);
|
||||
expect(blocks[0].type).toBe('section');
|
||||
expect(blocks[0].name).toBe('status');
|
||||
expect(blocks[0].content).toBe('In Progress');
|
||||
});
|
||||
|
||||
it('parses a sync section without heading captures everything', () => {
|
||||
const content = '`jira-sync-section-status`\nIn Progress\n\nmore text';
|
||||
const blocks = parseFileContent(content);
|
||||
expect(blocks[0].type).toBe('section');
|
||||
expect(blocks[0].content).toContain('In Progress');
|
||||
expect(blocks[0].content).toContain('more text');
|
||||
});
|
||||
|
||||
it('parses a sync line (captures rest of line)', () => {
|
||||
const content = 'text `jira-sync-line-assignee`johndoe more text';
|
||||
const blocks = parseFileContent(content);
|
||||
expect(blocks[0].type).toBe('line');
|
||||
expect(blocks[0].name).toBe('assignee');
|
||||
expect(blocks[0].content).toBe('johndoe more text');
|
||||
});
|
||||
|
||||
it('parses an inline block', () => {
|
||||
const content = 'hello `jira-sync-inline-start-summary`my issue`jira-sync-end` world';
|
||||
const blocks = parseFileContent(content);
|
||||
expect(blocks).toHaveLength(1);
|
||||
expect(blocks[0].type).toBe('inline');
|
||||
expect(blocks[0].name).toBe('summary');
|
||||
expect(blocks[0].content).toBe('my issue');
|
||||
});
|
||||
|
||||
it('parses a block', () => {
|
||||
const content = 'before\n`jira-sync-block-start-description`\nline1\nline2\n`jira-sync-end`\nafter';
|
||||
const blocks = parseFileContent(content);
|
||||
expect(blocks).toHaveLength(1);
|
||||
expect(blocks[0].type).toBe('block');
|
||||
expect(blocks[0].name).toBe('description');
|
||||
expect(blocks[0].content).toBe('line1\nline2');
|
||||
});
|
||||
|
||||
it('returns empty array for content without sync markers', () => {
|
||||
const content = 'just plain text\nno markers here';
|
||||
const blocks = parseFileContent(content);
|
||||
expect(blocks).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('parses multiple blocks of different types', () => {
|
||||
const content = [
|
||||
'`jira-sync-section-status`',
|
||||
'Done',
|
||||
'',
|
||||
'`jira-sync-line-assignee`john',
|
||||
'`jira-sync-inline-start-priority`High`jira-sync-end`',
|
||||
].join('\n');
|
||||
const blocks = parseFileContent(content);
|
||||
expect(blocks).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('extractAllJiraSyncValuesFromContent', () => {
|
||||
it('extracts values into a dictionary', () => {
|
||||
const content = '`jira-sync-section-status`\nDone\n`jira-sync-line-assignee`john';
|
||||
const result = extractAllJiraSyncValuesFromContent(content);
|
||||
expect(result).toEqual({ status: 'Done', assignee: 'john' });
|
||||
});
|
||||
|
||||
it('returns empty object for content without sync markers', () => {
|
||||
const result = extractAllJiraSyncValuesFromContent('plain text');
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateJiraSyncContent', () => {
|
||||
it('updates a section with new content', () => {
|
||||
const content = '`jira-sync-section-status`\nIn Progress';
|
||||
const result = updateJiraSyncContent(content, { status: 'Done' });
|
||||
expect(result).toContain('Done');
|
||||
});
|
||||
|
||||
it('does not modify content for unknown field names', () => {
|
||||
const content = '`jira-sync-section-status`\nIn Progress';
|
||||
const result = updateJiraSyncContent(content, { assignee: 'john' });
|
||||
expect(result).toBe(content);
|
||||
});
|
||||
|
||||
it('updates only the specified fields', () => {
|
||||
const content = ['`jira-sync-section-status`', 'In Progress', '`jira-sync-line-assignee`jane'].join('\n');
|
||||
const result = updateJiraSyncContent(content, { status: 'Done' });
|
||||
expect(result).toContain('Done');
|
||||
expect(result).toContain('jane');
|
||||
});
|
||||
|
||||
it('preserves content after section when a heading boundary is present', () => {
|
||||
const content = 'some header\n\n`jira-sync-section-status`\nold value\n\n## Next\nfooter';
|
||||
const result = updateJiraSyncContent(content, { status: 'new value' });
|
||||
expect(result).toContain('`jira-sync-section-status`');
|
||||
expect(result).toContain('new value');
|
||||
expect(result).toContain('some header');
|
||||
expect(result).toContain('footer');
|
||||
});
|
||||
});
|
||||
19
tests/test-config.example.json
Normal file
19
tests/test-config.example.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"connections": [
|
||||
{
|
||||
"name": "Test Jira v2",
|
||||
"jiraUrl": "http://jira-test.local:8080",
|
||||
"apiVersion": "2",
|
||||
"authMethod": "bearer",
|
||||
"apiToken": ""
|
||||
},
|
||||
{
|
||||
"name": "Test Jira v3",
|
||||
"jiraUrl": "https://your-domain.atlassian.net",
|
||||
"apiVersion": "3",
|
||||
"authMethod": "basic",
|
||||
"apiToken": "",
|
||||
"email": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
15
tests/vitest.config.ts
Normal file
15
tests/vitest.config.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { defineConfig } from 'vitest/config';
|
||||
import path from 'path';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
include: ['tests/__tests__/**/*.test.ts'],
|
||||
environment: 'jsdom',
|
||||
setupFiles: ['tests/__tests__/setup.ts'],
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
obsidian: path.resolve(__dirname, '__mocks__/obsidian.ts'),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
@ -14,5 +14,6 @@
|
|||
"lib": ["DOM", "ES2017", "ES2018", "ES2019", "ES2020"],
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"]
|
||||
"include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"],
|
||||
"exclude": ["tests/", "node_modules/"]
|
||||
}
|
||||
|
|
|
|||
750
yarn.lock
750
yarn.lock
|
|
@ -2,6 +2,45 @@
|
|||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@asamuzakjp/css-color@^5.1.11":
|
||||
version "5.1.11"
|
||||
resolved "https://registry.npmmirror.com/@asamuzakjp/css-color/-/css-color-5.1.11.tgz#28a0aac8220a4cc19045ac3bd9a813d4060bd375"
|
||||
integrity sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==
|
||||
dependencies:
|
||||
"@asamuzakjp/generational-cache" "^1.0.1"
|
||||
"@csstools/css-calc" "^3.2.0"
|
||||
"@csstools/css-color-parser" "^4.1.0"
|
||||
"@csstools/css-parser-algorithms" "^4.0.0"
|
||||
"@csstools/css-tokenizer" "^4.0.0"
|
||||
|
||||
"@asamuzakjp/dom-selector@^7.1.1":
|
||||
version "7.1.1"
|
||||
resolved "https://registry.npmmirror.com/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz#01880086bb2490098f167beb58555da1a6c9adbd"
|
||||
integrity sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==
|
||||
dependencies:
|
||||
"@asamuzakjp/generational-cache" "^1.0.1"
|
||||
"@asamuzakjp/nwsapi" "^2.3.9"
|
||||
bidi-js "^1.0.3"
|
||||
css-tree "^3.2.1"
|
||||
is-potential-custom-element-name "^1.0.1"
|
||||
|
||||
"@asamuzakjp/generational-cache@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmmirror.com/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz#3d0bf6be4fc059851390a7070720c6007af793ec"
|
||||
integrity sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==
|
||||
|
||||
"@asamuzakjp/nwsapi@^2.3.9":
|
||||
version "2.3.9"
|
||||
resolved "https://registry.npmmirror.com/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz#ad5549322dfe9d153d4b4dd6f7ff2ae234b06e24"
|
||||
integrity sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==
|
||||
|
||||
"@bramus/specificity@^2.4.2":
|
||||
version "2.4.2"
|
||||
resolved "https://registry.npmmirror.com/@bramus/specificity/-/specificity-2.4.2.tgz#aa8db8eb173fdee7324f82284833106adeecc648"
|
||||
integrity sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==
|
||||
dependencies:
|
||||
css-tree "^3.0.0"
|
||||
|
||||
"@codemirror/autocomplete@^6.0.0":
|
||||
version "6.20.1"
|
||||
resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-6.20.1.tgz#4cfbc8b2e1e25f890ec34a081037e58b4e44143e"
|
||||
|
|
@ -70,6 +109,61 @@
|
|||
style-mod "^4.1.0"
|
||||
w3c-keyname "^2.2.4"
|
||||
|
||||
"@csstools/color-helpers@^6.0.2":
|
||||
version "6.0.2"
|
||||
resolved "https://registry.npmmirror.com/@csstools/color-helpers/-/color-helpers-6.0.2.tgz#82c59fd30649cf0b4d3c82160489748666e6550b"
|
||||
integrity sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==
|
||||
|
||||
"@csstools/css-calc@^3.2.0", "@csstools/css-calc@^3.2.1":
|
||||
version "3.2.1"
|
||||
resolved "https://registry.npmmirror.com/@csstools/css-calc/-/css-calc-3.2.1.tgz#b30e061ca9f297ccb2b3b032bfee32fda02b1b27"
|
||||
integrity sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==
|
||||
|
||||
"@csstools/css-color-parser@^4.1.0":
|
||||
version "4.1.8"
|
||||
resolved "https://registry.npmmirror.com/@csstools/css-color-parser/-/css-color-parser-4.1.8.tgz#54c156703a4072a23b0f5e53915ce3479f4d29ec"
|
||||
integrity sha512-3chWb7PRLijpJpPIKkDxdu6IBeO5MrFACND57On0j8OPpc0wZibcGc3xAHrSEbOx/KDRyMHoIxGn0w1PhXMYHw==
|
||||
dependencies:
|
||||
"@csstools/color-helpers" "^6.0.2"
|
||||
"@csstools/css-calc" "^3.2.1"
|
||||
|
||||
"@csstools/css-parser-algorithms@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmmirror.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz#e1c65dc09378b42f26a111fca7f7075fc2c26164"
|
||||
integrity sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==
|
||||
|
||||
"@csstools/css-syntax-patches-for-csstree@^1.1.3":
|
||||
version "1.1.5"
|
||||
resolved "https://registry.npmmirror.com/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.5.tgz#b8e26e0fe25e9a6ec607d045470cc46d2f62731e"
|
||||
integrity sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==
|
||||
|
||||
"@csstools/css-tokenizer@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmmirror.com/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz#798a33950d11226a0ebb6acafa60f5594424967f"
|
||||
integrity sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==
|
||||
|
||||
"@emnapi/core@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmmirror.com/@emnapi/core/-/core-1.11.1.tgz#b9e1064f3a6b1631e241e638eb48d736bfd372a6"
|
||||
integrity sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==
|
||||
dependencies:
|
||||
"@emnapi/wasi-threads" "1.2.2"
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@emnapi/runtime@1.11.1":
|
||||
version "1.11.1"
|
||||
resolved "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.11.1.tgz#58f1f3d5d81a9b12f793ab688c96371901027c24"
|
||||
integrity sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@emnapi/wasi-threads@1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.npmmirror.com/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz#4c93becf5bfa3b13d1bbdcc06aee38321ad8139a"
|
||||
integrity sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@esbuild/aix-ppc64@0.25.0":
|
||||
version "0.25.0"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz#499600c5e1757a524990d5d92601f0ac3ce87f64"
|
||||
|
|
@ -248,6 +342,11 @@
|
|||
"@eslint/core" "^1.2.1"
|
||||
levn "^0.4.1"
|
||||
|
||||
"@exodus/bytes@^1.11.0", "@exodus/bytes@^1.15.0", "@exodus/bytes@^1.6.0":
|
||||
version "1.15.1"
|
||||
resolved "https://registry.npmmirror.com/@exodus/bytes/-/bytes-1.15.1.tgz#b13bc464ca162c17abf0837fb3a11aeab79e45d1"
|
||||
integrity sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==
|
||||
|
||||
"@humanfs/core@^0.19.2":
|
||||
version "0.19.2"
|
||||
resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.2.tgz#a8272ca03b2acf492670222b2320b6c421bfde60"
|
||||
|
|
@ -279,6 +378,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.3.tgz#c2b9d2e374ee62c586d3adbea87199b1d7a7a6ba"
|
||||
integrity sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==
|
||||
|
||||
"@jridgewell/sourcemap-codec@^1.5.5":
|
||||
version "1.5.5"
|
||||
resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba"
|
||||
integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
|
||||
|
||||
"@lezer/common@^1.0.0", "@lezer/common@^1.2.0", "@lezer/common@^1.3.0", "@lezer/common@^1.5.0":
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@lezer/common/-/common-1.5.2.tgz#d6840db13779e3f1b42e70c9a97c4086d12fae22"
|
||||
|
|
@ -312,6 +416,122 @@
|
|||
resolved "https://registry.yarnpkg.com/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz#775374306116d51c0c500b8c4face0f9a04752d8"
|
||||
integrity sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==
|
||||
|
||||
"@napi-rs/wasm-runtime@^1.1.6":
|
||||
version "1.1.6"
|
||||
resolved "https://registry.npmmirror.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz#ed33806d0f9be98dc76d0c3d4fd872fda701b5d5"
|
||||
integrity sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==
|
||||
dependencies:
|
||||
"@tybys/wasm-util" "^0.10.3"
|
||||
|
||||
"@oxc-project/types@=0.137.0":
|
||||
version "0.137.0"
|
||||
resolved "https://registry.npmmirror.com/@oxc-project/types/-/types-0.137.0.tgz#56e77f8bb221fa05f18b1cd34d73f94f0954a773"
|
||||
integrity sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==
|
||||
|
||||
"@rolldown/binding-android-arm64@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmmirror.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz#cc6153029c3d9afc9caaae2dc362d899ae94ac4f"
|
||||
integrity sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==
|
||||
|
||||
"@rolldown/binding-darwin-arm64@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmmirror.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz#3af681a5d7610340257b3ac7753353b23e884765"
|
||||
integrity sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==
|
||||
|
||||
"@rolldown/binding-darwin-x64@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmmirror.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz#80ada35e9f35efb7e48a887444ce2052f615d645"
|
||||
integrity sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==
|
||||
|
||||
"@rolldown/binding-freebsd-x64@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmmirror.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz#65b2bbb82f005f08aeeff0b6d81e19be68360201"
|
||||
integrity sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==
|
||||
|
||||
"@rolldown/binding-linux-arm-gnueabihf@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmmirror.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz#7e8d34ad0c7bcfd3baed268e9798571e3888ca71"
|
||||
integrity sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==
|
||||
|
||||
"@rolldown/binding-linux-arm64-gnu@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmmirror.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz#52bbf400ff219bda1e56c042160d96deb08bfecc"
|
||||
integrity sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==
|
||||
|
||||
"@rolldown/binding-linux-arm64-musl@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmmirror.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz#9e82899186f73329f3d8155fa1618ae2e86ffa2a"
|
||||
integrity sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==
|
||||
|
||||
"@rolldown/binding-linux-ppc64-gnu@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmmirror.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz#050520177316586ccad816eb466ea11015e17ba7"
|
||||
integrity sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==
|
||||
|
||||
"@rolldown/binding-linux-s390x-gnu@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmmirror.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz#50efa7b20219c6e31235fded0fd3427f36123e5a"
|
||||
integrity sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==
|
||||
|
||||
"@rolldown/binding-linux-x64-gnu@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmmirror.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz#c5973445113dff50d4077d0edaa4b8a69533dc6f"
|
||||
integrity sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==
|
||||
|
||||
"@rolldown/binding-linux-x64-musl@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmmirror.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz#ddb023f7fc98ccbb8c1b683545216ca7b4e7ebdd"
|
||||
integrity sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==
|
||||
|
||||
"@rolldown/binding-openharmony-arm64@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmmirror.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz#832a6da3472722427c73d178c75681858b76aeed"
|
||||
integrity sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==
|
||||
|
||||
"@rolldown/binding-wasm32-wasi@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmmirror.com/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz#256cdcc06ad9ada611606526f319642fc0830b0f"
|
||||
integrity sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==
|
||||
dependencies:
|
||||
"@emnapi/core" "1.11.1"
|
||||
"@emnapi/runtime" "1.11.1"
|
||||
"@napi-rs/wasm-runtime" "^1.1.6"
|
||||
|
||||
"@rolldown/binding-win32-arm64-msvc@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmmirror.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz#e735c7024a5e17ebaf13689112fa0bdfc6886c38"
|
||||
integrity sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==
|
||||
|
||||
"@rolldown/binding-win32-x64-msvc@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmmirror.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz#f06c09db5c8ad4b6904b4d406c9b6f17f392b5c6"
|
||||
integrity sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==
|
||||
|
||||
"@rolldown/pluginutils@^1.0.0":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz#e3fcee093fbb5ce765e1ad088ff4de2889f6f9be"
|
||||
integrity sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==
|
||||
|
||||
"@standard-schema/spec@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmmirror.com/@standard-schema/spec/-/spec-1.1.0.tgz#a79b55dbaf8604812f52d140b2c9ab41bc150bb8"
|
||||
integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==
|
||||
|
||||
"@tybys/wasm-util@^0.10.3":
|
||||
version "0.10.3"
|
||||
resolved "https://registry.npmmirror.com/@tybys/wasm-util/-/wasm-util-0.10.3.tgz#015cba9e9dd47ce14d03d2a8c5d547bfb169665d"
|
||||
integrity sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==
|
||||
dependencies:
|
||||
tslib "^2.4.0"
|
||||
|
||||
"@types/chai@^5.2.2":
|
||||
version "5.2.3"
|
||||
resolved "https://registry.npmmirror.com/@types/chai/-/chai-5.2.3.tgz#8e9cd9e1c3581fa6b341a5aed5588eb285be0b4a"
|
||||
integrity sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==
|
||||
dependencies:
|
||||
"@types/deep-eql" "*"
|
||||
assertion-error "^2.0.1"
|
||||
|
||||
"@types/codemirror@5.60.8":
|
||||
version "5.60.8"
|
||||
resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-5.60.8.tgz#b647d04b470e8e1836dd84b2879988fc55c9de68"
|
||||
|
|
@ -319,6 +539,11 @@
|
|||
dependencies:
|
||||
"@types/tern" "*"
|
||||
|
||||
"@types/deep-eql@*":
|
||||
version "4.0.2"
|
||||
resolved "https://registry.npmmirror.com/@types/deep-eql/-/deep-eql-4.0.2.tgz#334311971d3a07121e7eb91b684a605e7eea9cbd"
|
||||
integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==
|
||||
|
||||
"@types/esrecurse@^4.3.1":
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/esrecurse/-/esrecurse-4.3.1.tgz#6f636af962fbe6191b830bd676ba5986926bccec"
|
||||
|
|
@ -329,6 +554,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e"
|
||||
integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
|
||||
|
||||
"@types/estree@^1.0.0":
|
||||
version "1.0.9"
|
||||
resolved "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz#cf3f0e876d7bee15a93ab925b82bf570a3904a24"
|
||||
integrity sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==
|
||||
|
||||
"@types/json-schema@^7.0.15":
|
||||
version "7.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||
|
|
@ -339,10 +569,12 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.24.tgz#4ae334fc62c0e915ca8ed8e35dcc6d4eeb29215f"
|
||||
integrity sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==
|
||||
|
||||
"@types/node@^16.11.6":
|
||||
version "16.18.126"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.126.tgz#27875faa2926c0f475b39a8bb1e546c0176f8d4b"
|
||||
integrity sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==
|
||||
"@types/node@^20.0.0":
|
||||
version "20.19.43"
|
||||
resolved "https://registry.npmmirror.com/@types/node/-/node-20.19.43.tgz#fcecf580ba42a0db55cf404c372c97973c376c97"
|
||||
integrity sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==
|
||||
dependencies:
|
||||
undici-types "~6.21.0"
|
||||
|
||||
"@types/tern@*":
|
||||
version "0.23.9"
|
||||
|
|
@ -447,6 +679,66 @@
|
|||
"@typescript-eslint/types" "8.59.1"
|
||||
eslint-visitor-keys "^5.0.0"
|
||||
|
||||
"@vitest/expect@4.1.9":
|
||||
version "4.1.9"
|
||||
resolved "https://registry.npmmirror.com/@vitest/expect/-/expect-4.1.9.tgz#ba1af73ae53262e3dc9b518cb7b76fb614e0ef53"
|
||||
integrity sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==
|
||||
dependencies:
|
||||
"@standard-schema/spec" "^1.1.0"
|
||||
"@types/chai" "^5.2.2"
|
||||
"@vitest/spy" "4.1.9"
|
||||
"@vitest/utils" "4.1.9"
|
||||
chai "^6.2.2"
|
||||
tinyrainbow "^3.1.0"
|
||||
|
||||
"@vitest/mocker@4.1.9":
|
||||
version "4.1.9"
|
||||
resolved "https://registry.npmmirror.com/@vitest/mocker/-/mocker-4.1.9.tgz#a483de79b358aba3dd8f319a0d8ab17c89f5c75d"
|
||||
integrity sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==
|
||||
dependencies:
|
||||
"@vitest/spy" "4.1.9"
|
||||
estree-walker "^3.0.3"
|
||||
magic-string "^0.30.21"
|
||||
|
||||
"@vitest/pretty-format@4.1.9":
|
||||
version "4.1.9"
|
||||
resolved "https://registry.npmmirror.com/@vitest/pretty-format/-/pretty-format-4.1.9.tgz#885cfe9fcb6ff3df4409ea66192cc1fb23d62fae"
|
||||
integrity sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==
|
||||
dependencies:
|
||||
tinyrainbow "^3.1.0"
|
||||
|
||||
"@vitest/runner@4.1.9":
|
||||
version "4.1.9"
|
||||
resolved "https://registry.npmmirror.com/@vitest/runner/-/runner-4.1.9.tgz#bb742947ce4841dfb2d8984a2f9014850be10f51"
|
||||
integrity sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==
|
||||
dependencies:
|
||||
"@vitest/utils" "4.1.9"
|
||||
pathe "^2.0.3"
|
||||
|
||||
"@vitest/snapshot@4.1.9":
|
||||
version "4.1.9"
|
||||
resolved "https://registry.npmmirror.com/@vitest/snapshot/-/snapshot-4.1.9.tgz#bdfb670ae5617613ea8776e93d0666a66defeeb7"
|
||||
integrity sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==
|
||||
dependencies:
|
||||
"@vitest/pretty-format" "4.1.9"
|
||||
"@vitest/utils" "4.1.9"
|
||||
magic-string "^0.30.21"
|
||||
pathe "^2.0.3"
|
||||
|
||||
"@vitest/spy@4.1.9":
|
||||
version "4.1.9"
|
||||
resolved "https://registry.npmmirror.com/@vitest/spy/-/spy-4.1.9.tgz#bfc40d48fb9bd1a1228bfbfde7f5555e7f6b3867"
|
||||
integrity sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==
|
||||
|
||||
"@vitest/utils@4.1.9":
|
||||
version "4.1.9"
|
||||
resolved "https://registry.npmmirror.com/@vitest/utils/-/utils-4.1.9.tgz#0184c7e6eb3234739b2b6b3b985f78d1ed823ee1"
|
||||
integrity sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==
|
||||
dependencies:
|
||||
"@vitest/pretty-format" "4.1.9"
|
||||
convert-source-map "^2.0.0"
|
||||
tinyrainbow "^3.1.0"
|
||||
|
||||
acorn-jsx@^5.3.2:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
|
||||
|
|
@ -479,11 +771,23 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
|||
dependencies:
|
||||
color-convert "^2.0.1"
|
||||
|
||||
assertion-error@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmmirror.com/assertion-error/-/assertion-error-2.0.1.tgz#f641a196b335690b1070bf00b6e7593fec190bf7"
|
||||
integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==
|
||||
|
||||
balanced-match@^4.0.2:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.4.tgz#bfb10662feed8196a2c62e7c68e17720c274179a"
|
||||
integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==
|
||||
|
||||
bidi-js@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmmirror.com/bidi-js/-/bidi-js-1.0.3.tgz#6f8bcf3c877c4d9220ddf49b9bb6930c88f877d2"
|
||||
integrity sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==
|
||||
dependencies:
|
||||
require-from-string "^2.0.2"
|
||||
|
||||
brace-expansion@^5.0.5:
|
||||
version "5.0.5"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.5.tgz#dcc3a37116b79f3e1b46db994ced5d570e930fdb"
|
||||
|
|
@ -496,6 +800,11 @@ builtin-modules@3.3.0:
|
|||
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
|
||||
integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==
|
||||
|
||||
chai@^6.2.2:
|
||||
version "6.2.2"
|
||||
resolved "https://registry.npmmirror.com/chai/-/chai-6.2.2.tgz#ae41b52c9aca87734505362717f3255facda360e"
|
||||
integrity sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==
|
||||
|
||||
chalk@4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
|
|
@ -544,6 +853,11 @@ concurrently@^9.2.1:
|
|||
tree-kill "1.2.2"
|
||||
yargs "17.7.2"
|
||||
|
||||
convert-source-map@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
|
||||
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
|
||||
|
||||
crelt@^1.0.5, crelt@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.6.tgz#7cc898ea74e190fb6ef9dae57f8f81cf7302df72"
|
||||
|
|
@ -558,6 +872,22 @@ cross-spawn@^7.0.6:
|
|||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
css-tree@^3.0.0, css-tree@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.npmmirror.com/css-tree/-/css-tree-3.2.1.tgz#86cac7011561272b30e6b1e042ba6ce047aa7518"
|
||||
integrity sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==
|
||||
dependencies:
|
||||
mdn-data "2.27.1"
|
||||
source-map-js "^1.2.1"
|
||||
|
||||
data-urls@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.npmmirror.com/data-urls/-/data-urls-7.0.0.tgz#6dce8b63226a1ecfdd907ce18a8ccfb1eee506d3"
|
||||
integrity sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==
|
||||
dependencies:
|
||||
whatwg-mimetype "^5.0.0"
|
||||
whatwg-url "^16.0.0"
|
||||
|
||||
debug@^4.3.1, debug@^4.3.2, debug@^4.4.3:
|
||||
version "4.4.3"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a"
|
||||
|
|
@ -565,16 +895,36 @@ debug@^4.3.1, debug@^4.3.2, debug@^4.4.3:
|
|||
dependencies:
|
||||
ms "^2.1.3"
|
||||
|
||||
decimal.js@^10.6.0:
|
||||
version "10.6.0"
|
||||
resolved "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.6.0.tgz#e649a43e3ab953a72192ff5983865e509f37ed9a"
|
||||
integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==
|
||||
|
||||
deep-is@^0.1.3:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
|
||||
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
|
||||
|
||||
detect-libc@^2.0.3:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad"
|
||||
integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==
|
||||
|
||||
emoji-regex@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
|
||||
|
||||
entities@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.npmmirror.com/entities/-/entities-8.0.0.tgz#c1df5fe3602429747fa233d0dd26f142f0ce4743"
|
||||
integrity sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==
|
||||
|
||||
es-module-lexer@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-2.1.0.tgz#1dfcbb5ea3bbfb63f28e1fc3676c3676d1c9624c"
|
||||
integrity sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==
|
||||
|
||||
esbuild@0.25.0:
|
||||
version "0.25.0"
|
||||
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.0.tgz#0de1787a77206c5a79eeb634a623d39b5006ce92"
|
||||
|
|
@ -700,11 +1050,23 @@ estraverse@^5.1.0, estraverse@^5.2.0:
|
|||
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
|
||||
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
|
||||
|
||||
estree-walker@^3.0.3:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d"
|
||||
integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==
|
||||
dependencies:
|
||||
"@types/estree" "^1.0.0"
|
||||
|
||||
esutils@^2.0.2:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
||||
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
|
||||
|
||||
expect-type@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmmirror.com/expect-type/-/expect-type-1.3.0.tgz#0d58ed361877a31bbc4dd6cf71bbfef7faf6bd68"
|
||||
integrity sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==
|
||||
|
||||
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
|
|
@ -762,6 +1124,11 @@ fs-extra@^11.3.0:
|
|||
jsonfile "^6.0.1"
|
||||
universalify "^2.0.0"
|
||||
|
||||
fsevents@~2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
||||
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
||||
|
||||
get-caller-file@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
|
|
@ -794,6 +1161,13 @@ highlight.js@^11.11.1:
|
|||
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.11.1.tgz#fca06fa0e5aeecf6c4d437239135fabc15213585"
|
||||
integrity sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==
|
||||
|
||||
html-encoding-sniffer@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz#f8d9390b3b348b50d4f61c16dd2ef5c05980a882"
|
||||
integrity sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==
|
||||
dependencies:
|
||||
"@exodus/bytes" "^1.6.0"
|
||||
|
||||
husky@^9.1.7:
|
||||
version "9.1.7"
|
||||
resolved "https://registry.yarnpkg.com/husky/-/husky-9.1.7.tgz#d46a38035d101b46a70456a850ff4201344c0b2d"
|
||||
|
|
@ -831,11 +1205,43 @@ is-glob@^4.0.0, is-glob@^4.0.3:
|
|||
dependencies:
|
||||
is-extglob "^2.1.1"
|
||||
|
||||
is-potential-custom-element-name@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmmirror.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
|
||||
integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
|
||||
|
||||
isexe@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
|
||||
|
||||
jsdom@^29.1.1:
|
||||
version "29.1.1"
|
||||
resolved "https://registry.npmmirror.com/jsdom/-/jsdom-29.1.1.tgz#5b9704906f3cd510c34aa941ae2f8f7f8179df01"
|
||||
integrity sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==
|
||||
dependencies:
|
||||
"@asamuzakjp/css-color" "^5.1.11"
|
||||
"@asamuzakjp/dom-selector" "^7.1.1"
|
||||
"@bramus/specificity" "^2.4.2"
|
||||
"@csstools/css-syntax-patches-for-csstree" "^1.1.3"
|
||||
"@exodus/bytes" "^1.15.0"
|
||||
css-tree "^3.2.1"
|
||||
data-urls "^7.0.0"
|
||||
decimal.js "^10.6.0"
|
||||
html-encoding-sniffer "^6.0.0"
|
||||
is-potential-custom-element-name "^1.0.1"
|
||||
lru-cache "^11.3.5"
|
||||
parse5 "^8.0.1"
|
||||
saxes "^6.0.0"
|
||||
symbol-tree "^3.2.4"
|
||||
tough-cookie "^6.0.1"
|
||||
undici "^7.25.0"
|
||||
w3c-xmlserializer "^5.0.0"
|
||||
webidl-conversions "^8.0.1"
|
||||
whatwg-mimetype "^5.0.0"
|
||||
whatwg-url "^16.0.1"
|
||||
xml-name-validator "^5.0.0"
|
||||
|
||||
json-buffer@3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
|
||||
|
|
@ -875,6 +1281,80 @@ levn@^0.4.1:
|
|||
prelude-ls "^1.2.1"
|
||||
type-check "~0.4.0"
|
||||
|
||||
lightningcss-android-arm64@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.npmmirror.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz#f033885116dfefd9c6f54787523e3514b61e1968"
|
||||
integrity sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==
|
||||
|
||||
lightningcss-darwin-arm64@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.npmmirror.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz#50b71871b01c8199584b649e292547faea7af9b5"
|
||||
integrity sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==
|
||||
|
||||
lightningcss-darwin-x64@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.npmmirror.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz#35f3e97332d130b9ca181e11b568ded6aebc6d5e"
|
||||
integrity sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==
|
||||
|
||||
lightningcss-freebsd-x64@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.npmmirror.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz#9777a76472b64ed6ff94342ad64c7bafd794a575"
|
||||
integrity sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==
|
||||
|
||||
lightningcss-linux-arm-gnueabihf@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.npmmirror.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz#13ae652e1ab73b9135d7b7da172f666c410ad53d"
|
||||
integrity sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==
|
||||
|
||||
lightningcss-linux-arm64-gnu@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.npmmirror.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz#417858795a94592f680123a1b1f9da8a0e1ef335"
|
||||
integrity sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==
|
||||
|
||||
lightningcss-linux-arm64-musl@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.npmmirror.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz#6be36692e810b718040802fd809623cffe732133"
|
||||
integrity sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==
|
||||
|
||||
lightningcss-linux-x64-gnu@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.npmmirror.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz#0b7803af4eb21cfd38dd39fe2abbb53c7dd091f6"
|
||||
integrity sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==
|
||||
|
||||
lightningcss-linux-x64-musl@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.npmmirror.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz#88dc8ba865ddddb1ac5ef04b0f161804418c163b"
|
||||
integrity sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.npmmirror.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz#4f30ba3fa5e925f5b79f945e8cc0d176c3b1ab38"
|
||||
integrity sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==
|
||||
|
||||
lightningcss-win32-x64-msvc@1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.npmmirror.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz#141aa5605645064928902bb4af045fa7d9f4220a"
|
||||
integrity sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==
|
||||
|
||||
lightningcss@^1.32.0:
|
||||
version "1.32.0"
|
||||
resolved "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.32.0.tgz#b85aae96486dcb1bf49a7c8571221273f4f1e4a9"
|
||||
integrity sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==
|
||||
dependencies:
|
||||
detect-libc "^2.0.3"
|
||||
optionalDependencies:
|
||||
lightningcss-android-arm64 "1.32.0"
|
||||
lightningcss-darwin-arm64 "1.32.0"
|
||||
lightningcss-darwin-x64 "1.32.0"
|
||||
lightningcss-freebsd-x64 "1.32.0"
|
||||
lightningcss-linux-arm-gnueabihf "1.32.0"
|
||||
lightningcss-linux-arm64-gnu "1.32.0"
|
||||
lightningcss-linux-arm64-musl "1.32.0"
|
||||
lightningcss-linux-x64-gnu "1.32.0"
|
||||
lightningcss-linux-x64-musl "1.32.0"
|
||||
lightningcss-win32-arm64-msvc "1.32.0"
|
||||
lightningcss-win32-x64-msvc "1.32.0"
|
||||
|
||||
locate-path@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
|
||||
|
|
@ -887,6 +1367,23 @@ lodash@^4.17.23:
|
|||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c"
|
||||
integrity sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==
|
||||
|
||||
lru-cache@^11.3.5:
|
||||
version "11.5.1"
|
||||
resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.5.1.tgz#f3daa3540847b9737ebc02499ddb36765e54db4a"
|
||||
integrity sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==
|
||||
|
||||
magic-string@^0.30.21:
|
||||
version "0.30.21"
|
||||
resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91"
|
||||
integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==
|
||||
dependencies:
|
||||
"@jridgewell/sourcemap-codec" "^1.5.5"
|
||||
|
||||
mdn-data@2.27.1:
|
||||
version "2.27.1"
|
||||
resolved "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.27.1.tgz#e37b9c50880b75366c4d40ac63d9bbcacdb61f0e"
|
||||
integrity sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==
|
||||
|
||||
minimatch@^10.2.2, minimatch@^10.2.4:
|
||||
version "10.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1"
|
||||
|
|
@ -904,6 +1401,11 @@ ms@^2.1.3:
|
|||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
nanoid@^3.3.12:
|
||||
version "3.3.15"
|
||||
resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.15.tgz#36c490fad8c6e86c824c940dfdde999b69ed4316"
|
||||
integrity sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==
|
||||
|
||||
natural-compare@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
|
|
@ -917,6 +1419,11 @@ obsidian@latest:
|
|||
"@types/codemirror" "5.60.8"
|
||||
moment "2.29.4"
|
||||
|
||||
obug@^2.1.1:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.npmmirror.com/obug/-/obug-2.1.3.tgz#c02c60f95abd603409330e767db7f2823193331e"
|
||||
integrity sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==
|
||||
|
||||
optionator@^0.9.3:
|
||||
version "0.9.4"
|
||||
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734"
|
||||
|
|
@ -943,6 +1450,13 @@ p-locate@^5.0.0:
|
|||
dependencies:
|
||||
p-limit "^3.0.2"
|
||||
|
||||
parse5@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.npmmirror.com/parse5/-/parse5-8.0.1.tgz#f43bcd2cd683efe084075333e9ce0da7d06da31e"
|
||||
integrity sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==
|
||||
dependencies:
|
||||
entities "^8.0.0"
|
||||
|
||||
path-exists@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
|
||||
|
|
@ -953,11 +1467,30 @@ path-key@^3.1.0:
|
|||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
|
||||
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
||||
|
||||
picomatch@^4.0.4:
|
||||
pathe@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716"
|
||||
integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==
|
||||
|
||||
picocolors@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
|
||||
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
|
||||
|
||||
picomatch@^4.0.3, picomatch@^4.0.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.4.tgz#fd6f5e00a143086e074dffe4c924b8fb293b0589"
|
||||
integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==
|
||||
|
||||
postcss@^8.5.15:
|
||||
version "8.5.15"
|
||||
resolved "https://registry.npmmirror.com/postcss/-/postcss-8.5.15.tgz#d1eaf677a324e9ec02196da2d3fecf4a0b9a735c"
|
||||
integrity sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==
|
||||
dependencies:
|
||||
nanoid "^3.3.12"
|
||||
picocolors "^1.1.1"
|
||||
source-map-js "^1.2.1"
|
||||
|
||||
prelude-ls@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||
|
|
@ -968,7 +1501,7 @@ prettier@^3.8.3:
|
|||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.3.tgz#560f2de55bf01b4c0503bc629d5df99b9a1d09b0"
|
||||
integrity sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==
|
||||
|
||||
punycode@^2.1.0:
|
||||
punycode@^2.1.0, punycode@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
|
||||
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
|
||||
|
|
@ -983,6 +1516,35 @@ require-directory@^2.1.1:
|
|||
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||
integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
|
||||
|
||||
require-from-string@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
|
||||
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
|
||||
|
||||
rolldown@~1.1.2:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmmirror.com/rolldown/-/rolldown-1.1.3.tgz#87072bfd0d1bdd02a66076a261a62e8e49b3f0e2"
|
||||
integrity sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==
|
||||
dependencies:
|
||||
"@oxc-project/types" "=0.137.0"
|
||||
"@rolldown/pluginutils" "^1.0.0"
|
||||
optionalDependencies:
|
||||
"@rolldown/binding-android-arm64" "1.1.3"
|
||||
"@rolldown/binding-darwin-arm64" "1.1.3"
|
||||
"@rolldown/binding-darwin-x64" "1.1.3"
|
||||
"@rolldown/binding-freebsd-x64" "1.1.3"
|
||||
"@rolldown/binding-linux-arm-gnueabihf" "1.1.3"
|
||||
"@rolldown/binding-linux-arm64-gnu" "1.1.3"
|
||||
"@rolldown/binding-linux-arm64-musl" "1.1.3"
|
||||
"@rolldown/binding-linux-ppc64-gnu" "1.1.3"
|
||||
"@rolldown/binding-linux-s390x-gnu" "1.1.3"
|
||||
"@rolldown/binding-linux-x64-gnu" "1.1.3"
|
||||
"@rolldown/binding-linux-x64-musl" "1.1.3"
|
||||
"@rolldown/binding-openharmony-arm64" "1.1.3"
|
||||
"@rolldown/binding-wasm32-wasi" "1.1.3"
|
||||
"@rolldown/binding-win32-arm64-msvc" "1.1.3"
|
||||
"@rolldown/binding-win32-x64-msvc" "1.1.3"
|
||||
|
||||
rxjs@7.8.2:
|
||||
version "7.8.2"
|
||||
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.2.tgz#955bc473ed8af11a002a2be52071bf475638607b"
|
||||
|
|
@ -990,6 +1552,13 @@ rxjs@7.8.2:
|
|||
dependencies:
|
||||
tslib "^2.1.0"
|
||||
|
||||
saxes@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.npmmirror.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5"
|
||||
integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==
|
||||
dependencies:
|
||||
xmlchars "^2.2.0"
|
||||
|
||||
semver@^7.7.3:
|
||||
version "7.7.4"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a"
|
||||
|
|
@ -1012,6 +1581,26 @@ shell-quote@1.8.3:
|
|||
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.3.tgz#55e40ef33cf5c689902353a3d8cd1a6725f08b4b"
|
||||
integrity sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==
|
||||
|
||||
siginfo@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmmirror.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30"
|
||||
integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==
|
||||
|
||||
source-map-js@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||
|
||||
stackback@0.0.2:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.npmmirror.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b"
|
||||
integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==
|
||||
|
||||
std-env@^4.0.0-rc.1:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.npmmirror.com/std-env/-/std-env-4.1.0.tgz#45899abc590d86d682e87f0acd1033a75084cd3f"
|
||||
integrity sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==
|
||||
|
||||
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
|
|
@ -1047,6 +1636,21 @@ supports-color@^7.1.0:
|
|||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
symbol-tree@^3.2.4:
|
||||
version "3.2.4"
|
||||
resolved "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
||||
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
|
||||
|
||||
tinybench@^2.9.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.npmmirror.com/tinybench/-/tinybench-2.9.0.tgz#103c9f8ba6d7237a47ab6dd1dcff77251863426b"
|
||||
integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==
|
||||
|
||||
tinyexec@^1.0.2:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.2.4.tgz#ae45bb2edebda94c70f4ea897e0f1243e470db71"
|
||||
integrity sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==
|
||||
|
||||
tinyglobby@^0.2.15:
|
||||
version "0.2.16"
|
||||
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.16.tgz#1c3b7eb953fce42b226bc5a1ee06428281aff3d6"
|
||||
|
|
@ -1055,6 +1659,45 @@ tinyglobby@^0.2.15:
|
|||
fdir "^6.5.0"
|
||||
picomatch "^4.0.4"
|
||||
|
||||
tinyglobby@^0.2.17:
|
||||
version "0.2.17"
|
||||
resolved "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.17.tgz#562a9a6c9eb2b3b123d39719f9af5bb44fcd7631"
|
||||
integrity sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==
|
||||
dependencies:
|
||||
fdir "^6.5.0"
|
||||
picomatch "^4.0.4"
|
||||
|
||||
tinyrainbow@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmmirror.com/tinyrainbow/-/tinyrainbow-3.1.0.tgz#1d8a623893f95cf0a2ddb9e5d11150e191409421"
|
||||
integrity sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==
|
||||
|
||||
tldts-core@^7.4.4:
|
||||
version "7.4.4"
|
||||
resolved "https://registry.npmmirror.com/tldts-core/-/tldts-core-7.4.4.tgz#a06de228c70a334e26d1597af7a82376e8588bce"
|
||||
integrity sha512-vwVLJVvvpslm7vqAH7+XNj/neA/Ynq7DT2EEcMuwc5YzN5XaMyRAqxwU+uX3azZ1FQtB2gvrvnLnAEkvYlVdfg==
|
||||
|
||||
tldts@^7.0.5:
|
||||
version "7.4.4"
|
||||
resolved "https://registry.npmmirror.com/tldts/-/tldts-7.4.4.tgz#343191348abbff80118732a1a84e90fcf95b29df"
|
||||
integrity sha512-kFXFK7O4WPextIUAOk8qtnw9dxR9UIXP9CjuH1cTBVBZMDeQcUPgr/IazGiw1B0Yiw5L75gHLWeW4iD793r90g==
|
||||
dependencies:
|
||||
tldts-core "^7.4.4"
|
||||
|
||||
tough-cookie@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-6.0.1.tgz#a495f833836609ed983c19bc65639cfbceb54c76"
|
||||
integrity sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==
|
||||
dependencies:
|
||||
tldts "^7.0.5"
|
||||
|
||||
tr46@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.npmmirror.com/tr46/-/tr46-6.0.0.tgz#f5a1ae546a0adb32a277a2278d0d17fa2f9093e6"
|
||||
integrity sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==
|
||||
dependencies:
|
||||
punycode "^2.3.1"
|
||||
|
||||
tree-kill@1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
|
||||
|
|
@ -1070,7 +1713,7 @@ tslib@2.4.0:
|
|||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
|
||||
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
|
||||
|
||||
tslib@^2.1.0:
|
||||
tslib@^2.1.0, tslib@^2.4.0:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
|
||||
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
||||
|
|
@ -1087,6 +1730,16 @@ typescript@^6.0.3:
|
|||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-6.0.3.tgz#90251dc007916e972786cb94d74d15b185577d21"
|
||||
integrity sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==
|
||||
|
||||
undici-types@~6.21.0:
|
||||
version "6.21.0"
|
||||
resolved "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb"
|
||||
integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==
|
||||
|
||||
undici@^7.25.0:
|
||||
version "7.28.0"
|
||||
resolved "https://registry.npmmirror.com/undici/-/undici-7.28.0.tgz#97d64564198b285bc281f0e8e29597e3d11fe7ec"
|
||||
integrity sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==
|
||||
|
||||
universalify@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
|
||||
|
|
@ -1099,11 +1752,76 @@ uri-js@^4.2.2:
|
|||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
"vite@^6.0.0 || ^7.0.0 || ^8.0.0":
|
||||
version "8.1.0"
|
||||
resolved "https://registry.npmmirror.com/vite/-/vite-8.1.0.tgz#0734dc1a48faeb2bd5f5b16b66dcbfae484fec55"
|
||||
integrity sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==
|
||||
dependencies:
|
||||
lightningcss "^1.32.0"
|
||||
picomatch "^4.0.4"
|
||||
postcss "^8.5.15"
|
||||
rolldown "~1.1.2"
|
||||
tinyglobby "^0.2.17"
|
||||
optionalDependencies:
|
||||
fsevents "~2.3.3"
|
||||
|
||||
vitest@^4.1.9:
|
||||
version "4.1.9"
|
||||
resolved "https://registry.npmmirror.com/vitest/-/vitest-4.1.9.tgz#98f22fbd70e2a18c4a92bb20624bc92e5dfac5f3"
|
||||
integrity sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==
|
||||
dependencies:
|
||||
"@vitest/expect" "4.1.9"
|
||||
"@vitest/mocker" "4.1.9"
|
||||
"@vitest/pretty-format" "4.1.9"
|
||||
"@vitest/runner" "4.1.9"
|
||||
"@vitest/snapshot" "4.1.9"
|
||||
"@vitest/spy" "4.1.9"
|
||||
"@vitest/utils" "4.1.9"
|
||||
es-module-lexer "^2.0.0"
|
||||
expect-type "^1.3.0"
|
||||
magic-string "^0.30.21"
|
||||
obug "^2.1.1"
|
||||
pathe "^2.0.3"
|
||||
picomatch "^4.0.3"
|
||||
std-env "^4.0.0-rc.1"
|
||||
tinybench "^2.9.0"
|
||||
tinyexec "^1.0.2"
|
||||
tinyglobby "^0.2.15"
|
||||
tinyrainbow "^3.1.0"
|
||||
vite "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
why-is-node-running "^2.3.0"
|
||||
|
||||
w3c-keyname@^2.2.4:
|
||||
version "2.2.8"
|
||||
resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz#7b17c8c6883d4e8b86ac8aba79d39e880f8869c5"
|
||||
integrity sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==
|
||||
|
||||
w3c-xmlserializer@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c"
|
||||
integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==
|
||||
dependencies:
|
||||
xml-name-validator "^5.0.0"
|
||||
|
||||
webidl-conversions@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-8.0.1.tgz#0657e571fe6f06fcb15ca50ed1fdbcb495cd1686"
|
||||
integrity sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==
|
||||
|
||||
whatwg-mimetype@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz#d8232895dbd527ceaee74efd4162008fb8a8cf48"
|
||||
integrity sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==
|
||||
|
||||
whatwg-url@^16.0.0, whatwg-url@^16.0.1:
|
||||
version "16.0.1"
|
||||
resolved "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-16.0.1.tgz#047f7f4bd36ef76b7198c172d1b1cebc66f764dd"
|
||||
integrity sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==
|
||||
dependencies:
|
||||
"@exodus/bytes" "^1.11.0"
|
||||
tr46 "^6.0.0"
|
||||
webidl-conversions "^8.0.1"
|
||||
|
||||
which@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
||||
|
|
@ -1111,6 +1829,14 @@ which@^2.0.1:
|
|||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
why-is-node-running@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npmmirror.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz#a3f69a97107f494b3cdc3bdddd883a7d65cebf04"
|
||||
integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==
|
||||
dependencies:
|
||||
siginfo "^2.0.0"
|
||||
stackback "0.0.2"
|
||||
|
||||
word-wrap@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34"
|
||||
|
|
@ -1125,6 +1851,16 @@ wrap-ansi@^7.0.0:
|
|||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
xml-name-validator@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673"
|
||||
integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==
|
||||
|
||||
xmlchars@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
|
||||
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
|
||||
|
||||
y18n@^5.0.5:
|
||||
version "5.0.8"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
||||
|
|
|
|||
Loading…
Reference in a new issue