exarch-rs 0.1.1 → 0.1.2

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.
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "exarch-rs",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Memory-safe archive extraction library with built-in security validation",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
package/src/lib.rs CHANGED
@@ -208,9 +208,13 @@ pub fn extract_archive_sync(
208
208
  let default_config = exarch_core::SecurityConfig::default();
209
209
  let config_ref = config.map_or(&default_config, |c| c.as_core());
210
210
 
211
- // Run extraction synchronously
212
- let report = exarch_core::extract_archive(&archive_path, &output_dir, config_ref)
213
- .map_err(convert_error)?;
211
+ // Run extraction synchronously with panic safety
212
+ // CRITICAL: Never panic across FFI boundary
213
+ let report = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
214
+ exarch_core::extract_archive(&archive_path, &output_dir, config_ref)
215
+ }))
216
+ .map_err(|_| Error::from_reason("Internal panic during archive extraction"))?
217
+ .map_err(convert_error)?;
214
218
 
215
219
  Ok(ExtractionReport::from(report))
216
220
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Tests for archive creation functions
3
3
  */
4
- const { describe, it, beforeEach } = require('node:test');
4
+ const { describe, it, beforeEach, afterEach } = require('node:test');
5
5
  const assert = require('node:assert');
6
6
  const fs = require('node:fs');
7
7
  const path = require('node:path');
@@ -39,6 +39,12 @@ describe('createArchive (async)', () => {
39
39
  outputPath = path.join(tempDir, 'output.tar.gz');
40
40
  });
41
41
 
