PageSourceSearch

https://zherenzhanglab.github.io/tools/growth-curve-analyser/parameters-webr.js

js zherenzhanglab.github.io collected 2026-10-03 10:14:48 UTC 5,138 bytes, 103 lines download raw bytes

1import {
2  WEBR_MODULE_URL, WEBR_VERSION, GCPLYR_VERSION, WEBR_PACKAGE_REPOSITORY,
3  GCPLYR_ARTIFACT_URL, GCPLYR_ARTIFACT_SHA256, GCPLYR_RUNTIME_DEPENDENCIES
4} from "./webr-gcplyr-config.js";
5import { eligibleParameterMeasurements, parameterInputToCsv, gcplyrParameterScript, parseParameterCsv } from "./parameters-core.js";
6
7let runtimePromise;
8
9function bytesToHex(bytes) {
10  return [...bytes].map(byte => byte.toString(16).padStart(2, "0")).join("");
11}
12
13function tarText(bytes) {
14  const zero = bytes.indexOf(0);
15  return new TextDecoder().decode(zero === -1 ? bytes : bytes.subarray(0, zero));
16}
17
18async function ensureDirectory(webR, path) {
19  const parts = path.split("/").filter(Boolean);
20  let current = "";
21  for (const part of parts) {
22    current += "/" + part;
23    try { await webR.FS.mkdir(current); } catch { /* Directory already exists. */ }
24  }
25}
26
27async function extractTgzToLibrary(webR, artifact) {
28  const stream = new Blob([artifact]).stream().pipeThrough(new DecompressionStream("gzip"));
29  const tar = new Uint8Array(await new Response(stream).arrayBuffer());
30  const libraryPath = await webR.evalRString(".libPaths()[1]");
31  for (let offset = 0; offset + 512 <= tar.length;) {
32    const header = tar.subarray(offset, offset + 512);
33    if (header.every(byte => byte === 0)) break;
34    const name = tarText(header.subarray(0, 100));
35    const prefix = tarText(header.subarray(345, 500));
36    const relativePath = (prefix ? prefix + "/" : "") + name;
37    const size = Number.parseInt(tarText(header.subarray(124, 136)).trim() || "0", 8);
38    const type = String.fromCharCode(header[156] || 48);
39    if (!relativePath || relativePath.startsWith("/") || relativePath.split("/").includes("..")) {
40      throw new Error("Unsafe path in pinned gcplyr artifact.");
41    }
42    const target = libraryPath + "/" + relativePath.replace(/\/$/, "");
43    if (type === "5") {
44      await ensureDirectory(webR, target);
45    } else if (type === "0" || type === "\0") {
46      await ensureDirectory(webR, target.slice(0, target.lastIndexOf("/")));
47      await webR.FS.writeFile(target, tar.slice(offset + 512, offset + 512 + size));
48    }
49    offset += 512 + Math.ceil(size / 512) * 512;
50  }
51}
52
53async function installPinnedGcplyr(webR) {
54  await webR.installPackages(GCPLYR_RUNTIME_DEPENDENCIES, { repos: WEBR_PACKAGE_REPOSITORY });
55  const response = await fetch(GCPLYR_ARTIFACT_URL);
56  if (!response.ok) throw new Error(`Unable to download pinned gcplyr artifact (HTTP ${response.status}).`);
57  const artifact = new Uint8Array(await response.arrayBuffer());
58  const digest = bytesToHex(new Uint8Array(await crypto.subtle.digest("SHA-256", artifact)));
59  if (digest !== GCPLYR_ARTIFACT_SHA256) {
60    throw new Error(`Pinned gcplyr artifact checksum mismatch: expected ${GCPLYR_ARTIFACT_SHA256}, received ${digest}.`);
61  }
62  await extractTgzToLibrary(webR, artifact);
63}
64
65async function createRuntime(onStage) {
66  onStage("runtime", "loading", `Downloading webR ${WEBR_VERSION}`);
67  const { WebR, ChannelType } = await import(WEBR_MODULE_URL);
68  const webR = new WebR({ interactive: false, channelType: ChannelType.PostMessage });
69  await webR.init();
70  onStage("runtime", "done", `webR ${WEBR_VERSION} · PostMessage`);
71  onStage("package", "loading", `Loading checksum-pinned gcplyr ${GCPLYR_VERSION} and dependencies`);
72  await installPinnedGcplyr(webR);
73  const actualVersion = await webR.evalRString('as.character(packageVersion("gcplyr"))');
74  if (actualVersion !== GCPLYR_VERSION) {
75    await webR.close();
76    throw new Error(`Pinned gcplyr version mismatch: expected ${GCPLYR_VERSION}, received ${actualVersion}`);
77  }
78  onStage("package", "done", `gcplyr ${actualVersion}`);
79  return webR;
80}
81
82async function getRuntime(onStage) {
83  if (!runtimePromise) runtimePromise = createRuntime(onStage).catch(error => { runtimePromise = null; throw error; });
84  const runtime = await runtimePromise;
85  onStage("runtime", "done", `webR ${WEBR_VERSION} · PostMessage`);
86  onStage("package", "done", `gcplyr ${GCPLYR_VERSION}`);
87  return runtime;
88}
89
90export async function runGcplyrParameters(processedMeasurements, onStage = () => {}) {
91  const eligible = eligibleParameterMeasurements(processedMeasurements);
92  if (!eligible.length) throw new Error("No eligible sample or control measurements are available for parameter analysis.");
93  const webR = await getRuntime(onStage);
94  onStage("data", "loading", "Copying processed measurements into browser-side R memory");
95  await webR.FS.writeFile("/tmp/processed_measurements.csv", new TextEncoder().encode(parameterInputToCsv(eligible)));
96  onStage("data", "done", `${eligible.length.toLocaleString()} rows · no upload`);
97  onStage("analysis", "loading", "Calculating replicate-level growth parameters");
98  await webR.evalRVoid(gcplyrParameterScript());
99  const bytes = await webR.FS.readFile("/tmp/gcplyr_parameter_results.csv");
100  const rows = parseParameterCsv(new TextDecoder().decode(bytes));
101  onStage("analysis", "done", `${rows.length} replicate curves analysed`);
102  return { rows, eligibleMeasurements: eligible.length, gcplyrVersion: GCPLYR_VERSION, webRVersion: WEBR_VERSION };
103}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.