app-builder-lib 26.13.1 → 26.14.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 (36) hide show
  1. package/helpers/dynamic-import.js +24 -7
  2. package/out/asar/asarFileChecker.js.map +1 -1
  3. package/out/node-module-collector/moduleManager.d.ts +6 -0
  4. package/out/node-module-collector/moduleManager.js +65 -55
  5. package/out/node-module-collector/moduleManager.js.map +1 -1
  6. package/out/node-module-collector/nodeModulesCollector.js +1 -1
  7. package/out/node-module-collector/nodeModulesCollector.js.map +1 -1
  8. package/out/node-module-collector/pnpmNodeModulesCollector.js +7 -5
  9. package/out/node-module-collector/pnpmNodeModulesCollector.js.map +1 -1
  10. package/out/options/macOptions.d.ts +29 -0
  11. package/out/options/macOptions.js.map +1 -1
  12. package/out/platformPackager.d.ts +1 -5
  13. package/out/platformPackager.js +9 -31
  14. package/out/platformPackager.js.map +1 -1
  15. package/out/targets/LinuxTargetHelper.d.ts +1 -1
  16. package/out/targets/LinuxTargetHelper.js.map +1 -1
  17. package/out/targets/appimage/appImageUtil.d.ts +2 -2
  18. package/out/targets/appimage/appImageUtil.js.map +1 -1
  19. package/out/targets/blockmap/blockmap.d.ts +16 -0
  20. package/out/targets/blockmap/blockmap.js +149 -0
  21. package/out/targets/blockmap/blockmap.js.map +1 -0
  22. package/out/targets/differentialUpdateInfoBuilder.js +3 -3
  23. package/out/targets/differentialUpdateInfoBuilder.js.map +1 -1
  24. package/out/targets/nsis/nsisValidation.js +5 -4
  25. package/out/targets/nsis/nsisValidation.js.map +1 -1
  26. package/out/toolsets/icons.d.ts +8 -0
  27. package/out/toolsets/icons.js +37 -0
  28. package/out/toolsets/icons.js.map +1 -0
  29. package/out/util/iconConverter.d.ts +25 -0
  30. package/out/util/iconConverter.js +260 -0
  31. package/out/util/iconConverter.js.map +1 -0
  32. package/out/version.d.ts +1 -1
  33. package/out/version.js +1 -1
  34. package/out/version.js.map +1 -1
  35. package/package.json +9 -8
  36. package/scheme.json +14 -0
@@ -28,7 +28,6 @@ function checkMakensisOutput(stdout, stderr) {
28
28
  * non-portable installers). Skipped for the intermediate uninstaller build.
29
29
  */
