openapi-sdkgen 10.1.0 → 11.0.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.
@@ -1,48 +1,48 @@
1
1
  {
2
2
  "repository": "jinyongp/openapi-sdkgen",
3
- "version": "10.1.0",
4
- "tag": "v10.1.0",
3
+ "version": "11.0.0",
4
+ "tag": "v11.0.0",
5
5
  "cacheEnv": "OPENAPI_SDKGEN_CACHE_DIR",
6
6
  "targets": {
7
7
  "darwin-arm64": {
8
- "asset": "openapi-sdkgen_10.1.0_darwin_arm64.tar.gz",
8
+ "asset": "openapi-sdkgen_11.0.0_darwin_arm64.tar.gz",
9
9
  "executable": "openapi-sdkgen",
10
- "sha256": "fe7c6c6c6ae6568a34a2a66b16cc2ff65f036955fc395e07fa8de3009beaa4a0"
10
+ "sha256": "4e63a69b3e2b7037d5233a57989bf782e0aab7efc9c3325c9cd5c88778773944"
11
11
  },
12
12
  "darwin-x64": {
13
- "asset": "openapi-sdkgen_10.1.0_darwin_amd64.tar.gz",
13
+ "asset": "openapi-sdkgen_11.0.0_darwin_amd64.tar.gz",
14
14
  "executable": "openapi-sdkgen",
15
- "sha256": "6b3957cf690006d969699e4e29de4ea51ab2bfaec2ec58e70c627e1a87f3a53e"
15
+ "sha256": "59ef63ca1d3cad71c6afdcc8160e8fb219deb71f612de652cfa9568e42a40b78"
16
16
  },
17
17
  "linux-arm64-gnu": {
18
- "asset": "openapi-sdkgen_10.1.0_linux_arm64.tar.gz",
18
+ "asset": "openapi-sdkgen_11.0.0_linux_arm64.tar.gz",
19
19
  "executable": "openapi-sdkgen",
20
- "sha256": "47b0f5798c5f1c87f3a1c41b5d5b35fedb093b6a43f64e0fe3c4f7b54676a58e"
20
+ "sha256": "263709d9ad9d878bafb4f205d6285c5af7994ade7358a5d6d5eec34a021d66a0"
21
21
  },
22
22
  "linux-arm64-musl": {
23
- "asset": "openapi-sdkgen_10.1.0_linux_arm64.tar.gz",
23
+ "asset": "openapi-sdkgen_11.0.0_linux_arm64.tar.gz",
24
24
  "executable": "openapi-sdkgen",
25
- "sha256": "47b0f5798c5f1c87f3a1c41b5d5b35fedb093b6a43f64e0fe3c4f7b54676a58e"
25
+ "sha256": "263709d9ad9d878bafb4f205d6285c5af7994ade7358a5d6d5eec34a021d66a0"
26
26
  },
27
27
  "linux-x64-gnu": {
28
- "asset": "openapi-sdkgen_10.1.0_linux_amd64.tar.gz",
28
+ "asset": "openapi-sdkgen_11.0.0_linux_amd64.tar.gz",
29
29
  "executable": "openapi-sdkgen",
30
- "sha256": "a1a7eda43a29a5a819d3daedfe2b495a2e0bd444e7e15d91467b86a722304cd4"
30
+ "sha256": "c27dd263442e2465428218353bc09582e5cfec6f6b9323cdd761c3716570cc7e"
31
31
  },
32
32
  "linux-x64-musl": {
33
- "asset": "openapi-sdkgen_10.1.0_linux_amd64.tar.gz",
33
+ "asset": "openapi-sdkgen_11.0.0_linux_amd64.tar.gz",
34
34
  "executable": "openapi-sdkgen",
35
- "sha256": "a1a7eda43a29a5a819d3daedfe2b495a2e0bd444e7e15d91467b86a722304cd4"
35
+ "sha256": "c27dd263442e2465428218353bc09582e5cfec6f6b9323cdd761c3716570cc7e"
36
36
  },
37
37
  "win32-arm64": {
38
- "asset": "openapi-sdkgen_10.1.0_windows_arm64.zip",
38
+ "asset": "openapi-sdkgen_11.0.0_windows_arm64.zip",
39
39
  "executable": "openapi-sdkgen.exe",
40
- "sha256": "41edea99f9b24bb8b4d2da2cb14e590bff428e0ad95a2de9e149e8350253f6e1"
40
+ "sha256": "c42024638d0455f69a0b550588cab42e74ce9a5b5eb8b4be51bb36a69336c464"
41
41
  },
42
42
  "win32-x64": {
43
- "asset": "openapi-sdkgen_10.1.0_windows_amd64.zip",
43
+ "asset": "openapi-sdkgen_11.0.0_windows_amd64.zip",
44
44
  "executable": "openapi-sdkgen.exe",
45
- "sha256": "ab63a70b77931c2ab203db7323b12035f07af5c46f6800f529fe21457b3f8b76"
45
+ "sha256": "6c86ee49d3be20d5b4b6e07dc27c00db30b93a815c846db93c7eeae1138beab0"
46
46
  }
47
47
  }
48
48
  }
