smolmachines 1.22.0 → 1.22.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/dist/assets.js CHANGED
@@ -20,6 +20,7 @@ exports.wireBundledAssets = wireBundledAssets;
20
20
  exports.wireDefaultHardening = wireDefaultHardening;
21
21
  const node_fs_1 = require("node:fs");
22
22
  const node_path_1 = require("node:path");
23
+ const runtime_require_1 = require("./runtime-require");
23
24
  /** The per-platform npm package that carries the native addon, boot helper,
24
25
  * hypervisor libraries and guest rootfs for each supported
25
26
  * `${process.platform}-${process.arch}`. npm installs only the one matching
@@ -34,12 +35,12 @@ exports.PLATFORM_PACKAGES = {
34
35
  /** `native/` inside the installed platform package, or undefined when it is not
35
36
  * installed (unsupported platform, `--omit=optional`, or a lockfile written on
36
37
  * another platform that dropped it). */
37
- function platformPackageNativeDir(platformArch) {
38
+ function platformPackageNativeDir(platformArch, dir) {
38
39
  const pkg = exports.PLATFORM_PACKAGES[platformArch];
39
40
  if (!pkg)
40
41
  return undefined;
41
42
  try {
42
- return (0, node_path_1.join)((0, node_path_1.dirname)(require.resolve(`${pkg}/package.json`)), 'native');
43
+ return (0, node_path_1.join)((0, node_path_1.dirname)((0, runtime_require_1.requireFrom)(dir).resolve(`${pkg}/package.json`)), 'native');
43
44
  }
44
45
  catch {
45
46
  return undefined;
@@ -59,15 +60,19 @@ function wireBundledAssets() {
59
60
  }
60
61
  const platformArch = `${process.platform}-${process.arch}`;
61
62
  const helperName = process.platform === 'win32' ? 'smol-vmm.exe' : 'smol-vmm';
62
- // A source checkout / CI build keeps the assets next to the package
63
- // (`__dirname` is the package root from source (tsx) and `dist/` when built —
63
+ // A source checkout / CI build keeps the assets next to the package (the SDK
64
+ // directory is the package root from source (tsx) and `dist/` when built —
64
65
  // check both layouts); a published install gets them from the per-platform
65
- // package.
66
- const candidates = [
67
- (0, node_path_1.join)(__dirname, 'native', platformArch),
68
- (0, node_path_1.join)(__dirname, '..', 'native', platformArch),
69
- platformPackageNativeDir(platformArch),
70
- ].filter((dir) => dir !== undefined);
66
+ // package. `sdkDir()` finds the package on disk even when the SDK's
67
+ // JavaScript was bundled, where `__dirname` is the bundler's output.
68
+ const dir = (0, runtime_require_1.sdkDir)();
69
+ const candidates = dir
70
+ ? [
71
+ (0, node_path_1.join)(dir, 'native', platformArch),
72
+ (0, node_path_1.join)(dir, '..', 'native', platformArch),
73
+ platformPackageNativeDir(platformArch, dir),
74
+ ].filter((d) => d !== undefined)
75
+ : [];
71
76
  for (const nativeDir of candidates) {
72
77
  if (!(0, node_fs_1.existsSync)(nativeDir))
73
78
  continue;
package/dist/binding.d.ts CHANGED
@@ -219,7 +219,7 @@ export declare class NapiMachine {
219
219
  */
220
220
  static connect(name: string, interceptorAddress?: string | undefined | null, interceptorToken?: string | undefined | null): NapiMachine
221
221
  /** Create a stopped machine from a portable live checkpoint on disk. */
222
- static restoreCheckpoint(name: string, artifact: string): NapiMachine
222
+ static restoreCheckpoint(name: string, artifact: string, keepIdentity?: boolean | undefined | null): NapiMachine
223
223
  /** Export a stored checkpoint directory as one portable checkpoint file. */
224
224
  static exportCheckpoint(source: string, output: string): number
225
225
  /** Remove objects that no retained checkpoint in a local store references. */
package/dist/native.d.ts CHANGED
@@ -144,7 +144,7 @@ export interface NapiMachineCtor {
144
144
  new (config: NativeMachineConfig): NapiMachine;
145
145
  connect(name: string, interceptorAddress?: string, interceptorToken?: string): NapiMachine;
146
146
  list(): Promise<NativeMachineSummary[]>;
147
- restoreCheckpoint(name: string, artifact: string): NapiMachine;
147
+ restoreCheckpoint(name: string, artifact: string, keepIdentity?: boolean): NapiMachine;
148
148
  exportCheckpoint(source: string, output: string): number;
149
149
  pruneCheckpointStore(store: string): number;
150
150
  checkHost(): NativeHostAvailability;
package/dist/native.js CHANGED
@@ -11,6 +11,7 @@
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.getNapiMachine = getNapiMachine;
13
13
  const assets_1 = require("./assets");
14
+ const runtime_require_1 = require("./runtime-require");
14
15
  /** Explain a failed addon load in terms of the per-platform package that
15
16
  * carries it. The usual causes are an unsupported platform, an install with
16
17
  * `--omit=optional`, or a lockfile written on another OS/arch that recorded no
@@ -45,8 +46,12 @@ function getNapiMachine() {
45
46
  const assets = (0, assets_1.wireBundledAssets)();
46
47
  let binding;
47
48
  try {
48
- // eslint-disable-next-line @typescript-eslint/no-var-requires
49
- binding = require("./binding.js");
49
+ // Loaded through Node's own loader so a bundler never follows it into
50
+ // the `.node` addon (see runtime-require.ts).
51
+ const dir = (0, runtime_require_1.sdkDir)();
52
+ if (!dir)
53
+ throw new Error("the smolmachines package was not found on disk");
54
+ binding = (0, runtime_require_1.requireFrom)(dir)("./binding.js");
50
55
  }
51
56
  catch (error) {
52
57
  throw missingPlatformPackageError(error);
@@ -0,0 +1,23 @@
1
+ /** Load the native half of the SDK through Node's own module loader.
2
+ *
3
+ * Bundlers (webpack, Turbopack, esbuild, Vite SSR) follow every `require(...)`
4
+ * and `require.resolve(...)` they can see. Following `./binding.js` leads them
5
+ * into the platform `.node` addon, which they then try to parse as JavaScript
6
+ * ("Module parse failed: Unexpected character"), so `import 'smolmachines'` in
7
+ * a Next.js route failed to build unless the app listed the package in
8
+ * `serverExternalPackages`. Bundling also moves `__dirname` to the bundler's
9
+ * output directory, so the bundled boot helper and libraries could no longer
10
+ * be found next to this file.
11
+ *
12
+ * Everything here reaches Node's `module` builtin without a call a bundler can
13
+ * trace, and locates the installed package on disk, so the native addon and
14
+ * its assets load from `node_modules` whether or not the SDK's JavaScript was
15
+ * bundled. The pure-JavaScript cloud path never touches this file.
16
+ */
17
+ /** A `require` that resolves relative to `dir`, invisible to bundlers. */
18
+ export declare function requireFrom(dir: string): NodeRequire;
19
+ /** The directory holding `binding.js` and the source-layout `native/` assets:
20
+ * this file's own directory when the SDK runs unbundled (the package root from
21
+ * source, `dist/` when built), otherwise the installed package's `dist/`,
22
+ * resolved from the app's working directory. Undefined when neither exists. */
23
+ export declare function sdkDir(): string | undefined;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ /** Load the native half of the SDK through Node's own module loader.
3
+ *
4
+ * Bundlers (webpack, Turbopack, esbuild, Vite SSR) follow every `require(...)`
5
+ * and `require.resolve(...)` they can see. Following `./binding.js` leads them
6
+ * into the platform `.node` addon, which they then try to parse as JavaScript
7
+ * ("Module parse failed: Unexpected character"), so `import 'smolmachines'` in
8
+ * a Next.js route failed to build unless the app listed the package in
9
+ * `serverExternalPackages`. Bundling also moves `__dirname` to the bundler's
10
+ * output directory, so the bundled boot helper and libraries could no longer
11
+ * be found next to this file.
12
+ *
13
+ * Everything here reaches Node's `module` builtin without a call a bundler can
14
+ * trace, and locates the installed package on disk, so the native addon and
15
+ * its assets load from `node_modules` whether or not the SDK's JavaScript was
16
+ * bundled. The pure-JavaScript cloud path never touches this file.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.requireFrom = requireFrom;
20
+ exports.sdkDir = sdkDir;
21
+ const node_fs_1 = require("node:fs");
22
+ const node_path_1 = require("node:path");
23
+ function moduleApi() {
24
+ const getBuiltinModule = process
25
+ .getBuiltinModule;
26
+ if (typeof getBuiltinModule === 'function') {
27
+ return getBuiltinModule('node:module');
28
+ }
29
+ // Node older than 20.16 / 22.3 has no getBuiltinModule. A direct eval keeps
30
+ // the CommonJS `require` in scope while hiding the call from static analysis.
31
+ // eslint-disable-next-line no-eval
32
+ return eval('require')('node:module');
33
+ }
34
+ /** A `require` that resolves relative to `dir`, invisible to bundlers. */
35
+ function requireFrom(dir) {
36
+ return moduleApi().createRequire((0, node_path_1.join)(dir, 'noop.js'));
37
+ }
38
+ function isSdkPackage(dir) {
39
+ const manifest = (0, node_path_1.join)(dir, 'package.json');
40
+ if (!(0, node_fs_1.existsSync)(manifest))
41
+ return false;
42
+ try {
43
+ return JSON.parse((0, node_fs_1.readFileSync)(manifest, 'utf8')).name === 'smolmachines';
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ }
49
+ let cachedSdkDir;
50
+ /** The directory holding `binding.js` and the source-layout `native/` assets:
51
+ * this file's own directory when the SDK runs unbundled (the package root from
52
+ * source, `dist/` when built), otherwise the installed package's `dist/`,
53
+ * resolved from the app's working directory. Undefined when neither exists. */
54
+ function sdkDir() {
55
+ if (cachedSdkDir !== undefined)
56
+ return cachedSdkDir ?? undefined;
57
+ let found;
58
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(__dirname, 'binding.js')) && [__dirname, (0, node_path_1.join)(__dirname, '..')].some(isSdkPackage)) {
59
+ found = __dirname;
60
+ }
61
+ else {
62
+ try {
63
+ // The package's `exports` map only exposes ".", so resolve its entry
64
+ // (`dist/index.js`) rather than `smolmachines/package.json`.
65
+ const dist = (0, node_path_1.dirname)(requireFrom(process.cwd()).resolve('smolmachines'));
66
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(dist, 'binding.js')))
67
+ found = dist;
68
+ }
69
+ catch {
70
+ found = undefined;
71
+ }
72
+ }
73
+ cachedSdkDir = found ?? null;
74
+ return found;
75
+ }
package/dist/transport.js CHANGED
@@ -1588,7 +1588,7 @@ async function restoreCheckpointTransport(checkpointId, name, conn, options) {
1588
1588
  let restored;
1589
1589
  try {
1590
1590
  const handleSignals = conn.handleSignals ?? true;
1591
- restored = new LocalTransport((0, native_1.getNapiMachine)().restoreCheckpoint(name, path), handleSignals, undefined, handleSignals);
1591
+ restored = new LocalTransport((0, native_1.getNapiMachine)().restoreCheckpoint(name, path, options?.keepIdentity), handleSignals, undefined, handleSignals);
1592
1592
  // Between install and first boot: the one moment a restored machine's
1593
1593
  // policy can be its own rather than the checkpoint's.
1594
1594
  if (options?.networkPolicy !== undefined)
@@ -1602,6 +1602,9 @@ async function restoreCheckpointTransport(checkpointId, name, conn, options) {
1602
1602
  throw (0, errors_1.wrapNativeError)(error);
1603
1603
  }
1604
1604
  }
1605
+ if (options?.keepIdentity) {
1606
+ throw new errors_1.InvalidConfigError("keepIdentity is local-only: a cloud restore always gives the new machine its own identity.");
1607
+ }
1605
1608
  const explicitKey = conn.apiKey ?? process.env.SMOL_CLOUD_TOKEN;
1606
1609
  const { apiKey: cliKey, endpoint: cliUrl } = cliSession(conn.target);
1607
1610
  const key = explicitKey ?? cliKey ?? cliConfigApiKey();
package/dist/types.d.ts CHANGED
@@ -267,6 +267,12 @@ export interface RestoreCheckpointOptions {
267
267
  * The checkpoint must have been taken on the `"virtio-net"` backend for an
268
268
  * allow list or `deny-all`. Local target only. */
269
269
  networkPolicy?: NetworkPolicy;
270
+ /** Restore the machine as itself going back in time rather than as a clone:
271
+ * it keeps the hostname and machine ID it was saved with, which skips about
272
+ * a second of identity reset on its first start. For rewinding a machine to
273
+ * an earlier save point; do not run two machines from one checkpoint with
274
+ * it. Local target only. */
275
+ keepIdentity?: boolean;
270
276
  }
271
277
  /** Per-call execution options. */
272
278
  export interface ExecOptions {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smolmachines",
3
- "version": "1.22.0",
3
+ "version": "1.22.1",
4
4
  "description": "Embed isolated microVM sandboxes directly in your code — no server to run.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "smol-machines",
@@ -62,8 +62,8 @@
62
62
  "typescript": "^5.6.0"
63
63
  },
64
64
  "optionalDependencies": {
65
- "smolmachines-darwin-arm64": "1.22.0",
66
- "smolmachines-linux-x64-gnu": "1.22.0",
67
- "smolmachines-linux-arm64-gnu": "1.22.0"
65
+ "smolmachines-darwin-arm64": "1.22.1",
66
+ "smolmachines-linux-x64-gnu": "1.22.1",
67
+ "smolmachines-linux-arm64-gnu": "1.22.1"
68
68
  }
69
69
  }