ryugi-artifacts 1.16.3 → 1.18.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.
package/dist/cli.cjs CHANGED
@@ -922,7 +922,7 @@ async function main(argv) {
922
922
  }, emit);
923
923
  }
924
924
  if (command === "--version" || command === "-v") {
925
- console.log("1.16.3");
925
+ console.log("1.18.0");
926
926
  return 0;
927
927
  }
928
928
  if (command === "--help" || command === "-h") {
package/dist/server.cjs CHANGED
@@ -18585,11 +18585,18 @@ function agentAddOnBoard(value, specs) {
18585
18585
  if (!spec) return { error: `no board ${v6.board}` };
18586
18586
  return asBoardAgentOp({ op: "add", anchor: { page: spec.page, board: v6.board, x: v6.x ?? spec.w, y: v6.y ?? 0 }, text: v6.text });
18587
18587
  }
18588
+ var hashCache = /* @__PURE__ */ new Map();
18588
18589
  function fileHash(dir, relative) {
18589
18590
  const file = resolveBoardFile(dir, relative);
18590
18591
  if (!file) return null;
18591
18592
  try {
18592
- return import_node_crypto2.default.createHash("sha1").update(import_node_fs8.default.readFileSync(file)).digest("hex").slice(0, 12);
18593
+ const stat = import_node_fs8.default.statSync(file);
18594
+ const stamp2 = `${stat.size}:${stat.mtimeMs}`;
18595
+ const cached = hashCache.get(file);
18596
+ if (cached?.stamp === stamp2) return cached.hash;
18597
+ const hash = import_node_crypto2.default.createHash("sha1").update(import_node_fs8.default.readFileSync(file)).digest("hex").slice(0, 12);
18598
+ hashCache.set(file, { stamp: stamp2, hash });
18599
+ return hash;
18593
18600
  } catch {
18594
18601
  return null;
18595
18602
  }
@@ -18608,7 +18615,12 @@ function stamp(anchor, hashes, groups = {}) {
18608
18615
  if (!boards.length) return withoutHashes(anchor);
18609
18616
  return { ...withoutHashes(anchor), hashes: Object.fromEntries(boards.map((board) => [board, hashes[board] ?? "missing"])) };
18610
18617
  }
18611
- var isStale = (thread, hashes) => Object.entries(thread.anchor.hashes ?? {}).some(([board, hash]) => hashes[board] !== hash);
18618
+ var sameMembers = (thread, groups) => {
18619
+ const group = thread.anchor.group;
18620
+ if (!group) return true;
18621
+ return Object.keys(thread.anchor.hashes ?? {}).sort().join("\n") === [...groups[group]?.boards ?? []].sort().join("\n");
18622
+ };
18623
+ var isStale = (thread, hashes, groups) => !sameMembers(thread, groups) || Object.entries(thread.anchor.hashes ?? {}).some(([board, hash]) => hashes[board] !== hash);
18612
18624
  function createBoardCommentRegistry(options) {
18613
18625
  const stores = /* @__PURE__ */ new Map();
18614
18626
  const hashes = /* @__PURE__ */ new Map();
@@ -18633,7 +18645,8 @@ function createBoardCommentRegistry(options) {
18633
18645
  };
18634
18646
  const stale = (id2) => {
18635
18647
  const now = currentHashes(id2);
18636
- return store(id2).current().threads.filter((thread) => isStale(thread, now)).map((thread) => thread.id);
18648
+ const groups = groupSpecs(dirOf(id2));
18649
+ return store(id2).current().threads.filter((thread) => isStale(thread, now, groups)).map((thread) => thread.id);
18637
18650
  };
18638
18651
  const push2 = (id2) => {
18639
18652
  const ids = stale(id2);
@@ -88660,10 +88673,29 @@ function send(res, status, body, headers = {}) {
88660
88673
  function sendJson(res, status, value) {
88661
88674
  send(res, status, JSON.stringify(value), { "Content-Type": MIME2[".json"] });
88662
88675
  }
88663
- function sendFile(res, file, headers = {}) {
88664
- import_node_fs10.default.readFile(file, (err, data) => {
88665
- if (err) send(res, 404, "Not found");
88666
- else send(res, 200, data, { "Content-Type": mimeOf(file), ...headers });
88676
+ function byteRange(range, size) {
88677
+ const match = range && /^bytes=(\d*)-(\d*)$/.exec(range.trim());
88678
+ if (!match || !match[1] && !match[2]) return null;
88679
+ const start = match[1] ? Number(match[1]) : Math.max(0, size - Number(match[2]));
88680
+ const end = match[1] && match[2] ? Math.min(Number(match[2]), size - 1) : size - 1;
88681
+ return start > end || start >= size ? "unsatisfiable" : { start, end };
88682
+ }
88683
+ function sendFile(res, file, headers = {}, range) {
88684
+ import_node_fs10.default.stat(file, (err, stat) => {
88685
+ if (err || !stat.isFile()) {
88686
+ send(res, 404, "Not found");
88687
+ return;
88688
+ }
88689
+ const base = { "Referrer-Policy": "no-referrer", "Cache-Control": "no-store", "Content-Type": mimeOf(file), "Accept-Ranges": "bytes", ...headers };
88690
+ const part = byteRange(range, stat.size);
88691
+ if (part === "unsatisfiable") {
88692
+ res.writeHead(416, { ...base, "Content-Range": `bytes */${stat.size}` });
88693
+ res.end();
88694
+ return;
88695
+ }
88696
+ if (part) res.writeHead(206, { ...base, "Content-Range": `bytes ${part.start}-${part.end}/${stat.size}`, "Content-Length": String(part.end - part.start + 1) });
88697
+ else res.writeHead(200, { ...base, "Content-Length": String(stat.size) });
88698
+ import_node_fs10.default.createReadStream(file, part ?? {}).on("error", () => res.destroy()).pipe(res);
88667
88699
  });
88668
88700
  }
88669
88701
 
@@ -90071,14 +90103,16 @@ function createAutoPublisher(options) {
90071
90103
  var import_node_fs21 = __toESM(require("node:fs"), 1);
90072
90104
  var RESOLVE = { artboard: resolveBoardFile, document: resolveDocumentAsset, html: resolveHtmlFile };
90073
90105
  var isArtifactPage = (type, relative) => type === "html" ? relative === "index.html" : relative.endsWith(".html");
90074
- function readArtifactFile(dir, type, relative, uiRuntimeUrl) {
90106
+ function readArtifactFile(dir, type, relative, uiRuntimeUrl, maxBytes = Infinity) {
90075
90107
  if (!resolveInside(dir, relative)) return null;
90076
90108
  const target = RESOLVE[type](dir, relative);
90077
90109
  if (!target) return null;
90078
90110
  let data;
90079
90111
  try {
90112
+ if (import_node_fs21.default.statSync(target).size > maxBytes) throw new RangeError("too large");
90080
90113
  data = import_node_fs21.default.readFileSync(target);
90081
- } catch {
90114
+ } catch (err) {
90115
+ if (err instanceof RangeError) throw err;
90082
90116
  return null;
90083
90117
  }
90084
90118
  if (!isArtifactPage(type, relative)) return { contentType: mimeOf(target), data };
@@ -90462,7 +90496,7 @@ async function startArtifactsServer(options) {
90462
90496
  }
90463
90497
  };
90464
90498
  const isAuthorized = (req, keyFromQuery) => keyFromQuery !== null && safeEqual(keyFromQuery, token) || safeEqual(parseCookies(req.headers.cookie)[cookieName] ?? "", token);
90465
- const serveContent = (res, rest) => {
90499
+ const serveContent = (res, rest, range) => {
90466
90500
  if (rest.startsWith("runtime/")) {
90467
90501
  if (!runtimeDir) {
90468
90502
  send(res, 404, "Not found");
@@ -90498,7 +90532,7 @@ async function startArtifactsServer(options) {
90498
90532
  const page = ref.type === "html" ? relative === "index.html" : relative.endsWith(".html");
90499
90533
  const headers = { "Content-Security-Policy": CONTENT_CSP[page && ref.type === "document" ? "html" : ref.type], "Access-Control-Allow-Origin": "*" };
90500
90534
  if (!page) {
90501
- sendFile(res, target, headers);
90535
+ sendFile(res, target, headers, range);
90502
90536
  return;
90503
90537
  }
90504
90538
  import_node_fs23.default.readFile(target, "utf8", (err, source) => {
@@ -90598,7 +90632,7 @@ async function startArtifactsServer(options) {
90598
90632
  }
90599
90633
  if (pathname.startsWith("/c/")) {
90600
90634
  if (!pathname.startsWith(contentPrefix)) send(res, 404, "Not found");
90601
- else serveContent(res, pathname.slice(contentPrefix.length));
90635
+ else serveContent(res, pathname.slice(contentPrefix.length), req.headers.range);
90602
90636
  return;
90603
90637
  }
90604
90638
  if (cloud && pathname === "/api/cloud/callback") {
@@ -90826,7 +90860,7 @@ async function startArtifactsServer(options) {
90826
90860
  const request = typeof body === "object" && body !== null ? body : {};
90827
90861
  if (request.op === "meta") return artifactMeta(ref, dir, contentOrigin);
90828
90862
  if (request.op === "file") {
90829
- const file = typeof request.path === "string" ? readArtifactFile(dir, ref.type, request.path, CLOUD_RUNTIME_URL) : null;
90863
+ const file = typeof request.path === "string" ? readArtifactFile(dir, ref.type, request.path, CLOUD_RUNTIME_URL, MAX_RELAY_FILE) : null;
90830
90864
  if (!file) throw new Error("not found");
90831
90865
  if (file.data.length > MAX_RELAY_FILE) throw new Error("too large");
90832
90866
  return { contentType: file.contentType, data: file.data.toString("base64") };
@@ -91045,7 +91079,7 @@ function openReady(payload) {
91045
91079
  }
91046
91080
 
91047
91081
  // package.json
91048
- var version = "1.16.3";
91082
+ var version = "1.18.0";
91049
91083
 
91050
91084
  // src/server/main.ts
91051
91085
  async function main() {
@@ -1 +1 @@
1
- import{i as e}from"./rolldown-runtime-COiEfhsA.js";import{n as t,t as n}from"./jsx-runtime-CldxteV5.js";import{a as r}from"./useSandboxFrame-N5uRz7Cc.js";import{t as i}from"./index.js";import a from"./CloudDocument-DQ8BLzr7.js";import{DocumentView as o,t as s}from"./DocumentPage-CgCHoFVv.js";var c=e(t(),1),l=`1.16.3`,u=e=>/^(\d+)\.(\d+)\./.exec(e??``)?.slice(1,3).join(`.`)??null;function d(e,t){let n=u(e);return n!==null&&n===u(t)}var f=(e,t)=>`电脑上的 ryugi 是 ${t??`未知版本`},网页是 ${e},版本对上之前只能评论`,p=n();function m({id:e,state:t,contentBase:n,liveContentBase:u,comments:m}){let{transport:h,presence:g}=s(e,u??n??``),_=g?.online??!1;r(e,{online:g?.online??null});let[v,y]=(0,c.useState)(!1);if((0,c.useEffect)(()=>{_&&y(!0)},[_]),!h||!(v||_))return!t||!n?(0,p.jsx)(i,{children:g===null?`正在连接…`:`还没发布。电脑在线时,这里会实时显示。`}):(0,p.jsx)(a,{id:e,state:t,contentBase:n,comments:m,offline:g!==null&&!_});let b=_&&d(l,g?.version??null),x=_&&!b?f(l,g?.version??null):null;return(0,p.jsx)(o,{id:e,transport:h,live:_,cloud:{online:_,canEdit:b,reason:x}})}export{m as default};
1
+ import{i as e}from"./rolldown-runtime-COiEfhsA.js";import{n as t,t as n}from"./jsx-runtime-CldxteV5.js";import{a as r}from"./useSandboxFrame-N5uRz7Cc.js";import{t as i}from"./index.js";import a from"./CloudDocument-DQ8BLzr7.js";import{DocumentView as o,t as s}from"./DocumentPage-CgCHoFVv.js";var c=e(t(),1),l=`1.18.0`,u=e=>/^(\d+)\.(\d+)\./.exec(e??``)?.slice(1,3).join(`.`)??null;function d(e,t){let n=u(e);return n!==null&&n===u(t)}var f=(e,t)=>`电脑上的 ryugi 是 ${t??`未知版本`},网页是 ${e},版本对上之前只能评论`,p=n();function m({id:e,state:t,contentBase:n,liveContentBase:u,comments:m}){let{transport:h,presence:g}=s(e,u??n??``),_=g?.online??!1;r(e,{online:g?.online??null});let[v,y]=(0,c.useState)(!1);if((0,c.useEffect)(()=>{_&&y(!0)},[_]),!h||!(v||_))return!t||!n?(0,p.jsx)(i,{children:g===null?`正在连接…`:`还没发布。电脑在线时,这里会实时显示。`}):(0,p.jsx)(a,{id:e,state:t,contentBase:n,comments:m,offline:g!==null&&!_});let b=_&&d(l,g?.version??null),x=_&&!b?f(l,g?.version??null):null;return(0,p.jsx)(o,{id:e,transport:h,live:_,cloud:{online:_,canEdit:b,reason:x}})}export{m as default};