42
+ afterEach(() => {
43
+ if (tempDir && fs.existsSync(tempDir)) {
44
+ fs.rmSync(tempDir, { recursive: true, force: true });
45
+ }
46
+ });
47
+
42
48
  it('should create a tar.gz archive', async () => {
43
49
  const report = await createArchive(outputPath, [sourceDir]);
44
50
 
@@ -95,6 +101,12 @@ describe('createArchiveSync', () => {
95
101
  outputPath = path.join(tempDir, 'output.tar.gz');
96
102
  });
97
103
 
104
+ afterEach(() => {
105
+ if (tempDir && fs.existsSync(tempDir)) {
106
+ fs.rmSync(tempDir, { recursive: true, force: true });
107
+ }
108
+ });
109
+
98
110
  it('should create archive synchronously', () => {
99
111
  const report = createArchiveSync(outputPath, [sourceDir]);
100
112
 
@@ -1,10 +1,7 @@
1
1
  /**
2
2
  * Tests for archive extraction functions
3
- *
4
- * NOTE: Extraction tests are skipped until exarch-core extract_archive API is fully implemented.
5
- * The current implementation is a placeholder (see exarch-core/src/api.rs).
6
3
  */
7
- const { describe, it, beforeEach } = require('node:test');
4
+ const { describe, it, beforeEach, afterEach } = require('node:test');
8
5
  const assert = require('node:assert');
9
6
  const fs = require('node:fs');
10
7
  const path = require('node:path');
@@ -42,19 +39,29 @@ describe('extractArchive (async)', () => {
42
39
  fs.mkdirSync(outputDir);
43
40
  });
44
41
 
45
- // TODO: Enable when core extract_archive is implemented
46
- it.skip('should extract a valid archive', async () => {
42
+ afterEach(() => {
43
+ if (tempDir && fs.existsSync(tempDir)) {
44
+ fs.rmSync(tempDir, { recursive: true, force: true });
45
+ }
46
+ });
47
+
48
+ it('should extract a valid archive', async () => {
47
49
  createValidArchive(archivePath, tempDir);
48
50
 
49
51
  const report = await extractArchive(archivePath, outputDir);
50
52
 
51
- assert.ok(report.filesExtracted >= 1);
53
+ assert.strictEqual(report.filesExtracted, 1);
52
54
  assert.ok(report.bytesWritten >= 13);
53
55
  assert.ok(report.durationMs >= 0);
56
+
57
+ // Verify extracted file exists and has correct content
58
+ const extractedFile = path.join(outputDir, 'hello.txt');
59
+ assert.ok(fs.existsSync(extractedFile), 'Extracted file should exist');
60
+ const content = fs.readFileSync(extractedFile, 'utf8');
61
+ assert.strictEqual(content, 'Hello, World!');
54
62
  });
55
63
 
56
- // TODO: Enable when core extract_archive is implemented
57
- it.skip('should accept custom SecurityConfig', async () => {
64
+ it('should accept custom SecurityConfig', async () => {
58
65
  createValidArchive(archivePath, tempDir);
59
66
 
60
67
  const config = new SecurityConfig();
@@ -63,15 +70,6 @@ describe('extractArchive (async)', () => {
63
70
 
64
71
  assert.ok(report.filesExtracted >= 1);
65
72
  });
66
-
67
- it('should return empty report for valid archive (placeholder)', async () => {
68
- createValidArchive(archivePath, tempDir);
69
-
70
- const report = await extractArchive(archivePath, outputDir);
71
-
72
- // Core extract_archive is currently a placeholder
73
- assert.strictEqual(report.filesExtracted, 0);
74
- });
75
73
  });
76
74
 
77
75
  describe('extractArchiveSync', () => {
@@ -86,18 +84,28 @@ describe('extractArchiveSync', () => {
86
84
  fs.mkdirSync(outputDir);
87
85
  });
88
86
 
89
- // TODO: Enable when core extract_archive is implemented
90
- it.skip('should extract a valid archive synchronously', () => {
87
+ afterEach(() => {
88
+ if (tempDir && fs.existsSync(tempDir)) {
89
+ fs.rmSync(tempDir, { recursive: true, force: true });
90
+ }
91
+ });
92
+
93
+ it('should extract a valid archive synchronously', () => {
91
94
  createValidArchive(archivePath, tempDir);
92
95
 
93
96
  const report = extractArchiveSync(archivePath, outputDir);
94
97
 
95
- assert.ok(report.filesExtracted >= 1);
98
+ assert.strictEqual(report.filesExtracted, 1);
96
99
  assert.ok(report.bytesWritten >= 13);
100
+
101
+ // Verify extracted file exists and has correct content
102
+ const extractedFile = path.join(outputDir, 'hello.txt');
103
+ assert.ok(fs.existsSync(extractedFile), 'Extracted file should exist');
104
+ const content = fs.readFileSync(extractedFile, 'utf8');
105
+ assert.strictEqual(content, 'Hello, World!');
97
106
  });
98
107
 
99
- // TODO: Enable when core extract_archive is implemented
100
- it.skip('should accept custom SecurityConfig', () => {
108
+ it('should accept custom SecurityConfig', () => {
101
109
  createValidArchive(archivePath, tempDir);
102
110
 
103
111
  const config = new SecurityConfig();
@@ -106,13 +114,4 @@ describe('extractArchiveSync', () => {
106
114
 
107
115
  assert.ok(report.filesExtracted >= 1);
108
116
  });
109
-
110
- it('should return empty report for valid archive (placeholder)', () => {
111
- createValidArchive(archivePath, tempDir);
112
-
113
- const report = extractArchiveSync(archivePath, outputDir);
114
-
115
- // Core extract_archive is currently a placeholder
116
- assert.strictEqual(report.filesExtracted, 0);
117
- });
118
117
  });
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Tests for listArchive and verifyArchive functions
3
3
  */
4
- const { describe, it, beforeEach } = require('node:test');
4
+ const { describe, it, beforeEach, afterEach } = require('node:test');
5
5
  const assert = require('node:assert');
6
6
  const fs = require('node:fs');
7
7
  const path = require('node:path');
@@ -38,6 +38,12 @@ describe('listArchive (async)', () => {
38
38
  archivePath = path.join(tempDir, 'test.tar.gz');
39
39
  });
40
40
 
41
+ afterEach(() => {
42
+ if (tempDir && fs.existsSync(tempDir)) {
43
+ fs.rmSync(tempDir, { recursive: true, force: true });
44
+ }
45
+ });
46
+
41
47
  it('should list archive contents', async () => {
42
48
  createValidArchive(archivePath, tempDir);
43
49
 
@@ -78,6 +84,12 @@ describe('listArchiveSync', () => {
78
84
  archivePath = path.join(tempDir, 'test.tar.gz');
79
85
  });
80
86
 
87
+ afterEach(() => {
88
+ if (tempDir && fs.existsSync(tempDir)) {
89
+ fs.rmSync(tempDir, { recursive: true, force: true });
90
+ }
91
+ });
92
+
81
93
  it('should list archive contents synchronously', () => {
82
94
  createValidArchive(archivePath, tempDir);
83
95
 
@@ -98,6 +110,12 @@ describe('verifyArchive (async)', () => {
98
110
  archivePath = path.join(tempDir, 'test.tar.gz');
99
111
  });
100
112
 
113
+ afterEach(() => {
114
+ if (tempDir && fs.existsSync(tempDir)) {
115
+ fs.rmSync(tempDir, { recursive: true, force: true });
116
+ }
117
+ });
118
+
101
119
  it('should verify a valid archive', async () => {
102
120
  createValidArchive(archivePath, tempDir);
103
121
 
@@ -137,6 +155,12 @@ describe('verifyArchiveSync', () => {
137
155
  archivePath = path.join(tempDir, 'test.tar.gz');
138
156
  });
139
157
 
158
+ afterEach(() => {
159
+ if (tempDir && fs.existsSync(tempDir)) {
160
+ fs.rmSync(tempDir, { recursive: true, force: true });
161
+ }
162
+ });
163
+
140
164
  it('should verify archive synchronously', () => {
141
165
  createValidArchive(archivePath, tempDir);
142
166