@gallop.software/studio 1.0.6 → 1.2.0

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.
@@ -19,9 +19,14 @@ function getAllThumbnailPaths(originalPath) {
19
19
  getThumbnailPath(originalPath, "sm")
20
20
  ];
21
21
  }
22
+ function isProcessed(entry) {
23
+ if (!entry) return false;
24
+ return !!(entry.f || entry.lg || entry.md || entry.sm);
25
+ }
22
26
 
23
27
  export {
24
28
  getThumbnailPath,
25
- getAllThumbnailPaths
29
+ getAllThumbnailPaths,
30
+ isProcessed
26
31
  };
27
- //# sourceMappingURL=chunk-FDWPNRNZ.mjs.map
32
+ //# sourceMappingURL=chunk-VQJAJVAQ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\n * Dimensions object {w, h}\n */\nexport interface Dimensions {\n w: number\n h: number\n}\n\n/**\n * Meta entry - works for images and non-images\n * o: original dimensions, b: blurhash, c: CDN index\n * sm/md/lg/f: thumbnail dimensions (presence implies processed)\n */\nexport interface MetaEntry {\n o?: Dimensions // original dimensions {w, h}\n b?: string // blurhash\n sm?: Dimensions // small thumbnail (300px width)\n md?: Dimensions // medium thumbnail (700px width)\n lg?: Dimensions // large thumbnail (1400px width)\n f?: Dimensions // full size (capped at 2560px width)\n c?: number // CDN index - index into _cdns array\n}\n\n/**\n * Full meta schema including special keys\n * _cdns: Array of CDN base URLs\n * Other keys: file paths from public folder\n */\nexport interface FullMeta {\n _cdns?: string[] // Array of CDN base URLs\n [key: string]: MetaEntry | string[] | undefined\n}\n\n/**\n * Meta schema - keyed by path from public folder\n * Example: { \"/portfolio/photo.jpg\": { o: {w:2400,h:1600}, b: \"...\", sm: {w:300,h:200}, ... } }\n */\nexport type LeanMeta = Record<string, MetaEntry>\n\n// Alias for compatibility\nexport type LeanImageEntry = MetaEntry\n\n/**\n * File/folder item for browser\n */\nexport interface FileItem {\n name: string\n path: string\n type: 'file' | 'folder'\n size?: number\n dimensions?: { width: number; height: number }\n isProcessed?: boolean\n cdnPushed?: boolean\n cdnBaseUrl?: string // CDN base URL when pushed to cloud\n isRemote?: boolean // true if CDN URL doesn't match R2 (external import)\n isProtected?: boolean // true for images folder and its contents (cannot select/modify)\n // Folder-specific properties\n fileCount?: number\n totalSize?: number\n // For showing thumbnails - path to -sm version if exists\n thumbnail?: string\n // Whether a processed thumbnail exists\n hasThumbnail?: boolean\n}\n\n/**\n * Studio configuration\n */\nexport interface StudioConfig {\n r2AccountId?: string\n r2AccessKeyId?: string\n r2SecretAccessKey?: string\n r2BucketName?: string\n r2PublicUrl?: string\n thumbnailSizes?: {\n small: number\n medium: number\n large: number\n }\n}\n\n/**\n * Get thumbnail path from original image path\n */\nexport function getThumbnailPath(originalPath: string, size: 'sm' | 'md' | 'lg' | 'full'): string {\n if (size === 'full') {\n const ext = originalPath.match(/\\.\\w+$/)?.[0] || '.jpg'\n const base = originalPath.replace(/\\.\\w+$/, '')\n const outputExt = ext.toLowerCase() === '.png' ? '.png' : '.jpg'\n return `/images${base}${outputExt}`\n }\n const ext = originalPath.match(/\\.\\w+$/)?.[0] || '.jpg'\n const base = originalPath.replace(/\\.\\w+$/, '')\n const outputExt = ext.toLowerCase() === '.png' ? '.png' : '.jpg'\n return `/images${base}-${size}${outputExt}`\n}\n\n/**\n * Get all thumbnail paths for an image\n */\nexport function getAllThumbnailPaths(originalPath: string): string[] {\n return [\n getThumbnailPath(originalPath, 'full'),\n getThumbnailPath(originalPath, 'lg'),\n getThumbnailPath(originalPath, 'md'),\n getThumbnailPath(originalPath, 'sm'),\n ]\n}\n\n/**\n * Check if an image entry is processed (has any thumbnail dimensions)\n */\nexport function isProcessed(entry: MetaEntry | undefined): boolean {\n if (!entry) return false\n return !!(entry.f || entry.lg || entry.md || entry.sm)\n}\n"],"mappings":";AAoFO,SAAS,iBAAiB,cAAsB,MAA2C;AAChG,MAAI,SAAS,QAAQ;AACnB,UAAMA,OAAM,aAAa,MAAM,QAAQ,IAAI,CAAC,KAAK;AACjD,UAAMC,QAAO,aAAa,QAAQ,UAAU,EAAE;AAC9C,UAAMC,aAAYF,KAAI,YAAY,MAAM,SAAS,SAAS;AAC1D,WAAO,UAAUC,KAAI,GAAGC,UAAS;AAAA,EACnC;AACA,QAAM,MAAM,aAAa,MAAM,QAAQ,IAAI,CAAC,KAAK;AACjD,QAAM,OAAO,aAAa,QAAQ,UAAU,EAAE;AAC9C,QAAM,YAAY,IAAI,YAAY,MAAM,SAAS,SAAS;AAC1D,SAAO,UAAU,IAAI,IAAI,IAAI,GAAG,SAAS;AAC3C;AAKO,SAAS,qBAAqB,cAAgC;AACnE,SAAO;AAAA,IACL,iBAAiB,cAAc,MAAM;AAAA,IACrC,iBAAiB,cAAc,IAAI;AAAA,IACnC,iBAAiB,cAAc,IAAI;AAAA,IACnC,iBAAiB,cAAc,IAAI;AAAA,EACrC;AACF;AAKO,SAAS,YAAY,OAAuC;AACjE,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,CAAC,EAAE,MAAM,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM;AACrD;","names":["ext","base","outputExt"]}
@@ -19,9 +19,14 @@ function getAllThumbnailPaths(originalPath) {
19
19
  getThumbnailPath(originalPath, "sm")
20
20
  ];
