| author | |
| committer | |
| log | 0eba548282b96ee420ad0b1a3f5d9d4487376b90 |
| tree | d831a177719d88b2ce1fd2c161b870ad90f7199c |
| parent | 68c0dd3b7095cae054a9db5e48157e89f65c0664 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: codex <codex@users.noreply.github.com>4 files changed, 200 insertions(+), 79 deletions(-)
apps/server/src/assets/AssetAccess.test.ts+3-2| ... | @@ -88,7 +88,7 @@ describe("AssetAccess", () => { | ... | @@ -88,7 +88,7 @@ describe("AssetAccess", () => { |
| 88 | }).pipe(Effect.flip); | 88 | }).pipe(Effect.flip); |
| 89 | expect(error.message).toBe("Workspace file path must be relative to the project root."); | 89 | expect(error.message).toBe("Workspace file path must be relative to the project root."); |
| 90 | expect(error).toMatchObject({ | 90 | expect(error).toMatchObject({ |
| 91 | operation: "validate-workspace-path", | 91 | _tag: "AssetWorkspacePathValidationError", |
| 92 | resource: { | 92 | resource: { |
| 93 | _tag: "workspace-file", | 93 | _tag: "workspace-file", |
| 94 | threadId: "thread-1", | 94 | threadId: "thread-1", |
| ... | @@ -130,7 +130,7 @@ describe("AssetAccess", () => { | ... | @@ -130,7 +130,7 @@ describe("AssetAccess", () => { |
| 130 | 130 | ||
| 131 | expect(error.message).toBe("Failed to inspect the workspace asset."); | 131 | expect(error.message).toBe("Failed to inspect the workspace asset."); |
| 132 | expect(error).toMatchObject({ | 132 | expect(error).toMatchObject({ |
| 133 | operation: "inspect-workspace-asset", | 133 | _tag: "AssetWorkspaceAssetInspectionError", |
| 134 | resource: { | 134 | resource: { |
| 135 | _tag: "workspace-file", | 135 | _tag: "workspace-file", |
| 136 | threadId: "thread-1", | 136 | threadId: "thread-1", |
| ... | @@ -268,6 +268,7 @@ describe("AssetAccess", () => { | ... | @@ -268,6 +268,7 @@ describe("AssetAccess", () => { |
| 268 | ); | 268 | ); |
| 269 | 269 | ||
| 270 | expect(error.message).toBe("Failed to resolve project favicon."); | 270 | expect(error.message).toBe("Failed to resolve project favicon."); |
| 271 | expect(error._tag).toBe("AssetProjectFaviconResolutionError"); | ||
| 271 | expect(error.cause).toBe(resolutionCause); | 272 | expect(error.cause).toBe(resolutionCause); |
| 272 | }).pipe(Effect.provide(testLayer)), | 273 | }).pipe(Effect.provide(testLayer)), |
| 273 | ); | 274 | ); |
apps/server/src/assets/AssetAccess.ts+28-43| ... | @@ -1,5 +1,18 @@ | ... | @@ -1,5 +1,18 @@ |
| 1 | import type { AssetResource } from "@t3tools/contracts"; | 1 | import type { AssetResource } from "@t3tools/contracts"; |
| 2 | import { AssetAccessError } from "@t3tools/contracts"; | 2 | import { |
| 3 | AssetAttachmentNotFoundError, | ||
| 4 | AssetPreviewTypeValidationError, | ||
| 5 | AssetProjectFaviconInspectionError, | ||
| 6 | AssetProjectFaviconNotFoundError, | ||
| 7 | AssetProjectFaviconResolutionError, | ||
| 8 | AssetSigningKeyLoadError, | ||
| 9 | AssetWorkspaceAssetInspectionError, | ||
| 10 | AssetWorkspaceAssetNotFoundError, | ||
| 11 | AssetWorkspaceContextNotFoundError, | ||
| 12 | AssetWorkspacePathValidationError, | ||
| 13 | AssetWorkspaceResolutionError, | ||
| 14 | AssetWorkspaceRootNormalizationError, | ||
| 15 | } from "@t3tools/contracts"; | ||
| 3 | import { | 16 | import { |
| 4 | isWorkspaceImagePreviewPath, | 17 | isWorkspaceImagePreviewPath, |
| 5 | isWorkspacePreviewEntryPath, | 18 | isWorkspacePreviewEntryPath, |
| ... | @@ -165,19 +178,15 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i | ... | @@ -165,19 +178,15 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i |
| 165 | switch (input.resource._tag) { | 178 | switch (input.resource._tag) { |
| 166 | case "workspace-file": { | 179 | case "workspace-file": { |
| 167 | if (!input.workspaceRoot) { | 180 | if (!input.workspaceRoot) { |
| 168 | return yield* new AssetAccessError({ | 181 | return yield* new AssetWorkspaceContextNotFoundError({ |
| 169 | operation: "resolve-workspace-context", | ||
| 170 | resource: input.resource, | 182 | resource: input.resource, |
| 171 | message: "Workspace context was not found.", | ||
| 172 | }); | 183 | }); |
| 173 | } | 184 | } |
| 174 | const workspaceRoot = yield* workspacePaths.normalizeWorkspaceRoot(input.workspaceRoot).pipe( | 185 | const workspaceRoot = yield* workspacePaths.normalizeWorkspaceRoot(input.workspaceRoot).pipe( |
| 175 | Effect.mapError( | 186 | Effect.mapError( |
| 176 | (cause) => | 187 | (cause) => |
| 177 | new AssetAccessError({ | 188 | new AssetWorkspaceRootNormalizationError({ |
| 178 | operation: "normalize-workspace-root", | ||
| 179 | resource: input.resource, | 189 | resource: input.resource, |
| 180 | message: "Failed to normalize the workspace root.", | ||
| 181 | cause, | 190 | cause, |
| 182 | }), | 191 | }), |
| 183 | ), | 192 | ), |
| ... | @@ -190,19 +199,15 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i | ... | @@ -190,19 +199,15 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i |
| 190 | .pipe( | 199 | .pipe( |
| 191 | Effect.mapError( | 200 | Effect.mapError( |
| 192 | (cause) => | 201 | (cause) => |
| 193 | new AssetAccessError({ | 202 | new AssetWorkspacePathValidationError({ |
| 194 | operation: "validate-workspace-path", | ||
| 195 | resource: input.resource, | 203 | resource: input.resource, |
| 196 | message: "Workspace file path must be relative to the project root.", | ||
| 197 | cause, | 204 | cause, |
| 198 | }), | 205 | }), |
| 199 | ), | 206 | ), |
| 200 | ); | 207 | ); |
| 201 | if (!isWorkspacePreviewEntryPath(resolved.relativePath)) { | 208 | if (!isWorkspacePreviewEntryPath(resolved.relativePath)) { |
| 202 | return yield* new AssetAccessError({ | 209 | return yield* new AssetPreviewTypeValidationError({ |
| 203 | operation: "validate-preview-type", | ||
| 204 | resource: input.resource, | 210 | resource: input.resource, |
| 205 | message: "Only browser documents and images can be previewed.", | ||
| 206 | }); | 211 | }); |
| 207 | } | 212 | } |
| 208 | const canonicalFile = yield* resolveCanonicalWorkspaceFile({ | 213 | const canonicalFile = yield* resolveCanonicalWorkspaceFile({ |
| ... | @@ -211,28 +216,22 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i | ... | @@ -211,28 +216,22 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i |
| 211 | }).pipe( | 216 | }).pipe( |
| 212 | Effect.mapError( | 217 | Effect.mapError( |
| 213 | (cause) => | 218 | (cause) => |
| 214 | new AssetAccessError({ | 219 | new AssetWorkspaceAssetInspectionError({ |
| 215 | operation: "inspect-workspace-asset", | ||
| 216 | resource: input.resource, | 220 | resource: input.resource, |
| 217 | message: "Failed to inspect the workspace asset.", | ||
| 218 | cause, | 221 | cause, |
| 219 | }), | 222 | }), |
| 220 | ), | 223 | ), |
| 221 | ); | 224 | ); |
| 222 | if (!canonicalFile) { | 225 | if (!canonicalFile) { |
| 223 | return yield* new AssetAccessError({ | 226 | return yield* new AssetWorkspaceAssetNotFoundError({ |
| 224 | operation: "locate-workspace-asset", | ||
| 225 | resource: input.resource, | 227 | resource: input.resource, |
| 226 | message: "Workspace asset was not found.", | ||
| 227 | }); | 228 | }); |
| 228 | } | 229 | } |
| 229 | const canonicalWorkspaceRoot = yield* fileSystem.realPath(workspaceRoot).pipe( | 230 | const canonicalWorkspaceRoot = yield* fileSystem.realPath(workspaceRoot).pipe( |
| 230 | Effect.mapError( | 231 | Effect.mapError( |
| 231 | (cause) => | 232 | (cause) => |
| 232 | new AssetAccessError({ | 233 | new AssetWorkspaceResolutionError({ |
| 233 | operation: "resolve-workspace", | ||
| 234 | resource: input.resource, | 234 | resource: input.resource, |
| 235 | message: "Failed to resolve workspace.", | ||
| 236 | cause, | 235 | cause, |
| 237 | }), | 236 | }), |
| 238 | ), | 237 | ), |
| ... | @@ -262,10 +261,8 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i | ... | @@ -262,10 +261,8 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i |
| 262 | attachmentId: input.resource.attachmentId, | 261 | attachmentId: input.resource.attachmentId, |
| 263 | }); | 262 | }); |
| 264 | if (!attachmentPath) { | 263 | if (!attachmentPath) { |
| 265 | return yield* new AssetAccessError({ | 264 | return yield* new AssetAttachmentNotFoundError({ |
| 266 | operation: "locate-attachment", | ||
| 267 | resource: input.resource, | 265 | resource: input.resource, |
| 268 | message: "Attachment was not found.", | ||
| 269 | }); | 266 | }); |
| 270 | } | 267 | } |
| 271 | claims = { | 268 | claims = { |
| ... | @@ -281,10 +278,8 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i | ... | @@ -281,10 +278,8 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i |
| 281 | const workspaceRoot = yield* workspacePaths.normalizeWorkspaceRoot(input.resource.cwd).pipe( | 278 | const workspaceRoot = yield* workspacePaths.normalizeWorkspaceRoot(input.resource.cwd).pipe( |
| 282 | Effect.mapError( | 279 | Effect.mapError( |
| 283 | (cause) => | 280 | (cause) => |
| 284 | new AssetAccessError({ | 281 | new AssetWorkspaceRootNormalizationError({ |
| 285 | operation: "normalize-workspace-root", | ||
| 286 | resource: input.resource, | 282 | resource: input.resource, |
| 287 | message: "Failed to normalize the workspace root.", | ||
| 288 | cause, | 283 | cause, |
| 289 | }), | 284 | }), |
| 290 | ), | 285 | ), |
| ... | @@ -293,10 +288,8 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i | ... | @@ -293,10 +288,8 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i |
| 293 | const faviconPath = yield* faviconResolver.resolvePath(workspaceRoot).pipe( | 288 | const faviconPath = yield* faviconResolver.resolvePath(workspaceRoot).pipe( |
| 294 | Effect.mapError( | 289 | Effect.mapError( |
| 295 | (cause) => | 290 | (cause) => |
| 296 | new AssetAccessError({ | 291 | new AssetProjectFaviconResolutionError({ |
| 297 | operation: "resolve-project-favicon", | ||
| 298 | resource: input.resource, | 292 | resource: input.resource, |
| 299 | message: "Failed to resolve project favicon.", | ||
| 300 | cause, | 293 | cause, |
| 301 | }), | 294 | }), |
| 302 | ), | 295 | ), |
| ... | @@ -307,19 +300,15 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i | ... | @@ -307,19 +300,15 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i |
| 307 | !(yield* resolveCanonicalWorkspaceFile({ workspaceRoot, relativePath }).pipe( | 300 | !(yield* resolveCanonicalWorkspaceFile({ workspaceRoot, relativePath }).pipe( |
| 308 | Effect.mapError( | 301 | Effect.mapError( |
| 309 | (cause) => | 302 | (cause) => |
| 310 | new AssetAccessError({ | 303 | new AssetProjectFaviconInspectionError({ |
| 311 | operation: "inspect-project-favicon", | ||
| 312 | resource: input.resource, | 304 | resource: input.resource, |
| 313 | message: "Failed to inspect the project favicon.", | ||
| 314 | cause, | 305 | cause, |
| 315 | }), | 306 | }), |
| 316 | ), | 307 | ), |
| 317 | )) | 308 | )) |
| 318 | ) { | 309 | ) { |
| 319 | return yield* new AssetAccessError({ | 310 | return yield* new AssetProjectFaviconNotFoundError({ |
| 320 | operation: "locate-project-favicon", | ||
| 321 | resource: input.resource, | 311 | resource: input.resource, |
| 322 | message: "Project favicon was not found.", | ||
| 323 | }); | 312 | }); |
| 324 | } | 313 | } |
| 325 | claims = { | 314 | claims = { |
| ... | @@ -328,10 +317,8 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i | ... | @@ -328,10 +317,8 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i |
| 328 | workspaceRoot: yield* fileSystem.realPath(workspaceRoot).pipe( | 317 | workspaceRoot: yield* fileSystem.realPath(workspaceRoot).pipe( |
| 329 | Effect.mapError( | 318 | Effect.mapError( |
| 330 | (cause) => | 319 | (cause) => |
| 331 | new AssetAccessError({ | 320 | new AssetWorkspaceResolutionError({ |
| 332 | operation: "resolve-workspace", | ||
| 333 | resource: input.resource, | 321 | resource: input.resource, |
| 334 | message: "Failed to resolve workspace.", | ||
| 335 | cause, | 322 | cause, |
| 336 | }), | 323 | }), |
| 337 | ), | 324 | ), |
| ... | @@ -348,10 +335,8 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i | ... | @@ -348,10 +335,8 @@ export const issueAssetUrl = Effect.fn("AssetAccess.issueAssetUrl")(function* (i |
| 348 | const signingSecret = yield* secretStore.getOrCreateRandom(SIGNING_SECRET_NAME, 32).pipe( | 335 | const signingSecret = yield* secretStore.getOrCreateRandom(SIGNING_SECRET_NAME, 32).pipe( |
| 349 | Effect.mapError( | 336 | Effect.mapError( |
| 350 | (cause) => | 337 | (cause) => |
| 351 | new AssetAccessError({ | 338 | new AssetSigningKeyLoadError({ |
| 352 | operation: "load-signing-key", | ||
| 353 | resource: input.resource, | 339 | resource: input.resource, |
| 354 | message: "Failed to load the asset signing key.", | ||
| 355 | cause, | 340 | cause, |
| 356 | }), | 341 | }), |
| 357 | ), | 342 | ), |
apps/server/src/ws.ts+6-13| ... | @@ -46,7 +46,8 @@ import { | ... | @@ -46,7 +46,8 @@ import { |
| 46 | OrchestrationReplayEventsError, | 46 | OrchestrationReplayEventsError, |
| 47 | type FilesystemBrowseFailure, | 47 | type FilesystemBrowseFailure, |
| 48 | FilesystemBrowseError, | 48 | FilesystemBrowseError, |
| 49 | AssetAccessError, | 49 | AssetWorkspaceContextNotFoundError, |
| 50 | AssetWorkspaceContextResolutionError, | ||
| 50 | EnvironmentAuthorizationError, | 51 | EnvironmentAuthorizationError, |
| 51 | ThreadId, | 52 | ThreadId, |
| 52 | type TerminalAttachStreamEvent, | 53 | type TerminalAttachStreamEvent, |
| ... | @@ -1413,19 +1414,15 @@ const makeWsRpcLayer = (currentSession: EnvironmentAuth.AuthenticatedSession) => | ... | @@ -1413,19 +1414,15 @@ const makeWsRpcLayer = (currentSession: EnvironmentAuth.AuthenticatedSession) => |
| 1413 | .pipe( | 1414 | .pipe( |
| 1414 | Effect.mapError( | 1415 | Effect.mapError( |
| 1415 | (cause) => | 1416 | (cause) => |
| 1416 | new AssetAccessError({ | 1417 | new AssetWorkspaceContextResolutionError({ |
| 1417 | operation: "resolve-workspace-context", | ||
| 1418 | resource: input.resource, | 1418 | resource: input.resource, |
| 1419 | message: "Failed to resolve workspace context.", | ||
| 1420 | cause, | 1419 | cause, |
| 1421 | }), | 1420 | }), |
| 1422 | ), | 1421 | ), |
| 1423 | ); | 1422 | ); |
| 1424 | if (Option.isNone(thread)) { | 1423 | if (Option.isNone(thread)) { |
| 1425 | return yield* new AssetAccessError({ | 1424 | return yield* new AssetWorkspaceContextNotFoundError({ |
| 1426 | operation: "resolve-workspace-context", | ||
| 1427 | resource: input.resource, | 1425 | resource: input.resource, |
| 1428 | message: "Workspace context was not found.", | ||
| 1429 | }); | 1426 | }); |
| 1430 | } | 1427 | } |
| 1431 | const project = yield* projectionSnapshotQuery | 1428 | const project = yield* projectionSnapshotQuery |
| ... | @@ -1433,19 +1430,15 @@ const makeWsRpcLayer = (currentSession: EnvironmentAuth.AuthenticatedSession) => | ... | @@ -1433,19 +1430,15 @@ const makeWsRpcLayer = (currentSession: EnvironmentAuth.AuthenticatedSession) => |
| 1433 | .pipe( | 1430 | .pipe( |
| 1434 | Effect.mapError( | 1431 | Effect.mapError( |
| 1435 | (cause) => | 1432 | (cause) => |
| 1436 | new AssetAccessError({ | 1433 | new AssetWorkspaceContextResolutionError({ |
| 1437 | operation: "resolve-workspace-context", | ||
| 1438 | resource: input.resource, | 1434 | resource: input.resource, |
| 1439 | message: "Failed to resolve workspace context.", | ||
| 1440 | cause, | 1435 | cause, |
| 1441 | }), | 1436 | }), |
| 1442 | ), | 1437 | ), |
| 1443 | ); | 1438 | ); |
| 1444 | if (Option.isNone(project)) { | 1439 | if (Option.isNone(project)) { |
| 1445 | return yield* new AssetAccessError({ | 1440 | return yield* new AssetWorkspaceContextNotFoundError({ |
| 1446 | operation: "resolve-workspace-context", | ||
| 1447 | resource: input.resource, | 1441 | resource: input.resource, |
| 1448 | message: "Workspace context was not found.", | ||
| 1449 | }); | 1442 | }); |
| 1450 | } | 1443 | } |
| 1451 | return yield* issueAssetUrl({ | 1444 | return yield* issueAssetUrl({ |
packages/contracts/src/assets.ts+163-21| ... | @@ -29,28 +29,170 @@ export const AssetCreateUrlResult = Schema.Struct({ | ... | @@ -29,28 +29,170 @@ export const AssetCreateUrlResult = Schema.Struct({ |
| 29 | }); | 29 | }); |
| 30 | export type AssetCreateUrlResult = typeof AssetCreateUrlResult.Type; | 30 | export type AssetCreateUrlResult = typeof AssetCreateUrlResult.Type; |
| 31 | 31 | ||
| 32 | export const AssetAccessOperation = Schema.Literals([ | 32 | export class AssetWorkspaceContextNotFoundError extends Schema.TaggedErrorClass<AssetWorkspaceContextNotFoundError>()( |
| 33 | "resolve-workspace-context", | 33 | "AssetWorkspaceContextNotFoundError", |
| 34 | "normalize-workspace-root", | 34 | { |
| 35 | "validate-workspace-path", | 35 | resource: AssetResource, |
| 36 | "validate-preview-type", | 36 | }, |
| 37 | "inspect-workspace-asset", | 37 | ) { |
| 38 | "locate-workspace-asset", | 38 | override get message(): string { |
| 39 | "resolve-workspace", | 39 | return "Workspace context was not found."; |
| 40 | "locate-attachment", | 40 | } |
| 41 | "resolve-project-favicon", | 41 | } |
| 42 | "inspect-project-favicon", | 42 | |
| 43 | "locate-project-favicon", | 43 | export class AssetWorkspaceContextResolutionError extends Schema.TaggedErrorClass<AssetWorkspaceContextResolutionError>()( |
| 44 | "load-signing-key", | 44 | "AssetWorkspaceContextResolutionError", |
| 45 | ]); | 45 | { |
| 46 | export type AssetAccessOperation = typeof AssetAccessOperation.Type; | 46 | resource: AssetResource, |
| 47 | cause: Schema.Defect(), | ||
| 48 | }, | ||
| 49 | ) { | ||
| 50 | override get message(): string { | ||
| 51 | return "Failed to resolve workspace context."; | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | export class AssetWorkspaceRootNormalizationError extends Schema.TaggedErrorClass<AssetWorkspaceRootNormalizationError>()( | ||
| 56 | "AssetWorkspaceRootNormalizationError", | ||
| 57 | { | ||
| 58 | resource: AssetResource, | ||
| 59 | cause: Schema.Defect(), | ||
| 60 | }, | ||
| 61 | ) { | ||
| 62 | override get message(): string { | ||
| 63 | return "Failed to normalize the workspace root."; | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | export class AssetWorkspacePathValidationError extends Schema.TaggedErrorClass<AssetWorkspacePathValidationError>()( | ||
| 68 | "AssetWorkspacePathValidationError", | ||
| 69 | { | ||
| 70 | resource: AssetResource, | ||
| 71 | cause: Schema.Defect(), | ||
| 72 | }, | ||
| 73 | ) { | ||
| 74 | override get message(): string { | ||
| 75 | return "Workspace file path must be relative to the project root."; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | export class AssetPreviewTypeValidationError extends Schema.TaggedErrorClass<AssetPreviewTypeValidationError>()( | ||
| 80 | "AssetPreviewTypeValidationError", | ||
| 81 | { | ||
| 82 | resource: AssetResource, | ||
| 83 | }, | ||
| 84 | ) { | ||
| 85 | override get message(): string { | ||
| 86 | return "Only browser documents and images can be previewed."; | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | export class AssetWorkspaceAssetInspectionError extends Schema.TaggedErrorClass<AssetWorkspaceAssetInspectionError>()( | ||
| 91 | "AssetWorkspaceAssetInspectionError", | ||
| 92 | { | ||
| 93 | resource: AssetResource, | ||
| 94 | cause: Schema.Defect(), | ||
| 95 | }, | ||
| 96 | ) { | ||
| 97 | override get message(): string { | ||
| 98 | return "Failed to inspect the workspace asset."; | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | export class AssetWorkspaceAssetNotFoundError extends Schema.TaggedErrorClass<AssetWorkspaceAssetNotFoundError>()( | ||
| 103 | "AssetWorkspaceAssetNotFoundError", | ||
| 104 | { | ||
| 105 | resource: AssetResource, | ||
| 106 | }, | ||
| 107 | ) { | ||
| 108 | override get message(): string { | ||
| 109 | return "Workspace asset was not found."; | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | export class AssetWorkspaceResolutionError extends Schema.TaggedErrorClass<AssetWorkspaceResolutionError>()( | ||
| 114 | "AssetWorkspaceResolutionError", | ||
| 115 | { | ||
| 116 | resource: AssetResource, | ||
| 117 | cause: Schema.Defect(), | ||
| 118 | }, | ||
| 119 | ) { | ||
| 120 | override get message(): string { | ||
| 121 | return "Failed to resolve workspace."; | ||
| 122 | } | ||
| 123 | } | ||
| 47 | 124 | ||
| 48 | export class AssetAccessError extends Schema.TaggedErrorClass<AssetAccessError>()( | 125 | export class AssetAttachmentNotFoundError extends Schema.TaggedErrorClass<AssetAttachmentNotFoundError>()( |
| 49 | "AssetAccessError", | 126 | "AssetAttachmentNotFoundError", |
| 50 | { | 127 | { |
| 51 | operation: AssetAccessOperation, | ||
| 52 | resource: AssetResource, | 128 | resource: AssetResource, |
| 53 | message: TrimmedNonEmptyString, | ||
| 54 | cause: Schema.optional(Schema.Defect()), | ||
| 55 | }, | 129 | }, |
| 56 | ) {} | 130 | ) { |
| 131 | override get message(): string { | ||
| 132 | return "Attachment was not found."; | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | export class AssetProjectFaviconResolutionError extends Schema.TaggedErrorClass<AssetProjectFaviconResolutionError>()( | ||
| 137 | "AssetProjectFaviconResolutionError", | ||
| 138 | { | ||
| 139 | resource: AssetResource, | ||
| 140 | cause: Schema.Defect(), | ||
| 141 | }, | ||
| 142 | ) { | ||
| 143 | override get message(): string { | ||
| 144 | return "Failed to resolve project favicon."; | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | export class AssetProjectFaviconInspectionError extends Schema.TaggedErrorClass<AssetProjectFaviconInspectionError>()( | ||
| 149 | "AssetProjectFaviconInspectionError", | ||
| 150 | { | ||
| 151 | resource: AssetResource, | ||
| 152 | cause: Schema.Defect(), | ||
| 153 | }, | ||
| 154 | ) { | ||
| 155 | override get message(): string { | ||
| 156 | return "Failed to inspect the project favicon."; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | export class AssetProjectFaviconNotFoundError extends Schema.TaggedErrorClass<AssetProjectFaviconNotFoundError>()( | ||
| 161 | "AssetProjectFaviconNotFoundError", | ||
| 162 | { | ||
| 163 | resource: AssetResource, | ||
| 164 | }, | ||
| 165 | ) { | ||
| 166 | override get message(): string { | ||
| 167 | return "Project favicon was not found."; | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | export class AssetSigningKeyLoadError extends Schema.TaggedErrorClass<AssetSigningKeyLoadError>()( | ||
| 172 | "AssetSigningKeyLoadError", | ||
| 173 | { | ||
| 174 | resource: AssetResource, | ||
| 175 | cause: Schema.Defect(), | ||
| 176 | }, | ||
| 177 | ) { | ||
| 178 | override get message(): string { | ||
| 179 | return "Failed to load the asset signing key."; | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | export const AssetAccessError = Schema.Union([ | ||
| 184 | AssetWorkspaceContextNotFoundError, | ||
| 185 | AssetWorkspaceContextResolutionError, | ||
| 186 | AssetWorkspaceRootNormalizationError, | ||
| 187 | AssetWorkspacePathValidationError, | ||
| 188 | AssetPreviewTypeValidationError, | ||
| 189 | AssetWorkspaceAssetInspectionError, | ||
| 190 | AssetWorkspaceAssetNotFoundError, | ||
| 191 | AssetWorkspaceResolutionError, | ||
| 192 | AssetAttachmentNotFoundError, | ||
| 193 | AssetProjectFaviconResolutionError, | ||
| 194 | AssetProjectFaviconInspectionError, | ||
| 195 | AssetProjectFaviconNotFoundError, | ||
| 196 | AssetSigningKeyLoadError, | ||
| 197 | ]); | ||
| 198 | export type AssetAccessError = typeof AssetAccessError.Type; |