| ... | ... | @@ -18,7 +18,6 @@ import { resolveCatalogDependencies } from "./lib/resolve-catalog.ts"; |
| 18 | 18 | import * as NodeRuntime from "@effect/platform-node/NodeRuntime"; |
| 19 | 19 | import * as NodeServices from "@effect/platform-node/NodeServices"; |
| 20 | 20 | import * as Config from "effect/Config"; |
| 21 | | import * as Data from "effect/Data"; |
| 22 | 21 | import * as Effect from "effect/Effect"; |
| 23 | 22 | import * as FileSystem from "effect/FileSystem"; |
| 24 | 23 | import * as Layer from "effect/Layer"; |
| ... | ... | @@ -126,19 +125,238 @@ const getDefaultArch = Effect.fn("getDefaultArch")(function* (platform: typeof B |
| 126 | 125 | return yield* getDefaultBuildArch(platform, config); |
| 127 | 126 | }); |
| 128 | 127 | |
| 129 | | export class BuildScriptError extends Data.TaggedError("BuildScriptError")<{ |
| 130 | | readonly message: string; |
| 131 | | readonly cause?: unknown; |
| 132 | | }> { |
| 133 | | static fromMacPasskeySigningConfiguration( |
| 128 | export class MacPasskeySigningConfigurationResolutionError extends Schema.TaggedErrorClass<MacPasskeySigningConfigurationResolutionError>()( |
| 129 | "MacPasskeySigningConfigurationResolutionError", |
| 130 | { |
| 131 | cause: Schema.Defect(), |
| 132 | }, |
| 133 | ) { |
| 134 | static fromCause( |
| 134 | 135 | cause: unknown, |
| 135 | | ): MacPasskeySigningConfigurationError | BuildScriptError { |
| 136 | ): MacPasskeySigningConfigurationError | MacPasskeySigningConfigurationResolutionError { |
| 136 | 137 | return isMacPasskeySigningConfigurationError(cause) |
| 137 | 138 | ? cause |
| 138 | | : new BuildScriptError({ |
| 139 | | message: "Failed to resolve macOS passkey signing configuration.", |
| 140 | | cause, |
| 141 | | }); |
| 139 | : new MacPasskeySigningConfigurationResolutionError({ cause }); |
| 140 | } |
| 141 | |
| 142 | override get message(): string { |
| 143 | return "Failed to resolve macOS passkey signing configuration."; |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | export class ClerkPasskeyNativePackageMissingError extends Schema.TaggedErrorClass<ClerkPasskeyNativePackageMissingError>()( |
| 148 | "ClerkPasskeyNativePackageMissingError", |
| 149 | { |
| 150 | packageName: Schema.String, |
| 151 | binaryFileName: Schema.String, |
| 152 | packageEntryPath: Schema.String, |
| 153 | platform: BuildPlatform, |
| 154 | arch: BuildArch, |
| 155 | cause: Schema.Defect(), |
| 156 | }, |
| 157 | ) { |
| 158 | override get message(): string { |
| 159 | return `Clerk passkey native package is missing: ${this.packageName}`; |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | export class UnsupportedHostBuildPlatformError extends Schema.TaggedErrorClass<UnsupportedHostBuildPlatformError>()( |
| 164 | "UnsupportedHostBuildPlatformError", |
| 165 | { |
| 166 | hostPlatform: Schema.String, |
| 167 | }, |
| 168 | ) { |
| 169 | override get message(): string { |
| 170 | return `Unsupported host platform '${this.hostPlatform}'.`; |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | const InvalidMockUpdateServerPortReason = Schema.Literals([ |
| 175 | "not-numeric", |
| 176 | "not-integer", |
| 177 | "out-of-range", |
| 178 | ]); |
| 179 | |
| 180 | export class InvalidMockUpdateServerPortError extends Schema.TaggedErrorClass<InvalidMockUpdateServerPortError>()( |
| 181 | "InvalidMockUpdateServerPortError", |
| 182 | { |
| 183 | reason: InvalidMockUpdateServerPortReason, |
| 184 | inputLength: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0)), |
| 185 | cause: Schema.Defect(), |
| 186 | }, |
| 187 | ) { |
| 188 | override get message(): string { |
| 189 | return "Invalid mock update server port."; |
| 190 | } |
| 191 | |
| 192 | static fromConfigValue(configuredPort: string, cause: unknown) { |
| 193 | return new InvalidMockUpdateServerPortError({ |
| 194 | reason: invalidMockUpdateServerPortReason(configuredPort), |
| 195 | inputLength: configuredPort.length, |
| 196 | cause, |
| 197 | }); |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | export class BuildCommandFailedError extends Schema.TaggedErrorClass<BuildCommandFailedError>()( |
| 202 | "BuildCommandFailedError", |
| 203 | { |
| 204 | command: Schema.String, |
| 205 | exitCode: Schema.Int, |
| 206 | stdoutTail: Schema.optionalKey(Schema.String), |
| 207 | stderrTail: Schema.optionalKey(Schema.String), |
| 208 | }, |
| 209 | ) { |
| 210 | override get message(): string { |
| 211 | const outputSections = [ |
| 212 | `Command: ${this.command}`, |
| 213 | formatOutputSection("stdout", this.stdoutTail ?? ""), |
| 214 | formatOutputSection("stderr", this.stderrTail ?? ""), |
| 215 | ].filter((section): section is string => section !== undefined); |
| 216 | const outputSuffix = outputSections.length > 0 ? `\n\n${outputSections.join("\n\n")}` : ""; |
| 217 | return `Command exited with non-zero exit code (${this.exitCode})${outputSuffix}`; |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | const desktopIconPlatformNames = { |
| 222 | mac: "macOS", |
| 223 | linux: "Linux", |
| 224 | win: "Windows", |
| 225 | } satisfies Record<typeof BuildPlatform.Type, string>; |
| 226 | |
| 227 | export class DesktopIconSourceMissingError extends Schema.TaggedErrorClass<DesktopIconSourceMissingError>()( |
| 228 | "DesktopIconSourceMissingError", |
| 229 | { |
| 230 | platform: BuildPlatform, |
| 231 | sourcePath: Schema.String, |
| 232 | }, |
| 233 | ) { |
| 234 | override get message(): string { |
| 235 | return `Desktop ${desktopIconPlatformNames[this.platform]} icon source is missing at ${this.sourcePath}`; |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | export class BundledClientAssetsMissingError extends Schema.TaggedErrorClass<BundledClientAssetsMissingError>()( |
| 240 | "BundledClientAssetsMissingError", |
| 241 | { |
| 242 | indexPath: Schema.String, |
| 243 | missingFiles: Schema.Array(Schema.String), |
| 244 | }, |
| 245 | ) { |
| 246 | override get message(): string { |
| 247 | const preview = this.missingFiles.slice(0, 6).join(", "); |
| 248 | const suffix = this.missingFiles.length > 6 ? ` (+${this.missingFiles.length - 6} more)` : ""; |
| 249 | return `Bundled client references missing files in ${this.indexPath}: ${preview}${suffix}. Rebuild web/server artifacts.`; |
| 250 | } |
| 251 | } |
| 252 | |
| 253 | export class UnsupportedDesktopBuildPlatformError extends Schema.TaggedErrorClass<UnsupportedDesktopBuildPlatformError>()( |
| 254 | "UnsupportedDesktopBuildPlatformError", |
| 255 | { |
| 256 | platform: Schema.String, |
| 257 | }, |
| 258 | ) { |
| 259 | override get message(): string { |
| 260 | return `Unsupported platform '${this.platform}'.`; |
| 261 | } |
| 262 | } |
| 263 | |
| 264 | const dependencyResolutionDescriptions = { |
| 265 | "server-production": "production dependencies", |
| 266 | "workspace-overrides": "overrides", |
| 267 | "desktop-runtime": "desktop runtime dependencies", |
| 268 | } as const; |
| 269 | const DependencyResolutionKind = Schema.Literals([ |
| 270 | "server-production", |
| 271 | "workspace-overrides", |
| 272 | "desktop-runtime", |
| 273 | ]); |
| 274 | |
| 275 | export class DesktopBuildDependencyResolutionError extends Schema.TaggedErrorClass<DesktopBuildDependencyResolutionError>()( |
| 276 | "DesktopBuildDependencyResolutionError", |
| 277 | { |
| 278 | kind: DependencyResolutionKind, |
| 279 | manifestPath: Schema.String, |
| 280 | cause: Schema.Defect(), |
| 281 | }, |
| 282 | ) { |
| 283 | override get message(): string { |
| 284 | return `Could not resolve ${dependencyResolutionDescriptions[this.kind]} from ${this.manifestPath}.`; |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | export class MissingServerProductionDependenciesError extends Schema.TaggedErrorClass<MissingServerProductionDependenciesError>()( |
| 289 | "MissingServerProductionDependenciesError", |
| 290 | { |
| 291 | manifestPath: Schema.String, |
| 292 | }, |
| 293 | ) { |
| 294 | override get message(): string { |
| 295 | return `Could not resolve production dependencies from ${this.manifestPath}.`; |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | const DesktopBuildInputArtifact = Schema.Literals([ |
| 300 | "desktop-dist", |
| 301 | "desktop-resources", |
| 302 | "server-dist", |
| 303 | "bundled-server-client", |
| 304 | ]); |
| 305 | type DesktopBuildInputArtifact = typeof DesktopBuildInputArtifact.Type; |
| 306 | const desktopBuildInputArtifactNames = { |
| 307 | "desktop-dist": "desktopDist", |
| 308 | "desktop-resources": "desktopResources", |
| 309 | "server-dist": "serverDist", |
| 310 | "bundled-server-client": "bundled server client", |
| 311 | } satisfies Record<DesktopBuildInputArtifact, string>; |
| 312 | |
| 313 | export class MissingDesktopBuildInputError extends Schema.TaggedErrorClass<MissingDesktopBuildInputError>()( |
| 314 | "MissingDesktopBuildInputError", |
| 315 | { |
| 316 | artifact: DesktopBuildInputArtifact, |
| 317 | artifactPath: Schema.String, |
| 318 | buildCommand: Schema.Literal("vp run build:desktop"), |
| 319 | }, |
| 320 | ) { |
| 321 | override get message(): string { |
| 322 | return `Missing ${desktopBuildInputArtifactNames[this.artifact]} at ${this.artifactPath}. Run '${this.buildCommand}' first.`; |
| 323 | } |
| 324 | } |
| 325 | |
| 326 | export class MacProvisioningProfileNotFoundError extends Schema.TaggedErrorClass<MacProvisioningProfileNotFoundError>()( |
| 327 | "MacProvisioningProfileNotFoundError", |
| 328 | { |
| 329 | provisioningProfilePath: Schema.String, |
| 330 | }, |
| 331 | ) { |
| 332 | override get message(): string { |
| 333 | return `macOS provisioning profile not found: ${this.provisioningProfilePath}`; |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | export class DesktopBuildDistDirectoryMissingError extends Schema.TaggedErrorClass<DesktopBuildDistDirectoryMissingError>()( |
| 338 | "DesktopBuildDistDirectoryMissingError", |
| 339 | { |
| 340 | distPath: Schema.String, |
| 341 | platform: BuildPlatform, |
| 342 | arch: BuildArch, |
| 343 | }, |
| 344 | ) { |
| 345 | override get message(): string { |
| 346 | return `Build completed but dist directory was not found at ${this.distPath}`; |
| 347 | } |
| 348 | } |
| 349 | |
| 350 | export class DesktopBuildNoArtifactsProducedError extends Schema.TaggedErrorClass<DesktopBuildNoArtifactsProducedError>()( |
| 351 | "DesktopBuildNoArtifactsProducedError", |
| 352 | { |
| 353 | distPath: Schema.String, |
| 354 | platform: BuildPlatform, |
| 355 | arch: BuildArch, |
| 356 | }, |
| 357 | ) { |
| 358 | override get message(): string { |
| 359 | return `Build completed but no files were produced in ${this.distPath}`; |
| 142 | 360 | } |
| 143 | 361 | } |
| 144 | 362 | |
| ... | ... | @@ -616,8 +834,12 @@ const stageClerkPasskeyNativeBinaries = Effect.fn("stageClerkPasskeyNativeBinari |
| 616 | 834 | const sourcePath = yield* Effect.try({ |
| 617 | 835 | try: () => packageRequire.resolve(artifact.packageName), |
| 618 | 836 | catch: (cause) => |
| 619 | | new BuildScriptError({ |
| 620 | | message: `Clerk passkey native package is missing: ${artifact.packageName}`, |
| 837 | new ClerkPasskeyNativePackageMissingError({ |
| 838 | packageName: artifact.packageName, |
| 839 | binaryFileName: artifact.binaryFileName, |
| 840 | packageEntryPath, |
| 841 | platform, |
| 842 | arch, |
| 621 | 843 | cause, |
| 622 | 844 | }), |
| 623 | 845 | }); |
| ... | ... | @@ -691,6 +913,18 @@ const MockUpdateServerPortSchema = Schema.NumberFromString.check( |
| 691 | 913 | ); |
| 692 | 914 | const decodeMockUpdateServerPort = Schema.decodeUnknownEffect(MockUpdateServerPortSchema); |
| 693 | 915 | |
| 916 | function invalidMockUpdateServerPortReason( |
| 917 | configuredPort: string, |
| 918 | ): typeof InvalidMockUpdateServerPortReason.Type { |
| 919 | const parsed = Number(configuredPort); |
| 920 | if (!Number.isFinite(parsed)) return "not-numeric"; |
| 921 | if (!Number.isInteger(parsed)) return "not-integer"; |
| 922 | if (parsed < 1 || parsed > 65535) return "out-of-range"; |
| 923 | // This mapper is only called after schema decoding failed. An otherwise |
| 924 | // valid integer therefore used a representation the decoder did not accept. |
| 925 | return "not-numeric"; |
| 926 | } |
| 927 | |
| 694 | 928 | const resolveBooleanFlag = (flag: Option.Option<boolean>, envValue: boolean) => |
| 695 | 929 | Option.getOrElse(flag, () => envValue); |
| 696 | 930 | const mergeOptions = <A>(a: Option.Option<A>, b: Option.Option<A>, defaultValue: A) => |
| ... | ... | @@ -722,9 +956,7 @@ export const resolveBuildOptions = Effect.fn("resolveBuildOptions")(function* ( |
| 722 | 956 | ); |
| 723 | 957 | |
| 724 | 958 | if (!platform) { |
| 725 | | return yield* new BuildScriptError({ |
| 726 | | message: `Unsupported host platform '${hostPlatform}'.`, |
| 727 | | }); |
| 959 | return yield* new UnsupportedHostBuildPlatformError({ hostPlatform }); |
| 728 | 960 | } |
| 729 | 961 | |
| 730 | 962 | const target = mergeOptions(input.target, env.target, PLATFORM_CONFIG[platform].defaultTarget); |
| ... | ... | @@ -745,17 +977,16 @@ export const resolveBuildOptions = Effect.fn("resolveBuildOptions")(function* ( |
| 745 | 977 | const verbose = resolveBooleanFlag(input.verbose, env.verbose); |
| 746 | 978 | |
| 747 | 979 | const mockUpdates = resolveBooleanFlag(input.mockUpdates, env.mockUpdates); |
| 980 | const configuredMockUpdateServerPort = Option.getOrUndefined(env.mockUpdateServerPort); |
| 748 | 981 | const mockUpdateServerPort = |
| 749 | 982 | Option.getOrUndefined(input.mockUpdateServerPort) ?? |
| 750 | | (yield* resolveMockUpdateServerPort(Option.getOrUndefined(env.mockUpdateServerPort)).pipe( |
| 751 | | Effect.mapError( |
| 752 | | (cause) => |
| 753 | | new BuildScriptError({ |
| 754 | | message: "Invalid mock update server port.", |
| 755 | | cause, |
| 756 | | }), |
| 757 | | ), |
| 758 | | )); |
| 983 | (configuredMockUpdateServerPort === undefined |
| 984 | ? undefined |
| 985 | : yield* resolveMockUpdateServerPort(configuredMockUpdateServerPort).pipe( |
| 986 | Effect.mapError((cause) => |
| 987 | InvalidMockUpdateServerPortError.fromConfigValue(configuredMockUpdateServerPort, cause), |
| 988 | ), |
| 989 | )); |
| 759 | 990 | |
| 760 | 991 | return { |
| 761 | 992 | platform, |
| ... | ... | @@ -775,7 +1006,7 @@ export const resolveBuildOptions = Effect.fn("resolveBuildOptions")(function* ( |
| 775 | 1006 | const runCommand = Effect.fn("runCommand")(function* ( |
| 776 | 1007 | command: ChildProcess.Command, |
| 777 | 1008 | options: { |
| 778 | | readonly label?: string; |
| 1009 | readonly label: string; |
| 779 | 1010 | readonly verbose: boolean; |
| 780 | 1011 | }, |
| 781 | 1012 | ) { |
| ... | ... | @@ -791,14 +1022,11 @@ const runCommand = Effect.fn("runCommand")(function* ( |
| 791 | 1022 | ); |
| 792 | 1023 | |
| 793 | 1024 | if (exitCode !== 0) { |
| 794 | | const outputSections = [ |
| 795 | | options.label ? `Command: ${options.label}` : undefined, |
| 796 | | formatOutputSection("stdout", stdout), |
| 797 | | formatOutputSection("stderr", stderr), |
| 798 | | ].filter((section): section is string => section !== undefined); |
| 799 | | const outputSuffix = outputSections.length > 0 ? `\n\n${outputSections.join("\n\n")}` : ""; |
| 800 | | return yield* new BuildScriptError({ |
| 801 | | message: `Command exited with non-zero exit code (${exitCode})${outputSuffix}`, |
| 1025 | return yield* new BuildCommandFailedError({ |
| 1026 | command: options.label, |
| 1027 | exitCode, |
| 1028 | ...(stdout.trim() ? { stdoutTail: stdout } : {}), |
| 1029 | ...(stderr.trim() ? { stderrTail: stderr } : {}), |
| 802 | 1030 | }); |
| 803 | 1031 | } |
| 804 | 1032 | }); |
| ... | ... | @@ -845,8 +1073,9 @@ function stageMacIcons(stageResourcesDir: string, sourcePng: string, verbose: bo |
| 845 | 1073 | const fs = yield* FileSystem.FileSystem; |
| 846 | 1074 | const path = yield* Path.Path; |
| 847 | 1075 | if (!(yield* fs.exists(sourcePng))) { |
| 848 | | return yield* new BuildScriptError({ |
| 849 | | message: `Desktop macOS icon source is missing at ${sourcePng}`, |
| 1076 | return yield* new DesktopIconSourceMissingError({ |
| 1077 | platform: "mac", |
| 1078 | sourcePath: sourcePng, |
| 850 | 1079 | }); |
| 851 | 1080 | } |
| 852 | 1081 | |
| ... | ... | @@ -871,8 +1100,9 @@ function stageLinuxIcons(stageResourcesDir: string, sourcePng: string, verbose: |
| 871 | 1100 | const fs = yield* FileSystem.FileSystem; |
| 872 | 1101 | const path = yield* Path.Path; |
| 873 | 1102 | if (!(yield* fs.exists(sourcePng))) { |
| 874 | | return yield* new BuildScriptError({ |
| 875 | | message: `Desktop Linux icon source is missing at ${sourcePng}`, |
| 1103 | return yield* new DesktopIconSourceMissingError({ |
| 1104 | platform: "linux", |
| 1105 | sourcePath: sourcePng, |
| 876 | 1106 | }); |
| 877 | 1107 | } |
| 878 | 1108 | |
| ... | ... | @@ -931,8 +1161,9 @@ function stageWindowsIcons(stageResourcesDir: string, sourceIco: string) { |
| 931 | 1161 | const fs = yield* FileSystem.FileSystem; |
| 932 | 1162 | const path = yield* Path.Path; |
| 933 | 1163 | if (!(yield* fs.exists(sourceIco))) { |
| 934 | | return yield* new BuildScriptError({ |
| 935 | | message: `Desktop Windows icon source is missing at ${sourceIco}`, |
| 1164 | return yield* new DesktopIconSourceMissingError({ |
| 1165 | platform: "win", |
| 1166 | sourcePath: sourceIco, |
| 936 | 1167 | }); |
| 937 | 1168 | } |
| 938 | 1169 | |
| ... | ... | @@ -969,10 +1200,9 @@ function validateBundledClientAssets(clientDir: string) { |
| 969 | 1200 | } |
| 970 | 1201 | |
| 971 | 1202 | if (missing.length > 0) { |
| 972 | | const preview = missing.slice(0, 6).join(", "); |
| 973 | | const suffix = missing.length > 6 ? ` (+${missing.length - 6} more)` : ""; |
| 974 | | return yield* new BuildScriptError({ |
| 975 | | message: `Bundled client references missing files in ${indexPath}: ${preview}${suffix}. Rebuild web/server artifacts.`, |
| 1203 | return yield* new BundledClientAssetsMissingError({ |
| 1204 | indexPath, |
| 1205 | missingFiles: missing, |
| 976 | 1206 | }); |
| 977 | 1207 | } |
| 978 | 1208 | }); |
| ... | ... | @@ -1174,8 +1404,8 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* ( |
| 1174 | 1404 | |
| 1175 | 1405 | const platformConfig = PLATFORM_CONFIG[options.platform]; |
| 1176 | 1406 | if (!platformConfig) { |
| 1177 | | return yield* new BuildScriptError({ |
| 1178 | | message: `Unsupported platform '${options.platform}'.`, |
| 1407 | return yield* new UnsupportedDesktopBuildPlatformError({ |
| 1408 | platform: options.platform, |
| 1179 | 1409 | }); |
| 1180 | 1410 | } |
| 1181 | 1411 | |
| ... | ... | @@ -1183,16 +1413,17 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* ( |
| 1183 | 1413 | |
| 1184 | 1414 | const serverDependencies = serverPackageJson.dependencies; |
| 1185 | 1415 | if (!serverDependencies || Object.keys(serverDependencies).length === 0) { |
| 1186 | | return yield* new BuildScriptError({ |
| 1187 | | message: "Could not resolve production dependencies from apps/server/package.json.", |
| 1416 | return yield* new MissingServerProductionDependenciesError({ |
| 1417 | manifestPath: "apps/server/package.json", |
| 1188 | 1418 | }); |
| 1189 | 1419 | } |
| 1190 | 1420 | |
| 1191 | 1421 | const resolvedOverrides = yield* Effect.try({ |
| 1192 | 1422 | try: () => resolveCatalogDependencies(workspaceOverrides, workspaceCatalog, "apps/desktop"), |
| 1193 | 1423 | catch: (cause) => |
| 1194 | | new BuildScriptError({ |
| 1195 | | message: "Could not resolve overrides from pnpm-workspace.yaml.", |
| 1424 | new DesktopBuildDependencyResolutionError({ |
| 1425 | kind: "workspace-overrides", |
| 1426 | manifestPath: "pnpm-workspace.yaml", |
| 1196 | 1427 | cause, |
| 1197 | 1428 | }), |
| 1198 | 1429 | }); |
| ... | ... | @@ -1200,16 +1431,18 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* ( |
| 1200 | 1431 | const resolvedServerDependencies = yield* Effect.try({ |
| 1201 | 1432 | try: () => resolveCatalogDependencies(serverDependencies, workspaceCatalog, "apps/server"), |
| 1202 | 1433 | catch: (cause) => |
| 1203 | | new BuildScriptError({ |
| 1204 | | message: "Could not resolve production dependencies from apps/server/package.json.", |
| 1434 | new DesktopBuildDependencyResolutionError({ |
| 1435 | kind: "server-production", |
| 1436 | manifestPath: "apps/server/package.json", |
| 1205 | 1437 | cause, |
| 1206 | 1438 | }), |
| 1207 | 1439 | }); |
| 1208 | 1440 | const resolvedDesktopRuntimeDependencies = yield* Effect.try({ |
| 1209 | 1441 | try: () => resolveDesktopRuntimeDependencies(desktopPackageJson.dependencies, workspaceCatalog), |
| 1210 | 1442 | catch: (cause) => |
| 1211 | | new BuildScriptError({ |
| 1212 | | message: "Could not resolve desktop runtime dependencies from apps/desktop/package.json.", |
| 1443 | new DesktopBuildDependencyResolutionError({ |
| 1444 | kind: "desktop-runtime", |
| 1445 | manifestPath: "apps/desktop/package.json", |
| 1213 | 1446 | cause, |
| 1214 | 1447 | }), |
| 1215 | 1448 | }); |
| ... | ... | @@ -1243,17 +1476,25 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* ( |
| 1243 | 1476 | ); |
| 1244 | 1477 | } |
| 1245 | 1478 | |
| 1246 | | for (const [label, dir] of Object.entries(distDirs)) { |
| 1247 | | if (!(yield* fs.exists(dir))) { |
| 1248 | | return yield* new BuildScriptError({ |
| 1249 | | message: `Missing ${label} at ${dir}. Run 'vp run build:desktop' first.`, |
| 1479 | const requiredBuildInputs = [ |
| 1480 | { artifact: "desktop-dist", artifactPath: distDirs.desktopDist }, |
| 1481 | { artifact: "desktop-resources", artifactPath: distDirs.desktopResources }, |
| 1482 | { artifact: "server-dist", artifactPath: distDirs.serverDist }, |
| 1483 | ] as const; |
| 1484 | for (const input of requiredBuildInputs) { |
| 1485 | if (!(yield* fs.exists(input.artifactPath))) { |
| 1486 | return yield* new MissingDesktopBuildInputError({ |
| 1487 | ...input, |
| 1488 | buildCommand: "vp run build:desktop", |
| 1250 | 1489 | }); |
| 1251 | 1490 | } |
| 1252 | 1491 | } |
| 1253 | 1492 | |
| 1254 | 1493 | if (!(yield* fs.exists(bundledClientEntry))) { |
| 1255 | | return yield* new BuildScriptError({ |
| 1256 | | message: `Missing bundled server client at ${bundledClientEntry}. Run 'vp run build:desktop' first.`, |
| 1494 | return yield* new MissingDesktopBuildInputError({ |
| 1495 | artifact: "bundled-server-client", |
| 1496 | artifactPath: bundledClientEntry, |
| 1497 | buildCommand: "vp run build:desktop", |
| 1257 | 1498 | }); |
| 1258 | 1499 | } |
| 1259 | 1500 | |
| ... | ... | @@ -1285,7 +1526,7 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* ( |
| 1285 | 1526 | options.platform === "mac" && options.signed |
| 1286 | 1527 | ? yield* Effect.try({ |
| 1287 | 1528 | try: () => resolveMacPasskeySigningConfiguration(loadRepoEnv({ repoRoot })), |
| 1288 | | catch: BuildScriptError.fromMacPasskeySigningConfiguration, |
| 1529 | catch: MacPasskeySigningConfigurationResolutionError.fromCause, |
| 1289 | 1530 | }) |
| 1290 | 1531 | : undefined; |
| 1291 | 1532 | const macPasskeySigning = configuredMacPasskeySigning |
| ... | ... | @@ -1302,8 +1543,8 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* ( |
| 1302 | 1543 | : undefined; |
| 1303 | 1544 | if (macPasskeySigning && macEntitlementsPath) { |
| 1304 | 1545 | if (!(yield* fs.exists(macPasskeySigning.provisioningProfilePath))) { |
| 1305 | | return yield* new BuildScriptError({ |
| 1306 | | message: `macOS provisioning profile not found: ${macPasskeySigning.provisioningProfilePath}`, |
| 1546 | return yield* new MacProvisioningProfileNotFoundError({ |
| 1547 | provisioningProfilePath: macPasskeySigning.provisioningProfilePath, |
| 1307 | 1548 | }); |
| 1308 | 1549 | } |
| 1309 | 1550 | yield* fs.writeFileString(macEntitlementsPath, renderMacPasskeyEntitlements(macPasskeySigning)); |
| ... | ... | @@ -1442,8 +1683,10 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* ( |
| 1442 | 1683 | |
| 1443 | 1684 | const stageDistDir = path.join(stageAppDir, "dist"); |
| 1444 | 1685 | if (!(yield* fs.exists(stageDistDir))) { |
| 1445 | | return yield* new BuildScriptError({ |
| 1446 | | message: `Build completed but dist directory was not found at ${stageDistDir}`, |
| 1686 | return yield* new DesktopBuildDistDirectoryMissingError({ |
| 1687 | distPath: stageDistDir, |
| 1688 | platform: options.platform, |
| 1689 | arch: options.arch, |
| 1447 | 1690 | }); |
| 1448 | 1691 | } |
| 1449 | 1692 | |
| ... | ... | @@ -1462,8 +1705,10 @@ const buildDesktopArtifact = Effect.fn("buildDesktopArtifact")(function* ( |
| 1462 | 1705 | } |
| 1463 | 1706 | |
| 1464 | 1707 | if (copiedArtifacts.length === 0) { |
| 1465 | | return yield* new BuildScriptError({ |
| 1466 | | message: `Build completed but no files were produced in ${stageDistDir}`, |
| 1708 | return yield* new DesktopBuildNoArtifactsProducedError({ |
| 1709 | distPath: stageDistDir, |
| 1710 | platform: options.platform, |
| 1711 | arch: options.arch, |
| 1467 | 1712 | }); |
| 1468 | 1713 | } |
| 1469 | 1714 | |