@hyperframes/studio-server 0.8.24 → 0.8.26
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/index.js +30 -5
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -160,6 +160,7 @@ function collectProjectSignatureFiles(projectDir, dir, files) {
|
|
|
160
160
|
files.push({
|
|
161
161
|
file,
|
|
162
162
|
mtimeMs: stat.mtimeMs,
|
|
163
|
+
ctimeMs: stat.ctimeMs,
|
|
163
164
|
size: stat.size,
|
|
164
165
|
textContentEligible: isTextContentEligible(file, stat.size)
|
|
165
166
|
});
|
|
@@ -181,6 +182,7 @@ function collectProjectSignatureManifestFiles(projectDir, files) {
|
|
|
181
182
|
files.push({
|
|
182
183
|
file,
|
|
183
184
|
mtimeMs: stat.mtimeMs,
|
|
185
|
+
ctimeMs: stat.ctimeMs,
|
|
184
186
|
size: stat.size,
|
|
185
187
|
textContentEligible: isTextContentEligible(file, stat.size)
|
|
186
188
|
});
|
|
@@ -196,6 +198,8 @@ function createProjectFingerprint(projectDir, files) {
|
|
|
196
198
|
hash.update("\0");
|
|
197
199
|
hash.update(String(entry.mtimeMs));
|
|
198
200
|
hash.update("\0");
|
|
201
|
+
hash.update(String(entry.ctimeMs));
|
|
202
|
+
hash.update("\0");
|
|
199
203
|
hash.update(entry.textContentEligible ? "text" : "binary");
|
|
200
204
|
hash.update("\0");
|
|
201
205
|
}
|
|
@@ -1443,6 +1447,9 @@ function validateGsapMutationRequest(c, body) {
|
|
|
1443
1447
|
if (!body || typeof body !== "object" || !("type" in body) || !body.type) {
|
|
1444
1448
|
return c.json({ error: "mutation type required" }, 400);
|
|
1445
1449
|
}
|
|
1450
|
+
if (containsRawGsapExpression(body)) {
|
|
1451
|
+
return c.json({ error: "raw JavaScript expressions are not accepted" }, 400);
|
|
1452
|
+
}
|
|
1446
1453
|
const unsafeFields = findUnsafeMutationValues(body);
|
|
1447
1454
|
if (unsafeFields.length > 0) return rejectUnsafeMutationValues(c, unsafeFields);
|
|
1448
1455
|
if (body.type === "shift-positions-batch" && (!("shifts" in body) || !Array.isArray(body.shifts))) {
|
|
@@ -1450,6 +1457,12 @@ function validateGsapMutationRequest(c, body) {
|
|
|
1450
1457
|
}
|
|
1451
1458
|
return null;
|
|
1452
1459
|
}
|
|
1460
|
+
function containsRawGsapExpression(value) {
|
|
1461
|
+
if (typeof value === "string") return value.startsWith("__raw:");
|
|
1462
|
+
if (Array.isArray(value)) return value.some(containsRawGsapExpression);
|
|
1463
|
+
if (!value || typeof value !== "object") return false;
|
|
1464
|
+
return Object.values(value).some(containsRawGsapExpression);
|
|
1465
|
+
}
|
|
1453
1466
|
async function prepareGsapMutationScript(c, res, firstMutation) {
|
|
1454
1467
|
const beforeHtml = readFileSync5(res.absPath, "utf-8");
|
|
1455
1468
|
let html = beforeHtml;
|
|
@@ -2748,27 +2761,32 @@ function registerFileRoutes(api, adapter) {
|
|
|
2748
2761
|
parsed.body.operations
|
|
2749
2762
|
);
|
|
2750
2763
|
if (patched === originalContent) {
|
|
2764
|
+
const version2 = fileContentVersion(originalContent);
|
|
2765
|
+
c.header("ETag", version2);
|
|
2751
2766
|
return c.json({
|
|
2752
2767
|
ok: true,
|
|
2753
2768
|
changed: false,
|
|
2754
2769
|
matched,
|
|
2755
2770
|
content: originalContent,
|
|
2756
|
-
path: ctx.filePath
|
|
2771
|
+
path: ctx.filePath,
|
|
2772
|
+
version: version2
|
|
2757
2773
|
});
|
|
2758
2774
|
}
|
|
2759
|
-
const { backupPath } = writeMutationResult(
|
|
2775
|
+
const { backupPath, version } = writeMutationResult(
|
|
2760
2776
|
c,
|
|
2761
2777
|
ctx.project.dir,
|
|
2762
2778
|
ctx.filePath,
|
|
2763
2779
|
ctx.absPath,
|
|
2764
2780
|
patched
|
|
2765
2781
|
);
|
|
2782
|
+
c.header("ETag", version);
|
|
2766
2783
|
return c.json({
|
|
2767
2784
|
ok: true,
|
|
2768
2785
|
changed: true,
|
|
2769
2786
|
matched,
|
|
2770
2787
|
content: patched,
|
|
2771
2788
|
path: ctx.filePath,
|
|
2789
|
+
version,
|
|
2772
2790
|
backupPath
|
|
2773
2791
|
});
|
|
2774
2792
|
});
|
|
@@ -4178,8 +4196,9 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4178
4196
|
if (!adapter.generateThumbnail) {
|
|
4179
4197
|
return c.json({ error: "Thumbnails not available" }, 501);
|
|
4180
4198
|
}
|
|
4181
|
-
const
|
|
4182
|
-
if (!
|
|
4199
|
+
const resolved = await resolveProjectAndSignature(adapter, c.req.param("id"));
|
|
4200
|
+
if (!resolved) return c.json({ error: "not found" }, 404);
|
|
4201
|
+
const { project, signature: projectSignature } = resolved;
|
|
4183
4202
|
let compPath = decodeURIComponent(
|
|
4184
4203
|
c.req.path.replace(`/projects/${project.id}/thumbnail/`, "").split("?")[0] ?? ""
|
|
4185
4204
|
);
|
|
@@ -4232,6 +4251,7 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4232
4251
|
const cacheDir = join11(project.dir, ".thumbnails");
|
|
4233
4252
|
const selectorKey = selector ? `_${selector.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 80)}_${selectorIndex ?? 0}` : "";
|
|
4234
4253
|
const urlVersionKey = urlVersion ? `_${urlVersion.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32)}` : "";
|
|
4254
|
+
const projectSignatureKey = `_${createHash4("sha1").update(projectSignature).digest("hex").slice(0, 16)}`;
|
|
4235
4255
|
const outputScale = outputMode === "source" ? 1 : outputMode === "storyboard" ? Math.min(
|
|
4236
4256
|
1,
|
|
4237
4257
|
STORYBOARD_MAX_OUTPUT_DIMENSION / compW,
|
|
@@ -4239,7 +4259,7 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4239
4259
|
) : Math.min(1, THUMBNAIL_MAX_OUTPUT_WIDTH / compW, THUMBNAIL_MAX_OUTPUT_HEIGHT / compH);
|
|
4240
4260
|
const outputWidth = Math.max(1, Math.round(compW * outputScale));
|
|
4241
4261
|
const outputHeight = Math.max(1, Math.round(compH * outputScale));
|
|
4242
|
-
const cacheKey = `${THUMBNAIL_CACHE_VERSION}${urlVersionKey}${manualEditsKey}${motionKey}${sourceKey}_${format}_${outputMode}_${compPath.replace(/\//g, "_")}_${compW}x${compH}_${outputWidth}x${outputHeight}_${sourceMtime}_${seekTime.toFixed(2)}${selectorKey}.${format === "png" ? "png" : "jpg"}`;
|
|
4262
|
+
const cacheKey = `${THUMBNAIL_CACHE_VERSION}${urlVersionKey}${projectSignatureKey}${manualEditsKey}${motionKey}${sourceKey}_${format}_${outputMode}_${compPath.replace(/\//g, "_")}_${compW}x${compH}_${outputWidth}x${outputHeight}_${sourceMtime}_${seekTime.toFixed(2)}${selectorKey}.${format === "png" ? "png" : "jpg"}`;
|
|
4243
4263
|
const cachePath = join11(cacheDir, cacheKey);
|
|
4244
4264
|
if (!prunedCacheDirs.has(cacheDir)) {
|
|
4245
4265
|
prunedCacheDirs.add(cacheDir);
|
|
@@ -4273,6 +4293,11 @@ function registerThumbnailRoutes(api, adapter) {
|
|
|
4273
4293
|
signal
|
|
4274
4294
|
});
|
|
4275
4295
|
if (!generated) return null;
|
|
4296
|
+
const afterGeneration = await resolveProjectAndSignature(adapter, project.id);
|
|
4297
|
+
const freshSignature = createProjectSignature(project.dir);
|
|
4298
|
+
if (!afterGeneration || afterGeneration.project.dir !== project.dir || afterGeneration.signature !== projectSignature || freshSignature !== projectSignature) {
|
|
4299
|
+
return generated;
|
|
4300
|
+
}
|
|
4276
4301
|
if (!existsSync8(cacheDir)) mkdirSync5(cacheDir, { recursive: true });
|
|
4277
4302
|
writeThumbnailAtomically(cachePath, generated);
|
|
4278
4303
|
return generated;
|