@@ -531,7 +531,7 @@ var require_yauzl = __commonJS({
531
531
  var PassThrough = require("stream").PassThrough;
532
532
  var Writable = require("stream").Writable;
533
533
  var crc32 = typeof zlib.crc32 === "function" ? zlib.crc32 : require_crc32();
534
- exports2.open = open;
534
+ exports2.open = open2;
535
535
  exports2.fromFd = fromFd;
536
536
  exports2.fromBuffer = fromBuffer;
537
537
  exports2.fromRandomAccessReader = fromRandomAccessReader;
@@ -549,7 +549,7 @@ var require_yauzl = __commonJS({
549
549
  exports2.RandomAccessReader = RandomAccessReader;
550
550
  function openPromise(path, options2) {
551
551
  return new Promise((resolve2, reject) => {
552
- open(path, { ...options2, lazyEntries: true }, function(err, zipfile) {
552
+ open2(path, { ...options2, lazyEntries: true }, function(err, zipfile) {
553
553
  if (err) return reject(err);
554
554
  resolve2(zipfile);
555
555
  });
@@ -579,7 +579,7 @@ var require_yauzl = __commonJS({
579
579
  });
580
580
  });
581
581
  }
582
- function open(path, options2, callback) {
582
+ function open2(path, options2, callback) {
583
583
  if (typeof options2 === "function") {
584
584
  callback = options2;
585
585
  options2 = null;
@@ -1391,13 +1391,93 @@ var import_node_process3 = __toESM(require("node:process"));
1391
1391
 
1392
1392
  // src/native/launcher/cache.ts
1393
1393
  var import_node_crypto3 = require("node:crypto");
1394
- var import_promises2 = require("node:fs/promises");
1394
+ var import_node_fs8 = require("node:fs");
1395
+ var import_promises3 = require("node:fs/promises");
1395
1396
  var import_node_os = require("node:os");
1396
1397
  var import_node_path11 = require("node:path");
1397
1398
  var import_node_process = __toESM(require("node:process"));
1398
- var import_promises3 = require("node:timers/promises");
1399
+
1400
+ // src/native/limits.ts
1401
+ function nativeLimits(env = process.env) {
1402
+ const read = (suffix, fallback) => {
1403
+ const name = `RELEASEWAY_NATIVE_${suffix}`;
1404
+ const raw = env[name];
1405
+ if (raw === void 0 || raw === "") return fallback;
1406
+ const value = Number(raw);
1407
+ if (!/^\d+$/.test(raw) || !Number.isSafeInteger(value) || value <= 0) {
1408
+ throw new Error(`${name} must be a positive safe integer`);
1409
+ }
1410
+ return value;
1411
+ };
1412
+ return {
1413
+ apiTimeoutMs: read("API_TIMEOUT_MS", 3e4),
1414
+ downloadTimeoutMs: read("DOWNLOAD_TIMEOUT_MS", 24e4),
1415
+ extractTimeoutMs: read("EXTRACT_TIMEOUT_MS", 12e4),
1416
+ maxArchiveBytes: read("MAX_ARCHIVE_BYTES", 1024 ** 3),
1417
+ maxExecutableBytes: read("MAX_EXECUTABLE_BYTES", 1024 ** 3),
1418
+ maxExpandedBytes: read("MAX_EXPANDED_BYTES", 4 * 1024 ** 3)
1419
+ };
1420
+ }
1421
+ async function withDeadline(label, timeoutMs, operation) {
1422
+ const controller = new AbortController();
1423
+ let timer;
1424
+ const expired = new Promise((_2, reject) => {
1425
+ const deadline = performance.now() + timeoutMs;
1426
+ const expire = () => {
1427
+ const remaining = deadline - performance.now();
1428
+ if (remaining > 0) {
1429
+ timer = setTimeout(expire, Math.min(Math.ceil(remaining), 2 ** 31 - 1));
1430
+ return;
1431
+ }
1432
+ const error = new Error(`${label} exceeded ${timeoutMs}ms preparation budget`);
1433
+ controller.abort(error);
1434
+ reject(error);
1435
+ };
1436
+ expire();
1437
+ });
1438
+ try {
1439
+ return await Promise.race([operation(controller.signal), expired]);
1440
+ } finally {
1441
+ clearTimeout(timer);
1442
+ }
1443
+ }
1444
+ async function consumeBoundedResponse(response, maxBytes, signal, consume) {
1445
+ const declared = Number(response.headers.get("content-length"));
1446
+ if (Number.isFinite(declared) && declared > maxBytes) {
1447
+ await response.body?.cancel();
1448
+ throw new Error(`Download exceeds ${maxBytes} byte limit`);
1449
+ }
1450
+ if (!response.body) return;
1451
+ const reader = response.body.getReader();
1452
+ const abort = () => {
1453
+ void reader.cancel(signal.reason).catch(() => {
1454
+ });
1455
+ };
1456
+ signal.addEventListener("abort", abort, { once: true });
1457
+ let total = 0;
1458
+ try {
1459
+ while (true) {
1460
+ signal.throwIfAborted();
1461
+ const { done, value } = await reader.read();
1462
+ signal.throwIfAborted();
1463
+ if (done) break;
1464
+ total += value.byteLength;
1465
+ if (total > maxBytes) throw new Error(`Download exceeds ${maxBytes} byte limit`);
1466
+ await consume(value);
1467
+ }
1468
+ } finally {
1469
+ signal.removeEventListener("abort", abort);
1470
+ await reader.cancel().catch(() => {
1471
+ });
1472
+ reader.releaseLock();
1473
+ }
1474
+ }
1475
+
1476
+ // src/native/launcher/cache.ts
1477
+ var import_promises4 = require("node:timers/promises");
1399
1478
 
1400
1479
  // src/native/launcher/archive.ts
1480
+ var import_node_fs7 = require("node:fs");
1401
1481
  var import_node_path10 = require("node:path");
1402
1482
 
1403
1483
  // node_modules/tar/dist/esm/index.min.js
@@ -4385,44 +4465,73 @@ function normalizeArchivePath(value) {
4385
4465
  function isTarRegularFile(type) {
4386
4466
  return type === "File" || type === "OldFile" || type === "ContiguousFile";
4387
4467
  }
4388
- async function extractTarGzExecutable(archivePath, executablePath) {
4468
+ async function extractTarGzExecutable(archivePath, executablePath, limits = nativeLimits()) {
4389
4469
  const wanted = normalizeArchivePath(executablePath);
4390
4470
  const seen = /* @__PURE__ */ new Set();
4391
4471
  const chunks = [];
4392
4472
  let matches = 0;
4393
4473
  let scanError;
4394
- await Ct({
4395
- file: archivePath,
4396
- strict: true,
4397
- onentry(entry) {
4398
- if (scanError) {
4399
- entry.resume();
4400
- return;
4401
- }
4402
- try {
4403
- const normalized = normalizeArchivePath(entry.path.replace(/\/$/, ""));
4404
- if (seen.has(normalized)) {
4405
- throw new Error(`Duplicate archive entry path: ${normalized}`);
4406
- }
4407
- seen.add(normalized);
4408
- if (entry.type === "Directory") {
4474
+ let expanded = 0;
4475
+ let executableBytes = 0;
4476
+ await withDeadline("Native tar extraction", limits.extractTimeoutMs, (signal) => new Promise((resolve2, reject) => {
4477
+ const input = (0, import_node_fs7.createReadStream)(archivePath);
4478
+ const parser = Ct({
4479
+ strict: true,
4480
+ onentry(entry) {
4481
+ if (scanError) {
4482
+ entry.resume();
4409
4483
  return;
4410
4484
  }
4411
- if (!isTarRegularFile(entry.type)) {
4412
- throw new Error(
4413
- `Unsupported non-regular tar entry ${entry.type}: ${entry.path}`
4414
- );
4415
- }
4416
- if (normalized === wanted) {
4417
- matches += 1;
4418
- entry.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
4485
+ try {
4486
+ expanded += entry.size;
4487
+ if (expanded > limits.maxExpandedBytes) throw new Error("Native archive exceeds expanded byte limit");
4488
+ const normalized = normalizeArchivePath(entry.path.replace(/\/$/, ""));
4489
+ if (seen.has(normalized)) {
4490
+ throw new Error(`Duplicate archive entry path: ${normalized}`);
4491
+ }
4492
+ seen.add(normalized);
4493
+ if (entry.type === "Directory") {
4494
+ return;
4495
+ }
4496
+ if (!isTarRegularFile(entry.type)) {
4497
+ throw new Error(
4498
+ `Unsupported non-regular tar entry ${entry.type}: ${entry.path}`
4499
+ );
4500
+ }
4501
+ if (normalized === wanted) {
4502
+ if (entry.size > limits.maxExecutableBytes) throw new Error("Native executable exceeds byte limit");
4503
+ matches += 1;
4504
+ entry.on("data", (chunk) => {
4505
+ executableBytes += chunk.length;
4506
+ if (executableBytes > limits.maxExecutableBytes) {
4507
+ parser.abort(new Error("Native executable exceeds byte limit"));
4508
+ return;
4509
+ }
4510
+ chunks.push(Buffer.from(chunk));
4511
+ });
4512
+ }
4513
+ } catch (error) {
4514
+ scanError = error instanceof Error ? error : new Error(String(error));
4515
+ entry.resume();
4516
+ parser.abort(scanError);
4419
4517
  }
4420
- } catch (error) {
4421
- scanError = error instanceof Error ? error : new Error(String(error));
4422
- entry.resume();
4423
4518
  }
4424
- }
4425
- });
4519
+ });
4520
+ const abort = () => parser.abort(signal.reason);
4521
+ signal.addEventListener("abort", abort, { once: true });
4522
+ const finish = (error) => {
4523
+ signal.removeEventListener("abort", abort);
4524
+ input.destroy();
4525
+ if (error) reject(error);
4526
+ else resolve2();
4527
+ };
4528
+ input.on("error", (error) => {
4529
+ parser.abort(error);
4530
+ });
4531
+ parser.on("error", finish);
4532
+ parser.on("end", () => finish());
4533
+ input.pipe(parser);
4534
+ }));
4426
4535
  if (scanError) {
4427
4536
  throw scanError;
4428
4537
  }
@@ -4464,90 +4573,117 @@ function openZip(path) {
4464
4573
  });
4465
4574
  });
4466
4575
  }
4467
- function readZipEntry(zip, entry) {
4576
+ function readZipEntry(zip, entry, maxBytes, signal) {
4468
4577
  return new Promise((resolve2, reject) => {
4469
4578
  zip.openReadStream(entry, (error, stream) => {
4470
4579
  if (error || !stream) {
4471
4580
  reject(error ?? new Error("Failed to read zip entry"));
4472
4581
  return;
4473
4582
  }
4583
+ if (signal.aborted) {
4584
+ stream.destroy();
4585
+ reject(signal.reason);
4586
+ return;
4587
+ }
4474
4588
  const chunks = [];
4475
- stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
4589
+ let total = 0;
4590
+ const abort = () => stream.destroy(signal.reason);
4591
+ signal.addEventListener("abort", abort, { once: true });
4592
+ stream.on("close", () => signal.removeEventListener("abort", abort));
4593
+ stream.on("data", (chunk) => {
4594
+ total += chunk.length;
4595
+ if (total > maxBytes) stream.destroy(new Error("Native executable exceeds byte limit"));
4596
+ else chunks.push(Buffer.from(chunk));
4597
+ });
4476
4598
  stream.on("error", reject);
4477
4599
  stream.on("end", () => resolve2(Buffer.concat(chunks)));
4478
4600
  });
4479
4601
  });
4480
4602
  }
4481
- async function extractZipExecutable(archivePath, executablePath) {
4482
- const wanted = normalizeArchivePath(executablePath);
4483
- const zip = await openZip(archivePath);
4484
- const seen = /* @__PURE__ */ new Set();
4485
- let match;
4486
- let matches = 0;
4487
- return new Promise((resolve2, reject) => {
4488
- let settled = false;
4489
- const fail = (error) => {
4490
- if (settled) return;
4491
- settled = true;
4492
- try {
4493
- zip.close();
4494
- } catch {
4495
- }
4496
- reject(error);
4497
- };
4498
- zip.on("error", fail);
4499
- zip.on("entry", async (entry) => {
4500
- try {
4501
- const raw = entry.fileName.replace(/\/$/, "");
4502
- const normalized = normalizeArchivePath(raw);
4503
- if (seen.has(normalized)) {
4504
- throw new Error(`Duplicate archive entry path: ${normalized}`);
4505
- }
4506
- seen.add(normalized);
4507
- assertZipEntryType(entry, normalized);
4508
- const directory = entry.fileName.endsWith("/") || ((zipUnixMode(entry) ?? 0) & 61440) === 16384;
4509
- if (normalized === wanted) {
4510
- if (directory) {
4511
- throw new Error(
4512
- `Configured native executable is not a regular zip file: ${wanted}`
4513
- );
4514
- }
4515
- matches += 1;
4516
- match = await readZipEntry(zip, entry);
4603
+ async function extractZipExecutable(archivePath, executablePath, limits = nativeLimits()) {
4604
+ return withDeadline("Native zip extraction", limits.extractTimeoutMs, async (signal) => {
4605
+ const wanted = normalizeArchivePath(executablePath);
4606
+ const zip = await openZip(archivePath);
4607
+ const seen = /* @__PURE__ */ new Set();
4608
+ let match;
4609
+ let matches = 0;
4610
+ let expanded = 0;
4611
+ return new Promise((resolve2, reject) => {
4612
+ let settled = false;
4613
+ const fail = (error) => {
4614
+ if (settled) return;
4615
+ settled = true;
4616
+ try {
4617
+ zip.close();
4618
+ } catch {
4517
4619
  }
4518
- zip.readEntry();
4519
- } catch (error) {
4520
- fail(error);
4521
- }
4522
- });
4523
- zip.on("end", () => {
4524
- if (settled) return;
4525
- settled = true;
4526
- if (matches !== 1 || !match) {
4527
- reject(
4528
- new Error(
4529
- `Native executable ${wanted} must occur exactly once in zip; found ${matches}`
4530
- )
4531
- );
4620
+ reject(error);
4621
+ };
4622
+ zip.on("error", fail);
4623
+ const abort = () => fail(signal.reason);
4624
+ signal.addEventListener("abort", abort, { once: true });
4625
+ zip.on("close", () => signal.removeEventListener("abort", abort));
4626
+ if (signal.aborted) {
4627
+ fail(signal.reason);
4532
4628
  return;
4533
4629
  }
4534
- resolve2(match);
4630
+ zip.on("entry", async (entry) => {
4631
+ try {
4632
+ expanded += entry.uncompressedSize;
4633
+ if (expanded > limits.maxExpandedBytes) throw new Error("Native archive exceeds expanded byte limit");
4634
+ const raw = entry.fileName.replace(/\/$/, "");
4635
+ const normalized = normalizeArchivePath(raw);
4636
+ if (seen.has(normalized)) {
4637
+ throw new Error(`Duplicate archive entry path: ${normalized}`);
4638
+ }
4639
+ seen.add(normalized);
4640
+ assertZipEntryType(entry, normalized);
4641
+ const directory = entry.fileName.endsWith("/") || ((zipUnixMode(entry) ?? 0) & 61440) === 16384;
4642
+ if (normalized === wanted) {
4643
+ if (directory) {
4644
+ throw new Error(
4645
+ `Configured native executable is not a regular zip file: ${wanted}`
4646
+ );
4647
+ }
4648
+ matches += 1;
4649
+ if (entry.uncompressedSize > limits.maxExecutableBytes) throw new Error("Native executable exceeds byte limit");
4650
+ match = await readZipEntry(zip, entry, limits.maxExecutableBytes, signal);
4651
+ }
4652
+ zip.readEntry();
4653
+ } catch (error) {
4654
+ fail(error);
4655
+ }
4656
+ });
4657
+ zip.on("end", () => {
4658
+ if (settled) return;
4659
+ settled = true;
4660
+ if (matches !== 1 || !match) {
4661
+ reject(
4662
+ new Error(
4663
+ `Native executable ${wanted} must occur exactly once in zip; found ${matches}`
4664
+ )
4665
+ );
4666
+ return;
4667
+ }
4668
+ resolve2(match);
4669
+ });
4670
+ zip.readEntry();
4535
4671
  });
4536
- zip.readEntry();
4537
4672
  });
4538
4673
  }
4539
- async function extractExecutable(archivePath, assetName, executablePath) {
4674
+ async function extractExecutable(archivePath, assetName, executablePath, limits = nativeLimits()) {
4540
4675
  if (assetName.endsWith(".tar.gz")) {
4541
- return extractTarGzExecutable(archivePath, executablePath);
4676
+ return extractTarGzExecutable(archivePath, executablePath, limits);
4542
4677
  }
4543
4678
  if (assetName.endsWith(".zip")) {
4544
- return extractZipExecutable(archivePath, executablePath);
4679
+ return extractZipExecutable(archivePath, executablePath, limits);
4545
4680
  }
4546
4681
  throw new Error(`Unsupported native archive format: ${assetName}`);
4547
4682
  }
4548
4683
 
4549
4684
  // src/native/launcher/download.ts
4550
4685
  var import_node_crypto2 = require("node:crypto");
4686
+ var import_promises2 = require("node:fs/promises");
4551
4687
  function encodeRepository(repository) {
4552
4688
  const parts = repository.split("/");
4553
4689
  if (parts.length !== 2 || !parts[0] || !parts[1]) {
@@ -4555,6 +4691,32 @@ function encodeRepository(repository) {
4555
4691
  }
4556
4692
  return parts.map(encodeURIComponent).join("/");
4557
4693
  }
4694
+ async function downloadReleaseAssetToFile(repository, tag, asset, destination, expectedSha256, env = process.env, fetchImpl = fetch) {
4695
+ const limits = nativeLimits(env);
4696
+ const file = await (0, import_promises2.open)(destination, "wx", 384);
4697
+ try {
4698
+ await withDeadline("Native asset download", limits.downloadTimeoutMs, async (signal) => {
4699
+ const response = await fetchImpl(releaseAssetUrl(repository, tag, asset), {
4700
+ signal,
4701
+ redirect: "follow",
4702
+ headers: { "User-Agent": "releaseway-npm-actions-native" }
4703
+ });
4704
+ if (!response.ok) {
4705
+ throw new Error(`Failed to download native GitHub Release asset ${asset}: HTTP ${response.status}`);
4706
+ }
4707
+ const hash = (0, import_node_crypto2.createHash)("sha256");
4708
+ await consumeBoundedResponse(response, limits.maxArchiveBytes, signal, async (chunk) => {
4709
+ hash.update(chunk);
4710
+ await file.writeFile(chunk);
4711
+ });
4712
+ if (hash.digest("hex") !== expectedSha256.toLowerCase()) {
4713
+ throw new Error("Native asset SHA-256 digest mismatch");
4714
+ }
4715
+ });
4716
+ } finally {
4717
+ await file.close();
4718
+ }
4719
+ }
4558
4720
  function releaseAssetUrl(repository, tag, asset) {
4559
4721
  return `https://github.com/${encodeRepository(repository)}/releases/download/${encodeURIComponent(tag)}/${encodeURIComponent(asset)}`;
4560
4722
  }
@@ -4568,25 +4730,16 @@ function verifySha256(bytes, expectedHex) {
4568
4730
  throw new Error("Native asset SHA-256 digest mismatch");
4569
4731
  }
4570
4732
  }
4571
- async function downloadReleaseAsset(repository, tag, asset, fetchImpl = fetch) {
4572
- const response = await fetchImpl(releaseAssetUrl(repository, tag, asset), {
4573
- redirect: "follow",
4574
- headers: {
4575
- "User-Agent": "releaseway-npm-actions-native"
4576
- }
4577
- });
4578
- if (!response.ok) {
4579
- throw new Error(
4580
- `Failed to download native GitHub Release asset ${asset}: HTTP ${response.status}`
4581
- );
4582
- }
4583
- return Buffer.from(await response.arrayBuffer());
4584
- }
4585
4733
 
4586
4734
  // src/native/launcher/cache.ts
4587
4735
  function sha256(bytes) {
4588
4736
  return (0, import_node_crypto3.createHash)("sha256").update(bytes).digest("hex");
4589
4737
  }
4738
+ async function fileSha256(path) {
4739
+ const hash = (0, import_node_crypto3.createHash)("sha256");
4740
+ for await (const chunk of (0, import_node_fs8.createReadStream)(path)) hash.update(chunk);
4741
+ return hash.digest("hex");
4742
+ }
4590
4743
  function archiveFormat(asset) {
4591
4744
  if (asset.endsWith(".tar.gz")) {
4592
4745
  return "tar.gz";
@@ -4660,14 +4813,13 @@ function executableCacheKey(executable) {
4660
4813
  async function validateArchiveCache(directory, target) {
4661
4814
  try {
4662
4815
  const metadata = JSON.parse(
4663
- await (0, import_promises2.readFile)((0, import_node_path11.join)(directory, "archive.json"), "utf8")
4816
+ await (0, import_promises3.readFile)((0, import_node_path11.join)(directory, "archive.json"), "utf8")
4664
4817
  );
4665
4818
  if (metadata.schema !== 2 || metadata.assetSha256 !== target.sha256 || metadata.archiveFormat !== archiveFormat(target.asset)) {
4666
4819
  return void 0;
4667
4820
  }
4668
4821
  const archive = (0, import_node_path11.join)(directory, "archive.bin");
4669
- const bytes = await (0, import_promises2.readFile)(archive);
4670
- if (sha256(bytes) !== target.sha256) {
4822
+ if (await fileSha256(archive) !== target.sha256) {
4671
4823
  return void 0;
4672
4824
  }
4673
4825
  return archive;
@@ -4678,15 +4830,14 @@ async function validateArchiveCache(directory, target) {
4678
4830
  async function validateExecutableCache(directory, target, executable) {
4679
4831
  try {
4680
4832
  const metadata = JSON.parse(
4681
- await (0, import_promises2.readFile)((0, import_node_path11.join)(directory, "metadata.json"), "utf8")
4833
+ await (0, import_promises3.readFile)((0, import_node_path11.join)(directory, "metadata.json"), "utf8")
4682
4834
  );
4683
4835
  const executableFile = cachedExecutableName(executable);
4684
4836
  if (metadata.schema !== 2 || metadata.assetSha256 !== target.sha256 || metadata.sourceExecutable !== executable || typeof metadata.executableSha256 !== "string" || !/^[0-9a-f]{64}$/.test(metadata.executableSha256) || metadata.executableFile !== executableFile) {
4685
4837
  return void 0;
4686
4838
  }
4687
4839
  const path = (0, import_node_path11.join)(directory, executableFile);
4688
- const bytes = await (0, import_promises2.readFile)(path);
4689
- if (sha256(bytes) !== metadata.executableSha256) {
4840
+ if (await fileSha256(path) !== metadata.executableSha256) {
4690
4841
  return void 0;
4691
4842
  }
4692
4843
  return path;
@@ -4705,10 +4856,19 @@ async function acquireCacheLock(lockDirectory, validate, label, options2) {
4705
4856
  return { kind: "cached", value: cached };
4706
4857
  }
4707
4858
  try {
4708
- await (0, import_promises2.mkdir)(lockDirectory);
4859
+ await (0, import_promises3.mkdir)(lockDirectory);
4860
+ const heartbeat = setInterval(() => {
4861
+ const now = /* @__PURE__ */ new Date();
4862
+ void (0, import_promises3.utimes)(lockDirectory, now, now).catch(() => {
4863
+ });
4864
+ }, Math.max(1, Math.min(1e3, Math.floor(staleMs / 3))));
4865
+ heartbeat.unref();
4709
4866
  return {
4710
4867
  kind: "owner",
4711
- release: () => (0, import_promises2.rm)(lockDirectory, { recursive: true, force: true })
4868
+ release: async () => {
4869
+ clearInterval(heartbeat);
4870
+ await (0, import_promises3.rm)(lockDirectory, { recursive: true, force: true });
4871
+ }
4712
4872
  };
4713
4873
  } catch (error) {
4714
4874
  if (!error || typeof error !== "object" || !("code" in error) || String(error.code) !== "EEXIST") {
@@ -4716,9 +4876,9 @@ async function acquireCacheLock(lockDirectory, validate, label, options2) {
4716
4876
  }
4717
4877
  }
4718
4878
  try {
4719
- const lockStat = await (0, import_promises2.stat)(lockDirectory);
4879
+ const lockStat = await (0, import_promises3.stat)(lockDirectory);
4720
4880
  if (Date.now() - lockStat.mtimeMs > staleMs) {
4721
- await (0, import_promises2.rm)(lockDirectory, { recursive: true, force: true });
4881
+ await (0, import_promises3.rm)(lockDirectory, { recursive: true, force: true });
4722
4882
  continue;
4723
4883
  }
4724
4884
  } catch (error) {
@@ -4730,25 +4890,25 @@ async function acquireCacheLock(lockDirectory, validate, label, options2) {
4730
4890
  if (Date.now() - started > timeoutMs) {
4731
4891
  throw new Error(`Timed out waiting for native cache lock for ${label}`);
4732
4892
  }
4733
- await (0, import_promises3.setTimeout)(pollMs);
4893
+ await (0, import_promises4.setTimeout)(pollMs);
4734
4894
  }
4735
4895
  }
4736
4896
  async function promoteDirectory(temp, finalDirectory, validate) {
4737
4897
  try {
4738
- await (0, import_promises2.rename)(temp, finalDirectory);
4898
+ await (0, import_promises3.rename)(temp, finalDirectory);
4739
4899
  return validate();
4740
4900
  } catch (error) {
4741
4901
  if (!error || typeof error !== "object" || !("code" in error) || !["EEXIST", "ENOTEMPTY", "EPERM"].includes(String(error.code))) {
4742
4902
  throw error;
4743
4903
  }
4744
- await (0, import_promises2.rm)(temp, { recursive: true, force: true });
4904
+ await (0, import_promises3.rm)(temp, { recursive: true, force: true });
4745
4905
  return validate();
4746
4906
  }
4747
4907
  }
4748
4908
  async function prepareNativeArchive(manifest, target, root, options2) {
4749
4909
  const directory = (0, import_node_path11.join)(root, target.sha256);
4750
4910
  const executables = (0, import_node_path11.join)(directory, "executables");
4751
- await (0, import_promises2.mkdir)(executables, { recursive: true });
4911
+ await (0, import_promises3.mkdir)(executables, { recursive: true });
4752
4912
  const validate = () => validateArchiveCache(directory, target);
4753
4913
  const existing = await validate();
4754
4914
  if (existing) {
@@ -4768,34 +4928,35 @@ async function prepareNativeArchive(manifest, target, root, options2) {
4768
4928
  if (afterLock) {
4769
4929
  return afterLock;
4770
4930
  }
4771
- await (0, import_promises2.rm)((0, import_node_path11.join)(directory, "archive.bin"), { force: true });
4772
- await (0, import_promises2.rm)((0, import_node_path11.join)(directory, "archive.json"), { force: true });
4773
- const temp = await (0, import_promises2.mkdtemp)(
4931
+ await (0, import_promises3.rm)((0, import_node_path11.join)(directory, "archive.bin"), { force: true });
4932
+ await (0, import_promises3.rm)((0, import_node_path11.join)(directory, "archive.json"), { force: true });
4933
+ const temp = await (0, import_promises3.mkdtemp)(
4774
4934
  (0, import_node_path11.join)(root, `.${target.sha256}.archive.tmp-`)
4775
4935
  );
4776
4936
  try {
4777
- const download = options2.download ?? ((repository, tag, asset) => downloadReleaseAsset(repository, tag, asset));
4778
- const archiveBytes = await download(
4779
- manifest.repository,
4780
- manifest.tag,
4781
- target.asset
4782
- );
4783
- verifySha256(archiveBytes, target.sha256);
4784
4937
  const tempArchive = (0, import_node_path11.join)(temp, "archive.bin");
4785
4938
  const tempMetadata = (0, import_node_path11.join)(temp, "archive.json");
4786
- await (0, import_promises2.writeFile)(tempArchive, archiveBytes, { mode: 384 });
4939
+ if (options2.download) {
4940
+ const limits = nativeLimits(options2.env);
4941
+ const archiveBytes = await withDeadline("Native asset download", limits.downloadTimeoutMs, () => options2.download(manifest.repository, manifest.tag, target.asset));
4942
+ if (archiveBytes.length > limits.maxArchiveBytes) throw new Error("Native archive exceeds byte limit");
4943
+ verifySha256(archiveBytes, target.sha256);
4944
+ await (0, import_promises3.writeFile)(tempArchive, archiveBytes, { mode: 384 });
4945
+ } else {
4946
+ await downloadReleaseAssetToFile(manifest.repository, manifest.tag, target.asset, tempArchive, target.sha256, options2.env);
4947
+ }
4787
4948
  const metadata = {
4788
4949
  schema: 2,
4789
4950
  assetSha256: target.sha256,
4790
4951
  archiveFormat: archiveFormat(target.asset)
4791
4952
  };
4792
- await (0, import_promises2.writeFile)(
4953
+ await (0, import_promises3.writeFile)(
4793
4954
  tempMetadata,
4794
4955
  JSON.stringify(metadata, null, 2) + "\n",
4795
4956
  { encoding: "utf8", mode: 384 }
4796
4957
  );
4797
- await (0, import_promises2.rename)(tempArchive, (0, import_node_path11.join)(directory, "archive.bin"));
4798
- await (0, import_promises2.rename)(tempMetadata, (0, import_node_path11.join)(directory, "archive.json"));
4958
+ await (0, import_promises3.rename)(tempArchive, (0, import_node_path11.join)(directory, "archive.bin"));
4959
+ await (0, import_promises3.rename)(tempMetadata, (0, import_node_path11.join)(directory, "archive.json"));
4799
4960
  const promoted = await validate();
4800
4961
  if (!promoted) {
4801
4962
  throw new Error(
@@ -4804,7 +4965,7 @@ async function prepareNativeArchive(manifest, target, root, options2) {
4804
4965
  }
4805
4966
  return promoted;
4806
4967
  } finally {
4807
- await (0, import_promises2.rm)(temp, { recursive: true, force: true });
4968
+ await (0, import_promises3.rm)(temp, { recursive: true, force: true });
4808
4969
  }
4809
4970
  } finally {
4810
4971
  await lock.release();
@@ -4834,19 +4995,20 @@ async function prepareExecutableFromArchive(archivePath, target, root, options2)
4834
4995
  if (afterLock) {
4835
4996
  return afterLock;
4836
4997
  }
4837
- await (0, import_promises2.rm)(finalDirectory, { recursive: true, force: true });
4838
- const temp = await (0, import_promises2.mkdtemp)((0, import_node_path11.join)(executables, `.${key}.tmp-`));
4998
+ await (0, import_promises3.rm)(finalDirectory, { recursive: true, force: true });
4999
+ const temp = await (0, import_promises3.mkdtemp)((0, import_node_path11.join)(executables, `.${key}.tmp-`));
4839
5000
  try {
4840
5001
  const executableBytes = await extractExecutable(
4841
5002
  archivePath,
4842
5003
  target.asset,
4843
- executable
5004
+ executable,
5005
+ nativeLimits(options2.env)
4844
5006
  );
4845
5007
  const executableFile = cachedExecutableName(executable);
4846
5008
  const executablePath = (0, import_node_path11.join)(temp, executableFile);
4847
- await (0, import_promises2.writeFile)(executablePath, executableBytes, { mode: 493 });
5009
+ await (0, import_promises3.writeFile)(executablePath, executableBytes, { mode: 493 });
4848
5010
  if ((options2.platform ?? import_node_process.default.platform) !== "win32") {
4849
- await (0, import_promises2.chmod)(executablePath, 493);
5011
+ await (0, import_promises3.chmod)(executablePath, 493);
4850
5012
  }
4851
5013
  const metadata = {
4852
5014
  schema: 2,
@@ -4855,7 +5017,7 @@ async function prepareExecutableFromArchive(archivePath, target, root, options2)
4855
5017
  executableSha256: sha256(executableBytes),
4856
5018
  executableFile
4857
5019
  };
4858
- await (0, import_promises2.writeFile)(
5020
+ await (0, import_promises3.writeFile)(
4859
5021
  (0, import_node_path11.join)(temp, "metadata.json"),
4860
5022
  JSON.stringify(metadata, null, 2) + "\n",
4861
5023
  { encoding: "utf8", mode: 384 }
@@ -4872,7 +5034,7 @@ async function prepareExecutableFromArchive(archivePath, target, root, options2)
4872
5034
  }
4873
5035
  return promoted;
4874
5036
  } finally {
4875
- await (0, import_promises2.rm)(temp, { recursive: true, force: true });
5037
+ await (0, import_promises3.rm)(temp, { recursive: true, force: true });
4876
5038
  }
4877
5039
  } finally {
4878
5040
  await lock.release();
@@ -4884,7 +5046,7 @@ async function prepareNativeExecutable(manifest, target, options2 = {}) {
4884
5046
  platform: options2.platform,
4885
5047
  home: options2.home
4886
5048
  });
4887
- await (0, import_promises2.mkdir)(root, { recursive: true });
5049
+ await (0, import_promises3.mkdir)(root, { recursive: true });
4888
5050
  const archivePath = await prepareNativeArchive(
4889
5051
  manifest,
4890
5052
  target,
@@ -4900,7 +5062,7 @@ async function prepareNativeExecutable(manifest, target, options2 = {}) {
4900
5062
  }
4901
5063
 
4902
5064
  // src/native/launcher/manifest.ts
4903
- var import_promises4 = require("node:fs/promises");
5065
+ var import_promises5 = require("node:fs/promises");
4904
5066
 
4905
5067
  // src/native/validate.ts
4906
5068
  var SUPPORTED_NATIVE_TARGETS = /* @__PURE__ */ new Set([
@@ -4986,7 +5148,7 @@ function parseNativeManifest(value) {
4986
5148
  }
4987
5149
  async function loadNativeManifest(manifestPath) {
4988
5150
  try {
4989
- const source = await (0, import_promises4.readFile)(manifestPath, "utf8");
5151
+ const source = await (0, import_promises5.readFile)(manifestPath, "utf8");
4990
5152
  return parseNativeManifest(JSON.parse(source));
4991
5153
  } catch (error) {
4992
5154
  throw new Error(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openapi-sdkgen",
3
- "version": "10.1.0",
3
+ "version": "11.0.0",
4
4
  "description": "Generate application SDK source from OpenAPI documents.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",