noteconnection 1.8.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +257 -1803
- package/dist/src/agent_workspace.contract.parity.test.js +105 -421
- package/dist/src/agent_workspace.frontend.test.js +410 -11
- package/dist/src/android.knowledgebase.picker.contract.test.js +102 -0
- package/dist/src/appimage.patchelf.test.js +113 -0
- package/dist/src/appimage.portability.test.js +156 -0
- package/dist/src/appimage.update.test.js +127 -0
- package/dist/src/backend/FileLoader.js +18 -4
- package/dist/src/backend/FileLoader.test.js +125 -0
- package/dist/src/backend/GraphBuilder.identity.test.js +86 -0
- package/dist/src/backend/GraphBuilder.js +66 -18
- package/dist/src/backend/GraphBuilder.matching.test.js +127 -0
- package/dist/src/backend/GraphMetrics.js +0 -3
- package/dist/src/backend/ResourceIdentity.js +155 -0
- package/dist/src/backend/ResourceIdentity.test.js +113 -0
- package/dist/src/backend/algorithms/CycleDetection.js +0 -1
- package/dist/src/backend/algorithms/HybridEngine.js +0 -3
- package/dist/src/backend/algorithms/LayoutEngine.js +0 -1
- package/dist/src/backend/algorithms/StatisticalAnalyzer.js +0 -4
- package/dist/src/backend/algorithms/TopologicalSort.js +0 -2
- package/dist/src/backend/build_dag.js +0 -1
- package/dist/src/capacitor.bridge.serialization.contract.test.js +3 -0
- package/dist/src/capacitor.runtime.contract.test.js +11 -2
- package/dist/src/core/Graph.js +138 -42
- package/dist/src/core/Graph.test.js +74 -0
- package/dist/src/core/NoteConnection.js +2 -2
- package/dist/src/core/PathBridge.js +265 -2
- package/dist/src/core/ResourceReference.js +17 -0
- package/dist/src/desktop.godot.pack.test.js +142 -0
- package/dist/src/desktop.runtime.graph.test.js +66 -0
- package/dist/src/export/WorkspaceExportBundle.js +44 -0
- package/dist/src/export/WorkspaceExportBundle.test.js +61 -0
- package/dist/src/foundation.release.evidence.contract.test.js +127 -9
- package/dist/src/foundation.sqlite.runtime.contract.test.js +6 -0
- package/dist/src/frontend/README.md +257 -1803
- package/dist/src/frontend/agent_workspace.js +665 -7
- package/dist/src/frontend/index.html +31 -2
- package/dist/src/frontend/knowledge_projection_contract.js +230 -0
- package/dist/src/frontend/knowledge_projection_store.js +214 -0
- package/dist/src/frontend/libs/path_core.js +158 -42
- package/dist/src/frontend/locales/en.json +32 -0
- package/dist/src/frontend/locales/zh.json +32 -0
- package/dist/src/frontend/mobile_budget_runtime.js +33 -0
- package/dist/src/frontend/mobile_exact_analyzer.js +393 -0
- package/dist/src/frontend/mobile_identity_contract.js +221 -0
- package/dist/src/frontend/mobile_semantic_comparator.js +264 -0
- package/dist/src/frontend/path.html +6 -2
- package/dist/src/frontend/simulationWorker.js +1 -1
- package/dist/src/frontend/source_manager.js +110 -4
- package/dist/src/frontend/storage_provider.js +661 -71
- package/dist/src/frontend/styles.css +107 -13
- package/dist/src/frontend/workspace_panes.js +111 -7
- package/dist/src/frontend.runtime.assets.contract.test.js +81 -0
- package/dist/src/graph.answer.operator.contract.test.js +72 -0
- package/dist/src/index.js +6 -2
- package/dist/src/index.knowledge-root.test.js +118 -0
- package/dist/src/indexing/IndexLifecycle.js +22 -0
- package/dist/src/indexing/IndexLifecycle.test.js +24 -0
- package/dist/src/knowledge.api.contract.test.js +4 -2
- package/dist/src/knowledge_projection.contract.test.js +134 -0
- package/dist/src/knowledge_projection_store.test.js +200 -0
- package/dist/src/learning/AnswerQualityEvaluation.js +153 -0
- package/dist/src/learning/AnswerQualityEvaluation.test.js +118 -0
- package/dist/src/learning/KnowledgeLearningPlatform.js +4687 -3892
- package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +418 -0
- package/dist/src/learning/KnowledgeLearningPlatform.test.js +243 -6
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +25 -1
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +13 -5
- package/dist/src/learning/MeasurementEvidenceConflicts.test.js +78 -0
- package/dist/src/learning/PublicAnswerContract.test.js +85 -0
- package/dist/src/learning/PublicEvidenceSelection.test.js +94 -0
- package/dist/src/learning/PublicMeasurementDisclosure.test.js +54 -0
- package/dist/src/learning/agentConversationExecution.js +211 -0
- package/dist/src/learning/agentConversationExecution.test.js +209 -0
- package/dist/src/learning/agentConversationSerialization.js +266 -0
- package/dist/src/learning/agentConversationSerialization.test.js +165 -0
- package/dist/src/learning/agentResponseBudget.js +179 -0
- package/dist/src/learning/agentResponseBudget.test.js +97 -0
- package/dist/src/learning/agentWorkspaceCapabilityContracts.js +65 -0
- package/dist/src/learning/answerReleaseReview.js +1191 -250
- package/dist/src/learning/answerReleaseReview.test.js +1293 -7
- package/dist/src/learning/answerTaskPlan.js +473 -0
- package/dist/src/learning/conversationComposer.js +627 -386
- package/dist/src/learning/conversationComposer.test.js +1403 -0
- package/dist/src/learning/evidenceContextAssembler.js +312 -120
- package/dist/src/learning/graphAnswerCoverage.js +37 -0
- package/dist/src/learning/graphAnswerCoverage.test.js +93 -0
- package/dist/src/learning/graphAnswerCoverageCalibration.js +59 -0
- package/dist/src/learning/graphAnswerCoverageCalibration.test.js +46 -0
- package/dist/src/learning/graphAnswerFacts.js +94 -0
- package/dist/src/learning/graphAnswerFacts.test.js +39 -0
- package/dist/src/learning/graphAnswerPlan.js +908 -0
- package/dist/src/learning/graphAnswerPlan.test.js +907 -0
- package/dist/src/learning/graphAnswerQualityCalibration.js +255 -0
- package/dist/src/learning/graphAnswerQualityCalibration.test.js +72 -0
- package/dist/src/learning/graphAnswerQualityPolicy.js +30 -0
- package/dist/src/learning/graphClaimMatcher.js +104 -0
- package/dist/src/learning/graphConditionedContext.js +109 -0
- package/dist/src/learning/graphConditionedContext.test.js +99 -0
- package/dist/src/learning/graphContextAssembler.js +3 -13
- package/dist/src/learning/graphContextAssembler.test.js +2 -2
- package/dist/src/learning/graphExpansionPolicy.js +22 -0
- package/dist/src/learning/graphExpansionPolicy.test.js +33 -0
- package/dist/src/learning/identityMetadata.test.js +26 -0
- package/dist/src/learning/index.js +1 -0
- package/dist/src/learning/mobileAnswerProjection.js +104 -0
- package/dist/src/learning/mobileAnswerProjection.test.js +102 -0
- package/dist/src/learning/queryBackend.js +34 -10
- package/dist/src/learning/queryBackend.test.js +21 -0
- package/dist/src/learning/ragContextPack.js +115 -16
- package/dist/src/learning/ragContextPack.test.js +151 -0
- package/dist/src/learning/ragEvidenceQuality.js +96 -0
- package/dist/src/learning/ragEvidenceQuality.test.js +29 -0
- package/dist/src/learning/ragPublicText.js +67 -4
- package/dist/src/learning/ragPublicText.test.js +57 -0
- package/dist/src/learning/ragSufficiencyJudge.js +11 -1
- package/dist/src/learning/ragSufficiencyProviderJudge.js +16 -26
- package/dist/src/learning/ragSufficiencyProviderJudge.test.js +51 -0
- package/dist/src/learning/requestNormalization.js +106 -0
- package/dist/src/learning/requestNormalization.test.js +101 -0
- package/dist/src/learning/store.js +116 -12
- package/dist/src/learning/store.test.js +299 -0
- package/dist/src/learning/vectorAccelerationAdapter.js +22 -6
- package/dist/src/learning/vectorAccelerationAdapter.test.js +30 -0
- package/dist/src/learning/workspaceHydration.js +168 -0
- package/dist/src/learning/workspaceHydration.test.js +30 -0
- package/dist/src/license.policy.contract.test.js +9 -4
- package/dist/src/markdown/MarkdownGateway.js +4 -0
- package/dist/src/markdown/MarkdownGateway.test.js +59 -0
- package/dist/src/middleware/SseResponseWriter.js +122 -0
- package/dist/src/middleware/SseResponseWriter.test.js +54 -0
- package/dist/src/middleware/auth.js +28 -7
- package/dist/src/middleware/auth.test.js +33 -0
- package/dist/src/middleware/index.js +2 -1
- package/dist/src/mobile.android.evidence.contract.test.js +146 -0
- package/dist/src/mobile.android.signing.contract.test.js +66 -0
- package/dist/src/mobile.artifact.contract.test.js +144 -0
- package/dist/src/mobile.budget.contract.test.js +90 -0
- package/dist/src/mobile.canonical-id.readiness.test.js +54 -0
- package/dist/src/mobile.graph.parity.contract.test.js +207 -0
- package/dist/src/mobile.identity.contract.test.js +66 -0
- package/dist/src/mobile.identity.corpus.manifest.test.js +72 -0
- package/dist/src/mobile.identity.corpus.test.js +40 -0
- package/dist/src/mobile.native.recovery.contract.test.js +64 -0
- package/dist/src/mobile.pipeline.test.js +11 -1
- package/dist/src/mobile.profile.contract.test.js +225 -0
- package/dist/src/mobile.semantic.parity.test.js +92 -0
- package/dist/src/mobile_exact_analyzer.test.js +113 -0
- package/dist/src/notemd/ContentGenerator.js +4 -9
- package/dist/src/notemd/FileProcessor.js +4 -9
- package/dist/src/notemd/NotemdService.js +12 -17
- package/dist/src/notemd/Translator.js +4 -9
- package/dist/src/notemd/progressReporter.js +9 -0
- package/dist/src/notemd.server.integration.test.js +119 -4
- package/dist/src/notemd.workflow.pipeline.test.js +1 -1
- package/dist/src/pathbridge.handshake.contract.test.js +29 -0
- package/dist/src/pathbridge.host-adapter.contract.test.js +133 -0
- package/dist/src/pathcore.bundle.contract.test.js +8 -0
- package/dist/src/platform/ExportProfile.js +28 -0
- package/dist/src/platform/MobileBudget.js +11 -0
- package/dist/src/platform/PlatformCapabilities.js +14 -0
- package/dist/src/platform/PlatformCapabilities.test.js +6 -0
- package/dist/src/query_backend.external_http.integration.test.js +2 -2
- package/dist/src/release.godot.mirror.contract.test.js +28 -4
- package/dist/src/resources/ResourceRegistry.js +40 -0
- package/dist/src/resources/ResourceRegistry.test.js +36 -0
- package/dist/src/routes/index.js +0 -4
- package/dist/src/routes/knowledge.js +46 -33
- package/dist/src/routes/knowledgePayload.js +185 -0
- package/dist/src/routes/knowledgePayload.test.js +53 -0
- package/dist/src/routes/markdown.js +8 -0
- package/dist/src/routes/notemd.js +990 -234
- package/dist/src/routes/registry.contract.test.js +5 -2
- package/dist/src/routes/registry.shadow.contract.test.js +22 -0
- package/dist/src/routes/render.js +14 -51
- package/dist/src/runtime.capabilities.test.js +18 -2
- package/dist/src/server.js +566 -1090
- package/dist/src/server.migration.test.js +417 -15
- package/dist/src/sidecar.build.lock.contract.test.js +201 -0
- package/dist/src/sidecar.freshness.contract.test.js +184 -0
- package/dist/src/sidecar.replacement.boundary.contract.test.js +6 -3
- package/dist/src/sidecar.supply.readiness.contract.test.js +11 -0
- package/dist/src/simulation.worker.offline.test.js +104 -0
- package/dist/src/storage.provider.capacitor.content.contract.test.js +4 -1
- package/dist/src/storage.provider.contract.test.js +152 -1
- package/dist/src/tauri.frontend.build.contract.test.js +8 -0
- package/dist/src/tauri.window.evidence.execution.test.js +68 -0
- package/dist/src/utils/RuntimePaths.js +6 -0
- package/dist/src/utils/RuntimePaths.test.js +27 -0
- package/dist/src/windows.installer.build.test.js +109 -0
- package/dist/src/windows.installer.payload.test.js +76 -0
- package/dist/src/windows.installer.runtime.test.js +69 -0
- package/dist/src/workspace/WorkspaceRegistry.js +13 -0
- package/package.json +32 -10
- package/dist/src/routes/data.js +0 -267
- package/dist/src/routes/diagnostics.js +0 -51
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("fs"));
|
|
37
|
+
const os = __importStar(require("os"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const child_process_1 = require("child_process");
|
|
40
|
+
const { sha256File } = require('../scripts/sidecar-build-fingerprint');
|
|
41
|
+
const describeLinux = process.platform === 'linux' ? describe : describe.skip;
|
|
42
|
+
describeLinux('AppImage pkg sidecar patchelf boundary', () => {
|
|
43
|
+
let directory;
|
|
44
|
+
let server;
|
|
45
|
+
let transcript;
|
|
46
|
+
let buildEnvironment;
|
|
47
|
+
const adapter = path.resolve(__dirname, '../scripts/appimage-patchelf.js');
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
directory = fs.mkdtempSync(path.join(os.tmpdir(), 'appimage patchelf '));
|
|
50
|
+
server = path.join(directory, 'NoteConnection.AppDir/usr/bin/server');
|
|
51
|
+
transcript = path.join(directory, 'patchelf-calls.jsonl');
|
|
52
|
+
fs.mkdirSync(path.dirname(server), { recursive: true });
|
|
53
|
+
fs.writeFileSync(server, Buffer.from('\x7fELF embedded offsets\0appended pkg payload'));
|
|
54
|
+
const originalPatchelf = path.join(directory, 'actual-patchelf');
|
|
55
|
+
fs.writeFileSync(originalPatchelf, `#!${process.execPath}\n
|
|
56
|
+
const fs = require('fs');
|
|
57
|
+
const args = process.argv.slice(2);
|
|
58
|
+
fs.appendFileSync(process.env.APPIMAGE_PATCHELF_TEST_LOG, JSON.stringify(args) + '\\n');
|
|
59
|
+
if (args[0] === '--set-rpath') fs.appendFileSync(args[2], ' ELF rewrite');
|
|
60
|
+
process.stdout.write('original stdout\\n');
|
|
61
|
+
process.stderr.write('original stderr\\n');
|
|
62
|
+
process.exit(args[0] === '--fail' ? 23 : 0);
|
|
63
|
+
`, { mode: 0o755 });
|
|
64
|
+
buildEnvironment = {
|
|
65
|
+
...process.env,
|
|
66
|
+
NOTE_CONNECTION_APPIMAGE_PATCHELF: originalPatchelf,
|
|
67
|
+
NOTE_CONNECTION_APPIMAGE_SERVER_SUFFIX: 'NoteConnection.AppDir/usr/bin/server',
|
|
68
|
+
NOTE_CONNECTION_APPIMAGE_SERVER_SHA256: sha256File(server),
|
|
69
|
+
APPIMAGE_PATCHELF_TEST_LOG: transcript,
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
afterEach(() => fs.rmSync(directory, { recursive: true, force: true }));
|
|
73
|
+
test('preserves the complete pkg sidecar through repeated RPATH writes', () => {
|
|
74
|
+
for (let scan = 0; scan < 2; scan += 1) {
|
|
75
|
+
const command = (0, child_process_1.spawnSync)(adapter, ['--set-rpath', '$ORIGIN/../lib', server], { env: buildEnvironment, encoding: 'utf8' });
|
|
76
|
+
expect(command.status).toBe(0);
|
|
77
|
+
expect(command.stderr).toBe('');
|
|
78
|
+
}
|
|
79
|
+
expect(sha256File(server)).toBe(buildEnvironment.NOTE_CONNECTION_APPIMAGE_SERVER_SHA256);
|
|
80
|
+
expect(fs.existsSync(transcript)).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
test('forwards read queries and preserves stdout, stderr, and argument boundaries', () => {
|
|
83
|
+
const args = ['--print-rpath', server];
|
|
84
|
+
const command = (0, child_process_1.spawnSync)(adapter, args, { env: buildEnvironment, encoding: 'utf8' });
|
|
85
|
+
expect(command.status).toBe(0);
|
|
86
|
+
expect(command.stdout).toBe('original stdout\n');
|
|
87
|
+
expect(command.stderr).toBe('original stderr\n');
|
|
88
|
+
expect(JSON.parse(fs.readFileSync(transcript, 'utf8'))).toEqual(args);
|
|
89
|
+
expect(sha256File(server)).toBe(buildEnvironment.NOTE_CONNECTION_APPIMAGE_SERVER_SHA256);
|
|
90
|
+
});
|
|
91
|
+
test.each(['other.AppDir/usr/bin/server', 'usr/bin/server', 'NoteConnection.AppDir/usr/bin/npm', 'NoteConnection.AppDir/usr/lib/libexample.so'])('forwards RPATH writes for %s', (relative) => {
|
|
92
|
+
const executable = path.join(directory, relative);
|
|
93
|
+
fs.mkdirSync(path.dirname(executable), { recursive: true });
|
|
94
|
+
fs.writeFileSync(executable, 'other ELF');
|
|
95
|
+
const args = ['--set-rpath', '$ORIGIN/path with spaces', executable];
|
|
96
|
+
const command = (0, child_process_1.spawnSync)(adapter, args, { env: buildEnvironment, encoding: 'utf8' });
|
|
97
|
+
expect(command.status).toBe(0);
|
|
98
|
+
expect(JSON.parse(fs.readFileSync(transcript, 'utf8'))).toEqual(args);
|
|
99
|
+
expect(fs.readFileSync(executable, 'utf8')).toBe('other ELF ELF rewrite');
|
|
100
|
+
});
|
|
101
|
+
test('rejects an already changed packaged server without rewriting it again', () => {
|
|
102
|
+
fs.appendFileSync(server, 'changed');
|
|
103
|
+
const changedSha256 = sha256File(server);
|
|
104
|
+
const command = (0, child_process_1.spawnSync)(adapter, ['--set-rpath', '$ORIGIN/../lib', server], { env: buildEnvironment, encoding: 'utf8' });
|
|
105
|
+
expect(command.status).toBe(1);
|
|
106
|
+
expect(command.stderr).toMatch(/differs from the original pkg sidecar/);
|
|
107
|
+
expect(sha256File(server)).toBe(changedSha256);
|
|
108
|
+
expect(fs.existsSync(transcript)).toBe(false);
|
|
109
|
+
});
|
|
110
|
+
test('preserves a delegated failure exit status', () => {
|
|
111
|
+
expect((0, child_process_1.spawnSync)(adapter, ['--fail'], { env: buildEnvironment }).status).toBe(23);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("fs"));
|
|
37
|
+
const os = __importStar(require("os"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const { verifyAppDir, verifySquashfsPermissions } = require('../scripts/verify-appimage');
|
|
40
|
+
const { sha256File } = require('../scripts/sidecar-build-fingerprint');
|
|
41
|
+
describe('stored AppImage permissions', () => {
|
|
42
|
+
test('accepts portable files, directories, and symlinks regardless of stored owner', () => {
|
|
43
|
+
expect(() => verifySquashfsPermissions([
|
|
44
|
+
'drwxr-xr-x 0/0 64 2026-10-03 00:00 squashfs-root',
|
|
45
|
+
'-rwxr-xr-x 0/0 64 2026-10-03 00:00 squashfs-root/AppRun.wrapped',
|
|
46
|
+
'-rw-r--r-- 1001/1001 64 2026-10-03 00:00 squashfs-root/NoteConnection.png',
|
|
47
|
+
'lrwxrwxrwx 0/0 18 2026-10-03 00:00 squashfs-root/.DirIcon -> NoteConnection.png',
|
|
48
|
+
].join('\n'))).not.toThrow();
|
|
49
|
+
});
|
|
50
|
+
test.each(['-rwxrwx---', '-rwxr--r--', '-rw-rw----', 'drwx------', 'drwxr--r--'])('rejects stored mode %s even when the build owner could use it', (mode) => {
|
|
51
|
+
expect(() => verifySquashfsPermissions(`${mode} 0/0 64 2026-10-03 00:00 squashfs-root/entry`))
|
|
52
|
+
.toThrow(/inaccessible to other users/);
|
|
53
|
+
});
|
|
54
|
+
test('rejects missing manifest output', () => {
|
|
55
|
+
expect(() => verifySquashfsPermissions('')).toThrow(/contains no file modes/);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
const describeLinux = process.platform === 'linux' ? describe : describe.skip;
|
|
59
|
+
describeLinux('AppImage desktop integration portability', () => {
|
|
60
|
+
let directory;
|
|
61
|
+
let appDir;
|
|
62
|
+
let sourceServer;
|
|
63
|
+
const desktop = '[Desktop Entry]\nName=NoteConnection\nType=Application\nExec=npm %U\nIcon=npm\nTerminal=false\n';
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
directory = fs.mkdtempSync(path.join(os.tmpdir(), 'appimage portability '));
|
|
66
|
+
appDir = path.join(directory, 'NoteConnection.AppDir');
|
|
67
|
+
fs.mkdirSync(path.join(appDir, 'usr/bin'), { recursive: true });
|
|
68
|
+
fs.mkdirSync(path.join(appDir, 'usr/share/applications'), { recursive: true });
|
|
69
|
+
fs.mkdirSync(path.join(appDir, 'usr/share/icons/hicolor/128x128/apps'), { recursive: true });
|
|
70
|
+
fs.writeFileSync(path.join(appDir, 'AppRun'), '#!/bin/sh\nexec "$APPDIR/AppRun.wrapped" "$@"\n', { mode: 0o755 });
|
|
71
|
+
fs.writeFileSync(path.join(appDir, 'AppRun.wrapped'), '#!/bin/sh\nexit 0\n', { mode: 0o755 });
|
|
72
|
+
fs.writeFileSync(path.join(appDir, 'usr/bin/npm'), '#!/bin/sh\nexit 0\n', { mode: 0o755 });
|
|
73
|
+
sourceServer = path.join(directory, 'original-server');
|
|
74
|
+
fs.writeFileSync(sourceServer, Buffer.from('\x7fELF embedded offsets\0pkg payload'), { mode: 0o755 });
|
|
75
|
+
fs.copyFileSync(sourceServer, path.join(appDir, 'usr/bin/server'));
|
|
76
|
+
fs.writeFileSync(path.join(appDir, 'usr/share/applications/NoteConnection.desktop'), desktop);
|
|
77
|
+
fs.writeFileSync(path.join(appDir, 'NoteConnection.png'), 'icon fixture');
|
|
78
|
+
fs.symlinkSync('usr/share/applications/NoteConnection.desktop', path.join(appDir, 'NoteConnection.desktop'));
|
|
79
|
+
fs.symlinkSync('NoteConnection.png', path.join(appDir, '.DirIcon'));
|
|
80
|
+
fs.symlinkSync('../../../../../../NoteConnection.png', path.join(appDir, 'usr/share/icons/hicolor/128x128/apps/npm.png'));
|
|
81
|
+
});
|
|
82
|
+
afterEach(() => fs.rmSync(directory, { recursive: true, force: true }));
|
|
83
|
+
test('accepts relative integration links after moving the complete image', () => {
|
|
84
|
+
const relocated = path.join(directory, 'relocated image');
|
|
85
|
+
fs.renameSync(appDir, relocated);
|
|
86
|
+
expect(verifyAppDir(relocated, sourceServer)).toEqual({
|
|
87
|
+
desktopPath: path.join(relocated, 'usr/share/applications/NoteConnection.desktop'),
|
|
88
|
+
iconPath: path.join(relocated, 'NoteConnection.png'),
|
|
89
|
+
executablePath: path.join(relocated, 'usr/bin/npm'),
|
|
90
|
+
serverSha256: sha256File(sourceServer),
|
|
91
|
+
name: 'NoteConnection',
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
test('rejects the original absolute .DirIcon link even while its build-host target exists', () => {
|
|
95
|
+
fs.unlinkSync(path.join(appDir, '.DirIcon'));
|
|
96
|
+
fs.symlinkSync(path.join(appDir, 'NoteConnection.png'), path.join(appDir, '.DirIcon'));
|
|
97
|
+
expect(fs.readFileSync(path.join(appDir, '.DirIcon'), 'utf8')).toBe('icon fixture');
|
|
98
|
+
expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/\.DirIcon: absolute symlink/);
|
|
99
|
+
});
|
|
100
|
+
test.each(['.DirIcon', 'NoteConnection.desktop', 'AppRun', 'AppRun.wrapped', 'usr/bin/npm', 'usr/bin/server'])('rejects dangling integration entry %s', (entry) => {
|
|
101
|
+
fs.unlinkSync(path.join(appDir, entry));
|
|
102
|
+
fs.symlinkSync('missing-file', path.join(appDir, entry));
|
|
103
|
+
expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/ENOENT/);
|
|
104
|
+
});
|
|
105
|
+
test('rejects a relative link outside the image even when the target exists', () => {
|
|
106
|
+
fs.writeFileSync(path.join(directory, 'outside.png'), 'external icon');
|
|
107
|
+
fs.unlinkSync(path.join(appDir, '.DirIcon'));
|
|
108
|
+
fs.symlinkSync('../outside.png', path.join(appDir, '.DirIcon'));
|
|
109
|
+
expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/path leaves the AppImage/);
|
|
110
|
+
});
|
|
111
|
+
test('rejects an absolute link hidden in a relative icon chain', () => {
|
|
112
|
+
fs.unlinkSync(path.join(appDir, 'NoteConnection.png'));
|
|
113
|
+
fs.writeFileSync(path.join(directory, 'outside.png'), 'external icon');
|
|
114
|
+
fs.symlinkSync(path.join(directory, 'outside.png'), path.join(appDir, 'NoteConnection.png'));
|
|
115
|
+
expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/absolute symlink/);
|
|
116
|
+
});
|
|
117
|
+
test('rejects a symlinked ancestor directory leading outside the image', () => {
|
|
118
|
+
fs.renameSync(path.join(appDir, 'usr'), path.join(directory, 'usr'));
|
|
119
|
+
fs.symlinkSync('../usr', path.join(appDir, 'usr'));
|
|
120
|
+
expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/path leaves the AppImage/);
|
|
121
|
+
});
|
|
122
|
+
test('rejects a symlink cycle', () => {
|
|
123
|
+
fs.unlinkSync(path.join(appDir, '.DirIcon'));
|
|
124
|
+
fs.symlinkSync('.DirIcon', path.join(appDir, '.DirIcon'));
|
|
125
|
+
expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/symlink loop/);
|
|
126
|
+
});
|
|
127
|
+
test.each(['AppRun', 'AppRun.wrapped', 'usr/bin/npm'])('rejects %s when only the build owner and group can execute it', (entry) => {
|
|
128
|
+
fs.chmodSync(path.join(appDir, entry), 0o770);
|
|
129
|
+
expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/readable by other users|executable by other users/);
|
|
130
|
+
});
|
|
131
|
+
test('rejects an unreadable icon', () => {
|
|
132
|
+
fs.chmodSync(path.join(appDir, 'NoteConnection.png'), 0o600);
|
|
133
|
+
expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/readable by other users/);
|
|
134
|
+
});
|
|
135
|
+
test('rejects a sidecar whose appended payload moved during packaging', () => {
|
|
136
|
+
const bytes = fs.readFileSync(sourceServer);
|
|
137
|
+
fs.writeFileSync(path.join(appDir, 'usr/bin/server'), Buffer.concat([bytes.subarray(0, 21), Buffer.alloc(4096), bytes.subarray(21)]));
|
|
138
|
+
expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/Packaged server differs/);
|
|
139
|
+
});
|
|
140
|
+
test('rejects same-size sidecar corruption', () => {
|
|
141
|
+
const bytes = fs.readFileSync(sourceServer);
|
|
142
|
+
bytes[bytes.length - 1] ^= 1;
|
|
143
|
+
fs.writeFileSync(path.join(appDir, 'usr/bin/server'), bytes);
|
|
144
|
+
expect(() => verifyAppDir(appDir, sourceServer)).toThrow(/Packaged server differs/);
|
|
145
|
+
});
|
|
146
|
+
test.each([
|
|
147
|
+
['Name=NoteConnection\n', '', /missing Name/],
|
|
148
|
+
['Type=Application', 'Type=Link', /Type must be Application/],
|
|
149
|
+
['Exec=npm %U', 'Exec=missing', /ENOENT/],
|
|
150
|
+
['Exec=npm %U', 'Exec=/usr/bin/npm', /must name a bundled executable/],
|
|
151
|
+
['Icon=npm', 'Icon=missing', /no bundled theme image/],
|
|
152
|
+
])('rejects invalid desktop metadata (%s)', (before, after, message) => {
|
|
153
|
+
fs.writeFileSync(path.join(appDir, 'usr/share/applications/NoteConnection.desktop'), desktop.replace(before, after));
|
|
154
|
+
expect(() => verifyAppDir(appDir, sourceServer)).toThrow(message);
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("fs"));
|
|
37
|
+
const os = __importStar(require("os"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const { APPIMAGE_UPDATE_INFORMATION, verifyAppImageUpdate } = require('../scripts/appimage-update');
|
|
40
|
+
const describeLinux = process.platform === 'linux' ? describe : describe.skip;
|
|
41
|
+
describeLinux('AppImage update artifact boundary', () => {
|
|
42
|
+
let directory;
|
|
43
|
+
let artifact;
|
|
44
|
+
const filename = 'NoteConnection_fixture_amd64.AppImage';
|
|
45
|
+
const updateInformation = 'gh-releases-zsync|Jacobinwwey|NoteConnection|latest|NoteConnection_*_amd64.AppImage.zsync';
|
|
46
|
+
const runtime = `#!/bin/sh\nprintf '%s\\n' '${updateInformation}'\n`;
|
|
47
|
+
// Generated by bundled official zsyncmake 0.6.2: zsyncmake -u "$filename" "$filename".
|
|
48
|
+
// The shell fixture exposes the runtime metadata boundary; its table is not synthesized by this test.
|
|
49
|
+
const control = Buffer.from('enN5bmM6IDAuNi4yCkZpbGVuYW1lOiBOb3RlQ29ubmVjdGlvbl9maXh0dXJlX2FtZDY0LkFwcEltYWdlCk1UaW1lOiBNb24sIDA1IE9jdCAyMDI2IDA0OjM4OjAwICswMDAwCkJsb2Nrc2l6ZTogMjA0OApMZW5ndGg6IDExNgpIYXNoLUxlbmd0aHM6IDEsMiw0ClVSTDogTm90ZUNvbm5lY3Rpb25fZml4dHVyZV9hbWQ2NC5BcHBJbWFnZQpTSEEtMTogYjY3NDMyZTc0ZDM1ZjI5MmNmOTA1ZWMzZDk2MGI5MDg5YjFkYjQwMQoK3tOgETED', 'base64');
|
|
50
|
+
beforeEach(() => {
|
|
51
|
+
directory = fs.mkdtempSync(path.join(os.tmpdir(), 'appimage update '));
|
|
52
|
+
artifact = path.join(directory, filename);
|
|
53
|
+
fs.writeFileSync(artifact, runtime, { mode: 0o755 });
|
|
54
|
+
fs.writeFileSync(`${artifact}.zsync`, control);
|
|
55
|
+
});
|
|
56
|
+
afterEach(() => fs.rmSync(directory, { recursive: true, force: true }));
|
|
57
|
+
test('accepts an official control file for the exact final bytes and Latest amd64 target', () => {
|
|
58
|
+
expect(APPIMAGE_UPDATE_INFORMATION).toBe(updateInformation);
|
|
59
|
+
expect(verifyAppImageUpdate(artifact)).toEqual({
|
|
60
|
+
updateInformation,
|
|
61
|
+
zsyncPath: `${artifact}.zsync`,
|
|
62
|
+
sha1: 'b67432e74d35f292cf905ec3d960b9089b1db401',
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
test('requires the companion file at the adjacent release path', () => {
|
|
66
|
+
fs.renameSync(`${artifact}.zsync`, path.join(directory, 'elsewhere.zsync'));
|
|
67
|
+
expect(() => verifyAppImageUpdate(artifact)).toThrow(/ENOENT/);
|
|
68
|
+
});
|
|
69
|
+
test.each(['', updateInformation.replace('Jacobinwwey', 'other-owner'),
|
|
70
|
+
updateInformation.replace('|NoteConnection|', '|other-repository|'),
|
|
71
|
+
updateInformation.replace('|latest|', '|v1.9.0|'),
|
|
72
|
+
updateInformation.replace('_amd64.', '_aarch64.'),
|
|
73
|
+
updateInformation.replace('_amd64.', '_*.'),
|
|
74
|
+
])('rejects missing or wrong embedded update target: %s', (metadata) => {
|
|
75
|
+
fs.writeFileSync(artifact, `#!/bin/sh\nprintf '%s\\n' '${metadata}'\n`);
|
|
76
|
+
expect(() => verifyAppImageUpdate(artifact)).toThrow(/update information must be/);
|
|
77
|
+
});
|
|
78
|
+
test.each(['Other_fixture_amd64.AppImage', 'NoteConnection_fixture_aarch64.AppImage'])('rejects unsupported product or architecture: %s', (wrongName) => {
|
|
79
|
+
const moved = path.join(directory, wrongName);
|
|
80
|
+
fs.renameSync(artifact, moved);
|
|
81
|
+
fs.renameSync(`${artifact}.zsync`, `${moved}.zsync`);
|
|
82
|
+
expect(() => verifyAppImageUpdate(moved)).toThrow(/supported product amd64 filename/);
|
|
83
|
+
});
|
|
84
|
+
test('rejects a stale control file after AppImage bytes are appended', () => {
|
|
85
|
+
fs.appendFileSync(artifact, '# rebuilt\n');
|
|
86
|
+
expect(() => verifyAppImageUpdate(artifact)).toThrow(/Length does not match/);
|
|
87
|
+
});
|
|
88
|
+
test('rejects same-size AppImage corruption even when metadata still prints correctly', () => {
|
|
89
|
+
fs.writeFileSync(artifact, runtime.replace('printf ', 'printf\t'));
|
|
90
|
+
expect(() => verifyAppImageUpdate(artifact)).toThrow(/SHA-1 does not match/);
|
|
91
|
+
});
|
|
92
|
+
test.each([
|
|
93
|
+
['Filename: NoteConnection_fixture_amd64.AppImage', 'Filename: old.AppImage', /Filename and relative URL/],
|
|
94
|
+
['URL: NoteConnection_fixture_amd64.AppImage', 'URL: https://example.com/image.AppImage', /Filename and relative URL/],
|
|
95
|
+
['Length: 116', 'Length: 117', /Length does not match/],
|
|
96
|
+
['Length: 116', 'Length: 1.16e2', /Length does not match/],
|
|
97
|
+
['Blocksize: 2048', 'Blocksize: 0', /Blocksize/],
|
|
98
|
+
['Blocksize: 2048', 'Blocksize: 2047', /Blocksize/],
|
|
99
|
+
['Blocksize: 2048', 'Blocksize: 9007199254740991', /Blocksize/],
|
|
100
|
+
['Hash-Lengths: 1,2,4', 'Hash-Lengths: 3,2,4', /Hash-Lengths/],
|
|
101
|
+
['Hash-Lengths: 1,2,4', 'Hash-Lengths: 1,5,4', /Hash-Lengths/],
|
|
102
|
+
['Hash-Lengths: 1,2,4', 'Hash-Lengths: 1,2,17', /Hash-Lengths/],
|
|
103
|
+
['Hash-Lengths: 1,2,4', 'Hash-Lengths: 1,2,3', /checksum table/],
|
|
104
|
+
['SHA-1: b67432e74d35f292cf905ec3d960b9089b1db401', 'SHA-1: 0000000000000000000000000000000000000000', /SHA-1 does not match/],
|
|
105
|
+
['zsync: 0.6.2', 'zsync: unknown', /format version/],
|
|
106
|
+
['MTime:', 'Min-Version:', /unsupported/],
|
|
107
|
+
['MTime:', 'Unknown:', /unsupported/],
|
|
108
|
+
['MTime:', 'Z-URL:', /unsupported/],
|
|
109
|
+
['MTime:', 'Filename:', /duplicate/],
|
|
110
|
+
['Filename: Note', 'Filename: \xceote', /malformed/],
|
|
111
|
+
])('rejects malformed or mismatched control field: %s', (before, after, message) => {
|
|
112
|
+
fs.writeFileSync(`${artifact}.zsync`, Buffer.from(control.toString('latin1').replace(before, after), 'latin1'));
|
|
113
|
+
expect(() => verifyAppImageUpdate(artifact)).toThrow(message);
|
|
114
|
+
});
|
|
115
|
+
test('rejects a truncated checksum table', () => {
|
|
116
|
+
fs.writeFileSync(`${artifact}.zsync`, control.subarray(0, -1));
|
|
117
|
+
expect(() => verifyAppImageUpdate(artifact)).toThrow(/checksum table/);
|
|
118
|
+
});
|
|
119
|
+
test('rejects trailing bytes after the checksum table', () => {
|
|
120
|
+
fs.appendFileSync(`${artifact}.zsync`, Buffer.from([0]));
|
|
121
|
+
expect(() => verifyAppImageUpdate(artifact)).toThrow(/checksum table/);
|
|
122
|
+
});
|
|
123
|
+
test('rejects a header without the checksum table separator', () => {
|
|
124
|
+
fs.writeFileSync(`${artifact}.zsync`, control.subarray(0, control.indexOf('\n\n')));
|
|
125
|
+
expect(() => verifyAppImageUpdate(artifact)).toThrow(/separator/);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.FileLoader = void 0;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
|
+
const ResourceIdentity_1 = require("./ResourceIdentity");
|
|
39
40
|
/**
|
|
40
41
|
* Service to load files from a directory.
|
|
41
42
|
* 从目录加载文件的服务。
|
|
@@ -47,13 +48,19 @@ class FileLoader {
|
|
|
47
48
|
* @param dirPath Directory path | 目录路径
|
|
48
49
|
* @param extensions File extensions to include (e.g., ['.md']) | 要包含的文件扩展名
|
|
49
50
|
*/
|
|
50
|
-
static async loadFiles(dirPath, extensions = ['.md']) {
|
|
51
|
+
static async loadFiles(dirPath, extensions = ['.md'], workspaceRoot = dirPath) {
|
|
51
52
|
const filePaths = [];
|
|
52
|
-
// Check if directory exists
|
|
53
53
|
if (!fs.existsSync(dirPath)) {
|
|
54
54
|
console.warn(`Directory not found: ${dirPath}`);
|
|
55
55
|
return [];
|
|
56
56
|
}
|
|
57
|
+
// Compare canonical directories before scanning: an alias must neither
|
|
58
|
+
// escape the workspace nor give the same source a second relative identity.
|
|
59
|
+
const identityRoot = fs.realpathSync.native(workspaceRoot);
|
|
60
|
+
const scanRoot = fs.realpathSync.native(dirPath);
|
|
61
|
+
if (scanRoot !== identityRoot) {
|
|
62
|
+
(0, ResourceIdentity_1.normalizeResourceRelativePath)(identityRoot, scanRoot);
|
|
63
|
+
}
|
|
57
64
|
// 1. Gather all file paths first (Sequential directory scan is safer for handles)
|
|
58
65
|
async function scanDir(currentPath) {
|
|
59
66
|
// Use withFileTypes to avoid extra stat calls
|
|
@@ -76,7 +83,7 @@ class FileLoader {
|
|
|
76
83
|
console.warn(`Failed to read directory: ${currentPath}`, e);
|
|
77
84
|
}
|
|
78
85
|
}
|
|
79
|
-
await scanDir(
|
|
86
|
+
await scanDir(scanRoot);
|
|
80
87
|
// 2. Read files with concurrency limit
|
|
81
88
|
const results = [];
|
|
82
89
|
const CONCURRENCY_LIMIT = 100; // Conservative limit
|
|
@@ -87,10 +94,17 @@ class FileLoader {
|
|
|
87
94
|
try {
|
|
88
95
|
const content = await fs.promises.readFile(fullPath, 'utf-8');
|
|
89
96
|
const filename = path.basename(fullPath, path.extname(fullPath));
|
|
97
|
+
const relativePath = (0, ResourceIdentity_1.normalizeResourceRelativePath)(identityRoot, fullPath);
|
|
98
|
+
const identity = (0, ResourceIdentity_1.createResourceIdentity)(relativePath, filename, content);
|
|
90
99
|
return {
|
|
91
100
|
filepath: fullPath,
|
|
92
101
|
filename: filename,
|
|
93
|
-
content: content
|
|
102
|
+
content: content,
|
|
103
|
+
relativePath,
|
|
104
|
+
sourceUri: identity.sourceUri,
|
|
105
|
+
canonicalId: identity.canonicalId,
|
|
106
|
+
revision: identity.revision,
|
|
107
|
+
identityAliases: identity.identityAliases,
|
|
94
108
|
};
|
|
95
109
|
}
|
|
96
110
|
catch (e) {
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("fs"));
|
|
37
|
+
const os = __importStar(require("os"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const FileLoader_1 = require("./FileLoader");
|
|
40
|
+
describe('FileLoader resource paths', () => {
|
|
41
|
+
test('records a normalized path relative to the loaded workspace', async () => {
|
|
42
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'noteconnection-file-loader-'));
|
|
43
|
+
const nested = path.join(root, 'algebra');
|
|
44
|
+
fs.mkdirSync(nested, { recursive: true });
|
|
45
|
+
fs.writeFileSync(path.join(nested, 'index.md'), '# Algebra', 'utf8');
|
|
46
|
+
try {
|
|
47
|
+
const files = await FileLoader_1.FileLoader.loadFiles(root);
|
|
48
|
+
expect(files).toHaveLength(1);
|
|
49
|
+
expect(files[0].relativePath).toBe('algebra/index.md');
|
|
50
|
+
expect(files[0].sourceUri).toBe('note://workspace/v1/algebra/index.md');
|
|
51
|
+
expect(files[0].canonicalId).toBe('algebra/index');
|
|
52
|
+
expect(files[0].revision).toMatch(/^sha256:[0-9a-f]{64}$/);
|
|
53
|
+
expect(files[0].identityAliases).toEqual(expect.arrayContaining([
|
|
54
|
+
'index',
|
|
55
|
+
'algebra/index.md',
|
|
56
|
+
]));
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
test('keeps identity stable when loading a workspace subdirectory', async () => {
|
|
63
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'noteconnection-file-loader-root-'));
|
|
64
|
+
const nested = path.join(root, 'algebra');
|
|
65
|
+
fs.mkdirSync(nested, { recursive: true });
|
|
66
|
+
fs.writeFileSync(path.join(nested, 'index.md'), '# Algebra', 'utf8');
|
|
67
|
+
try {
|
|
68
|
+
const files = await FileLoader_1.FileLoader.loadFiles(nested, ['.md'], root);
|
|
69
|
+
expect(files).toHaveLength(1);
|
|
70
|
+
expect(files[0].relativePath).toBe('algebra/index.md');
|
|
71
|
+
expect(files[0].sourceUri).toBe('note://workspace/v1/algebra/index.md');
|
|
72
|
+
expect(files[0].canonicalId).toBe('algebra/index');
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
test('rejects an outside scan directory instead of returning an empty successful load', async () => {
|
|
79
|
+
const fixtureRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'noteconnection-file-loader-boundary-'));
|
|
80
|
+
const workspaceRoot = path.join(fixtureRoot, 'workspace');
|
|
81
|
+
const outside = path.join(fixtureRoot, 'outside');
|
|
82
|
+
fs.mkdirSync(workspaceRoot);
|
|
83
|
+
fs.mkdirSync(outside);
|
|
84
|
+
fs.writeFileSync(path.join(outside, 'private.md'), '# Outside');
|
|
85
|
+
try {
|
|
86
|
+
await expect(FileLoader_1.FileLoader.loadFiles(outside, ['.md'], workspaceRoot)).rejects.toThrow(/outside workspace root/i);
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
fs.rmSync(fixtureRoot, { recursive: true, force: true });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
test('rejects a scan directory that escapes the workspace through a symlink', async () => {
|
|
93
|
+
const fixtureRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'noteconnection-file-loader-symlink-'));
|
|
94
|
+
const workspaceRoot = path.join(fixtureRoot, 'workspace');
|
|
95
|
+
const outside = path.join(fixtureRoot, 'outside');
|
|
96
|
+
fs.mkdirSync(workspaceRoot);
|
|
97
|
+
fs.mkdirSync(outside);
|
|
98
|
+
fs.writeFileSync(path.join(outside, 'private.md'), '# Outside');
|
|
99
|
+
const link = path.join(workspaceRoot, 'linked');
|
|
100
|
+
fs.symlinkSync(outside, link, process.platform === 'win32' ? 'junction' : 'dir');
|
|
101
|
+
try {
|
|
102
|
+
await expect(FileLoader_1.FileLoader.loadFiles(link, ['.md'], workspaceRoot)).rejects.toThrow(/outside workspace root/i);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
fs.rmSync(fixtureRoot, { recursive: true, force: true });
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
test('keeps canonical identities stable through an in-workspace directory alias', async () => {
|
|
109
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'noteconnection-file-loader-alias-'));
|
|
110
|
+
const nested = path.join(root, 'algebra');
|
|
111
|
+
fs.mkdirSync(nested);
|
|
112
|
+
fs.writeFileSync(path.join(nested, 'index.md'), '# Algebra');
|
|
113
|
+
const link = path.join(root, 'alias');
|
|
114
|
+
fs.symlinkSync(nested, link, process.platform === 'win32' ? 'junction' : 'dir');
|
|
115
|
+
try {
|
|
116
|
+
const files = await FileLoader_1.FileLoader.loadFiles(link, ['.md'], root);
|
|
117
|
+
expect(files).toHaveLength(1);
|
|
118
|
+
expect(files[0].relativePath).toBe('algebra/index.md');
|
|
119
|
+
expect(files[0].sourceUri).toBe('note://workspace/v1/algebra/index.md');
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
});
|