30
30
  async function verifyInstallerSize(outFile, defines) {
31
- var _a;
32
31
  let archiveSize = 0;
33
32
  for (const key of ["APP_64", "APP_32", "APP_ARM64"]) {
34
33
  const p = defines[key];
@@ -43,9 +42,11 @@ async function verifyInstallerSize(outFile, defines) {
43
42
  return;
44
43
  }
45
44
  const outStat = await (0, builder_util_1.statOrNull)(outFile);
46
- const actualSize = (_a = outStat === null || outStat === void 0 ? void 0 : outStat.size) !== null && _a !== void 0 ? _a : 0;
47
- if (actualSize < archiveSize) {
48
- throw new Error(`Generated installer (${actualSize} bytes) is smaller than the embedded archive(s) (${archiveSize} bytes) — ` +
45
+ if (outStat == null) {
46
+ throw new Error(`Generated installer was not created at "${outFile}" — output may be incomplete. Check available disk space and try again.`);
47
+ }
48
+ if (outStat.size < archiveSize) {
49
+ throw new Error(`Generated installer (${outStat.size} bytes) is smaller than the embedded archive(s) (${archiveSize} bytes) — ` +
49
50
  `output may be incomplete. Check available disk space and try again.`);
50
51
  }
51
52
  }
@@ -1 +1 @@
1
- {"version":3,"file":"nsisValidation.js","sourceRoot":"","sources":["../../../src/targets/nsis/nsisValidation.ts"],"names":[],"mappings":";;AAUA,kDASC;AAUD,kDAwBC;AArDD,+CAAoD;AAGpD;;;;;;GAMG;AACH,SAAgB,mBAAmB,CAAC,MAAc,EAAE,MAAc;IAChE,MAAM,UAAU,GAAG,MAAM;SACtB,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SAClB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;IAElC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,wBAAS,CAAC,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACpD,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,mBAAmB,CAAC,OAAe,EAAE,OAAgB;;IACzE,IAAI,WAAW,GAAG,CAAC,CAAA;IACnB,KAAK,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,CAAU,EAAE,CAAC;QAC7D,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;QACtB,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC1B,MAAM,CAAC,GAAG,MAAM,IAAA,yBAAU,EAAC,CAAC,CAAC,CAAA;YAC7B,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;gBACd,WAAW,IAAI,CAAC,CAAC,IAAI,CAAA;YACvB,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;QACtB,OAAM;IACR,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,IAAA,yBAAU,EAAC,OAAO,CAAC,CAAA;IACzC,MAAM,UAAU,GAAG,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,IAAI,mCAAI,CAAC,CAAA;IACrC,IAAI,UAAU,GAAG,WAAW,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,wBAAwB,UAAU,oDAAoD,WAAW,YAAY;YAC3G,qEAAqE,CACxE,CAAA;IACH,CAAC;AACH,CAAC","sourcesContent":["import { ExecError, statOrNull } from \"builder-util\"\nimport type { Defines } from \"./Defines\"\n\n/**\n * Validates makensis stdout/stderr after a zero-exit run.\n *\n * NSIS can emit \"Error:\" lines on stderr and still exit 0 (e.g. disk-full\n * scenarios where the OS silently drops writes). Also checks the\n * \"Install data: <written> / <expected> bytes\" progress line for truncation.\n */\nexport function checkMakensisOutput(stdout: string, stderr: string): void {\n const errorLines = stderr\n .split(\"\\n\")\n .map(l => l.trim())\n .filter(l => /^Error:/i.test(l))\n\n if (errorLines.length > 0) {\n throw new ExecError(\"makensis\", 0, stdout, stderr)\n }\n}\n\n/**\n * Verifies the generated installer is at least as large as the sum of the\n * embedded archive(s). An installer smaller than its payload is definitively\n * truncated regardless of the makensis exit code.\n *\n * Only runs when APP_64/APP_32/APP_ARM64 defines are present (i.e. normal,\n * non-portable installers). Skipped for the intermediate uninstaller build.\n */\nexport async function verifyInstallerSize(outFile: string, defines: Defines): Promise<void> {\n let archiveSize = 0\n for (const key of [\"APP_64\", \"APP_32\", \"APP_ARM64\"] as const) {\n const p = defines[key]\n if (typeof p === \"string\") {\n const s = await statOrNull(p)\n if (s != null) {\n archiveSize += s.size\n }\n }\n }\n\n if (archiveSize === 0) {\n return\n }\n\n const outStat = await statOrNull(outFile)\n const actualSize = outStat?.size ?? 0\n if (actualSize < archiveSize) {\n throw new Error(\n `Generated installer (${actualSize} bytes) is smaller than the embedded archive(s) (${archiveSize} bytes) — ` +\n `output may be incomplete. Check available disk space and try again.`\n )\n }\n}\n"]}
1
+ {"version":3,"file":"nsisValidation.js","sourceRoot":"","sources":["../../../src/targets/nsis/nsisValidation.ts"],"names":[],"mappings":";;AAUA,kDASC;AAUD,kDA0BC;AAvDD,+CAAoD;AAGpD;;;;;;GAMG;AACH,SAAgB,mBAAmB,CAAC,MAAc,EAAE,MAAc;IAChE,MAAM,UAAU,GAAG,MAAM;SACtB,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SAClB,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;IAElC,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,wBAAS,CAAC,UAAU,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IACpD,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,mBAAmB,CAAC,OAAe,EAAE,OAAgB;IACzE,IAAI,WAAW,GAAG,CAAC,CAAA;IACnB,KAAK,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,CAAU,EAAE,CAAC;QAC7D,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;QACtB,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YAC1B,MAAM,CAAC,GAAG,MAAM,IAAA,yBAAU,EAAC,CAAC,CAAC,CAAA;YAC7B,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;gBACd,WAAW,IAAI,CAAC,CAAC,IAAI,CAAA;YACvB,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,WAAW,KAAK,CAAC,EAAE,CAAC;QACtB,OAAM;IACR,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,IAAA,yBAAU,EAAC,OAAO,CAAC,CAAA;IACzC,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,2CAA2C,OAAO,yEAAyE,CAAC,CAAA;IAC9I,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,GAAG,WAAW,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CACb,wBAAwB,OAAO,CAAC,IAAI,oDAAoD,WAAW,YAAY;YAC7G,qEAAqE,CACxE,CAAA;IACH,CAAC;AACH,CAAC","sourcesContent":["import { ExecError, statOrNull } from \"builder-util\"\nimport type { Defines } from \"./Defines\"\n\n/**\n * Validates makensis stdout/stderr after a zero-exit run.\n *\n * NSIS can emit \"Error:\" lines on stderr and still exit 0 (e.g. disk-full\n * scenarios where the OS silently drops writes). Also checks the\n * \"Install data: <written> / <expected> bytes\" progress line for truncation.\n */\nexport function checkMakensisOutput(stdout: string, stderr: string): void {\n const errorLines = stderr\n .split(\"\\n\")\n .map(l => l.trim())\n .filter(l => /^Error:/i.test(l))\n\n if (errorLines.length > 0) {\n throw new ExecError(\"makensis\", 0, stdout, stderr)\n }\n}\n\n/**\n * Verifies the generated installer is at least as large as the sum of the\n * embedded archive(s). An installer smaller than its payload is definitively\n * truncated regardless of the makensis exit code.\n *\n * Only runs when APP_64/APP_32/APP_ARM64 defines are present (i.e. normal,\n * non-portable installers). Skipped for the intermediate uninstaller build.\n */\nexport async function verifyInstallerSize(outFile: string, defines: Defines): Promise<void> {\n let archiveSize = 0\n for (const key of [\"APP_64\", \"APP_32\", \"APP_ARM64\"] as const) {\n const p = defines[key]\n if (typeof p === \"string\") {\n const s = await statOrNull(p)\n if (s != null) {\n archiveSize += s.size\n }\n }\n }\n\n if (archiveSize === 0) {\n return\n }\n\n const outStat = await statOrNull(outFile)\n if (outStat == null) {\n throw new Error(`Generated installer was not created at \"${outFile}\" — output may be incomplete. Check available disk space and try again.`)\n }\n if (outStat.size < archiveSize) {\n throw new Error(\n `Generated installer (${outStat.size} bytes) is smaller than the embedded archive(s) (${archiveSize} bytes) — ` +\n `output may be incomplete. Check available disk space and try again.`\n )\n }\n}\n"]}
@@ -0,0 +1,8 @@
1
+ export declare function getIconsToolsetPath(): Promise<string>;
2
+ type IconConversionOptions = {
3
+ inputFile: string;
4
+ outputFormat: "icns" | "ico" | "set";
5
+ outDir: string;
6
+ };
7
+ export declare function runIconsTool({ inputFile, outputFormat, outDir }: IconConversionOptions): Promise<void>;
8
+ export {};
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getIconsToolsetPath = getIconsToolsetPath;
4
+ exports.runIconsTool = runIconsTool;
5
+ const builder_util_1 = require("builder-util");
6
+ const path = require("path");
7
+ const electronGet_1 = require("../util/electronGet");
8
+ const iconsToolsChecksums = {
9
+ "icons-bundle.tar.gz": "a96b7322c2562dfa53e675c343b17326b79709b793fe8023314584a7891d2b44",
10
+ };
11
+ async function getIconsToolsetPath() {
12
+ const envPath = await (0, builder_util_1.resolveEnvToolsetPath)("ELECTRON_BUILDER_ICONS_TOOLSET_DIR", "directory");
13
+ if (envPath != null) {
14
+ return envPath;
15
+ }
16
+ return (0, electronGet_1.downloadBuilderToolset)({
17
+ releaseName: "icons@1.0.1",
18
+ filenameWithExt: "icons-bundle.tar.gz",
19
+ checksums: iconsToolsChecksums,
20
+ githubOrgRepo: "electron-userland/electron-builder-binaries",
21
+ });
22
+ }
23
+ const VALID_OUTPUT_FORMATS = ["icns", "ico", "set"];
24
+ async function runIconsTool({ inputFile, outputFormat, outDir }) {
25
+ if (!VALID_OUTPUT_FORMATS.includes(outputFormat)) {
26
+ throw new builder_util_1.InvalidConfigurationError(`Invalid icon output format: ${outputFormat}`);
27
+ }
28
+ const safeInput = (0, builder_util_1.sanitizeDirPath)(inputFile);
29
+ const safeOutDir = (0, builder_util_1.sanitizeDirPath)(outDir);
30
+ const toolsetPath = await getIconsToolsetPath();
31
+ const scriptPath = path.resolve(toolsetPath, "icon-tool.js");
32
+ if (!(await (0, builder_util_1.exists)(scriptPath))) {
33
+ throw new builder_util_1.InvalidConfigurationError(`Icons tool not found at expected path: ${scriptPath}`);
34
+ }
35
+ await (0, builder_util_1.exec)(process.execPath, [scriptPath, `--input=${safeInput}`, `--format=${outputFormat}`, `--out=${safeOutDir}`], { shell: false });
36
+ }
37
+ //# sourceMappingURL=icons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"icons.js","sourceRoot":"","sources":["../../src/toolsets/icons.ts"],"names":[],"mappings":";;AAQA,kDAWC;AAUD,oCAaC;AA1CD,+CAA8G;AAC9G,6BAA4B;AAC5B,qDAA4D;AAE5D,MAAM,mBAAmB,GAAG;IAC1B,qBAAqB,EAAE,kEAAkE;CACjF,CAAA;AAEH,KAAK,UAAU,mBAAmB;IACvC,MAAM,OAAO,GAAG,MAAM,IAAA,oCAAqB,EAAC,oCAAoC,EAAE,WAAW,CAAC,CAAA;IAC9F,IAAI,OAAO,IAAI,IAAI,EAAE,CAAC;QACpB,OAAO,OAAO,CAAA;IAChB,CAAC;IACD,OAAO,IAAA,oCAAsB,EAAC;QAC5B,WAAW,EAAE,aAAa;QAC1B,eAAe,EAAE,qBAAqB;QACtC,SAAS,EAAE,mBAAmB;QAC9B,aAAa,EAAE,6CAA6C;KAC7D,CAAC,CAAA;AACJ,CAAC;AAQD,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAU,CAAA;AAErD,KAAK,UAAU,YAAY,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAyB;IAC3F,IAAI,CAAE,oBAA0C,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,wCAAyB,CAAC,+BAA+B,YAAY,EAAE,CAAC,CAAA;IACpF,CAAC;IACD,MAAM,SAAS,GAAG,IAAA,8BAAe,EAAC,SAAS,CAAC,CAAA;IAC5C,MAAM,UAAU,GAAG,IAAA,8BAAe,EAAC,MAAM,CAAC,CAAA;IAE1C,MAAM,WAAW,GAAG,MAAM,mBAAmB,EAAE,CAAA;IAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,cAAc,CAAC,CAAA;IAC5D,IAAI,CAAC,CAAC,MAAM,IAAA,qBAAM,EAAC,UAAU,CAAC,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,wCAAyB,CAAC,0CAA0C,UAAU,EAAE,CAAC,CAAA;IAC7F,CAAC;IACD,MAAM,IAAA,mBAAI,EAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,UAAU,EAAE,WAAW,SAAS,EAAE,EAAE,YAAY,YAAY,EAAE,EAAE,SAAS,UAAU,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;AACzI,CAAC","sourcesContent":["import { exec, exists, InvalidConfigurationError, resolveEnvToolsetPath, sanitizeDirPath } from \"builder-util\"\nimport * as path from \"path\"\nimport { downloadBuilderToolset } from \"../util/electronGet\"\n\nconst iconsToolsChecksums = {\n \"icons-bundle.tar.gz\": \"a96b7322c2562dfa53e675c343b17326b79709b793fe8023314584a7891d2b44\",\n} as const\n\nexport async function getIconsToolsetPath(): Promise<string> {\n const envPath = await resolveEnvToolsetPath(\"ELECTRON_BUILDER_ICONS_TOOLSET_DIR\", \"directory\")\n if (envPath != null) {\n return envPath\n }\n return downloadBuilderToolset({\n releaseName: \"icons@1.0.1\",\n filenameWithExt: \"icons-bundle.tar.gz\",\n checksums: iconsToolsChecksums,\n githubOrgRepo: \"electron-userland/electron-builder-binaries\",\n })\n}\n\ntype IconConversionOptions = {\n inputFile: string\n outputFormat: \"icns\" | \"ico\" | \"set\"\n outDir: string\n}\n\nconst VALID_OUTPUT_FORMATS = [\"icns\", \"ico\", \"set\"] as const\n\nexport async function runIconsTool({ inputFile, outputFormat, outDir }: IconConversionOptions): Promise<void> {\n if (!(VALID_OUTPUT_FORMATS as readonly string[]).includes(outputFormat)) {\n throw new InvalidConfigurationError(`Invalid icon output format: ${outputFormat}`)\n }\n const safeInput = sanitizeDirPath(inputFile)\n const safeOutDir = sanitizeDirPath(outDir)\n\n const toolsetPath = await getIconsToolsetPath()\n const scriptPath = path.resolve(toolsetPath, \"icon-tool.js\")\n if (!(await exists(scriptPath))) {\n throw new InvalidConfigurationError(`Icons tool not found at expected path: ${scriptPath}`)\n }\n await exec(process.execPath, [scriptPath, `--input=${safeInput}`, `--format=${outputFormat}`, `--out=${safeOutDir}`], { shell: false })\n}\n"]}
@@ -0,0 +1,25 @@
1
+ export interface IconInfo {
2
+ file: string;
3
+ size: number;
4
+ }
5
+ export type IconFormat = "icns" | "ico" | "set";
6
+ export interface IconConvertResult {
7
+ icons: IconInfo[];
8
+ isFallback: boolean;
9
+ error?: string;
10
+ errorCode?: string;
11
+ }
12
+ type IconConversionConfig = {
13
+ sources: string[];
14
+ fallbackSources: string[];
15
+ roots: string[];
16
+ format: IconFormat;
17
+ outDir: string;
18
+ };
19
+ export declare function convertIcon({ sources, fallbackSources, roots, format, outDir }: IconConversionConfig): Promise<IconConvertResult>;
20
+ export declare function getPngSize(filePath: string): Promise<{
21
+ width: number;
22
+ height: number;
23
+ }>;
24
+ export declare function buildSourceCandidates(sources: string[], format: IconFormat): string[];
25
+ export {};
@@ -0,0 +1,260 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertIcon = convertIcon;
4
+ exports.getPngSize = getPngSize;
5
+ exports.buildSourceCandidates = buildSourceCandidates;
6
+ const promises_1 = require("fs/promises");
7
+ const path = require("path");
8
+ const icons_1 = require("../toolsets/icons");
9
+ class IconConversionError extends Error {
10
+ constructor(message, errorCode) {
11
+ super(message);
12
+ this.errorCode = errorCode;
13
+ this.code = errorCode;
14
+ }
15
+ }
16
+ async function convertIcon({ sources, fallbackSources, roots, format, outDir }) {
17
+ const candidates = buildSourceCandidates(sources, format);
18
+ let icons = await doConvertIcon(candidates, roots, format, outDir);
19
+ let isFallback = false;
20
+ if (icons == null) {
21
+ const fallbackCandidates = buildSourceCandidates(fallbackSources, format);
22
+ icons = await doConvertIcon(fallbackCandidates, roots, format, outDir);
23
+ isFallback = true;
24
+ }
25
+ return { icons: icons !== null && icons !== void 0 ? icons : [], isFallback };
26
+ }
27
+ // ─── PNG dimension reader ─────────────────────────────────────────────────────
28
+ // Reads width/height from the PNG IHDR chunk at a fixed offset (no dependencies).
29
+ async function getPngSize(filePath) {
30
+ const buf = await (0, promises_1.readFile)(filePath);
31
+ // PNG signature = 8 bytes, IHDR chunk header = 8 bytes → width at 16, height at 20
32
+ if (buf.length < 24) {
33
+ return { width: 0, height: 0 };
34
+ }
35
+ return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20) };
36
+ }
37
+ // ─── ICO header parser ────────────────────────────────────────────────────────
38
+ async function getIcoMaxSize(filePath) {
39
+ const buf = await (0, promises_1.readFile)(filePath);
40
+ if (buf.length < 6 || buf[0] !== 0 || buf[1] !== 0 || buf[2] !== 1 || buf[3] !== 0) {
41
+ return null;
42
+ }
43
+ const count = buf.readUInt16LE(4);
44
+ let max = 0;
45
+ for (let i = 0; i < count; i++) {
46
+ const off = 6 + i * 16;
47
+ if (off + 2 > buf.length) {
48
+ break;
49
+ }
50
+ const w = buf[off] || 256;
51
+ const h = buf[off + 1] || 256;
52
+ if (w > max) {
53
+ max = w;
54
+ }
55
+ if (h > max) {
56
+ max = h;
57
+ }
58
+ }
59
+ return max;
60
+ }
61
+ // ─── Set output remapper ──────────────────────────────────────────────────────
62
+ // CLI outputs NxN.png; remap to icon_NxN.png to preserve existing naming convention.
63
+ async function remapSetOutput(outDir) {
64
+ const entries = await (0, promises_1.readdir)(outDir);
65
+ const result = [];
66
+ for (const name of entries) {
67
+ const match = name.match(/^(\d+)x\d+\.png$/);
68
+ if (!match) {
69
+ continue;
70
+ }
71
+ const size = parseInt(match[1], 10);
72
+ const oldPath = path.join(outDir, name);
73
+ const newPath = path.join(outDir, `icon_${size}x${size}.png`);
74
+ await (0, promises_1.rename)(oldPath, newPath);
75
+ result.push({ file: newPath, size });
76
+ }
77
+ return result.sort((a, b) => a.size - b.size);
78
+ }
79
+ // ─── CLI output collector ─────────────────────────────────────────────────────
80
+ async function collectCliOutput(outDir, format, sourceMaxSize = 0) {
81
+ if (format === "icns") {
82
+ return [{ file: path.join(outDir, "icon.icns"), size: sourceMaxSize }];
83
+ }
84
+ if (format === "ico") {
85
+ const icoFile = path.join(outDir, "icon.ico");
86
+ const size = await getIcoMaxSize(icoFile);
87
+ return [{ file: icoFile, size: size !== null && size !== void 0 ? size : sourceMaxSize }];
88
+ }
89
+ return remapSetOutput(outDir);
90
+ }
91
+ // ─── File resolution (matches go fileResolver.go + icon-converter.go) ────────
92
+ function imageHasExtension(name, format) {
93
+ return name.endsWith("." + format) || name.endsWith(".png") || name.endsWith(".ico") || name.endsWith(".svg") || name.endsWith(".icns");
94
+ }
95
+ function buildSourceCandidates(sources, format) {
96
+ const result = [];
97
+ for (const src of sources) {
98
+ if (imageHasExtension(src, format)) {
99
+ result.push(src);
100
+ }
101
+ else {
102
+ if (format !== "set") {
103
+ result.push(src + "." + format);
104
+ }
105
+ result.push(src);
106
+ result.push(src + ".png");
107
+ result.push(src + ".svg");
108
+ if (format !== "icns") {
109
+ result.push(src + ".icns");
110
+ if (format !== "ico") {
111
+ result.push(src + ".ico");
112
+ }
113
+ }
114
+ }
115
+ }
116
+ if (format !== "set") {
117
+ result.push("icon." + format);
118
+ }
119
+ for (const setName of ["icons", "icon"]) {
120
+ result.push(setName);
121
+ }
122
+ result.push("icon.png");
123
+ result.push("icon.svg");
124
+ if (format !== "icns") {
125
+ result.push("icon.icns");
126
+ if (format !== "ico") {
127
+ result.push("icon.ico");
128
+ }
129
+ }
130
+ // Deduplicate while preserving order so resolveSourceFile never stats the same path twice
131
+ return [...new Set(result)];
132
+ }
133
+ async function resolveSourceFile(candidates, roots) {
134
+ for (const candidate of candidates) {
135
+ const absPath = path.isAbsolute(candidate) ? path.normalize(candidate) : null;
136
+ const searchPaths = absPath ? [absPath] : roots.map(r => path.join(r, candidate));
137
+ for (const p of searchPaths) {
138
+ try {
139
+ const s = await (0, promises_1.stat)(p);
140
+ return { resolved: p, isDir: s.isDirectory() };
141
+ }
142
+ catch {
143
+ // not found, try next
144
+ }
145
+ }
146
+ }
147
+ return null;
148
+ }
149
+ // ─── Directory icon collection (go collect-icons.go) ────────────────────────
150
+ async function collectIconsFromDir(dir) {
151
+ const entries = await (0, promises_1.readdir)(dir);
152
+ const sizeMap = new Map();
153
+ let fallbackFile = null;
154
+ for (const name of entries) {
155
+ if (name === "icon.png" || name === "icon.PNG") {
156
+ fallbackFile = path.join(dir, name);
157
+ continue;
158
+ }
159
+ const match = name.match(/^(\d+)(?:x\d+)?\.png$/i);
160
+ if (!match) {
161
+ continue;
162
+ }
163
+ const size = parseInt(match[1], 10);
164
+ if (isNaN(size)) {
165
+ continue;
166
+ }
167
+ const filePath = path.join(dir, name);
168
+ const existing = sizeMap.get(size);
169
+ if (!existing || name.length < path.basename(existing.file).length) {
170
+ sizeMap.set(size, { file: filePath, size });
171
+ }
172
+ }
173
+ const icons = Array.from(sizeMap.values()).sort((a, b) => a.size - b.size);
174
+ return { icons, fallbackFile };
175
+ }
176
+ // ─── Main conversion logic ───────────────────────────────────────────────────
177
+ async function doConvertIcon(candidates, roots, format, outDir) {
178
+ const found = await resolveSourceFile(candidates, roots);
179
+ if (!found) {
180
+ return null;
181
+ }
182
+ const { resolved, isDir } = found;
183
+ // SVG source for set format: return the SVG directly — Linux targets place it in
184
+ // the freedesktop scalable/ dir. Never call getPngSize on an SVG file.
185
+ if (!isDir && resolved.endsWith(".svg") && format === "set") {
186
+ return [{ file: resolved, size: 1024 }];
187
+ }
188
+ const outExt = format === "set" ? ".png" : "." + format;
189
+ // If source already has the target extension and is not a directory, return it directly
190
+ if (!isDir && resolved.endsWith(outExt)) {
191
+ if (format === "icns") {
192
+ return [{ file: resolved, size: 0 }];
193
+ }
194
+ if (format === "ico") {
195
+ const size = await getIcoMaxSize(resolved);
196
+ if (size === null) {
197
+ throw new IconConversionError(`Icon is not a valid ICO file: ${resolved}`, "ERR_ICON_UNKNOWN_FORMAT");
198
+ }
199
+ if (size < 256) {
200
+ throw new IconConversionError(`Icon must be at least 256x256 pixels, provided: ${size}x${size}`, "ERR_ICON_TOO_SMALL");
201
+ }
202
+ return [{ file: resolved, size }];
203
+ }
204
+ // set: source is already a .png — return as-is with its dimensions
205
+ const { width, height } = await getPngSize(resolved);
206
+ return [{ file: resolved, size: Math.max(width, height) }];
207
+ }
208
+ if (isDir) {
209
+ const { icons, fallbackFile } = await collectIconsFromDir(resolved);
210
+ if (format === "set") {
211
+ if (icons.length > 0) {
212
+ return icons;
213
+ }
214
+ if (fallbackFile) {
215
+ return doConvertSingleFile(fallbackFile, format, outDir);
216
+ }
217
+ return null;
218
+ }
219
+ if (icons.length > 0) {
220
+ // Use largest available PNG from the directory as CLI input
221
+ const maxIcon = icons[icons.length - 1];
222
+ await (0, promises_1.mkdir)(outDir, { recursive: true });
223
+ await (0, icons_1.runIconsTool)({ inputFile: maxIcon.file, outputFormat: format, outDir });
224
+ return collectCliOutput(outDir, format, maxIcon.size);
225
+ }
226
+ if (fallbackFile) {
227
+ return doConvertSingleFile(fallbackFile, format, outDir);
228
+ }
229
+ return null;
230
+ }
231
+ // Single file: ICNS → ico or set — pass ICNS directly to CLI (CLI handles extraction)
232
+ if (resolved.endsWith(".icns") && format !== "icns") {
233
+ await (0, promises_1.mkdir)(outDir, { recursive: true });
234
+ await (0, icons_1.runIconsTool)({ inputFile: resolved, outputFormat: format, outDir });
235
+ return collectCliOutput(outDir, format);
236
+ }
237
+ return doConvertSingleFile(resolved, format, outDir);
238
+ }
239
+ async function doConvertSingleFile(sourceFile, format, outDir) {
240
+ const ext = path.extname(sourceFile).toLowerCase();
241
+ let maxSize;
242
+ if (ext === ".svg") {
243
+ maxSize = 1024; // CLI rasterizes SVG at 1024px
244
+ }
245
+ else {
246
+ const { width, height } = await getPngSize(sourceFile);
247
+ maxSize = Math.max(width, height);
248
+ if (maxSize === 0) {
249
+ return null;
250
+ }
251
+ const recommendedMin = format === "icns" ? 512 : 256;
252
+ if (maxSize < recommendedMin) {
253
+ throw new IconConversionError(`Icon must be at least ${recommendedMin}x${recommendedMin} pixels, provided: ${maxSize}x${maxSize}`, "ERR_ICON_TOO_SMALL");
254
+ }
255
+ }
256
+ await (0, promises_1.mkdir)(outDir, { recursive: true });
257
+ await (0, icons_1.runIconsTool)({ inputFile: sourceFile, outputFormat: format, outDir });
258
+ return collectCliOutput(outDir, format, maxSize);
259
+ }
260
+ //# sourceMappingURL=iconConverter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iconConverter.js","sourceRoot":"","sources":["../../src/util/iconConverter.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, readFile, readdir, rename, stat } from \"fs/promises\"\nimport * as path from \"path\"\nimport { runIconsTool } from \"../toolsets/icons\"\n\nclass IconConversionError extends Error {\n constructor(\n message: string,\n readonly errorCode: string\n ) {\n super(message)\n ;(this as NodeJS.ErrnoException).code = errorCode\n }\n}\n\nexport interface IconInfo {\n file: string\n size: number\n}\n\nexport type IconFormat = \"icns\" | \"ico\" | \"set\"\n\nexport interface IconConvertResult {\n icons: IconInfo[]\n isFallback: boolean\n error?: string\n errorCode?: string\n}\n\n// ─── Public API ──────────────────────────────────────────────────────────────\n\ntype IconConversionConfig = {\n sources: string[]\n fallbackSources: string[]\n roots: string[]\n format: IconFormat\n outDir: string\n}\n\nexport async function convertIcon({ sources, fallbackSources, roots, format, outDir }: IconConversionConfig): Promise<IconConvertResult> {\n const candidates = buildSourceCandidates(sources, format)\n\n let icons = await doConvertIcon(candidates, roots, format, outDir)\n\n let isFallback = false\n if (icons == null) {\n const fallbackCandidates = buildSourceCandidates(fallbackSources, format)\n icons = await doConvertIcon(fallbackCandidates, roots, format, outDir)\n isFallback = true\n }\n\n return { icons: icons ?? [], isFallback }\n}\n\n// ─── PNG dimension reader ─────────────────────────────────────────────────────\n// Reads width/height from the PNG IHDR chunk at a fixed offset (no dependencies).\n\nexport async function getPngSize(filePath: string): Promise<{ width: number; height: number }> {\n const buf = await readFile(filePath)\n // PNG signature = 8 bytes, IHDR chunk header = 8 bytes → width at 16, height at 20\n if (buf.length < 24) {\n return { width: 0, height: 0 }\n }\n return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20) }\n}\n\n// ─── ICO header parser ────────────────────────────────────────────────────────\n\nasync function getIcoMaxSize(filePath: string): Promise<number | null> {\n const buf = await readFile(filePath)\n if (buf.length < 6 || buf[0] !== 0 || buf[1] !== 0 || buf[2] !== 1 || buf[3] !== 0) {\n return null\n }\n const count = buf.readUInt16LE(4)\n let max = 0\n for (let i = 0; i < count; i++) {\n const off = 6 + i * 16\n if (off + 2 > buf.length) {\n break\n }\n const w = buf[off] || 256\n const h = buf[off + 1] || 256\n if (w > max) {\n max = w\n }\n if (h > max) {\n max = h\n }\n }\n return max\n}\n\n// ─── Set output remapper ──────────────────────────────────────────────────────\n// CLI outputs NxN.png; remap to icon_NxN.png to preserve existing naming convention.\n\nasync function remapSetOutput(outDir: string): Promise<IconInfo[]> {\n const entries = await readdir(outDir)\n const result: IconInfo[] = []\n for (const name of entries) {\n const match = name.match(/^(\\d+)x\\d+\\.png$/)\n if (!match) {\n continue\n }\n const size = parseInt(match[1], 10)\n const oldPath = path.join(outDir, name)\n const newPath = path.join(outDir, `icon_${size}x${size}.png`)\n await rename(oldPath, newPath)\n result.push({ file: newPath, size })\n }\n return result.sort((a, b) => a.size - b.size)\n}\n\n// ─── CLI output collector ─────────────────────────────────────────────────────\n\nasync function collectCliOutput(outDir: string, format: IconFormat, sourceMaxSize = 0): Promise<IconInfo[]> {\n if (format === \"icns\") {\n return [{ file: path.join(outDir, \"icon.icns\"), size: sourceMaxSize }]\n }\n if (format === \"ico\") {\n const icoFile = path.join(outDir, \"icon.ico\")\n const size = await getIcoMaxSize(icoFile)\n return [{ file: icoFile, size: size ?? sourceMaxSize }]\n }\n return remapSetOutput(outDir)\n}\n\n// ─── File resolution (matches go fileResolver.go + icon-converter.go) ────────\n\nfunction imageHasExtension(name: string, format: string): boolean {\n return name.endsWith(\".\" + format) || name.endsWith(\".png\") || name.endsWith(\".ico\") || name.endsWith(\".svg\") || name.endsWith(\".icns\")\n}\n\nexport function buildSourceCandidates(sources: string[], format: IconFormat): string[] {\n const result: string[] = []\n for (const src of sources) {\n if (imageHasExtension(src, format)) {\n result.push(src)\n } else {\n if (format !== \"set\") {\n result.push(src + \".\" + format)\n }\n result.push(src)\n result.push(src + \".png\")\n result.push(src + \".svg\")\n if (format !== \"icns\") {\n result.push(src + \".icns\")\n if (format !== \"ico\") {\n result.push(src + \".ico\")\n }\n }\n }\n }\n if (format !== \"set\") {\n result.push(\"icon.\" + format)\n }\n for (const setName of [\"icons\", \"icon\"]) {\n result.push(setName)\n }\n result.push(\"icon.png\")\n result.push(\"icon.svg\")\n if (format !== \"icns\") {\n result.push(\"icon.icns\")\n if (format !== \"ico\") {\n result.push(\"icon.ico\")\n }\n }\n // Deduplicate while preserving order so resolveSourceFile never stats the same path twice\n return [...new Set(result)]\n}\n\nasync function resolveSourceFile(candidates: string[], roots: string[]): Promise<{ resolved: string; isDir: boolean } | null> {\n for (const candidate of candidates) {\n const absPath = path.isAbsolute(candidate) ? path.normalize(candidate) : null\n const searchPaths = absPath ? [absPath] : roots.map(r => path.join(r, candidate))\n\n for (const p of searchPaths) {\n try {\n const s = await stat(p)\n return { resolved: p, isDir: s.isDirectory() }\n } catch {\n // not found, try next\n }\n }\n }\n return null\n}\n\n// ─── Directory icon collection (go collect-icons.go) ────────────────────────\n\nasync function collectIconsFromDir(dir: string): Promise<{ icons: IconInfo[]; fallbackFile: string | null }> {\n const entries = await readdir(dir)\n const sizeMap = new Map<number, IconInfo>()\n let fallbackFile: string | null = null\n\n for (const name of entries) {\n if (name === \"icon.png\" || name === \"icon.PNG\") {\n fallbackFile = path.join(dir, name)\n continue\n }\n const match = name.match(/^(\\d+)(?:x\\d+)?\\.png$/i)\n if (!match) {\n continue\n }\n const size = parseInt(match[1], 10)\n if (isNaN(size)) {\n continue\n }\n const filePath = path.join(dir, name)\n const existing = sizeMap.get(size)\n if (!existing || name.length < path.basename(existing.file).length) {\n sizeMap.set(size, { file: filePath, size })\n }\n }\n\n const icons = Array.from(sizeMap.values()).sort((a, b) => a.size - b.size)\n return { icons, fallbackFile }\n}\n\n// ─── Main conversion logic ───────────────────────────────────────────────────\n\nasync function doConvertIcon(candidates: string[], roots: string[], format: IconFormat, outDir: string): Promise<IconInfo[] | null> {\n const found = await resolveSourceFile(candidates, roots)\n if (!found) {\n return null\n }\n\n const { resolved, isDir } = found\n\n // SVG source for set format: return the SVG directly — Linux targets place it in\n // the freedesktop scalable/ dir. Never call getPngSize on an SVG file.\n if (!isDir && resolved.endsWith(\".svg\") && format === \"set\") {\n return [{ file: resolved, size: 1024 }]\n }\n\n const outExt = format === \"set\" ? \".png\" : \".\" + format\n\n // If source already has the target extension and is not a directory, return it directly\n if (!isDir && resolved.endsWith(outExt)) {\n if (format === \"icns\") {\n return [{ file: resolved, size: 0 }]\n }\n if (format === \"ico\") {\n const size = await getIcoMaxSize(resolved)\n if (size === null) {\n throw new IconConversionError(`Icon is not a valid ICO file: ${resolved}`, \"ERR_ICON_UNKNOWN_FORMAT\")\n }\n if (size < 256) {\n throw new IconConversionError(`Icon must be at least 256x256 pixels, provided: ${size}x${size}`, \"ERR_ICON_TOO_SMALL\")\n }\n return [{ file: resolved, size }]\n }\n // set: source is already a .png — return as-is with its dimensions\n const { width, height } = await getPngSize(resolved)\n return [{ file: resolved, size: Math.max(width, height) }]\n }\n\n if (isDir) {\n const { icons, fallbackFile } = await collectIconsFromDir(resolved)\n\n if (format === \"set\") {\n if (icons.length > 0) {\n return icons\n }\n if (fallbackFile) {\n return doConvertSingleFile(fallbackFile, format, outDir)\n }\n return null\n }\n\n if (icons.length > 0) {\n // Use largest available PNG from the directory as CLI input\n const maxIcon = icons[icons.length - 1]\n await mkdir(outDir, { recursive: true })\n await runIconsTool({ inputFile: maxIcon.file, outputFormat: format, outDir })\n return collectCliOutput(outDir, format, maxIcon.size)\n }\n\n if (fallbackFile) {\n return doConvertSingleFile(fallbackFile, format, outDir)\n }\n return null\n }\n\n // Single file: ICNS → ico or set — pass ICNS directly to CLI (CLI handles extraction)\n if (resolved.endsWith(\".icns\") && format !== \"icns\") {\n await mkdir(outDir, { recursive: true })\n await runIconsTool({ inputFile: resolved, outputFormat: format, outDir })\n return collectCliOutput(outDir, format)\n }\n\n return doConvertSingleFile(resolved, format, outDir)\n}\n\nasync function doConvertSingleFile(sourceFile: string, format: IconFormat, outDir: string): Promise<IconInfo[] | null> {\n const ext = path.extname(sourceFile).toLowerCase()\n let maxSize: number\n\n if (ext === \".svg\") {\n maxSize = 1024 // CLI rasterizes SVG at 1024px\n } else {\n const { width, height } = await getPngSize(sourceFile)\n maxSize = Math.max(width, height)\n if (maxSize === 0) {\n return null\n }\n const recommendedMin = format === \"icns\" ? 512 : 256\n if (maxSize < recommendedMin) {\n throw new IconConversionError(`Icon must be at least ${recommendedMin}x${recommendedMin} pixels, provided: ${maxSize}x${maxSize}`, \"ERR_ICON_TOO_SMALL\")\n }\n }\n\n await mkdir(outDir, { recursive: true })\n await runIconsTool({ inputFile: sourceFile, outputFormat: format, outDir })\n return collectCliOutput(outDir, format, maxSize)\n}\n"]}
package/out/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const PACKAGE_VERSION = "26.13.1";
1
+ export declare const PACKAGE_VERSION = "26.14.0";
package/out/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PACKAGE_VERSION = void 0;
4
- exports.PACKAGE_VERSION = "26.13.1";
4
+ exports.PACKAGE_VERSION = "26.14.0";
5
5
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":";;;AAAa,QAAA,eAAe,GAAG,SAAS,CAAA","sourcesContent":["export const PACKAGE_VERSION = \"26.13.1\"\n"]}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":";;;AAAa,QAAA,eAAe,GAAG,SAAS,CAAA","sourcesContent":["export const PACKAGE_VERSION = \"26.14.0\"\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "app-builder-lib",
3
3
  "description": "electron-builder lib",
