@agimon-ai/po-author 0.0.0-alpha.72 → 0.0.0-alpha.73

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.
@@ -958,6 +958,7 @@ const VIDEO_MIMES = {
958
958
  const IMAGE_SAVE_MAX_BYTES = 33554432;
959
959
  const PDF_MAX_BYTES = 33554432;
960
960
  const VIDEO_MAX_BYTES = 134217728;
961
+ const READ_CHUNK_BYTES = 1048576;
961
962
  const extensionOf = (path) => path.toLowerCase().split("/").at(-1)?.split(".").slice(1).at(-1) ?? "";
962
963
  /** Reuse the native decoder's supported image paths for canvas-specific host layout. */
963
964
  function authorNativeIsImage(path) {
@@ -1078,49 +1079,74 @@ async function loadAuthorFile(sessionId, path, signal) {
1078
1079
  if (format !== void 0) return await loadStructuredFile(sessionId, path, format, signal);
1079
1080
  const artifact = path.startsWith(AUTHOR_ARTIFACT_PREFIX);
1080
1081
  const query = { path: artifact ? path.slice(9) : path };
1081
- const result = artifact ? await artifactFiles.session(sessionId).content({
1082
- query,
1083
- signal
1084
- }) : await sessionFiles.session(sessionId).file({
1085
- query,
1086
- signal
1087
- });
1088
- if (!result.ok) throw new Error(result.error || "This file could not be read.");
1089
- const response = result.response;
1090
- if (!response) throw new Error("This file could not be read.");
1082
+ const load = async (headers) => {
1083
+ if (signal.aborted) throw new Error("Opening was cancelled.");
1084
+ const result = artifact ? await artifactFiles.session(sessionId).content({
1085
+ query,
1086
+ headers,
1087
+ signal
1088
+ }) : await sessionFiles.session(sessionId).file({
1089
+ query,
1090
+ headers,
1091
+ signal
1092
+ });
1093
+ const response = result.response;
1094
+ if (result.status === 412) throw new Error("This file changed. Reopen it.");
1095
+ const empty = result.status === 416 && response?.headers.get("content-range") === "bytes */0";
1096
+ if (!result.ok && !empty) throw new Error(result.error || "This file could not be read.");
1097
+ if (!response) throw new Error("This file could not be read.");
1098
+ return response;
1099
+ };
1100
+ const response = await load({ Range: `bytes=0-1048575` });
1091
1101
  const extension = extensionOf(query.path);
1092
1102
  const mediaMime = extension === "pdf" ? "application/pdf" : VIDEO_MIMES[extension];
1093
1103
  const limit = extension === "pdf" ? PDF_MAX_BYTES : mediaMime ? VIDEO_MAX_BYTES : MAX_FILE_BYTES;
1094
1104
  if (Number(response.headers.get("content-length") ?? 0) > limit) throw new Error("This file is too large to open here.");
1095
1105
  const mimeType = (response.headers.get("content-type")?.split(";")[0]?.trim() ?? "").toLowerCase();
1096
1106
  const sourceSha256 = response.headers.get("X-File-SHA256") ?? void 0;
1097
- if (mediaMime !== void 0) {
1098
- const bytes = new Uint8Array(await response.arrayBuffer());
1107
+ let bytes;
1108
+ if (response.status === 206) {
1109
+ const range = /^bytes 0-(\d+)\/(\d+)$/.exec(response.headers.get("content-range") ?? "");
1110
+ const size = range ? Number(range[2]) : NaN;
1111
+ const version = response.headers.get("etag");
1112
+ if (!Number.isSafeInteger(size) || size < 1 || !version || version.startsWith("W/")) throw new Error("This file returned an invalid range or version.");
1113
+ if (size > limit) throw new Error("This file is too large to open here.");
1114
+ if (Number(range[1]) !== Math.min(READ_CHUNK_BYTES, size) - 1) throw new Error("This file returned an invalid range.");
1115
+ bytes = new Uint8Array(size);
1116
+ let part = response;
1117
+ for (let offset = 0; offset < size; offset += READ_CHUNK_BYTES) {
1118
+ const end = Math.min(offset + READ_CHUNK_BYTES, size) - 1;
1119
+ if (offset > 0) part = await load({
1120
+ Range: `bytes=${offset}-${end}`,
1121
+ "If-Match": version
1122
+ });
1123
+ if (part.status !== 206 || part.headers.get("etag") !== version || part.headers.get("content-range") !== `bytes ${offset}-${end}/${size}`) throw new Error("This file changed or returned an invalid range.");
1124
+ const chunk = new Uint8Array(await part.arrayBuffer());
1125
+ if (signal.aborted) throw new Error("Opening was cancelled.");
1126
+ if (chunk.byteLength !== end - offset + 1) throw new Error("This file response is incomplete.");
1127
+ bytes.set(chunk, offset);
1128
+ }
1129
+ } else {
1130
+ bytes = new Uint8Array(await response.arrayBuffer());
1099
1131
  if (signal.aborted) throw new Error("Opening was cancelled.");
1132
+ if (response.status === 416 && bytes.byteLength !== 0) throw new Error("This file response is incomplete.");
1100
1133
  if (bytes.byteLength > limit) throw new Error("This file is too large to open here.");
1101
- return {
1102
- kind: extension === "pdf" ? "pdf" : "video",
1103
- bytes,
1104
- mimeType: mediaMime,
1105
- ...sourceSha256 === void 0 ? {} : { sourceSha256 }
1106
- };
1107
- }
1108
- if (IMAGE_TYPES.has(mimeType)) {
1109
- const bytes = new Uint8Array(await response.arrayBuffer());
1110
- if (signal.aborted) throw new Error("Opening was cancelled.");
1111
- if (bytes.byteLength > MAX_FILE_BYTES) throw new Error("This file is too large to open here.");
1112
- return {
1113
- kind: "image",
1114
- data: encodeBase64(bytes),
1115
- mimeType,
1116
- ...sourceSha256 === void 0 ? {} : { sourceSha256 }
1117
- };
1118
1134
  }
1135
+ if (mediaMime !== void 0) return {
1136
+ kind: extension === "pdf" ? "pdf" : "video",
1137
+ bytes,
1138
+ mimeType: mediaMime,
1139
+ ...sourceSha256 === void 0 ? {} : { sourceSha256 }
1140
+ };
1141
+ if (IMAGE_TYPES.has(mimeType)) return {
1142
+ kind: "image",
1143
+ data: encodeBase64(bytes),
1144
+ mimeType,
1145
+ ...sourceSha256 === void 0 ? {} : { sourceSha256 }
1146
+ };
1119
1147
  if (mimeType.startsWith("text/") || mimeType === "application/json" || TEXT_EXTENSIONS.has(extension)) {
1120
- const text = await response.text();
1121
- if (signal.aborted) throw new Error("Opening was cancelled.");
1148
+ const text = new TextDecoder().decode(bytes);
1122
1149
  if (new Blob([text]).size > MAX_FILE_BYTES) throw new Error("This file is too large to open here.");
1123
- const sourceSha256 = response.headers.get("X-File-SHA256") ?? void 0;
1124
1150
  return {
1125
1151
  kind: "text",
1126
1152
  text,