@hyperframes/aws-lambda 0.8.177 → 0.8.179
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/README.md +9 -4
- package/dist/_chunks/chunk-AK7J3QQN.js +1006 -0
- package/dist/_chunks/chunk-PK7PO3MH.js +596 -0
- package/dist/_chunks/chunk-YS3KY3C5.js +19 -0
- package/dist/_chunks/chunk-ZY5YIH5W.js +380 -0
- package/dist/cdk/index.js +13 -1
- package/dist/contentStore.d.ts +5 -0
- package/dist/handler.js +5 -1137
- package/dist/index.js +26 -1648
- package/dist/s3PlanV2Publisher.d.ts +1 -0
- package/dist/s3Transport.d.ts +14 -1
- package/dist/sdk/deploySite.d.ts +17 -17
- package/dist/sdk/index.d.ts +3 -2
- package/dist/sdk/index.js +38 -572
- package/dist/sdk/prebuiltHandler.d.ts +17 -0
- package/dist/sdk/renderToLambda.d.ts +8 -2
- package/dist/siteManifest.d.ts +18 -0
- package/package.json +4 -2
- package/prebuilt/handler-zip.json +6 -0
- package/prebuilt/template.yaml +782 -0
- package/scripts/build-zip.ts +3 -3
- package/scripts/pin-handler-zip.test.ts +101 -0
- package/scripts/pin-handler-zip.ts +59 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CONTENT_STORE_REFRESH_AFTER_DAYS,
|
|
3
|
+
contentStoreUri
|
|
4
|
+
} from "./chunk-YS3KY3C5.js";
|
|
5
|
+
|
|
6
|
+
// src/s3Transport.ts
|
|
7
|
+
import {
|
|
8
|
+
createReadStream,
|
|
9
|
+
createWriteStream,
|
|
10
|
+
existsSync,
|
|
11
|
+
mkdirSync,
|
|
12
|
+
rmSync,
|
|
13
|
+
statSync
|
|
14
|
+
} from "node:fs";
|
|
15
|
+
import { createHash } from "node:crypto";
|
|
16
|
+
import { dirname } from "node:path";
|
|
17
|
+
import { pipeline } from "node:stream/promises";
|
|
18
|
+
import {
|
|
19
|
+
CopyObjectCommand,
|
|
20
|
+
GetObjectCommand,
|
|
21
|
+
HeadObjectCommand,
|
|
22
|
+
PutObjectCommand
|
|
23
|
+
} from "@aws-sdk/client-s3";
|
|
24
|
+
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
25
|
+
import * as tar from "tar";
|
|
26
|
+
function parseS3Uri(uri) {
|
|
27
|
+
if (!uri.startsWith("s3://")) {
|
|
28
|
+
throw new Error(`[s3Transport] expected s3:// URI, got: ${JSON.stringify(uri)}`);
|
|
29
|
+
}
|
|
30
|
+
const rest = uri.slice("s3://".length);
|
|
31
|
+
const slash = rest.indexOf("/");
|
|
32
|
+
if (slash === -1) {
|
|
33
|
+
throw new Error(`[s3Transport] missing key in s3 URI: ${JSON.stringify(uri)}`);
|
|
34
|
+
}
|
|
35
|
+
const bucket = rest.slice(0, slash);
|
|
36
|
+
const key = rest.slice(slash + 1);
|
|
37
|
+
if (!bucket || !key) {
|
|
38
|
+
throw new Error(`[s3Transport] empty bucket or key in s3 URI: ${JSON.stringify(uri)}`);
|
|
39
|
+
}
|
|
40
|
+
return { bucket, key };
|
|
41
|
+
}
|
|
42
|
+
function formatS3Uri(loc) {
|
|
43
|
+
return `s3://${loc.bucket}/${loc.key}`;
|
|
44
|
+
}
|
|
45
|
+
async function downloadS3ObjectToFile(client, uri, destPath) {
|
|
46
|
+
const { bucket, key } = parseS3Uri(uri);
|
|
47
|
+
const response = await client.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
|
|
48
|
+
const body = response.Body;
|
|
49
|
+
if (!body) {
|
|
50
|
+
throw new Error(`[s3Transport] s3 GetObject returned empty body for ${uri}`);
|
|
51
|
+
}
|
|
52
|
+
mkdirSync(dirname(destPath), { recursive: true });
|
|
53
|
+
await pipeline(body, createWriteStream(destPath));
|
|
54
|
+
}
|
|
55
|
+
async function downloadS3ObjectToFileVerified(client, uri, destPath, expectedSha256) {
|
|
56
|
+
assertSha256(expectedSha256);
|
|
57
|
+
await downloadS3ObjectToFile(client, uri, destPath);
|
|
58
|
+
const actual = await sha256File(destPath);
|
|
59
|
+
if (actual !== expectedSha256) {
|
|
60
|
+
rmSync(destPath, { force: true });
|
|
61
|
+
const error = new Error(
|
|
62
|
+
`[s3Transport] PLAN_ARTIFACT_DIGEST_MISMATCH: ${uri} expected ${expectedSha256}, got ${actual}`
|
|
63
|
+
);
|
|
64
|
+
error.name = "PLAN_ARTIFACT_DIGEST_MISMATCH";
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function presignVerifiedS3GetUrl(client, uri, expectedSha256, expectedSize, expiresInSeconds) {
|
|
69
|
+
assertSha256(expectedSha256);
|
|
70
|
+
const { bucket, key } = parseS3Uri(uri);
|
|
71
|
+
const head = await client.send(
|
|
72
|
+
new HeadObjectCommand({ Bucket: bucket, Key: key, ChecksumMode: "ENABLED" })
|
|
73
|
+
);
|
|
74
|
+
const expectedChecksum = Buffer.from(expectedSha256, "hex").toString("base64");
|
|
75
|
+
if (head.ChecksumSHA256 !== expectedChecksum || head.ContentLength !== expectedSize) {
|
|
76
|
+
const error = new Error(
|
|
77
|
+
`[s3Transport] PLAN_ARTIFACT_DIGEST_MISMATCH: ${uri} expected ${expectedSha256} (${expectedSize} bytes), S3 holds checksum ${String(head.ChecksumSHA256)} (${String(head.ContentLength)} bytes)`
|
|
78
|
+
);
|
|
79
|
+
error.name = "PLAN_ARTIFACT_DIGEST_MISMATCH";
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
return getSignedUrl(client, new GetObjectCommand({ Bucket: bucket, Key: key }), {
|
|
83
|
+
expiresIn: expiresInSeconds
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
async function uploadFileToS3(client, localPath, uri, contentType) {
|
|
87
|
+
if (!existsSync(localPath)) {
|
|
88
|
+
throw new Error(`[s3Transport] upload source missing: ${localPath}`);
|
|
89
|
+
}
|
|
90
|
+
const { bucket, key } = parseS3Uri(uri);
|
|
91
|
+
const size = statSync(localPath).size;
|
|
92
|
+
await client.send(
|
|
93
|
+
new PutObjectCommand({
|
|
94
|
+
Bucket: bucket,
|
|
95
|
+
Key: key,
|
|
96
|
+
Body: createReadStream(localPath),
|
|
97
|
+
ContentType: contentType,
|
|
98
|
+
ContentLength: size
|
|
99
|
+
})
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
async function uploadContentAddressedFileToS3(client, localPath, uri, expectedSha256, contentType) {
|
|
103
|
+
return (await putImmutableFile(client, localPath, uri, expectedSha256, contentType)).outcome;
|
|
104
|
+
}
|
|
105
|
+
async function putImmutableFile(client, localPath, uri, expectedSha256, contentType) {
|
|
106
|
+
assertSha256(expectedSha256);
|
|
107
|
+
if (!existsSync(localPath)) {
|
|
108
|
+
throw new Error(`[s3Transport] upload source missing: ${localPath}`);
|
|
109
|
+
}
|
|
110
|
+
const { bucket, key } = parseS3Uri(uri);
|
|
111
|
+
const size = statSync(localPath).size;
|
|
112
|
+
const existing = await inspectContentAddressedObject(client, bucket, key, size, expectedSha256);
|
|
113
|
+
if (existing.state === "matching")
|
|
114
|
+
return { outcome: "reused", lastModified: existing.lastModified };
|
|
115
|
+
if (existing.state === "conflict") throwImmutableObjectConflict(uri);
|
|
116
|
+
const body = createReadStream(localPath);
|
|
117
|
+
try {
|
|
118
|
+
await client.send(
|
|
119
|
+
new PutObjectCommand({
|
|
120
|
+
Bucket: bucket,
|
|
121
|
+
Key: key,
|
|
122
|
+
Body: body,
|
|
123
|
+
ContentType: contentType,
|
|
124
|
+
ContentLength: size,
|
|
125
|
+
Metadata: { sha256: expectedSha256 },
|
|
126
|
+
ChecksumSHA256: Buffer.from(expectedSha256, "hex").toString("base64"),
|
|
127
|
+
// HEAD followed by an unconditional PUT can overwrite a conflicting
|
|
128
|
+
// object published by a concurrent planner. Conditional create makes
|
|
129
|
+
// immutable CAS and fixed-key manifest publication race-safe.
|
|
130
|
+
IfNoneMatch: "*"
|
|
131
|
+
})
|
|
132
|
+
);
|
|
133
|
+
return { outcome: "uploaded" };
|
|
134
|
+
} catch (error) {
|
|
135
|
+
if (!isS3PreconditionFailed(error)) throw error;
|
|
136
|
+
const raced = await inspectContentAddressedObject(client, bucket, key, size, expectedSha256);
|
|
137
|
+
if (raced.state === "matching") return { outcome: "reused" };
|
|
138
|
+
if (raced.state === "conflict") throwImmutableObjectConflict(uri);
|
|
139
|
+
throw error;
|
|
140
|
+
} finally {
|
|
141
|
+
body.destroy();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function mapConcurrent(values, concurrency, fn) {
|
|
145
|
+
let cursor = 0;
|
|
146
|
+
async function worker() {
|
|
147
|
+
while (cursor < values.length) {
|
|
148
|
+
const index = cursor++;
|
|
149
|
+
await fn(values[index]);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const results = await Promise.allSettled(
|
|
153
|
+
Array.from({ length: Math.min(concurrency, values.length) }, () => worker())
|
|
154
|
+
);
|
|
155
|
+
const failure = results.find(
|
|
156
|
+
(result) => result.status === "rejected"
|
|
157
|
+
);
|
|
158
|
+
if (failure) throw failure.reason;
|
|
159
|
+
}
|
|
160
|
+
async function sha256File(path) {
|
|
161
|
+
const hash = createHash("sha256");
|
|
162
|
+
for await (const chunk of createReadStream(path)) {
|
|
163
|
+
hash.update(chunk);
|
|
164
|
+
}
|
|
165
|
+
return hash.digest("hex");
|
|
166
|
+
}
|
|
167
|
+
function assertSha256(value) {
|
|
168
|
+
if (!/^[a-f0-9]{64}$/.test(value)) {
|
|
169
|
+
throw new Error(
|
|
170
|
+
`[s3Transport] expected lowercase SHA-256 digest, got ${JSON.stringify(value)}`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
async function inspectContentAddressedObject(client, bucket, key, expectedSize, expectedSha256) {
|
|
175
|
+
try {
|
|
176
|
+
const existing = await client.send(
|
|
177
|
+
new HeadObjectCommand({ Bucket: bucket, Key: key, ChecksumMode: "ENABLED" })
|
|
178
|
+
);
|
|
179
|
+
return existing.ContentLength === expectedSize && existing.Metadata?.sha256 === expectedSha256 ? { state: "matching", lastModified: existing.LastModified } : { state: "conflict" };
|
|
180
|
+
} catch (error) {
|
|
181
|
+
if (isS3NotFound(error)) return { state: "missing" };
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
async function putContentStoreFile(client, localPath, bucket, sha256) {
|
|
186
|
+
const uri = contentStoreUri(bucket, sha256);
|
|
187
|
+
const { outcome, lastModified } = await putImmutableFile(client, localPath, uri, sha256);
|
|
188
|
+
if (lastModified) await refreshAgingObject(client, uri, sha256, lastModified);
|
|
189
|
+
return outcome;
|
|
190
|
+
}
|
|
191
|
+
async function ensureContentStoreObject(client, bucket, sha256, sizeBytes) {
|
|
192
|
+
const uri = contentStoreUri(bucket, sha256);
|
|
193
|
+
const { key } = parseS3Uri(uri);
|
|
194
|
+
const existing = await inspectContentAddressedObject(client, bucket, key, sizeBytes, sha256);
|
|
195
|
+
if (existing.state === "matching") {
|
|
196
|
+
if (existing.lastModified) await refreshAgingObject(client, uri, sha256, existing.lastModified);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (existing.state === "conflict") throwImmutableObjectConflict(uri);
|
|
200
|
+
const error = new Error(
|
|
201
|
+
`[s3Transport] PLAN_ARTIFACT_MISSING: ${uri} is not in the content store`
|
|
202
|
+
);
|
|
203
|
+
error.name = "PLAN_ARTIFACT_MISSING";
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
async function presignS3GetUrl(client, uri, expiresInSeconds) {
|
|
207
|
+
const { bucket, key } = parseS3Uri(uri);
|
|
208
|
+
return getSignedUrl(client, new GetObjectCommand({ Bucket: bucket, Key: key }), {
|
|
209
|
+
expiresIn: expiresInSeconds
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
async function refreshAgingObject(client, uri, sha256, lastModified) {
|
|
213
|
+
const { bucket, key } = parseS3Uri(uri);
|
|
214
|
+
const ageMs = Date.now() - lastModified.getTime();
|
|
215
|
+
if (ageMs < CONTENT_STORE_REFRESH_AFTER_DAYS * 864e5) return;
|
|
216
|
+
await client.send(
|
|
217
|
+
new CopyObjectCommand({
|
|
218
|
+
Bucket: bucket,
|
|
219
|
+
Key: key,
|
|
220
|
+
CopySource: `${bucket}/${key}`,
|
|
221
|
+
MetadataDirective: "REPLACE",
|
|
222
|
+
Metadata: { sha256 },
|
|
223
|
+
ChecksumAlgorithm: "SHA256"
|
|
224
|
+
})
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
function throwImmutableObjectConflict(uri) {
|
|
228
|
+
const error = new Error(
|
|
229
|
+
`[s3Transport] PLAN_ARTIFACT_DIGEST_MISMATCH: immutable object ${uri} already exists with different digest metadata or size`
|
|
230
|
+
);
|
|
231
|
+
error.name = "PLAN_ARTIFACT_DIGEST_MISMATCH";
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
async function s3ObjectExists(client, uri) {
|
|
235
|
+
const { bucket, key } = parseS3Uri(uri);
|
|
236
|
+
try {
|
|
237
|
+
await client.send(new HeadObjectCommand({ Bucket: bucket, Key: key }));
|
|
238
|
+
return true;
|
|
239
|
+
} catch (error) {
|
|
240
|
+
if (isS3NotFound(error)) return false;
|
|
241
|
+
throw error;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function isS3NotFound(error) {
|
|
245
|
+
if (!isRecord(error)) return false;
|
|
246
|
+
const metadata = isRecord(error.$metadata) ? error.$metadata : void 0;
|
|
247
|
+
return error.name === "NotFound" || error.name === "NoSuchKey" || metadata?.httpStatusCode === 404;
|
|
248
|
+
}
|
|
249
|
+
function isS3PreconditionFailed(error) {
|
|
250
|
+
if (!isRecord(error)) return false;
|
|
251
|
+
const metadata = isRecord(error.$metadata) ? error.$metadata : void 0;
|
|
252
|
+
return error.name === "PreconditionFailed" || metadata?.httpStatusCode === 412;
|
|
253
|
+
}
|
|
254
|
+
function isRecord(value) {
|
|
255
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
256
|
+
}
|
|
257
|
+
async function tarDirectory(sourceDir, destTarball, files) {
|
|
258
|
+
if (!existsSync(sourceDir) || !statSync(sourceDir).isDirectory()) {
|
|
259
|
+
throw new Error(`[s3Transport] tar source must be an existing directory: ${sourceDir}`);
|
|
260
|
+
}
|
|
261
|
+
mkdirSync(dirname(destTarball), { recursive: true });
|
|
262
|
+
await tar.create(
|
|
263
|
+
{ gzip: true, file: destTarball, cwd: sourceDir, follow: files !== void 0 },
|
|
264
|
+
files ?? ["."]
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
async function untarDirectory(tarballPath, destDir) {
|
|
268
|
+
if (!existsSync(tarballPath)) {
|
|
269
|
+
throw new Error(`[s3Transport] tarball missing: ${tarballPath}`);
|
|
270
|
+
}
|
|
271
|
+
if (existsSync(destDir)) {
|
|
272
|
+
rmSync(destDir, { recursive: true, force: true });
|
|
273
|
+
}
|
|
274
|
+
mkdirSync(destDir, { recursive: true });
|
|
275
|
+
await tar.extract({ file: tarballPath, cwd: destDir });
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/formatExtension.ts
|
|
279
|
+
var FORMAT_EXTENSIONS = {
|
|
280
|
+
mp4: ".mp4",
|
|
281
|
+
mov: ".mov",
|
|
282
|
+
webm: ".webm",
|
|
283
|
+
"png-sequence": ""
|
|
284
|
+
};
|
|
285
|
+
function formatExtension(format) {
|
|
286
|
+
return FORMAT_EXTENSIONS[format];
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// src/siteManifest.ts
|
|
290
|
+
import { readFileSync } from "node:fs";
|
|
291
|
+
import { extname, join } from "node:path";
|
|
292
|
+
var SITE_MANIFEST_NAME = "files.json";
|
|
293
|
+
var SITE_MANIFEST_INVALID = "SITE_MANIFEST_INVALID";
|
|
294
|
+
var VIDEO_EXTENSIONS_PLANNED_IN_PLACE = /* @__PURE__ */ new Set([".mp4", ".mov", ".m4v", ".webm", ".mkv"]);
|
|
295
|
+
var REMOTE_URL_TTL_SECONDS = 3600;
|
|
296
|
+
function siteManifestUri(bucket, siteId) {
|
|
297
|
+
return formatS3Uri({ bucket, key: `sites/${siteId}/${SITE_MANIFEST_NAME}` });
|
|
298
|
+
}
|
|
299
|
+
function siteTarballUri(bucket, siteId) {
|
|
300
|
+
return formatS3Uri({ bucket, key: `sites/${siteId}/project.tar.gz` });
|
|
301
|
+
}
|
|
302
|
+
function isSiteManifestUri(uri) {
|
|
303
|
+
return uri.endsWith(`/${SITE_MANIFEST_NAME}`);
|
|
304
|
+
}
|
|
305
|
+
function serializeSiteManifest(files) {
|
|
306
|
+
return JSON.stringify({ files });
|
|
307
|
+
}
|
|
308
|
+
function parseSiteManifest(text) {
|
|
309
|
+
const value = JSON.parse(text);
|
|
310
|
+
if (!Array.isArray(value.files)) throw siteManifestError("files must be an array");
|
|
311
|
+
const seen = /* @__PURE__ */ new Set();
|
|
312
|
+
return value.files.map((entry, index) => {
|
|
313
|
+
const { path, sha256, sizeBytes } = entry ?? {};
|
|
314
|
+
if (typeof path !== "string" || !isSafeRelativePath(path) || seen.has(path)) {
|
|
315
|
+
throw siteManifestError(`files[${index}].path is not a unique relative path`);
|
|
316
|
+
}
|
|
317
|
+
if (typeof sha256 !== "string" || !/^[a-f0-9]{64}$/.test(sha256)) {
|
|
318
|
+
throw siteManifestError(`files[${index}].sha256 is not a lowercase SHA-256 digest`);
|
|
319
|
+
}
|
|
320
|
+
if (!Number.isSafeInteger(sizeBytes) || sizeBytes < 0) {
|
|
321
|
+
throw siteManifestError(`files[${index}].sizeBytes is not a byte count`);
|
|
322
|
+
}
|
|
323
|
+
seen.add(path);
|
|
324
|
+
return { path, sha256, sizeBytes };
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
async function downloadSiteFiles(s3, manifestUri, projectDir, leaveVideosRemote) {
|
|
328
|
+
const manifestPath = `${projectDir}.${SITE_MANIFEST_NAME}`;
|
|
329
|
+
await downloadS3ObjectToFile(s3, manifestUri, manifestPath);
|
|
330
|
+
const files = parseSiteManifest(readFileSync(manifestPath, "utf-8"));
|
|
331
|
+
const { bucket } = parseS3Uri(manifestUri);
|
|
332
|
+
const remoteFiles = {};
|
|
333
|
+
await mapConcurrent(files, 16, async ({ path, sha256, sizeBytes }) => {
|
|
334
|
+
const uri = contentStoreUri(bucket, sha256);
|
|
335
|
+
if (leaveVideosRemote && VIDEO_EXTENSIONS_PLANNED_IN_PLACE.has(extname(path).toLowerCase())) {
|
|
336
|
+
remoteFiles[path] = {
|
|
337
|
+
url: await presignS3GetUrl(s3, uri, REMOTE_URL_TTL_SECONDS),
|
|
338
|
+
sha256,
|
|
339
|
+
sizeBytes
|
|
340
|
+
};
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
await downloadS3ObjectToFileVerified(s3, uri, join(projectDir, ...path.split("/")), sha256);
|
|
344
|
+
});
|
|
345
|
+
return remoteFiles;
|
|
346
|
+
}
|
|
347
|
+
function isSafeRelativePath(path) {
|
|
348
|
+
return path.split("/").every((segment) => segment !== "" && segment !== "." && segment !== "..") && !path.includes("\\");
|
|
349
|
+
}
|
|
350
|
+
function siteManifestError(detail) {
|
|
351
|
+
const error = new Error(`[siteManifest] ${SITE_MANIFEST_INVALID}: ${detail}`);
|
|
352
|
+
error.name = SITE_MANIFEST_INVALID;
|
|
353
|
+
return error;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export {
|
|
357
|
+
parseS3Uri,
|
|
358
|
+
formatS3Uri,
|
|
359
|
+
downloadS3ObjectToFile,
|
|
360
|
+
downloadS3ObjectToFileVerified,
|
|
361
|
+
presignVerifiedS3GetUrl,
|
|
362
|
+
uploadFileToS3,
|
|
363
|
+
uploadContentAddressedFileToS3,
|
|
364
|
+
mapConcurrent,
|
|
365
|
+
sha256File,
|
|
366
|
+
putContentStoreFile,
|
|
367
|
+
ensureContentStoreObject,
|
|
368
|
+
s3ObjectExists,
|
|
369
|
+
isS3NotFound,
|
|
370
|
+
tarDirectory,
|
|
371
|
+
untarDirectory,
|
|
372
|
+
formatExtension,
|
|
373
|
+
SITE_MANIFEST_INVALID,
|
|
374
|
+
siteManifestUri,
|
|
375
|
+
siteTarballUri,
|
|
376
|
+
isSiteManifestUri,
|
|
377
|
+
serializeSiteManifest,
|
|
378
|
+
parseSiteManifest,
|
|
379
|
+
downloadSiteFiles
|
|
380
|
+
};
|
package/dist/cdk/index.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CONTENT_STORE_PREFIX,
|
|
3
|
+
CONTENT_STORE_RETENTION_DAYS
|
|
4
|
+
} from "../_chunks/chunk-YS3KY3C5.js";
|
|
5
|
+
|
|
1
6
|
// src/cdk/HyperframesRenderStack.ts
|
|
2
7
|
import { dirname, resolve } from "node:path";
|
|
3
8
|
import { fileURLToPath } from "node:url";
|
|
@@ -42,6 +47,12 @@ var HyperframesRenderStack = class extends Construct {
|
|
|
42
47
|
enabled: true,
|
|
43
48
|
prefix: "renders/",
|
|
44
49
|
expiration: Duration.days(7)
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: "ExpireSharedContent",
|
|
53
|
+
enabled: true,
|
|
54
|
+
prefix: `${CONTENT_STORE_PREFIX}/`,
|
|
55
|
+
expiration: Duration.days(CONTENT_STORE_RETENTION_DAYS)
|
|
45
56
|
}
|
|
46
57
|
]
|
|
47
58
|
});
|
|
@@ -149,7 +160,8 @@ var HyperframesRenderStack = class extends Construct {
|
|
|
149
160
|
"PLAN_ARTIFACT_DIGEST_MISMATCH",
|
|
150
161
|
"FORMAT_NOT_SUPPORTED_IN_DISTRIBUTED",
|
|
151
162
|
"CHUNK_EXCEEDS_WORKER_TIMEOUT",
|
|
152
|
-
"ChromeBinaryUnavailableError"
|
|
163
|
+
"ChromeBinaryUnavailableError",
|
|
164
|
+
"SITE_MANIFEST_INVALID"
|
|
153
165
|
];
|
|
154
166
|
const NON_RETRYABLE_CHUNK = [
|
|
155
167
|
"FFMPEG_VERSION_MISMATCH",
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const CONTENT_STORE_PREFIX = "cas/sha256";
|
|
2
|
+
export declare const CONTENT_STORE_RETENTION_DAYS = 30;
|
|
3
|
+
/** Reusing an object at least this old rewrites it, so a plan's objects outlive it by 23+ days. */
|
|
4
|
+
export declare const CONTENT_STORE_REFRESH_AFTER_DAYS = 7;
|
|
5
|
+
export declare function contentStoreUri(bucket: string, sha256: string): string;
|