Skip to content

Commit e8d9065

Browse files
joyeecheungtargos
authored andcommitted
sea: support sea.getRawAsset()
This patch adds support for `sea.getRawAsset()` which is similar to `sea.getAsset()` but returns the raw asset in an array buffer without copying. Users should avoid writing to the returned array buffer. If the injected section is not marked as writable or not aligned, writing to the raw asset is likely to result in a crash. PR-URL: #50960 Refs: nodejs/single-executable#68 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com>
1 parent cea5295 commit e8d9065

6 files changed

+131
-0
lines changed

doc/api/single-executable-applications.md

+23
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ const image = getAsset('a.jpg');
219219
const text = getAsset('b.txt', 'utf8');
220220
// Returns a Blob containing the asset.
221221
const blob = getAssetAsBlob('a.jpg');
222+
// Returns an ArrayBuffer containing the raw asset without copying.
223+
const raw = getRawAsset('a.jpg');
222224
```
223225
224226
See documentation of the [`sea.getAsset()`][] and [`sea.getAssetAsBlob()`][]
@@ -316,6 +318,27 @@ An error is thrown when no matching asset can be found.
316318
* `type` {string} An optional mime type for the blob.
317319
* Returns: {Blob}
318320
321+
### `sea.getRawAsset(key)`
322+
323+
<!-- YAML
324+
added: REPLACEME
325+
-->
326+
327+
This method can be used to retrieve the assets configured to be bundled into the
328+
single-executable application at build time.
329+
An error is thrown when no matching asset can be found.
330+
331+
Unlike `sea.getRawAsset()` or `sea.getAssetAsBlob()`, this method does not
332+
return a copy. Instead, it returns the raw asset bundled inside the executable.
333+
334+
For now, users should avoid writing to the returned array buffer. If the
335+
injected section is not marked as writable or not aligned properly,
336+
writes to the returned array buffer is likely to result in a crash.
337+
338+
* `key` {string} the key for the asset in the dictionary specified by the
339+
`assets` field in the single-executable application configuration.
340+
* Returns: {string|ArrayBuffer}
341+
319342
### `require(id)` in the injected main script is not file based
320343
321344
`require()` in the injected main script is not the same as the [`require()`][]

lib/sea.js

+1
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,6 @@ function getAssetAsBlob(key, options) {
7171
module.exports = {
7272
isSea,
7373
getAsset,
74+
getRawAsset,
7475
getAssetAsBlob,
7576
};

test/fixtures/sea/get-asset-raw.js

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict';
2+
3+
const { isSea, getAsset, getRawAsset } = require('node:sea');
4+
const { readFileSync } = require('fs');
5+
const assert = require('assert');
6+
7+
assert(isSea());
8+
9+
{
10+
assert.throws(() => getRawAsset('nonexistent'), {
11+
code: 'ERR_SINGLE_EXECUTABLE_APPLICATION_ASSET_NOT_FOUND'
12+
});
13+
assert.throws(() => getRawAsset(null), {
14+
code: 'ERR_INVALID_ARG_TYPE'
15+
});
16+
assert.throws(() => getRawAsset(1), {
17+
code: 'ERR_INVALID_ARG_TYPE'
18+
});
19+
}
20+
21+
{
22+
// Check that the asset embedded is the same as the original.
23+
const assetOnDisk = readFileSync(process.env.__TEST_PERSON_JPG);
24+
const assetCopy = getAsset('person.jpg')
25+
const assetCopyBuffer = Buffer.from(assetCopy);
26+
assert.deepStrictEqual(assetCopyBuffer, assetOnDisk);
27+
28+
// Check that the copied asset is the same as the raw one.
29+
const rawAsset = getRawAsset('person.jpg');
30+
assert.deepStrictEqual(rawAsset, assetCopy);
31+
}

test/fixtures/sea/get-asset.js

+2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ const binaryAssetOnDisk = readFileSync(process.env.__TEST_PERSON_JPG);
7777
{
7878
const actualAsset = getAsset('utf8_test_text.txt', 'utf8')
7979
assert.strictEqual(actualAsset, textAssetOnDisk);
80+
// Log it out so that the test could compare it and see if
81+
// it's encoded/decoded correctly in the SEA.
8082
console.log(actualAsset);
8183
}
8284

test/sequential/sequential.status

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ test-performance-eventloopdelay: PASS, FLAKY
5151

5252
[$system==linux && $arch==ppc64]
5353
# https://github.com/nodejs/node/issues/50740
54+
test-single-executable-application-assets-raw: PASS, FLAKY
5455
test-single-executable-application-assets: PASS, FLAKY
5556
test-single-executable-application-disable-experimental-sea-warning: PASS, FLAKY
5657
test-single-executable-application-empty: PASS, FLAKY
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
5+
const {
6+
injectAndCodeSign,
7+
skipIfSingleExecutableIsNotSupported,
8+
} = require('../common/sea');
9+
10+
skipIfSingleExecutableIsNotSupported();
11+
12+
// This tests the snapshot support in single executable applications.
13+
const tmpdir = require('../common/tmpdir');
14+
15+
const { copyFileSync, writeFileSync, existsSync } = require('fs');
16+
const {
17+
spawnSyncAndExitWithoutError,
18+
} = require('../common/child_process');
19+
const assert = require('assert');
20+
const fixtures = require('../common/fixtures');
21+
22+
tmpdir.refresh();
23+
if (!tmpdir.hasEnoughSpace(120 * 1024 * 1024)) {
24+
common.skip('Not enough disk space');
25+
}
26+
27+
const configFile = tmpdir.resolve('sea-config.json');
28+
const seaPrepBlob = tmpdir.resolve('sea-prep.blob');
29+
const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'sea');
30+
31+
{
32+
tmpdir.refresh();
33+
copyFileSync(fixtures.path('sea', 'get-asset-raw.js'), tmpdir.resolve('sea.js'));
34+
copyFileSync(fixtures.path('person.jpg'), tmpdir.resolve('person.jpg'));
35+
writeFileSync(configFile, `
36+
{
37+
"main": "sea.js",
38+
"output": "sea-prep.blob",
39+
"assets": {
40+
"person.jpg": "person.jpg"
41+
}
42+
}
43+
`, 'utf8');
44+
45+
spawnSyncAndExitWithoutError(
46+
process.execPath,
47+
['--experimental-sea-config', 'sea-config.json'],
48+
{
49+
env: {
50+
NODE_DEBUG_NATIVE: 'SEA',
51+
...process.env,
52+
},
53+
cwd: tmpdir.path
54+
},
55+
{});
56+
57+
assert(existsSync(seaPrepBlob));
58+
59+
copyFileSync(process.execPath, outputFile);
60+
injectAndCodeSign(outputFile, seaPrepBlob);
61+
62+
spawnSyncAndExitWithoutError(
63+
outputFile,
64+
{
65+
env: {
66+
...process.env,
67+
NODE_DEBUG_NATIVE: 'SEA',
68+
__TEST_PERSON_JPG: fixtures.path('person.jpg'),
69+
}
70+
},
71+
{ }
72+
);
73+
}

0 commit comments

Comments
 (0)