@bud.sh/app 0.2.0 → 0.4.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/client/assets/bud/design.js +1 -1
- package/dist/client/assets/canvas-C8hyPWoT.js +1 -0
- package/dist/client/assets/difference-worker-BfF4_N3e.js +8 -0
- package/dist/client/assets/files-D_Tm7gCK.js +1 -0
- package/dist/client/assets/i18n-BtfXum8P.js +1 -0
- package/dist/client/assets/index-Dpxjiok1.js +2 -0
- package/dist/client/assets/pages-B-_CKvDS.js +1 -0
- package/dist/client/assets/project-branches-DzXdaT9L.js +1 -0
- package/dist/client/assets/project-comments-CQkB2jbE.js +1 -0
- package/dist/client/assets/project-thread-links-BN7zL0AX.js +1 -0
- package/dist/client/assets/{project-threads-DezXiVK6.js → project-threads-CHGG-6fo.js} +104 -104
- package/dist/client/assets/{project-work-CB1JHqGW.js → project-work-L5fYMSOu.js} +1 -1
- package/dist/client/assets/task-design-DYqcqq1b.js +23 -0
- package/dist/client/assets/tasks-A2FKo3_h.js +1 -0
- package/dist/client/assets/tasks-request-DGigNdyK.js +1 -0
- package/dist/client/assets/workspace-CvWmU2RK.js +2 -0
- package/dist/client/index.html +2 -1
- package/dist/hosted/assets/{canvas-C69-96GR.js → canvas-MWtOwaWm.js} +69 -41
- package/dist/hosted/assets/{comment-data-CiDuvXi2.js → comment-data-DsgJWq4n.js} +7195 -1067
- package/dist/hosted/assets/{files-DQL5hzs3.js → files-Wvo11G2A.js} +92 -89
- package/dist/hosted/assets/{pages-DFs7jB6M.js → pages-BAmRFVtU.js} +31 -22
- package/dist/hosted/assets/{project-branches-CffV4IS1.js → project-branches-BNloVosX.js} +5 -5
- package/dist/hosted/assets/{project-thread-links-B1Y2bDUx.js → project-thread-links-BmY8HQXt.js} +12 -12
- package/dist/hosted/assets/{screen-BroBb1Z_.js → screen-QmqmI2Aq.js} +18 -18
- package/dist/hosted/assets/{tasks-mzmNhe2d.js → tasks-C8iWPrWg.js} +29 -29
- package/dist/hosted/assets/{tasks-request-Dcrpd2_8.js → tasks-request-C89fN1dK.js} +7 -7
- package/dist/hosted/hosted.js +159 -31
- package/dist/node/design.js +6235 -139
- package/dist/node/index.js +99 -2
- package/dist/node-host/assets/{canvas-BZhzizzt.js → canvas-D_F8SIlm.js} +70 -42
- package/dist/node-host/assets/{comment-data-nUlI0szN.js → comment-data-q_8INiZz.js} +7221 -1093
- package/dist/node-host/assets/{files-CTzcWGjb.js → files-xazOrNCX.js} +92 -89
- package/dist/node-host/assets/{pages-0aWH7z1j.js → pages-D6VkmdfQ.js} +31 -22
- package/dist/node-host/assets/{project-branches-1zs_0J_y.js → project-branches-C26hwmo3.js} +5 -5
- package/dist/node-host/assets/{project-thread-links-LpUAGcDr.js → project-thread-links-BOzjws5W.js} +12 -12
- package/dist/node-host/assets/{screen-DQoTOWYR.js → screen-Dog-YByu.js} +18 -18
- package/dist/node-host/assets/{tasks-C0WqnM7Q.js → tasks-A1_bpb3_.js} +30 -30
- package/dist/node-host/assets/{tasks-request-Bwky5ORf.js → tasks-request-Bie1PpQP.js} +7 -7
- package/dist/node-host/node-host.js +1765 -1630
- package/dist/server/assets/{branch-previews-C_TVt_HQ.js → branch-previews-ClkoJ0lM.js} +116 -5
- package/dist/server/assets/{canvas-D6tIkONI.js → canvas-Dn4RYoU2.js} +70 -42
- package/dist/server/assets/{comment-data-BuArfgcc.js → comment-data-B0OLHBGp.js} +7219 -1091
- package/dist/server/assets/{files-DkrEB48U.js → files-C5Vilfe4.js} +92 -89
- package/dist/server/assets/{listener-BVbFsnmX.js → listener-o1STuLju.js} +43 -29
- package/dist/server/assets/{pages-iEdmK_o2.js → pages-BNa0MHEc.js} +31 -22
- package/dist/server/assets/{project-branches-B7rtrvMl.js → project-branches-GtQYyOH5.js} +5 -5
- package/dist/server/assets/{project-thread-links-ue_ad9IX.js → project-thread-links-Nfz9NIny.js} +12 -12
- package/dist/server/assets/{project-work-6aOyja4v.js → project-work-Cy_BpHwR.js} +2 -2
- package/dist/server/assets/{screen-DMUB9a-K.js → screen-BtcJmZwj.js} +18 -18
- package/dist/server/assets/{server-DUBjmaBw.js → server-CYvuOSBA.js} +1 -1
- package/dist/server/assets/{services-Og5rcF50.js → services-DK4fqrlF.js} +2 -2
- package/dist/server/assets/{tasks-C2oa2wSC.js → tasks-B82tHwyQ.js} +31 -31
- package/dist/server/assets/{tasks-request-Bk5EoY_p.js → tasks-request-BtJBlkxV.js} +7 -6
- package/dist/server/capture.js +1 -1
- package/dist/server/migration.js +3 -3
- package/dist/server/runtime.js +4 -4
- package/dist/server/server.js +3 -3
- package/dist/types/assets/{config-BzzRLcL-.d.ts → config-DIC2cf_S.d.ts} +27 -1
- package/dist/types/assets/{contract-jA5pqK6h.d.ts → contract-CAT-XFNq.d.ts} +3 -1
- package/dist/types/companion.d.ts +1 -1
- package/dist/types/hosted.d.ts +2 -2
- package/dist/types/index.d.ts +1 -1
- package/dist/types/node-host.d.ts +7 -1
- package/dist/types/screen.d.ts +1 -1
- package/package.json +1 -1
- package/dist/client/assets/canvas-DxaIItAs.js +0 -1
- package/dist/client/assets/difference-worker-BpqPBs5g.js +0 -8
- package/dist/client/assets/files-C-7xoSt4.js +0 -1
- package/dist/client/assets/index-sj6mWw9-.js +0 -2
- package/dist/client/assets/pages-BVrTX_Eq.js +0 -1
- package/dist/client/assets/project-branches-DpxQx-T4.js +0 -1
- package/dist/client/assets/project-comments-D897aU5O.js +0 -1
- package/dist/client/assets/project-thread-links-Bp_TseD0.js +0 -1
- package/dist/client/assets/task-design-bktfn_Ok.js +0 -23
- package/dist/client/assets/tasks-Cqfj4fQN.js +0 -1
- package/dist/client/assets/tasks-request-mWMk9pBo.js +0 -1
- package/dist/client/assets/workspace-9KxFcfyV.js +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $r as
|
|
2
|
-
import { n as requestTasks } from "./assets/tasks-request-
|
|
1
|
+
import { $r as parseWork, $t as Button, An as parseThreadStatus, Ar as parseWorkActionInput, Br as parseWorkSync, Cn as parseThreadCapabilities, Ct as legacyConversationHref, Dn as parseThreadLinkInput, Dr as parseElementAnchor, Dt as taskHref, En as parseThreadInput, Er as MAX_CANVAS_Y, Et as selectedTask, Fr as parseWorkItem, G as mergeThreads, Gr as parseMessage, Gt as fileRoutes, Hr as parseWorkUpdate, Ir as parseWorkItemRef, J as conversationId, Jr as parseWorkSchema, K as stableJson, Kr as parseMessageAuthor, Kt as pageRoutes, Lr as parseWorkMutationResult, Mr as parseWorkConnection, Nr as parseWorkCreate, On as parseThreadReplyInput, Or as parseWorkWorkspace, Pr as parseWorkDetail, Q as text$6, Qr as nonempty$1, Rr as parseWorkQuery, S as writeRows, Sn as parseThread, Tn as parseThreadId, Tt as readConversationLocation, Ur as parseWorkValue, V as parseTaskDesign, Vr as parseWorkSyncResult, W as CommentMergeError, Wr as valueByRole, X as parseCommentThread, Xr as WorkValidationError, Y as object$2, Yr as statusCategory, Z as revision$2, Zr as identifier$3, Zt as Loader, _ as requestComments, _n as RESYNC, an as LiveStatus, at as FieldCardHeader, b as plainRow, bn as MAX_THREADS, ci as parsePreviewHash, ct as initialShellTabs, d as saveWorkspace, di as parsePreviewViewport, dn as isLocale, dt as readTabsCookie, ei as record$2, fn as locale, ft as stepShellTab, gt as visitShellTab, hn as Text, ht as tabsCookieValue, ii as PREVIEW_TILE_HEIGHT, it as FieldCardContent, jn as parseThreadStatusInput, jr as parseWorkActivity, jt as legacyFileHref, kn as parseThreadSnapshot, kr as parseSectionData, l as projectStore, li as parsePreviewId, ln as readPreference, lt as openShellTab, mn as useLocaleSource, mt as tabsCookieName, n as unavailableComments, ni as MAX_PREVIEW_HEIGHT, nn as ShellTabBar, oi as parsePreviewBranch, ot as activeTab, pn as t, pt as tabHref, q as parseCommentInput$1, qr as fieldByRole, qt as taskRoutes, r as followThreads, ri as PREVIEW_THUMBNAIL_WIDTHS, rt as FieldCard, si as parsePreviewCapture, sn as AppShell, st as closeShellTab, t as commentsUnavailable, ti as strict$2, ui as parsePreviewPins, un as writePreference, ut as parseShellTabs, v as projectLive, vn as parseLiveId, wn as parseThreadFilter, xn as parseAnchor, y as routes, yn as parseWorkChange, yt as conversationDestination, zr as parseWorkSnapshot } from "./assets/comment-data-q_8INiZz.js";
|
|
2
|
+
import { n as requestTasks } from "./assets/tasks-request-Bie1PpQP.js";
|
|
3
3
|
import { access, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, symlink, writeFile } from "node:fs/promises";
|
|
4
4
|
import * as Path from "node:path";
|
|
5
5
|
import { dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
@@ -9,6 +9,7 @@ import * as Crypto from "node:crypto";
|
|
|
9
9
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
10
10
|
import { any, array, boolean, createSchema, defaulted, enum_, fail, number, object, optional, parse, parseSafe, string } from "remix/data-schema";
|
|
11
11
|
import { gunzipSync, gzipSync } from "node:zlib";
|
|
12
|
+
import { RoutePattern } from "remix/route-pattern";
|
|
12
13
|
import { staticFiles } from "remix/middleware/static";
|
|
13
14
|
import { createController, createRouter } from "remix/router";
|
|
14
15
|
import * as NodeChildProcess from "node:child_process";
|
|
@@ -18,13 +19,13 @@ import * as NFS from "node:fs";
|
|
|
18
19
|
import { readFileSync } from "node:fs";
|
|
19
20
|
import * as OS from "node:os";
|
|
20
21
|
import { tmpdir } from "node:os";
|
|
21
|
-
import { RoutePattern } from "remix/route-pattern";
|
|
22
22
|
import { clientEntry, navigate, on, ref } from "remix/component";
|
|
23
23
|
import { jsx, jsxs } from "remix/component/jsx-runtime";
|
|
24
24
|
import { createMultiMatcher } from "remix/route-pattern/match";
|
|
25
25
|
import { renderToString } from "remix/component/server";
|
|
26
26
|
import { createRedirectResponse } from "remix/response/redirect";
|
|
27
27
|
import { createServer } from "node:net";
|
|
28
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
28
29
|
//#region ../beads-d1/src/lib/d1.ts
|
|
29
30
|
/**
|
|
30
31
|
* A failure whose outcome is known: the statements did not apply. Transports throw it for
|
|
@@ -7646,7 +7647,7 @@ var logWithLevel$1 = (level) => (...message) => {
|
|
|
7646
7647
|
return void_$2;
|
|
7647
7648
|
});
|
|
7648
7649
|
};
|
|
7649
|
-
var colors = {
|
|
7650
|
+
var colors$1 = {
|
|
7650
7651
|
bold: "1",
|
|
7651
7652
|
red: "31",
|
|
7652
7653
|
green: "32",
|
|
@@ -7658,7 +7659,7 @@ var colors = {
|
|
|
7658
7659
|
black: "30",
|
|
7659
7660
|
bgBrightRed: "101"
|
|
7660
7661
|
};
|
|
7661
|
-
colors.gray, colors.blue, colors.green, colors.yellow, colors.red, colors.bgBrightRed, colors.black;
|
|
7662
|
+
colors$1.gray, colors$1.blue, colors$1.green, colors$1.yellow, colors$1.red, colors$1.bgBrightRed, colors$1.black;
|
|
7662
7663
|
var defaultDateFormat = (date) => `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}:${date.getSeconds().toString().padStart(2, "0")}.${date.getMilliseconds().toString().padStart(3, "0")}`;
|
|
7663
7664
|
/** @internal */
|
|
7664
7665
|
var defaultLogger = /*#__PURE__*/ loggerMake(({ cause, date, fiber, logLevel, message }) => {
|
|
@@ -22149,658 +22150,1409 @@ function httpPreviewStorage(base, options = {}) {
|
|
|
22149
22150
|
};
|
|
22150
22151
|
}
|
|
22151
22152
|
//#endregion
|
|
22152
|
-
//#region ../
|
|
22153
|
-
var
|
|
22154
|
-
//#endregion
|
|
22155
|
-
//#region ../beads/src/lib/validation.ts
|
|
22156
|
-
/** Beads returned or was sent data of the wrong shape; `field` names where. */
|
|
22157
|
-
var BeadsValidationError = class extends Error {
|
|
22158
|
-
field;
|
|
22159
|
-
constructor(field) {
|
|
22160
|
-
super(`Invalid Beads data at ${field}.`);
|
|
22161
|
-
this.name = "BeadsValidationError";
|
|
22162
|
-
this.field = field;
|
|
22163
|
-
}
|
|
22164
|
-
};
|
|
22165
|
-
/**
|
|
22166
|
-
* Checks that a value is a JSON object.
|
|
22167
|
-
* @param value - Untrusted data.
|
|
22168
|
-
* @param field - Where the value sits, for the error.
|
|
22169
|
-
* @returns The object.
|
|
22170
|
-
*/
|
|
22171
|
-
function object$1(value, field) {
|
|
22172
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new BeadsValidationError(field);
|
|
22173
|
-
return value;
|
|
22174
|
-
}
|
|
22175
|
-
/**
|
|
22176
|
-
* Checks a string without NUL characters within a length limit.
|
|
22177
|
-
* @param value - Untrusted data.
|
|
22178
|
-
* @param field - Where the value sits, for the error.
|
|
22179
|
-
* @param max - The most characters it may have.
|
|
22180
|
-
* @returns The string.
|
|
22181
|
-
*/
|
|
22182
|
-
function text$4(value, field, max = 262144) {
|
|
22183
|
-
if (typeof value !== "string" || value.length > max || value.includes("\0")) throw new BeadsValidationError(field);
|
|
22184
|
-
return value;
|
|
22185
|
-
}
|
|
22186
|
-
/**
|
|
22187
|
-
* Checks an ID, name or commit made of letters, digits, dots, underscores and hyphens.
|
|
22188
|
-
* @param value - Untrusted data.
|
|
22189
|
-
* @param field - Where the value sits, for the error.
|
|
22190
|
-
* @returns The identifier.
|
|
22191
|
-
*/
|
|
22192
|
-
function identifier$1(value, field) {
|
|
22193
|
-
let result = text$4(value, field, 512);
|
|
22194
|
-
if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(result)) throw new BeadsValidationError(field);
|
|
22195
|
-
return result;
|
|
22196
|
-
}
|
|
22197
|
-
/**
|
|
22198
|
-
* Checks an optional string.
|
|
22199
|
-
* @param value - Untrusted data.
|
|
22200
|
-
* @param field - Where the value sits, for the error.
|
|
22201
|
-
* @param max - The most characters it may have.
|
|
22202
|
-
* @returns The string, or null when it is missing.
|
|
22203
|
-
*/
|
|
22204
|
-
function nullableText(value, field, max) {
|
|
22205
|
-
return value == null ? null : text$4(value, field, max);
|
|
22206
|
-
}
|
|
22153
|
+
//#region ../project/src/lib/validate.ts
|
|
22154
|
+
var errors = /* @__PURE__ */ new WeakMap();
|
|
22207
22155
|
/**
|
|
22208
|
-
*
|
|
22209
|
-
* @param
|
|
22210
|
-
* @param
|
|
22211
|
-
* @
|
|
22212
|
-
* @returns The integer.
|
|
22156
|
+
* Validates config, throwing its first problem as the `Error` the config checks report.
|
|
22157
|
+
* @param schema - The config schema.
|
|
22158
|
+
* @param value - The project's untrusted setting.
|
|
22159
|
+
* @returns The validated, normalized setting.
|
|
22213
22160
|
*/
|
|
22214
|
-
function
|
|
22215
|
-
|
|
22216
|
-
return value;
|
|
22161
|
+
function parseConfig(schema, value) {
|
|
22162
|
+
let result = parseSafe(schema, value, { abortEarly: true });
|
|
22163
|
+
if (result.success) return result.value;
|
|
22164
|
+
let issue = result.issues[0];
|
|
22165
|
+
throw errors.get(issue) ?? new Error(issue.message);
|
|
22217
22166
|
}
|
|
22218
22167
|
/**
|
|
22219
|
-
*
|
|
22220
|
-
* @param
|
|
22221
|
-
* @param
|
|
22222
|
-
* @
|
|
22223
|
-
* @returns The integer, or null when it is missing.
|
|
22168
|
+
* Reports any failure of `schema` with one message.
|
|
22169
|
+
* @param schema - The schema to run.
|
|
22170
|
+
* @param message - The message for any of its failures.
|
|
22171
|
+
* @returns The described schema.
|
|
22224
22172
|
*/
|
|
22225
|
-
function
|
|
22226
|
-
return
|
|
22173
|
+
function described(schema, message) {
|
|
22174
|
+
return createSchema((value, context) => {
|
|
22175
|
+
let result = schema["~run"](value, context);
|
|
22176
|
+
return result.issues ? fail(message, context.path) : result;
|
|
22177
|
+
});
|
|
22227
22178
|
}
|
|
22228
22179
|
/**
|
|
22229
|
-
*
|
|
22230
|
-
* @param
|
|
22231
|
-
* @param
|
|
22232
|
-
* @returns The
|
|
22180
|
+
* Applies a rule after `schema` validates.
|
|
22181
|
+
* @param schema - The schema to run first.
|
|
22182
|
+
* @param rule - Returns a message, or undefined when the value is valid.
|
|
22183
|
+
* @returns The checked schema.
|
|
22233
22184
|
*/
|
|
22234
|
-
function
|
|
22235
|
-
|
|
22236
|
-
|
|
22185
|
+
function check(schema, rule) {
|
|
22186
|
+
return createSchema((value, context) => {
|
|
22187
|
+
let result = schema["~run"](value, context);
|
|
22188
|
+
if (result.issues) return result;
|
|
22189
|
+
let message = rule(result.value);
|
|
22190
|
+
return message === void 0 ? result : fail(message, context.path);
|
|
22191
|
+
});
|
|
22237
22192
|
}
|
|
22238
22193
|
/**
|
|
22239
|
-
*
|
|
22240
|
-
* @param
|
|
22241
|
-
* @param
|
|
22242
|
-
* @returns The
|
|
22194
|
+
* Validates with a function that throws, such as a URL or route map parser, keeping its error.
|
|
22195
|
+
* @param schema - The schema to run first.
|
|
22196
|
+
* @param parse - Turns its value into the output, or throws.
|
|
22197
|
+
* @returns The delegating schema.
|
|
22243
22198
|
*/
|
|
22244
|
-
function
|
|
22245
|
-
|
|
22246
|
-
|
|
22247
|
-
|
|
22199
|
+
function delegate(schema, parse) {
|
|
22200
|
+
return createSchema((value, context) => {
|
|
22201
|
+
let result = schema["~run"](value, context);
|
|
22202
|
+
if (result.issues) return result;
|
|
22203
|
+
try {
|
|
22204
|
+
return { value: parse(result.value) };
|
|
22205
|
+
} catch (error) {
|
|
22206
|
+
if (!(error instanceof Error)) throw error;
|
|
22207
|
+
let failure = fail(error.message, context.path);
|
|
22208
|
+
errors.set(failure.issues[0], error);
|
|
22209
|
+
return failure;
|
|
22210
|
+
}
|
|
22211
|
+
});
|
|
22248
22212
|
}
|
|
22249
22213
|
/**
|
|
22250
|
-
*
|
|
22251
|
-
* @param
|
|
22252
|
-
* @
|
|
22253
|
-
* @returns The timestamp, or null when it is missing.
|
|
22214
|
+
* Chooses a schema from the unvalidated value.
|
|
22215
|
+
* @param choose - Picks the schema for a value.
|
|
22216
|
+
* @returns The selecting schema.
|
|
22254
22217
|
*/
|
|
22255
|
-
function
|
|
22256
|
-
return value
|
|
22218
|
+
function select$1(choose) {
|
|
22219
|
+
return createSchema((value, context) => choose(value)["~run"](value, context));
|
|
22257
22220
|
}
|
|
22258
22221
|
/**
|
|
22259
|
-
*
|
|
22260
|
-
* @param
|
|
22261
|
-
* @param
|
|
22262
|
-
* @
|
|
22263
|
-
* @param max - The most entries it may have.
|
|
22264
|
-
* @returns The parsed entries.
|
|
22222
|
+
* Substitutes a default for null or undefined.
|
|
22223
|
+
* @param schema - The schema for present values.
|
|
22224
|
+
* @param value - Makes the default.
|
|
22225
|
+
* @returns The schema with its default.
|
|
22265
22226
|
*/
|
|
22266
|
-
function
|
|
22267
|
-
|
|
22268
|
-
return value.map((entry, i) => parse(entry, `${field}[${i}]`));
|
|
22227
|
+
function fallback(schema, value) {
|
|
22228
|
+
return createSchema((input, context) => input == null ? { value: value() } : schema["~run"](input, context));
|
|
22269
22229
|
}
|
|
22270
22230
|
/**
|
|
22271
|
-
*
|
|
22272
|
-
* @param
|
|
22273
|
-
* @param
|
|
22274
|
-
* @
|
|
22275
|
-
* @returns The values.
|
|
22231
|
+
* A settings object that names its unsupported keys before validating its fields.
|
|
22232
|
+
* @param name - The setting's name in messages, such as `previews`.
|
|
22233
|
+
* @param shape - The schema for each supported key.
|
|
22234
|
+
* @returns The settings schema.
|
|
22276
22235
|
*/
|
|
22277
|
-
function
|
|
22278
|
-
|
|
22279
|
-
return
|
|
22236
|
+
function settings(name, shape) {
|
|
22237
|
+
let schema = object(shape);
|
|
22238
|
+
return createSchema((value, context) => {
|
|
22239
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return fail(`${name} must be an object.`, context.path);
|
|
22240
|
+
for (let key of Object.keys(value)) if (!Object.hasOwn(shape, key)) return fail(`${name}.${key} is not a supported setting.`, [...context.path, key]);
|
|
22241
|
+
return schema["~run"](value, context);
|
|
22242
|
+
});
|
|
22280
22243
|
}
|
|
22281
22244
|
/**
|
|
22282
|
-
*
|
|
22283
|
-
* @param
|
|
22284
|
-
* @param
|
|
22285
|
-
* @param
|
|
22286
|
-
* @
|
|
22287
|
-
* @returns The metadata.
|
|
22245
|
+
* A list whose length is checked before its entries.
|
|
22246
|
+
* @param item - The schema for each entry.
|
|
22247
|
+
* @param message - The message when it is not a list or is too short.
|
|
22248
|
+
* @param minimum - The fewest entries it may have.
|
|
22249
|
+
* @returns The list schema.
|
|
22288
22250
|
*/
|
|
22289
|
-
function
|
|
22290
|
-
|
|
22291
|
-
|
|
22292
|
-
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
22293
|
-
if (typeof value === "string") return text$4(value, field);
|
|
22294
|
-
if (Array.isArray(value)) return value.map((entry, index) => metadata(entry, `${field}[${index}]`, depth + 1, budget));
|
|
22295
|
-
let input = object$1(value, field);
|
|
22296
|
-
return Object.fromEntries(Object.entries(input).map(([key, entry]) => [text$4(key, field, 512), metadata(entry, `${field}.${key}`, depth + 1, budget)]));
|
|
22251
|
+
function list(item, message, minimum = 0) {
|
|
22252
|
+
let items = array(item);
|
|
22253
|
+
return createSchema((value, context) => !Array.isArray(value) || value.length < minimum ? fail(message, context.path) : items["~run"](value, context));
|
|
22297
22254
|
}
|
|
22298
|
-
|
|
22299
|
-
|
|
22255
|
+
//#endregion
|
|
22256
|
+
//#region ../project/src/lib/files.ts
|
|
22257
|
+
/** The Files settings, with their defaults filled in. */
|
|
22258
|
+
var BudFilesSchema = defaulted(settings("files", { edit: fallback(described(boolean(), "files.edit must be true or false."), () => true) }), () => ({ edit: true }));
|
|
22259
|
+
//#endregion
|
|
22260
|
+
//#region ../project/src/lib/flows.ts
|
|
22261
|
+
var message$1 = "flows must be [{ id, title, start, script, steps: [{ id, title }], block? }] with short ids, local paths and unique steps.";
|
|
22262
|
+
var slug = (value) => typeof value === "string" && /^[a-z0-9][a-z0-9-]{0,62}$/.test(value);
|
|
22263
|
+
var text$4 = (value) => typeof value === "string" && value.trim() !== "" && value.length <= 200;
|
|
22264
|
+
function flow(value) {
|
|
22265
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
22266
|
+
let { id, title, start, script, steps, block = [] } = value;
|
|
22267
|
+
if (!slug(id) || !text$4(title) || !text$4(script) || script.includes("\0")) return null;
|
|
22268
|
+
if (typeof start !== "string" || !start.startsWith("/") || start.startsWith("//")) return null;
|
|
22269
|
+
if (new URL(start, "http://bud.invalid").origin !== "http://bud.invalid") return null;
|
|
22270
|
+
if (!Array.isArray(steps) || !steps.length || steps.length > 50) return null;
|
|
22271
|
+
let resolved = steps.map((step) => {
|
|
22272
|
+
let { id: stepId, title: stepTitle } = step ?? {};
|
|
22273
|
+
return slug(stepId) && text$4(stepTitle) ? {
|
|
22274
|
+
id: stepId,
|
|
22275
|
+
title: stepTitle.trim()
|
|
22276
|
+
} : null;
|
|
22277
|
+
});
|
|
22278
|
+
if (resolved.some((step) => !step)) return null;
|
|
22279
|
+
let ids = resolved.map((step) => step.id);
|
|
22280
|
+
if (new Set(ids).size !== ids.length) return null;
|
|
22281
|
+
if (!Array.isArray(block) || block.some((part) => !text$4(part))) return null;
|
|
22300
22282
|
return {
|
|
22301
|
-
id
|
|
22302
|
-
|
|
22303
|
-
|
|
22304
|
-
|
|
22305
|
-
|
|
22306
|
-
|
|
22283
|
+
id,
|
|
22284
|
+
title: title.trim(),
|
|
22285
|
+
start,
|
|
22286
|
+
script: script.trim(),
|
|
22287
|
+
steps: resolved,
|
|
22288
|
+
block
|
|
22307
22289
|
};
|
|
22308
22290
|
}
|
|
22291
|
+
/** The flows, none when omitted. */
|
|
22292
|
+
var BudFlowsSchema = createSchema((value, context) => {
|
|
22293
|
+
if (value === void 0) return { value: [] };
|
|
22294
|
+
if (!Array.isArray(value)) return fail(message$1, context.path);
|
|
22295
|
+
let flows = value.map(flow);
|
|
22296
|
+
if (flows.some((resolved) => !resolved)) return fail(message$1, context.path);
|
|
22297
|
+
let ids = flows.map((resolved) => resolved.id);
|
|
22298
|
+
if (new Set(ids).size !== ids.length) return fail(message$1, context.path);
|
|
22299
|
+
return { value: flows };
|
|
22300
|
+
});
|
|
22309
22301
|
/**
|
|
22310
|
-
*
|
|
22311
|
-
*
|
|
22312
|
-
* @param
|
|
22313
|
-
* @
|
|
22302
|
+
* Where a flow's step is pictured: a route of its own, named `<flow>/<step>`, in a group named
|
|
22303
|
+
* after the flow. Its path is the start page with the step as its fragment, unique among routes.
|
|
22304
|
+
* @param flow - The flow.
|
|
22305
|
+
* @param step - One of its steps.
|
|
22306
|
+
* @returns The step's route.
|
|
22314
22307
|
*/
|
|
22315
|
-
function
|
|
22316
|
-
let input = object$1(value, field);
|
|
22317
|
-
let string = (key, max) => text$4(input[key], `${field}.${key}`, max);
|
|
22318
|
-
let optional = (key, max) => nullableText(input[key], `${field}.${key}`, max);
|
|
22319
|
-
let count = (key) => nullableInteger(input[key], `${field}.${key}`);
|
|
22320
|
-
let date = (key) => nullableTimestamp(input[key], `${field}.${key}`);
|
|
22308
|
+
function flowStepRoute(flow, step) {
|
|
22321
22309
|
return {
|
|
22322
|
-
id:
|
|
22323
|
-
|
|
22324
|
-
|
|
22325
|
-
|
|
22326
|
-
|
|
22327
|
-
|
|
22328
|
-
|
|
22329
|
-
|
|
22330
|
-
notes: string("notes"),
|
|
22331
|
-
assignee: optional("assignee"),
|
|
22332
|
-
owner: optional("owner"),
|
|
22333
|
-
createdBy: optional("createdBy"),
|
|
22334
|
-
createdAt: timestamp(input.createdAt, `${field}.createdAt`),
|
|
22335
|
-
updatedAt: timestamp(input.updatedAt, `${field}.updatedAt`),
|
|
22336
|
-
startedAt: date("startedAt"),
|
|
22337
|
-
closedAt: date("closedAt"),
|
|
22338
|
-
closeReason: optional("closeReason"),
|
|
22339
|
-
dueAt: date("dueAt"),
|
|
22340
|
-
deferUntil: date("deferUntil"),
|
|
22341
|
-
estimatedMinutes: count("estimatedMinutes"),
|
|
22342
|
-
externalRef: optional("externalRef"),
|
|
22343
|
-
specId: optional("specId"),
|
|
22344
|
-
labels: unique(array$1(input.labels, `${field}.labels`, (item, path) => text$4(item, path, 512), 1e3), (item) => item, `${field}.labels`),
|
|
22345
|
-
parentId: input.parentId == null ? null : identifier$1(input.parentId, `${field}.parentId`),
|
|
22346
|
-
dependencies: array$1(input.dependencies, `${field}.dependencies`, dependency),
|
|
22347
|
-
dependents: array$1(input.dependents, `${field}.dependents`, dependency),
|
|
22348
|
-
dependencyCount: count("dependencyCount"),
|
|
22349
|
-
dependentCount: count("dependentCount"),
|
|
22350
|
-
commentCount: count("commentCount"),
|
|
22351
|
-
epicTotalChildren: count("epicTotalChildren"),
|
|
22352
|
-
epicClosedChildren: count("epicClosedChildren"),
|
|
22353
|
-
epicCloseable: input.epicCloseable == null ? null : boolean$2(input.epicCloseable, `${field}.epicCloseable`),
|
|
22354
|
-
revision: optional("revision", 256),
|
|
22355
|
-
metadata: metadata(input.metadata, `${field}.metadata`),
|
|
22356
|
-
contentLoaded: boolean$2(input.contentLoaded, `${field}.contentLoaded`)
|
|
22310
|
+
id: `${flow.id}/${step.id}`,
|
|
22311
|
+
path: `${flow.start.replace(/#.*$/, "")}#${flow.id}-${step.id}`,
|
|
22312
|
+
title: step.title,
|
|
22313
|
+
group: flow.title,
|
|
22314
|
+
flow: {
|
|
22315
|
+
id: flow.id,
|
|
22316
|
+
step: step.id
|
|
22317
|
+
}
|
|
22357
22318
|
};
|
|
22358
22319
|
}
|
|
22359
|
-
|
|
22360
|
-
|
|
22361
|
-
|
|
22362
|
-
|
|
22363
|
-
|
|
22364
|
-
|
|
22365
|
-
|
|
22366
|
-
|
|
22367
|
-
|
|
22368
|
-
|
|
22369
|
-
|
|
22370
|
-
|
|
22371
|
-
|
|
22372
|
-
|
|
22373
|
-
|
|
22374
|
-
|
|
22375
|
-
|
|
22376
|
-
|
|
22377
|
-
|
|
22378
|
-
|
|
22379
|
-
|
|
22380
|
-
|
|
22381
|
-
|
|
22320
|
+
//#endregion
|
|
22321
|
+
//#region ../project/src/lib/logo.ts
|
|
22322
|
+
/** The image types a logo may be, by extension. */
|
|
22323
|
+
var LOGO_TYPES = Object.freeze({
|
|
22324
|
+
".svg": "image/svg+xml",
|
|
22325
|
+
".png": "image/png",
|
|
22326
|
+
".webp": "image/webp",
|
|
22327
|
+
".jpg": "image/jpeg",
|
|
22328
|
+
".jpeg": "image/jpeg",
|
|
22329
|
+
".gif": "image/gif",
|
|
22330
|
+
".ico": "image/x-icon"
|
|
22331
|
+
});
|
|
22332
|
+
var logoMessage = "logo must be an image path inside the project (.svg, .png, .webp, .jpg, .gif or .ico), or { light, dark }.";
|
|
22333
|
+
function image(value) {
|
|
22334
|
+
if (typeof value !== "string" || value.length > 1024 || value.includes("\0")) return null;
|
|
22335
|
+
let path = value.trim().replaceAll("\\", "/");
|
|
22336
|
+
if (!path || path.startsWith("/") || /^[a-z]:/i.test(path)) return null;
|
|
22337
|
+
if (path.split("/").includes("..")) return null;
|
|
22338
|
+
let extension = path.slice(path.lastIndexOf(".")).toLowerCase();
|
|
22339
|
+
return Object.hasOwn(LOGO_TYPES, extension) ? path : null;
|
|
22340
|
+
}
|
|
22341
|
+
/** A logo in its one resolved form, or null for Bud's own mark. */
|
|
22342
|
+
var BudLogoSchema = createSchema((value, context) => {
|
|
22343
|
+
if (value === void 0) return { value: null };
|
|
22344
|
+
let light = image(typeof value === "object" && value ? value.light : value);
|
|
22345
|
+
let raw = typeof value === "object" && value ? value.dark : void 0;
|
|
22346
|
+
let dark = raw === void 0 ? null : image(raw);
|
|
22347
|
+
let keys = typeof value === "object" && value ? Object.keys(value) : [];
|
|
22348
|
+
if (light && (raw === void 0 || dark) && keys.every((key) => key === "light" || key === "dark")) return { value: {
|
|
22349
|
+
light,
|
|
22350
|
+
dark
|
|
22351
|
+
} };
|
|
22352
|
+
return fail(logoMessage, context.path);
|
|
22353
|
+
});
|
|
22354
|
+
//#endregion
|
|
22355
|
+
//#region ../project/src/lib/theme.ts
|
|
22356
|
+
/**
|
|
22357
|
+
* Bud's colors and shadows a theme may set, as `--bud-<name>` in its styles. Everything else
|
|
22358
|
+
* Bud derives from them.
|
|
22359
|
+
*/
|
|
22360
|
+
var THEME_TOKENS = Object.freeze([
|
|
22361
|
+
"background",
|
|
22362
|
+
"shell",
|
|
22363
|
+
"main",
|
|
22364
|
+
"canvas",
|
|
22365
|
+
"panel",
|
|
22366
|
+
"board",
|
|
22367
|
+
"editor",
|
|
22368
|
+
"editor-tabs",
|
|
22369
|
+
"foreground",
|
|
22370
|
+
"muted",
|
|
22371
|
+
"primary",
|
|
22372
|
+
"primary-hover",
|
|
22373
|
+
"primary-foreground",
|
|
22374
|
+
"border",
|
|
22375
|
+
"tab-divider",
|
|
22376
|
+
"hover",
|
|
22377
|
+
"selected",
|
|
22378
|
+
"control",
|
|
22379
|
+
"control-active",
|
|
22380
|
+
"control-selected",
|
|
22381
|
+
"card",
|
|
22382
|
+
"menu",
|
|
22383
|
+
"menu-hover",
|
|
22384
|
+
"card-frame",
|
|
22385
|
+
"card-surface",
|
|
22386
|
+
"card-border",
|
|
22387
|
+
"card-frame-border",
|
|
22388
|
+
"card-badge",
|
|
22389
|
+
"overlay-shadow",
|
|
22390
|
+
"tooltip",
|
|
22391
|
+
"board-control",
|
|
22392
|
+
"board-control-hover",
|
|
22393
|
+
"board-card",
|
|
22394
|
+
"control-elevation",
|
|
22395
|
+
"board-elevation",
|
|
22396
|
+
"board-card-elevation",
|
|
22397
|
+
"card-elevation",
|
|
22398
|
+
"board-card-lift",
|
|
22399
|
+
"frame-control",
|
|
22400
|
+
"modal-elevation",
|
|
22401
|
+
"segment-elevation",
|
|
22402
|
+
"control-open-elevation",
|
|
22403
|
+
"board-segment",
|
|
22404
|
+
"scrollbar",
|
|
22405
|
+
"focus",
|
|
22406
|
+
"scrollbar-hover",
|
|
22407
|
+
"tooltip-border",
|
|
22408
|
+
"tooltip-shadow"
|
|
22409
|
+
]);
|
|
22410
|
+
var themeMessage = `theme must be { light, dark }, each mapping Bud's tokens (${THEME_TOKENS.slice(0, 3).join(", ")}, …) to CSS colors or shadows.`;
|
|
22411
|
+
function cssValue(value) {
|
|
22412
|
+
if (typeof value !== "string") return null;
|
|
22413
|
+
let trimmed = value.trim();
|
|
22414
|
+
if (!trimmed || trimmed.length > 300) return null;
|
|
22415
|
+
if (!/^[#a-zA-Z0-9(),.%\s/+-]+$/.test(trimmed) || /url\s*\(/i.test(trimmed)) return null;
|
|
22416
|
+
return trimmed;
|
|
22417
|
+
}
|
|
22418
|
+
function colors(value) {
|
|
22419
|
+
if (value === void 0) return {};
|
|
22420
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
22421
|
+
let result = {};
|
|
22422
|
+
for (let [name, raw] of Object.entries(value)) {
|
|
22423
|
+
let checked = cssValue(raw);
|
|
22424
|
+
if (!THEME_TOKENS.includes(name) || checked === null) return null;
|
|
22425
|
+
result[name] = checked;
|
|
22382
22426
|
}
|
|
22383
|
-
|
|
22384
|
-
"unavailable",
|
|
22385
|
-
"not-initialized",
|
|
22386
|
-
"unsupported-version"
|
|
22387
|
-
].includes(input.state)) throw new BeadsValidationError("connection.state");
|
|
22388
|
-
return {
|
|
22389
|
-
state: input.state,
|
|
22390
|
-
version: nullableText(input.version, "connection.version", 128),
|
|
22391
|
-
message: text$4(input.message, "connection.message", 1e3)
|
|
22392
|
-
};
|
|
22427
|
+
return Object.freeze(result);
|
|
22393
22428
|
}
|
|
22394
|
-
/**
|
|
22395
|
-
|
|
22396
|
-
|
|
22397
|
-
|
|
22398
|
-
|
|
22399
|
-
|
|
22400
|
-
|
|
22401
|
-
let
|
|
22402
|
-
|
|
22403
|
-
|
|
22404
|
-
|
|
22405
|
-
description: text$4(input.description, `${field}.description`),
|
|
22406
|
-
custom: boolean$2(input.custom, `${field}.custom`)
|
|
22429
|
+
/** A theme in its one resolved form; Bud's own colors when absent. */
|
|
22430
|
+
var BudThemeSchema = createSchema((value, context) => {
|
|
22431
|
+
if (value === void 0) return { value: {
|
|
22432
|
+
light: {},
|
|
22433
|
+
dark: {}
|
|
22434
|
+
} };
|
|
22435
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return fail(themeMessage, context.path);
|
|
22436
|
+
let { light, dark, ...rest } = value;
|
|
22437
|
+
let resolved = {
|
|
22438
|
+
light: colors(light),
|
|
22439
|
+
dark: colors(dark)
|
|
22407
22440
|
};
|
|
22408
|
-
|
|
22441
|
+
if (Object.keys(rest).length || !resolved.light || !resolved.dark) return fail(themeMessage, context.path);
|
|
22442
|
+
return { value: {
|
|
22443
|
+
light: resolved.light,
|
|
22444
|
+
dark: resolved.dark
|
|
22445
|
+
} };
|
|
22446
|
+
});
|
|
22409
22447
|
/**
|
|
22410
|
-
*
|
|
22411
|
-
* @param
|
|
22412
|
-
* @
|
|
22413
|
-
* @returns The type.
|
|
22448
|
+
* The theme as styles that follow Bud's own, so its tokens win in each color scheme.
|
|
22449
|
+
* @param theme - The resolved theme.
|
|
22450
|
+
* @returns A `<style>` element, or an empty string for Bud's own colors.
|
|
22414
22451
|
*/
|
|
22415
|
-
function
|
|
22416
|
-
let
|
|
22417
|
-
|
|
22418
|
-
"
|
|
22419
|
-
"
|
|
22420
|
-
"custom"
|
|
22421
|
-
].includes(input.kind)) throw new BeadsValidationError(`${field}.kind`);
|
|
22422
|
-
return {
|
|
22423
|
-
name: identifier$1(input.name, `${field}.name`),
|
|
22424
|
-
description: text$4(input.description, `${field}.description`),
|
|
22425
|
-
kind: input.kind
|
|
22452
|
+
function themeStyle(theme) {
|
|
22453
|
+
let block = (selector, values) => {
|
|
22454
|
+
let entries = Object.entries(values);
|
|
22455
|
+
if (!entries.length) return "";
|
|
22456
|
+
return `${selector}{${entries.map(([name, value]) => `--bud-${name}:${value};`).join("")}}`;
|
|
22426
22457
|
};
|
|
22458
|
+
let css = block(":root:not([data-theme='dark'])", theme.light) + block(":root[data-theme='dark']", theme.dark);
|
|
22459
|
+
return css ? `<style data-bud-theme>${css}</style>` : "";
|
|
22427
22460
|
}
|
|
22428
|
-
|
|
22429
|
-
|
|
22430
|
-
|
|
22431
|
-
|
|
22432
|
-
|
|
22433
|
-
function parseBeadsSnapshot(value) {
|
|
22434
|
-
let input = object$1(value, "snapshot");
|
|
22435
|
-
let connection = parseBeadsConnection(input.connection);
|
|
22436
|
-
let issues = unique(array$1(input.issues, "snapshot.issues", parseBeadIssue, MAX_BEADS), (issue) => issue.id, "snapshot.issues");
|
|
22437
|
-
let readyIds = unique(array$1(input.readyIds, "snapshot.readyIds", identifier$1, MAX_BEADS), (id) => id, "snapshot.readyIds");
|
|
22438
|
-
let statuses = unique(array$1(input.statuses, "snapshot.statuses", parseBeadStatus, 1e3), (item) => item.name, "snapshot.statuses");
|
|
22439
|
-
let types = unique(array$1(input.types, "snapshot.types", parseBeadType, 1e3), (item) => item.name, "snapshot.types");
|
|
22440
|
-
let truncated = boolean$2(input.truncated, "snapshot.truncated");
|
|
22441
|
-
if (connection.state !== "ready" && (issues.length || readyIds.length || statuses.length || types.length || truncated)) throw new BeadsValidationError("snapshot.connection");
|
|
22442
|
-
return {
|
|
22443
|
-
connection,
|
|
22444
|
-
issues,
|
|
22445
|
-
readyIds,
|
|
22446
|
-
statuses,
|
|
22447
|
-
types,
|
|
22448
|
-
truncated
|
|
22449
|
-
};
|
|
22461
|
+
//#endregion
|
|
22462
|
+
//#region ../project/src/lib/branches.ts
|
|
22463
|
+
var message = "branches must be { main?, build?: { install?, build?, serve? } } with a branch name and nonempty shell commands.";
|
|
22464
|
+
function branch(value) {
|
|
22465
|
+
return typeof value === "string" && value.length <= 255 && /^[A-Za-z0-9._/-]+$/.test(value) && !value.startsWith("-") && !value.startsWith("/") && !value.endsWith("/") && !value.endsWith(".lock") && !value.includes("..") && !value.includes("//") ? value : null;
|
|
22450
22466
|
}
|
|
22451
|
-
|
|
22452
|
-
|
|
22453
|
-
* @param value - Untrusted data.
|
|
22454
|
-
* @param field - Where the value sits, for the error.
|
|
22455
|
-
* @returns The comment.
|
|
22456
|
-
*/
|
|
22457
|
-
function parseBeadDiscussion(value, field = "discussion") {
|
|
22458
|
-
let input = object$1(value, field);
|
|
22459
|
-
return {
|
|
22460
|
-
id: identifier$1(input.id, `${field}.id`),
|
|
22461
|
-
issueId: identifier$1(input.issueId, `${field}.issueId`),
|
|
22462
|
-
author: text$4(input.author, `${field}.author`),
|
|
22463
|
-
body: text$4(input.body, `${field}.body`),
|
|
22464
|
-
createdAt: timestamp(input.createdAt, `${field}.createdAt`)
|
|
22465
|
-
};
|
|
22467
|
+
function command(value) {
|
|
22468
|
+
return typeof value === "string" && value.trim() && value.length <= 4096 && !value.includes("\0") ? value.trim() : null;
|
|
22466
22469
|
}
|
|
22467
|
-
/**
|
|
22468
|
-
|
|
22469
|
-
|
|
22470
|
-
|
|
22471
|
-
|
|
22472
|
-
|
|
22473
|
-
|
|
22474
|
-
let input = object$1(value, field);
|
|
22475
|
-
return {
|
|
22476
|
-
commit: identifier$1(input.commit, `${field}.commit`),
|
|
22477
|
-
author: text$4(input.author, `${field}.author`),
|
|
22478
|
-
createdAt: timestamp(input.createdAt, `${field}.createdAt`),
|
|
22479
|
-
issue: input.issue == null ? null : parseBeadIssue(input.issue, `${field}.issue`)
|
|
22470
|
+
/** The branch settings, defaults filled in. */
|
|
22471
|
+
var BudBranchesSchema = createSchema((value, context) => {
|
|
22472
|
+
let empty = {
|
|
22473
|
+
main: null,
|
|
22474
|
+
install: null,
|
|
22475
|
+
build: null,
|
|
22476
|
+
serve: null
|
|
22480
22477
|
};
|
|
22481
|
-
}
|
|
22478
|
+
if (value === void 0) return { value: empty };
|
|
22479
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return fail(message, context.path);
|
|
22480
|
+
let input = value;
|
|
22481
|
+
let build = input.build ?? {};
|
|
22482
|
+
if (!build || typeof build !== "object" || Array.isArray(build) || Object.keys(input).some((key) => key !== "main" && key !== "build")) return fail(message, context.path);
|
|
22483
|
+
let steps = build;
|
|
22484
|
+
if (Object.keys(steps).some((key) => ![
|
|
22485
|
+
"install",
|
|
22486
|
+
"build",
|
|
22487
|
+
"serve"
|
|
22488
|
+
].includes(key))) return fail(message, context.path);
|
|
22489
|
+
let resolved = {
|
|
22490
|
+
main: input.main === void 0 ? null : branch(input.main),
|
|
22491
|
+
install: steps.install === void 0 ? null : command(steps.install),
|
|
22492
|
+
build: steps.build === void 0 ? null : command(steps.build),
|
|
22493
|
+
serve: steps.serve === void 0 ? null : command(steps.serve)
|
|
22494
|
+
};
|
|
22495
|
+
if (input.main !== void 0 && !resolved.main || [
|
|
22496
|
+
"install",
|
|
22497
|
+
"build",
|
|
22498
|
+
"serve"
|
|
22499
|
+
].some((key) => steps[key] !== void 0 && !resolved[key])) return fail(message, context.path);
|
|
22500
|
+
return { value: resolved };
|
|
22501
|
+
});
|
|
22502
|
+
//#endregion
|
|
22503
|
+
//#region ../project/src/lib/storage.ts
|
|
22482
22504
|
/**
|
|
22483
|
-
*
|
|
22484
|
-
* @param
|
|
22485
|
-
* @param
|
|
22486
|
-
* @
|
|
22505
|
+
* A project module named in the config: a ./ path inside the project, outside node_modules.
|
|
22506
|
+
* @param name - The config option, for the error message.
|
|
22507
|
+
* @param server - Whether it runs on the server (.server.ts) rather than in the browser.
|
|
22508
|
+
* @param message - What to say instead of the default when it is invalid.
|
|
22509
|
+
* @returns A schema for the module's path.
|
|
22487
22510
|
*/
|
|
22488
|
-
function
|
|
22489
|
-
let
|
|
22490
|
-
return {
|
|
22491
|
-
id: identifier$1(input.id, `${field}.id`),
|
|
22492
|
-
issueId: identifier$1(input.issueId, `${field}.issueId`),
|
|
22493
|
-
type: identifier$1(input.type, `${field}.type`),
|
|
22494
|
-
actor: text$4(input.actor, `${field}.actor`),
|
|
22495
|
-
oldValue: nullableText(input.oldValue, `${field}.oldValue`),
|
|
22496
|
-
newValue: nullableText(input.newValue, `${field}.newValue`),
|
|
22497
|
-
comment: nullableText(input.comment, `${field}.comment`),
|
|
22498
|
-
createdAt: timestamp(input.createdAt, `${field}.createdAt`)
|
|
22499
|
-
};
|
|
22511
|
+
function modulePath(name, server, message) {
|
|
22512
|
+
let extension = server ? /\.server\.ts$/ : /\.(?:ts|tsx|js|jsx)$/;
|
|
22513
|
+
return described(string().refine((value) => value.length <= 1024 && value.startsWith("./") && !/[\u0000-\u001f\u007f\\?#%]/.test(value) && extension.test(value) && (server || !/\.server\.[cm]?[jt]sx?$/.test(value)) && !value.slice(2).split("/").some((part) => !part || part === "." || part === ".." || part === "node_modules")), message ?? (server ? `${name} must be a built-in adapter or a ./ module ending in .server.ts inside the project.` : `${name} must be bud:beads or a ./ browser module ending in .ts, .tsx, .js or .jsx inside the project.`));
|
|
22500
22514
|
}
|
|
22501
|
-
|
|
22502
|
-
|
|
22503
|
-
|
|
22504
|
-
|
|
22505
|
-
|
|
22506
|
-
|
|
22507
|
-
|
|
22508
|
-
|
|
22509
|
-
|
|
22510
|
-
if (input.state === "unsupported" && items.length === 0 && truncated === false) return {
|
|
22511
|
-
state: "unsupported",
|
|
22512
|
-
items: [],
|
|
22513
|
-
truncated: false
|
|
22514
|
-
};
|
|
22515
|
-
throw new BeadsValidationError(`${field}.state`);
|
|
22516
|
-
}
|
|
22517
|
-
/**
|
|
22518
|
-
* Validates an issue's detail, whose sections must all belong to that issue.
|
|
22519
|
-
* @param value - Untrusted data.
|
|
22520
|
-
* @returns The detail.
|
|
22521
|
-
*/
|
|
22522
|
-
function parseBeadDetail(value) {
|
|
22523
|
-
let input = object$1(value, "detail");
|
|
22524
|
-
let connection = parseBeadsConnection(input.connection);
|
|
22525
|
-
if (connection.state !== "ready") throw new BeadsValidationError("detail.connection");
|
|
22526
|
-
let issue = parseBeadIssue(input.issue);
|
|
22527
|
-
let discussion = section$3(input.discussion, "discussion", parseBeadDiscussion, 200);
|
|
22528
|
-
let history = section$3(input.history, "history", parseBeadHistory, 50);
|
|
22529
|
-
let events = section$3(input.events, "events", parseBeadEvent, 50);
|
|
22530
|
-
if (!issue.contentLoaded || discussion.items.some((entry) => entry.issueId !== issue.id) || events.items.some((entry) => entry.issueId !== issue.id) || history.items.some((entry) => entry.issue && entry.issue.id !== issue.id)) throw new BeadsValidationError("detail.issue");
|
|
22531
|
-
return {
|
|
22532
|
-
connection,
|
|
22533
|
-
issue,
|
|
22534
|
-
discussion,
|
|
22535
|
-
history,
|
|
22536
|
-
events
|
|
22537
|
-
};
|
|
22515
|
+
var builtInStores = enum_([
|
|
22516
|
+
false,
|
|
22517
|
+
"bud:beads",
|
|
22518
|
+
"bud:memory",
|
|
22519
|
+
"bud:git-comments"
|
|
22520
|
+
]);
|
|
22521
|
+
function store(kind) {
|
|
22522
|
+
let module = modulePath(`storage.${kind}`, true);
|
|
22523
|
+
return defaulted(check(select$1((value) => value === false || value === "bud:beads" || value === "bud:memory" || value === "bud:git-comments" ? builtInStores : module), (value) => kind === "work" && value === "bud:git-comments" ? "storage.work cannot use bud:git-comments; it only stores threads." : void 0), "bud:beads");
|
|
22538
22524
|
}
|
|
22525
|
+
/** The selected work and discussion stores, with Beads as the default for both. */
|
|
22526
|
+
var BudStorageSchema = defaulted(settings("storage", {
|
|
22527
|
+
work: store("work"),
|
|
22528
|
+
threads: store("threads")
|
|
22529
|
+
}), () => ({
|
|
22530
|
+
work: "bud:beads",
|
|
22531
|
+
threads: "bud:beads"
|
|
22532
|
+
}));
|
|
22533
|
+
/** Task presentation, chosen separately from its storage. */
|
|
22534
|
+
var BudDesignsSchema = defaulted(settings("designs", {
|
|
22535
|
+
tasks: defaulted(select$1((value) => value === "bud:beads" ? enum_(["bud:beads"]) : modulePath("designs.tasks", false)), "bud:beads"),
|
|
22536
|
+
pages: defaulted(enum_(["path", "title"]), "path")
|
|
22537
|
+
}), () => ({
|
|
22538
|
+
tasks: "bud:beads",
|
|
22539
|
+
pages: "path"
|
|
22540
|
+
}));
|
|
22539
22541
|
//#endregion
|
|
22540
|
-
//#region ../
|
|
22541
|
-
var
|
|
22542
|
-
"
|
|
22543
|
-
"
|
|
22544
|
-
"
|
|
22545
|
-
|
|
22546
|
-
|
|
22547
|
-
|
|
22548
|
-
|
|
22549
|
-
|
|
22550
|
-
|
|
22551
|
-
|
|
22552
|
-
|
|
22553
|
-
"
|
|
22554
|
-
|
|
22555
|
-
|
|
22556
|
-
|
|
22557
|
-
"
|
|
22558
|
-
|
|
22559
|
-
|
|
22560
|
-
|
|
22561
|
-
|
|
22562
|
-
|
|
22563
|
-
|
|
22564
|
-
|
|
22565
|
-
|
|
22566
|
-
|
|
22567
|
-
|
|
22568
|
-
"
|
|
22569
|
-
"
|
|
22570
|
-
|
|
22571
|
-
|
|
22572
|
-
|
|
22573
|
-
|
|
22574
|
-
return
|
|
22575
|
-
}
|
|
22576
|
-
|
|
22577
|
-
|
|
22578
|
-
|
|
22579
|
-
|
|
22542
|
+
//#region ../project/src/lib/picker.ts
|
|
22543
|
+
var builtIn = /* @__PURE__ */ new Set([
|
|
22544
|
+
"bud:dom",
|
|
22545
|
+
"bud:react",
|
|
22546
|
+
"bud:react-grab"
|
|
22547
|
+
]);
|
|
22548
|
+
var projectPicker = modulePath("picker", false, "picker must be bud:dom, bud:react, bud:react-grab or a ./ browser module ending in .ts, .tsx, .js or .jsx inside the project.");
|
|
22549
|
+
/** The picker, with Bud's DOM picker as the default. */
|
|
22550
|
+
var BudPickerSchema = createSchema((value, context) => {
|
|
22551
|
+
if (value === void 0) return { value: {
|
|
22552
|
+
name: "bud:dom",
|
|
22553
|
+
module: null
|
|
22554
|
+
} };
|
|
22555
|
+
if (typeof value === "string" && builtIn.has(value)) return { value: {
|
|
22556
|
+
name: value,
|
|
22557
|
+
module: null
|
|
22558
|
+
} };
|
|
22559
|
+
let result = projectPicker["~run"](value, context);
|
|
22560
|
+
return result.issues ? result : { value: {
|
|
22561
|
+
name: result.value,
|
|
22562
|
+
module: result.value
|
|
22563
|
+
} };
|
|
22564
|
+
});
|
|
22565
|
+
//#endregion
|
|
22566
|
+
//#region ../project/src/lib/previews.ts
|
|
22567
|
+
var readyMessage = "ready must be a CSS selector, { selector } or { expression }.";
|
|
22568
|
+
/** A readiness condition; a string is a selector. */
|
|
22569
|
+
var PreviewReadySchema = createSchema((value, context) => {
|
|
22570
|
+
let ready = typeof value === "string" ? { selector: value } : value;
|
|
22571
|
+
if (ready && typeof ready === "object" && !Array.isArray(ready)) {
|
|
22572
|
+
let entries = Object.entries(ready);
|
|
22573
|
+
let [key, text] = entries[0] ?? [];
|
|
22574
|
+
if (entries.length === 1 && (key === "selector" || key === "expression") && typeof text === "string" && text.trim() && text.length <= 4096) return { value: key === "selector" ? { selector: text.trim() } : { expression: text.trim() } };
|
|
22575
|
+
}
|
|
22576
|
+
return fail(readyMessage, context.path);
|
|
22577
|
+
});
|
|
22578
|
+
var defaultThemes = () => ["light", "dark"];
|
|
22579
|
+
var defaultViewports = () => [{
|
|
22580
|
+
id: "desktop",
|
|
22581
|
+
name: "Desktop",
|
|
22582
|
+
width: 1440,
|
|
22583
|
+
height: 900,
|
|
22584
|
+
mobile: false
|
|
22585
|
+
}];
|
|
22586
|
+
var viewportsMessage = "previews.viewports must list up to 4 { name, width, height?, mobile? } with distinct names, widths from 240 to 3840 and heights from 240 to 4000.";
|
|
22587
|
+
function viewportId(name) {
|
|
22588
|
+
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 32);
|
|
22580
22589
|
}
|
|
22590
|
+
var ViewportsSchema = createSchema((value, context) => {
|
|
22591
|
+
if (!Array.isArray(value) || value.length === 0 || value.length > 4) return fail(viewportsMessage, context.path);
|
|
22592
|
+
let viewports = [];
|
|
22593
|
+
for (let entry of value) {
|
|
22594
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return fail(viewportsMessage, context.path);
|
|
22595
|
+
let { name, width, height, mobile, ...rest } = entry;
|
|
22596
|
+
let size = (pixels, from, to) => typeof pixels === "number" && Number.isInteger(pixels) && pixels >= from && pixels <= to;
|
|
22597
|
+
if (Object.keys(rest).length || typeof name !== "string" || !name.trim() || name.length > 40 || !viewportId(name) || !size(width, 240, 3840) || height !== void 0 && !size(height, 240, 4e3) || mobile !== void 0 && typeof mobile !== "boolean") return fail(viewportsMessage, context.path);
|
|
22598
|
+
let phone = mobile ?? width < 800;
|
|
22599
|
+
viewports.push({
|
|
22600
|
+
id: viewportId(name),
|
|
22601
|
+
name: name.trim(),
|
|
22602
|
+
width,
|
|
22603
|
+
height: height ?? (phone ? Math.round(width * 844 / 390) : 900),
|
|
22604
|
+
mobile: phone
|
|
22605
|
+
});
|
|
22606
|
+
}
|
|
22607
|
+
if (new Set(viewports.map((viewport) => viewport.id)).size !== viewports.length) return fail(viewportsMessage, context.path);
|
|
22608
|
+
return { value: viewports };
|
|
22609
|
+
});
|
|
22610
|
+
/** The preview settings, with their defaults filled in. */
|
|
22611
|
+
var BudPreviewsSchema = defaulted(settings("previews", {
|
|
22612
|
+
scale: fallback(described(number().refine((scale) => Number.isInteger(scale) && scale >= 1 && scale <= 3), "previews.scale must be 1, 2 or 3."), () => 2),
|
|
22613
|
+
themes: fallback(described(array(enum_(["light", "dark"])).refine((themes) => themes.length > 0 && new Set(themes).size === themes.length), "previews.themes must list light, dark or both, once each."), defaultThemes),
|
|
22614
|
+
viewports: fallback(ViewportsSchema, defaultViewports),
|
|
22615
|
+
ready: fallback(PreviewReadySchema, () => null),
|
|
22616
|
+
readyTimeout: fallback(described(number().refine((ms) => Number.isInteger(ms) && ms >= 1e3 && ms <= 6e5), "previews.readyTimeout must be whole milliseconds from 1000 to 600000."), () => 6e4),
|
|
22617
|
+
css: fallback(described(string().refine((css) => css.length <= 2e4 && !css.includes("<")), "previews.css must be CSS text without <, up to 20000 characters."), () => "")
|
|
22618
|
+
}), () => ({
|
|
22619
|
+
scale: 2,
|
|
22620
|
+
themes: defaultThemes(),
|
|
22621
|
+
viewports: defaultViewports(),
|
|
22622
|
+
ready: null,
|
|
22623
|
+
readyTimeout: 6e4,
|
|
22624
|
+
css: ""
|
|
22625
|
+
}));
|
|
22626
|
+
//#endregion
|
|
22627
|
+
//#region ../project/src/lib/screen-routes.ts
|
|
22581
22628
|
/**
|
|
22582
|
-
* Validates
|
|
22583
|
-
* @param
|
|
22584
|
-
* @
|
|
22629
|
+
* Validates a screen's route map without importing its component module.
|
|
22630
|
+
* @param routes - A Remix route map shared with the screen's definition.
|
|
22631
|
+
* @param path - The configured screen's static path.
|
|
22632
|
+
* @returns Named patterns confined to the screen's path.
|
|
22585
22633
|
*/
|
|
22586
|
-
function
|
|
22587
|
-
let
|
|
22588
|
-
let
|
|
22589
|
-
|
|
22590
|
-
|
|
22591
|
-
|
|
22592
|
-
|
|
22593
|
-
|
|
22594
|
-
|
|
22595
|
-
|
|
22596
|
-
|
|
22597
|
-
|
|
22598
|
-
|
|
22599
|
-
|
|
22600
|
-
|
|
22601
|
-
|
|
22602
|
-
|
|
22603
|
-
|
|
22604
|
-
|
|
22605
|
-
|
|
22606
|
-
|
|
22607
|
-
|
|
22608
|
-
|
|
22609
|
-
|
|
22610
|
-
if ("metadata" in input) {
|
|
22611
|
-
let source = object$1(input.metadata, "patch.metadata");
|
|
22612
|
-
if (JSON.stringify(source).length > 32e3) throw new BeadsValidationError("patch.metadata");
|
|
22613
|
-
patch.metadata = metadata(source, "patch.metadata");
|
|
22634
|
+
function resolveScreenRoutes(routes, path) {
|
|
22635
|
+
let result = [];
|
|
22636
|
+
let seen = /* @__PURE__ */ new Set();
|
|
22637
|
+
function visit(value, prefix) {
|
|
22638
|
+
if (!value || typeof value !== "object" || Array.isArray(value) || seen.has(value)) throw new Error("Screen routes must be a Remix route map.");
|
|
22639
|
+
seen.add(value);
|
|
22640
|
+
for (let [key, entry] of Object.entries(value)) {
|
|
22641
|
+
let name = prefix ? `${prefix}.${key}` : key;
|
|
22642
|
+
if (!entry || typeof entry !== "object") throw new Error(`Screen route ${name} must be a Remix route.`);
|
|
22643
|
+
if (!("pattern" in entry)) {
|
|
22644
|
+
visit(entry, name);
|
|
22645
|
+
continue;
|
|
22646
|
+
}
|
|
22647
|
+
if (!["GET", "ANY"].includes(entry.method) || typeof entry.href !== "function" || typeof entry.pattern?.source !== "string") throw new Error(`Screen route ${name} must be a GET route.`);
|
|
22648
|
+
let pattern = RoutePattern.parse(entry.pattern.source);
|
|
22649
|
+
let parts = pattern.toJSON();
|
|
22650
|
+
let pathname = `/${parts.pathname}`;
|
|
22651
|
+
if (parts.protocol || parts.hostname || parts.port || parts.pathname.split("/").some((segment) => /^\.{1,2}$/.test(decodeURIComponent(segment))) || pathname !== path && !pathname.startsWith(`${path}/`) && !pathname.startsWith(`${path}(/`)) throw new Error(`Screen route ${name} must stay under ${path}.`);
|
|
22652
|
+
result.push({
|
|
22653
|
+
name,
|
|
22654
|
+
pattern: pattern.source
|
|
22655
|
+
});
|
|
22656
|
+
}
|
|
22657
|
+
seen.delete(value);
|
|
22614
22658
|
}
|
|
22615
|
-
|
|
22616
|
-
|
|
22659
|
+
visit(routes, "");
|
|
22660
|
+
if (!result.length) throw new Error("Screen routes must contain at least one route.");
|
|
22661
|
+
return result;
|
|
22617
22662
|
}
|
|
22663
|
+
//#endregion
|
|
22664
|
+
//#region ../project/src/lib/screens.ts
|
|
22618
22665
|
/**
|
|
22619
|
-
*
|
|
22620
|
-
* @
|
|
22621
|
-
* @returns The fields, with a title.
|
|
22666
|
+
* Bud's browser of your live pages, with their comments.
|
|
22667
|
+
* @returns The built-in screen, ready to relabel by spreading.
|
|
22622
22668
|
*/
|
|
22623
|
-
function
|
|
22624
|
-
let patch = parseBeadPatch(value);
|
|
22625
|
-
if (!patch.title) throw new BeadsValidationError("create.title");
|
|
22669
|
+
function pages() {
|
|
22626
22670
|
return {
|
|
22627
|
-
|
|
22628
|
-
|
|
22671
|
+
id: "pages",
|
|
22672
|
+
label: "Pages",
|
|
22673
|
+
icon: "browser",
|
|
22674
|
+
path: "/pages",
|
|
22675
|
+
module: "bud:pages"
|
|
22629
22676
|
};
|
|
22630
22677
|
}
|
|
22631
22678
|
/**
|
|
22632
|
-
*
|
|
22633
|
-
* @
|
|
22634
|
-
* @returns The action.
|
|
22679
|
+
* Bud's canvas of every route's screenshot.
|
|
22680
|
+
* @returns The built-in screen, ready to relabel by spreading.
|
|
22635
22681
|
*/
|
|
22636
|
-
function
|
|
22637
|
-
|
|
22638
|
-
|
|
22639
|
-
|
|
22640
|
-
|
|
22641
|
-
|
|
22642
|
-
|
|
22643
|
-
|
|
22644
|
-
"patch"
|
|
22645
|
-
]);
|
|
22646
|
-
return {
|
|
22647
|
-
kind: input.kind,
|
|
22648
|
-
expectedRevision,
|
|
22649
|
-
patch: parseBeadPatch(input.patch)
|
|
22650
|
-
};
|
|
22651
|
-
case "claim":
|
|
22652
|
-
strict$1(input, "action", ["kind", "expectedRevision"]);
|
|
22653
|
-
return {
|
|
22654
|
-
kind: input.kind,
|
|
22655
|
-
expectedRevision
|
|
22656
|
-
};
|
|
22657
|
-
case "close":
|
|
22658
|
-
case "reopen":
|
|
22659
|
-
strict$1(input, "action", [
|
|
22660
|
-
"kind",
|
|
22661
|
-
"expectedRevision",
|
|
22662
|
-
"reason"
|
|
22663
|
-
]);
|
|
22664
|
-
return {
|
|
22665
|
-
kind: input.kind,
|
|
22666
|
-
expectedRevision,
|
|
22667
|
-
reason: text$4(input.reason ?? "", "action.reason", 4e3)
|
|
22668
|
-
};
|
|
22669
|
-
case "add-dependency":
|
|
22670
|
-
strict$1(input, "action", [
|
|
22671
|
-
"kind",
|
|
22672
|
-
"expectedRevision",
|
|
22673
|
-
"targetId",
|
|
22674
|
-
"type"
|
|
22675
|
-
]);
|
|
22676
|
-
if (!dependencyTypes.includes(input.type)) throw new BeadsValidationError("action.type");
|
|
22677
|
-
return {
|
|
22678
|
-
kind: input.kind,
|
|
22679
|
-
expectedRevision,
|
|
22680
|
-
targetId: identifier$1(input.targetId, "action.targetId"),
|
|
22681
|
-
type: input.type
|
|
22682
|
-
};
|
|
22683
|
-
case "remove-dependency":
|
|
22684
|
-
strict$1(input, "action", [
|
|
22685
|
-
"kind",
|
|
22686
|
-
"expectedRevision",
|
|
22687
|
-
"targetId"
|
|
22688
|
-
]);
|
|
22689
|
-
return {
|
|
22690
|
-
kind: input.kind,
|
|
22691
|
-
expectedRevision,
|
|
22692
|
-
targetId: identifier$1(input.targetId, "action.targetId")
|
|
22693
|
-
};
|
|
22694
|
-
case "comment": {
|
|
22695
|
-
strict$1(input, "action", [
|
|
22696
|
-
"kind",
|
|
22697
|
-
"expectedRevision",
|
|
22698
|
-
"body"
|
|
22699
|
-
]);
|
|
22700
|
-
let body = nonempty(input.body, "action.body", 6e4);
|
|
22701
|
-
if (new TextEncoder().encode(body).byteLength > 122880) throw new BeadsValidationError("action.body");
|
|
22702
|
-
return {
|
|
22703
|
-
kind: input.kind,
|
|
22704
|
-
expectedRevision,
|
|
22705
|
-
body
|
|
22706
|
-
};
|
|
22707
|
-
}
|
|
22708
|
-
default: throw new BeadsValidationError("action.kind");
|
|
22709
|
-
}
|
|
22682
|
+
function canvas() {
|
|
22683
|
+
return {
|
|
22684
|
+
id: "canvas",
|
|
22685
|
+
label: "Canvas",
|
|
22686
|
+
icon: "grid",
|
|
22687
|
+
path: "/canvas",
|
|
22688
|
+
module: "bud:canvas"
|
|
22689
|
+
};
|
|
22710
22690
|
}
|
|
22711
22691
|
/**
|
|
22712
|
-
*
|
|
22713
|
-
* @
|
|
22714
|
-
* @returns The request.
|
|
22692
|
+
* Bud's Beads task board.
|
|
22693
|
+
* @returns The built-in screen, ready to relabel by spreading.
|
|
22715
22694
|
*/
|
|
22716
|
-
function
|
|
22717
|
-
|
|
22718
|
-
|
|
22719
|
-
|
|
22695
|
+
function tasks() {
|
|
22696
|
+
return {
|
|
22697
|
+
id: "tasks",
|
|
22698
|
+
label: "Tasks",
|
|
22699
|
+
icon: "tasks",
|
|
22700
|
+
path: "/tasks",
|
|
22701
|
+
module: "bud:tasks"
|
|
22702
|
+
};
|
|
22720
22703
|
}
|
|
22721
|
-
//#endregion
|
|
22722
|
-
//#region ../beads/src/lib/protocol.ts
|
|
22723
22704
|
/**
|
|
22724
|
-
*
|
|
22725
|
-
*
|
|
22726
|
-
* @param value - The record.
|
|
22727
|
-
* @param contentLoaded - Whether it holds the issue's long content.
|
|
22728
|
-
* @returns The issue.
|
|
22705
|
+
* Bud's file browser and review.
|
|
22706
|
+
* @returns The built-in screen, ready to relabel by spreading.
|
|
22729
22707
|
*/
|
|
22730
|
-
function
|
|
22731
|
-
|
|
22732
|
-
|
|
22733
|
-
|
|
22734
|
-
|
|
22735
|
-
|
|
22736
|
-
|
|
22737
|
-
|
|
22738
|
-
|
|
22739
|
-
|
|
22740
|
-
|
|
22741
|
-
|
|
22742
|
-
|
|
22743
|
-
|
|
22744
|
-
|
|
22745
|
-
|
|
22746
|
-
|
|
22747
|
-
|
|
22748
|
-
|
|
22749
|
-
|
|
22750
|
-
|
|
22751
|
-
|
|
22752
|
-
|
|
22753
|
-
|
|
22754
|
-
|
|
22755
|
-
|
|
22708
|
+
function files() {
|
|
22709
|
+
return {
|
|
22710
|
+
id: "files",
|
|
22711
|
+
label: "Files",
|
|
22712
|
+
icon: "files",
|
|
22713
|
+
path: "/files",
|
|
22714
|
+
module: "bud:files"
|
|
22715
|
+
};
|
|
22716
|
+
}
|
|
22717
|
+
/** The screens shown when the config doesn't list any. */
|
|
22718
|
+
var defaultScreens = [
|
|
22719
|
+
pages(),
|
|
22720
|
+
canvas(),
|
|
22721
|
+
tasks(),
|
|
22722
|
+
files()
|
|
22723
|
+
];
|
|
22724
|
+
var builtIns$1 = new Map(defaultScreens.map((screen) => [screen.module, screen]));
|
|
22725
|
+
/** First path segments owned by Bud's routes, its development server and its public assets. */
|
|
22726
|
+
var reservedScreenPaths = [
|
|
22727
|
+
"/comments",
|
|
22728
|
+
...defaultScreens.map((screen) => screen.path),
|
|
22729
|
+
"/api",
|
|
22730
|
+
"/health",
|
|
22731
|
+
"/__bud",
|
|
22732
|
+
"/assets",
|
|
22733
|
+
"/app",
|
|
22734
|
+
"/src",
|
|
22735
|
+
"/node_modules",
|
|
22736
|
+
"/agents",
|
|
22737
|
+
"/brand",
|
|
22738
|
+
"/fonts",
|
|
22739
|
+
"/frameworks",
|
|
22740
|
+
"/licenses"
|
|
22741
|
+
];
|
|
22742
|
+
var ScreenSchema = check(described(any().refine((value) => !!value && typeof value === "object"), "Each screen must be an object."), ({ id, label, icon = "layers", module, path }) => {
|
|
22743
|
+
if (typeof id !== "string" || !/^[a-z][a-z0-9-]{0,31}$/.test(id)) return "Screen ids are 1-32 lowercase letters, digits and hyphens.";
|
|
22744
|
+
if (typeof label !== "string" || !label.trim()) return `Screen ${id} needs a label.`;
|
|
22745
|
+
if (typeof icon !== "string" || !icon) return `Screen ${id} needs an icon name.`;
|
|
22746
|
+
if (typeof module !== "string") return `Screen ${id} needs a module.`;
|
|
22747
|
+
let builtIn = builtIns$1.get(module);
|
|
22748
|
+
path ??= builtIn?.path ?? `/${id}`;
|
|
22749
|
+
if (builtIn) {
|
|
22750
|
+
if (id !== builtIn.id || path !== builtIn.path) return `${module} keeps id "${builtIn.id}" and path "${builtIn.path}".`;
|
|
22751
|
+
return;
|
|
22756
22752
|
}
|
|
22757
|
-
return
|
|
22758
|
-
|
|
22759
|
-
|
|
22760
|
-
|
|
22761
|
-
|
|
22762
|
-
|
|
22763
|
-
|
|
22764
|
-
|
|
22765
|
-
|
|
22766
|
-
|
|
22767
|
-
|
|
22768
|
-
|
|
22769
|
-
|
|
22770
|
-
|
|
22771
|
-
|
|
22772
|
-
|
|
22773
|
-
|
|
22774
|
-
|
|
22775
|
-
|
|
22776
|
-
|
|
22777
|
-
|
|
22778
|
-
|
|
22779
|
-
|
|
22780
|
-
|
|
22781
|
-
|
|
22782
|
-
dependencies: dependencies("dependencies"),
|
|
22783
|
-
dependents: dependencies("dependents"),
|
|
22784
|
-
dependencyCount: input.dependency_count ?? null,
|
|
22785
|
-
dependentCount: input.dependent_count ?? null,
|
|
22786
|
-
commentCount: input.comment_count ?? null,
|
|
22787
|
-
epicTotalChildren: input.epic_total_children ?? null,
|
|
22788
|
-
epicClosedChildren: input.epic_closed_children ?? null,
|
|
22789
|
-
epicCloseable: input.epic_closeable ?? null,
|
|
22790
|
-
revision: input.revision ?? null,
|
|
22791
|
-
metadata: input.metadata ?? null,
|
|
22792
|
-
contentLoaded
|
|
22753
|
+
if (!/^\.\/[^\0\\]+\.(?:ts|tsx|js|jsx)$/.test(module)) return `Screen ${id} must be a ./ module ending in .ts, .tsx, .js or .jsx.`;
|
|
22754
|
+
if (typeof path !== "string" || !/^\/[a-z0-9][a-z0-9-]*$/.test(path)) return `Screen ${id} needs one static path segment, such as /${id}.`;
|
|
22755
|
+
if (defaultScreens.some((screen) => screen.id === id)) return `Screen id "${id}" is reserved for a built-in screen.`;
|
|
22756
|
+
if (reservedScreenPaths.includes(path)) return `Bud reserves the path ${path}.`;
|
|
22757
|
+
});
|
|
22758
|
+
/** Bud's navigation in order; file and icon checks need the app and happen at startup. */
|
|
22759
|
+
var BudScreensSchema = defaulted(delegate(described(array(any()).refine((screens) => screens.length > 0), "screens must list at least one screen."), (screens) => {
|
|
22760
|
+
let ids = /* @__PURE__ */ new Set();
|
|
22761
|
+
let paths = /* @__PURE__ */ new Set();
|
|
22762
|
+
return screens.map((value) => {
|
|
22763
|
+
let input = parseConfig(ScreenSchema, value);
|
|
22764
|
+
let { id, label, icon = "layers", module } = input;
|
|
22765
|
+
let path = input.path ?? builtIns$1.get(module)?.path ?? `/${id}`;
|
|
22766
|
+
if (ids.has(id) || paths.has(path)) throw new Error("Screen ids and paths must be unique.");
|
|
22767
|
+
ids.add(id);
|
|
22768
|
+
paths.add(path);
|
|
22769
|
+
if (input.routes !== void 0) resolveScreenRoutes(input.routes, path);
|
|
22770
|
+
return {
|
|
22771
|
+
id,
|
|
22772
|
+
label,
|
|
22773
|
+
icon,
|
|
22774
|
+
path,
|
|
22775
|
+
module,
|
|
22776
|
+
...input.routes === void 0 ? {} : { routes: input.routes }
|
|
22777
|
+
};
|
|
22793
22778
|
});
|
|
22779
|
+
}), () => defaultScreens.map((screen) => ({ ...screen })));
|
|
22780
|
+
//#endregion
|
|
22781
|
+
//#region ../project/src/lib/config.ts
|
|
22782
|
+
var objectLike = (value) => !!value && typeof value === "object";
|
|
22783
|
+
function section$3(message, shape) {
|
|
22784
|
+
let schema = object(shape);
|
|
22785
|
+
return createSchema((value, context) => objectLike(value) ? schema["~run"](Array.isArray(value) ? { ...value } : value, context) : fail(message, context.path));
|
|
22794
22786
|
}
|
|
22795
|
-
|
|
22796
|
-
|
|
22797
|
-
|
|
22798
|
-
|
|
22799
|
-
|
|
22800
|
-
|
|
22801
|
-
|
|
22802
|
-
|
|
22803
|
-
|
|
22787
|
+
var ConfigFields = check(section$3("Bud config must export an object.", {
|
|
22788
|
+
routes: described(array(any()).refine((routes) => routes.length > 0), "bud.config must define at least one route."),
|
|
22789
|
+
name: optional(described(string().refine((name) => !!name.trim()), "name must be a nonempty string.")),
|
|
22790
|
+
target: optional(section$3("target must be an object.", {
|
|
22791
|
+
origin: any(),
|
|
22792
|
+
port: any()
|
|
22793
|
+
})),
|
|
22794
|
+
comments: optional(section$3("comments must be an object.", { remote: any() })),
|
|
22795
|
+
port: any(),
|
|
22796
|
+
screens: any(),
|
|
22797
|
+
storage: any(),
|
|
22798
|
+
designs: any(),
|
|
22799
|
+
previews: any(),
|
|
22800
|
+
files: any(),
|
|
22801
|
+
logo: any(),
|
|
22802
|
+
theme: any(),
|
|
22803
|
+
locale: any(),
|
|
22804
|
+
picker: any(),
|
|
22805
|
+
branches: any(),
|
|
22806
|
+
flows: any()
|
|
22807
|
+
}), ({ target }) => {
|
|
22808
|
+
if (target?.port === 0) return "target.port must be a listening port, not zero.";
|
|
22809
|
+
if (target?.origin !== void 0 && (typeof target.origin !== "string" || !target.origin.trim())) return "target.origin must be an HTTP(S) origin.";
|
|
22810
|
+
});
|
|
22811
|
+
var RouteSchema = check(any().transform((input) => typeof input === "string" ? {
|
|
22812
|
+
path: input,
|
|
22813
|
+
title: input === "/" ? "Home" : input
|
|
22814
|
+
} : input), (route) => {
|
|
22815
|
+
if (!route || typeof route.path !== "string" || !route.path.startsWith("/") || route.path.startsWith("//")) return "Canvas routes must be local paths beginning with a single /.";
|
|
22816
|
+
if (new URL(route.path, "http://bud.invalid").origin !== "http://bud.invalid" || route.path.includes("\\")) return "Canvas routes must stay on the target origin.";
|
|
22817
|
+
let id = route.id ?? routePath(route.path);
|
|
22818
|
+
if (typeof id !== "string" || !id.trim()) return "Canvas route IDs and paths must be unique.";
|
|
22819
|
+
if (typeof route.title !== "string" || !route.title.trim()) return "Each route needs a title.";
|
|
22820
|
+
if (route.pattern !== void 0 && (typeof route.pattern !== "string" || !route.pattern.startsWith("/"))) return "A route pattern must be a path beginning with /.";
|
|
22821
|
+
if (route.group !== void 0 && (typeof route.group !== "string" || !route.group.trim() || route.group.length > 120)) return "A route group must be a short name.";
|
|
22822
|
+
if (route.ready !== void 0 && route.ready !== false && !parseSafe(PreviewReadySchema, route.ready).success) return "A route ready must be false, a CSS selector, { selector } or { expression }.";
|
|
22823
|
+
}).transform((route) => {
|
|
22824
|
+
let path = routePath(route.path);
|
|
22825
|
+
return {
|
|
22826
|
+
id: route.id ?? path,
|
|
22827
|
+
path,
|
|
22828
|
+
title: route.title,
|
|
22829
|
+
...route.pattern === void 0 ? {} : { pattern: route.pattern },
|
|
22830
|
+
...route.group === void 0 ? {} : { group: route.group.trim() },
|
|
22831
|
+
...route.ready === void 0 ? {} : { ready: route.ready === false ? null : parseConfig(PreviewReadySchema, route.ready) }
|
|
22832
|
+
};
|
|
22833
|
+
});
|
|
22834
|
+
function routePath(path) {
|
|
22835
|
+
let url = new URL(path, "http://bud.invalid");
|
|
22836
|
+
let search = url.searchParams.toString();
|
|
22837
|
+
return `${url.pathname}${search ? `?${search}` : ""}${url.hash}`;
|
|
22838
|
+
}
|
|
22839
|
+
/** Canvas routes with unique IDs and normalized paths. */
|
|
22840
|
+
var BudRoutesSchema = check(list(RouteSchema, "bud.config must define at least one route.", 1), (routes) => new Set(routes.map((route) => route.id)).size !== routes.length || new Set(routes.map((route) => route.path)).size !== routes.length ? "Canvas route IDs and paths must be unique." : void 0);
|
|
22841
|
+
var port = (value) => value === void 0 || Number.isInteger(value) && value >= 0 && value <= 65535;
|
|
22842
|
+
var remote$1 = (value) => typeof value === "string" && /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(value);
|
|
22843
|
+
/** A project's config, validated and normalized before Bud starts any listeners. */
|
|
22844
|
+
var BudConfigSchema = delegate(check(delegate(check(check(delegate(check(delegate(ConfigFields, (config) => ({
|
|
22845
|
+
...config,
|
|
22846
|
+
routes: parseConfig(BudRoutesSchema, config.routes)
|
|
22847
|
+
})), ({ port: companion, target }) => port(companion ?? 0) && port(target?.port) ? void 0 : "Ports must be integers between 0 and 65535."), ({ target, ...config }) => {
|
|
22848
|
+
let origin = target?.origin ?? (target?.port ? `http://127.0.0.1:${target.port}` : void 0);
|
|
22849
|
+
return {
|
|
22850
|
+
...config,
|
|
22851
|
+
targetOrigin: origin ? normalizeOrigin(origin, "target.origin must be an HTTP(S) origin without a path or credentials.") : origin
|
|
22852
|
+
};
|
|
22853
|
+
}), ({ locale }) => locale === void 0 || locale === "en" || locale === "de" ? void 0 : "locale must be \"en\" or \"de\"."), ({ comments }) => remote$1(comments?.remote ?? "origin") ? void 0 : "comments.remote must be a Git remote name."), (config) => {
|
|
22854
|
+
let flows = parseConfig(BudFlowsSchema, config.flows);
|
|
22855
|
+
let steps = flows.flatMap((flow) => flow.steps.map((step) => flowStepRoute(flow, step)));
|
|
22856
|
+
let home = config.routes.findIndex((route) => route.path === "/") + 1;
|
|
22857
|
+
return {
|
|
22858
|
+
...config,
|
|
22859
|
+
flows,
|
|
22860
|
+
routes: [
|
|
22861
|
+
...config.routes.slice(0, home),
|
|
22862
|
+
...steps,
|
|
22863
|
+
...config.routes.slice(home)
|
|
22864
|
+
]
|
|
22865
|
+
};
|
|
22866
|
+
}), ({ routes }) => new Set(routes.map((route) => route.id)).size !== routes.length || new Set(routes.map((route) => route.path)).size !== routes.length ? "A flow step must not reuse a route’s id or path." : void 0), (config) => ({
|
|
22867
|
+
name: config.name ?? "Your project",
|
|
22868
|
+
routes: config.routes,
|
|
22869
|
+
flows: config.flows,
|
|
22870
|
+
port: config.port ?? 0,
|
|
22871
|
+
targetOrigin: config.targetOrigin,
|
|
22872
|
+
remote: config.comments?.remote ?? "origin",
|
|
22873
|
+
screens: parseConfig(BudScreensSchema, config.screens),
|
|
22874
|
+
storage: parseConfig(BudStorageSchema, config.storage),
|
|
22875
|
+
designs: parseConfig(BudDesignsSchema, config.designs),
|
|
22876
|
+
previews: parseConfig(BudPreviewsSchema, config.previews),
|
|
22877
|
+
files: parseConfig(BudFilesSchema, config.files),
|
|
22878
|
+
logo: parseConfig(BudLogoSchema, config.logo),
|
|
22879
|
+
theme: parseConfig(BudThemeSchema, config.theme),
|
|
22880
|
+
locale: config.locale === void 0 ? "en" : config.locale,
|
|
22881
|
+
picker: parseConfig(BudPickerSchema, config.picker),
|
|
22882
|
+
branches: parseConfig(BudBranchesSchema, config.branches)
|
|
22883
|
+
}));
|
|
22884
|
+
/**
|
|
22885
|
+
* Validates executable project config before starting any listeners.
|
|
22886
|
+
* @param value - The config module's default export.
|
|
22887
|
+
* @returns Normalized route IDs, ports, and URLs.
|
|
22888
|
+
*/
|
|
22889
|
+
function resolveBudConfig(value) {
|
|
22890
|
+
return parseConfig(BudConfigSchema, value);
|
|
22891
|
+
}
|
|
22892
|
+
/**
|
|
22893
|
+
* Returns a bare HTTP(S) origin, rejecting credentials, paths, queries and fragments.
|
|
22894
|
+
* @param origin - The URL to check.
|
|
22895
|
+
* @param message - The error's message when it is not a bare origin.
|
|
22896
|
+
* @returns The URL's origin.
|
|
22897
|
+
*/
|
|
22898
|
+
function normalizeOrigin(origin, message) {
|
|
22899
|
+
let url = new URL(origin);
|
|
22900
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.pathname !== "/" || url.search || url.hash) throw new Error(message);
|
|
22901
|
+
return url.origin;
|
|
22902
|
+
}
|
|
22903
|
+
//#endregion
|
|
22904
|
+
//#region ../beads/src/lib/contracts.ts
|
|
22905
|
+
var MAX_BEADS = 1e3;
|
|
22906
|
+
//#endregion
|
|
22907
|
+
//#region ../beads/src/lib/validation.ts
|
|
22908
|
+
/** Beads returned or was sent data of the wrong shape; `field` names where. */
|
|
22909
|
+
var BeadsValidationError = class extends Error {
|
|
22910
|
+
field;
|
|
22911
|
+
constructor(field) {
|
|
22912
|
+
super(`Invalid Beads data at ${field}.`);
|
|
22913
|
+
this.name = "BeadsValidationError";
|
|
22914
|
+
this.field = field;
|
|
22915
|
+
}
|
|
22916
|
+
};
|
|
22917
|
+
/**
|
|
22918
|
+
* Checks that a value is a JSON object.
|
|
22919
|
+
* @param value - Untrusted data.
|
|
22920
|
+
* @param field - Where the value sits, for the error.
|
|
22921
|
+
* @returns The object.
|
|
22922
|
+
*/
|
|
22923
|
+
function object$1(value, field) {
|
|
22924
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new BeadsValidationError(field);
|
|
22925
|
+
return value;
|
|
22926
|
+
}
|
|
22927
|
+
/**
|
|
22928
|
+
* Checks a string without NUL characters within a length limit.
|
|
22929
|
+
* @param value - Untrusted data.
|
|
22930
|
+
* @param field - Where the value sits, for the error.
|
|
22931
|
+
* @param max - The most characters it may have.
|
|
22932
|
+
* @returns The string.
|
|
22933
|
+
*/
|
|
22934
|
+
function text$3(value, field, max = 262144) {
|
|
22935
|
+
if (typeof value !== "string" || value.length > max || value.includes("\0")) throw new BeadsValidationError(field);
|
|
22936
|
+
return value;
|
|
22937
|
+
}
|
|
22938
|
+
/**
|
|
22939
|
+
* Checks an ID, name or commit made of letters, digits, dots, underscores and hyphens.
|
|
22940
|
+
* @param value - Untrusted data.
|
|
22941
|
+
* @param field - Where the value sits, for the error.
|
|
22942
|
+
* @returns The identifier.
|
|
22943
|
+
*/
|
|
22944
|
+
function identifier$1(value, field) {
|
|
22945
|
+
let result = text$3(value, field, 512);
|
|
22946
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(result)) throw new BeadsValidationError(field);
|
|
22947
|
+
return result;
|
|
22948
|
+
}
|
|
22949
|
+
/**
|
|
22950
|
+
* Checks an optional string.
|
|
22951
|
+
* @param value - Untrusted data.
|
|
22952
|
+
* @param field - Where the value sits, for the error.
|
|
22953
|
+
* @param max - The most characters it may have.
|
|
22954
|
+
* @returns The string, or null when it is missing.
|
|
22955
|
+
*/
|
|
22956
|
+
function nullableText(value, field, max) {
|
|
22957
|
+
return value == null ? null : text$3(value, field, max);
|
|
22958
|
+
}
|
|
22959
|
+
/**
|
|
22960
|
+
* Checks a nonnegative safe integer.
|
|
22961
|
+
* @param value - Untrusted data.
|
|
22962
|
+
* @param field - Where the value sits, for the error.
|
|
22963
|
+
* @param max - The largest value it may have.
|
|
22964
|
+
* @returns The integer.
|
|
22965
|
+
*/
|
|
22966
|
+
function integer(value, field, max = Number.MAX_SAFE_INTEGER) {
|
|
22967
|
+
if (!Number.isSafeInteger(value) || value < 0 || value > max) throw new BeadsValidationError(field);
|
|
22968
|
+
return value;
|
|
22969
|
+
}
|
|
22970
|
+
/**
|
|
22971
|
+
* Checks an optional nonnegative integer.
|
|
22972
|
+
* @param value - Untrusted data.
|
|
22973
|
+
* @param field - Where the value sits, for the error.
|
|
22974
|
+
* @param max - The largest value it may have.
|
|
22975
|
+
* @returns The integer, or null when it is missing.
|
|
22976
|
+
*/
|
|
22977
|
+
function nullableInteger(value, field, max) {
|
|
22978
|
+
return value == null ? null : integer(value, field, max);
|
|
22979
|
+
}
|
|
22980
|
+
/**
|
|
22981
|
+
* Checks a boolean.
|
|
22982
|
+
* @param value - Untrusted data.
|
|
22983
|
+
* @param field - Where the value sits, for the error.
|
|
22984
|
+
* @returns The boolean.
|
|
22985
|
+
*/
|
|
22986
|
+
function boolean$2(value, field) {
|
|
22987
|
+
if (typeof value !== "boolean") throw new BeadsValidationError(field);
|
|
22988
|
+
return value;
|
|
22989
|
+
}
|
|
22990
|
+
/**
|
|
22991
|
+
* Checks an ISO 8601 timestamp with a time zone.
|
|
22992
|
+
* @param value - Untrusted data.
|
|
22993
|
+
* @param field - Where the value sits, for the error.
|
|
22994
|
+
* @returns The timestamp as given.
|
|
22995
|
+
*/
|
|
22996
|
+
function timestamp(value, field) {
|
|
22997
|
+
let result = text$3(value, field, 64);
|
|
22998
|
+
if (!/^\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d(?:\.\d+)?(?:Z|[+-]\d\d:\d\d)$/.test(result) || !Number.isFinite(Date.parse(result))) throw new BeadsValidationError(field);
|
|
22999
|
+
return result;
|
|
23000
|
+
}
|
|
23001
|
+
/**
|
|
23002
|
+
* Checks an optional ISO 8601 timestamp.
|
|
23003
|
+
* @param value - Untrusted data.
|
|
23004
|
+
* @param field - Where the value sits, for the error.
|
|
23005
|
+
* @returns The timestamp, or null when it is missing.
|
|
23006
|
+
*/
|
|
23007
|
+
function nullableTimestamp(value, field) {
|
|
23008
|
+
return value == null ? null : timestamp(value, field);
|
|
23009
|
+
}
|
|
23010
|
+
/**
|
|
23011
|
+
* Checks a bounded array and each of its entries.
|
|
23012
|
+
* @param value - Untrusted data.
|
|
23013
|
+
* @param field - Where the value sits, for the error.
|
|
23014
|
+
* @param parse - Checks one entry.
|
|
23015
|
+
* @param max - The most entries it may have.
|
|
23016
|
+
* @returns The parsed entries.
|
|
23017
|
+
*/
|
|
23018
|
+
function array$1(value, field, parse, max = 1e4) {
|
|
23019
|
+
if (!Array.isArray(value) || value.length > max) throw new BeadsValidationError(field);
|
|
23020
|
+
return value.map((entry, i) => parse(entry, `${field}[${i}]`));
|
|
23021
|
+
}
|
|
23022
|
+
/**
|
|
23023
|
+
* Checks that no two values share a key.
|
|
23024
|
+
* @param values - The values to check.
|
|
23025
|
+
* @param key - Gives each value's key.
|
|
23026
|
+
* @param field - Where the values sit, for the error.
|
|
23027
|
+
* @returns The values.
|
|
23028
|
+
*/
|
|
23029
|
+
function unique(values, key, field) {
|
|
23030
|
+
if (new Set(values.map(key)).size !== values.length) throw new BeadsValidationError(field);
|
|
23031
|
+
return values;
|
|
23032
|
+
}
|
|
23033
|
+
/**
|
|
23034
|
+
* Checks JSON metadata, bounding its depth and its total number of values.
|
|
23035
|
+
* @param value - Untrusted data.
|
|
23036
|
+
* @param field - Where the value sits, for the error.
|
|
23037
|
+
* @param depth - How deep this value is nested.
|
|
23038
|
+
* @param budget - The values left to check, shared across the whole tree.
|
|
23039
|
+
* @returns The metadata.
|
|
23040
|
+
*/
|
|
23041
|
+
function metadata(value, field, depth = 0, budget = { remaining: 1e4 }) {
|
|
23042
|
+
if (depth > 12 || --budget.remaining < 0) throw new BeadsValidationError(field);
|
|
23043
|
+
if (value === null || typeof value === "boolean") return value;
|
|
23044
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
23045
|
+
if (typeof value === "string") return text$3(value, field);
|
|
23046
|
+
if (Array.isArray(value)) return value.map((entry, index) => metadata(entry, `${field}[${index}]`, depth + 1, budget));
|
|
23047
|
+
let input = object$1(value, field);
|
|
23048
|
+
return Object.fromEntries(Object.entries(input).map(([key, entry]) => [text$3(key, field, 512), metadata(entry, `${field}.${key}`, depth + 1, budget)]));
|
|
23049
|
+
}
|
|
23050
|
+
function dependency(value, field) {
|
|
23051
|
+
let input = object$1(value, field);
|
|
23052
|
+
return {
|
|
23053
|
+
id: identifier$1(input.id, `${field}.id`),
|
|
23054
|
+
type: identifier$1(input.type, `${field}.type`),
|
|
23055
|
+
title: nullableText(input.title, `${field}.title`),
|
|
23056
|
+
status: nullableText(input.status, `${field}.status`, 128),
|
|
23057
|
+
priority: nullableInteger(input.priority, `${field}.priority`, 4),
|
|
23058
|
+
issueType: nullableText(input.issueType, `${field}.issueType`, 128)
|
|
23059
|
+
};
|
|
23060
|
+
}
|
|
23061
|
+
/**
|
|
23062
|
+
* Validates an issue.
|
|
23063
|
+
* @param value - Untrusted data.
|
|
23064
|
+
* @param field - Where the value sits, for the error.
|
|
23065
|
+
* @returns The issue.
|
|
23066
|
+
*/
|
|
23067
|
+
function parseBeadIssue(value, field = "issue") {
|
|
23068
|
+
let input = object$1(value, field);
|
|
23069
|
+
let string = (key, max) => text$3(input[key], `${field}.${key}`, max);
|
|
23070
|
+
let optional = (key, max) => nullableText(input[key], `${field}.${key}`, max);
|
|
23071
|
+
let count = (key) => nullableInteger(input[key], `${field}.${key}`);
|
|
23072
|
+
let date = (key) => nullableTimestamp(input[key], `${field}.${key}`);
|
|
23073
|
+
return {
|
|
23074
|
+
id: identifier$1(input.id, `${field}.id`),
|
|
23075
|
+
title: string("title"),
|
|
23076
|
+
status: identifier$1(input.status, `${field}.status`),
|
|
23077
|
+
priority: integer(input.priority, `${field}.priority`, 4),
|
|
23078
|
+
type: identifier$1(input.type, `${field}.type`),
|
|
23079
|
+
description: string("description"),
|
|
23080
|
+
design: string("design"),
|
|
23081
|
+
acceptanceCriteria: string("acceptanceCriteria"),
|
|
23082
|
+
notes: string("notes"),
|
|
23083
|
+
assignee: optional("assignee"),
|
|
23084
|
+
owner: optional("owner"),
|
|
23085
|
+
createdBy: optional("createdBy"),
|
|
23086
|
+
createdAt: timestamp(input.createdAt, `${field}.createdAt`),
|
|
23087
|
+
updatedAt: timestamp(input.updatedAt, `${field}.updatedAt`),
|
|
23088
|
+
startedAt: date("startedAt"),
|
|
23089
|
+
closedAt: date("closedAt"),
|
|
23090
|
+
closeReason: optional("closeReason"),
|
|
23091
|
+
dueAt: date("dueAt"),
|
|
23092
|
+
deferUntil: date("deferUntil"),
|
|
23093
|
+
estimatedMinutes: count("estimatedMinutes"),
|
|
23094
|
+
externalRef: optional("externalRef"),
|
|
23095
|
+
specId: optional("specId"),
|
|
23096
|
+
labels: unique(array$1(input.labels, `${field}.labels`, (item, path) => text$3(item, path, 512), 1e3), (item) => item, `${field}.labels`),
|
|
23097
|
+
parentId: input.parentId == null ? null : identifier$1(input.parentId, `${field}.parentId`),
|
|
23098
|
+
dependencies: array$1(input.dependencies, `${field}.dependencies`, dependency),
|
|
23099
|
+
dependents: array$1(input.dependents, `${field}.dependents`, dependency),
|
|
23100
|
+
dependencyCount: count("dependencyCount"),
|
|
23101
|
+
dependentCount: count("dependentCount"),
|
|
23102
|
+
commentCount: count("commentCount"),
|
|
23103
|
+
epicTotalChildren: count("epicTotalChildren"),
|
|
23104
|
+
epicClosedChildren: count("epicClosedChildren"),
|
|
23105
|
+
epicCloseable: input.epicCloseable == null ? null : boolean$2(input.epicCloseable, `${field}.epicCloseable`),
|
|
23106
|
+
revision: optional("revision", 256),
|
|
23107
|
+
metadata: metadata(input.metadata, `${field}.metadata`),
|
|
23108
|
+
contentLoaded: boolean$2(input.contentLoaded, `${field}.contentLoaded`)
|
|
23109
|
+
};
|
|
23110
|
+
}
|
|
23111
|
+
/**
|
|
23112
|
+
* Validates a connection state.
|
|
23113
|
+
* @param value - Untrusted data.
|
|
23114
|
+
* @returns The connection.
|
|
23115
|
+
*/
|
|
23116
|
+
function parseBeadsConnection(value) {
|
|
23117
|
+
let input = object$1(value, "connection");
|
|
23118
|
+
if (input.state === "ready") {
|
|
23119
|
+
let project = object$1(input.project, "connection.project");
|
|
23120
|
+
if (input.schemaVersion !== 1) throw new BeadsValidationError("connection.schemaVersion");
|
|
23121
|
+
return {
|
|
23122
|
+
state: "ready",
|
|
23123
|
+
version: text$3(input.version, "connection.version", 128),
|
|
23124
|
+
schemaVersion: 1,
|
|
23125
|
+
project: {
|
|
23126
|
+
name: text$3(project.name, "project.name", 512),
|
|
23127
|
+
prefix: nullableText(project.prefix, "project.prefix", 512),
|
|
23128
|
+
backend: text$3(project.backend, "project.backend", 128),
|
|
23129
|
+
mode: nullableText(project.mode, "project.mode", 128),
|
|
23130
|
+
redirected: boolean$2(project.redirected, "project.redirected"),
|
|
23131
|
+
worktree: boolean$2(project.worktree, "project.worktree")
|
|
23132
|
+
}
|
|
23133
|
+
};
|
|
23134
|
+
}
|
|
23135
|
+
if (![
|
|
23136
|
+
"unavailable",
|
|
23137
|
+
"not-initialized",
|
|
23138
|
+
"unsupported-version"
|
|
23139
|
+
].includes(input.state)) throw new BeadsValidationError("connection.state");
|
|
23140
|
+
return {
|
|
23141
|
+
state: input.state,
|
|
23142
|
+
version: nullableText(input.version, "connection.version", 128),
|
|
23143
|
+
message: text$3(input.message, "connection.message", 1e3)
|
|
23144
|
+
};
|
|
23145
|
+
}
|
|
23146
|
+
/**
|
|
23147
|
+
* Validates a status definition.
|
|
23148
|
+
* @param value - Untrusted data.
|
|
23149
|
+
* @param field - Where the value sits, for the error.
|
|
23150
|
+
* @returns The status.
|
|
23151
|
+
*/
|
|
23152
|
+
function parseBeadStatus(value, field = "status") {
|
|
23153
|
+
let input = object$1(value, field);
|
|
23154
|
+
return {
|
|
23155
|
+
name: identifier$1(input.name, `${field}.name`),
|
|
23156
|
+
category: nullableText(input.category, `${field}.category`, 128),
|
|
23157
|
+
description: text$3(input.description, `${field}.description`),
|
|
23158
|
+
custom: boolean$2(input.custom, `${field}.custom`)
|
|
23159
|
+
};
|
|
23160
|
+
}
|
|
23161
|
+
/**
|
|
23162
|
+
* Validates an issue type definition.
|
|
23163
|
+
* @param value - Untrusted data.
|
|
23164
|
+
* @param field - Where the value sits, for the error.
|
|
23165
|
+
* @returns The type.
|
|
23166
|
+
*/
|
|
23167
|
+
function parseBeadType(value, field = "type") {
|
|
23168
|
+
let input = object$1(value, field);
|
|
23169
|
+
if (![
|
|
23170
|
+
"core",
|
|
23171
|
+
"system",
|
|
23172
|
+
"custom"
|
|
23173
|
+
].includes(input.kind)) throw new BeadsValidationError(`${field}.kind`);
|
|
23174
|
+
return {
|
|
23175
|
+
name: identifier$1(input.name, `${field}.name`),
|
|
23176
|
+
description: text$3(input.description, `${field}.description`),
|
|
23177
|
+
kind: input.kind
|
|
23178
|
+
};
|
|
23179
|
+
}
|
|
23180
|
+
/**
|
|
23181
|
+
* Validates a board snapshot, which holds no rows unless its connection is ready.
|
|
23182
|
+
* @param value - Untrusted data.
|
|
23183
|
+
* @returns The snapshot.
|
|
23184
|
+
*/
|
|
23185
|
+
function parseBeadsSnapshot(value) {
|
|
23186
|
+
let input = object$1(value, "snapshot");
|
|
23187
|
+
let connection = parseBeadsConnection(input.connection);
|
|
23188
|
+
let issues = unique(array$1(input.issues, "snapshot.issues", parseBeadIssue, MAX_BEADS), (issue) => issue.id, "snapshot.issues");
|
|
23189
|
+
let readyIds = unique(array$1(input.readyIds, "snapshot.readyIds", identifier$1, MAX_BEADS), (id) => id, "snapshot.readyIds");
|
|
23190
|
+
let statuses = unique(array$1(input.statuses, "snapshot.statuses", parseBeadStatus, 1e3), (item) => item.name, "snapshot.statuses");
|
|
23191
|
+
let types = unique(array$1(input.types, "snapshot.types", parseBeadType, 1e3), (item) => item.name, "snapshot.types");
|
|
23192
|
+
let truncated = boolean$2(input.truncated, "snapshot.truncated");
|
|
23193
|
+
if (connection.state !== "ready" && (issues.length || readyIds.length || statuses.length || types.length || truncated)) throw new BeadsValidationError("snapshot.connection");
|
|
23194
|
+
return {
|
|
23195
|
+
connection,
|
|
23196
|
+
issues,
|
|
23197
|
+
readyIds,
|
|
23198
|
+
statuses,
|
|
23199
|
+
types,
|
|
23200
|
+
truncated
|
|
23201
|
+
};
|
|
23202
|
+
}
|
|
23203
|
+
/**
|
|
23204
|
+
* Validates an issue comment.
|
|
23205
|
+
* @param value - Untrusted data.
|
|
23206
|
+
* @param field - Where the value sits, for the error.
|
|
23207
|
+
* @returns The comment.
|
|
23208
|
+
*/
|
|
23209
|
+
function parseBeadDiscussion(value, field = "discussion") {
|
|
23210
|
+
let input = object$1(value, field);
|
|
23211
|
+
return {
|
|
23212
|
+
id: identifier$1(input.id, `${field}.id`),
|
|
23213
|
+
issueId: identifier$1(input.issueId, `${field}.issueId`),
|
|
23214
|
+
author: text$3(input.author, `${field}.author`),
|
|
23215
|
+
body: text$3(input.body, `${field}.body`),
|
|
23216
|
+
createdAt: timestamp(input.createdAt, `${field}.createdAt`)
|
|
23217
|
+
};
|
|
23218
|
+
}
|
|
23219
|
+
/**
|
|
23220
|
+
* Validates an issue's version at one commit.
|
|
23221
|
+
* @param value - Untrusted data.
|
|
23222
|
+
* @param field - Where the value sits, for the error.
|
|
23223
|
+
* @returns The version.
|
|
23224
|
+
*/
|
|
23225
|
+
function parseBeadHistory(value, field = "history") {
|
|
23226
|
+
let input = object$1(value, field);
|
|
23227
|
+
return {
|
|
23228
|
+
commit: identifier$1(input.commit, `${field}.commit`),
|
|
23229
|
+
author: text$3(input.author, `${field}.author`),
|
|
23230
|
+
createdAt: timestamp(input.createdAt, `${field}.createdAt`),
|
|
23231
|
+
issue: input.issue == null ? null : parseBeadIssue(input.issue, `${field}.issue`)
|
|
23232
|
+
};
|
|
23233
|
+
}
|
|
23234
|
+
/**
|
|
23235
|
+
* Validates an event log entry.
|
|
23236
|
+
* @param value - Untrusted data.
|
|
23237
|
+
* @param field - Where the value sits, for the error.
|
|
23238
|
+
* @returns The event.
|
|
23239
|
+
*/
|
|
23240
|
+
function parseBeadEvent(value, field = "event") {
|
|
23241
|
+
let input = object$1(value, field);
|
|
23242
|
+
return {
|
|
23243
|
+
id: identifier$1(input.id, `${field}.id`),
|
|
23244
|
+
issueId: identifier$1(input.issueId, `${field}.issueId`),
|
|
23245
|
+
type: identifier$1(input.type, `${field}.type`),
|
|
23246
|
+
actor: text$3(input.actor, `${field}.actor`),
|
|
23247
|
+
oldValue: nullableText(input.oldValue, `${field}.oldValue`),
|
|
23248
|
+
newValue: nullableText(input.newValue, `${field}.newValue`),
|
|
23249
|
+
comment: nullableText(input.comment, `${field}.comment`),
|
|
23250
|
+
createdAt: timestamp(input.createdAt, `${field}.createdAt`)
|
|
23251
|
+
};
|
|
23252
|
+
}
|
|
23253
|
+
function section$2(value, field, parser, max) {
|
|
23254
|
+
let input = object$1(value, field);
|
|
23255
|
+
let items = array$1(input.items, `${field}.items`, parser, max);
|
|
23256
|
+
let truncated = boolean$2(input.truncated, `${field}.truncated`);
|
|
23257
|
+
if (input.state === "available") return {
|
|
23258
|
+
state: "available",
|
|
23259
|
+
items,
|
|
23260
|
+
truncated
|
|
23261
|
+
};
|
|
23262
|
+
if (input.state === "unsupported" && items.length === 0 && truncated === false) return {
|
|
23263
|
+
state: "unsupported",
|
|
23264
|
+
items: [],
|
|
23265
|
+
truncated: false
|
|
23266
|
+
};
|
|
23267
|
+
throw new BeadsValidationError(`${field}.state`);
|
|
23268
|
+
}
|
|
23269
|
+
/**
|
|
23270
|
+
* Validates an issue's detail, whose sections must all belong to that issue.
|
|
23271
|
+
* @param value - Untrusted data.
|
|
23272
|
+
* @returns The detail.
|
|
23273
|
+
*/
|
|
23274
|
+
function parseBeadDetail(value) {
|
|
23275
|
+
let input = object$1(value, "detail");
|
|
23276
|
+
let connection = parseBeadsConnection(input.connection);
|
|
23277
|
+
if (connection.state !== "ready") throw new BeadsValidationError("detail.connection");
|
|
23278
|
+
let issue = parseBeadIssue(input.issue);
|
|
23279
|
+
let discussion = section$2(input.discussion, "discussion", parseBeadDiscussion, 200);
|
|
23280
|
+
let history = section$2(input.history, "history", parseBeadHistory, 50);
|
|
23281
|
+
let events = section$2(input.events, "events", parseBeadEvent, 50);
|
|
23282
|
+
if (!issue.contentLoaded || discussion.items.some((entry) => entry.issueId !== issue.id) || events.items.some((entry) => entry.issueId !== issue.id) || history.items.some((entry) => entry.issue && entry.issue.id !== issue.id)) throw new BeadsValidationError("detail.issue");
|
|
23283
|
+
return {
|
|
23284
|
+
connection,
|
|
23285
|
+
issue,
|
|
23286
|
+
discussion,
|
|
23287
|
+
history,
|
|
23288
|
+
events
|
|
23289
|
+
};
|
|
23290
|
+
}
|
|
23291
|
+
//#endregion
|
|
23292
|
+
//#region ../beads/src/lib/actions.ts
|
|
23293
|
+
var dependencyTypes = [
|
|
23294
|
+
"blocks",
|
|
23295
|
+
"tracks",
|
|
23296
|
+
"related",
|
|
23297
|
+
"discovered-from",
|
|
23298
|
+
"until",
|
|
23299
|
+
"caused-by",
|
|
23300
|
+
"validates",
|
|
23301
|
+
"relates-to",
|
|
23302
|
+
"supersedes"
|
|
23303
|
+
];
|
|
23304
|
+
var fields$2 = [
|
|
23305
|
+
"title",
|
|
23306
|
+
"type",
|
|
23307
|
+
"status",
|
|
23308
|
+
"priority",
|
|
23309
|
+
"assignee",
|
|
23310
|
+
"labels",
|
|
23311
|
+
"parentId",
|
|
23312
|
+
"description",
|
|
23313
|
+
"design",
|
|
23314
|
+
"acceptanceCriteria",
|
|
23315
|
+
"notes",
|
|
23316
|
+
"estimatedMinutes",
|
|
23317
|
+
"externalRef",
|
|
23318
|
+
"specId",
|
|
23319
|
+
"dueAt",
|
|
23320
|
+
"deferUntil",
|
|
23321
|
+
"metadata"
|
|
23322
|
+
];
|
|
23323
|
+
function strict$1(value, name, allowed) {
|
|
23324
|
+
let input = object$1(value, name);
|
|
23325
|
+
for (let key of Object.keys(input)) if (!allowed.includes(key)) throw new BeadsValidationError(`${name}.${key}`);
|
|
23326
|
+
return input;
|
|
23327
|
+
}
|
|
23328
|
+
function nonempty(value, field, max) {
|
|
23329
|
+
let result = text$3(value, field, max).trim();
|
|
23330
|
+
if (!result) throw new BeadsValidationError(field);
|
|
23331
|
+
return result;
|
|
23332
|
+
}
|
|
23333
|
+
/**
|
|
23334
|
+
* Validates an update's fields, rejecting unknown and oversized ones.
|
|
23335
|
+
* @param value - Untrusted data.
|
|
23336
|
+
* @returns The patch.
|
|
23337
|
+
*/
|
|
23338
|
+
function parseBeadPatch(value) {
|
|
23339
|
+
let input = strict$1(value, "patch", fields$2);
|
|
23340
|
+
let patch = {};
|
|
23341
|
+
if (!Object.keys(input).length) throw new BeadsValidationError("patch");
|
|
23342
|
+
if ("title" in input) patch.title = nonempty(input.title, "patch.title", 1e3);
|
|
23343
|
+
if ("type" in input) patch.type = identifier$1(input.type, "patch.type");
|
|
23344
|
+
if ("status" in input) patch.status = identifier$1(input.status, "patch.status");
|
|
23345
|
+
if ("priority" in input) patch.priority = integer(input.priority, "patch.priority", 4);
|
|
23346
|
+
if ("assignee" in input) patch.assignee = text$3(input.assignee, "patch.assignee", 256).trim();
|
|
23347
|
+
if ("labels" in input) patch.labels = unique(array$1(input.labels, "patch.labels", (value, field) => {
|
|
23348
|
+
let label = nonempty(value, field, 128);
|
|
23349
|
+
if (/[\r\n,]/.test(label)) throw new BeadsValidationError(field);
|
|
23350
|
+
return label;
|
|
23351
|
+
}, 100), (label) => label, "patch.labels");
|
|
23352
|
+
if ("parentId" in input) patch.parentId = input.parentId === null ? null : identifier$1(input.parentId, "patch.parentId");
|
|
23353
|
+
for (let key of [
|
|
23354
|
+
"description",
|
|
23355
|
+
"design",
|
|
23356
|
+
"acceptanceCriteria",
|
|
23357
|
+
"notes"
|
|
23358
|
+
]) if (key in input) patch[key] = text$3(input[key], `patch.${key}`, 6e4);
|
|
23359
|
+
if ("estimatedMinutes" in input) patch.estimatedMinutes = integer(input.estimatedMinutes, "patch.estimatedMinutes", 1e6);
|
|
23360
|
+
for (let key of ["externalRef", "specId"]) if (key in input) patch[key] = text$3(input[key], `patch.${key}`, 2048);
|
|
23361
|
+
for (let key of ["dueAt", "deferUntil"]) if (key in input) patch[key] = text$3(input[key], `patch.${key}`, 128);
|
|
23362
|
+
if ("metadata" in input) {
|
|
23363
|
+
let source = object$1(input.metadata, "patch.metadata");
|
|
23364
|
+
if (JSON.stringify(source).length > 32e3) throw new BeadsValidationError("patch.metadata");
|
|
23365
|
+
patch.metadata = metadata(source, "patch.metadata");
|
|
23366
|
+
}
|
|
23367
|
+
if (new TextEncoder().encode(JSON.stringify(patch)).byteLength > 122880) throw new BeadsValidationError("patch");
|
|
23368
|
+
return patch;
|
|
23369
|
+
}
|
|
23370
|
+
/**
|
|
23371
|
+
* Validates a new issue's fields.
|
|
23372
|
+
* @param value - Untrusted data.
|
|
23373
|
+
* @returns The fields, with a title.
|
|
23374
|
+
*/
|
|
23375
|
+
function parseBeadCreate(value) {
|
|
23376
|
+
let patch = parseBeadPatch(value);
|
|
23377
|
+
if (!patch.title) throw new BeadsValidationError("create.title");
|
|
23378
|
+
return {
|
|
23379
|
+
...patch,
|
|
23380
|
+
title: patch.title
|
|
23381
|
+
};
|
|
23382
|
+
}
|
|
23383
|
+
/**
|
|
23384
|
+
* Validates a change to an issue.
|
|
23385
|
+
* @param value - Untrusted data.
|
|
23386
|
+
* @returns The action.
|
|
23387
|
+
*/
|
|
23388
|
+
function parseBeadAction(value) {
|
|
23389
|
+
let input = object$1(value, "action");
|
|
23390
|
+
let expectedRevision = nonempty(input.expectedRevision, "action.expectedRevision", 256);
|
|
23391
|
+
switch (input.kind) {
|
|
23392
|
+
case "update":
|
|
23393
|
+
strict$1(input, "action", [
|
|
23394
|
+
"kind",
|
|
23395
|
+
"expectedRevision",
|
|
23396
|
+
"patch"
|
|
23397
|
+
]);
|
|
23398
|
+
return {
|
|
23399
|
+
kind: input.kind,
|
|
23400
|
+
expectedRevision,
|
|
23401
|
+
patch: parseBeadPatch(input.patch)
|
|
23402
|
+
};
|
|
23403
|
+
case "claim":
|
|
23404
|
+
strict$1(input, "action", ["kind", "expectedRevision"]);
|
|
23405
|
+
return {
|
|
23406
|
+
kind: input.kind,
|
|
23407
|
+
expectedRevision
|
|
23408
|
+
};
|
|
23409
|
+
case "close":
|
|
23410
|
+
case "reopen":
|
|
23411
|
+
strict$1(input, "action", [
|
|
23412
|
+
"kind",
|
|
23413
|
+
"expectedRevision",
|
|
23414
|
+
"reason"
|
|
23415
|
+
]);
|
|
23416
|
+
return {
|
|
23417
|
+
kind: input.kind,
|
|
23418
|
+
expectedRevision,
|
|
23419
|
+
reason: text$3(input.reason ?? "", "action.reason", 4e3)
|
|
23420
|
+
};
|
|
23421
|
+
case "add-dependency":
|
|
23422
|
+
strict$1(input, "action", [
|
|
23423
|
+
"kind",
|
|
23424
|
+
"expectedRevision",
|
|
23425
|
+
"targetId",
|
|
23426
|
+
"type"
|
|
23427
|
+
]);
|
|
23428
|
+
if (!dependencyTypes.includes(input.type)) throw new BeadsValidationError("action.type");
|
|
23429
|
+
return {
|
|
23430
|
+
kind: input.kind,
|
|
23431
|
+
expectedRevision,
|
|
23432
|
+
targetId: identifier$1(input.targetId, "action.targetId"),
|
|
23433
|
+
type: input.type
|
|
23434
|
+
};
|
|
23435
|
+
case "remove-dependency":
|
|
23436
|
+
strict$1(input, "action", [
|
|
23437
|
+
"kind",
|
|
23438
|
+
"expectedRevision",
|
|
23439
|
+
"targetId"
|
|
23440
|
+
]);
|
|
23441
|
+
return {
|
|
23442
|
+
kind: input.kind,
|
|
23443
|
+
expectedRevision,
|
|
23444
|
+
targetId: identifier$1(input.targetId, "action.targetId")
|
|
23445
|
+
};
|
|
23446
|
+
case "comment": {
|
|
23447
|
+
strict$1(input, "action", [
|
|
23448
|
+
"kind",
|
|
23449
|
+
"expectedRevision",
|
|
23450
|
+
"body"
|
|
23451
|
+
]);
|
|
23452
|
+
let body = nonempty(input.body, "action.body", 6e4);
|
|
23453
|
+
if (new TextEncoder().encode(body).byteLength > 122880) throw new BeadsValidationError("action.body");
|
|
23454
|
+
return {
|
|
23455
|
+
kind: input.kind,
|
|
23456
|
+
expectedRevision,
|
|
23457
|
+
body
|
|
23458
|
+
};
|
|
23459
|
+
}
|
|
23460
|
+
default: throw new BeadsValidationError("action.kind");
|
|
23461
|
+
}
|
|
23462
|
+
}
|
|
23463
|
+
/**
|
|
23464
|
+
* Validates a sync request.
|
|
23465
|
+
* @param value - Untrusted data.
|
|
23466
|
+
* @returns The request.
|
|
23467
|
+
*/
|
|
23468
|
+
function parseBeadSync(value) {
|
|
23469
|
+
let input = strict$1(value, "sync", ["direction"]);
|
|
23470
|
+
if (input.direction !== "pull" && input.direction !== "push") throw new BeadsValidationError("sync.direction");
|
|
23471
|
+
return { direction: input.direction };
|
|
23472
|
+
}
|
|
23473
|
+
//#endregion
|
|
23474
|
+
//#region ../beads/src/lib/protocol.ts
|
|
23475
|
+
/**
|
|
23476
|
+
* Turns a CLI or export issue record into the browser's issue. CLI and export records share this
|
|
23477
|
+
* one boundary before reaching the browser DTOs.
|
|
23478
|
+
* @param value - The record.
|
|
23479
|
+
* @param contentLoaded - Whether it holds the issue's long content.
|
|
23480
|
+
* @returns The issue.
|
|
23481
|
+
*/
|
|
23482
|
+
function parseCliIssue(value, contentLoaded = false) {
|
|
23483
|
+
let input = object$1(value, "issue");
|
|
23484
|
+
let id = identifier$1(input.id, "issue.id");
|
|
23485
|
+
function dependencies(field) {
|
|
23486
|
+
return array$1(input[field] ?? [], `issue.${field}`, (entry, path) => {
|
|
23487
|
+
let edge = object$1(entry, path);
|
|
23488
|
+
if ("depends_on_id" in edge) {
|
|
23489
|
+
if (identifier$1(edge.issue_id, `${path}.issue_id`) !== id) throw new Error("Dependency belongs to a different issue.");
|
|
23490
|
+
return {
|
|
23491
|
+
id: identifier$1(edge.depends_on_id, `${path}.depends_on_id`),
|
|
23492
|
+
type: identifier$1(edge.type, `${path}.type`),
|
|
23493
|
+
title: null,
|
|
23494
|
+
status: null,
|
|
23495
|
+
priority: null,
|
|
23496
|
+
issueType: null
|
|
23497
|
+
};
|
|
23498
|
+
}
|
|
23499
|
+
return {
|
|
23500
|
+
id: edge.id,
|
|
23501
|
+
type: edge.dependency_type,
|
|
23502
|
+
title: edge.title ?? null,
|
|
23503
|
+
status: edge.status ?? null,
|
|
23504
|
+
priority: edge.priority ?? null,
|
|
23505
|
+
issueType: edge.issue_type ?? null
|
|
23506
|
+
};
|
|
23507
|
+
});
|
|
23508
|
+
}
|
|
23509
|
+
return parseBeadIssue({
|
|
23510
|
+
id,
|
|
23511
|
+
title: input.title,
|
|
23512
|
+
status: input.status,
|
|
23513
|
+
priority: input.priority,
|
|
23514
|
+
type: input.issue_type,
|
|
23515
|
+
description: input.description ?? "",
|
|
23516
|
+
design: input.design ?? "",
|
|
23517
|
+
acceptanceCriteria: input.acceptance_criteria ?? "",
|
|
23518
|
+
notes: input.notes ?? "",
|
|
23519
|
+
assignee: input.assignee ?? null,
|
|
23520
|
+
owner: input.owner ?? null,
|
|
23521
|
+
createdBy: input.created_by ?? null,
|
|
23522
|
+
createdAt: input.created_at,
|
|
23523
|
+
updatedAt: input.updated_at,
|
|
23524
|
+
startedAt: input.started_at ?? null,
|
|
23525
|
+
closedAt: input.closed_at ?? null,
|
|
23526
|
+
closeReason: input.close_reason ?? null,
|
|
23527
|
+
dueAt: input.due_at ?? null,
|
|
23528
|
+
deferUntil: input.defer_until ?? null,
|
|
23529
|
+
estimatedMinutes: input.estimated_minutes ?? null,
|
|
23530
|
+
externalRef: input.external_ref ?? null,
|
|
23531
|
+
specId: input.spec_id ?? null,
|
|
23532
|
+
labels: input.labels ?? [],
|
|
23533
|
+
parentId: input.parent ?? null,
|
|
23534
|
+
dependencies: dependencies("dependencies"),
|
|
23535
|
+
dependents: dependencies("dependents"),
|
|
23536
|
+
dependencyCount: input.dependency_count ?? null,
|
|
23537
|
+
dependentCount: input.dependent_count ?? null,
|
|
23538
|
+
commentCount: input.comment_count ?? null,
|
|
23539
|
+
epicTotalChildren: input.epic_total_children ?? null,
|
|
23540
|
+
epicClosedChildren: input.epic_closed_children ?? null,
|
|
23541
|
+
epicCloseable: input.epic_closeable ?? null,
|
|
23542
|
+
revision: input.revision ?? null,
|
|
23543
|
+
metadata: input.metadata ?? null,
|
|
23544
|
+
contentLoaded
|
|
23545
|
+
});
|
|
23546
|
+
}
|
|
23547
|
+
/**
|
|
23548
|
+
* Turns the CLI's issue comments into discussion entries with unique IDs.
|
|
23549
|
+
* @param value - The CLI's comment list, or nothing.
|
|
23550
|
+
* @returns The comments.
|
|
23551
|
+
*/
|
|
23552
|
+
function parseCliDiscussion(value) {
|
|
23553
|
+
return unique(array$1(value ?? [], "comments", (entry, field) => {
|
|
23554
|
+
let item = object$1(entry, field);
|
|
23555
|
+
return parseBeadDiscussion({
|
|
22804
23556
|
id: item.id,
|
|
22805
23557
|
issueId: item.issue_id,
|
|
22806
23558
|
author: item.author,
|
|
@@ -23280,7 +24032,7 @@ function payload$1(value) {
|
|
|
23280
24032
|
return /^[{[]/.test(value.trim()) ? null : value;
|
|
23281
24033
|
}
|
|
23282
24034
|
}
|
|
23283
|
-
function text$
|
|
24035
|
+
function text$2(value) {
|
|
23284
24036
|
return typeof value === "string" || typeof value === "number" ? String(value).replace(/\s+/g, " ").trim() : "";
|
|
23285
24037
|
}
|
|
23286
24038
|
function field(value, key) {
|
|
@@ -23289,7 +24041,7 @@ function field(value, key) {
|
|
|
23289
24041
|
function changed$1(key, before, after) {
|
|
23290
24042
|
let name = fields[key] ?? label$1(key).toLowerCase();
|
|
23291
24043
|
if (key === "labels") {
|
|
23292
|
-
let labels = (value) => Array.isArray(value) ? value.map(text$
|
|
24044
|
+
let labels = (value) => Array.isArray(value) ? value.map(text$2).filter(Boolean) : text$2(value).split(",").map((v) => v.trim()).filter(Boolean);
|
|
23293
24045
|
let oldLabels = labels(before);
|
|
23294
24046
|
let newLabels = labels(after);
|
|
23295
24047
|
let added = newLabels.filter((value) => !oldLabels.includes(value));
|
|
@@ -23305,7 +24057,7 @@ function changed$1(key, before, after) {
|
|
|
23305
24057
|
"metadata"
|
|
23306
24058
|
].includes(key)) return `updated ${name}`;
|
|
23307
24059
|
let display = (value) => {
|
|
23308
|
-
let result = text$
|
|
24060
|
+
let result = text$2(value);
|
|
23309
24061
|
if (!result) return key === "assignee" ? "Unassigned" : "None";
|
|
23310
24062
|
if (key === "priority" && /^\d$/.test(result)) return `P${result}`;
|
|
23311
24063
|
return key === "status" || key === "issue_type" ? label$1(result) : result;
|
|
@@ -23326,7 +24078,7 @@ function beadActivitySummary(event) {
|
|
|
23326
24078
|
case "created": return "created this task";
|
|
23327
24079
|
case "closed":
|
|
23328
24080
|
case "reopened": {
|
|
23329
|
-
let reason = text$
|
|
24081
|
+
let reason = text$2(payload$1(event.comment)) || text$2(field(after, "close_reason")) || text$2(field(after, "reason"));
|
|
23330
24082
|
if (!reason || [
|
|
23331
24083
|
"closed",
|
|
23332
24084
|
"open",
|
|
@@ -23340,12 +24092,12 @@ function beadActivitySummary(event) {
|
|
|
23340
24092
|
case "label_added":
|
|
23341
24093
|
case "label_removed": {
|
|
23342
24094
|
let verb = event.type === "label_added" ? "added" : "removed";
|
|
23343
|
-
let value = text$
|
|
24095
|
+
let value = text$2(field(after ?? before, "label")) || text$2(payload$1(event.comment)).replace(/^(?:Added|Removed) label:\s*/i, "");
|
|
23344
24096
|
return `${verb} a label${value ? `: ${value}` : ""}`;
|
|
23345
24097
|
}
|
|
23346
24098
|
case "dependency_added":
|
|
23347
24099
|
case "dependency_removed": {
|
|
23348
|
-
let value = text$
|
|
24100
|
+
let value = text$2(after ?? before);
|
|
23349
24101
|
let match = /^(?:Added|Removed) dependency: \S+ (\S+) (\S+)$/.exec(value);
|
|
23350
24102
|
let target = match?.[2] ?? (/^[\w.-]+$/.test(value) ? value : "");
|
|
23351
24103
|
let kind = match?.[1];
|
|
@@ -24436,7 +25188,7 @@ function validate$1(parse, reason = "protocol") {
|
|
|
24436
25188
|
})
|
|
24437
25189
|
});
|
|
24438
25190
|
}
|
|
24439
|
-
function section$
|
|
25191
|
+
function section$1(source, map) {
|
|
24440
25192
|
return source.state === "available" ? {
|
|
24441
25193
|
...source,
|
|
24442
25194
|
items: source.items.map(map)
|
|
@@ -24591,7 +25343,7 @@ var beadsStoreWorkAdapterLayer = effect(WorkAdapter, gen(function* () {
|
|
|
24591
25343
|
return yield* validate$1(() => parseWorkDetail({
|
|
24592
25344
|
connection: beadsWorkConnection(session.connection, schema),
|
|
24593
25345
|
item: item(issue, schema, ready.ids.includes(id) ? true : ready.truncated ? void 0 : false),
|
|
24594
|
-
discussion: section$
|
|
25346
|
+
discussion: section$1(discussion, (message) => ({
|
|
24595
25347
|
id: message.id,
|
|
24596
25348
|
body: message.body,
|
|
24597
25349
|
createdAt: message.createdAt,
|
|
@@ -24617,13 +25369,13 @@ var beadsStoreWorkAdapterLayer = effect(WorkAdapter, gen(function* () {
|
|
|
24617
25369
|
let events = yield* session.events(id);
|
|
24618
25370
|
data = yield* validate$1(() => ({
|
|
24619
25371
|
kind: "activity",
|
|
24620
|
-
data: section$
|
|
25372
|
+
data: section$1(events, beadsWorkActivity)
|
|
24621
25373
|
}));
|
|
24622
25374
|
} else if (sectionId === "history") {
|
|
24623
25375
|
let history = yield* session.history(id);
|
|
24624
25376
|
data = yield* validate$1(() => ({
|
|
24625
25377
|
kind: "history",
|
|
24626
|
-
data: section$
|
|
25378
|
+
data: section$1(history, (entry) => ({
|
|
24627
25379
|
revision: entry.commit,
|
|
24628
25380
|
author: entry.author,
|
|
24629
25381
|
at: entry.createdAt,
|
|
@@ -24958,7 +25710,7 @@ function parseThreadRecord(value) {
|
|
|
24958
25710
|
if (initial.parentId !== null) throw new WorkValidationError("thread.metadata.initial.parentId");
|
|
24959
25711
|
let bindings = unique(unique(array$1(input.bindings, "bindings", binding, MAX_BEADS_THREAD_BYTES), (entry) => entry.id, "bindings.id"), (entry) => entry.nativeId, "bindings.nativeId");
|
|
24960
25712
|
if (input.source === void 0 !== (input.sourceId === void 0)) throw new WorkValidationError("thread.metadata.source");
|
|
24961
|
-
if (input.source !== void 0 && (!text$
|
|
25713
|
+
if (input.source !== void 0 && (!text$3(input.source, "thread.metadata.source", 512).trim() || !text$3(input.sourceId, "thread.metadata.sourceId", 512).trim())) throw new WorkValidationError("thread.metadata.source");
|
|
24962
25714
|
return {
|
|
24963
25715
|
version: 1,
|
|
24964
25716
|
kind,
|
|
@@ -24969,8 +25721,8 @@ function parseThreadRecord(value) {
|
|
|
24969
25721
|
createdAt: timestamp(input.createdAt, "thread.metadata.createdAt"),
|
|
24970
25722
|
updatedAt: timestamp(input.updatedAt, "thread.metadata.updatedAt"),
|
|
24971
25723
|
...input.source === void 0 ? {} : {
|
|
24972
|
-
source: text$
|
|
24973
|
-
sourceId: text$
|
|
25724
|
+
source: text$3(input.source, "thread.metadata.source", 512),
|
|
25725
|
+
sourceId: text$3(input.sourceId, "thread.metadata.sourceId", 512)
|
|
24974
25726
|
}
|
|
24975
25727
|
};
|
|
24976
25728
|
}
|
|
@@ -26355,7 +27107,7 @@ function identifier(value) {
|
|
|
26355
27107
|
if (typeof value !== "string" || value.length > 512 || !/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(value)) throw failure$4("input", "Select a valid task ID.");
|
|
26356
27108
|
return value;
|
|
26357
27109
|
}
|
|
26358
|
-
function text$
|
|
27110
|
+
function text$1(value, field, max, required = false) {
|
|
26359
27111
|
if (typeof value !== "string" || value.length > max || value.includes("\0")) throw failure$4("input", `The ${field} is invalid.`);
|
|
26360
27112
|
if (required && !value.trim()) throw failure$4("input", `The ${field} is required.`);
|
|
26361
27113
|
return value;
|
|
@@ -26365,7 +27117,7 @@ function readFailure(cause) {
|
|
|
26365
27117
|
if (cause instanceof Error && /no such table/i.test(cause.message)) return failure$4("not-initialized", "This D1 database has no Beads schema. Run migrateBeadsD1 before using it.", { cause });
|
|
26366
27118
|
return failure$4("command", "Unable to read project tasks from the D1 store.", { cause });
|
|
26367
27119
|
}
|
|
26368
|
-
async function select
|
|
27120
|
+
async function select(core, sql, params = []) {
|
|
26369
27121
|
try {
|
|
26370
27122
|
return (await core.db.prepare(sql).bind(...params).all()).results;
|
|
26371
27123
|
} catch (cause) {
|
|
@@ -26456,10 +27208,10 @@ function edgeJson(row) {
|
|
|
26456
27208
|
* @returns Rows ordered by priority, newest first.
|
|
26457
27209
|
*/
|
|
26458
27210
|
async function listLabel(core, query) {
|
|
26459
|
-
return (await select
|
|
27211
|
+
return (await select(core, `SELECT ${columns}, ${edges} FROM beads_issues i
|
|
26460
27212
|
WHERE EXISTS (SELECT 1 FROM beads_labels l WHERE l.issue_id = i.id AND l.label = ?)
|
|
26461
27213
|
${query.includeInfra ? "" : `AND ${board$1}`}
|
|
26462
|
-
ORDER BY i.priority, i.created_at DESC, i.id LIMIT ?`, [text$
|
|
27214
|
+
ORDER BY i.priority, i.created_at DESC, i.id LIMIT ?`, [text$1(query.label, "label", 512, true), Math.max(0, Math.trunc(query.limit))])).map((row) => listJson(row, true));
|
|
26463
27215
|
}
|
|
26464
27216
|
/**
|
|
26465
27217
|
* Lists durable work items as brief board rows, like `bd list --all --flat --brief`.
|
|
@@ -26468,7 +27220,7 @@ async function listLabel(core, query) {
|
|
|
26468
27220
|
* @returns Rows ordered by priority, newest first.
|
|
26469
27221
|
*/
|
|
26470
27222
|
async function listBoard(core, limit) {
|
|
26471
|
-
return (await select
|
|
27223
|
+
return (await select(core, `SELECT ${columns}, ${edges} FROM beads_issues i WHERE ${board$1}
|
|
26472
27224
|
ORDER BY i.priority, i.created_at DESC, i.id LIMIT ?`, [limit])).map((row) => listJson(row, false));
|
|
26473
27225
|
}
|
|
26474
27226
|
/**
|
|
@@ -26478,7 +27230,7 @@ async function listBoard(core, limit) {
|
|
|
26478
27230
|
* @returns Rows ordered by priority, newest first.
|
|
26479
27231
|
*/
|
|
26480
27232
|
async function listReady(core, limit) {
|
|
26481
|
-
return (await select
|
|
27233
|
+
return (await select(core, `SELECT ${columns}, ${edges} FROM beads_issues i
|
|
26482
27234
|
WHERE ${board$1} AND i.status = 'open'
|
|
26483
27235
|
AND (i.defer_until IS NULL OR julianday(i.defer_until) <= julianday(?))
|
|
26484
27236
|
AND NOT EXISTS (
|
|
@@ -26556,7 +27308,7 @@ async function listComments(core, id, limit) {
|
|
|
26556
27308
|
* @returns Native events.
|
|
26557
27309
|
*/
|
|
26558
27310
|
async function listEvents(core, id, limit) {
|
|
26559
|
-
return (await select
|
|
27311
|
+
return (await select(core, `SELECT id, issue_id, event_type, actor, old_value, new_value, comment, created_at
|
|
26560
27312
|
FROM beads_events WHERE issue_id = ? ORDER BY seq DESC LIMIT ?`, [id, limit])).map((row) => {
|
|
26561
27313
|
let out = {
|
|
26562
27314
|
id: row.id,
|
|
@@ -26702,12 +27454,12 @@ function revision$1(issue, expected) {
|
|
|
26702
27454
|
return Number(expected);
|
|
26703
27455
|
}
|
|
26704
27456
|
async function exists$1(core, id) {
|
|
26705
|
-
return (await select
|
|
27457
|
+
return (await select(core, "SELECT 1 AS found FROM beads_issues WHERE id = ?", [id])).length > 0;
|
|
26706
27458
|
}
|
|
26707
27459
|
var alphabet$1 = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
26708
27460
|
async function allocate(core, options) {
|
|
26709
27461
|
if (options.parentId) {
|
|
26710
|
-
let children = await select
|
|
27462
|
+
let children = await select(core, "SELECT id FROM beads_issues WHERE id LIKE ? ESCAPE '\\'", [`${options.parentId.replace(/[\\%_]/g, (match) => `\\${match}`)}.%`]);
|
|
26711
27463
|
let next = 1;
|
|
26712
27464
|
for (let row of children) {
|
|
26713
27465
|
let suffix = String(row.id).slice(options.parentId.length + 1);
|
|
@@ -26751,7 +27503,7 @@ async function checkEdge(core, id, target, type) {
|
|
|
26751
27503
|
if (target === id) throw failure$4("cycle", "This relationship would create a dependency cycle. Choose a different task.");
|
|
26752
27504
|
if (!await exists$1(core, target)) throw failure$4("not-found", "This task or relationship target no longer exists. Refresh before trying again.");
|
|
26753
27505
|
if (!blockingTypes.includes(type)) return;
|
|
26754
|
-
if ((await select
|
|
27506
|
+
if ((await select(core, `WITH RECURSIVE reach(id) AS (
|
|
26755
27507
|
SELECT ? UNION
|
|
26756
27508
|
SELECT d.depends_on_id FROM beads_dependencies d JOIN reach r ON d.issue_id = r.id
|
|
26757
27509
|
WHERE d.type IN ${blocking})
|
|
@@ -26981,7 +27733,7 @@ function reopenPlan(plan, _issue, reason, expected) {
|
|
|
26981
27733
|
}
|
|
26982
27734
|
function labels(values) {
|
|
26983
27735
|
return values.map((label) => {
|
|
26984
|
-
let value = text$
|
|
27736
|
+
let value = text$1(label, "label", 512, true);
|
|
26985
27737
|
if (/[\r\n]/.test(value)) throw failure$4("input", "The label is invalid.");
|
|
26986
27738
|
return value;
|
|
26987
27739
|
});
|
|
@@ -26997,9 +27749,9 @@ function metadataObject(value) {
|
|
|
26997
27749
|
* @returns The native row, as `bd create --json` prints it.
|
|
26998
27750
|
*/
|
|
26999
27751
|
async function createRecord(core, value) {
|
|
27000
|
-
let title = text$
|
|
27001
|
-
let type = knownType(text$
|
|
27002
|
-
let actor = text$
|
|
27752
|
+
let title = text$1(value.title, "title", 1e3, true);
|
|
27753
|
+
let type = knownType(text$1(value.type, "type", 128, true));
|
|
27754
|
+
let actor = text$1(value.actor, "actor", 512, true);
|
|
27003
27755
|
let names = labels(value.labels);
|
|
27004
27756
|
let metadata = metadataObject(value.metadata);
|
|
27005
27757
|
let wisp = type === "message";
|
|
@@ -27047,8 +27799,8 @@ async function createRecord(core, value) {
|
|
|
27047
27799
|
*/
|
|
27048
27800
|
async function addComment(core, id, value) {
|
|
27049
27801
|
identifier(id);
|
|
27050
|
-
let author = text$
|
|
27051
|
-
let body = text$
|
|
27802
|
+
let author = text$1(value.author, "author", 512, true);
|
|
27803
|
+
let body = text$1(value.body, "message", 4194304);
|
|
27052
27804
|
let plan = new Plan(core, id, core.now(), author);
|
|
27053
27805
|
let comment = {
|
|
27054
27806
|
id: crypto.randomUUID(),
|
|
@@ -27075,7 +27827,7 @@ async function addComment(core, id, value) {
|
|
|
27075
27827
|
async function addDependency(core, id, target, type) {
|
|
27076
27828
|
identifier(id);
|
|
27077
27829
|
identifier(target);
|
|
27078
|
-
let kind = text$
|
|
27830
|
+
let kind = text$1(type, "relationship type", 128, true);
|
|
27079
27831
|
if (!/^[a-z][a-z-]*$/.test(kind)) throw failure$4("input", "The relationship type is invalid.");
|
|
27080
27832
|
if (!await exists$1(core, id)) throw failure$4("not-found", messages.missing);
|
|
27081
27833
|
await checkEdge(core, id, target, kind);
|
|
@@ -27097,7 +27849,7 @@ async function addDependency(core, id, target, type) {
|
|
|
27097
27849
|
async function removeDependency(core, id, target) {
|
|
27098
27850
|
identifier(id);
|
|
27099
27851
|
identifier(target);
|
|
27100
|
-
if (!(await select
|
|
27852
|
+
if (!(await select(core, "SELECT 1 AS found FROM beads_dependencies WHERE issue_id = ? AND depends_on_id = ?", [id, target])).length) throw failure$4("not-found", "This relationship no longer exists. Refresh before trying again.");
|
|
27101
27853
|
let plan = new Plan(core, id, core.now());
|
|
27102
27854
|
plan.update({});
|
|
27103
27855
|
plan.removeEdge(target);
|
|
@@ -27115,9 +27867,9 @@ async function removeDependency(core, id, target) {
|
|
|
27115
27867
|
*/
|
|
27116
27868
|
async function promote(core, id, value) {
|
|
27117
27869
|
identifier(id);
|
|
27118
|
-
let plan = new Plan(core, id, core.now(), text$
|
|
27870
|
+
let plan = new Plan(core, id, core.now(), text$1(value.actor, "actor", 512, true));
|
|
27119
27871
|
plan.update({ ephemeral: 0 });
|
|
27120
|
-
plan.event("promoted", { comment: text$
|
|
27872
|
+
plan.event("promoted", { comment: text$1(value.reason, "reason", 4096) });
|
|
27121
27873
|
await apply(core, plan);
|
|
27122
27874
|
}
|
|
27123
27875
|
/**
|
|
@@ -27130,7 +27882,7 @@ async function close(core, id, reason) {
|
|
|
27130
27882
|
identifier(id);
|
|
27131
27883
|
let issue = await required(core, id);
|
|
27132
27884
|
let plan = new Plan(core, id, core.now());
|
|
27133
|
-
closePlan(core, plan, issue, text$
|
|
27885
|
+
closePlan(core, plan, issue, text$1(reason, "reason", 4096));
|
|
27134
27886
|
await apply(core, plan);
|
|
27135
27887
|
}
|
|
27136
27888
|
/**
|
|
@@ -27144,7 +27896,7 @@ async function reopen(core, id, reason) {
|
|
|
27144
27896
|
let issue = await required(core, id);
|
|
27145
27897
|
if (issue.status !== "closed") return;
|
|
27146
27898
|
let plan = new Plan(core, id, core.now());
|
|
27147
|
-
reopenPlan(plan, issue, text$
|
|
27899
|
+
reopenPlan(plan, issue, text$1(reason, "reason", 4096));
|
|
27148
27900
|
await apply(core, plan);
|
|
27149
27901
|
}
|
|
27150
27902
|
/**
|
|
@@ -27307,7 +28059,7 @@ async function importJsonl(core, jsonl) {
|
|
|
27307
28059
|
merged: 0
|
|
27308
28060
|
};
|
|
27309
28061
|
for (let record of records) {
|
|
27310
|
-
let [existing] = await select
|
|
28062
|
+
let [existing] = await select(core, "SELECT updated_at FROM beads_issues WHERE id = ?", [record.issue.id]);
|
|
27311
28063
|
let plan = new Plan(core, record.issue.id, at, record.issue.createdBy ?? core.actor);
|
|
27312
28064
|
if (!existing) {
|
|
27313
28065
|
plan.insert(record.values);
|
|
@@ -27392,7 +28144,7 @@ function protocol(operation, parse) {
|
|
|
27392
28144
|
})
|
|
27393
28145
|
});
|
|
27394
28146
|
}
|
|
27395
|
-
function section
|
|
28147
|
+
function section(items, limit) {
|
|
27396
28148
|
return {
|
|
27397
28149
|
state: "available",
|
|
27398
28150
|
items: items.slice(0, limit),
|
|
@@ -27547,13 +28299,13 @@ function makeD1BeadsStore(options) {
|
|
|
27547
28299
|
comments: (value, parse) => id(value).pipe(flatMap$1(() => run(() => listComments(store, value))), flatMap$1((rows) => protocol("comments", () => parse(rows)))),
|
|
27548
28300
|
ready: readyIssues,
|
|
27549
28301
|
issue,
|
|
27550
|
-
discussion: (value) => id(value).pipe(flatMap$1(() => run(() => listComments(store, value, 201))), flatMap$1((rows) => protocol("comments", () => section
|
|
28302
|
+
discussion: (value) => id(value).pipe(flatMap$1(() => run(() => listComments(store, value, 201))), flatMap$1((rows) => protocol("comments", () => section(parseCliDiscussion(rows), 200)))),
|
|
27551
28303
|
history: () => succeed$2({
|
|
27552
28304
|
state: "unsupported",
|
|
27553
28305
|
items: [],
|
|
27554
28306
|
truncated: false
|
|
27555
28307
|
}),
|
|
27556
|
-
events: (value) => id(value).pipe(flatMap$1(() => run(() => listEvents(store, value, 51))), flatMap$1((rows) => protocol("history", () => section
|
|
28308
|
+
events: (value) => id(value).pipe(flatMap$1(() => run(() => listEvents(store, value, 51))), flatMap$1((rows) => protocol("history", () => section(parseCliEvents(rows), 50)))),
|
|
27557
28309
|
snapshot: gen(function* () {
|
|
27558
28310
|
let issues = yield* run(() => listBoard(store, MAX_BEADS + 1)).pipe(flatMap$1((rows) => protocol("list", () => parseCliIssues(rows))));
|
|
27559
28311
|
let unblocked = yield* readyIssues(MAX_BEADS + 1);
|
|
@@ -35942,1021 +36694,381 @@ var watchNode = (path, info, options) => callback((queue) => acquireRelease(sync
|
|
|
35942
36694
|
return;
|
|
35943
36695
|
case "change":
|
|
35944
36696
|
offerUnsafe(queue, {
|
|
35945
|
-
_tag: "Update",
|
|
35946
|
-
path
|
|
35947
|
-
});
|
|
35948
|
-
return;
|
|
35949
|
-
}
|
|
35950
|
-
});
|
|
35951
|
-
watcher.on("error", (error) => {
|
|
35952
|
-
failCauseUnsafe(queue, fail$7(systemError({
|
|
35953
|
-
module: "FileSystem",
|
|
35954
|
-
_tag: "Unknown",
|
|
35955
|
-
method: "watch",
|
|
35956
|
-
pathOrDescriptor: path,
|
|
35957
|
-
cause: error
|
|
35958
|
-
})));
|
|
35959
|
-
});
|
|
35960
|
-
watcher.on("close", () => {
|
|
35961
|
-
endUnsafe(queue);
|
|
35962
|
-
});
|
|
35963
|
-
return watcher;
|
|
35964
|
-
}), (watcher) => sync(() => watcher.close())).pipe(tap$1(() => sync(() => options?.onReady?.()))));
|
|
35965
|
-
var watch = (backend, path, options) => all$1([stat$1(path), serviceOption(WatchBackend)]).pipe(map$2(([stat, callerBackend]) => orElse(callerBackend, () => backend).pipe(flatMap$5((_) => _.register(path, stat, options)), getOrElse(() => watchNode(path, stat, options)))), unwrap$1);
|
|
35966
|
-
var writeFile$1 = (path, data, options) => callback$1((resume, signal) => {
|
|
35967
|
-
try {
|
|
35968
|
-
NFS.writeFile(path, data, {
|
|
35969
|
-
signal,
|
|
35970
|
-
flag: options?.flag,
|
|
35971
|
-
mode: options?.mode
|
|
35972
|
-
}, (err) => {
|
|
35973
|
-
if (err) resume(fail$6(handleErrnoException("FileSystem", "writeFile")(err, [path])));
|
|
35974
|
-
else resume(void_);
|
|
35975
|
-
});
|
|
35976
|
-
} catch (err) {
|
|
35977
|
-
resume(fail$6(handleBadArgument("writeFile")(err)));
|
|
35978
|
-
}
|
|
35979
|
-
});
|
|
35980
|
-
var makeFileSystem = /*#__PURE__*/ map$2(/*#__PURE__*/ serviceOption(WatchBackend), (backend) => make$14({
|
|
35981
|
-
access: access$1,
|
|
35982
|
-
chmod,
|
|
35983
|
-
chown,
|
|
35984
|
-
copy,
|
|
35985
|
-
copyFile,
|
|
35986
|
-
glob,
|
|
35987
|
-
link,
|
|
35988
|
-
makeDirectory,
|
|
35989
|
-
makeTempDirectory,
|
|
35990
|
-
makeTempDirectoryScoped,
|
|
35991
|
-
makeTempFile,
|
|
35992
|
-
makeTempFileScoped,
|
|
35993
|
-
open,
|
|
35994
|
-
readDirectory,
|
|
35995
|
-
readFile: readFile$1,
|
|
35996
|
-
readLink,
|
|
35997
|
-
realPath,
|
|
35998
|
-
remove,
|
|
35999
|
-
rename: rename$1,
|
|
36000
|
-
stat: stat$1,
|
|
36001
|
-
symlink: symlink$1,
|
|
36002
|
-
truncate,
|
|
36003
|
-
utimes,
|
|
36004
|
-
watch(path, options) {
|
|
36005
|
-
return watch(backend, path, options);
|
|
36006
|
-
},
|
|
36007
|
-
writeFile: writeFile$1
|
|
36008
|
-
}));
|
|
36009
|
-
//#endregion
|
|
36010
|
-
//#region ../../node_modules/.pnpm/@effect+platform-node@4.0.5_effect@4.0.5/node_modules/@effect/platform-node/dist/NodeFileSystem.js
|
|
36011
|
-
/**
|
|
36012
|
-
* Node.js `FileSystem` layer for programs that perform real filesystem I/O.
|
|
36013
|
-
*
|
|
36014
|
-
* The exported layer satisfies the platform-independent `FileSystem` service
|
|
36015
|
-
* with Node-backed operations for files, directories, metadata, permissions,
|
|
36016
|
-
* links, temporary paths, and path watching. Effects still call the service from
|
|
36017
|
-
* `effect/FileSystem`; this module only chooses the Node implementation.
|
|
36018
|
-
*
|
|
36019
|
-
* @stability unstable
|
|
36020
|
-
* @since 4.0.0
|
|
36021
|
-
*/
|
|
36022
|
-
/**
|
|
36023
|
-
* Provides the `FileSystem` service backed by Node filesystem APIs.
|
|
36024
|
-
*
|
|
36025
|
-
* @stability unstable
|
|
36026
|
-
* @category layers
|
|
36027
|
-
* @since 4.0.0
|
|
36028
|
-
*/
|
|
36029
|
-
var layer$2 = /* @__PURE__ */ effect(FileSystem)(makeFileSystem);
|
|
36030
|
-
//#endregion
|
|
36031
|
-
//#region ../../node_modules/.pnpm/@effect+platform-node-shared@4.0.5_effect@4.0.5/node_modules/@effect/platform-node-shared/dist/NodePath.js
|
|
36032
|
-
/**
|
|
36033
|
-
* Node-backed provider for Effect's `Path` service.
|
|
36034
|
-
*
|
|
36035
|
-
* This module turns Node's `node:path` and `node:url` APIs into `Path` layers.
|
|
36036
|
-
* `layer` uses the host platform path implementation, while `layerPosix` and
|
|
36037
|
-
* `layerWin32` provide fixed POSIX and Windows variants. All three layers also
|
|
36038
|
-
* include helpers for converting between file paths and file URLs.
|
|
36039
|
-
*
|
|
36040
|
-
* @stability unstable
|
|
36041
|
-
* @since 4.0.0
|
|
36042
|
-
*/
|
|
36043
|
-
var fileUrlOps = (windows) => ({
|
|
36044
|
-
fromFileUrl: (url) => try_({
|
|
36045
|
-
try: () => NodeUrl.fileURLToPath(url, { windows }),
|
|
36046
|
-
catch: (cause) => new BadArgument({
|
|
36047
|
-
module: "Path",
|
|
36048
|
-
method: "fromFileUrl",
|
|
36049
|
-
cause
|
|
36050
|
-
})
|
|
36051
|
-
}),
|
|
36052
|
-
toFileUrl: (path) => try_({
|
|
36053
|
-
try: () => NodeUrl.pathToFileURL(path, { windows }),
|
|
36054
|
-
catch: (cause) => new BadArgument({
|
|
36055
|
-
module: "Path",
|
|
36056
|
-
method: "toFileUrl",
|
|
36057
|
-
cause
|
|
36058
|
-
})
|
|
36059
|
-
})
|
|
36060
|
-
});
|
|
36061
|
-
({
|
|
36062
|
-
...Path.posix,
|
|
36063
|
-
.../*#__PURE__*/ fileUrlOps(false)
|
|
36064
|
-
});
|
|
36065
|
-
({
|
|
36066
|
-
...Path.win32,
|
|
36067
|
-
.../*#__PURE__*/ fileUrlOps(true)
|
|
36068
|
-
});
|
|
36069
|
-
provideMerge(layer$4, mergeAll$1(layer$2, /* @__PURE__ */ succeed$3(Path$1)(/*#__PURE__*/ make$13({
|
|
36070
|
-
...Path,
|
|
36071
|
-
.../*#__PURE__*/ fileUrlOps(void 0)
|
|
36072
|
-
}))));
|
|
36073
|
-
TaggedError("GitInputError");
|
|
36074
|
-
TaggedError("GitCommandError");
|
|
36075
|
-
TaggedError("GitRepositoryError");
|
|
36076
|
-
TaggedError("GitIdentityError");
|
|
36077
|
-
TaggedError("GitProtocolError");
|
|
36078
|
-
TaggedError("GitConflictError");
|
|
36079
|
-
TaggedError("GitIndexError");
|
|
36080
|
-
//#endregion
|
|
36081
|
-
//#region ../git/src/lib/validation.ts
|
|
36082
|
-
var text$1 = String$1.check(isNonEmpty(), isPattern(/^[^\0]*$/));
|
|
36083
|
-
var oid = String$1.check(isPattern(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/));
|
|
36084
|
-
var path$1 = text$1.check(makeFilter((value) => !value.startsWith("/") && !value.includes("\\") && value.split("/").every((part) => part.length > 0 && part !== "." && part !== ".." && part.toLowerCase() !== ".git")));
|
|
36085
|
-
var ref$1 = text$1.check(makeFilter((value) => value.startsWith("refs/") && !/[\x00-\x20\x7f~^:?*\[\\]/.test(value) && !value.includes("..") && !value.includes("@{") && !value.endsWith(".") && value.split("/").every((part) => part.length > 0 && !part.startsWith(".") && !part.endsWith(".lock"))));
|
|
36086
|
-
var revision = Union([
|
|
36087
|
-
oid,
|
|
36088
|
-
ref$1,
|
|
36089
|
-
Literal("HEAD")
|
|
36090
|
-
]);
|
|
36091
|
-
String$1.check(isPattern(/^[A-Za-z0-9][A-Za-z0-9._-]*$/));
|
|
36092
|
-
var mode = Literals([
|
|
36093
|
-
"100644",
|
|
36094
|
-
"100755",
|
|
36095
|
-
"120000"
|
|
36096
|
-
]);
|
|
36097
|
-
Struct({
|
|
36098
|
-
name: text$1.check(isPattern(/^[^\r\n<>]+$/)),
|
|
36099
|
-
email: text$1.check(isPattern(/^[^\r\n<>]+$/))
|
|
36100
|
-
});
|
|
36101
|
-
Struct({
|
|
36102
|
-
ref: ref$1,
|
|
36103
|
-
expectedHead: NullOr(oid),
|
|
36104
|
-
files: ArraySchema(Struct({
|
|
36105
|
-
path: path$1,
|
|
36106
|
-
contents: Union([
|
|
36107
|
-
String$1,
|
|
36108
|
-
Uint8Array$1,
|
|
36109
|
-
Null
|
|
36110
|
-
]),
|
|
36111
|
-
mode: optional$1(mode)
|
|
36112
|
-
})).check(isMaxLength(1e4)),
|
|
36113
|
-
message: text$1,
|
|
36114
|
-
extraParents: optional$1(ArraySchema(oid).check(isMaxLength(16)))
|
|
36115
|
-
});
|
|
36116
|
-
Struct({
|
|
36117
|
-
...Struct({
|
|
36118
|
-
base: optional$1(oid),
|
|
36119
|
-
target: optional$1(oid),
|
|
36120
|
-
staged: optional$1(Boolean$1),
|
|
36121
|
-
paths: optional$1(ArraySchema(path$1).check(isMaxLength(1e4)))
|
|
36122
|
-
}).fields,
|
|
36123
|
-
context: optional$1(Int$1.check(isBetween({
|
|
36124
|
-
minimum: 0,
|
|
36125
|
-
maximum: 1e3
|
|
36126
|
-
})))
|
|
36127
|
-
});
|
|
36128
|
-
Struct({ environment: optional$1(Record(String$1, Union([String$1, Undefined]))) });
|
|
36129
|
-
Struct({
|
|
36130
|
-
commit: oid,
|
|
36131
|
-
path: path$1,
|
|
36132
|
-
limit: Int$1.check(isBetween({
|
|
36133
|
-
minimum: 1,
|
|
36134
|
-
maximum: 101
|
|
36135
|
-
})),
|
|
36136
|
-
skip: optional$1(Int$1.check(isBetween({
|
|
36137
|
-
minimum: 0,
|
|
36138
|
-
maximum: 1e4
|
|
36139
|
-
})))
|
|
36140
|
-
});
|
|
36141
|
-
Struct({
|
|
36142
|
-
from: revision,
|
|
36143
|
-
limit: Int$1.check(isBetween({
|
|
36144
|
-
minimum: 1,
|
|
36145
|
-
maximum: 500
|
|
36146
|
-
}))
|
|
36147
|
-
});
|
|
36148
|
-
Int$1.check(isBetween({
|
|
36149
|
-
minimum: 1,
|
|
36150
|
-
maximum: 1e3
|
|
36151
|
-
}));
|
|
36152
|
-
//#endregion
|
|
36153
|
-
//#region ../git/src/lib/git.ts
|
|
36154
|
-
/** Opens the Git repository that contains a directory. */
|
|
36155
|
-
var Git = class extends Service()("@bud.sh/git/Git") {};
|
|
36156
|
-
//#endregion
|
|
36157
|
-
//#region ../live/src/server/hub.ts
|
|
36158
|
-
/**
|
|
36159
|
-
* Orders every published change into one stream per companion. Clients resume from their
|
|
36160
|
-
* last event id; anything older than the replay buffer, or from an earlier companion,
|
|
36161
|
-
* receives a resync instead.
|
|
36162
|
-
*/
|
|
36163
|
-
var LiveHub = class {
|
|
36164
|
-
epoch = randomBytes(8).toString("hex");
|
|
36165
|
-
#sequence = 0;
|
|
36166
|
-
#buffer = [];
|
|
36167
|
-
#replay;
|
|
36168
|
-
#heartbeatMs;
|
|
36169
|
-
#listeners = /* @__PURE__ */ new Set();
|
|
36170
|
-
constructor(options = {}) {
|
|
36171
|
-
this.#replay = options.replay ?? 1024;
|
|
36172
|
-
this.#heartbeatMs = options.heartbeatMs ?? 15e3;
|
|
36173
|
-
}
|
|
36174
|
-
/**
|
|
36175
|
-
* Sends a change to every connected client.
|
|
36176
|
-
* @param topic - The domain, such as `previews` or `files`.
|
|
36177
|
-
* @param data - JSON data each domain validates with its own schema.
|
|
36178
|
-
*/
|
|
36179
|
-
publish(topic, data) {
|
|
36180
|
-
let event = {
|
|
36181
|
-
id: `${this.epoch}:${++this.#sequence}`,
|
|
36182
|
-
topic,
|
|
36183
|
-
data
|
|
36184
|
-
};
|
|
36185
|
-
this.#buffer.push(event);
|
|
36186
|
-
if (this.#buffer.length > this.#replay) this.#buffer.shift();
|
|
36187
|
-
for (let listener of [...this.#listeners]) listener(event);
|
|
36188
|
-
}
|
|
36189
|
-
/**
|
|
36190
|
-
* Opens one client's `text/event-stream`.
|
|
36191
|
-
* @param lastEventId - The client's `Last-Event-ID`, if it is resuming.
|
|
36192
|
-
* @param signal - The request's lifetime; aborting closes the stream.
|
|
36193
|
-
* @returns The response body.
|
|
36194
|
-
*/
|
|
36195
|
-
open(lastEventId, signal) {
|
|
36196
|
-
let encoder = new TextEncoder();
|
|
36197
|
-
let close = () => {};
|
|
36198
|
-
return new ReadableStream({
|
|
36199
|
-
start: (controller) => {
|
|
36200
|
-
let send = (text) => controller.enqueue(encoder.encode(text));
|
|
36201
|
-
let write = (event) => send(`id: ${event.id}\nevent: ${event.topic}\ndata: ${JSON.stringify(event.data)}\n\n`);
|
|
36202
|
-
let missed = this.#since(lastEventId);
|
|
36203
|
-
if (missed) for (let event of missed) write(event);
|
|
36204
|
-
else write({
|
|
36205
|
-
id: `${this.epoch}:${this.#sequence}`,
|
|
36206
|
-
topic: RESYNC,
|
|
36207
|
-
data: null
|
|
36208
|
-
});
|
|
36209
|
-
this.#listeners.add(write);
|
|
36210
|
-
let heartbeat = setInterval(() => send(": heartbeat\n\n"), this.#heartbeatMs);
|
|
36211
|
-
close = () => {
|
|
36212
|
-
clearInterval(heartbeat);
|
|
36213
|
-
this.#listeners.delete(write);
|
|
36214
|
-
try {
|
|
36215
|
-
controller.close();
|
|
36216
|
-
} catch {}
|
|
36217
|
-
};
|
|
36218
|
-
if (signal.aborted) close();
|
|
36219
|
-
else signal.addEventListener("abort", close, { once: true });
|
|
36220
|
-
},
|
|
36221
|
-
cancel: () => close()
|
|
36697
|
+
_tag: "Update",
|
|
36698
|
+
path
|
|
36699
|
+
});
|
|
36700
|
+
return;
|
|
36701
|
+
}
|
|
36702
|
+
});
|
|
36703
|
+
watcher.on("error", (error) => {
|
|
36704
|
+
failCauseUnsafe(queue, fail$7(systemError({
|
|
36705
|
+
module: "FileSystem",
|
|
36706
|
+
_tag: "Unknown",
|
|
36707
|
+
method: "watch",
|
|
36708
|
+
pathOrDescriptor: path,
|
|
36709
|
+
cause: error
|
|
36710
|
+
})));
|
|
36711
|
+
});
|
|
36712
|
+
watcher.on("close", () => {
|
|
36713
|
+
endUnsafe(queue);
|
|
36714
|
+
});
|
|
36715
|
+
return watcher;
|
|
36716
|
+
}), (watcher) => sync(() => watcher.close())).pipe(tap$1(() => sync(() => options?.onReady?.()))));
|
|
36717
|
+
var watch = (backend, path, options) => all$1([stat$1(path), serviceOption(WatchBackend)]).pipe(map$2(([stat, callerBackend]) => orElse(callerBackend, () => backend).pipe(flatMap$5((_) => _.register(path, stat, options)), getOrElse(() => watchNode(path, stat, options)))), unwrap$1);
|
|
36718
|
+
var writeFile$1 = (path, data, options) => callback$1((resume, signal) => {
|
|
36719
|
+
try {
|
|
36720
|
+
NFS.writeFile(path, data, {
|
|
36721
|
+
signal,
|
|
36722
|
+
flag: options?.flag,
|
|
36723
|
+
mode: options?.mode
|
|
36724
|
+
}, (err) => {
|
|
36725
|
+
if (err) resume(fail$6(handleErrnoException("FileSystem", "writeFile")(err, [path])));
|
|
36726
|
+
else resume(void_);
|
|
36222
36727
|
});
|
|
36728
|
+
} catch (err) {
|
|
36729
|
+
resume(fail$6(handleBadArgument("writeFile")(err)));
|
|
36223
36730
|
}
|
|
36224
|
-
|
|
36225
|
-
|
|
36226
|
-
|
|
36227
|
-
|
|
36228
|
-
|
|
36229
|
-
|
|
36230
|
-
|
|
36231
|
-
|
|
36731
|
+
});
|
|
36732
|
+
var makeFileSystem = /*#__PURE__*/ map$2(/*#__PURE__*/ serviceOption(WatchBackend), (backend) => make$14({
|
|
36733
|
+
access: access$1,
|
|
36734
|
+
chmod,
|
|
36735
|
+
chown,
|
|
36736
|
+
copy,
|
|
36737
|
+
copyFile,
|
|
36738
|
+
glob,
|
|
36739
|
+
link,
|
|
36740
|
+
makeDirectory,
|
|
36741
|
+
makeTempDirectory,
|
|
36742
|
+
makeTempDirectoryScoped,
|
|
36743
|
+
makeTempFile,
|
|
36744
|
+
makeTempFileScoped,
|
|
36745
|
+
open,
|
|
36746
|
+
readDirectory,
|
|
36747
|
+
readFile: readFile$1,
|
|
36748
|
+
readLink,
|
|
36749
|
+
realPath,
|
|
36750
|
+
remove,
|
|
36751
|
+
rename: rename$1,
|
|
36752
|
+
stat: stat$1,
|
|
36753
|
+
symlink: symlink$1,
|
|
36754
|
+
truncate,
|
|
36755
|
+
utimes,
|
|
36756
|
+
watch(path, options) {
|
|
36757
|
+
return watch(backend, path, options);
|
|
36758
|
+
},
|
|
36759
|
+
writeFile: writeFile$1
|
|
36760
|
+
}));
|
|
36232
36761
|
//#endregion
|
|
36233
|
-
//#region
|
|
36234
|
-
/** The companion's ordered live channel: publishes changes and opens client streams. */
|
|
36235
|
-
var LiveChannel = class extends Service()("@bud.sh/live/LiveChannel") {};
|
|
36762
|
+
//#region ../../node_modules/.pnpm/@effect+platform-node@4.0.5_effect@4.0.5/node_modules/@effect/platform-node/dist/NodeFileSystem.js
|
|
36236
36763
|
/**
|
|
36237
|
-
*
|
|
36238
|
-
*
|
|
36239
|
-
*
|
|
36764
|
+
* Node.js `FileSystem` layer for programs that perform real filesystem I/O.
|
|
36765
|
+
*
|
|
36766
|
+
* The exported layer satisfies the platform-independent `FileSystem` service
|
|
36767
|
+
* with Node-backed operations for files, directories, metadata, permissions,
|
|
36768
|
+
* links, temporary paths, and path watching. Effects still call the service from
|
|
36769
|
+
* `effect/FileSystem`; this module only chooses the Node implementation.
|
|
36770
|
+
*
|
|
36771
|
+
* @stability unstable
|
|
36772
|
+
* @since 4.0.0
|
|
36240
36773
|
*/
|
|
36241
|
-
function liveChannelLayer(options = {}) {
|
|
36242
|
-
return sync$1(LiveChannel, () => {
|
|
36243
|
-
let hub = new LiveHub(options);
|
|
36244
|
-
return {
|
|
36245
|
-
epoch: hub.epoch,
|
|
36246
|
-
publish: (topic, data) => sync(() => hub.publish(topic, data)),
|
|
36247
|
-
open: (lastEventId, signal) => hub.open(lastEventId, signal)
|
|
36248
|
-
};
|
|
36249
|
-
});
|
|
36250
|
-
}
|
|
36251
36774
|
/**
|
|
36252
|
-
*
|
|
36253
|
-
*
|
|
36254
|
-
* @
|
|
36255
|
-
* @
|
|
36256
|
-
* @
|
|
36775
|
+
* Provides the `FileSystem` service backed by Node filesystem APIs.
|
|
36776
|
+
*
|
|
36777
|
+
* @stability unstable
|
|
36778
|
+
* @category layers
|
|
36779
|
+
* @since 4.0.0
|
|
36257
36780
|
*/
|
|
36258
|
-
|
|
36259
|
-
return gen(function* () {
|
|
36260
|
-
let channel = yield* LiveChannel;
|
|
36261
|
-
yield* changes.pipe(runForEach((change) => channel.publish(topic, change)), ignore$2, forkScoped);
|
|
36262
|
-
});
|
|
36263
|
-
}
|
|
36264
|
-
//#endregion
|
|
36265
|
-
//#region ../project/src/server/errors.ts
|
|
36266
|
-
/** Expected project startup/configuration failure; nested causes remain server-only. */
|
|
36267
|
-
var ProjectError = class extends TaggedError("ProjectError") {};
|
|
36781
|
+
var layer$2 = /* @__PURE__ */ effect(FileSystem)(makeFileSystem);
|
|
36268
36782
|
//#endregion
|
|
36269
|
-
//#region
|
|
36783
|
+
//#region ../../node_modules/.pnpm/@effect+platform-node-shared@4.0.5_effect@4.0.5/node_modules/@effect/platform-node-shared/dist/NodePath.js
|
|
36270
36784
|
/**
|
|
36271
|
-
*
|
|
36272
|
-
*
|
|
36273
|
-
*
|
|
36274
|
-
*
|
|
36275
|
-
*
|
|
36785
|
+
* Node-backed provider for Effect's `Path` service.
|
|
36786
|
+
*
|
|
36787
|
+
* This module turns Node's `node:path` and `node:url` APIs into `Path` layers.
|
|
36788
|
+
* `layer` uses the host platform path implementation, while `layerPosix` and
|
|
36789
|
+
* `layerWin32` provide fixed POSIX and Windows variants. All three layers also
|
|
36790
|
+
* include helpers for converting between file paths and file URLs.
|
|
36791
|
+
*
|
|
36792
|
+
* @stability unstable
|
|
36793
|
+
* @since 4.0.0
|
|
36276
36794
|
*/
|
|
36277
|
-
|
|
36278
|
-
|
|
36279
|
-
|
|
36280
|
-
|
|
36281
|
-
|
|
36282
|
-
|
|
36283
|
-
reason: "root",
|
|
36284
|
-
message: "Cannot open the module project root.",
|
|
36285
|
-
cause
|
|
36286
|
-
})));
|
|
36287
|
-
let file = yield* fs.realPath(path.resolve(project, module)).pipe(mapError$2((cause) => new ProjectError({
|
|
36288
|
-
reason: "read",
|
|
36289
|
-
message: `${setting} cannot find ${module} in the project.`,
|
|
36290
|
-
cause
|
|
36291
|
-
})));
|
|
36292
|
-
let relative = path.relative(project, file);
|
|
36293
|
-
if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative) || relative.split(path.sep).includes("node_modules")) return yield* new ProjectError({
|
|
36294
|
-
reason: "invalid",
|
|
36295
|
-
message: `${setting} must resolve to a project-owned file outside node_modules.`
|
|
36296
|
-
});
|
|
36297
|
-
if (kind === "server" && !file.endsWith(".server.ts")) return yield* new ProjectError({
|
|
36298
|
-
reason: "invalid",
|
|
36299
|
-
message: `${setting} must resolve to a .server.ts module.`
|
|
36300
|
-
});
|
|
36301
|
-
if (kind === "browser" && (!/\.(?:ts|tsx|js|jsx)$/.test(file) || /\.server\.[cm]?[jt]sx?$/.test(file))) return yield* new ProjectError({
|
|
36302
|
-
reason: "invalid",
|
|
36303
|
-
message: `${setting} must resolve to a browser module.`
|
|
36304
|
-
});
|
|
36305
|
-
if ((yield* fs.stat(file).pipe(mapError$2((cause) => new ProjectError({
|
|
36306
|
-
reason: "read",
|
|
36307
|
-
message: `${setting} cannot inspect ${module}.`,
|
|
36795
|
+
var fileUrlOps = (windows) => ({
|
|
36796
|
+
fromFileUrl: (url) => try_({
|
|
36797
|
+
try: () => NodeUrl.fileURLToPath(url, { windows }),
|
|
36798
|
+
catch: (cause) => new BadArgument({
|
|
36799
|
+
module: "Path",
|
|
36800
|
+
method: "fromFileUrl",
|
|
36308
36801
|
cause
|
|
36309
|
-
})
|
|
36310
|
-
|
|
36311
|
-
|
|
36312
|
-
})
|
|
36313
|
-
|
|
36314
|
-
|
|
36315
|
-
|
|
36802
|
+
})
|
|
36803
|
+
}),
|
|
36804
|
+
toFileUrl: (path) => try_({
|
|
36805
|
+
try: () => NodeUrl.pathToFileURL(path, { windows }),
|
|
36806
|
+
catch: (cause) => new BadArgument({
|
|
36807
|
+
module: "Path",
|
|
36808
|
+
method: "toFileUrl",
|
|
36316
36809
|
cause
|
|
36317
|
-
})
|
|
36318
|
-
|
|
36319
|
-
});
|
|
36320
|
-
}
|
|
36321
|
-
//#endregion
|
|
36322
|
-
//#region ../project/src/lib/validate.ts
|
|
36323
|
-
var errors = /* @__PURE__ */ new WeakMap();
|
|
36324
|
-
/**
|
|
36325
|
-
* Validates config, throwing its first problem as the `Error` the config checks report.
|
|
36326
|
-
* @param schema - The config schema.
|
|
36327
|
-
* @param value - The project's untrusted setting.
|
|
36328
|
-
* @returns The validated, normalized setting.
|
|
36329
|
-
*/
|
|
36330
|
-
function parseConfig(schema, value) {
|
|
36331
|
-
let result = parseSafe(schema, value, { abortEarly: true });
|
|
36332
|
-
if (result.success) return result.value;
|
|
36333
|
-
let issue = result.issues[0];
|
|
36334
|
-
throw errors.get(issue) ?? new Error(issue.message);
|
|
36335
|
-
}
|
|
36336
|
-
/**
|
|
36337
|
-
* Reports any failure of `schema` with one message.
|
|
36338
|
-
* @param schema - The schema to run.
|
|
36339
|
-
* @param message - The message for any of its failures.
|
|
36340
|
-
* @returns The described schema.
|
|
36341
|
-
*/
|
|
36342
|
-
function described(schema, message) {
|
|
36343
|
-
return createSchema((value, context) => {
|
|
36344
|
-
let result = schema["~run"](value, context);
|
|
36345
|
-
return result.issues ? fail(message, context.path) : result;
|
|
36346
|
-
});
|
|
36347
|
-
}
|
|
36348
|
-
/**
|
|
36349
|
-
* Applies a rule after `schema` validates.
|
|
36350
|
-
* @param schema - The schema to run first.
|
|
36351
|
-
* @param rule - Returns a message, or undefined when the value is valid.
|
|
36352
|
-
* @returns The checked schema.
|
|
36353
|
-
*/
|
|
36354
|
-
function check(schema, rule) {
|
|
36355
|
-
return createSchema((value, context) => {
|
|
36356
|
-
let result = schema["~run"](value, context);
|
|
36357
|
-
if (result.issues) return result;
|
|
36358
|
-
let message = rule(result.value);
|
|
36359
|
-
return message === void 0 ? result : fail(message, context.path);
|
|
36360
|
-
});
|
|
36361
|
-
}
|
|
36362
|
-
/**
|
|
36363
|
-
* Validates with a function that throws, such as a URL or route map parser, keeping its error.
|
|
36364
|
-
* @param schema - The schema to run first.
|
|
36365
|
-
* @param parse - Turns its value into the output, or throws.
|
|
36366
|
-
* @returns The delegating schema.
|
|
36367
|
-
*/
|
|
36368
|
-
function delegate(schema, parse) {
|
|
36369
|
-
return createSchema((value, context) => {
|
|
36370
|
-
let result = schema["~run"](value, context);
|
|
36371
|
-
if (result.issues) return result;
|
|
36372
|
-
try {
|
|
36373
|
-
return { value: parse(result.value) };
|
|
36374
|
-
} catch (error) {
|
|
36375
|
-
if (!(error instanceof Error)) throw error;
|
|
36376
|
-
let failure = fail(error.message, context.path);
|
|
36377
|
-
errors.set(failure.issues[0], error);
|
|
36378
|
-
return failure;
|
|
36379
|
-
}
|
|
36380
|
-
});
|
|
36381
|
-
}
|
|
36382
|
-
/**
|
|
36383
|
-
* Chooses a schema from the unvalidated value.
|
|
36384
|
-
* @param choose - Picks the schema for a value.
|
|
36385
|
-
* @returns The selecting schema.
|
|
36386
|
-
*/
|
|
36387
|
-
function select(choose) {
|
|
36388
|
-
return createSchema((value, context) => choose(value)["~run"](value, context));
|
|
36389
|
-
}
|
|
36390
|
-
/**
|
|
36391
|
-
* Substitutes a default for null or undefined.
|
|
36392
|
-
* @param schema - The schema for present values.
|
|
36393
|
-
* @param value - Makes the default.
|
|
36394
|
-
* @returns The schema with its default.
|
|
36395
|
-
*/
|
|
36396
|
-
function fallback(schema, value) {
|
|
36397
|
-
return createSchema((input, context) => input == null ? { value: value() } : schema["~run"](input, context));
|
|
36398
|
-
}
|
|
36399
|
-
/**
|
|
36400
|
-
* A settings object that names its unsupported keys before validating its fields.
|
|
36401
|
-
* @param name - The setting's name in messages, such as `previews`.
|
|
36402
|
-
* @param shape - The schema for each supported key.
|
|
36403
|
-
* @returns The settings schema.
|
|
36404
|
-
*/
|
|
36405
|
-
function settings(name, shape) {
|
|
36406
|
-
let schema = object(shape);
|
|
36407
|
-
return createSchema((value, context) => {
|
|
36408
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return fail(`${name} must be an object.`, context.path);
|
|
36409
|
-
for (let key of Object.keys(value)) if (!Object.hasOwn(shape, key)) return fail(`${name}.${key} is not a supported setting.`, [...context.path, key]);
|
|
36410
|
-
return schema["~run"](value, context);
|
|
36411
|
-
});
|
|
36412
|
-
}
|
|
36413
|
-
/**
|
|
36414
|
-
* A list whose length is checked before its entries.
|
|
36415
|
-
* @param item - The schema for each entry.
|
|
36416
|
-
* @param message - The message when it is not a list or is too short.
|
|
36417
|
-
* @param minimum - The fewest entries it may have.
|
|
36418
|
-
* @returns The list schema.
|
|
36419
|
-
*/
|
|
36420
|
-
function list(item, message, minimum = 0) {
|
|
36421
|
-
let items = array(item);
|
|
36422
|
-
return createSchema((value, context) => !Array.isArray(value) || value.length < minimum ? fail(message, context.path) : items["~run"](value, context));
|
|
36423
|
-
}
|
|
36424
|
-
//#endregion
|
|
36425
|
-
//#region ../project/src/lib/files.ts
|
|
36426
|
-
/** The Files settings, with their defaults filled in. */
|
|
36427
|
-
var BudFilesSchema = defaulted(settings("files", { edit: fallback(described(boolean(), "files.edit must be true or false."), () => true) }), () => ({ edit: true }));
|
|
36428
|
-
//#endregion
|
|
36429
|
-
//#region ../project/src/lib/flows.ts
|
|
36430
|
-
var message$1 = "flows must be [{ id, title, start, script, steps: [{ id, title }], block? }] with short ids, local paths and unique steps.";
|
|
36431
|
-
var slug = (value) => typeof value === "string" && /^[a-z0-9][a-z0-9-]{0,62}$/.test(value);
|
|
36432
|
-
var text = (value) => typeof value === "string" && value.trim() !== "" && value.length <= 200;
|
|
36433
|
-
function flow(value) {
|
|
36434
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
36435
|
-
let { id, title, start, script, steps, block = [] } = value;
|
|
36436
|
-
if (!slug(id) || !text(title) || !text(script) || script.includes("\0")) return null;
|
|
36437
|
-
if (typeof start !== "string" || !start.startsWith("/") || start.startsWith("//")) return null;
|
|
36438
|
-
if (new URL(start, "http://bud.invalid").origin !== "http://bud.invalid") return null;
|
|
36439
|
-
if (!Array.isArray(steps) || !steps.length || steps.length > 50) return null;
|
|
36440
|
-
let resolved = steps.map((step) => {
|
|
36441
|
-
let { id: stepId, title: stepTitle } = step ?? {};
|
|
36442
|
-
return slug(stepId) && text(stepTitle) ? {
|
|
36443
|
-
id: stepId,
|
|
36444
|
-
title: stepTitle.trim()
|
|
36445
|
-
} : null;
|
|
36446
|
-
});
|
|
36447
|
-
if (resolved.some((step) => !step)) return null;
|
|
36448
|
-
let ids = resolved.map((step) => step.id);
|
|
36449
|
-
if (new Set(ids).size !== ids.length) return null;
|
|
36450
|
-
if (!Array.isArray(block) || block.some((part) => !text(part))) return null;
|
|
36451
|
-
return {
|
|
36452
|
-
id,
|
|
36453
|
-
title: title.trim(),
|
|
36454
|
-
start,
|
|
36455
|
-
script: script.trim(),
|
|
36456
|
-
steps: resolved,
|
|
36457
|
-
block
|
|
36458
|
-
};
|
|
36459
|
-
}
|
|
36460
|
-
/** The flows, none when omitted. */
|
|
36461
|
-
var BudFlowsSchema = createSchema((value, context) => {
|
|
36462
|
-
if (value === void 0) return { value: [] };
|
|
36463
|
-
if (!Array.isArray(value)) return fail(message$1, context.path);
|
|
36464
|
-
let flows = value.map(flow);
|
|
36465
|
-
if (flows.some((resolved) => !resolved)) return fail(message$1, context.path);
|
|
36466
|
-
let ids = flows.map((resolved) => resolved.id);
|
|
36467
|
-
if (new Set(ids).size !== ids.length) return fail(message$1, context.path);
|
|
36468
|
-
return { value: flows };
|
|
36469
|
-
});
|
|
36470
|
-
/**
|
|
36471
|
-
* Where a flow's step is pictured: a route of its own, named `<flow>/<step>`, in a group named
|
|
36472
|
-
* after the flow. Its path is the start page with the step as its fragment, unique among routes.
|
|
36473
|
-
* @param flow - The flow.
|
|
36474
|
-
* @param step - One of its steps.
|
|
36475
|
-
* @returns The step's route.
|
|
36476
|
-
*/
|
|
36477
|
-
function flowStepRoute(flow, step) {
|
|
36478
|
-
return {
|
|
36479
|
-
id: `${flow.id}/${step.id}`,
|
|
36480
|
-
path: `${flow.start.replace(/#.*$/, "")}#${flow.id}-${step.id}`,
|
|
36481
|
-
title: step.title,
|
|
36482
|
-
group: flow.title,
|
|
36483
|
-
flow: {
|
|
36484
|
-
id: flow.id,
|
|
36485
|
-
step: step.id
|
|
36486
|
-
}
|
|
36487
|
-
};
|
|
36488
|
-
}
|
|
36489
|
-
//#endregion
|
|
36490
|
-
//#region ../project/src/lib/logo.ts
|
|
36491
|
-
/** The image types a logo may be, by extension. */
|
|
36492
|
-
var LOGO_TYPES = Object.freeze({
|
|
36493
|
-
".svg": "image/svg+xml",
|
|
36494
|
-
".png": "image/png",
|
|
36495
|
-
".webp": "image/webp",
|
|
36496
|
-
".jpg": "image/jpeg",
|
|
36497
|
-
".jpeg": "image/jpeg",
|
|
36498
|
-
".gif": "image/gif",
|
|
36499
|
-
".ico": "image/x-icon"
|
|
36810
|
+
})
|
|
36811
|
+
})
|
|
36500
36812
|
});
|
|
36501
|
-
|
|
36502
|
-
|
|
36503
|
-
|
|
36504
|
-
let path = value.trim().replaceAll("\\", "/");
|
|
36505
|
-
if (!path || path.startsWith("/") || /^[a-z]:/i.test(path)) return null;
|
|
36506
|
-
if (path.split("/").includes("..")) return null;
|
|
36507
|
-
let extension = path.slice(path.lastIndexOf(".")).toLowerCase();
|
|
36508
|
-
return Object.hasOwn(LOGO_TYPES, extension) ? path : null;
|
|
36509
|
-
}
|
|
36510
|
-
/** A logo in its one resolved form, or null for Bud's own mark. */
|
|
36511
|
-
var BudLogoSchema = createSchema((value, context) => {
|
|
36512
|
-
if (value === void 0) return { value: null };
|
|
36513
|
-
let light = image(typeof value === "object" && value ? value.light : value);
|
|
36514
|
-
let raw = typeof value === "object" && value ? value.dark : void 0;
|
|
36515
|
-
let dark = raw === void 0 ? null : image(raw);
|
|
36516
|
-
let keys = typeof value === "object" && value ? Object.keys(value) : [];
|
|
36517
|
-
if (light && (raw === void 0 || dark) && keys.every((key) => key === "light" || key === "dark")) return { value: {
|
|
36518
|
-
light,
|
|
36519
|
-
dark
|
|
36520
|
-
} };
|
|
36521
|
-
return fail(logoMessage, context.path);
|
|
36813
|
+
({
|
|
36814
|
+
...Path.posix,
|
|
36815
|
+
.../*#__PURE__*/ fileUrlOps(false)
|
|
36522
36816
|
});
|
|
36523
|
-
|
|
36524
|
-
|
|
36525
|
-
|
|
36526
|
-
function branch(value) {
|
|
36527
|
-
return typeof value === "string" && value.length <= 255 && /^[A-Za-z0-9._/-]+$/.test(value) && !value.startsWith("-") && !value.startsWith("/") && !value.endsWith("/") && !value.endsWith(".lock") && !value.includes("..") && !value.includes("//") ? value : null;
|
|
36528
|
-
}
|
|
36529
|
-
function command(value) {
|
|
36530
|
-
return typeof value === "string" && value.trim() && value.length <= 4096 && !value.includes("\0") ? value.trim() : null;
|
|
36531
|
-
}
|
|
36532
|
-
/** The branch settings, defaults filled in. */
|
|
36533
|
-
var BudBranchesSchema = createSchema((value, context) => {
|
|
36534
|
-
let empty = {
|
|
36535
|
-
main: null,
|
|
36536
|
-
install: null,
|
|
36537
|
-
build: null,
|
|
36538
|
-
serve: null
|
|
36539
|
-
};
|
|
36540
|
-
if (value === void 0) return { value: empty };
|
|
36541
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return fail(message, context.path);
|
|
36542
|
-
let input = value;
|
|
36543
|
-
let build = input.build ?? {};
|
|
36544
|
-
if (!build || typeof build !== "object" || Array.isArray(build) || Object.keys(input).some((key) => key !== "main" && key !== "build")) return fail(message, context.path);
|
|
36545
|
-
let steps = build;
|
|
36546
|
-
if (Object.keys(steps).some((key) => ![
|
|
36547
|
-
"install",
|
|
36548
|
-
"build",
|
|
36549
|
-
"serve"
|
|
36550
|
-
].includes(key))) return fail(message, context.path);
|
|
36551
|
-
let resolved = {
|
|
36552
|
-
main: input.main === void 0 ? null : branch(input.main),
|
|
36553
|
-
install: steps.install === void 0 ? null : command(steps.install),
|
|
36554
|
-
build: steps.build === void 0 ? null : command(steps.build),
|
|
36555
|
-
serve: steps.serve === void 0 ? null : command(steps.serve)
|
|
36556
|
-
};
|
|
36557
|
-
if (input.main !== void 0 && !resolved.main || [
|
|
36558
|
-
"install",
|
|
36559
|
-
"build",
|
|
36560
|
-
"serve"
|
|
36561
|
-
].some((key) => steps[key] !== void 0 && !resolved[key])) return fail(message, context.path);
|
|
36562
|
-
return { value: resolved };
|
|
36817
|
+
({
|
|
36818
|
+
...Path.win32,
|
|
36819
|
+
.../*#__PURE__*/ fileUrlOps(true)
|
|
36563
36820
|
});
|
|
36821
|
+
provideMerge(layer$4, mergeAll$1(layer$2, /* @__PURE__ */ succeed$3(Path$1)(/*#__PURE__*/ make$13({
|
|
36822
|
+
...Path,
|
|
36823
|
+
.../*#__PURE__*/ fileUrlOps(void 0)
|
|
36824
|
+
}))));
|
|
36825
|
+
TaggedError("GitInputError");
|
|
36826
|
+
TaggedError("GitCommandError");
|
|
36827
|
+
TaggedError("GitRepositoryError");
|
|
36828
|
+
TaggedError("GitIdentityError");
|
|
36829
|
+
TaggedError("GitProtocolError");
|
|
36830
|
+
TaggedError("GitConflictError");
|
|
36831
|
+
TaggedError("GitIndexError");
|
|
36564
36832
|
//#endregion
|
|
36565
|
-
//#region ../
|
|
36566
|
-
|
|
36567
|
-
|
|
36568
|
-
|
|
36569
|
-
|
|
36570
|
-
|
|
36571
|
-
|
|
36572
|
-
|
|
36573
|
-
|
|
36574
|
-
let extension = server ? /\.server\.ts$/ : /\.(?:ts|tsx|js|jsx)$/;
|
|
36575
|
-
return described(string().refine((value) => value.length <= 1024 && value.startsWith("./") && !/[\u0000-\u001f\u007f\\?#%]/.test(value) && extension.test(value) && (server || !/\.server\.[cm]?[jt]sx?$/.test(value)) && !value.slice(2).split("/").some((part) => !part || part === "." || part === ".." || part === "node_modules")), message ?? (server ? `${name} must be a built-in adapter or a ./ module ending in .server.ts inside the project.` : `${name} must be bud:beads or a ./ browser module ending in .ts, .tsx, .js or .jsx inside the project.`));
|
|
36576
|
-
}
|
|
36577
|
-
var builtInStores = enum_([
|
|
36578
|
-
false,
|
|
36579
|
-
"bud:beads",
|
|
36580
|
-
"bud:memory",
|
|
36581
|
-
"bud:git-comments"
|
|
36833
|
+
//#region ../git/src/lib/validation.ts
|
|
36834
|
+
var text = String$1.check(isNonEmpty(), isPattern(/^[^\0]*$/));
|
|
36835
|
+
var oid = String$1.check(isPattern(/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/));
|
|
36836
|
+
var path$1 = text.check(makeFilter((value) => !value.startsWith("/") && !value.includes("\\") && value.split("/").every((part) => part.length > 0 && part !== "." && part !== ".." && part.toLowerCase() !== ".git")));
|
|
36837
|
+
var ref$1 = text.check(makeFilter((value) => value.startsWith("refs/") && !/[\x00-\x20\x7f~^:?*\[\\]/.test(value) && !value.includes("..") && !value.includes("@{") && !value.endsWith(".") && value.split("/").every((part) => part.length > 0 && !part.startsWith(".") && !part.endsWith(".lock"))));
|
|
36838
|
+
var revision = Union([
|
|
36839
|
+
oid,
|
|
36840
|
+
ref$1,
|
|
36841
|
+
Literal("HEAD")
|
|
36582
36842
|
]);
|
|
36583
|
-
|
|
36584
|
-
|
|
36585
|
-
|
|
36586
|
-
|
|
36587
|
-
|
|
36588
|
-
var BudStorageSchema = defaulted(settings("storage", {
|
|
36589
|
-
work: store("work"),
|
|
36590
|
-
threads: store("threads")
|
|
36591
|
-
}), () => ({
|
|
36592
|
-
work: "bud:beads",
|
|
36593
|
-
threads: "bud:beads"
|
|
36594
|
-
}));
|
|
36595
|
-
/** Task presentation, chosen separately from its storage. */
|
|
36596
|
-
var BudDesignsSchema = defaulted(settings("designs", {
|
|
36597
|
-
tasks: defaulted(select((value) => value === "bud:beads" ? enum_(["bud:beads"]) : modulePath("designs.tasks", false)), "bud:beads"),
|
|
36598
|
-
pages: defaulted(enum_(["path", "title"]), "path")
|
|
36599
|
-
}), () => ({
|
|
36600
|
-
tasks: "bud:beads",
|
|
36601
|
-
pages: "path"
|
|
36602
|
-
}));
|
|
36603
|
-
//#endregion
|
|
36604
|
-
//#region ../project/src/lib/picker.ts
|
|
36605
|
-
var builtIn = /* @__PURE__ */ new Set([
|
|
36606
|
-
"bud:dom",
|
|
36607
|
-
"bud:react",
|
|
36608
|
-
"bud:react-grab"
|
|
36843
|
+
String$1.check(isPattern(/^[A-Za-z0-9][A-Za-z0-9._-]*$/));
|
|
36844
|
+
var mode = Literals([
|
|
36845
|
+
"100644",
|
|
36846
|
+
"100755",
|
|
36847
|
+
"120000"
|
|
36609
36848
|
]);
|
|
36610
|
-
|
|
36611
|
-
|
|
36612
|
-
|
|
36613
|
-
|
|
36614
|
-
|
|
36615
|
-
|
|
36616
|
-
|
|
36617
|
-
|
|
36618
|
-
|
|
36619
|
-
|
|
36620
|
-
|
|
36621
|
-
|
|
36622
|
-
|
|
36623
|
-
|
|
36624
|
-
|
|
36625
|
-
}
|
|
36849
|
+
Struct({
|
|
36850
|
+
name: text.check(isPattern(/^[^\r\n<>]+$/)),
|
|
36851
|
+
email: text.check(isPattern(/^[^\r\n<>]+$/))
|
|
36852
|
+
});
|
|
36853
|
+
Struct({
|
|
36854
|
+
ref: ref$1,
|
|
36855
|
+
expectedHead: NullOr(oid),
|
|
36856
|
+
files: ArraySchema(Struct({
|
|
36857
|
+
path: path$1,
|
|
36858
|
+
contents: Union([
|
|
36859
|
+
String$1,
|
|
36860
|
+
Uint8Array$1,
|
|
36861
|
+
Null
|
|
36862
|
+
]),
|
|
36863
|
+
mode: optional$1(mode)
|
|
36864
|
+
})).check(isMaxLength(1e4)),
|
|
36865
|
+
message: text,
|
|
36866
|
+
extraParents: optional$1(ArraySchema(oid).check(isMaxLength(16)))
|
|
36626
36867
|
});
|
|
36627
|
-
|
|
36628
|
-
|
|
36629
|
-
|
|
36630
|
-
|
|
36631
|
-
|
|
36632
|
-
|
|
36633
|
-
|
|
36634
|
-
|
|
36635
|
-
|
|
36636
|
-
|
|
36637
|
-
}
|
|
36638
|
-
return fail(readyMessage, context.path);
|
|
36868
|
+
Struct({
|
|
36869
|
+
...Struct({
|
|
36870
|
+
base: optional$1(oid),
|
|
36871
|
+
target: optional$1(oid),
|
|
36872
|
+
staged: optional$1(Boolean$1),
|
|
36873
|
+
paths: optional$1(ArraySchema(path$1).check(isMaxLength(1e4)))
|
|
36874
|
+
}).fields,
|
|
36875
|
+
context: optional$1(Int$1.check(isBetween({
|
|
36876
|
+
minimum: 0,
|
|
36877
|
+
maximum: 1e3
|
|
36878
|
+
})))
|
|
36639
36879
|
});
|
|
36640
|
-
|
|
36641
|
-
|
|
36642
|
-
|
|
36643
|
-
|
|
36644
|
-
|
|
36645
|
-
|
|
36646
|
-
|
|
36647
|
-
}
|
|
36648
|
-
|
|
36649
|
-
|
|
36650
|
-
|
|
36651
|
-
}
|
|
36652
|
-
var ViewportsSchema = createSchema((value, context) => {
|
|
36653
|
-
if (!Array.isArray(value) || value.length === 0 || value.length > 4) return fail(viewportsMessage, context.path);
|
|
36654
|
-
let viewports = [];
|
|
36655
|
-
for (let entry of value) {
|
|
36656
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return fail(viewportsMessage, context.path);
|
|
36657
|
-
let { name, width, height, mobile, ...rest } = entry;
|
|
36658
|
-
let size = (pixels, from, to) => typeof pixels === "number" && Number.isInteger(pixels) && pixels >= from && pixels <= to;
|
|
36659
|
-
if (Object.keys(rest).length || typeof name !== "string" || !name.trim() || name.length > 40 || !viewportId(name) || !size(width, 240, 3840) || height !== void 0 && !size(height, 240, 4e3) || mobile !== void 0 && typeof mobile !== "boolean") return fail(viewportsMessage, context.path);
|
|
36660
|
-
let phone = mobile ?? width < 800;
|
|
36661
|
-
viewports.push({
|
|
36662
|
-
id: viewportId(name),
|
|
36663
|
-
name: name.trim(),
|
|
36664
|
-
width,
|
|
36665
|
-
height: height ?? (phone ? Math.round(width * 844 / 390) : 900),
|
|
36666
|
-
mobile: phone
|
|
36667
|
-
});
|
|
36668
|
-
}
|
|
36669
|
-
if (new Set(viewports.map((viewport) => viewport.id)).size !== viewports.length) return fail(viewportsMessage, context.path);
|
|
36670
|
-
return { value: viewports };
|
|
36880
|
+
Struct({ environment: optional$1(Record(String$1, Union([String$1, Undefined]))) });
|
|
36881
|
+
Struct({
|
|
36882
|
+
commit: oid,
|
|
36883
|
+
path: path$1,
|
|
36884
|
+
limit: Int$1.check(isBetween({
|
|
36885
|
+
minimum: 1,
|
|
36886
|
+
maximum: 101
|
|
36887
|
+
})),
|
|
36888
|
+
skip: optional$1(Int$1.check(isBetween({
|
|
36889
|
+
minimum: 0,
|
|
36890
|
+
maximum: 1e4
|
|
36891
|
+
})))
|
|
36671
36892
|
});
|
|
36672
|
-
|
|
36673
|
-
|
|
36674
|
-
|
|
36675
|
-
|
|
36676
|
-
|
|
36677
|
-
|
|
36678
|
-
|
|
36679
|
-
|
|
36680
|
-
|
|
36681
|
-
|
|
36682
|
-
themes: defaultThemes(),
|
|
36683
|
-
viewports: defaultViewports(),
|
|
36684
|
-
ready: null,
|
|
36685
|
-
readyTimeout: 6e4,
|
|
36686
|
-
css: ""
|
|
36893
|
+
Struct({
|
|
36894
|
+
from: revision,
|
|
36895
|
+
limit: Int$1.check(isBetween({
|
|
36896
|
+
minimum: 1,
|
|
36897
|
+
maximum: 500
|
|
36898
|
+
}))
|
|
36899
|
+
});
|
|
36900
|
+
Int$1.check(isBetween({
|
|
36901
|
+
minimum: 1,
|
|
36902
|
+
maximum: 1e3
|
|
36687
36903
|
}));
|
|
36688
36904
|
//#endregion
|
|
36689
|
-
//#region ../
|
|
36905
|
+
//#region ../git/src/lib/git.ts
|
|
36906
|
+
/** Opens the Git repository that contains a directory. */
|
|
36907
|
+
var Git = class extends Service()("@bud.sh/git/Git") {};
|
|
36908
|
+
//#endregion
|
|
36909
|
+
//#region ../live/src/server/hub.ts
|
|
36690
36910
|
/**
|
|
36691
|
-
*
|
|
36692
|
-
*
|
|
36693
|
-
*
|
|
36694
|
-
* @returns Named patterns confined to the screen's path.
|
|
36911
|
+
* Orders every published change into one stream per companion. Clients resume from their
|
|
36912
|
+
* last event id; anything older than the replay buffer, or from an earlier companion,
|
|
36913
|
+
* receives a resync instead.
|
|
36695
36914
|
*/
|
|
36696
|
-
|
|
36697
|
-
|
|
36698
|
-
|
|
36699
|
-
|
|
36700
|
-
|
|
36701
|
-
|
|
36702
|
-
|
|
36703
|
-
|
|
36704
|
-
|
|
36705
|
-
|
|
36706
|
-
visit(entry, name);
|
|
36707
|
-
continue;
|
|
36708
|
-
}
|
|
36709
|
-
if (!["GET", "ANY"].includes(entry.method) || typeof entry.href !== "function" || typeof entry.pattern?.source !== "string") throw new Error(`Screen route ${name} must be a GET route.`);
|
|
36710
|
-
let pattern = RoutePattern.parse(entry.pattern.source);
|
|
36711
|
-
let parts = pattern.toJSON();
|
|
36712
|
-
let pathname = `/${parts.pathname}`;
|
|
36713
|
-
if (parts.protocol || parts.hostname || parts.port || parts.pathname.split("/").some((segment) => /^\.{1,2}$/.test(decodeURIComponent(segment))) || pathname !== path && !pathname.startsWith(`${path}/`) && !pathname.startsWith(`${path}(/`)) throw new Error(`Screen route ${name} must stay under ${path}.`);
|
|
36714
|
-
result.push({
|
|
36715
|
-
name,
|
|
36716
|
-
pattern: pattern.source
|
|
36717
|
-
});
|
|
36718
|
-
}
|
|
36719
|
-
seen.delete(value);
|
|
36915
|
+
var LiveHub = class {
|
|
36916
|
+
epoch = randomBytes(8).toString("hex");
|
|
36917
|
+
#sequence = 0;
|
|
36918
|
+
#buffer = [];
|
|
36919
|
+
#replay;
|
|
36920
|
+
#heartbeatMs;
|
|
36921
|
+
#listeners = /* @__PURE__ */ new Set();
|
|
36922
|
+
constructor(options = {}) {
|
|
36923
|
+
this.#replay = options.replay ?? 1024;
|
|
36924
|
+
this.#heartbeatMs = options.heartbeatMs ?? 15e3;
|
|
36720
36925
|
}
|
|
36721
|
-
|
|
36722
|
-
|
|
36723
|
-
|
|
36724
|
-
|
|
36926
|
+
/**
|
|
36927
|
+
* Sends a change to every connected client.
|
|
36928
|
+
* @param topic - The domain, such as `previews` or `files`.
|
|
36929
|
+
* @param data - JSON data each domain validates with its own schema.
|
|
36930
|
+
*/
|
|
36931
|
+
publish(topic, data) {
|
|
36932
|
+
let event = {
|
|
36933
|
+
id: `${this.epoch}:${++this.#sequence}`,
|
|
36934
|
+
topic,
|
|
36935
|
+
data
|
|
36936
|
+
};
|
|
36937
|
+
this.#buffer.push(event);
|
|
36938
|
+
if (this.#buffer.length > this.#replay) this.#buffer.shift();
|
|
36939
|
+
for (let listener of [...this.#listeners]) listener(event);
|
|
36940
|
+
}
|
|
36941
|
+
/**
|
|
36942
|
+
* Opens one client's `text/event-stream`.
|
|
36943
|
+
* @param lastEventId - The client's `Last-Event-ID`, if it is resuming.
|
|
36944
|
+
* @param signal - The request's lifetime; aborting closes the stream.
|
|
36945
|
+
* @returns The response body.
|
|
36946
|
+
*/
|
|
36947
|
+
open(lastEventId, signal) {
|
|
36948
|
+
let encoder = new TextEncoder();
|
|
36949
|
+
let close = () => {};
|
|
36950
|
+
return new ReadableStream({
|
|
36951
|
+
start: (controller) => {
|
|
36952
|
+
let send = (text) => controller.enqueue(encoder.encode(text));
|
|
36953
|
+
let write = (event) => send(`id: ${event.id}\nevent: ${event.topic}\ndata: ${JSON.stringify(event.data)}\n\n`);
|
|
36954
|
+
let missed = this.#since(lastEventId);
|
|
36955
|
+
if (missed) for (let event of missed) write(event);
|
|
36956
|
+
else write({
|
|
36957
|
+
id: `${this.epoch}:${this.#sequence}`,
|
|
36958
|
+
topic: RESYNC,
|
|
36959
|
+
data: null
|
|
36960
|
+
});
|
|
36961
|
+
this.#listeners.add(write);
|
|
36962
|
+
let heartbeat = setInterval(() => send(": heartbeat\n\n"), this.#heartbeatMs);
|
|
36963
|
+
close = () => {
|
|
36964
|
+
clearInterval(heartbeat);
|
|
36965
|
+
this.#listeners.delete(write);
|
|
36966
|
+
try {
|
|
36967
|
+
controller.close();
|
|
36968
|
+
} catch {}
|
|
36969
|
+
};
|
|
36970
|
+
if (signal.aborted) close();
|
|
36971
|
+
else signal.addEventListener("abort", close, { once: true });
|
|
36972
|
+
},
|
|
36973
|
+
cancel: () => close()
|
|
36974
|
+
});
|
|
36975
|
+
}
|
|
36976
|
+
#since(lastEventId) {
|
|
36977
|
+
let last = lastEventId ? parseLiveId(lastEventId) : null;
|
|
36978
|
+
if (!last || last.epoch !== this.epoch || last.sequence > this.#sequence) return null;
|
|
36979
|
+
let oldest = this.#buffer[0] ? parseLiveId(this.#buffer[0].id).sequence : this.#sequence + 1;
|
|
36980
|
+
if (last.sequence < oldest - 1) return null;
|
|
36981
|
+
return this.#buffer.filter((event) => parseLiveId(event.id).sequence > last.sequence);
|
|
36982
|
+
}
|
|
36983
|
+
};
|
|
36725
36984
|
//#endregion
|
|
36726
|
-
//#region ../
|
|
36727
|
-
/**
|
|
36728
|
-
|
|
36729
|
-
* @returns The built-in screen, ready to relabel by spreading.
|
|
36730
|
-
*/
|
|
36731
|
-
function pages() {
|
|
36732
|
-
return {
|
|
36733
|
-
id: "pages",
|
|
36734
|
-
label: "Pages",
|
|
36735
|
-
icon: "browser",
|
|
36736
|
-
path: "/pages",
|
|
36737
|
-
module: "bud:pages"
|
|
36738
|
-
};
|
|
36739
|
-
}
|
|
36740
|
-
/**
|
|
36741
|
-
* Bud's canvas of every route's screenshot.
|
|
36742
|
-
* @returns The built-in screen, ready to relabel by spreading.
|
|
36743
|
-
*/
|
|
36744
|
-
function canvas() {
|
|
36745
|
-
return {
|
|
36746
|
-
id: "canvas",
|
|
36747
|
-
label: "Canvas",
|
|
36748
|
-
icon: "grid",
|
|
36749
|
-
path: "/canvas",
|
|
36750
|
-
module: "bud:canvas"
|
|
36751
|
-
};
|
|
36752
|
-
}
|
|
36753
|
-
/**
|
|
36754
|
-
* Bud's Beads task board.
|
|
36755
|
-
* @returns The built-in screen, ready to relabel by spreading.
|
|
36756
|
-
*/
|
|
36757
|
-
function tasks() {
|
|
36758
|
-
return {
|
|
36759
|
-
id: "tasks",
|
|
36760
|
-
label: "Tasks",
|
|
36761
|
-
icon: "tasks",
|
|
36762
|
-
path: "/tasks",
|
|
36763
|
-
module: "bud:tasks"
|
|
36764
|
-
};
|
|
36765
|
-
}
|
|
36985
|
+
//#region ../live/src/server/service.ts
|
|
36986
|
+
/** The companion's ordered live channel: publishes changes and opens client streams. */
|
|
36987
|
+
var LiveChannel = class extends Service()("@bud.sh/live/LiveChannel") {};
|
|
36766
36988
|
/**
|
|
36767
|
-
*
|
|
36768
|
-
* @
|
|
36989
|
+
* One ordered live channel per companion.
|
|
36990
|
+
* @param options - Replay buffer and heartbeat settings.
|
|
36991
|
+
* @returns The LiveChannel layer.
|
|
36769
36992
|
*/
|
|
36770
|
-
function
|
|
36771
|
-
return {
|
|
36772
|
-
|
|
36773
|
-
label: "Files",
|
|
36774
|
-
icon: "files",
|
|
36775
|
-
path: "/files",
|
|
36776
|
-
module: "bud:files"
|
|
36777
|
-
};
|
|
36778
|
-
}
|
|
36779
|
-
/** The screens shown when the config doesn't list any. */
|
|
36780
|
-
var defaultScreens = [
|
|
36781
|
-
pages(),
|
|
36782
|
-
canvas(),
|
|
36783
|
-
tasks(),
|
|
36784
|
-
files()
|
|
36785
|
-
];
|
|
36786
|
-
var builtIns$1 = new Map(defaultScreens.map((screen) => [screen.module, screen]));
|
|
36787
|
-
/** First path segments owned by Bud's routes, its development server and its public assets. */
|
|
36788
|
-
var reservedScreenPaths = [
|
|
36789
|
-
"/comments",
|
|
36790
|
-
...defaultScreens.map((screen) => screen.path),
|
|
36791
|
-
"/api",
|
|
36792
|
-
"/health",
|
|
36793
|
-
"/__bud",
|
|
36794
|
-
"/assets",
|
|
36795
|
-
"/app",
|
|
36796
|
-
"/src",
|
|
36797
|
-
"/node_modules",
|
|
36798
|
-
"/agents",
|
|
36799
|
-
"/brand",
|
|
36800
|
-
"/fonts",
|
|
36801
|
-
"/frameworks",
|
|
36802
|
-
"/licenses"
|
|
36803
|
-
];
|
|
36804
|
-
var ScreenSchema = check(described(any().refine((value) => !!value && typeof value === "object"), "Each screen must be an object."), ({ id, label, icon = "layers", module, path }) => {
|
|
36805
|
-
if (typeof id !== "string" || !/^[a-z][a-z0-9-]{0,31}$/.test(id)) return "Screen ids are 1-32 lowercase letters, digits and hyphens.";
|
|
36806
|
-
if (typeof label !== "string" || !label.trim()) return `Screen ${id} needs a label.`;
|
|
36807
|
-
if (typeof icon !== "string" || !icon) return `Screen ${id} needs an icon name.`;
|
|
36808
|
-
if (typeof module !== "string") return `Screen ${id} needs a module.`;
|
|
36809
|
-
let builtIn = builtIns$1.get(module);
|
|
36810
|
-
path ??= builtIn?.path ?? `/${id}`;
|
|
36811
|
-
if (builtIn) {
|
|
36812
|
-
if (id !== builtIn.id || path !== builtIn.path) return `${module} keeps id "${builtIn.id}" and path "${builtIn.path}".`;
|
|
36813
|
-
return;
|
|
36814
|
-
}
|
|
36815
|
-
if (!/^\.\/[^\0\\]+\.(?:ts|tsx|js|jsx)$/.test(module)) return `Screen ${id} must be a ./ module ending in .ts, .tsx, .js or .jsx.`;
|
|
36816
|
-
if (typeof path !== "string" || !/^\/[a-z0-9][a-z0-9-]*$/.test(path)) return `Screen ${id} needs one static path segment, such as /${id}.`;
|
|
36817
|
-
if (defaultScreens.some((screen) => screen.id === id)) return `Screen id "${id}" is reserved for a built-in screen.`;
|
|
36818
|
-
if (reservedScreenPaths.includes(path)) return `Bud reserves the path ${path}.`;
|
|
36819
|
-
});
|
|
36820
|
-
/** Bud's navigation in order; file and icon checks need the app and happen at startup. */
|
|
36821
|
-
var BudScreensSchema = defaulted(delegate(described(array(any()).refine((screens) => screens.length > 0), "screens must list at least one screen."), (screens) => {
|
|
36822
|
-
let ids = /* @__PURE__ */ new Set();
|
|
36823
|
-
let paths = /* @__PURE__ */ new Set();
|
|
36824
|
-
return screens.map((value) => {
|
|
36825
|
-
let input = parseConfig(ScreenSchema, value);
|
|
36826
|
-
let { id, label, icon = "layers", module } = input;
|
|
36827
|
-
let path = input.path ?? builtIns$1.get(module)?.path ?? `/${id}`;
|
|
36828
|
-
if (ids.has(id) || paths.has(path)) throw new Error("Screen ids and paths must be unique.");
|
|
36829
|
-
ids.add(id);
|
|
36830
|
-
paths.add(path);
|
|
36831
|
-
if (input.routes !== void 0) resolveScreenRoutes(input.routes, path);
|
|
36993
|
+
function liveChannelLayer(options = {}) {
|
|
36994
|
+
return sync$1(LiveChannel, () => {
|
|
36995
|
+
let hub = new LiveHub(options);
|
|
36832
36996
|
return {
|
|
36833
|
-
|
|
36834
|
-
|
|
36835
|
-
|
|
36836
|
-
path,
|
|
36837
|
-
module,
|
|
36838
|
-
...input.routes === void 0 ? {} : { routes: input.routes }
|
|
36997
|
+
epoch: hub.epoch,
|
|
36998
|
+
publish: (topic, data) => sync(() => hub.publish(topic, data)),
|
|
36999
|
+
open: (lastEventId, signal) => hub.open(lastEventId, signal)
|
|
36839
37000
|
};
|
|
36840
37001
|
});
|
|
36841
|
-
}), () => defaultScreens.map((screen) => ({ ...screen })));
|
|
36842
|
-
//#endregion
|
|
36843
|
-
//#region ../project/src/lib/config.ts
|
|
36844
|
-
var objectLike = (value) => !!value && typeof value === "object";
|
|
36845
|
-
function section(message, shape) {
|
|
36846
|
-
let schema = object(shape);
|
|
36847
|
-
return createSchema((value, context) => objectLike(value) ? schema["~run"](Array.isArray(value) ? { ...value } : value, context) : fail(message, context.path));
|
|
36848
|
-
}
|
|
36849
|
-
var ConfigFields = check(section("Bud config must export an object.", {
|
|
36850
|
-
routes: described(array(any()).refine((routes) => routes.length > 0), "bud.config must define at least one route."),
|
|
36851
|
-
name: optional(described(string().refine((name) => !!name.trim()), "name must be a nonempty string.")),
|
|
36852
|
-
target: optional(section("target must be an object.", {
|
|
36853
|
-
origin: any(),
|
|
36854
|
-
port: any()
|
|
36855
|
-
})),
|
|
36856
|
-
comments: optional(section("comments must be an object.", { remote: any() })),
|
|
36857
|
-
port: any(),
|
|
36858
|
-
screens: any(),
|
|
36859
|
-
storage: any(),
|
|
36860
|
-
designs: any(),
|
|
36861
|
-
previews: any(),
|
|
36862
|
-
files: any(),
|
|
36863
|
-
logo: any(),
|
|
36864
|
-
picker: any(),
|
|
36865
|
-
branches: any(),
|
|
36866
|
-
flows: any()
|
|
36867
|
-
}), ({ target }) => {
|
|
36868
|
-
if (target?.port === 0) return "target.port must be a listening port, not zero.";
|
|
36869
|
-
if (target?.origin !== void 0 && (typeof target.origin !== "string" || !target.origin.trim())) return "target.origin must be an HTTP(S) origin.";
|
|
36870
|
-
});
|
|
36871
|
-
var RouteSchema = check(any().transform((input) => typeof input === "string" ? {
|
|
36872
|
-
path: input,
|
|
36873
|
-
title: input === "/" ? "Home" : input
|
|
36874
|
-
} : input), (route) => {
|
|
36875
|
-
if (!route || typeof route.path !== "string" || !route.path.startsWith("/") || route.path.startsWith("//")) return "Canvas routes must be local paths beginning with a single /.";
|
|
36876
|
-
if (new URL(route.path, "http://bud.invalid").origin !== "http://bud.invalid" || route.path.includes("\\")) return "Canvas routes must stay on the target origin.";
|
|
36877
|
-
let id = route.id ?? routePath(route.path);
|
|
36878
|
-
if (typeof id !== "string" || !id.trim()) return "Canvas route IDs and paths must be unique.";
|
|
36879
|
-
if (typeof route.title !== "string" || !route.title.trim()) return "Each route needs a title.";
|
|
36880
|
-
if (route.pattern !== void 0 && (typeof route.pattern !== "string" || !route.pattern.startsWith("/"))) return "A route pattern must be a path beginning with /.";
|
|
36881
|
-
if (route.group !== void 0 && (typeof route.group !== "string" || !route.group.trim() || route.group.length > 120)) return "A route group must be a short name.";
|
|
36882
|
-
if (route.ready !== void 0 && route.ready !== false && !parseSafe(PreviewReadySchema, route.ready).success) return "A route ready must be false, a CSS selector, { selector } or { expression }.";
|
|
36883
|
-
}).transform((route) => {
|
|
36884
|
-
let path = routePath(route.path);
|
|
36885
|
-
return {
|
|
36886
|
-
id: route.id ?? path,
|
|
36887
|
-
path,
|
|
36888
|
-
title: route.title,
|
|
36889
|
-
...route.pattern === void 0 ? {} : { pattern: route.pattern },
|
|
36890
|
-
...route.group === void 0 ? {} : { group: route.group.trim() },
|
|
36891
|
-
...route.ready === void 0 ? {} : { ready: route.ready === false ? null : parseConfig(PreviewReadySchema, route.ready) }
|
|
36892
|
-
};
|
|
36893
|
-
});
|
|
36894
|
-
function routePath(path) {
|
|
36895
|
-
let url = new URL(path, "http://bud.invalid");
|
|
36896
|
-
let search = url.searchParams.toString();
|
|
36897
|
-
return `${url.pathname}${search ? `?${search}` : ""}${url.hash}`;
|
|
36898
37002
|
}
|
|
36899
|
-
/** Canvas routes with unique IDs and normalized paths. */
|
|
36900
|
-
var BudRoutesSchema = check(list(RouteSchema, "bud.config must define at least one route.", 1), (routes) => new Set(routes.map((route) => route.id)).size !== routes.length || new Set(routes.map((route) => route.path)).size !== routes.length ? "Canvas route IDs and paths must be unique." : void 0);
|
|
36901
|
-
var port = (value) => value === void 0 || Number.isInteger(value) && value >= 0 && value <= 65535;
|
|
36902
|
-
var remote = (value) => typeof value === "string" && /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(value);
|
|
36903
|
-
/** A project's config, validated and normalized before Bud starts any listeners. */
|
|
36904
|
-
var BudConfigSchema = delegate(check(delegate(check(delegate(check(delegate(ConfigFields, (config) => ({
|
|
36905
|
-
...config,
|
|
36906
|
-
routes: parseConfig(BudRoutesSchema, config.routes)
|
|
36907
|
-
})), ({ port: companion, target }) => port(companion ?? 0) && port(target?.port) ? void 0 : "Ports must be integers between 0 and 65535."), ({ target, ...config }) => {
|
|
36908
|
-
let origin = target?.origin ?? (target?.port ? `http://127.0.0.1:${target.port}` : void 0);
|
|
36909
|
-
return {
|
|
36910
|
-
...config,
|
|
36911
|
-
targetOrigin: origin ? normalizeOrigin(origin, "target.origin must be an HTTP(S) origin without a path or credentials.") : origin
|
|
36912
|
-
};
|
|
36913
|
-
}), ({ comments }) => remote(comments?.remote ?? "origin") ? void 0 : "comments.remote must be a Git remote name."), (config) => {
|
|
36914
|
-
let flows = parseConfig(BudFlowsSchema, config.flows);
|
|
36915
|
-
let steps = flows.flatMap((flow) => flow.steps.map((step) => flowStepRoute(flow, step)));
|
|
36916
|
-
let home = config.routes.findIndex((route) => route.path === "/") + 1;
|
|
36917
|
-
return {
|
|
36918
|
-
...config,
|
|
36919
|
-
flows,
|
|
36920
|
-
routes: [
|
|
36921
|
-
...config.routes.slice(0, home),
|
|
36922
|
-
...steps,
|
|
36923
|
-
...config.routes.slice(home)
|
|
36924
|
-
]
|
|
36925
|
-
};
|
|
36926
|
-
}), ({ routes }) => new Set(routes.map((route) => route.id)).size !== routes.length || new Set(routes.map((route) => route.path)).size !== routes.length ? "A flow step must not reuse a route’s id or path." : void 0), (config) => ({
|
|
36927
|
-
name: config.name ?? "Your project",
|
|
36928
|
-
routes: config.routes,
|
|
36929
|
-
flows: config.flows,
|
|
36930
|
-
port: config.port ?? 0,
|
|
36931
|
-
targetOrigin: config.targetOrigin,
|
|
36932
|
-
remote: config.comments?.remote ?? "origin",
|
|
36933
|
-
screens: parseConfig(BudScreensSchema, config.screens),
|
|
36934
|
-
storage: parseConfig(BudStorageSchema, config.storage),
|
|
36935
|
-
designs: parseConfig(BudDesignsSchema, config.designs),
|
|
36936
|
-
previews: parseConfig(BudPreviewsSchema, config.previews),
|
|
36937
|
-
files: parseConfig(BudFilesSchema, config.files),
|
|
36938
|
-
logo: parseConfig(BudLogoSchema, config.logo),
|
|
36939
|
-
picker: parseConfig(BudPickerSchema, config.picker),
|
|
36940
|
-
branches: parseConfig(BudBranchesSchema, config.branches)
|
|
36941
|
-
}));
|
|
36942
37003
|
/**
|
|
36943
|
-
*
|
|
36944
|
-
*
|
|
36945
|
-
* @
|
|
37004
|
+
* Publishes every element of a domain's change stream on the live channel, for the lifetime
|
|
37005
|
+
* of the layer that forks it.
|
|
37006
|
+
* @param topic - The topic clients subscribe to.
|
|
37007
|
+
* @param changes - The domain's change stream.
|
|
37008
|
+
* @returns An effect to fork in a scoped layer.
|
|
36946
37009
|
*/
|
|
36947
|
-
function
|
|
36948
|
-
return
|
|
37010
|
+
function publishChanges(topic, changes) {
|
|
37011
|
+
return gen(function* () {
|
|
37012
|
+
let channel = yield* LiveChannel;
|
|
37013
|
+
yield* changes.pipe(runForEach((change) => channel.publish(topic, change)), ignore$2, forkScoped);
|
|
37014
|
+
});
|
|
36949
37015
|
}
|
|
37016
|
+
//#endregion
|
|
37017
|
+
//#region ../project/src/server/errors.ts
|
|
37018
|
+
/** Expected project startup/configuration failure; nested causes remain server-only. */
|
|
37019
|
+
var ProjectError = class extends TaggedError("ProjectError") {};
|
|
37020
|
+
//#endregion
|
|
37021
|
+
//#region ../project/src/server/modules.ts
|
|
36950
37022
|
/**
|
|
36951
|
-
*
|
|
36952
|
-
* @param
|
|
36953
|
-
* @param
|
|
36954
|
-
* @
|
|
37023
|
+
* Confines a validated config module to a regular, bounded file in its project.
|
|
37024
|
+
* @param root - The trusted consumer directory.
|
|
37025
|
+
* @param module - A project-relative module already validated by the config resolver.
|
|
37026
|
+
* @param options - The config setting and whether its source runs on the server or browser.
|
|
37027
|
+
* @returns The canonical filename, without evaluating its code.
|
|
36955
37028
|
*/
|
|
36956
|
-
function
|
|
36957
|
-
|
|
36958
|
-
|
|
36959
|
-
|
|
37029
|
+
function projectModuleFile(root, module, options) {
|
|
37030
|
+
return gen(function* () {
|
|
37031
|
+
let fs = yield* FileSystem;
|
|
37032
|
+
let path = yield* Path$1;
|
|
37033
|
+
let { setting, kind } = options;
|
|
37034
|
+
let project = yield* fs.realPath(root).pipe(mapError$2((cause) => new ProjectError({
|
|
37035
|
+
reason: "root",
|
|
37036
|
+
message: "Cannot open the module project root.",
|
|
37037
|
+
cause
|
|
37038
|
+
})));
|
|
37039
|
+
let file = yield* fs.realPath(path.resolve(project, module)).pipe(mapError$2((cause) => new ProjectError({
|
|
37040
|
+
reason: "read",
|
|
37041
|
+
message: `${setting} cannot find ${module} in the project.`,
|
|
37042
|
+
cause
|
|
37043
|
+
})));
|
|
37044
|
+
let relative = path.relative(project, file);
|
|
37045
|
+
if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative) || relative.split(path.sep).includes("node_modules")) return yield* new ProjectError({
|
|
37046
|
+
reason: "invalid",
|
|
37047
|
+
message: `${setting} must resolve to a project-owned file outside node_modules.`
|
|
37048
|
+
});
|
|
37049
|
+
if (kind === "server" && !file.endsWith(".server.ts")) return yield* new ProjectError({
|
|
37050
|
+
reason: "invalid",
|
|
37051
|
+
message: `${setting} must resolve to a .server.ts module.`
|
|
37052
|
+
});
|
|
37053
|
+
if (kind === "browser" && (!/\.(?:ts|tsx|js|jsx)$/.test(file) || /\.server\.[cm]?[jt]sx?$/.test(file))) return yield* new ProjectError({
|
|
37054
|
+
reason: "invalid",
|
|
37055
|
+
message: `${setting} must resolve to a browser module.`
|
|
37056
|
+
});
|
|
37057
|
+
if ((yield* fs.stat(file).pipe(mapError$2((cause) => new ProjectError({
|
|
37058
|
+
reason: "read",
|
|
37059
|
+
message: `${setting} cannot inspect ${module}.`,
|
|
37060
|
+
cause
|
|
37061
|
+
})))).type !== "File") return yield* new ProjectError({
|
|
37062
|
+
reason: "invalid",
|
|
37063
|
+
message: `${setting} must be a regular module file.`
|
|
37064
|
+
});
|
|
37065
|
+
yield* readText(file, { maxBytes: 262144 }).pipe(mapError$2((cause) => new ProjectError({
|
|
37066
|
+
reason: "read",
|
|
37067
|
+
message: `${setting} must be a readable module no larger than 256 KiB.`,
|
|
37068
|
+
cause
|
|
37069
|
+
})));
|
|
37070
|
+
return file;
|
|
37071
|
+
});
|
|
36960
37072
|
}
|
|
36961
37073
|
//#endregion
|
|
36962
37074
|
//#region ../project/src/server/config.ts
|
|
@@ -37361,10 +37473,10 @@ function ProjectLiveStatus(handle) {
|
|
|
37361
37473
|
//#endregion
|
|
37362
37474
|
//#region app/client/screens.ts
|
|
37363
37475
|
var builtIns = {
|
|
37364
|
-
"bud:pages": () => import("./assets/pages-
|
|
37365
|
-
"bud:canvas": () => import("./assets/canvas-
|
|
37366
|
-
"bud:tasks": () => import("./assets/tasks-
|
|
37367
|
-
"bud:files": () => import("./assets/files-
|
|
37476
|
+
"bud:pages": () => import("./assets/pages-D6VkmdfQ.js"),
|
|
37477
|
+
"bud:canvas": () => import("./assets/canvas-D_F8SIlm.js"),
|
|
37478
|
+
"bud:tasks": () => import("./assets/tasks-A1_bpb3_.js"),
|
|
37479
|
+
"bud:files": () => import("./assets/files-xazOrNCX.js")
|
|
37368
37480
|
};
|
|
37369
37481
|
/**
|
|
37370
37482
|
* Imports a screen module: a built-in by its key, any other by the URL the server gave it.
|
|
@@ -37450,7 +37562,7 @@ function ScreenHost(handle) {
|
|
|
37450
37562
|
} catch (cause) {
|
|
37451
37563
|
if (handle.signal.aborted) return;
|
|
37452
37564
|
console.error(cause);
|
|
37453
|
-
error = cause instanceof Error && cause.message ? cause.message :
|
|
37565
|
+
error = cause instanceof Error && cause.message ? cause.message : t("app.client.screenHost.couldNotOpen", { label });
|
|
37454
37566
|
}
|
|
37455
37567
|
handle.update();
|
|
37456
37568
|
}
|
|
@@ -37462,7 +37574,7 @@ function ScreenHost(handle) {
|
|
|
37462
37574
|
let context = opened?.context;
|
|
37463
37575
|
let routeError = "";
|
|
37464
37576
|
if (opened && context) try {
|
|
37465
|
-
if (location.routes && !matchScreenRoutes(location.routes, location.href)) throw new Error(
|
|
37577
|
+
if (location.routes && !matchScreenRoutes(location.routes, location.href)) throw new Error(t("app.client.screenHost.noRouteMatches", { href: location.href }));
|
|
37466
37578
|
context = matchScreenContext(opened.screen.routes, {
|
|
37467
37579
|
...context,
|
|
37468
37580
|
active: handle.props.active,
|
|
@@ -37470,7 +37582,7 @@ function ScreenHost(handle) {
|
|
|
37470
37582
|
url: new URL(location.href, context.url.origin)
|
|
37471
37583
|
});
|
|
37472
37584
|
} catch (cause) {
|
|
37473
|
-
routeError = cause instanceof Error ? cause.message : "
|
|
37585
|
+
routeError = cause instanceof Error ? cause.message : t("app.client.screenHost.invalidRoute");
|
|
37474
37586
|
}
|
|
37475
37587
|
return /* @__PURE__ */ jsx("div", {
|
|
37476
37588
|
class: "h-full min-h-0",
|
|
@@ -37487,7 +37599,7 @@ function ScreenHost(handle) {
|
|
|
37487
37599
|
class: "max-w-2xl",
|
|
37488
37600
|
role: "alert",
|
|
37489
37601
|
children: [/* @__PURE__ */ jsx(FieldCardHeader, {
|
|
37490
|
-
title:
|
|
37602
|
+
title: t("app.client.screenHost.didntOpen", { label }),
|
|
37491
37603
|
subtitle: error
|
|
37492
37604
|
}), /* @__PURE__ */ jsxs(FieldCardContent, {
|
|
37493
37605
|
class: "flex-col items-start gap-3",
|
|
@@ -37495,17 +37607,17 @@ function ScreenHost(handle) {
|
|
|
37495
37607
|
as: "p",
|
|
37496
37608
|
variant: "ui",
|
|
37497
37609
|
tone: "muted",
|
|
37498
|
-
children: "
|
|
37610
|
+
children: t("app.client.screenHost.fixAndRetry")
|
|
37499
37611
|
}), /* @__PURE__ */ jsx(Button, {
|
|
37500
37612
|
mix: on("click", () => {
|
|
37501
37613
|
if ("error" in module || handle.props.taskDesign && "error" in handle.props.taskDesign) window.location.reload();
|
|
37502
37614
|
else open();
|
|
37503
37615
|
}),
|
|
37504
|
-
children: "
|
|
37616
|
+
children: t("app.client.screenHost.retry")
|
|
37505
37617
|
})]
|
|
37506
37618
|
})]
|
|
37507
37619
|
})
|
|
37508
|
-
}) : /* @__PURE__ */ jsx(Loader, { label:
|
|
37620
|
+
}) : /* @__PURE__ */ jsx(Loader, { label: t("app.client.screenHost.loading", { label }) })
|
|
37509
37621
|
});
|
|
37510
37622
|
};
|
|
37511
37623
|
}
|
|
@@ -37524,11 +37636,11 @@ async function resolveConversation(id, targets, token, signal, fetcher = fetch)
|
|
|
37524
37636
|
id = parseThreadId(id);
|
|
37525
37637
|
let thread = await requestComments(routes.comments.get.href({ id }), token, signal, parseThread, fetcher);
|
|
37526
37638
|
signal.throwIfAborted();
|
|
37527
|
-
if (thread.id !== id) throw new Error("
|
|
37639
|
+
if (thread.id !== id) throw new Error(t("app.client.conversationRoute.anotherDiscussion"));
|
|
37528
37640
|
let destination = conversationDestination(thread);
|
|
37529
37641
|
let target = targets.find((target) => target.module === destination.module);
|
|
37530
|
-
if (!target) throw new Error("
|
|
37531
|
-
if (target.routes && !matchScreenRoutes(target.routes, destination.href)) throw new Error("
|
|
37642
|
+
if (!target) throw new Error(t("app.client.conversationRoute.enableOwningScreen"));
|
|
37643
|
+
if (target.routes && !matchScreenRoutes(target.routes, destination.href)) throw new Error(t("app.client.conversationRoute.locationUnavailable"));
|
|
37532
37644
|
return {
|
|
37533
37645
|
active: target.id,
|
|
37534
37646
|
module: { url: target.module },
|
|
@@ -37603,14 +37715,14 @@ function Workspace(handle) {
|
|
|
37603
37715
|
changed();
|
|
37604
37716
|
try {
|
|
37605
37717
|
let project = handle.props.project;
|
|
37606
|
-
if (!project) throw new Error("
|
|
37718
|
+
if (!project) throw new Error(t("app.client.workspace.connectProjectToOpenDiscussion"));
|
|
37607
37719
|
let next = await resolveConversation(id, handle.props.conversationTargets ?? [], project.token, signal);
|
|
37608
37720
|
if (!signal.aborted && conversation === id) {
|
|
37609
37721
|
presented = next;
|
|
37610
37722
|
hrefs.set(next.active, window.location.pathname);
|
|
37611
37723
|
}
|
|
37612
37724
|
} catch (cause) {
|
|
37613
|
-
if (!signal.aborted) error = cause instanceof Error ? cause.message : "
|
|
37725
|
+
if (!signal.aborted) error = cause instanceof Error ? cause.message : t("app.client.workspace.couldNotOpenDiscussion");
|
|
37614
37726
|
} finally {
|
|
37615
37727
|
if (request === controller && !signal.aborted) {
|
|
37616
37728
|
loading = false;
|
|
@@ -37653,7 +37765,7 @@ function Workspace(handle) {
|
|
|
37653
37765
|
let { pathname } = new URL(href, "http://bud.local");
|
|
37654
37766
|
if (readConversationLocation(href)) return {
|
|
37655
37767
|
href,
|
|
37656
|
-
title: "
|
|
37768
|
+
title: t("app.client.workspace.discussion"),
|
|
37657
37769
|
icon: "message"
|
|
37658
37770
|
};
|
|
37659
37771
|
let match = handle.props.screens.map((item) => ({
|
|
@@ -37815,19 +37927,19 @@ function Workspace(handle) {
|
|
|
37815
37927
|
children: project ? /* @__PURE__ */ jsxs("div", {
|
|
37816
37928
|
class: "h-full min-h-0",
|
|
37817
37929
|
mix: mount,
|
|
37818
|
-
children: [loading ? /* @__PURE__ */ jsx(Loader, { label: "
|
|
37930
|
+
children: [loading ? /* @__PURE__ */ jsx(Loader, { label: t("app.client.workspace.openingDiscussion") }) : error ? /* @__PURE__ */ jsx("div", {
|
|
37819
37931
|
class: "p-3 md:p-4",
|
|
37820
37932
|
children: /* @__PURE__ */ jsxs(FieldCard, {
|
|
37821
37933
|
class: "max-w-2xl",
|
|
37822
37934
|
role: "alert",
|
|
37823
37935
|
children: [/* @__PURE__ */ jsx(FieldCardHeader, {
|
|
37824
|
-
title:
|
|
37936
|
+
title: t("app.client.workspace.discussionDidntOpen"),
|
|
37825
37937
|
subtitle: error
|
|
37826
37938
|
}), /* @__PURE__ */ jsx(FieldCardContent, { children: /* @__PURE__ */ jsx(Button, {
|
|
37827
37939
|
mix: on("click", () => {
|
|
37828
37940
|
if (conversation) resolve(conversation);
|
|
37829
37941
|
}),
|
|
37830
|
-
children: "
|
|
37942
|
+
children: t("app.client.workspace.retry")
|
|
37831
37943
|
}) })]
|
|
37832
37944
|
})
|
|
37833
37945
|
}) : null, [...opened].map(([id, { module, taskDesign, location }]) => /* @__PURE__ */ jsx("div", {
|
|
@@ -37845,12 +37957,12 @@ function Workspace(handle) {
|
|
|
37845
37957
|
}) : /* @__PURE__ */ jsxs(FieldCard, {
|
|
37846
37958
|
class: "max-w-2xl",
|
|
37847
37959
|
children: [/* @__PURE__ */ jsx(FieldCardHeader, {
|
|
37848
|
-
title: "
|
|
37849
|
-
subtitle: "
|
|
37960
|
+
title: t("app.client.workspace.yourProjectSideBySide"),
|
|
37961
|
+
subtitle: t("app.client.workspace.openBudBesideLocalProject")
|
|
37850
37962
|
}), /* @__PURE__ */ jsx(FieldCardContent, { children: /* @__PURE__ */ jsx(Text, {
|
|
37851
37963
|
as: "p",
|
|
37852
37964
|
variant: "ui",
|
|
37853
|
-
children: "
|
|
37965
|
+
children: t("app.client.workspace.startProjectWithBudVitePlugin")
|
|
37854
37966
|
}) })]
|
|
37855
37967
|
})
|
|
37856
37968
|
});
|
|
@@ -38007,7 +38119,7 @@ function createAppController(template, project, runtime) {
|
|
|
38007
38119
|
let title = (conversation ? "Discussion" : screen.label).replace(/[&<>]/g, (character) => `&#${character.charCodeAt(0)};`);
|
|
38008
38120
|
let logo = project?.config.logo;
|
|
38009
38121
|
let shell = logo ? withLogoIcons(template, logo, (theme) => routes.logo.href({ theme })) : template;
|
|
38010
|
-
return new Response(shell.replace("<title>bud.sh</title>", () => `<title>${title} · bud.sh</title>`).replace("<!--app-html-->", () => content), { headers: { "Content-Type": "text/html; charset=utf-8" } });
|
|
38122
|
+
return new Response(shell.replace("<html lang=\"en\"", () => `<html lang="${locale()}"`).replace("<title>bud.sh</title>", () => `<title>${title} · bud.sh</title>`).replace("</head>", () => project ? `${themeStyle(project.config.theme)}</head>` : "</head>").replace("<!--app-html-->", () => content), { headers: { "Content-Type": "text/html; charset=utf-8" } });
|
|
38011
38123
|
}
|
|
38012
38124
|
return createController(routes, { actions: {
|
|
38013
38125
|
conversation({ params, request }) {
|
|
@@ -41139,6 +41251,19 @@ function previewAnchors(root) {
|
|
|
41139
41251
|
};
|
|
41140
41252
|
}
|
|
41141
41253
|
//#endregion
|
|
41254
|
+
//#region app/server/locale.ts
|
|
41255
|
+
var requestLocale = new AsyncLocalStorage();
|
|
41256
|
+
useLocaleSource(() => requestLocale.getStore());
|
|
41257
|
+
/**
|
|
41258
|
+
* Runs a request's handling in its language, so what it renders speaks it.
|
|
41259
|
+
* @param value - The request's locale; English when it isn't one of Bud's.
|
|
41260
|
+
* @param handle - The request's handling.
|
|
41261
|
+
* @returns What the handling returns.
|
|
41262
|
+
*/
|
|
41263
|
+
function inLocale(value, handle) {
|
|
41264
|
+
return requestLocale.run(isLocale(value) ? value : "en", handle);
|
|
41265
|
+
}
|
|
41266
|
+
//#endregion
|
|
41142
41267
|
//#region app/server/hosted/contract.ts
|
|
41143
41268
|
/**
|
|
41144
41269
|
* The screens a hosted Bud shows: Pages as captured pictures, and no Files, which need a
|
|
@@ -41294,7 +41419,10 @@ function createHostedApp(options) {
|
|
|
41294
41419
|
return made;
|
|
41295
41420
|
};
|
|
41296
41421
|
return {
|
|
41297
|
-
fetch: async (request, context) =>
|
|
41422
|
+
fetch: async (request, context) => {
|
|
41423
|
+
let router = routerFor(hostedAuthor(context?.author));
|
|
41424
|
+
return inLocale(context?.locale ?? config.locale, () => router.fetch(request));
|
|
41425
|
+
},
|
|
41298
41426
|
dispose: () => runtime.dispose()
|
|
41299
41427
|
};
|
|
41300
41428
|
}
|
|
@@ -41323,6 +41451,13 @@ async function createNodeHost(options) {
|
|
|
41323
41451
|
return response;
|
|
41324
41452
|
};
|
|
41325
41453
|
let { config } = await (await call("project")).json();
|
|
41454
|
+
if (options.theme) config = {
|
|
41455
|
+
...config,
|
|
41456
|
+
theme: resolveBudConfig({
|
|
41457
|
+
routes: ["/"],
|
|
41458
|
+
theme: options.theme
|
|
41459
|
+
}).theme
|
|
41460
|
+
};
|
|
41326
41461
|
let client = options.client ?? fileURLToPath(new URL("../client/", import.meta.url));
|
|
41327
41462
|
let template = await readFile(join(client, "index.html"), "utf8");
|
|
41328
41463
|
let pictures = httpPreviewStorage(remote("store"), {
|