21
21
  }
22
+ function isProcessed(entry) {
23
+ if (!entry) return false;
24
+ return !!(entry.f || entry.lg || entry.md || entry.sm);
25
+ }
26
+
22
27
 
23
28
 
24
29
 
25
30
 
26
- exports.getThumbnailPath = getThumbnailPath; exports.getAllThumbnailPaths = getAllThumbnailPaths;
27
- //# sourceMappingURL=chunk-WJJHVPLT.js.map
31
+ exports.getThumbnailPath = getThumbnailPath; exports.getAllThumbnailPaths = getAllThumbnailPaths; exports.isProcessed = isProcessed;
32
+ //# sourceMappingURL=chunk-WOHZ4LYG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/chrisb/Sites/studio/dist/chunk-WOHZ4LYG.js","../src/types.ts"],"names":["ext","base","outputExt"],"mappings":"AAAA;ACoFO,SAAS,gBAAA,CAAiB,YAAA,EAAsB,IAAA,EAA2C;AAChG,EAAA,GAAA,CAAI,KAAA,IAAS,MAAA,EAAQ;AACnB,IAAA,MAAMA,KAAAA,kBAAM,YAAA,mBAAa,KAAA,mBAAM,QAAQ,CAAA,4BAAA,CAAI,CAAC,IAAA,GAAK,MAAA;AACjD,IAAA,MAAMC,MAAAA,EAAO,YAAA,CAAa,OAAA,CAAQ,QAAA,EAAU,EAAE,CAAA;AAC9C,IAAA,MAAMC,WAAAA,EAAYF,IAAAA,CAAI,WAAA,CAAY,EAAA,IAAM,OAAA,EAAS,OAAA,EAAS,MAAA;AAC1D,IAAA,OAAO,CAAA,OAAA,EAAUC,KAAI,CAAA,EAAA;AACvB,EAAA;AACyB,EAAA;AACZ,EAAA;AACS,EAAA;AACG,EAAA;AAC3B;AAKgB;AACP,EAAA;AACY,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACnB,EAAA;AACF;AAK4B;AACP,EAAA;AACE,EAAA;AACvB;AD1F2B;AACA;AACA;AACA;AACA;AACA","file":"/Users/chrisb/Sites/studio/dist/chunk-WOHZ4LYG.js","sourcesContent":[null,"/**\n * Dimensions object {w, h}\n */\nexport interface Dimensions {\n w: number\n h: number\n}\n\n/**\n * Meta entry - works for images and non-images\n * o: original dimensions, b: blurhash, c: CDN index\n * sm/md/lg/f: thumbnail dimensions (presence implies processed)\n */\nexport interface MetaEntry {\n o?: Dimensions // original dimensions {w, h}\n b?: string // blurhash\n sm?: Dimensions // small thumbnail (300px width)\n md?: Dimensions // medium thumbnail (700px width)\n lg?: Dimensions // large thumbnail (1400px width)\n f?: Dimensions // full size (capped at 2560px width)\n c?: number // CDN index - index into _cdns array\n}\n\n/**\n * Full meta schema including special keys\n * _cdns: Array of CDN base URLs\n * Other keys: file paths from public folder\n */\nexport interface FullMeta {\n _cdns?: string[] // Array of CDN base URLs\n [key: string]: MetaEntry | string[] | undefined\n}\n\n/**\n * Meta schema - keyed by path from public folder\n * Example: { \"/portfolio/photo.jpg\": { o: {w:2400,h:1600}, b: \"...\", sm: {w:300,h:200}, ... } }\n */\nexport type LeanMeta = Record<string, MetaEntry>\n\n// Alias for compatibility\nexport type LeanImageEntry = MetaEntry\n\n/**\n * File/folder item for browser\n */\nexport interface FileItem {\n name: string\n path: string\n type: 'file' | 'folder'\n size?: number\n dimensions?: { width: number; height: number }\n isProcessed?: boolean\n cdnPushed?: boolean\n cdnBaseUrl?: string // CDN base URL when pushed to cloud\n isRemote?: boolean // true if CDN URL doesn't match R2 (external import)\n isProtected?: boolean // true for images folder and its contents (cannot select/modify)\n // Folder-specific properties\n fileCount?: number\n totalSize?: number\n // For showing thumbnails - path to -sm version if exists\n thumbnail?: string\n // Whether a processed thumbnail exists\n hasThumbnail?: boolean\n}\n\n/**\n * Studio configuration\n */\nexport interface StudioConfig {\n r2AccountId?: string\n r2AccessKeyId?: string\n r2SecretAccessKey?: string\n r2BucketName?: string\n r2PublicUrl?: string\n thumbnailSizes?: {\n small: number\n medium: number\n large: number\n }\n}\n\n/**\n * Get thumbnail path from original image path\n */\nexport function getThumbnailPath(originalPath: string, size: 'sm' | 'md' | 'lg' | 'full'): string {\n if (size === 'full') {\n const ext = originalPath.match(/\\.\\w+$/)?.[0] || '.jpg'\n const base = originalPath.replace(/\\.\\w+$/, '')\n const outputExt = ext.toLowerCase() === '.png' ? '.png' : '.jpg'\n return `/images${base}${outputExt}`\n }\n const ext = originalPath.match(/\\.\\w+$/)?.[0] || '.jpg'\n const base = originalPath.replace(/\\.\\w+$/, '')\n const outputExt = ext.toLowerCase() === '.png' ? '.png' : '.jpg'\n return `/images${base}-${size}${outputExt}`\n}\n\n/**\n * Get all thumbnail paths for an image\n */\nexport function getAllThumbnailPaths(originalPath: string): string[] {\n return [\n getThumbnailPath(originalPath, 'full'),\n getThumbnailPath(originalPath, 'lg'),\n getThumbnailPath(originalPath, 'md'),\n getThumbnailPath(originalPath, 'sm'),\n ]\n}\n\n/**\n * Check if an image entry is processed (has any thumbnail dimensions)\n */\nexport function isProcessed(entry: MetaEntry | undefined): boolean {\n if (!entry) return false\n return !!(entry.f || entry.lg || entry.md || entry.sm)\n}\n"]}
@@ -1,7 +1,8 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
3
 
