ryugi-artifacts 1.11.0 → 1.12.1
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 +181 -28
- package/dist/server.cjs +297 -75
- package/dist/viewer/index.js +12 -12
- package/dist/viewer/index2.css +29 -1
- package/package.json +1 -1
package/dist/server.cjs
CHANGED
|
@@ -1563,14 +1563,14 @@ var require_canvas = __commonJS({
|
|
|
1563
1563
|
|
|
1564
1564
|
// src/server/main.ts
|
|
1565
1565
|
var import_node_child_process2 = require("node:child_process");
|
|
1566
|
-
var
|
|
1566
|
+
var import_node_fs22 = __toESM(require("node:fs"), 1);
|
|
1567
1567
|
var import_node_os = __toESM(require("node:os"), 1);
|
|
1568
|
-
var
|
|
1568
|
+
var import_node_path19 = __toESM(require("node:path"), 1);
|
|
1569
1569
|
|
|
1570
1570
|
// src/server/app.ts
|
|
1571
|
-
var
|
|
1571
|
+
var import_node_fs20 = __toESM(require("node:fs"), 1);
|
|
1572
1572
|
var import_node_http = __toESM(require("node:http"), 1);
|
|
1573
|
-
var
|
|
1573
|
+
var import_node_path18 = __toESM(require("node:path"), 1);
|
|
1574
1574
|
|
|
1575
1575
|
// src/shared/types.ts
|
|
1576
1576
|
var ARTIFACT_TYPES = ["artboard", "document", "html"];
|
|
@@ -15642,19 +15642,19 @@ var ElementDOMSlot = class _ElementDOMSlot extends DOMSlot {
|
|
|
15642
15642
|
}
|
|
15643
15643
|
};
|
|
15644
15644
|
function indexPath(root, child) {
|
|
15645
|
-
const
|
|
15645
|
+
const path20 = [];
|
|
15646
15646
|
let node = child;
|
|
15647
15647
|
for (; node !== root && node !== null; node = node.parentNode) {
|
|
15648
15648
|
let i6 = 0;
|
|
15649
15649
|
for (let sibling = node.previousSibling; sibling !== null; sibling = sibling.previousSibling) {
|
|
15650
15650
|
i6++;
|
|
15651
15651
|
}
|
|
15652
|
-
|
|
15652
|
+
path20.push(i6);
|
|
15653
15653
|
}
|
|
15654
15654
|
if (!(node === root)) {
|
|
15655
15655
|
formatDevErrorMessage(`indexPath: root is not a parent of child`);
|
|
15656
15656
|
}
|
|
15657
|
-
return
|
|
15657
|
+
return path20.reverse();
|
|
15658
15658
|
}
|
|
15659
15659
|
// @__NO_SIDE_EFFECTS__
|
|
15660
15660
|
function readLexicalVersion() {
|
|
@@ -30434,9 +30434,9 @@ function getActiveElementDeep(root) {
|
|
|
30434
30434
|
function getComposedEventTarget(event) {
|
|
30435
30435
|
const target = event.target;
|
|
30436
30436
|
if (target !== null && isHTMLElement(target) && target.shadowRoot !== null && typeof event.composedPath === "function") {
|
|
30437
|
-
const
|
|
30438
|
-
if (
|
|
30439
|
-
return
|
|
30437
|
+
const path20 = event.composedPath();
|
|
30438
|
+
if (path20.length > 0) {
|
|
30439
|
+
return path20[0];
|
|
30440
30440
|
}
|
|
30441
30441
|
}
|
|
30442
30442
|
return target;
|
|
@@ -40532,10 +40532,10 @@ function _loadWasmModule(sync, filepath, src, imports2) {
|
|
|
40532
40532
|
var buf = null;
|
|
40533
40533
|
var isNode2 = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
|
|
40534
40534
|
if (filepath && isNode2) {
|
|
40535
|
-
var
|
|
40536
|
-
var
|
|
40535
|
+
var fs25 = null;
|
|
40536
|
+
var path20 = null;
|
|
40537
40537
|
return new Promise((resolve, reject) => {
|
|
40538
|
-
|
|
40538
|
+
fs25.readFile(path20.resolve(__dirname, filepath), (error, buffer) => {
|
|
40539
40539
|
if (error != null) {
|
|
40540
40540
|
reject(error);
|
|
40541
40541
|
} else {
|
|
@@ -41838,10 +41838,10 @@ var LoroDoc = class _LoroDoc {
|
|
|
41838
41838
|
* @param {string} path
|
|
41839
41839
|
* @returns {Value | Container | undefined}
|
|
41840
41840
|
*/
|
|
41841
|
-
getByPath(
|
|
41841
|
+
getByPath(path20) {
|
|
41842
41842
|
try {
|
|
41843
41843
|
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
41844
|
-
const ptr0 = passStringToWasm0(
|
|
41844
|
+
const ptr0 = passStringToWasm0(path20, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
41845
41845
|
const len0 = WASM_VECTOR_LEN;
|
|
41846
41846
|
wasm.lorodoc_getByPath(retptr, this.__wbg_ptr, ptr0, len0);
|
|
41847
41847
|
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
@@ -84952,15 +84952,18 @@ function isCloudRecord(value) {
|
|
|
84952
84952
|
function normalize(raw) {
|
|
84953
84953
|
const config = defaults();
|
|
84954
84954
|
if (typeof raw !== "object" || raw === null) return config;
|
|
84955
|
+
const { autoPublish } = raw;
|
|
84956
|
+
if (typeof autoPublish === "boolean") config.autoPublish = autoPublish;
|
|
84955
84957
|
const artifacts = raw.artifacts;
|
|
84956
84958
|
if (typeof artifacts !== "object" || artifacts === null) return config;
|
|
84957
84959
|
for (const [key2, value] of Object.entries(artifacts)) {
|
|
84958
84960
|
if (typeof value !== "object" || value === null) continue;
|
|
84959
|
-
const { visibility, cloud } = value;
|
|
84961
|
+
const { visibility, cloud, autoPublish: autoPublish2 } = value;
|
|
84960
84962
|
if (visibility !== void 0 && !isVisibility(visibility)) continue;
|
|
84961
84963
|
const entry = {};
|
|
84962
84964
|
if (visibility) entry.visibility = visibility;
|
|
84963
84965
|
if (isCloudRecord(cloud)) entry.cloud = { id: cloud.id, url: cloud.url, publishedAt: cloud.publishedAt, visibility: cloud.visibility };
|
|
84966
|
+
if (autoPublish2 === false) entry.autoPublish = false;
|
|
84964
84967
|
config.artifacts[key2] = entry;
|
|
84965
84968
|
}
|
|
84966
84969
|
return config;
|
|
@@ -85003,10 +85006,20 @@ function updateArtifactConfig(file, key2, patch) {
|
|
|
85003
85006
|
if (patch.visibility) entry.visibility = patch.visibility;
|
|
85004
85007
|
if (patch.cloud === null) delete entry.cloud;
|
|
85005
85008
|
else if (patch.cloud) entry.cloud = patch.cloud;
|
|
85009
|
+
if (patch.autoPublish === null) delete entry.autoPublish;
|
|
85010
|
+
else if (patch.autoPublish === false) entry.autoPublish = false;
|
|
85006
85011
|
config.artifacts[key2] = entry;
|
|
85007
85012
|
writeJsonAtomic(file, config);
|
|
85008
85013
|
return entry;
|
|
85009
85014
|
}
|
|
85015
|
+
function setProjectAutoPublish(file, value) {
|
|
85016
|
+
const { config, warning } = readConfig(file);
|
|
85017
|
+
if (warning) throw new Error(warning);
|
|
85018
|
+
if (value === null) delete config.autoPublish;
|
|
85019
|
+
else config.autoPublish = value;
|
|
85020
|
+
import_node_fs12.default.mkdirSync(import_node_path12.default.dirname(file), { recursive: true });
|
|
85021
|
+
writeJsonAtomic(file, config);
|
|
85022
|
+
}
|
|
85010
85023
|
|
|
85011
85024
|
// src/server/avatars.ts
|
|
85012
85025
|
var import_node_crypto3 = require("node:crypto");
|
|
@@ -85133,7 +85146,7 @@ function callbackPage(result) {
|
|
|
85133
85146
|
const body = result.ok ? `<p class="status">${CIRCLE_CHECK}\u5DF2\u8FDE\u63A5 Ryugi Cloud</p>
|
|
85134
85147
|
<h1>\u5DF2\u767B\u5F55<em>${escapeHtml(result.login)}</em></h1>
|
|
85135
85148
|
<p class="lead">\u8FD9\u53F0\u7535\u8111\u4E0A\u7684 Ryugi \u73B0\u5728\u53EF\u4EE5\u53D1\u5E03 artifact \u4E86\u3002</p>
|
|
85136
|
-
<ul class="rows">${row("\u5728 Ryugi \u9875\u9762\u91CC\u53D1\u5E03\u7684\uFF1F", "\u56DE\u5230 Ryugi\uFF0C\u5206\u4EAB\u9762\u677F\u4F1A\u81EA\u52A8\u5237\u65B0\u3002")}${row("\u8BA9 agent \u53D1\u5E03\u7684\uFF1F", "\u8BA9\u5B83\u518D\u8FD0\u884C\u4E00\u6B21\u53D1\u5E03\u547D\u4EE4\u3002")}</ul>` : `<p class="status">${CIRCLE_ALERT}\u672A\u8FDE\u63A5</p>
|
|
85149
|
+
<ul class="rows">${row("\u5728 Ryugi \u9875\u9762\u91CC\u53D1\u5E03\u7684\uFF1F", "\u56DE\u5230 Ryugi\uFF0C\u5206\u4EAB\u9762\u677F\u4F1A\u81EA\u52A8\u5237\u65B0\u3002")}${row("\u8BA9 agent \u53D1\u5E03\u7684\uFF1F", "\u8BA9\u5B83\u518D\u8FD0\u884C\u4E00\u6B21\u53D1\u5E03\u547D\u4EE4\u3002")}${result.autoPublish ? row("\u81EA\u52A8\u53D1\u5E03\u5DF2\u6253\u5F00", "agent \u6BCF\u8F6E\u7ED3\u675F\u540E\uFF0C\u6539\u8FC7\u7684 artifact \u4F1A\u4EE5\u79C1\u5BC6\u65B9\u5F0F\u540C\u6B65\u5230 Ryugi Cloud\u3002\u5173\u95ED\uFF1A\u8FD0\u884C <code>ryugi-artifacts config --auto-publish off</code>\u3002") : ""}</ul>` : `<p class="status">${CIRCLE_ALERT}\u672A\u8FDE\u63A5</p>
|
|
85137
85150
|
<h1>\u767B\u5F55\u6CA1\u6709\u5B8C\u6210</h1>
|
|
85138
85151
|
<p class="lead">${escapeHtml(result.detail)}</p>
|
|
85139
85152
|
<ul class="rows">${row("\u91CD\u65B0\u767B\u5F55", "\u56DE\u5230 Ryugi\uFF0C\u5728\u5206\u4EAB\u9762\u677F\u91CC\u518D\u70B9\u4E00\u6B21\u300C\u767B\u5F55\u300D\u3002")}</ul>`;
|
|
@@ -85259,12 +85272,39 @@ function withCloudRuntime(type, files) {
|
|
|
85259
85272
|
);
|
|
85260
85273
|
}
|
|
85261
85274
|
|
|
85275
|
+
// src/server/settings.ts
|
|
85276
|
+
var import_node_fs16 = __toESM(require("node:fs"), 1);
|
|
85277
|
+
var import_node_path16 = __toESM(require("node:path"), 1);
|
|
85278
|
+
var settingsFileFor = (credentialsFile2) => import_node_path16.default.join(import_node_path16.default.dirname(credentialsFile2), "config.json");
|
|
85279
|
+
function readSettings(file) {
|
|
85280
|
+
try {
|
|
85281
|
+
const raw = JSON.parse(import_node_fs16.default.readFileSync(file, "utf8"));
|
|
85282
|
+
return typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
|
|
85283
|
+
} catch {
|
|
85284
|
+
return {};
|
|
85285
|
+
}
|
|
85286
|
+
}
|
|
85287
|
+
function readGlobalAutoPublish(file) {
|
|
85288
|
+
const value = readSettings(file).autoPublish;
|
|
85289
|
+
return typeof value === "boolean" ? value : void 0;
|
|
85290
|
+
}
|
|
85291
|
+
function writeGlobalAutoPublish(file, value) {
|
|
85292
|
+
import_node_fs16.default.mkdirSync(import_node_path16.default.dirname(file), { recursive: true, mode: 448 });
|
|
85293
|
+
writeJsonAtomic(file, { ...readSettings(file), autoPublish: value });
|
|
85294
|
+
}
|
|
85295
|
+
function defaultGlobalAutoPublish(file) {
|
|
85296
|
+
if (readGlobalAutoPublish(file) === void 0) writeGlobalAutoPublish(file, true);
|
|
85297
|
+
}
|
|
85298
|
+
function autoPublishEnabled({ globalFile, projectFile }) {
|
|
85299
|
+
return readConfig(projectFile).config.autoPublish ?? readGlobalAutoPublish(globalFile) ?? false;
|
|
85300
|
+
}
|
|
85301
|
+
|
|
85262
85302
|
// src/server/cloud.ts
|
|
85263
85303
|
var LOGIN_STATE_TTL_MS = 10 * 60 * 1e3;
|
|
85264
85304
|
var UPLOAD_CONCURRENCY = 6;
|
|
85265
85305
|
var MAX_JSON_BODY = 4096;
|
|
85266
85306
|
var CLOUD_ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
85267
|
-
var ROUTE = /^\/api\/cloud\/(status|login|logout|artifacts|publish|visibility)(?:\/([^/]+)(?:\/([^/]+))?)?$/;
|
|
85307
|
+
var ROUTE = /^\/api\/cloud\/(status|login|logout|artifacts|publish|visibility|auto-publish)(?:\/([^/]+)(?:\/([^/]+))?)?$/;
|
|
85268
85308
|
var SignedOut = class extends Error {
|
|
85269
85309
|
};
|
|
85270
85310
|
var CloudRefused = class extends Error {
|
|
@@ -85274,6 +85314,15 @@ var CloudRefused = class extends Error {
|
|
|
85274
85314
|
}
|
|
85275
85315
|
status;
|
|
85276
85316
|
};
|
|
85317
|
+
var CloudCopyGone = class extends Error {
|
|
85318
|
+
};
|
|
85319
|
+
var CreateRefused = class extends CloudRefused {
|
|
85320
|
+
constructor(detail) {
|
|
85321
|
+
super(502, `cloud refused (403): ${detail}`);
|
|
85322
|
+
this.detail = detail;
|
|
85323
|
+
}
|
|
85324
|
+
detail;
|
|
85325
|
+
};
|
|
85277
85326
|
async function readJsonBody(req) {
|
|
85278
85327
|
const chunks = [];
|
|
85279
85328
|
let size = 0;
|
|
@@ -85298,7 +85347,13 @@ async function errorMessage(res) {
|
|
|
85298
85347
|
}
|
|
85299
85348
|
}
|
|
85300
85349
|
function createCloudRoutes(options) {
|
|
85301
|
-
const { origin, credentialsFile: credentialsFile2, configFile } = options;
|
|
85350
|
+
const { origin, credentialsFile: credentialsFile2, configFile, project } = options;
|
|
85351
|
+
const settingsFile = settingsFileFor(credentialsFile2);
|
|
85352
|
+
const autoPublishState = () => ({
|
|
85353
|
+
global: readGlobalAutoPublish(settingsFile) ?? null,
|
|
85354
|
+
project: readConfig(configFile).config.autoPublish ?? null,
|
|
85355
|
+
enabled: !!readCredentials(credentialsFile2, origin) && autoPublishEnabled({ globalFile: settingsFile, projectFile: configFile })
|
|
85356
|
+
});
|
|
85302
85357
|
const pendingStates = /* @__PURE__ */ new Map();
|
|
85303
85358
|
const loginUrl = () => {
|
|
85304
85359
|
const now = Date.now();
|
|
@@ -85309,7 +85364,7 @@ function createCloudRoutes(options) {
|
|
|
85309
85364
|
const challenge = import_node_crypto4.default.createHash("sha256").update(verifier).digest("base64url");
|
|
85310
85365
|
return `${origin}/auth/cli?port=${options.port()}&state=${state}&challenge=${challenge}`;
|
|
85311
85366
|
};
|
|
85312
|
-
const cloudFetch = async (method,
|
|
85367
|
+
const cloudFetch = async (method, path20, body) => {
|
|
85313
85368
|
const credentials = readCredentials(credentialsFile2, origin);
|
|
85314
85369
|
if (!credentials) throw new SignedOut();
|
|
85315
85370
|
const headers = { Authorization: `Bearer ${credentials.token}` };
|
|
@@ -85323,7 +85378,7 @@ function createCloudRoutes(options) {
|
|
|
85323
85378
|
}
|
|
85324
85379
|
let res;
|
|
85325
85380
|
try {
|
|
85326
|
-
res = await fetch(`${origin}${
|
|
85381
|
+
res = await fetch(`${origin}${path20}`, { method, headers, body: payload });
|
|
85327
85382
|
} catch (err) {
|
|
85328
85383
|
throw new CloudRefused(502, `could not reach ${origin}: ${err.message}`);
|
|
85329
85384
|
}
|
|
@@ -85335,7 +85390,18 @@ function createCloudRoutes(options) {
|
|
|
85335
85390
|
return res;
|
|
85336
85391
|
};
|
|
85337
85392
|
const artifactEntry = (key2) => readConfig(configFile).config.artifacts[key2] ?? {};
|
|
85338
|
-
const
|
|
85393
|
+
const locks = /* @__PURE__ */ new Map();
|
|
85394
|
+
const serialized = (key2, task) => {
|
|
85395
|
+
const run2 = (locks.get(key2) ?? Promise.resolve()).then(task, task);
|
|
85396
|
+
const tail = run2.catch(() => void 0);
|
|
85397
|
+
locks.set(key2, tail);
|
|
85398
|
+
void tail.then(() => {
|
|
85399
|
+
if (locks.get(key2) === tail) locks.delete(key2);
|
|
85400
|
+
});
|
|
85401
|
+
return run2;
|
|
85402
|
+
};
|
|
85403
|
+
const keyOfCloudId = (cloudId) => Object.entries(readConfig(configFile).config.artifacts).find(([, entry]) => entry.cloud?.id === cloudId)?.[0] ?? `cloud:${cloudId}`;
|
|
85404
|
+
const publish = async (ref, requested, { recreate = true } = {}) => {
|
|
85339
85405
|
const inspected = await options.inspect(ref);
|
|
85340
85406
|
if (!inspected) throw new CloudRefused(404, `${ref.type}/${ref.id} not found`);
|
|
85341
85407
|
if (inspected.reasons.length > 0 || !inspected.payload) return { status: 409, body: { error: "fix these before publishing", reasons: inspected.reasons } };
|
|
@@ -85349,10 +85415,11 @@ function createCloudRoutes(options) {
|
|
|
85349
85415
|
const entry = artifactEntry(key2);
|
|
85350
85416
|
const hashed = files.map((file) => ({ ...file, hash: import_node_crypto4.default.createHash("sha256").update(file.data).digest("hex") }));
|
|
85351
85417
|
const byHash = new Map(hashed.map((file) => [file.hash, file.data]));
|
|
85352
|
-
const manifest = hashed.map(({ path:
|
|
85353
|
-
const version = (visibility) => ({ title: inspected.title, payload: inspected.payload, files: manifest, ...visibility ? { visibility } : {} });
|
|
85418
|
+
const manifest = hashed.map(({ path: path20, hash }) => ({ path: path20, hash }));
|
|
85419
|
+
const version = (visibility) => ({ title: inspected.title, payload: inspected.payload, files: manifest, project, ...visibility ? { visibility } : {} });
|
|
85354
85420
|
const create3 = async () => {
|
|
85355
|
-
const res2 = await cloudFetch("POST", "/api/artifacts", { type: ref.type, visibility: requested ?? entry.visibility ?? "private" });
|
|
85421
|
+
const res2 = await cloudFetch("POST", "/api/artifacts", { type: ref.type, visibility: requested ?? entry.visibility ?? "private", project });
|
|
85422
|
+
if (res2.status === 403) throw new CreateRefused(await errorMessage(res2));
|
|
85356
85423
|
const created = await (await expectOk(res2)).json();
|
|
85357
85424
|
updateArtifactConfig(configFile, key2, {
|
|
85358
85425
|
visibility: created.visibility,
|
|
@@ -85380,7 +85447,10 @@ function createCloudRoutes(options) {
|
|
|
85380
85447
|
return cloudFetch("POST", `/api/artifacts/${id2}/versions`, version(visibility));
|
|
85381
85448
|
};
|
|
85382
85449
|
let res = entry.cloud ? await commit(entry.cloud.id, requested) : await commit(await create3(), void 0);
|
|
85383
|
-
if (res.status === 404 && entry.cloud)
|
|
85450
|
+
if (res.status === 404 && entry.cloud) {
|
|
85451
|
+
if (!recreate) throw new CloudCopyGone();
|
|
85452
|
+
res = await commit(await create3(), void 0);
|
|
85453
|
+
}
|
|
85384
85454
|
const published = await (await expectOk(res)).json();
|
|
85385
85455
|
updateArtifactConfig(configFile, key2, {
|
|
85386
85456
|
visibility: published.visibility,
|
|
@@ -85414,7 +85484,29 @@ function createCloudRoutes(options) {
|
|
|
85414
85484
|
const res = await cloudFetch("DELETE", `/api/artifacts/${cloudId}`);
|
|
85415
85485
|
if (res.status !== 404) await expectOk(res);
|
|
85416
85486
|
for (const [key2, entry] of Object.entries(readConfig(configFile).config.artifacts)) {
|
|
85417
|
-
if (entry.cloud?.id === cloudId) updateArtifactConfig(configFile, key2, { cloud: null });
|
|
85487
|
+
if (entry.cloud?.id === cloudId) updateArtifactConfig(configFile, key2, { cloud: null, autoPublish: false });
|
|
85488
|
+
}
|
|
85489
|
+
};
|
|
85490
|
+
const autoPublish = (key2) => serialized(key2, () => autoPublishNow(key2));
|
|
85491
|
+
const autoPublishNow = async (key2) => {
|
|
85492
|
+
const ref = artifactKeyOf(key2);
|
|
85493
|
+
if (!ref) return { kind: "gone" };
|
|
85494
|
+
const entry = artifactEntry(key2);
|
|
85495
|
+
if (entry.autoPublish === false) return { kind: "opted-out" };
|
|
85496
|
+
try {
|
|
85497
|
+
const result = await publish(ref, void 0, { recreate: false });
|
|
85498
|
+
if (result.status !== 200) return { kind: "not-ready" };
|
|
85499
|
+
return { kind: "published", created: result.body.id !== entry.cloud?.id };
|
|
85500
|
+
} catch (err) {
|
|
85501
|
+
if (err instanceof CloudCopyGone) {
|
|
85502
|
+
updateArtifactConfig(configFile, key2, { cloud: null, autoPublish: false });
|
|
85503
|
+
return { kind: "opted-out" };
|
|
85504
|
+
}
|
|
85505
|
+
if (err instanceof SignedOut) return { kind: "signed-out" };
|
|
85506
|
+
if (err instanceof CreateRefused) return { kind: "cannot-create", message: err.detail };
|
|
85507
|
+
if (err instanceof CloudRefused && err.status === 404) return { kind: "gone" };
|
|
85508
|
+
if (err instanceof CloudRefused && err.status === 413) return { kind: "not-ready" };
|
|
85509
|
+
return { kind: "failed", message: err.message };
|
|
85418
85510
|
}
|
|
85419
85511
|
};
|
|
85420
85512
|
const route = async (req, res, match) => {
|
|
@@ -85424,7 +85516,7 @@ function createCloudRoutes(options) {
|
|
|
85424
85516
|
if (action === "status" && method === "GET" && !first) {
|
|
85425
85517
|
const credentials = readCredentials(credentialsFile2, origin);
|
|
85426
85518
|
const { config, warning } = readConfig(configFile);
|
|
85427
|
-
sendJson(res, 200, { signedIn: !!credentials, login: credentials?.login ?? null, origin, artifacts: config.artifacts, warning: warning ?? null });
|
|
85519
|
+
sendJson(res, 200, { signedIn: !!credentials, login: credentials?.login ?? null, origin, artifacts: config.artifacts, autoPublish: autoPublishState(), warning: warning ?? null });
|
|
85428
85520
|
return;
|
|
85429
85521
|
}
|
|
85430
85522
|
if (action === "login" && method === "POST" && !first) {
|
|
@@ -85448,20 +85540,30 @@ function createCloudRoutes(options) {
|
|
|
85448
85540
|
}
|
|
85449
85541
|
if (action === "artifacts" && method === "DELETE" && first && !second) {
|
|
85450
85542
|
if (!CLOUD_ID.test(first)) throw new CloudRefused(400, "not a cloud artifact id");
|
|
85451
|
-
await deleteCloud(first);
|
|
85543
|
+
await serialized(keyOfCloudId(first), () => deleteCloud(first));
|
|
85452
85544
|
sendJson(res, 200, { deleted: first });
|
|
85453
85545
|
return;
|
|
85454
85546
|
}
|
|
85455
85547
|
if (action === "publish" && ref && method === "POST") {
|
|
85456
85548
|
const body = await readJsonBody(req);
|
|
85457
85549
|
if (body?.visibility !== void 0 && !isVisibility(body.visibility)) throw new CloudRefused(400, 'visibility must be "public" or "private"');
|
|
85458
|
-
const
|
|
85550
|
+
const key2 = `${ref.type}/${ref.id}`;
|
|
85551
|
+
const requested = body?.visibility;
|
|
85552
|
+
const result = await serialized(key2, async () => {
|
|
85553
|
+
const published = await publish(ref, requested);
|
|
85554
|
+
if (published.status === 200) updateArtifactConfig(configFile, key2, { autoPublish: null });
|
|
85555
|
+
return published;
|
|
85556
|
+
});
|
|
85459
85557
|
sendJson(res, result.status, result.body);
|
|
85460
85558
|
return;
|
|
85461
85559
|
}
|
|
85462
85560
|
if (action === "publish" && ref && method === "DELETE") {
|
|
85463
|
-
const
|
|
85464
|
-
|
|
85561
|
+
const key2 = `${ref.type}/${ref.id}`;
|
|
85562
|
+
await serialized(key2, async () => {
|
|
85563
|
+
const entry = artifactEntry(key2);
|
|
85564
|
+
if (entry.cloud) await deleteCloud(entry.cloud.id);
|
|
85565
|
+
updateArtifactConfig(configFile, key2, { autoPublish: false });
|
|
85566
|
+
});
|
|
85465
85567
|
sendJson(res, 200, { unpublished: `${ref.type}/${ref.id}` });
|
|
85466
85568
|
return;
|
|
85467
85569
|
}
|
|
@@ -85472,6 +85574,15 @@ function createCloudRoutes(options) {
|
|
|
85472
85574
|
sendJson(res, 200, await setVisibility(ref, visibility));
|
|
85473
85575
|
return;
|
|
85474
85576
|
}
|
|
85577
|
+
if (action === "auto-publish" && method === "POST" && !first) {
|
|
85578
|
+
const body = await readJsonBody(req);
|
|
85579
|
+
const value = body?.value;
|
|
85580
|
+
if (body?.scope === "global" && typeof value === "boolean") writeGlobalAutoPublish(settingsFile, value);
|
|
85581
|
+
else if (body?.scope === "project" && (typeof value === "boolean" || value === null)) setProjectAutoPublish(configFile, value);
|
|
85582
|
+
else throw new CloudRefused(400, 'send {"scope":"global","value":true|false} or {"scope":"project","value":true|false|null}');
|
|
85583
|
+
sendJson(res, 200, autoPublishState());
|
|
85584
|
+
return;
|
|
85585
|
+
}
|
|
85475
85586
|
send(res, 404, "Not found");
|
|
85476
85587
|
};
|
|
85477
85588
|
return {
|
|
@@ -85506,9 +85617,13 @@ function createCloudRoutes(options) {
|
|
|
85506
85617
|
return;
|
|
85507
85618
|
}
|
|
85508
85619
|
writeCredentials(credentialsFile2, { origin, token: issued.token, login: issued.login });
|
|
85509
|
-
|
|
85620
|
+
defaultGlobalAutoPublish(settingsFile);
|
|
85621
|
+
page(200, { ok: true, login: issued.login, autoPublish: autoPublishState().enabled });
|
|
85510
85622
|
},
|
|
85511
85623
|
match: (pathname) => ROUTE.exec(pathname),
|
|
85624
|
+
autoPublish,
|
|
85625
|
+
autoPublishEnabled: () => autoPublishState().enabled,
|
|
85626
|
+
token: () => readCredentials(credentialsFile2, origin)?.token ?? null,
|
|
85512
85627
|
async handle(req, res, match) {
|
|
85513
85628
|
try {
|
|
85514
85629
|
await route(req, res, match);
|
|
@@ -85528,7 +85643,7 @@ function createCloudRoutes(options) {
|
|
|
85528
85643
|
|
|
85529
85644
|
// src/server/comment-names.ts
|
|
85530
85645
|
var import_node_child_process = require("node:child_process");
|
|
85531
|
-
var
|
|
85646
|
+
var import_node_fs17 = __toESM(require("node:fs"), 1);
|
|
85532
85647
|
|
|
85533
85648
|
// src/server/agent.ts
|
|
85534
85649
|
var NAMES = {
|
|
@@ -85609,7 +85724,7 @@ function commentNames({ agentFile, projectDir, cloud }) {
|
|
|
85609
85724
|
},
|
|
85610
85725
|
agent: () => {
|
|
85611
85726
|
try {
|
|
85612
|
-
return detectAgent(JSON.parse(
|
|
85727
|
+
return detectAgent(JSON.parse(import_node_fs17.default.readFileSync(agentFile, "utf8")));
|
|
85613
85728
|
} catch {
|
|
85614
85729
|
return void 0;
|
|
85615
85730
|
}
|
|
@@ -85636,26 +85751,117 @@ function viewerPage(template, page) {
|
|
|
85636
85751
|
</head>`);
|
|
85637
85752
|
}
|
|
85638
85753
|
|
|
85754
|
+
// src/server/auto-publish.ts
|
|
85755
|
+
var import_node_crypto5 = __toESM(require("node:crypto"), 1);
|
|
85756
|
+
var import_node_fs18 = __toESM(require("node:fs"), 1);
|
|
85757
|
+
var SIGNED_OUT_MESSAGE = "auto-publish paused: the Ryugi Cloud sign-in expired";
|
|
85758
|
+
function isNotice(value) {
|
|
85759
|
+
if (typeof value !== "object" || value === null) return false;
|
|
85760
|
+
const { id: id2, kind, message } = value;
|
|
85761
|
+
return typeof id2 === "string" && (kind === "signed-out" || kind === "cap") && typeof message === "string";
|
|
85762
|
+
}
|
|
85763
|
+
function readAutoPublishState(file) {
|
|
85764
|
+
let raw = {};
|
|
85765
|
+
try {
|
|
85766
|
+
const parsed = JSON.parse(import_node_fs18.default.readFileSync(file, "utf8"));
|
|
85767
|
+
if (typeof parsed === "object" && parsed !== null) raw = parsed;
|
|
85768
|
+
} catch {
|
|
85769
|
+
}
|
|
85770
|
+
const dirty = Array.isArray(raw.dirty) ? raw.dirty.filter((key2) => typeof key2 === "string") : [];
|
|
85771
|
+
const paused = raw.paused;
|
|
85772
|
+
return {
|
|
85773
|
+
dirty,
|
|
85774
|
+
notice: isNotice(raw.notice) ? raw.notice : null,
|
|
85775
|
+
paused: paused && (typeof paused.token === "string" || paused.token === null) ? { token: paused.token } : null
|
|
85776
|
+
};
|
|
85777
|
+
}
|
|
85778
|
+
function createAutoPublisher(options) {
|
|
85779
|
+
const saved = readAutoPublishState(options.stateFile);
|
|
85780
|
+
const dirty = new Set(saved.dirty);
|
|
85781
|
+
let notice = saved.notice;
|
|
85782
|
+
let paused = saved.paused;
|
|
85783
|
+
const remarked = /* @__PURE__ */ new Set();
|
|
85784
|
+
const save = () => writeJsonAtomic(options.stateFile, { dirty: [...dirty], notice, paused });
|
|
85785
|
+
const raise = (kind, message) => {
|
|
85786
|
+
if (notice?.kind !== kind) notice = { id: import_node_crypto5.default.randomUUID(), kind, message };
|
|
85787
|
+
};
|
|
85788
|
+
const run2 = async () => {
|
|
85789
|
+
if (!options.enabled() || dirty.size === 0) return;
|
|
85790
|
+
const token = options.token();
|
|
85791
|
+
if (paused && paused.token === token) return;
|
|
85792
|
+
if (paused) {
|
|
85793
|
+
paused = null;
|
|
85794
|
+
if (notice?.kind === "signed-out") notice = null;
|
|
85795
|
+
}
|
|
85796
|
+
for (const key2 of [...dirty]) {
|
|
85797
|
+
remarked.delete(key2);
|
|
85798
|
+
const outcome = await options.publish(key2);
|
|
85799
|
+
if (outcome.kind === "signed-out") {
|
|
85800
|
+
paused = { token };
|
|
85801
|
+
raise("signed-out", SIGNED_OUT_MESSAGE);
|
|
85802
|
+
break;
|
|
85803
|
+
}
|
|
85804
|
+
if (outcome.kind === "published") {
|
|
85805
|
+
if (!remarked.has(key2)) dirty.delete(key2);
|
|
85806
|
+
if (outcome.created && notice?.kind === "cap") notice = null;
|
|
85807
|
+
} else if (outcome.kind === "opted-out" || outcome.kind === "gone") {
|
|
85808
|
+
dirty.delete(key2);
|
|
85809
|
+
} else if (outcome.kind === "cannot-create") {
|
|
85810
|
+
raise("cap", outcome.message);
|
|
85811
|
+
}
|
|
85812
|
+
}
|
|
85813
|
+
save();
|
|
85814
|
+
};
|
|
85815
|
+
let running = null;
|
|
85816
|
+
let queued = null;
|
|
85817
|
+
const flush = () => {
|
|
85818
|
+
if (!running) {
|
|
85819
|
+
running = run2().catch((err) => console.error(JSON.stringify({ type: "auto-publish-error", error: err.message }))).finally(() => {
|
|
85820
|
+
running = null;
|
|
85821
|
+
});
|
|
85822
|
+
return running;
|
|
85823
|
+
}
|
|
85824
|
+
queued ??= running.then(() => {
|
|
85825
|
+
queued = null;
|
|
85826
|
+
return flush();
|
|
85827
|
+
});
|
|
85828
|
+
return queued;
|
|
85829
|
+
};
|
|
85830
|
+
return {
|
|
85831
|
+
changed(key2) {
|
|
85832
|
+
if (!options.enabled()) return;
|
|
85833
|
+
dirty.add(key2);
|
|
85834
|
+
remarked.add(key2);
|
|
85835
|
+
save();
|
|
85836
|
+
},
|
|
85837
|
+
removed(key2) {
|
|
85838
|
+
if (!dirty.delete(key2)) return;
|
|
85839
|
+
save();
|
|
85840
|
+
},
|
|
85841
|
+
flush
|
|
85842
|
+
};
|
|
85843
|
+
}
|
|
85844
|
+
|
|
85639
85845
|
// src/server/watch.ts
|
|
85640
|
-
var
|
|
85641
|
-
var
|
|
85642
|
-
var toPosix = (file) => file.split(
|
|
85846
|
+
var import_node_fs19 = __toESM(require("node:fs"), 1);
|
|
85847
|
+
var import_node_path17 = __toESM(require("node:path"), 1);
|
|
85848
|
+
var toPosix = (file) => file.split(import_node_path17.default.sep).join("/");
|
|
85643
85849
|
var ignored = (file) => file === ".state" || file.startsWith(".state/");
|
|
85644
85850
|
function snapshot(root, dir = root, out = /* @__PURE__ */ new Map()) {
|
|
85645
85851
|
let entries2;
|
|
85646
85852
|
try {
|
|
85647
|
-
entries2 =
|
|
85853
|
+
entries2 = import_node_fs19.default.readdirSync(dir, { withFileTypes: true });
|
|
85648
85854
|
} catch {
|
|
85649
85855
|
return out;
|
|
85650
85856
|
}
|
|
85651
85857
|
for (const entry of entries2) {
|
|
85652
|
-
const full =
|
|
85653
|
-
const rel = toPosix(
|
|
85858
|
+
const full = import_node_path17.default.join(dir, entry.name);
|
|
85859
|
+
const rel = toPosix(import_node_path17.default.relative(root, full));
|
|
85654
85860
|
if (ignored(rel)) continue;
|
|
85655
85861
|
if (entry.isDirectory()) snapshot(root, full, out);
|
|
85656
85862
|
else {
|
|
85657
85863
|
try {
|
|
85658
|
-
out.set(rel,
|
|
85864
|
+
out.set(rel, import_node_fs19.default.statSync(full).mtimeMs);
|
|
85659
85865
|
} catch {
|
|
85660
85866
|
}
|
|
85661
85867
|
}
|
|
@@ -85688,7 +85894,7 @@ function watchTree(root, onChange, debounceMs = 120, backupPollMs = 1e3) {
|
|
|
85688
85894
|
}, debounceMs);
|
|
85689
85895
|
};
|
|
85690
85896
|
try {
|
|
85691
|
-
const watcher =
|
|
85897
|
+
const watcher = import_node_fs19.default.watch(root, { recursive: true }, (_event, filename) => {
|
|
85692
85898
|
if (filename) queue([toPosix(filename.toString())]);
|
|
85693
85899
|
});
|
|
85694
85900
|
watcher.on("error", () => {
|
|
@@ -85713,7 +85919,7 @@ function watchTree(root, onChange, debounceMs = 120, backupPollMs = 1e3) {
|
|
|
85713
85919
|
}
|
|
85714
85920
|
|
|
85715
85921
|
// src/server/ws.ts
|
|
85716
|
-
var
|
|
85922
|
+
var import_node_crypto6 = __toESM(require("node:crypto"), 1);
|
|
85717
85923
|
var WS_MAGIC = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
85718
85924
|
var OPCODE_TEXT = 1;
|
|
85719
85925
|
var OPCODE_CLOSE = 8;
|
|
@@ -85721,7 +85927,7 @@ var OPCODE_PING = 9;
|
|
|
85721
85927
|
var OPCODE_PONG = 10;
|
|
85722
85928
|
var MAX_CLIENT_FRAME = 8 * 1024 * 1024;
|
|
85723
85929
|
function acceptKey(clientKey) {
|
|
85724
|
-
return
|
|
85930
|
+
return import_node_crypto6.default.createHash("sha1").update(clientKey + WS_MAGIC).digest("base64");
|
|
85725
85931
|
}
|
|
85726
85932
|
function encodeFrame(opcode, payload) {
|
|
85727
85933
|
const len = payload.length;
|
|
@@ -85840,17 +86046,17 @@ function titleOf(payload, ref) {
|
|
|
85840
86046
|
return payload.canvas?.title || ref.id;
|
|
85841
86047
|
}
|
|
85842
86048
|
async function startArtifactsServer(options) {
|
|
85843
|
-
const root =
|
|
85844
|
-
const viewerDir =
|
|
85845
|
-
const runtimeDir = options.runtimeDir ?
|
|
85846
|
-
const stateDir =
|
|
85847
|
-
|
|
85848
|
-
|
|
86049
|
+
const root = import_node_path18.default.resolve(options.root);
|
|
86050
|
+
const viewerDir = import_node_path18.default.resolve(options.viewerDir);
|
|
86051
|
+
const runtimeDir = options.runtimeDir ? import_node_path18.default.resolve(options.runtimeDir) : null;
|
|
86052
|
+
const stateDir = import_node_path18.default.join(root, ".state");
|
|
86053
|
+
import_node_fs20.default.mkdirSync(stateDir, { recursive: true, mode: 448 });
|
|
86054
|
+
import_node_fs20.default.writeFileSync(import_node_path18.default.join(stateDir, ".gitignore"), "*\n");
|
|
85849
86055
|
if (options.configFile) ensureConfig(options.configFile);
|
|
85850
|
-
const portFile =
|
|
85851
|
-
const tokenFile =
|
|
86056
|
+
const portFile = import_node_path18.default.join(stateDir, ".last-port");
|
|
86057
|
+
const tokenFile = import_node_path18.default.join(stateDir, ".last-token");
|
|
85852
86058
|
const token = readPersisted(tokenFile) || randomToken();
|
|
85853
|
-
|
|
86059
|
+
import_node_fs20.default.writeFileSync(tokenFile, `${token}
|
|
85854
86060
|
`, { mode: 384 });
|
|
85855
86061
|
const contentToken = randomToken();
|
|
85856
86062
|
const runtimeWarnings = /* @__PURE__ */ new Map();
|
|
@@ -85871,7 +86077,7 @@ async function startArtifactsServer(options) {
|
|
|
85871
86077
|
for (const socket of clients) socket.write(frame);
|
|
85872
86078
|
};
|
|
85873
86079
|
const avatars = createAvatars(options.fetch);
|
|
85874
|
-
const names = commentNames({ agentFile:
|
|
86080
|
+
const names = commentNames({ agentFile: import_node_path18.default.join(stateDir, "agent"), projectDir: import_node_path18.default.resolve(root, "../.."), cloud: options.cloud });
|
|
85875
86081
|
const documents = createDocumentRegistry({
|
|
85876
86082
|
root,
|
|
85877
86083
|
stateDir,
|
|
@@ -85909,7 +86115,8 @@ async function startArtifactsServer(options) {
|
|
|
85909
86115
|
}
|
|
85910
86116
|
return { dir, title, reasons, payload: published };
|
|
85911
86117
|
};
|
|
85912
|
-
const cloud = options.cloud && options.configFile ? createCloudRoutes({ ...options.cloud, configFile: options.configFile, port: () => port, inspect }) : null;
|
|
86118
|
+
const cloud = options.cloud && options.configFile ? createCloudRoutes({ ...options.cloud, configFile: options.configFile, project: import_node_path18.default.basename(import_node_path18.default.resolve(root, "../..")), port: () => port, inspect }) : null;
|
|
86119
|
+
const autoPublisher = cloud ? createAutoPublisher({ stateFile: import_node_path18.default.join(stateDir, "auto-publish.json"), enabled: cloud.autoPublishEnabled, token: cloud.token, publish: cloud.autoPublish }) : null;
|
|
85913
86120
|
const sameOrigin = (req) => {
|
|
85914
86121
|
const origin2 = req.headers.origin;
|
|
85915
86122
|
const host = req.headers.host ?? "";
|
|
@@ -85987,7 +86194,7 @@ async function startArtifactsServer(options) {
|
|
|
85987
86194
|
};
|
|
85988
86195
|
const blockIdsOf = async (id2, dir) => {
|
|
85989
86196
|
await (await documents.open(id2))?.flush();
|
|
85990
|
-
const state = parseDocument2(
|
|
86197
|
+
const state = parseDocument2(import_node_fs20.default.readFileSync(import_node_path18.default.join(dir, "doc.xml"), "utf8"));
|
|
85991
86198
|
return new Set(state.root.children.flatMap((block) => block.$?.blockId ? [block.$.blockId] : []));
|
|
85992
86199
|
};
|
|
85993
86200
|
const serveAgentComments = async (req, res, id2) => {
|
|
@@ -86067,7 +86274,7 @@ async function startArtifactsServer(options) {
|
|
|
86067
86274
|
sendFile(res, target, headers);
|
|
86068
86275
|
return;
|
|
86069
86276
|
}
|
|
86070
|
-
|
|
86277
|
+
import_node_fs20.default.readFile(target, "utf8", (err, source) => {
|
|
86071
86278
|
if (err) send(res, 404, "Not found");
|
|
86072
86279
|
else {
|
|
86073
86280
|
const uiRuntimeUrl = runtimeDir ? `${contentPrefix}runtime/ryugi-ui.js` : void 0;
|
|
@@ -86092,7 +86299,7 @@ async function startArtifactsServer(options) {
|
|
|
86092
86299
|
title: titleOf(readArtifact(entry.dir, entry), entry),
|
|
86093
86300
|
updatedAt: latestMtime(entry.dir)
|
|
86094
86301
|
}));
|
|
86095
|
-
sendJson(res, 200, { project:
|
|
86302
|
+
sendJson(res, 200, { project: import_node_path18.default.basename(import_node_path18.default.dirname(import_node_path18.default.dirname(root))), artifacts: summaries, warnings });
|
|
86096
86303
|
};
|
|
86097
86304
|
function onClientText(socket, text2) {
|
|
86098
86305
|
let message;
|
|
@@ -86152,7 +86359,7 @@ async function startArtifactsServer(options) {
|
|
|
86152
86359
|
return;
|
|
86153
86360
|
}
|
|
86154
86361
|
if (pathname === "/robots.txt") {
|
|
86155
|
-
sendFile(res,
|
|
86362
|
+
sendFile(res, import_node_path18.default.join(viewerDir, "robots.txt"));
|
|
86156
86363
|
return;
|
|
86157
86364
|
}
|
|
86158
86365
|
if (pathname.startsWith("/c/")) {
|
|
@@ -86201,6 +86408,19 @@ async function startArtifactsServer(options) {
|
|
|
86201
86408
|
}
|
|
86202
86409
|
return;
|
|
86203
86410
|
}
|
|
86411
|
+
if (pathname === "/api/cloud/flush") {
|
|
86412
|
+
if (req.method !== "POST" || key2 === null || !safeEqual(key2, token)) {
|
|
86413
|
+
send(res, 403, "Forbidden: agent calls need ?key=");
|
|
86414
|
+
return;
|
|
86415
|
+
}
|
|
86416
|
+
if (!autoPublisher) sendJson(res, 404, { error: "cloud publishing is not configured" });
|
|
86417
|
+
else if (url2.searchParams.get("wait") === "1") void autoPublisher.flush().then(() => sendJson(res, 200, { flushed: true }));
|
|
86418
|
+
else {
|
|
86419
|
+
void autoPublisher.flush();
|
|
86420
|
+
sendJson(res, 202, { queued: true });
|
|
86421
|
+
}
|
|
86422
|
+
return;
|
|
86423
|
+
}
|
|
86204
86424
|
const cloudMatch = cloud?.match(pathname);
|
|
86205
86425
|
if (cloud && cloudMatch) {
|
|
86206
86426
|
if (req.method !== "GET" && !(key2 !== null && safeEqual(key2, token)) && !sameOrigin(req)) {
|
|
@@ -86236,7 +86456,7 @@ async function startArtifactsServer(options) {
|
|
|
86236
86456
|
if (SHELL_ROUTE.test(pathname) || PREVIEW_ROUTE.test(pathname) || pathname === "/index.html") {
|
|
86237
86457
|
const ref = artifactKeyOf(pathname.replace(/^\/(?:preview\/)?/, "").replace(/\/$/, ""));
|
|
86238
86458
|
const dir = ref && resolveArtifactDir(root, ref.type, ref.id);
|
|
86239
|
-
const project =
|
|
86459
|
+
const project = import_node_path18.default.basename(import_node_path18.default.dirname(import_node_path18.default.dirname(root)));
|
|
86240
86460
|
const title = ref ? dir ? titleOf(readArtifact(dir, ref), ref) : ref.id : `${project} \xB7 Artifacts`;
|
|
86241
86461
|
const description = ref ? `\u5728 ${project} \u9879\u76EE\u4E2D\u67E5\u770B ${title}\uFF0C\u8FD9\u662F\u4E00\u4E2A ${ref.type === "artboard" ? "Artboard" : ref.type === "document" ? "Document" : "HTML"} Artifact\u3002` : `\u6D4F\u89C8 ${project} \u9879\u76EE\u7684\u5168\u90E8 Artboard\u3001Document \u548C HTML Artifact\uFF0C\u9884\u89C8\u771F\u5B9E\u5185\u5BB9\uFF0C\u6309\u7C7B\u578B\u3001\u540D\u79F0\u548C\u66F4\u65B0\u65F6\u95F4\u67E5\u627E\u4F5C\u54C1\u3002`;
|
|
86242
86462
|
const canonical = `http://${urlHostForHttp(options.urlHost)}:${port}/${ref ? `${ref.type}/${ref.id}` : ""}`;
|
|
@@ -86245,7 +86465,7 @@ async function startArtifactsServer(options) {
|
|
|
86245
86465
|
"Content-Security-Policy": PREVIEW_ROUTE.test(pathname) ? VIEWER_CSP.replace("frame-ancestors 'none'", "frame-ancestors 'self'") : VIEWER_CSP
|
|
86246
86466
|
};
|
|
86247
86467
|
if (key2 !== null) headers["Set-Cookie"] = `${cookieName}=${token}; HttpOnly; SameSite=Strict; Path=/`;
|
|
86248
|
-
void
|
|
86468
|
+
void import_node_fs20.default.promises.readFile(import_node_path18.default.join(viewerDir, "index.html"), "utf8").then((template) => send(res, 200, viewerPage(template, { title, description, url: canonical }), headers)).catch((err) => send(res, err.code === "ENOENT" ? 404 : 500, "Viewer unavailable"));
|
|
86249
86469
|
return;
|
|
86250
86470
|
}
|
|
86251
86471
|
const target = resolveInside(viewerDir, pathname.slice(1));
|
|
@@ -86292,7 +86512,7 @@ async function startArtifactsServer(options) {
|
|
|
86292
86512
|
port = await listen(server, options.host, 0);
|
|
86293
86513
|
}
|
|
86294
86514
|
cookieName = `ryugi_artifacts_${port}`;
|
|
86295
|
-
|
|
86515
|
+
import_node_fs20.default.writeFileSync(portFile, `${port}
|
|
86296
86516
|
`, { mode: 384 });
|
|
86297
86517
|
const origin = `http://${urlHostForHttp(options.urlHost)}:${port}`;
|
|
86298
86518
|
const url = `${origin}/?key=${token}`;
|
|
@@ -86318,7 +86538,7 @@ async function startArtifactsServer(options) {
|
|
|
86318
86538
|
initialChanges.push({ ref: entry, payload, files: [] });
|
|
86319
86539
|
if (entry.type === "document") void documents.open(entry.id);
|
|
86320
86540
|
}
|
|
86321
|
-
writeJsonAtomic(
|
|
86541
|
+
writeJsonAtomic(import_node_path18.default.join(stateDir, "server-info"), { type: "server-started", url, port, root });
|
|
86322
86542
|
const watcher = watchTree(
|
|
86323
86543
|
root,
|
|
86324
86544
|
(files) => {
|
|
@@ -86336,6 +86556,7 @@ async function startArtifactsServer(options) {
|
|
|
86336
86556
|
const dir = resolveArtifactDir(root, ref.type, ref.id);
|
|
86337
86557
|
if (!dir) {
|
|
86338
86558
|
signatures.delete(key2);
|
|
86559
|
+
autoPublisher?.removed(key2);
|
|
86339
86560
|
const frame2 = textFrame(JSON.stringify({ type: "change", artifact: key2, files: changed }));
|
|
86340
86561
|
for (const socket of clients) socket.write(frame2);
|
|
86341
86562
|
continue;
|
|
@@ -86343,6 +86564,7 @@ async function startArtifactsServer(options) {
|
|
|
86343
86564
|
if (ref.type === "document" && (changed.includes("doc.xml") || changed.length === 0)) documents.fileChanged(ref.id);
|
|
86344
86565
|
if (ref.type === "document" && changed.includes("comments.json")) documents.commentsChanged(ref.id);
|
|
86345
86566
|
if (signatures.get(key2) === artifactSignature(dir)) continue;
|
|
86567
|
+
autoPublisher?.changed(key2);
|
|
86346
86568
|
runtimeWarnings.delete(key2);
|
|
86347
86569
|
const payload = refreshState(ref, dir);
|
|
86348
86570
|
const frame = textFrame(JSON.stringify({ type: "change", artifact: key2, files: changed }));
|
|
@@ -86376,7 +86598,7 @@ async function startArtifactsServer(options) {
|
|
|
86376
86598
|
}
|
|
86377
86599
|
|
|
86378
86600
|
// src/server/lifecycle.ts
|
|
86379
|
-
var
|
|
86601
|
+
var import_node_fs21 = __toESM(require("node:fs"), 1);
|
|
86380
86602
|
var DEFAULT_IDLE_TIMEOUT_MS = 4 * 60 * 60 * 1e3;
|
|
86381
86603
|
var DEFAULT_LIFECYCLE_CHECK_MS = 60 * 1e3;
|
|
86382
86604
|
var SERVER_ID_ARG = "--artifacts-server-id=";
|
|
@@ -86408,7 +86630,7 @@ function parseEnv(env, argv = []) {
|
|
|
86408
86630
|
function readOwners(file) {
|
|
86409
86631
|
let text2;
|
|
86410
86632
|
try {
|
|
86411
|
-
text2 =
|
|
86633
|
+
text2 = import_node_fs21.default.readFileSync(file, "utf8");
|
|
86412
86634
|
} catch {
|
|
86413
86635
|
return [];
|
|
86414
86636
|
}
|
|
@@ -86473,15 +86695,15 @@ function openReady(payload) {
|
|
|
86473
86695
|
// src/server/main.ts
|
|
86474
86696
|
async function main() {
|
|
86475
86697
|
const config = parseEnv(process.env, process.argv.slice(2));
|
|
86476
|
-
const root =
|
|
86477
|
-
const stateDir =
|
|
86698
|
+
const root = import_node_path19.default.resolve(config.root);
|
|
86699
|
+
const stateDir = import_node_path19.default.join(root, ".state");
|
|
86478
86700
|
const server = await startArtifactsServer({
|
|
86479
86701
|
root,
|
|
86480
|
-
viewerDir:
|
|
86481
|
-
runtimeDir:
|
|
86702
|
+
viewerDir: import_node_path19.default.join(__dirname, "viewer"),
|
|
86703
|
+
runtimeDir: import_node_path19.default.join(__dirname, "runtime"),
|
|
86482
86704
|
host: config.host,
|
|
86483
86705
|
urlHost: config.urlHost,
|
|
86484
|
-
configFile:
|
|
86706
|
+
configFile: import_node_path19.default.join(import_node_path19.default.dirname(root), "config.json"),
|
|
86485
86707
|
cloud: { origin: config.cloudOrigin, credentialsFile: credentialsFile(process.env, process.platform, import_node_os.default.homedir()) }
|
|
86486
86708
|
});
|
|
86487
86709
|
let lastActivity = Date.now();
|
|
@@ -86510,12 +86732,12 @@ async function main() {
|
|
|
86510
86732
|
if (stopping) return;
|
|
86511
86733
|
stopping = true;
|
|
86512
86734
|
console.log(JSON.stringify({ type: "server-stopped", reason }));
|
|
86513
|
-
|
|
86514
|
-
writeJsonAtomic(
|
|
86735
|
+
import_node_fs22.default.rmSync(import_node_path19.default.join(stateDir, "server-info"), { force: true });
|
|
86736
|
+
writeJsonAtomic(import_node_path19.default.join(stateDir, "server-stopped"), { reason, timestamp: Date.now() });
|
|
86515
86737
|
await server.close();
|
|
86516
86738
|
process.exit(0);
|
|
86517
86739
|
};
|
|
86518
|
-
const ownersFile =
|
|
86740
|
+
const ownersFile = import_node_path19.default.join(stateDir, "owners");
|
|
86519
86741
|
const timer = setInterval(() => {
|
|
86520
86742
|
const owners = readOwners(ownersFile);
|
|
86521
86743
|
const reason = shutdownReason({
|
|
@@ -86529,7 +86751,7 @@ async function main() {
|
|
|
86529
86751
|
timer.unref();
|
|
86530
86752
|
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
|
86531
86753
|
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
86532
|
-
|
|
86754
|
+
import_node_fs22.default.rmSync(import_node_path19.default.join(stateDir, "server-stopped"), { force: true });
|
|
86533
86755
|
console.log(JSON.stringify({ type: "server-started", url: server.url, port: server.port, root }));
|
|
86534
86756
|
}
|
|
86535
86757
|
main().catch((err) => {
|