4
- "version": "26.13.1",
4
+ "version": "26.14.0",
5
5
  "main": "out/index.js",
6
6
  "files": [
7
7
  "out",
@@ -55,6 +55,7 @@
55
55
  "@electron/rebuild": "4.0.3",
56
56
  "@electron/universal": "2.0.3",
57
57
  "@malept/flatpak-bundler": "^0.4.0",
58
+ "@noble/hashes": "^2.2.0",
58
59
  "@types/fs-extra": "9.0.13",
59
60
  "@peculiar/webcrypto": "^1.7.1",
60
61
  "ajv": "^8.18.0",
@@ -84,9 +85,9 @@
84
85
  "tiny-async-pool": "1.3.0",
85
86
  "unzipper": "^0.12.3",
86
87
  "which": "^5.0.0",
87
- "builder-util": "26.13.0",
88
- "electron-publish": "26.13.0",
89
- "builder-util-runtime": "9.6.3"
88
+ "electron-publish": "26.14.0",
89
+ "builder-util-runtime": "9.6.3",
90
+ "builder-util": "26.14.0"
90
91
  },
91
92
  "///": "babel in devDependencies for proton tests",
92
93
  "devDependencies": {
@@ -120,12 +121,12 @@
120
121
  "@types/unzipper": "^0.10.11",
121
122
  "@types/which": "^3.0.4",
122
123
  "toml": "^3.0.0",
123
- "dmg-builder": "26.13.1",
124
- "electron-builder-squirrel-windows": "26.13.1"
124
+ "dmg-builder": "26.14.0",
125
+ "electron-builder-squirrel-windows": "26.14.0"
125
126
  },