4
- var _chunkWJJHVPLTjs = require('../chunk-WJJHVPLT.js');
4
+
5
+ var _chunkWOHZ4LYGjs = require('../chunk-WOHZ4LYG.js');
5
6
 
6
7
  // src/handlers/index.ts
7
8
  var _server = require('next/server');
@@ -106,16 +107,18 @@ function getContentType(filePath) {
106
107
 
107
108
  var _sharp = require('sharp'); var _sharp2 = _interopRequireDefault(_sharp);
108
109
  var _blurhash = require('blurhash');
110
+ var FULL_MAX_WIDTH = 2560;
109
111
  var DEFAULT_SIZES = {
110
- small: { width: 300, suffix: "-sm" },
111
- medium: { width: 700, suffix: "-md" },
112
- large: { width: 1400, suffix: "-lg" }
112
+ small: { width: 300, suffix: "-sm", key: "sm" },
113
+ medium: { width: 700, suffix: "-md", key: "md" },
114
+ large: { width: 1400, suffix: "-lg", key: "lg" }
113
115
  };
114
116
  async function processImage(buffer, imageKey) {
115
117
  const sharpInstance = _sharp2.default.call(void 0, buffer);
116
118
  const metadata = await sharpInstance.metadata();
117
119
  const originalWidth = metadata.width || 0;
118
120
  const originalHeight = metadata.height || 0;
121
+ const ratio = originalHeight / originalWidth;
119
122
  const keyWithoutSlash = imageKey.startsWith("/") ? imageKey.slice(1) : imageKey;
120
123
  const baseName = _path2.default.basename(keyWithoutSlash, _path2.default.extname(keyWithoutSlash));
121
124
  const ext = _path2.default.extname(keyWithoutSlash).toLowerCase();
@@ -124,19 +127,34 @@ async function processImage(buffer, imageKey) {
124
127
  await _fs.promises.mkdir(imagesPath, { recursive: true });
125
128
  const isPng = ext === ".png";
126
129
  const outputExt = isPng ? ".png" : ".jpg";
130
+ const entry = {
131
+ o: { w: originalWidth, h: originalHeight }
132
+ };
127
133
  const fullFileName = imageDir === "." ? `${baseName}${outputExt}` : `${imageDir}/${baseName}${outputExt}`;
128
134
  const fullPath = _path2.default.join(process.cwd(), "public", "images", fullFileName);
129
- if (isPng) {
130
- await _sharp2.default.call(void 0, buffer).png({ quality: 85 }).toFile(fullPath);
135
+ let fullWidth = originalWidth;
136
+ let fullHeight = originalHeight;
137
+ if (originalWidth > FULL_MAX_WIDTH) {
138
+ fullWidth = FULL_MAX_WIDTH;
139
+ fullHeight = Math.round(FULL_MAX_WIDTH * ratio);
140
+ if (isPng) {
141
+ await _sharp2.default.call(void 0, buffer).resize(fullWidth, fullHeight).png({ quality: 85 }).toFile(fullPath);
142
+ } else {
143
+ await _sharp2.default.call(void 0, buffer).resize(fullWidth, fullHeight).jpeg({ quality: 85 }).toFile(fullPath);
144
+ }
131
145
  } else {
132
- await _sharp2.default.call(void 0, buffer).jpeg({ quality: 85 }).toFile(fullPath);
146
+ if (isPng) {
147
+ await _sharp2.default.call(void 0, buffer).png({ quality: 85 }).toFile(fullPath);
148
+ } else {
149
+ await _sharp2.default.call(void 0, buffer).jpeg({ quality: 85 }).toFile(fullPath);
150
+ }
133
151
  }
152
+ entry.f = { w: fullWidth, h: fullHeight };
134
153
  for (const [, sizeConfig] of Object.entries(DEFAULT_SIZES)) {
135
- const { width: maxWidth, suffix } = sizeConfig;
154
+ const { width: maxWidth, suffix, key } = sizeConfig;
136
155
  if (originalWidth <= maxWidth) {
137
156
  continue;
138
157
  }
139
- const ratio = originalHeight / originalWidth;
140
158
  const newHeight = Math.round(maxWidth * ratio);
141
159
  const sizeFileName = `${baseName}${suffix}${outputExt}`;
142
160
  const sizeFilePath = imageDir === "." ? sizeFileName : `${imageDir}/${sizeFileName}`;
@@ -146,14 +164,11 @@ async function processImage(buffer, imageKey) {
146
164
  } else {
147
165
  await _sharp2.default.call(void 0, buffer).resize(maxWidth, newHeight).jpeg({ quality: 80 }).toFile(sizePath);
148
166
  }
167
+ entry[key] = { w: maxWidth, h: newHeight };
149
168
  }
150
169
  const { data, info } = await _sharp2.default.call(void 0, buffer).resize(32, 32, { fit: "inside" }).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
151
- const blurhash = _blurhash.encode.call(void 0, new Uint8ClampedArray(data), info.width, info.height, 4, 4);
152
- return {
153
- w: originalWidth,
154
- h: originalHeight,
155
- b: blurhash
156
- };
170
+ entry.b = _blurhash.encode.call(void 0, new Uint8ClampedArray(data), info.width, info.height, 4, 4);
171
+ return entry;
157
172
  }
158
173
 
159
174
  // src/handlers/utils/cdn.ts
@@ -219,7 +234,7 @@ async function uploadToCdn(imageKey) {
219
234
  const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME;
220
235
  if (!bucketName) throw new Error("R2 bucket not configured");
221
236
  const r2 = getR2Client();
222
- for (const thumbPath of _chunkWJJHVPLTjs.getAllThumbnailPaths.call(void 0, imageKey)) {
237
+ for (const thumbPath of _chunkWOHZ4LYGjs.getAllThumbnailPaths.call(void 0, imageKey)) {
223
238
  const localPath = _path2.default.join(process.cwd(), "public", thumbPath);
224
239
  try {
225
240
  const fileBuffer = await _fs.promises.readFile(localPath);
@@ -236,7 +251,7 @@ async function uploadToCdn(imageKey) {
236
251
  }
237
252
  }
238
253
  async function deleteLocalThumbnails(imageKey) {
239
- for (const thumbPath of _chunkWJJHVPLTjs.getAllThumbnailPaths.call(void 0, imageKey)) {
254
+ for (const thumbPath of _chunkWOHZ4LYGjs.getAllThumbnailPaths.call(void 0, imageKey)) {
240
255
  const localPath = _path2.default.join(process.cwd(), "public", thumbPath);
241
256
  try {
242
257
  await _fs.promises.unlink(localPath);
@@ -281,7 +296,7 @@ async function deleteFromCdn(imageKey, hasThumbnails) {
281
296
  } catch (e4) {
282
297
  }
283
298
  if (hasThumbnails) {
284
- for (const thumbPath of _chunkWJJHVPLTjs.getAllThumbnailPaths.call(void 0, imageKey)) {
299
+ for (const thumbPath of _chunkWOHZ4LYGjs.getAllThumbnailPaths.call(void 0, imageKey)) {
285
300
  try {
286
301
  await r2.send(
287
302
  new (0, _clients3.DeleteObjectCommand)({
@@ -400,8 +415,9 @@ async function handleList(request) {
400
415
  let thumbnail;
401
416
  let hasThumbnail = false;
402
417
  let fileSize;
403
- if (isImage && entry.p === 1) {
404
- const thumbPath = _chunkWJJHVPLTjs.getThumbnailPath.call(void 0, key, "sm");
418
+ const entryIsProcessed = _chunkWOHZ4LYGjs.isProcessed.call(void 0, entry);
419
+ if (isImage && entryIsProcessed) {
420
+ const thumbPath = _chunkWOHZ4LYGjs.getThumbnailPath.call(void 0, key, "sm");
405
421
  if (isPushedToCloud && entry.c !== void 0) {
406
422
  const cdnUrl = cdnUrls[entry.c];
407
423
  if (cdnUrl) {
@@ -443,12 +459,12 @@ async function handleList(request) {
443
459
  size: fileSize,
444
460
  thumbnail,
445
461
  hasThumbnail,
446
- isProcessed: entry.p === 1,
462
+ isProcessed: entryIsProcessed,
447
463
  cdnPushed: isPushedToCloud,
448
464
  cdnBaseUrl: fileCdnUrl,
449
465
  isRemote,
450
466
  isProtected: isInsideImagesFolder,
451
- dimensions: entry.w && entry.h ? { width: entry.w, height: entry.h } : void 0
467
+ dimensions: entry.o ? { width: entry.o.w, height: entry.o.h } : void 0
452
468
  });
453
469
  }
454
470
  }
@@ -480,8 +496,9 @@ async function handleSearch(request) {
480
496
  const isRemote = isPushedToCloud && (!r2PublicUrl || fileCdnUrl !== r2PublicUrl);
481
497
  let thumbnail;
482
498
  let hasThumbnail = false;
483
- if (isImage && entry.p === 1) {
484
- const thumbPath = _chunkWJJHVPLTjs.getThumbnailPath.call(void 0, key, "sm");
499
+ const entryIsProcessed = _chunkWOHZ4LYGjs.isProcessed.call(void 0, entry);
500
+ if (isImage && entryIsProcessed) {
501
+ const thumbPath = _chunkWOHZ4LYGjs.getThumbnailPath.call(void 0, key, "sm");
485
502
  if (isPushedToCloud && entry.c !== void 0) {
486
503
  const cdnUrl = cdnUrls[entry.c];
487
504
  if (cdnUrl) {
@@ -514,11 +531,11 @@ async function handleSearch(request) {
514
531
  type: "file",
515
532
  thumbnail,
516
533
  hasThumbnail,
517
- isProcessed: entry.p === 1,
534
+ isProcessed: entryIsProcessed,
518
535
  cdnPushed: isPushedToCloud,
519
536
  cdnBaseUrl: fileCdnUrl,
520
537
  isRemote,
521
- dimensions: entry.w && entry.h ? { width: entry.w, height: entry.h } : void 0
538
+ dimensions: entry.o ? { width: entry.o.w, height: entry.o.h } : void 0
522
539
  });
523
540
  }
524
541
  return _server.NextResponse.json({ items });
@@ -736,7 +753,7 @@ async function handleDelete(request) {
736
753
  for (const key of Object.keys(meta)) {
737
754
  if (key.startsWith(prefix) || key === imageKey) {
738
755
  if (!meta[key].c) {
739
- for (const thumbPath of _chunkWJJHVPLTjs.getAllThumbnailPaths.call(void 0, key)) {
756
+ for (const thumbPath of _chunkWOHZ4LYGjs.getAllThumbnailPaths.call(void 0, key)) {
740
757
  const absoluteThumbPath = _path2.default.join(process.cwd(), "public", thumbPath);
741
758
  try {
742
759
  await _fs.promises.unlink(absoluteThumbPath);
@@ -752,7 +769,7 @@ async function handleDelete(request) {
752
769
  const isInImagesFolder = itemPath.startsWith("public/images/");
753
770
  if (!isInImagesFolder && entry) {
754
771
  if (!isPushedToCloud) {
755
- for (const thumbPath of _chunkWJJHVPLTjs.getAllThumbnailPaths.call(void 0, imageKey)) {
772
+ for (const thumbPath of _chunkWOHZ4LYGjs.getAllThumbnailPaths.call(void 0, imageKey)) {
756
773
  const absoluteThumbPath = _path2.default.join(process.cwd(), "public", thumbPath);
757
774
  try {
758
775
  await _fs.promises.unlink(absoluteThumbPath);
@@ -864,8 +881,8 @@ async function handleRename(request) {
864
881
  const newKey = "/" + newRelativePath;
865
882
  if (meta[oldKey]) {
866
883
  const entry = meta[oldKey];
867
- const oldThumbPaths = _chunkWJJHVPLTjs.getAllThumbnailPaths.call(void 0, oldKey);
868
- const newThumbPaths = _chunkWJJHVPLTjs.getAllThumbnailPaths.call(void 0, newKey);
884
+ const oldThumbPaths = _chunkWOHZ4LYGjs.getAllThumbnailPaths.call(void 0, oldKey);
885
+ const newThumbPaths = _chunkWOHZ4LYGjs.getAllThumbnailPaths.call(void 0, newKey);
869
886
  for (let i = 0; i < oldThumbPaths.length; i++) {
870
887
  const oldThumbPath = _path2.default.join(process.cwd(), "public", oldThumbPaths[i]);
871
888
  const newThumbPath = _path2.default.join(process.cwd(), "public", newThumbPaths[i]);
@@ -949,7 +966,7 @@ async function handleMoveStream(request) {
949
966
  const fileCdnUrl = isInCloud && entry.c !== void 0 ? cdnUrls[entry.c] : void 0;
950
967
  const isRemote = isInCloud && (!r2PublicUrl || fileCdnUrl !== r2PublicUrl);
951
968
  const isPushedToR2 = isInCloud && r2PublicUrl && fileCdnUrl === r2PublicUrl;
952
- const hasProcessedThumbnails = _optionalChain([entry, 'optionalAccess', _19 => _19.p]) === 1;
969
+ const hasProcessedThumbnails = _chunkWOHZ4LYGjs.isProcessed.call(void 0, entry);
953
970
  try {
954
971
  if (isRemote && isImage) {
955
972
  const remoteUrl = `${fileCdnUrl}${oldKey}`;
@@ -957,9 +974,8 @@ async function handleMoveStream(request) {
957
974
  await _fs.promises.mkdir(_path2.default.dirname(newAbsolutePath), { recursive: true });
958
975
  await _fs.promises.writeFile(newAbsolutePath, buffer);
959
976
  const newEntry = {
960
- w: _optionalChain([entry, 'optionalAccess', _20 => _20.w]),
961
- h: _optionalChain([entry, 'optionalAccess', _21 => _21.h]),
962
- b: _optionalChain([entry, 'optionalAccess', _22 => _22.b])
977
+ o: _optionalChain([entry, 'optionalAccess', _19 => _19.o]),
978
+ b: _optionalChain([entry, 'optionalAccess', _20 => _20.b])
963
979
  };
964
980
  delete meta[oldKey];
965
981
  meta[newKey] = newEntry;
@@ -968,17 +984,13 @@ async function handleMoveStream(request) {
968
984
  const buffer = await downloadFromCdn(oldKey);
969
985
  await _fs.promises.mkdir(_path2.default.dirname(newAbsolutePath), { recursive: true });
970
986
  await _fs.promises.writeFile(newAbsolutePath, buffer);
971
- const newEntry = {
972
- w: _optionalChain([entry, 'optionalAccess', _23 => _23.w]),
973
- h: _optionalChain([entry, 'optionalAccess', _24 => _24.h]),
974
- b: _optionalChain([entry, 'optionalAccess', _25 => _25.b])
987
+ let newEntry = {
988
+ o: _optionalChain([entry, 'optionalAccess', _21 => _21.o]),
989
+ b: _optionalChain([entry, 'optionalAccess', _22 => _22.b])
975
990
  };
976
991
  if (hasProcessedThumbnails) {
977
992
  const processedEntry = await processImage(buffer, newKey);
978
- newEntry.w = processedEntry.w;
979
- newEntry.h = processedEntry.h;
980
- newEntry.b = processedEntry.b;
981
- newEntry.p = 1;
993
+ newEntry = { ...newEntry, ...processedEntry };
982
994
  }
983
995
  await uploadOriginalToCdn(newKey);
984
996
  if (hasProcessedThumbnails) {
@@ -992,7 +1004,7 @@ async function handleMoveStream(request) {
992
1004
  if (hasProcessedThumbnails) {
993
1005
  await deleteLocalThumbnails(newKey);
994
1006
  }
995
- newEntry.c = _optionalChain([entry, 'optionalAccess', _26 => _26.c]);
1007
+ newEntry.c = _optionalChain([entry, 'optionalAccess', _23 => _23.c]);
996
1008
  delete meta[oldKey];
997
1009
  meta[newKey] = newEntry;
998
1010
  moved.push(itemPath);
@@ -1017,8 +1029,8 @@ async function handleMoveStream(request) {
1017
1029
  await _fs.promises.rename(absolutePath, newAbsolutePath);
1018
1030
  const stats = await _fs.promises.stat(newAbsolutePath);
1019
1031
  if (stats.isFile() && isImage && entry) {
1020
- const oldThumbPaths = _chunkWJJHVPLTjs.getAllThumbnailPaths.call(void 0, oldKey);
1021
- const newThumbPaths = _chunkWJJHVPLTjs.getAllThumbnailPaths.call(void 0, newKey);
1032
+ const oldThumbPaths = _chunkWOHZ4LYGjs.getAllThumbnailPaths.call(void 0, oldKey);
1033
+ const newThumbPaths = _chunkWOHZ4LYGjs.getAllThumbnailPaths.call(void 0, newKey);
1022
1034
  for (let j = 0; j < oldThumbPaths.length; j++) {
1023
1035
  const oldThumbPath = _path2.default.join(process.cwd(), "public", oldThumbPaths[j]);
1024
1036
  const newThumbPath = _path2.default.join(process.cwd(), "public", newThumbPaths[j]);
@@ -1082,7 +1094,7 @@ async function handleSync(request) {
1082
1094
  const accessKeyId = process.env.CLOUDFLARE_R2_ACCESS_KEY_ID;
1083
1095
  const secretAccessKey = process.env.CLOUDFLARE_R2_SECRET_ACCESS_KEY;
1084
1096
  const bucketName = process.env.CLOUDFLARE_R2_BUCKET_NAME;
1085
- const publicUrl = _optionalChain([process, 'access', _27 => _27.env, 'access', _28 => _28.CLOUDFLARE_R2_PUBLIC_URL, 'optionalAccess', _29 => _29.replace, 'call', _30 => _30(/\/\s*$/, "")]);
1097
+ const publicUrl = _optionalChain([process, 'access', _24 => _24.env, 'access', _25 => _25.CLOUDFLARE_R2_PUBLIC_URL, 'optionalAccess', _26 => _26.replace, 'call', _27 => _27(/\/\s*$/, "")]);
1086
1098
  if (!accountId || !accessKeyId || !secretAccessKey || !bucketName || !publicUrl) {
1087
1099
  return _server.NextResponse.json(
1088
1100
  { error: "R2 not configured. Set CLOUDFLARE_R2_* environment variables." },
@@ -1144,8 +1156,8 @@ async function handleSync(request) {
1144
1156
  })
1145
1157
  );
1146
1158
  urlsToPurge.push(`${publicUrl}${imageKey}`);
1147
- if (!isRemote && entry.p) {
1148
- for (const thumbPath of _chunkWJJHVPLTjs.getAllThumbnailPaths.call(void 0, imageKey)) {
1159
+ if (!isRemote && _chunkWOHZ4LYGjs.isProcessed.call(void 0, entry)) {
1160
+ for (const thumbPath of _chunkWOHZ4LYGjs.getAllThumbnailPaths.call(void 0, imageKey)) {
1149
1161
  const localPath = _path2.default.join(process.cwd(), "public", thumbPath);
1150
1162
  try {
1151
1163
  const fileBuffer = await _fs.promises.readFile(localPath);
@@ -1165,7 +1177,7 @@ async function handleSync(request) {
1165
1177
  entry.c = cdnIndex;
1166
1178
  if (!isRemote) {
1167
1179
  const originalLocalPath = _path2.default.join(process.cwd(), "public", imageKey);
1168
- for (const thumbPath of _chunkWJJHVPLTjs.getAllThumbnailPaths.call(void 0, imageKey)) {
1180
+ for (const thumbPath of _chunkWOHZ4LYGjs.getAllThumbnailPaths.call(void 0, imageKey)) {
1169
1181
  const localPath = _path2.default.join(process.cwd(), "public", thumbPath);
1170
1182
  try {
1171
1183
  await _fs.promises.unlink(localPath);
@@ -1198,7 +1210,7 @@ async function handleSync(request) {
1198
1210
  }
1199
1211
  }
1200
1212
  async function handleReprocess(request) {
1201
- const publicUrl = _optionalChain([process, 'access', _31 => _31.env, 'access', _32 => _32.CLOUDFLARE_R2_PUBLIC_URL, 'optionalAccess', _33 => _33.replace, 'call', _34 => _34(/\/\s*$/, "")]);
1213
+ const publicUrl = _optionalChain([process, 'access', _28 => _28.env, 'access', _29 => _29.CLOUDFLARE_R2_PUBLIC_URL, 'optionalAccess', _30 => _30.replace, 'call', _31 => _31(/\/\s*$/, "")]);
1202
1214
  try {
1203
1215
  const { imageKeys } = await request.json();
1204
1216
  if (!imageKeys || !Array.isArray(imageKeys) || imageKeys.length === 0) {
@@ -1216,7 +1228,7 @@ async function handleReprocess(request) {
1216
1228
  try {
1217
1229
  let buffer;
1218
1230
  const entry = getMetaEntry(meta, imageKey);
1219
- const existingCdnIndex = _optionalChain([entry, 'optionalAccess', _35 => _35.c]);
1231
+ const existingCdnIndex = _optionalChain([entry, 'optionalAccess', _32 => _32.c]);
1220
1232
  const existingCdnUrl = existingCdnIndex !== void 0 ? cdnUrls[existingCdnIndex] : void 0;
1221
1233
  const isInOurR2 = existingCdnUrl === publicUrl;
1222
1234
  const isRemote = existingCdnIndex !== void 0 && !isInOurR2;
@@ -1240,11 +1252,10 @@ async function handleReprocess(request) {
1240
1252
  }
1241
1253
  }
1242
1254
  const updatedEntry = await processImage(buffer, imageKey);
1243
- updatedEntry.p = 1;
1244
1255
  if (isInOurR2) {
1245
1256
  updatedEntry.c = existingCdnIndex;
1246
1257
  await uploadToCdn(imageKey);
1247
- for (const thumbPath of _chunkWJJHVPLTjs.getAllThumbnailPaths.call(void 0, imageKey)) {
1258
+ for (const thumbPath of _chunkWOHZ4LYGjs.getAllThumbnailPaths.call(void 0, imageKey)) {
1248
1259
  urlsToPurge.push(`${publicUrl}${thumbPath}`);
1249
1260
  }
1250
1261
  await deleteLocalThumbnails(imageKey);
@@ -1276,7 +1287,7 @@ async function handleReprocess(request) {
1276
1287
  }
1277
1288
  }
1278
1289
  async function handleProcessAllStream() {
1279
- const publicUrl = _optionalChain([process, 'access', _36 => _36.env, 'access', _37 => _37.CLOUDFLARE_R2_PUBLIC_URL, 'optionalAccess', _38 => _38.replace, 'call', _39 => _39(/\/\s*$/, "")]);
1290
+ const publicUrl = _optionalChain([process, 'access', _33 => _33.env, 'access', _34 => _34.CLOUDFLARE_R2_PUBLIC_URL, 'optionalAccess', _35 => _35.replace, 'call', _36 => _36(/\/\s*$/, "")]);
1280
1291
  const encoder = new TextEncoder();
1281
1292
  const stream = new ReadableStream({
1282
1293
  async start(controller) {
@@ -1297,7 +1308,7 @@ async function handleProcessAllStream() {
1297
1308
  for (const [key, entry] of getFileEntries(meta)) {
1298
1309
  const fileName = _path2.default.basename(key);
1299
1310
  if (!isImageFile(fileName)) continue;
1300
- if (!entry.p) {
1311
+ if (!_chunkWOHZ4LYGjs.isProcessed.call(void 0, entry)) {
1301
1312
  imagesToProcess.push({ key, entry });
1302
1313
  } else {
1303
1314
  alreadyProcessed++;
@@ -1347,10 +1358,10 @@ async function handleProcessAllStream() {
1347
1358
  await _fs.promises.writeFile(destPath, buffer);
1348
1359
  meta[key] = {
1349
1360
  ...entry,
1350
- w: 0,
1351
- h: 0,
1361
+ o: { w: 0, h: 0 },
1352
1362
  b: "",
1353
- p: 1
1363
+ f: { w: 0, h: 0 }
1364
+ // SVG has "full" to indicate processed
1354
1365
  };
1355
1366
  if (isRemote) {
1356
1367
  delete meta[key].c;
@@ -1359,13 +1370,12 @@ async function handleProcessAllStream() {
1359
1370
  const processedEntry = await processImage(buffer, key);
1360
1371
  meta[key] = {
1361
1372
  ...processedEntry,
1362
- p: 1,
1363
1373
  ...isInOurR2 ? { c: existingCdnIndex } : {}
1364
1374
  };
1365
1375
  }
1366
1376
  if (isInOurR2) {
1367
1377
  await uploadToCdn(key);
1368
- for (const thumbPath of _chunkWJJHVPLTjs.getAllThumbnailPaths.call(void 0, key)) {
1378
+ for (const thumbPath of _chunkWOHZ4LYGjs.getAllThumbnailPaths.call(void 0, key)) {
1369
1379
  urlsToPurge.push(`${publicUrl}${thumbPath}`);
1370
1380
  }
1371
1381
  await deleteLocalThumbnails(key);
@@ -1384,7 +1394,7 @@ async function handleProcessAllStream() {
1384
1394
  const trackedPaths = /* @__PURE__ */ new Set();
1385
1395
  for (const [imageKey, entry] of getFileEntries(meta)) {
1386
1396
  if (entry.c === void 0) {
1387
- for (const thumbPath of _chunkWJJHVPLTjs.getAllThumbnailPaths.call(void 0, imageKey)) {
1397
+ for (const thumbPath of _chunkWOHZ4LYGjs.getAllThumbnailPaths.call(void 0, imageKey)) {
1388
1398
  trackedPaths.add(thumbPath);
1389
1399
  }
1390
1400
  }
@@ -1556,7 +1566,7 @@ async function handleScanStream() {
1556
1566
  if (isImage) {
1557
1567
  const ext = _path2.default.extname(relativePath).toLowerCase();
1558
1568
  if (ext === ".svg") {
1559
- meta[imageKey] = { w: 0, h: 0, b: "" };
1569
+ meta[imageKey] = { o: { w: 0, h: 0 }, b: "" };
1560
1570
  } else {
1561
1571
  try {
1562
1572
  const buffer = await _fs.promises.readFile(fullPath);
@@ -1564,12 +1574,11 @@ async function handleScanStream() {
1564
1574
  const { data, info } = await _sharp2.default.call(void 0, buffer).resize(32, 32, { fit: "inside" }).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
1565
1575
  const blurhash = _blurhash.encode.call(void 0, new Uint8ClampedArray(data), info.width, info.height, 4, 4);
1566
1576
  meta[imageKey] = {
1567
- w: metadata.width || 0,
1568
- h: metadata.height || 0,
1577
+ o: { w: metadata.width || 0, h: metadata.height || 0 },
1569
1578
  b: blurhash
1570
1579
  };
1571
1580
  } catch (e37) {
1572
- meta[imageKey] = { w: 0, h: 0 };
1581
+ meta[imageKey] = { o: { w: 0, h: 0 } };
1573
1582
  }
1574
1583
  }
1575
1584
  } else {
@@ -1586,8 +1595,8 @@ async function handleScanStream() {
1586
1595
  const expectedThumbnails = /* @__PURE__ */ new Set();
1587
1596
  const fileEntries = getFileEntries(meta);
1588
1597
  for (const [imageKey, entry] of fileEntries) {
1589
- if (entry.c === void 0 && entry.p === 1) {
1590
- for (const thumbPath of _chunkWJJHVPLTjs.getAllThumbnailPaths.call(void 0, imageKey)) {
1598
+ if (entry.c === void 0 && _chunkWOHZ4LYGjs.isProcessed.call(void 0, entry)) {
1599
+ for (const thumbPath of _chunkWOHZ4LYGjs.getAllThumbnailPaths.call(void 0, imageKey)) {
1591
1600
  expectedThumbnails.add(thumbPath);
1592
1601
  }
1593
1602
  }
@@ -1719,8 +1728,7 @@ async function processRemoteImage(url) {
1719
1728
  const { data, info } = await _sharp2.default.call(void 0, buffer).resize(32, 32, { fit: "inside" }).ensureAlpha().raw().toBuffer({ resolveWithObject: true });
1720
1729
  const blurhash = _blurhash.encode.call(void 0, new Uint8ClampedArray(data), info.width, info.height, 4, 4);
1721
1730
  return {
1722
- w: metadata.width || 0,
1723
- h: metadata.height || 0,
1731
+ o: { w: metadata.width || 0, h: metadata.height || 0 },
1724
1732
  b: blurhash
1725
1733
  };
1726
1734
  }
@@ -1766,8 +1774,7 @@ async function handleImportUrls(request) {
1766
1774
  const cdnIndex = getOrAddCdnIndex(meta, base);
1767
1775
  const imageData = await processRemoteImage(url);
1768
1776
  setMetaEntry(meta, path9, {
1769
- w: imageData.w,
1770
- h: imageData.h,
1777
+ o: imageData.o,
1771
1778
  b: imageData.b,
1772
1779
  c: cdnIndex
1773
1780
  });