deepline 0.1.317 → 0.1.318
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.
- package/dist/bundling-sources/sdk/src/client.ts +60 -25
- package/dist/bundling-sources/sdk/src/release.ts +1 -1
- package/dist/cli/index.js +46 -27
- package/dist/cli/index.mjs +46 -27
- package/dist/index.js +46 -27
- package/dist/index.mjs +46 -27
- package/package.json +1 -1
|
@@ -1127,6 +1127,13 @@ function isStagedUploadMintUnsupported(error: unknown): boolean {
|
|
|
1127
1127
|
return error instanceof DeeplineError && error.statusCode === 404;
|
|
1128
1128
|
}
|
|
1129
1129
|
|
|
1130
|
+
function isStagedUploadDirectEgressDenied(error: unknown): boolean {
|
|
1131
|
+
return (
|
|
1132
|
+
error instanceof DeeplineError &&
|
|
1133
|
+
error.code === 'STAGED_FILE_UPLOAD_EGRESS_DENIED'
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1130
1137
|
function formatMegabytes(bytes: number): string {
|
|
1131
1138
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
1132
1139
|
}
|
|
@@ -1159,10 +1166,16 @@ async function uploadStagedFileToPresignedUrl(input: {
|
|
|
1159
1166
|
return;
|
|
1160
1167
|
}
|
|
1161
1168
|
const text = await response.text().catch(() => '');
|
|
1169
|
+
const egressDenied =
|
|
1170
|
+
response.status === 403 &&
|
|
1171
|
+
response.headers.get('x-deny-reason')?.trim().toLowerCase() ===
|
|
1172
|
+
'host_not_allowed';
|
|
1162
1173
|
lastError = new DeeplineError(
|
|
1163
1174
|
`Direct storage upload of ${input.logicalPath} failed: ${response.status} ${text.slice(0, 200)}`,
|
|
1164
1175
|
response.status,
|
|
1165
|
-
|
|
1176
|
+
egressDenied
|
|
1177
|
+
? 'STAGED_FILE_UPLOAD_EGRESS_DENIED'
|
|
1178
|
+
: 'STAGED_FILE_UPLOAD_FAILED',
|
|
1166
1179
|
{ logicalPath: input.logicalPath },
|
|
1167
1180
|
);
|
|
1168
1181
|
// 4xx (other than throttling) will not recover on retry.
|
|
@@ -2502,7 +2515,10 @@ export class DeeplineClient {
|
|
|
2502
2515
|
);
|
|
2503
2516
|
}
|
|
2504
2517
|
|
|
2505
|
-
|
|
2518
|
+
type DirectStageResult =
|
|
2519
|
+
| { ref: PlayStagedFileRef }
|
|
2520
|
+
| { fallbackFile: (typeof files)[number] };
|
|
2521
|
+
const directResults: DirectStageResult[] = await Promise.all(
|
|
2506
2522
|
files.map(async (file) => {
|
|
2507
2523
|
const upload = uploadByIdentity.get(
|
|
2508
2524
|
stagedUploadIdentity(file.logicalPath, file.contentHash),
|
|
@@ -2515,32 +2531,46 @@ export class DeeplineClient {
|
|
|
2515
2531
|
);
|
|
2516
2532
|
}
|
|
2517
2533
|
if (upload.alreadyStaged || !upload.uploadUrl) {
|
|
2518
|
-
return;
|
|
2534
|
+
return { ref: upload.ref };
|
|
2535
|
+
}
|
|
2536
|
+
try {
|
|
2537
|
+
await uploadStagedFileToPresignedUrl({
|
|
2538
|
+
url: upload.uploadUrl,
|
|
2539
|
+
headers: upload.uploadHeaders ?? {
|
|
2540
|
+
'content-type': file.contentType,
|
|
2541
|
+
},
|
|
2542
|
+
body: decodeBase64Bytes(file.contentBase64),
|
|
2543
|
+
logicalPath: file.logicalPath,
|
|
2544
|
+
});
|
|
2545
|
+
return { ref: upload.ref };
|
|
2546
|
+
} catch (error) {
|
|
2547
|
+
if (isStagedUploadDirectEgressDenied(error)) {
|
|
2548
|
+
return { fallbackFile: file };
|
|
2549
|
+
}
|
|
2550
|
+
throw error;
|
|
2519
2551
|
}
|
|
2520
|
-
await uploadStagedFileToPresignedUrl({
|
|
2521
|
-
url: upload.uploadUrl,
|
|
2522
|
-
headers: upload.uploadHeaders ?? {
|
|
2523
|
-
'content-type': file.contentType,
|
|
2524
|
-
},
|
|
2525
|
-
body: decodeBase64Bytes(file.contentBase64),
|
|
2526
|
-
logicalPath: file.logicalPath,
|
|
2527
|
-
});
|
|
2528
2552
|
}),
|
|
2529
2553
|
);
|
|
2530
2554
|
|
|
2531
|
-
return
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2555
|
+
return await Promise.all(
|
|
2556
|
+
directResults.map(async (result) => {
|
|
2557
|
+
if ('fallbackFile' in result) {
|
|
2558
|
+
const [ref] = await this.stagePlayFilesViaMultipart(
|
|
2559
|
+
[result.fallbackFile],
|
|
2560
|
+
'direct-egress-denied',
|
|
2561
|
+
);
|
|
2562
|
+
if (!ref) {
|
|
2563
|
+
throw new DeeplineError(
|
|
2564
|
+
`The staging server did not return an upload target for ${result.fallbackFile.logicalPath}.`,
|
|
2565
|
+
undefined,
|
|
2566
|
+
'STAGED_FILE_MINT_INCOMPLETE',
|
|
2567
|
+
);
|
|
2568
|
+
}
|
|
2569
|
+
return ref;
|
|
2570
|
+
}
|
|
2571
|
+
return result.ref;
|
|
2572
|
+
}),
|
|
2573
|
+
);
|
|
2544
2574
|
}
|
|
2545
2575
|
|
|
2546
2576
|
/**
|
|
@@ -2572,11 +2602,16 @@ export class DeeplineClient {
|
|
|
2572
2602
|
contentType: string;
|
|
2573
2603
|
bytes: number;
|
|
2574
2604
|
}>,
|
|
2605
|
+
reason: 'mint-unsupported' | 'direct-egress-denied' = 'mint-unsupported',
|
|
2575
2606
|
): Promise<PlayStagedFileRef[]> {
|
|
2576
2607
|
for (const file of files) {
|
|
2577
2608
|
if (file.bytes > STAGE_LEGACY_MULTIPART_MAX_BYTES) {
|
|
2609
|
+
const reasonMessage =
|
|
2610
|
+
reason === 'direct-egress-denied'
|
|
2611
|
+
? "direct storage is blocked by this environment's network egress policy"
|
|
2612
|
+
: 'the connected Deepline server does not support direct-to-storage uploads';
|
|
2578
2613
|
throw new DeeplineError(
|
|
2579
|
-
`Cannot stage ${file.logicalPath} (${formatMegabytes(file.bytes)}):
|
|
2614
|
+
`Cannot stage ${file.logicalPath} (${formatMegabytes(file.bytes)}): ${reasonMessage}, and this file exceeds the ~4.5MB request-body limit the legacy upload path is subject to.`,
|
|
2580
2615
|
413,
|
|
2581
2616
|
'STAGED_FILE_TOO_LARGE_FOR_LEGACY_UPLOAD',
|
|
2582
2617
|
{
|
|
@@ -157,7 +157,7 @@ export const SDK_RELEASE = {
|
|
|
157
157
|
// 0.1.253 makes play-page browser opening opt-in and retires --no-open.
|
|
158
158
|
// 0.1.254 removes the internal operations tree from the published SDK CLI.
|
|
159
159
|
// Operators use the checkout-local deepline-admin binary instead.
|
|
160
|
-
version: '0.1.
|
|
160
|
+
version: '0.1.318',
|
|
161
161
|
contracts: {
|
|
162
162
|
api: {
|
|
163
163
|
name: 'sdk-http-api',
|
package/dist/cli/index.js
CHANGED
|
@@ -1037,7 +1037,7 @@ var SDK_RELEASE = {
|
|
|
1037
1037
|
// 0.1.253 makes play-page browser opening opt-in and retires --no-open.
|
|
1038
1038
|
// 0.1.254 removes the internal operations tree from the published SDK CLI.
|
|
1039
1039
|
// Operators use the checkout-local deepline-admin binary instead.
|
|
1040
|
-
version: "0.1.
|
|
1040
|
+
version: "0.1.318",
|
|
1041
1041
|
contracts: {
|
|
1042
1042
|
api: {
|
|
1043
1043
|
name: "sdk-http-api",
|
|
@@ -3527,6 +3527,9 @@ function stagedUploadIdentity(logicalPath, contentHash) {
|
|
|
3527
3527
|
function isStagedUploadMintUnsupported(error) {
|
|
3528
3528
|
return error instanceof DeeplineError && error.statusCode === 404;
|
|
3529
3529
|
}
|
|
3530
|
+
function isStagedUploadDirectEgressDenied(error) {
|
|
3531
|
+
return error instanceof DeeplineError && error.code === "STAGED_FILE_UPLOAD_EGRESS_DENIED";
|
|
3532
|
+
}
|
|
3530
3533
|
function formatMegabytes(bytes) {
|
|
3531
3534
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
3532
3535
|
}
|
|
@@ -3547,10 +3550,11 @@ async function uploadStagedFileToPresignedUrl(input2) {
|
|
|
3547
3550
|
return;
|
|
3548
3551
|
}
|
|
3549
3552
|
const text = await response.text().catch(() => "");
|
|
3553
|
+
const egressDenied = response.status === 403 && response.headers.get("x-deny-reason")?.trim().toLowerCase() === "host_not_allowed";
|
|
3550
3554
|
lastError = new DeeplineError(
|
|
3551
3555
|
`Direct storage upload of ${input2.logicalPath} failed: ${response.status} ${text.slice(0, 200)}`,
|
|
3552
3556
|
response.status,
|
|
3553
|
-
"STAGED_FILE_UPLOAD_FAILED",
|
|
3557
|
+
egressDenied ? "STAGED_FILE_UPLOAD_EGRESS_DENIED" : "STAGED_FILE_UPLOAD_FAILED",
|
|
3554
3558
|
{ logicalPath: input2.logicalPath }
|
|
3555
3559
|
);
|
|
3556
3560
|
if (response.status < 500 && response.status !== 429) {
|
|
@@ -4450,7 +4454,7 @@ var DeeplineClient = class {
|
|
|
4450
4454
|
upload
|
|
4451
4455
|
);
|
|
4452
4456
|
}
|
|
4453
|
-
await Promise.all(
|
|
4457
|
+
const directResults = await Promise.all(
|
|
4454
4458
|
files.map(async (file) => {
|
|
4455
4459
|
const upload = uploadByIdentity.get(
|
|
4456
4460
|
stagedUploadIdentity(file.logicalPath, file.contentHash)
|
|
@@ -4463,31 +4467,45 @@ var DeeplineClient = class {
|
|
|
4463
4467
|
);
|
|
4464
4468
|
}
|
|
4465
4469
|
if (upload.alreadyStaged || !upload.uploadUrl) {
|
|
4466
|
-
return;
|
|
4470
|
+
return { ref: upload.ref };
|
|
4471
|
+
}
|
|
4472
|
+
try {
|
|
4473
|
+
await uploadStagedFileToPresignedUrl({
|
|
4474
|
+
url: upload.uploadUrl,
|
|
4475
|
+
headers: upload.uploadHeaders ?? {
|
|
4476
|
+
"content-type": file.contentType
|
|
4477
|
+
},
|
|
4478
|
+
body: decodeBase64Bytes(file.contentBase64),
|
|
4479
|
+
logicalPath: file.logicalPath
|
|
4480
|
+
});
|
|
4481
|
+
return { ref: upload.ref };
|
|
4482
|
+
} catch (error) {
|
|
4483
|
+
if (isStagedUploadDirectEgressDenied(error)) {
|
|
4484
|
+
return { fallbackFile: file };
|
|
4485
|
+
}
|
|
4486
|
+
throw error;
|
|
4467
4487
|
}
|
|
4468
|
-
await uploadStagedFileToPresignedUrl({
|
|
4469
|
-
url: upload.uploadUrl,
|
|
4470
|
-
headers: upload.uploadHeaders ?? {
|
|
4471
|
-
"content-type": file.contentType
|
|
4472
|
-
},
|
|
4473
|
-
body: decodeBase64Bytes(file.contentBase64),
|
|
4474
|
-
logicalPath: file.logicalPath
|
|
4475
|
-
});
|
|
4476
4488
|
})
|
|
4477
4489
|
);
|
|
4478
|
-
return
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4490
|
+
return await Promise.all(
|
|
4491
|
+
directResults.map(async (result) => {
|
|
4492
|
+
if ("fallbackFile" in result) {
|
|
4493
|
+
const [ref] = await this.stagePlayFilesViaMultipart(
|
|
4494
|
+
[result.fallbackFile],
|
|
4495
|
+
"direct-egress-denied"
|
|
4496
|
+
);
|
|
4497
|
+
if (!ref) {
|
|
4498
|
+
throw new DeeplineError(
|
|
4499
|
+
`The staging server did not return an upload target for ${result.fallbackFile.logicalPath}.`,
|
|
4500
|
+
void 0,
|
|
4501
|
+
"STAGED_FILE_MINT_INCOMPLETE"
|
|
4502
|
+
);
|
|
4503
|
+
}
|
|
4504
|
+
return ref;
|
|
4505
|
+
}
|
|
4506
|
+
return result.ref;
|
|
4507
|
+
})
|
|
4508
|
+
);
|
|
4491
4509
|
}
|
|
4492
4510
|
/**
|
|
4493
4511
|
* Mint short-lived presigned upload targets for staged play files.
|
|
@@ -4500,11 +4518,12 @@ var DeeplineClient = class {
|
|
|
4500
4518
|
const response = await this.http.post("/api/v2/plays/files/stage/mint", { files });
|
|
4501
4519
|
return response.uploads ?? [];
|
|
4502
4520
|
}
|
|
4503
|
-
async stagePlayFilesViaMultipart(files) {
|
|
4521
|
+
async stagePlayFilesViaMultipart(files, reason = "mint-unsupported") {
|
|
4504
4522
|
for (const file of files) {
|
|
4505
4523
|
if (file.bytes > STAGE_LEGACY_MULTIPART_MAX_BYTES) {
|
|
4524
|
+
const reasonMessage = reason === "direct-egress-denied" ? "direct storage is blocked by this environment's network egress policy" : "the connected Deepline server does not support direct-to-storage uploads";
|
|
4506
4525
|
throw new DeeplineError(
|
|
4507
|
-
`Cannot stage ${file.logicalPath} (${formatMegabytes(file.bytes)}):
|
|
4526
|
+
`Cannot stage ${file.logicalPath} (${formatMegabytes(file.bytes)}): ${reasonMessage}, and this file exceeds the ~4.5MB request-body limit the legacy upload path is subject to.`,
|
|
4508
4527
|
413,
|
|
4509
4528
|
"STAGED_FILE_TOO_LARGE_FOR_LEGACY_UPLOAD",
|
|
4510
4529
|
{
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1022,7 +1022,7 @@ var SDK_RELEASE = {
|
|
|
1022
1022
|
// 0.1.253 makes play-page browser opening opt-in and retires --no-open.
|
|
1023
1023
|
// 0.1.254 removes the internal operations tree from the published SDK CLI.
|
|
1024
1024
|
// Operators use the checkout-local deepline-admin binary instead.
|
|
1025
|
-
version: "0.1.
|
|
1025
|
+
version: "0.1.318",
|
|
1026
1026
|
contracts: {
|
|
1027
1027
|
api: {
|
|
1028
1028
|
name: "sdk-http-api",
|
|
@@ -3512,6 +3512,9 @@ function stagedUploadIdentity(logicalPath, contentHash) {
|
|
|
3512
3512
|
function isStagedUploadMintUnsupported(error) {
|
|
3513
3513
|
return error instanceof DeeplineError && error.statusCode === 404;
|
|
3514
3514
|
}
|
|
3515
|
+
function isStagedUploadDirectEgressDenied(error) {
|
|
3516
|
+
return error instanceof DeeplineError && error.code === "STAGED_FILE_UPLOAD_EGRESS_DENIED";
|
|
3517
|
+
}
|
|
3515
3518
|
function formatMegabytes(bytes) {
|
|
3516
3519
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
3517
3520
|
}
|
|
@@ -3532,10 +3535,11 @@ async function uploadStagedFileToPresignedUrl(input2) {
|
|
|
3532
3535
|
return;
|
|
3533
3536
|
}
|
|
3534
3537
|
const text = await response.text().catch(() => "");
|
|
3538
|
+
const egressDenied = response.status === 403 && response.headers.get("x-deny-reason")?.trim().toLowerCase() === "host_not_allowed";
|
|
3535
3539
|
lastError = new DeeplineError(
|
|
3536
3540
|
`Direct storage upload of ${input2.logicalPath} failed: ${response.status} ${text.slice(0, 200)}`,
|
|
3537
3541
|
response.status,
|
|
3538
|
-
"STAGED_FILE_UPLOAD_FAILED",
|
|
3542
|
+
egressDenied ? "STAGED_FILE_UPLOAD_EGRESS_DENIED" : "STAGED_FILE_UPLOAD_FAILED",
|
|
3539
3543
|
{ logicalPath: input2.logicalPath }
|
|
3540
3544
|
);
|
|
3541
3545
|
if (response.status < 500 && response.status !== 429) {
|
|
@@ -4435,7 +4439,7 @@ var DeeplineClient = class {
|
|
|
4435
4439
|
upload
|
|
4436
4440
|
);
|
|
4437
4441
|
}
|
|
4438
|
-
await Promise.all(
|
|
4442
|
+
const directResults = await Promise.all(
|
|
4439
4443
|
files.map(async (file) => {
|
|
4440
4444
|
const upload = uploadByIdentity.get(
|
|
4441
4445
|
stagedUploadIdentity(file.logicalPath, file.contentHash)
|
|
@@ -4448,31 +4452,45 @@ var DeeplineClient = class {
|
|
|
4448
4452
|
);
|
|
4449
4453
|
}
|
|
4450
4454
|
if (upload.alreadyStaged || !upload.uploadUrl) {
|
|
4451
|
-
return;
|
|
4455
|
+
return { ref: upload.ref };
|
|
4456
|
+
}
|
|
4457
|
+
try {
|
|
4458
|
+
await uploadStagedFileToPresignedUrl({
|
|
4459
|
+
url: upload.uploadUrl,
|
|
4460
|
+
headers: upload.uploadHeaders ?? {
|
|
4461
|
+
"content-type": file.contentType
|
|
4462
|
+
},
|
|
4463
|
+
body: decodeBase64Bytes(file.contentBase64),
|
|
4464
|
+
logicalPath: file.logicalPath
|
|
4465
|
+
});
|
|
4466
|
+
return { ref: upload.ref };
|
|
4467
|
+
} catch (error) {
|
|
4468
|
+
if (isStagedUploadDirectEgressDenied(error)) {
|
|
4469
|
+
return { fallbackFile: file };
|
|
4470
|
+
}
|
|
4471
|
+
throw error;
|
|
4452
4472
|
}
|
|
4453
|
-
await uploadStagedFileToPresignedUrl({
|
|
4454
|
-
url: upload.uploadUrl,
|
|
4455
|
-
headers: upload.uploadHeaders ?? {
|
|
4456
|
-
"content-type": file.contentType
|
|
4457
|
-
},
|
|
4458
|
-
body: decodeBase64Bytes(file.contentBase64),
|
|
4459
|
-
logicalPath: file.logicalPath
|
|
4460
|
-
});
|
|
4461
4473
|
})
|
|
4462
4474
|
);
|
|
4463
|
-
return
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4475
|
+
return await Promise.all(
|
|
4476
|
+
directResults.map(async (result) => {
|
|
4477
|
+
if ("fallbackFile" in result) {
|
|
4478
|
+
const [ref] = await this.stagePlayFilesViaMultipart(
|
|
4479
|
+
[result.fallbackFile],
|
|
4480
|
+
"direct-egress-denied"
|
|
4481
|
+
);
|
|
4482
|
+
if (!ref) {
|
|
4483
|
+
throw new DeeplineError(
|
|
4484
|
+
`The staging server did not return an upload target for ${result.fallbackFile.logicalPath}.`,
|
|
4485
|
+
void 0,
|
|
4486
|
+
"STAGED_FILE_MINT_INCOMPLETE"
|
|
4487
|
+
);
|
|
4488
|
+
}
|
|
4489
|
+
return ref;
|
|
4490
|
+
}
|
|
4491
|
+
return result.ref;
|
|
4492
|
+
})
|
|
4493
|
+
);
|
|
4476
4494
|
}
|
|
4477
4495
|
/**
|
|
4478
4496
|
* Mint short-lived presigned upload targets for staged play files.
|
|
@@ -4485,11 +4503,12 @@ var DeeplineClient = class {
|
|
|
4485
4503
|
const response = await this.http.post("/api/v2/plays/files/stage/mint", { files });
|
|
4486
4504
|
return response.uploads ?? [];
|
|
4487
4505
|
}
|
|
4488
|
-
async stagePlayFilesViaMultipart(files) {
|
|
4506
|
+
async stagePlayFilesViaMultipart(files, reason = "mint-unsupported") {
|
|
4489
4507
|
for (const file of files) {
|
|
4490
4508
|
if (file.bytes > STAGE_LEGACY_MULTIPART_MAX_BYTES) {
|
|
4509
|
+
const reasonMessage = reason === "direct-egress-denied" ? "direct storage is blocked by this environment's network egress policy" : "the connected Deepline server does not support direct-to-storage uploads";
|
|
4491
4510
|
throw new DeeplineError(
|
|
4492
|
-
`Cannot stage ${file.logicalPath} (${formatMegabytes(file.bytes)}):
|
|
4511
|
+
`Cannot stage ${file.logicalPath} (${formatMegabytes(file.bytes)}): ${reasonMessage}, and this file exceeds the ~4.5MB request-body limit the legacy upload path is subject to.`,
|
|
4493
4512
|
413,
|
|
4494
4513
|
"STAGED_FILE_TOO_LARGE_FOR_LEGACY_UPLOAD",
|
|
4495
4514
|
{
|
package/dist/index.js
CHANGED
|
@@ -760,7 +760,7 @@ var SDK_RELEASE = {
|
|
|
760
760
|
// 0.1.253 makes play-page browser opening opt-in and retires --no-open.
|
|
761
761
|
// 0.1.254 removes the internal operations tree from the published SDK CLI.
|
|
762
762
|
// Operators use the checkout-local deepline-admin binary instead.
|
|
763
|
-
version: "0.1.
|
|
763
|
+
version: "0.1.318",
|
|
764
764
|
contracts: {
|
|
765
765
|
api: {
|
|
766
766
|
name: "sdk-http-api",
|
|
@@ -3250,6 +3250,9 @@ function stagedUploadIdentity(logicalPath, contentHash) {
|
|
|
3250
3250
|
function isStagedUploadMintUnsupported(error) {
|
|
3251
3251
|
return error instanceof DeeplineError && error.statusCode === 404;
|
|
3252
3252
|
}
|
|
3253
|
+
function isStagedUploadDirectEgressDenied(error) {
|
|
3254
|
+
return error instanceof DeeplineError && error.code === "STAGED_FILE_UPLOAD_EGRESS_DENIED";
|
|
3255
|
+
}
|
|
3253
3256
|
function formatMegabytes(bytes) {
|
|
3254
3257
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
3255
3258
|
}
|
|
@@ -3270,10 +3273,11 @@ async function uploadStagedFileToPresignedUrl(input) {
|
|
|
3270
3273
|
return;
|
|
3271
3274
|
}
|
|
3272
3275
|
const text = await response.text().catch(() => "");
|
|
3276
|
+
const egressDenied = response.status === 403 && response.headers.get("x-deny-reason")?.trim().toLowerCase() === "host_not_allowed";
|
|
3273
3277
|
lastError = new DeeplineError(
|
|
3274
3278
|
`Direct storage upload of ${input.logicalPath} failed: ${response.status} ${text.slice(0, 200)}`,
|
|
3275
3279
|
response.status,
|
|
3276
|
-
"STAGED_FILE_UPLOAD_FAILED",
|
|
3280
|
+
egressDenied ? "STAGED_FILE_UPLOAD_EGRESS_DENIED" : "STAGED_FILE_UPLOAD_FAILED",
|
|
3277
3281
|
{ logicalPath: input.logicalPath }
|
|
3278
3282
|
);
|
|
3279
3283
|
if (response.status < 500 && response.status !== 429) {
|
|
@@ -4173,7 +4177,7 @@ var DeeplineClient = class {
|
|
|
4173
4177
|
upload
|
|
4174
4178
|
);
|
|
4175
4179
|
}
|
|
4176
|
-
await Promise.all(
|
|
4180
|
+
const directResults = await Promise.all(
|
|
4177
4181
|
files.map(async (file) => {
|
|
4178
4182
|
const upload = uploadByIdentity.get(
|
|
4179
4183
|
stagedUploadIdentity(file.logicalPath, file.contentHash)
|
|
@@ -4186,31 +4190,45 @@ var DeeplineClient = class {
|
|
|
4186
4190
|
);
|
|
4187
4191
|
}
|
|
4188
4192
|
if (upload.alreadyStaged || !upload.uploadUrl) {
|
|
4189
|
-
return;
|
|
4193
|
+
return { ref: upload.ref };
|
|
4194
|
+
}
|
|
4195
|
+
try {
|
|
4196
|
+
await uploadStagedFileToPresignedUrl({
|
|
4197
|
+
url: upload.uploadUrl,
|
|
4198
|
+
headers: upload.uploadHeaders ?? {
|
|
4199
|
+
"content-type": file.contentType
|
|
4200
|
+
},
|
|
4201
|
+
body: decodeBase64Bytes(file.contentBase64),
|
|
4202
|
+
logicalPath: file.logicalPath
|
|
4203
|
+
});
|
|
4204
|
+
return { ref: upload.ref };
|
|
4205
|
+
} catch (error) {
|
|
4206
|
+
if (isStagedUploadDirectEgressDenied(error)) {
|
|
4207
|
+
return { fallbackFile: file };
|
|
4208
|
+
}
|
|
4209
|
+
throw error;
|
|
4190
4210
|
}
|
|
4191
|
-
await uploadStagedFileToPresignedUrl({
|
|
4192
|
-
url: upload.uploadUrl,
|
|
4193
|
-
headers: upload.uploadHeaders ?? {
|
|
4194
|
-
"content-type": file.contentType
|
|
4195
|
-
},
|
|
4196
|
-
body: decodeBase64Bytes(file.contentBase64),
|
|
4197
|
-
logicalPath: file.logicalPath
|
|
4198
|
-
});
|
|
4199
4211
|
})
|
|
4200
4212
|
);
|
|
4201
|
-
return
|
|
4202
|
-
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4213
|
+
return await Promise.all(
|
|
4214
|
+
directResults.map(async (result) => {
|
|
4215
|
+
if ("fallbackFile" in result) {
|
|
4216
|
+
const [ref] = await this.stagePlayFilesViaMultipart(
|
|
4217
|
+
[result.fallbackFile],
|
|
4218
|
+
"direct-egress-denied"
|
|
4219
|
+
);
|
|
4220
|
+
if (!ref) {
|
|
4221
|
+
throw new DeeplineError(
|
|
4222
|
+
`The staging server did not return an upload target for ${result.fallbackFile.logicalPath}.`,
|
|
4223
|
+
void 0,
|
|
4224
|
+
"STAGED_FILE_MINT_INCOMPLETE"
|
|
4225
|
+
);
|
|
4226
|
+
}
|
|
4227
|
+
return ref;
|
|
4228
|
+
}
|
|
4229
|
+
return result.ref;
|
|
4230
|
+
})
|
|
4231
|
+
);
|
|
4214
4232
|
}
|
|
4215
4233
|
/**
|
|
4216
4234
|
* Mint short-lived presigned upload targets for staged play files.
|
|
@@ -4223,11 +4241,12 @@ var DeeplineClient = class {
|
|
|
4223
4241
|
const response = await this.http.post("/api/v2/plays/files/stage/mint", { files });
|
|
4224
4242
|
return response.uploads ?? [];
|
|
4225
4243
|
}
|
|
4226
|
-
async stagePlayFilesViaMultipart(files) {
|
|
4244
|
+
async stagePlayFilesViaMultipart(files, reason = "mint-unsupported") {
|
|
4227
4245
|
for (const file of files) {
|
|
4228
4246
|
if (file.bytes > STAGE_LEGACY_MULTIPART_MAX_BYTES) {
|
|
4247
|
+
const reasonMessage = reason === "direct-egress-denied" ? "direct storage is blocked by this environment's network egress policy" : "the connected Deepline server does not support direct-to-storage uploads";
|
|
4229
4248
|
throw new DeeplineError(
|
|
4230
|
-
`Cannot stage ${file.logicalPath} (${formatMegabytes(file.bytes)}):
|
|
4249
|
+
`Cannot stage ${file.logicalPath} (${formatMegabytes(file.bytes)}): ${reasonMessage}, and this file exceeds the ~4.5MB request-body limit the legacy upload path is subject to.`,
|
|
4231
4250
|
413,
|
|
4232
4251
|
"STAGED_FILE_TOO_LARGE_FOR_LEGACY_UPLOAD",
|
|
4233
4252
|
{
|
package/dist/index.mjs
CHANGED
|
@@ -686,7 +686,7 @@ var SDK_RELEASE = {
|
|
|
686
686
|
// 0.1.253 makes play-page browser opening opt-in and retires --no-open.
|
|
687
687
|
// 0.1.254 removes the internal operations tree from the published SDK CLI.
|
|
688
688
|
// Operators use the checkout-local deepline-admin binary instead.
|
|
689
|
-
version: "0.1.
|
|
689
|
+
version: "0.1.318",
|
|
690
690
|
contracts: {
|
|
691
691
|
api: {
|
|
692
692
|
name: "sdk-http-api",
|
|
@@ -3176,6 +3176,9 @@ function stagedUploadIdentity(logicalPath, contentHash) {
|
|
|
3176
3176
|
function isStagedUploadMintUnsupported(error) {
|
|
3177
3177
|
return error instanceof DeeplineError && error.statusCode === 404;
|
|
3178
3178
|
}
|
|
3179
|
+
function isStagedUploadDirectEgressDenied(error) {
|
|
3180
|
+
return error instanceof DeeplineError && error.code === "STAGED_FILE_UPLOAD_EGRESS_DENIED";
|
|
3181
|
+
}
|
|
3179
3182
|
function formatMegabytes(bytes) {
|
|
3180
3183
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
3181
3184
|
}
|
|
@@ -3196,10 +3199,11 @@ async function uploadStagedFileToPresignedUrl(input) {
|
|
|
3196
3199
|
return;
|
|
3197
3200
|
}
|
|
3198
3201
|
const text = await response.text().catch(() => "");
|
|
3202
|
+
const egressDenied = response.status === 403 && response.headers.get("x-deny-reason")?.trim().toLowerCase() === "host_not_allowed";
|
|
3199
3203
|
lastError = new DeeplineError(
|
|
3200
3204
|
`Direct storage upload of ${input.logicalPath} failed: ${response.status} ${text.slice(0, 200)}`,
|
|
3201
3205
|
response.status,
|
|
3202
|
-
"STAGED_FILE_UPLOAD_FAILED",
|
|
3206
|
+
egressDenied ? "STAGED_FILE_UPLOAD_EGRESS_DENIED" : "STAGED_FILE_UPLOAD_FAILED",
|
|
3203
3207
|
{ logicalPath: input.logicalPath }
|
|
3204
3208
|
);
|
|
3205
3209
|
if (response.status < 500 && response.status !== 429) {
|
|
@@ -4099,7 +4103,7 @@ var DeeplineClient = class {
|
|
|
4099
4103
|
upload
|
|
4100
4104
|
);
|
|
4101
4105
|
}
|
|
4102
|
-
await Promise.all(
|
|
4106
|
+
const directResults = await Promise.all(
|
|
4103
4107
|
files.map(async (file) => {
|
|
4104
4108
|
const upload = uploadByIdentity.get(
|
|
4105
4109
|
stagedUploadIdentity(file.logicalPath, file.contentHash)
|
|
@@ -4112,31 +4116,45 @@ var DeeplineClient = class {
|
|
|
4112
4116
|
);
|
|
4113
4117
|
}
|
|
4114
4118
|
if (upload.alreadyStaged || !upload.uploadUrl) {
|
|
4115
|
-
return;
|
|
4119
|
+
return { ref: upload.ref };
|
|
4120
|
+
}
|
|
4121
|
+
try {
|
|
4122
|
+
await uploadStagedFileToPresignedUrl({
|
|
4123
|
+
url: upload.uploadUrl,
|
|
4124
|
+
headers: upload.uploadHeaders ?? {
|
|
4125
|
+
"content-type": file.contentType
|
|
4126
|
+
},
|
|
4127
|
+
body: decodeBase64Bytes(file.contentBase64),
|
|
4128
|
+
logicalPath: file.logicalPath
|
|
4129
|
+
});
|
|
4130
|
+
return { ref: upload.ref };
|
|
4131
|
+
} catch (error) {
|
|
4132
|
+
if (isStagedUploadDirectEgressDenied(error)) {
|
|
4133
|
+
return { fallbackFile: file };
|
|
4134
|
+
}
|
|
4135
|
+
throw error;
|
|
4116
4136
|
}
|
|
4117
|
-
await uploadStagedFileToPresignedUrl({
|
|
4118
|
-
url: upload.uploadUrl,
|
|
4119
|
-
headers: upload.uploadHeaders ?? {
|
|
4120
|
-
"content-type": file.contentType
|
|
4121
|
-
},
|
|
4122
|
-
body: decodeBase64Bytes(file.contentBase64),
|
|
4123
|
-
logicalPath: file.logicalPath
|
|
4124
|
-
});
|
|
4125
4137
|
})
|
|
4126
4138
|
);
|
|
4127
|
-
return
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4139
|
+
return await Promise.all(
|
|
4140
|
+
directResults.map(async (result) => {
|
|
4141
|
+
if ("fallbackFile" in result) {
|
|
4142
|
+
const [ref] = await this.stagePlayFilesViaMultipart(
|
|
4143
|
+
[result.fallbackFile],
|
|
4144
|
+
"direct-egress-denied"
|
|
4145
|
+
);
|
|
4146
|
+
if (!ref) {
|
|
4147
|
+
throw new DeeplineError(
|
|
4148
|
+
`The staging server did not return an upload target for ${result.fallbackFile.logicalPath}.`,
|
|
4149
|
+
void 0,
|
|
4150
|
+
"STAGED_FILE_MINT_INCOMPLETE"
|
|
4151
|
+
);
|
|
4152
|
+
}
|
|
4153
|
+
return ref;
|
|
4154
|
+
}
|
|
4155
|
+
return result.ref;
|
|
4156
|
+
})
|
|
4157
|
+
);
|
|
4140
4158
|
}
|
|
4141
4159
|
/**
|
|
4142
4160
|
* Mint short-lived presigned upload targets for staged play files.
|
|
@@ -4149,11 +4167,12 @@ var DeeplineClient = class {
|
|
|
4149
4167
|
const response = await this.http.post("/api/v2/plays/files/stage/mint", { files });
|
|
4150
4168
|
return response.uploads ?? [];
|
|
4151
4169
|
}
|
|
4152
|
-
async stagePlayFilesViaMultipart(files) {
|
|
4170
|
+
async stagePlayFilesViaMultipart(files, reason = "mint-unsupported") {
|
|
4153
4171
|
for (const file of files) {
|
|
4154
4172
|
if (file.bytes > STAGE_LEGACY_MULTIPART_MAX_BYTES) {
|
|
4173
|
+
const reasonMessage = reason === "direct-egress-denied" ? "direct storage is blocked by this environment's network egress policy" : "the connected Deepline server does not support direct-to-storage uploads";
|
|
4155
4174
|
throw new DeeplineError(
|
|
4156
|
-
`Cannot stage ${file.logicalPath} (${formatMegabytes(file.bytes)}):
|
|
4175
|
+
`Cannot stage ${file.logicalPath} (${formatMegabytes(file.bytes)}): ${reasonMessage}, and this file exceeds the ~4.5MB request-body limit the legacy upload path is subject to.`,
|
|
4157
4176
|
413,
|
|
4158
4177
|
"STAGED_FILE_TOO_LARGE_FOR_LEGACY_UPLOAD",
|
|
4159
4178
|
{
|