@pi-archimedes/image-paste 0.7.0 → 0.8.0

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/clipboard.ts +72 -51
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-archimedes/image-paste",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "keywords": [
6
6
  "pi-package"
package/src/clipboard.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { spawnSync } from "node:child_process";
1
+ import { spawn, spawnSync } from "node:child_process";
2
2
  import { createRequire } from "node:module";
3
3
  import { existsSync } from "node:fs";
4
4
  import { readFile } from "node:fs/promises";
@@ -157,7 +157,7 @@ function encodePowerShell(script: string): string {
157
157
  return Buffer.from(script, "utf16le").toString("base64");
158
158
  }
159
159
 
160
- function readClipboardImageViaPowerShell(): ClipboardReadResult {
160
+ async function readClipboardImageViaPowerShell(): Promise<ClipboardReadResult> {
161
161
  const script = `
162
162
  $ErrorActionPreference = 'Stop'
163
163
  Add-Type -AssemblyName System.Windows.Forms
@@ -182,57 +182,78 @@ try {
182
182
  }
183
183
  `;
184
184
 
185
- const result = spawnSync(
186
- "powershell.exe",
187
- [
188
- "-NoProfile",
189
- "-NonInteractive",
190
- "-ExecutionPolicy",
191
- "Bypass",
192
- "-STA",
193
- "-EncodedCommand",
194
- encodePowerShell(script),
195
- ],
196
- {
197
- encoding: "utf8",
198
- timeout: READ_TIMEOUT_MS,
199
- maxBuffer: MAX_BUFFER_BYTES,
200
- windowsHide: true,
201
- },
202
- );
203
-
204
- if (result.error) {
205
- return {
206
- available: !isErrnoException(result.error) || result.error.code !== "ENOENT",
207
- image: null,
208
- };
209
- }
210
-
211
- if (result.status !== 0) {
212
- return { available: true, image: null };
213
- }
185
+ return new Promise((resolve) => {
186
+ const child = spawn(
187
+ "powershell.exe",
188
+ [
189
+ "-NoProfile",
190
+ "-NonInteractive",
191
+ "-ExecutionPolicy",
192
+ "Bypass",
193
+ "-STA",
194
+ "-EncodedCommand",
195
+ encodePowerShell(script),
196
+ ],
197
+ {
198
+ windowsHide: true,
199
+ },
200
+ );
214
201
 
215
- const base64 = result.stdout.trim();
216
- if (!base64) {
217
- return { available: true, image: null };
218
- }
202
+ // Timeout guard
203
+ const timeout = setTimeout(() => {
204
+ child.kill("SIGKILL");
205
+ resolve({ available: true, image: null });
206
+ }, READ_TIMEOUT_MS);
207
+
208
+ let stdout = "";
209
+ let exceededMaxBuffer = false;
210
+ child.stdout?.on("data", (data: Buffer) => {
211
+ if (stdout.length + data.length > MAX_BUFFER_BYTES) {
212
+ exceededMaxBuffer = true;
213
+ return;
214
+ }
215
+ stdout += data.toString("utf8");
216
+ });
217
+
218
+ child.on("error", (err: Error) => {
219
+ clearTimeout(timeout);
220
+ resolve({
221
+ available: !isErrnoException(err) || (err as NodeJS.ErrnoException).code !== "ENOENT",
222
+ image: null,
223
+ });
224
+ });
225
+
226
+ child.on("close", (status: number | null) => {
227
+ clearTimeout(timeout);
228
+ if (status !== 0 || exceededMaxBuffer) {
229
+ resolve({ available: true, image: null });
230
+ return;
231
+ }
219
232
 
220
- try {
221
- const bytes = Buffer.from(base64, "base64");
222
- if (bytes.length === 0) {
223
- return { available: true, image: null };
224
- }
233
+ const base64 = stdout.trim();
234
+ if (!base64) {
235
+ resolve({ available: true, image: null });
236
+ return;
237
+ }
225
238
 
226
- return {
227
- available: true,
228
- image: {
229
- bytes: new Uint8Array(bytes),
230
- mimeType: "image/png",
231
- },
232
- };
233
- } catch {
234
- return { available: true, image: null };
235
- }
239
+ try {
240
+ const bytes = Buffer.from(base64, "base64");
241
+ if (bytes.length === 0) {
242
+ resolve({ available: true, image: null });
243
+ return;
244
+ }
245
+ resolve({
246
+ available: true,
247
+ image: {
248
+ bytes: new Uint8Array(bytes),
249
+ mimeType: "image/png",
250
+ },
251
+ });
252
+ } catch {
253
+ resolve({ available: true, image: null });
254
+ }
255
+ });
256
+ });
236
257
  }
237
258
 
238
259
  function readClipboardImageViaWlPaste(): ClipboardReadResult {
@@ -362,7 +383,7 @@ export async function readClipboardImage(options?: {
362
383
  return nativeImage;
363
384
  }
364
385
 
365
- const powerShellImage = recordResult(readClipboardImageViaPowerShell());
386
+ const powerShellImage = recordResult(await readClipboardImageViaPowerShell());
366
387
  if (powerShellImage) {
367
388
  return powerShellImage;
368
389
  }