126
127
  "peerDependencies": {
127
- "dmg-builder": "26.13.1",
128
- "electron-builder-squirrel-windows": "26.13.1"
128
+ "dmg-builder": "26.14.0",
129
+ "electron-builder-squirrel-windows": "26.14.0"
129
130
  },
130
131
  "//": "electron-builder-squirrel-windows and dmg-builder added as dev dep for tests (as otherwise `require` doesn't work using Yarn 2)",
131
132
  "typings": "./out/index.d.ts"
package/scheme.json CHANGED
@@ -1010,6 +1010,20 @@
1010
1010
  "description": "Whether to create internet-enabled disk image (when it is downloaded using a browser it will automatically decompress the image, put the application on the desktop, unmount and remove the disk image file).",
1011
1011
  "type": "boolean"
1012
1012
  },
1013
+ "license": {
1014
+ "anyOf": [
1015
+ {
1016
+ "$ref": "#/definitions/Record<string,string>"
1017
+ },
1018
+ {
1019
+ "type": [
1020
+ "null",
1021
+ "string"
1022
+ ]
1023
+ }
1024
+ ],
1025
+ "description": "License agreement to display when the DMG is mounted.\n\nAccepts a single file path (`.rtf`, `.txt`, or `.html`) used as the English/default license,\nor a language-code → file-path map for multi-language builds.\n\nWhen set, this takes precedence over the file-naming convention\n(`license_en.txt`, etc. in the build resources directory).\nPaths are resolved relative to the build resources directory or project root."
1026
+ },
1013
1027
  "publish": {
1014
1028
  "anyOf": [
1015
1029
  {