mirror of
https://github.com/lllin000/PaperForge.git
synced 2026-07-22 06:50:53 +00:00
chore(plugin): finalize TS migration -- delete legacy testable.js
This commit is contained in:
parent
3ccf5c424e
commit
db6eea9a0a
5 changed files with 1576 additions and 5396 deletions
|
|
@ -1,157 +1,56 @@
|
|||
---
|
||||
phase: 02-code-review
|
||||
reviewed: 2026-05-14T12:00:00Z
|
||||
depth: deep
|
||||
phase: 02-code-review-command
|
||||
reviewed: 2026-05-24T09:10:00Z
|
||||
depth: standard
|
||||
files_reviewed: 1
|
||||
files_reviewed_list:
|
||||
- paperforge/memory/schema.py
|
||||
- paperforge/plugin/package.json
|
||||
findings:
|
||||
critical: 0
|
||||
warning: 3
|
||||
info: 2
|
||||
total: 5
|
||||
status: issues_found
|
||||
warning: 0
|
||||
info: 0
|
||||
total: 0
|
||||
status: clean
|
||||
---
|
||||
|
||||
# Phase 02: Code Review Report
|
||||
|
||||
**Reviewed:** 2026-05-14T12:00:00Z
|
||||
**Depth:** deep
|
||||
**Reviewed:** 2026-05-24T09:10:00Z
|
||||
**Depth:** standard
|
||||
**Files Reviewed:** 1
|
||||
**Status:** issues_found
|
||||
**Status:** clean
|
||||
|
||||
## Summary
|
||||
|
||||
Reviewed Task 1 implementation: addition of `CREATE_READING_LOG` and `CREATE_PROJECT_LOG` SQL table definitions to `paperforge/memory/schema.py`. The implementation faithfully follows the plan specification with correct SQL syntax, proper registration in `ensure_schema()`, correct `ALL_TABLES` inclusion, and `CURRENT_SCHEMA_VERSION` bumped from 1 to 2. All 5 existing schema unit tests pass. No critical defects were introduced.
|
||||
Reviewed Task 1 implementation (commit `8982c67`) — installation of TypeScript + esbuild build system dependencies in `paperforge/plugin/package.json`, as specified in [plugin-ts-migration plan](docs/superpowers/plans/2026-05-24-plugin-ts-migration.md).
|
||||
|
||||
However, three warnings were identified: (1) the foreign key from `reading_log` to `papers` will create a deletion-ordering conflict in `builder.py` once the table is populated (future Task 6 concern), (2) the column name `date` in `project_log` shadows a SQLite built-in function name, and (3) the foreign key column `reading_log.paper_id` lacks an index, which will cause full table scans on lookup queries.
|
||||
**Result: All reviewed files meet quality standards. No issues found.**
|
||||
|
||||
## Warnings
|
||||
### What was verified
|
||||
|
||||
### WR-01: Foreign Key Will Block `DELETE FROM papers` During Rebuild
|
||||
| Check | Result |
|
||||
|-------|--------|
|
||||
| **Plan alignment** | EXACT match — every version, script, and dependency matches the plan verbatim |
|
||||
| **Scripts added** | `dev` and `build` — exactly as specified |
|
||||
| **DevDeps added** | `typescript ^5.4.0`, `esbuild ^0.25.0`, `builtin-modules ^3.3.0`, `@types/node ^20.0.0` |
|
||||
| **Existing deps preserved** | `vitest`, `obsidian-test-mocks`, `jsdom`, `obsidian` — untouched |
|
||||
| **`package-lock.json`** | Committed alongside package.json (659 insertions, 120 deletions) |
|
||||
| **`npm install`** | Clean — all 4 packages installed, `npm ci --dry-run` reports "up to date" |
|
||||
| **`npm audit`** | 0 vulnerabilities (dev-only deps are expected security-wise) |
|
||||
| **Installed versions** | TypeScript 5.9.3, esbuild 0.25.12, builtin-modules 3.3.0, @types/node 20.19.41 |
|
||||
| **Commit message** | `"chore(plugin): add esbuild, typescript, @types/node devDeps"` — matches plan |
|
||||
| **No extra files** | Only `package.json` and `package-lock.json` were modified |
|
||||
| **Version reasonableness** | All ranges are stable and compatible with the planned tech stack |
|
||||
|
||||
**File:** `paperforge/memory/schema.py:153` (FK definition), `paperforge/memory/builder.py:84` (DELETE caller)
|
||||
### Specific concerns checked and cleared
|
||||
|
||||
**Issue:** The `reading_log` table defines `FOREIGN KEY (paper_id) REFERENCES papers(zotero_key)` without `ON DELETE CASCADE`. During non-schema-change rebuilds in `builder.py`, the code executes `DELETE FROM papers` (line 84) WITHOUT first clearing `reading_log`. Once Task 6 populates `reading_log` from JSONL, this DELETE will fail with an `IntegrityError` because SQLite enforces foreign key constraints on DML operations when `PRAGMA foreign_keys=ON` (which `db.py:34` explicitly enables).
|
||||
|
||||
Currently latent because `reading_log` is never populated (Task 6 has not been implemented). Will surface during the Task 6 rebuild flow.
|
||||
|
||||
**Fix:**
|
||||
|
||||
In `builder.py`, clear `reading_log` (and `project_log`) before deleting `papers`:
|
||||
|
||||
```python
|
||||
# In builder.py, before the DELETE FROM papers (line 84), add:
|
||||
conn.execute("DELETE FROM reading_log;")
|
||||
conn.execute("DELETE FROM project_log;")
|
||||
```
|
||||
|
||||
Alternatively, add `ON DELETE CASCADE` to the foreign key definition in `schema.py`:
|
||||
```python
|
||||
FOREIGN KEY (paper_id) REFERENCES papers(zotero_key) ON DELETE CASCADE
|
||||
```
|
||||
|
||||
The first approach (explicit DELETE) is preferred because it makes the rebuild flow explicit and doesn't silently cascade deletions that could surprise maintainers.
|
||||
- **`esbuild.config.mjs` not yet existing**: Expected — it will be added in Task 3. The scripts reference it preemptively, which is by design.
|
||||
- **Single-dash `-noEmit` / `-skipLibCheck`**: Intentional — matches the official Obsidian sample plugin convention, as specified in the plan.
|
||||
- **`builtin-modules` necessity**: Required by the esbuild config (Task 3) to extern Node.js built-ins at bundle time. Correct inclusion.
|
||||
- **`@types/node` scope**: `^20.0.0` correctly restricts to Node 20.x LTS types. No version creep risk.
|
||||
|
||||
---
|
||||
|
||||
### WR-02: Column Name `date` Shadows SQLite Built-in Function
|
||||
|
||||
**File:** `paperforge/memory/schema.py:161`
|
||||
|
||||
**Issue:** The `project_log` table uses `date` as a column name (line 161: `date TEXT NOT NULL`). While syntactically valid in SQLite (which allows function names as unquoted identifiers), `date` is a built-in SQL function. This creates ambiguity when reading queries — `SELECT date FROM project_log` works but `SELECT date(created_at) FROM project_log` is ambiguous. More importantly, if this schema is ever ported to another SQL dialect (PostgreSQL, MySQL), `date` is a reserved word and will require quoting.
|
||||
|
||||
**Fix:**
|
||||
|
||||
Rename the column to `log_date`, `entry_date`, or `recorded_date`:
|
||||
|
||||
```sql
|
||||
-- In CREATE_PROJECT_LOG:
|
||||
log_date TEXT NOT NULL,
|
||||
```
|
||||
|
||||
Note: This would also require updating the plan's Task 2 (`permanent.py`) and Task 6 (`builder.py`) where the column is referenced. If changing the schema column name is too invasive for this phase, consider adding a comment noting the potential ambiguity.
|
||||
|
||||
---
|
||||
|
||||
### WR-03: Missing Index on Foreign Key Column `reading_log.paper_id`
|
||||
|
||||
**File:** `paperforge/memory/schema.py:142`
|
||||
|
||||
**Issue:** The `reading_log` table has a foreign key on `paper_id` (line 142), but no index is created for this column. All queries filtering by `paper_id` (e.g., `get_reading_notes_for_paper()` in `permanent.py`, paper-context lookup) will require full table scans. As the reading log grows, this will degrade query performance.
|
||||
|
||||
**Fix:**
|
||||
|
||||
Add an index alongside the existing `EVENT_INDEX_SQL` block (following the established pattern in the file):
|
||||
|
||||
```python
|
||||
# After EVENT_INDEX_SQL (line 137), add:
|
||||
READING_LOG_INDEX_SQL = [
|
||||
"CREATE INDEX IF NOT EXISTS idx_reading_log_paper ON reading_log(paper_id);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_reading_log_project ON reading_log(project);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_reading_log_created ON reading_log(created_at);",
|
||||
]
|
||||
```
|
||||
|
||||
And register in `ensure_schema()` after the `EVENT_INDEX_SQL` loop:
|
||||
|
||||
```python
|
||||
for idx_sql in READING_LOG_INDEX_SQL:
|
||||
conn.execute(idx_sql)
|
||||
```
|
||||
|
||||
Also add a project-level index for `project_log`:
|
||||
|
||||
```python
|
||||
PROJECT_LOG_INDEX_SQL = [
|
||||
"CREATE INDEX IF NOT EXISTS idx_project_log_project ON project_log(project);",
|
||||
"CREATE INDEX IF NOT EXISTS idx_project_log_created ON project_log(created_at);",
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Info
|
||||
|
||||
### IN-01: Test Hardcodes Stale Schema Version
|
||||
|
||||
**File:** `tests/unit/memory/test_schema.py:73`
|
||||
|
||||
**Issue:** The test `test_get_schema_version_returns_stored_value` inserts `schema_version = '1'` and asserts the returned value equals 1. Since `CURRENT_SCHEMA_VERSION` was bumped to 2, the hardcoded '1' no longer matches the current version. While the test is verifying read-back behavior (not version equality), the stale value could confuse future maintainers who assume the test reflects the current schema version.
|
||||
|
||||
**Fix:**
|
||||
|
||||
Either update to a version-independent test or clarify with a comment:
|
||||
|
||||
```python
|
||||
# Test uses arbitrary version '1' for read-back verification (not tied to CURRENT_SCHEMA_VERSION)
|
||||
conn.execute(
|
||||
"INSERT INTO meta (key, value) VALUES ('schema_version', '1')"
|
||||
)
|
||||
conn.commit()
|
||||
assert get_schema_version(conn) == 1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### IN-02: `ALL_TABLES` Ordering — Child Tables After Parent (Latent DDL Risk)
|
||||
|
||||
**File:** `paperforge/memory/schema.py:175`
|
||||
|
||||
**Issue:** The `ALL_TABLES` list orders `papers` (index 1) before its child tables `paper_assets`, `paper_aliases`, `paper_events`, and now `reading_log` (indices 2-7). In `drop_all_tables()`, SQLite with `PRAGMA foreign_keys=ON` will reject `DROP TABLE papers` if any child table contains rows referencing papers. This is currently benign because `drop_all_tables()` is only called when schema versions mismatch — at which point the new `reading_log` table hasn't been populated yet. However, if `drop_all_tables` is ever called on a populated database, the ordering would cause a failure.
|
||||
|
||||
**Fix (if desired — pre-existing, not introduced by this change):**
|
||||
|
||||
Reorder `ALL_TABLES` so child tables (FK-referencing) appear before parent tables (FK-referenced):
|
||||
|
||||
```python
|
||||
ALL_TABLES = ["paper_fts", "reading_log", "project_log", "paper_events", "paper_aliases", "paper_assets", "papers", "meta"]
|
||||
```
|
||||
|
||||
Note: `paper_fts` is a virtual table and cannot have FK constraints, so its position is flexible.
|
||||
|
||||
---
|
||||
|
||||
_Reviewed: 2026-05-14T12:00:00Z_
|
||||
_Reviewer: the agent (gsd-code-reviewer)_
|
||||
_Depth: deep_
|
||||
_Reviewed: 2026-05-24T09:10:00Z_
|
||||
_Reviewer: VT-OS/OPENCODE (gsd-code-reviewer)_
|
||||
_Depth: standard_
|
||||
|
|
|
|||
1514
docs/superpowers/plans/2026-05-24-plugin-ts-migration.md
Normal file
1514
docs/superpowers/plans/2026-05-24-plugin-ts-migration.md
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
|
@ -1,293 +0,0 @@
|
|||
/**
|
||||
* Testable functions extracted from main.js for Vitest.
|
||||
* No obsidian dependency — safe to import in Node test environment.
|
||||
*/
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execFile, spawn } = require('node:child_process');
|
||||
|
||||
function readPathConfig(vaultPath, _fs) {
|
||||
const f = _fs || fs;
|
||||
const pfPath = path.join(vaultPath, 'paperforge.json');
|
||||
const defaults = {
|
||||
system_dir: 'System',
|
||||
resources_dir: 'Resources',
|
||||
literature_dir: 'Literature',
|
||||
base_dir: 'Bases',
|
||||
};
|
||||
|
||||
try {
|
||||
if (!f.existsSync(pfPath)) {
|
||||
return { ...defaults, _warning: 'paperforge.json not found; using defaults' };
|
||||
}
|
||||
const raw = f.readFileSync(pfPath, 'utf-8');
|
||||
const data = JSON.parse(raw);
|
||||
const vc = data.vault_config || {};
|
||||
return {
|
||||
system_dir: vc.system_dir || data.system_dir || defaults.system_dir,
|
||||
resources_dir: vc.resources_dir || data.resources_dir || defaults.resources_dir,
|
||||
literature_dir: vc.literature_dir || data.literature_dir || defaults.literature_dir,
|
||||
base_dir: vc.base_dir || data.base_dir || defaults.base_dir,
|
||||
_warning: null,
|
||||
};
|
||||
} catch {
|
||||
return { ...defaults, _warning: 'paperforge.json invalid; using defaults' };
|
||||
}
|
||||
}
|
||||
|
||||
function resolveRuntimePaths(vaultPath, _fs) {
|
||||
const cfg = readPathConfig(vaultPath, _fs);
|
||||
const systemRoot = path.join(vaultPath, cfg.system_dir, 'PaperForge');
|
||||
return {
|
||||
vault: vaultPath,
|
||||
systemDir: systemRoot,
|
||||
indexesDir: path.join(systemRoot, 'indexes'),
|
||||
logsDir: path.join(systemRoot, 'logs'),
|
||||
dbPath: path.join(systemRoot, 'indexes', 'paperforge.db'),
|
||||
memoryStatePath: path.join(systemRoot, 'indexes', 'memory-runtime-state.json'),
|
||||
vectorStatePath: path.join(systemRoot, 'indexes', 'vector-runtime-state.json'),
|
||||
healthStatePath: path.join(systemRoot, 'indexes', 'runtime-health.json'),
|
||||
buildStatePath: path.join(systemRoot, 'indexes', 'vector-build-state.json'),
|
||||
exportsDir: path.join(systemRoot, 'exports'),
|
||||
ocrDir: path.join(systemRoot, 'ocr'),
|
||||
pluginDataPath: path.join(vaultPath, '.obsidian', 'plugins', 'paperforge', 'data.json'),
|
||||
pfJsonPath: path.join(vaultPath, 'paperforge.json'),
|
||||
configWarning: cfg._warning,
|
||||
};
|
||||
}
|
||||
|
||||
// ── Runtime helpers ──
|
||||
|
||||
function resolvePythonExecutable(vaultPath, settings, _fs, _execFileSync) {
|
||||
const f = _fs || fs;
|
||||
const execSync = _execFileSync || require("node:child_process").execFileSync;
|
||||
|
||||
if (settings && settings.python_path && settings.python_path.trim()) {
|
||||
const manualPath = settings.python_path.trim();
|
||||
if (f.existsSync(manualPath)) {
|
||||
return { path: manualPath, source: "manual", extraArgs: [] };
|
||||
}
|
||||
}
|
||||
|
||||
const venvCandidates = [
|
||||
path.join(vaultPath, ".paperforge-test-venv", "Scripts", "python.exe"),
|
||||
path.join(vaultPath, ".venv", "Scripts", "python.exe"),
|
||||
path.join(vaultPath, "venv", "Scripts", "python.exe"),
|
||||
];
|
||||
for (const candidate of venvCandidates) {
|
||||
try {
|
||||
if (f.existsSync(candidate)) {
|
||||
return { path: candidate, source: "auto-detected", extraArgs: [] };
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
const systemCandidates = [
|
||||
{ path: "py", extraArgs: ["-3"] },
|
||||
{ path: "python", extraArgs: [] },
|
||||
{ path: "python3", extraArgs: [] },
|
||||
];
|
||||
for (const candidate of systemCandidates) {
|
||||
try {
|
||||
const verOut = execSync(candidate.path, [...candidate.extraArgs, "--version"], {
|
||||
encoding: "utf-8", timeout: 5000, windowsHide: true,
|
||||
});
|
||||
if (verOut && verOut.toLowerCase().includes("python")) {
|
||||
return { path: candidate.path, source: "auto-detected", extraArgs: candidate.extraArgs };
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return { path: "python", source: "auto-detected", extraArgs: [] };
|
||||
}
|
||||
|
||||
function getPluginVersion(app) {
|
||||
try {
|
||||
const manifest = app && app.plugins && app.plugins.plugins &&
|
||||
app.plugins.plugins["paperforge"] && app.plugins.plugins["paperforge"].manifest;
|
||||
return (manifest && manifest.version) || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function checkRuntimeVersion(pythonExe, pluginVersion, cwd, timeout, _execFile) {
|
||||
if (timeout === undefined) timeout = 10000;
|
||||
const exe = _execFile || execFile;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
exe(pythonExe, ["-c", "import paperforge; print(paperforge.__version__)"],
|
||||
{ cwd, timeout },
|
||||
(err, stdout) => {
|
||||
if (err) {
|
||||
resolve({ status: "not-installed", pyVersion: null, pluginVersion, error: err.message });
|
||||
return;
|
||||
}
|
||||
const pyVer = (stdout && stdout.trim()) || null;
|
||||
if (pyVer === pluginVersion) {
|
||||
resolve({ status: "match", pyVersion: pyVer, pluginVersion, error: null });
|
||||
} else {
|
||||
resolve({ status: "mismatch", pyVersion: pyVer, pluginVersion, error: null });
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ── Error helpers ──
|
||||
|
||||
function classifyError(errorCode) {
|
||||
const code = String(errorCode);
|
||||
const patterns = {
|
||||
ENOENT: { type: "python_missing", message: "Python executable not found", recoverable: true },
|
||||
"python-missing": { type: "python_missing", message: "Python executable not found", recoverable: true },
|
||||
MODULE_NOT_FOUND: { type: "import_failed", message: "PaperForge package not installed", recoverable: true },
|
||||
"import-failed": { type: "import_failed", message: "PaperForge package not installed", recoverable: true },
|
||||
"version-mismatch": { type: "version_mismatch", message: "Plugin and package versions differ", recoverable: true, action: "sync-runtime" },
|
||||
"pip-failed": { type: "pip_install_failure", message: "pip install command failed", recoverable: true },
|
||||
ETIMEDOUT: { type: "timeout", message: "Subprocess timed out", recoverable: true, action: "retry" },
|
||||
timeout: { type: "timeout", message: "Subprocess timed out", recoverable: true, action: "retry" },
|
||||
};
|
||||
const match = patterns[code];
|
||||
if (match) return { ...match };
|
||||
return { type: "unknown", message: String(errorCode), recoverable: false };
|
||||
}
|
||||
|
||||
function buildRuntimeInstallCommand(pythonExe, version, extraArgs) {
|
||||
if (extraArgs === undefined) extraArgs = [];
|
||||
const pypiPkg = `paperforge==${version}`;
|
||||
const gitUrl = `git+https://github.com/LLLin000/PaperForge.git@v${version}`;
|
||||
const pypiArgs = [...extraArgs, "-m", "pip", "install", "--upgrade", pypiPkg];
|
||||
const gitArgs = [...extraArgs, "-m", "pip", "install", "--upgrade", gitUrl];
|
||||
return { cmd: pythonExe, url: gitUrl.replace('@v', '@'), args: gitArgs, pypiArgs, gitArgs, timeout: 120000 };
|
||||
}
|
||||
|
||||
function parseRuntimeStatus(err, stdout, stderr) {
|
||||
if (!err && stdout) {
|
||||
return { status: "ok", version: stdout.trim() };
|
||||
}
|
||||
if (err && err.code === "ENOENT") {
|
||||
const classified = classifyError("ENOENT");
|
||||
return { status: "error", version: null, ...classified };
|
||||
}
|
||||
if (stderr && stderr.includes("No module named paperforge")) {
|
||||
const classified = classifyError("import-failed");
|
||||
return { status: "error", version: null, ...classified };
|
||||
}
|
||||
if (err && err.killed) {
|
||||
const classified = classifyError("timeout");
|
||||
return { status: "error", version: null, ...classified };
|
||||
}
|
||||
if (stderr && stderr.includes("ModuleNotFoundError")) {
|
||||
const classified = classifyError("import-failed");
|
||||
return { status: "error", version: null, ...classified };
|
||||
}
|
||||
return { status: "error", version: null, type: "unknown",
|
||||
message: err ? err.message : String(stderr), recoverable: false };
|
||||
}
|
||||
|
||||
// ── Action definitions ──
|
||||
|
||||
const ACTIONS = [
|
||||
{
|
||||
id: "paperforge-sync",
|
||||
title: "Sync Library",
|
||||
desc: "Pull new references from Zotero and generate literature notes",
|
||||
icon: "\u21BB",
|
||||
cmd: "sync",
|
||||
okMsg: "Sync complete",
|
||||
},
|
||||
{
|
||||
id: "paperforge-ocr",
|
||||
title: "Run OCR",
|
||||
desc: "Extract full text and figures from PDFs via PaddleOCR",
|
||||
icon: "\u229E",
|
||||
cmd: "ocr",
|
||||
okMsg: "OCR started",
|
||||
},
|
||||
{
|
||||
id: "paperforge-doctor",
|
||||
title: "Run Doctor",
|
||||
desc: "Verify PaperForge setup \u2014 check configs, Zotero, paths, and index health",
|
||||
icon: "\u2695",
|
||||
cmd: "doctor",
|
||||
okMsg: "Doctor complete",
|
||||
},
|
||||
{
|
||||
id: "paperforge-repair",
|
||||
title: "Repair Issues",
|
||||
desc: "Fix three-way state divergence, path errors, and rebuild index",
|
||||
icon: "\u21BA",
|
||||
cmd: "repair",
|
||||
args: ["--fix", "--fix-paths"],
|
||||
okMsg: "Repair complete",
|
||||
},
|
||||
];
|
||||
|
||||
function buildCommandArgs(action, key, filter) {
|
||||
const args = Array.isArray(action.args) ? [...action.args] : [];
|
||||
if (action.needsKey && key) args.push(key);
|
||||
if (action.needsFilter || filter) args.push("--all");
|
||||
return args;
|
||||
}
|
||||
|
||||
function runSubprocess(pythonExe, args, cwd, timeout, _spawn, env) {
|
||||
const sp = _spawn || spawn;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const startTime = Date.now();
|
||||
const opts = { cwd, timeout, windowsHide: true };
|
||||
if (env) opts.env = env;
|
||||
const child = sp(pythonExe, args, opts);
|
||||
const stdoutChunks = [];
|
||||
const stderrChunks = [];
|
||||
|
||||
child.stdout.on("data", (data) => { stdoutChunks.push(data.toString("utf-8")); });
|
||||
child.stderr.on("data", (data) => { stderrChunks.push(data.toString("utf-8")); });
|
||||
|
||||
child.on("close", (code) => {
|
||||
resolve({ stdout: stdoutChunks.join(""), stderr: stderrChunks.join(""),
|
||||
exitCode: code, elapsed: Date.now() - startTime });
|
||||
});
|
||||
|
||||
child.on("error", (err) => {
|
||||
resolve({ stdout: stdoutChunks.join(""),
|
||||
stderr: stderrChunks.join("") + "\n" + err.message,
|
||||
exitCode: -1, elapsed: Date.now() - startTime });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function shouldRenderVectorReady(vectorDepsOk, embedStatusText) {
|
||||
return vectorDepsOk === true;
|
||||
}
|
||||
|
||||
function getDisclosureState(store, key, defaultCollapsed) {
|
||||
if (!store || typeof store !== 'object') return !!defaultCollapsed;
|
||||
if (!Object.prototype.hasOwnProperty.call(store, key)) return !!defaultCollapsed;
|
||||
return !!store[key];
|
||||
}
|
||||
|
||||
function toggleDisclosureState(store, key, defaultCollapsed) {
|
||||
const next = !getDisclosureState(store, key, defaultCollapsed);
|
||||
if (store && typeof store === 'object') {
|
||||
store[key] = next;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
readPathConfig,
|
||||
resolveRuntimePaths,
|
||||
resolvePythonExecutable,
|
||||
getPluginVersion,
|
||||
checkRuntimeVersion,
|
||||
classifyError,
|
||||
buildRuntimeInstallCommand,
|
||||
parseRuntimeStatus,
|
||||
ACTIONS,
|
||||
buildCommandArgs,
|
||||
runSubprocess,
|
||||
shouldRenderVectorReady,
|
||||
getDisclosureState,
|
||||
toggleDisclosureState,
|
||||
};
|
||||
|
|
@ -17,6 +17,6 @@
|
|||
},
|
||||
"lib": ["ES2018", "DOM"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "tests/**/*.ts"],
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue