@bud.sh/app 0.4.1 → 0.5.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-DIKX_HCJ.js → canvas-Cu6R6xxT.js} +1 -1
- package/dist/client/assets/difference-worker-jfTSn-Tw.js +8 -0
- package/dist/client/assets/{edit-B050x0rV.js → edit-DUotSxjS.js} +12 -12
- package/dist/client/assets/files-js6ElfkU.js +1 -0
- package/dist/client/assets/i18n-DxqFr2mi.js +1 -0
- package/dist/client/assets/index-CFFxP0gS.css +2 -0
- package/dist/client/assets/{index-COvyj5zV.js → index-ChANGtl-.js} +2 -2
- package/dist/client/assets/lru-DzKYIXMA.js +1 -0
- package/dist/client/assets/pages-6_lhiQmz.js +1 -0
- package/dist/client/assets/{project-branches-DwKjCwZv.js → project-branches-DO897zM6.js} +1 -1
- package/dist/client/assets/project-comments-DFFKVpia.js +1 -0
- package/dist/client/assets/project-thread-links-NLFWQRD2.js +1 -0
- package/dist/client/assets/{project-threads-DeKXNqeP.js → project-threads-Lay351p4.js} +105 -105
- package/dist/client/assets/{project-work-B7PGQtV9.js → project-work-du0Y-L5Y.js} +1 -1
- package/dist/client/assets/rolldown-runtime-XKyaHtl9.js +1 -0
- package/dist/client/assets/task-design-Avb2RO4w.js +23 -0
- package/dist/client/assets/tasks-CA-E8bbK.js +1 -0
- package/dist/client/assets/{tasks-request-CsUB5__X.js → tasks-request-C55IeuiP.js} +1 -1
- package/dist/client/assets/workspace-Cw5HvX5d.js +2 -0
- package/dist/client/index.html +3 -3
- package/dist/hosted/assets/{canvas-gWj_ocKK.js → canvas-V36732ER.js} +6 -4
- package/dist/hosted/assets/{comment-data-C-rNCB1n.js → comment-data-CF2ZV_3k.js} +4021 -3645
- package/dist/hosted/assets/{files-CAPvbavY.js → files-DjSCjfU7.js} +5 -3
- package/dist/hosted/assets/{pages-DJiOvT-l.js → pages-BzjzeuPu.js} +3 -3
- package/dist/hosted/assets/{project-branches-BgrfdvtL.js → project-branches-BT97J9i7.js} +1 -1
- package/dist/hosted/assets/{project-thread-links-B7uzThL9.js → project-thread-links-BRLgByEe.js} +2 -2
- package/dist/hosted/assets/{screen-D4D9nh4V.js → screen-CKp2C7kx.js} +68 -137
- package/dist/hosted/assets/{tasks-C7aGLzBw.js → tasks-Dj7AyBJB.js} +12 -5
- package/dist/hosted/assets/{tasks-request-CikFdCgP.js → tasks-request-DZob623r.js} +1 -1
- package/dist/hosted/hosted.js +164 -16
- package/dist/node/assets/{src-BdvZ2mYc.js → src-CerrKjID.js} +57 -1
- package/dist/node/design.js +829 -789
- package/dist/node/server.js +25 -3
- package/dist/node-host/assets/{canvas-kXzp80a5.js → canvas-TRUfxfL5.js} +6 -4
- package/dist/node-host/assets/{comment-data-CeuJbJKJ.js → comment-data-DyIZNpfG.js} +4003 -3627
- package/dist/node-host/assets/{files-0YIuvKzb.js → files-Dh2TiQZq.js} +5 -3
- package/dist/node-host/assets/{pages-DOF22SyG.js → pages-BxpjbYhs.js} +3 -3
- package/dist/node-host/assets/{project-branches-BeEwMBIw.js → project-branches-B7HUGiVK.js} +1 -1
- package/dist/node-host/assets/{project-thread-links-Bs_MtfBC.js → project-thread-links-CauJF0Cz.js} +2 -2
- package/dist/node-host/assets/{screen-DRTAKkn3.js → screen-CZXWJEds.js} +68 -137
- package/dist/node-host/assets/{tasks-BA_hcbj5.js → tasks-Bs1IOpeU.js} +12 -5
- package/dist/node-host/assets/{tasks-request-Doc9y_20.js → tasks-request-ZWmbc8fE.js} +1 -1
- package/dist/node-host/node-host.js +164 -16
- package/dist/server/assets/{canvas-CiN6jNIZ.js → canvas-Dc1mYCwQ.js} +7 -5
- package/dist/server/assets/{comment-data-9SqALQjT.js → comment-data-CsjukwAW.js} +3773 -3608
- package/dist/server/assets/{events-6z5v7Uca.js → events-iHpLGr3k.js} +213 -2
- package/dist/server/assets/{files-DBVqeLdL.js → files-E9mSFeoc.js} +6 -4
- package/dist/server/assets/{listener-DGWKUc2o.js → listener-DN5TyOjJ.js} +35 -9
- package/dist/server/assets/{pages-DJMLr6j9.js → pages-BBBXyJWV.js} +4 -4
- package/dist/server/assets/{project-branches-DbKEmiQT.js → project-branches-B0YIK6Ur.js} +2 -2
- package/dist/server/assets/{project-thread-links-C0kV_Pcl.js → project-thread-links-CBOpf6zk.js} +3 -3
- package/dist/server/assets/{project-work-C0FN3w5Y.js → project-work-qSKFO5yj.js} +3 -3
- package/dist/server/assets/{screen-BtvzEiIa.js → screen-BIQA2mr4.js} +69 -138
- package/dist/server/assets/{server-CYvuOSBA.js → server-DxK-UB_Z.js} +30 -4
- package/dist/server/assets/{services-DK4fqrlF.js → services-BRrgXYlT.js} +105 -9
- package/dist/server/assets/{tasks-DX71x8f9.js → tasks-CiXodsLN.js} +14 -7
- package/dist/server/assets/{tasks-request-C3jk9Jyu.js → tasks-request-DhwCzpRi.js} +2 -2
- package/dist/server/migration.js +3 -3
- package/dist/server/runtime.js +3 -3
- package/dist/server/server.js +2 -2
- package/dist/types/server.d.ts +11 -1
- package/package.json +1 -1
- package/dist/client/assets/difference-worker-CwQNxTJL.js +0 -8
- package/dist/client/assets/files-BQ9O3Z8-.js +0 -1
- package/dist/client/assets/i18n-CL0jBP7Y.js +0 -1
- package/dist/client/assets/index-CjhJtepP.css +0 -2
- package/dist/client/assets/lru-BkhKJhvt.js +0 -1
- package/dist/client/assets/pages-BGl2Yuza.js +0 -1
- package/dist/client/assets/project-comments-Cdip2Y10.js +0 -1
- package/dist/client/assets/project-thread-links-BGIkGQeU.js +0 -1
- package/dist/client/assets/task-design-DJp5yho1.js +0 -23
- package/dist/client/assets/tasks-BHXj727C.js +0 -1
- package/dist/client/assets/workspace-CbO7RsI2.js +0 -2
package/dist/hosted/hosted.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $n as parsePreviewViewport, $t as Button, Ai as
|
|
2
|
-
import { n as requestTasks } from "./assets/tasks-request-
|
|
1
|
+
import { $i as createSchema, $n as parsePreviewViewport, $t as Button, Ai as parseWorkSnapshot, An as normalizeAttributeName, Bi as withoutMessages, Bn as createMultiMatcher, Ci as parseWorkConnection, Cn as composeMixedProps, Ct as legacyConversationHref, Di as parseWorkItemRef, Dn as FRAMEWORK_PROPS, Dt as taskHref, Ei as parseWorkItem, En as isEntry, Et as selectedTask, Fi as valueByRole, Fn as Frame, G as mergeThreads, Gi as identifier$3, Gn as PREVIEW_THUMBNAIL_WIDTHS, Gt as fileRoutes, Hi as parseWorkSchema, Hn as joinPatterns, Ii as messageDeletionRefusal, In as createComponent, J as conversationId, Ji as record$1, Jn as parsePreviewBranch, K as stableJson, Ki as nonempty$1, Kn as PREVIEW_TILE_HEIGHT, Kt as pageRoutes, Li as messageDeletionRefusals, Ln as createFrameHandle, Mi as parseWorkSyncResult, Mn as serializeStyleObject, Ni as parseWorkUpdate, Nn as shouldStringifyBooleanAttribute, Oi as parseWorkMutationResult, On as SELF_CLOSING_TAGS, Pi as parseWorkValue, Pn as Fragment, Q as text$6, Qi as boolean$2, Qn as parsePreviewPins, Ri as parseMessage, Rn as jsx, S as writeRows, Si as parseWorkActivity, Sn as normalizeUnsafeHTMLProps, Sr as parseThreadStatusInput, Ti as parseWorkDetail, Tn as clientEntry, Tt as readConversationLocation, Ui as statusCategory, Un as RoutePattern, V as parseTaskDesign, Vi as fieldByRole, Vn as equal, W as CommentMergeError, Wi as WorkValidationError, Wn as MAX_PREVIEW_HEIGHT, X as parseCommentThread, Xi as any, Xn as parsePreviewHash, Y as object$2, Yi as strict$2, Yn as parsePreviewCapture, Z as revision$2, Zi as array$2, Zn as parsePreviewId, Zt as Loader, _ as requestComments, _i as MAX_CANVAS_Y, _r as parseThreadInput, aa as object$1, an as LiveStatus, at as FieldCardHeader, b as plainRow, bi as parseSectionData, bn as stripFlushMarkers, br as parseThreadSnapshot, ca as parseSafe, ct as initialShellTabs, d as saveWorkspace, dn as hasMessage, dr as parseAnchor, dt as readTabsCookie, ea as defaulted, er as RESYNC, fn as isLocale, fr as parseThread, ft as stepShellTab, gn as Text, gr as parseThreadId, gt as visitShellTab, hn as useLocaleSource, hr as parseThreadFilter, ht as tabsCookieValue, ia as number$2, it as FieldCardContent, ji as parseWorkSync, jn as sanitizeUrlAttribute, jt as legacyFileHref, ki as parseWorkQuery, kn as isAllowedHostPropName, l as projectStore, la as string$2, ln as readPreference, lr as parseWorkChange, lt as openShellTab, mn as t, mr as parseThreadDeleteInput, mt as tabsCookieName, n as unavailableComments, na as fail$11, nn as ShellTabBar, oa as optional$3, ot as activeTab, pn as locale, pr as parseThreadCapabilities, pt as tabHref, q as parseCommentInput$1, qi as parseWork, qt as taskRoutes, r as followThreads, rt as FieldCard, sa as parse, sn as AppShell, st as closeShellTab, t as commentsUnavailable, ta as enum_, tr as parseLiveId, un as writePreference, ur as MAX_THREADS, ut as parseShellTabs, v as projectLive, vi as parseElementAnchor, vn as ref$1, vr as parseThreadLinkInput, wi as parseWorkCreate, wn as resolveMixDescriptors, xi as parseWorkActionInput, xn as on, xr as parseThreadStatus, y as routes, yi as parseWorkWorkspace, yn as appendFlushMarker, yr as parseThreadReplyInput, yt as conversationDestination, zi as parseMessageAuthor, zn as Route } from "./assets/comment-data-CF2ZV_3k.js";
|
|
2
|
+
import { n as requestTasks } from "./assets/tasks-request-DZob623r.js";
|
|
3
3
|
import * as Crypto from "node:crypto";
|
|
4
4
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
5
5
|
import * as Path from "node:path";
|
|
@@ -21312,6 +21312,9 @@ function beadsWorkItem(issue, schema, ready) {
|
|
|
21312
21312
|
function taskCount(count, hidden) {
|
|
21313
21313
|
return count === null ? null : Math.max(0, count - hidden);
|
|
21314
21314
|
}
|
|
21315
|
+
let field = (value, key) => value && typeof value === "object" && !Array.isArray(value) ? value[key] : void 0;
|
|
21316
|
+
let deletedIds = field(field(field(issue.metadata ?? void 0, "budDiscussion"), "record"), "deleted");
|
|
21317
|
+
let deleted = Array.isArray(deletedIds) ? deletedIds.length : 0;
|
|
21315
21318
|
let values = {
|
|
21316
21319
|
...properties,
|
|
21317
21320
|
metadata: taskMetadata(issue.metadata)
|
|
@@ -21337,7 +21340,7 @@ function beadsWorkItem(issue, schema, ready) {
|
|
|
21337
21340
|
]) delete values[key];
|
|
21338
21341
|
let counts = {};
|
|
21339
21342
|
for (let [key, value] of [
|
|
21340
|
-
["discussion", issue.commentCount],
|
|
21343
|
+
["discussion", taskCount(issue.commentCount, deleted)],
|
|
21341
21344
|
["children", issue.epicTotalChildren],
|
|
21342
21345
|
["childrenDone", issue.epicClosedChildren],
|
|
21343
21346
|
["outgoing", taskCount(issue.dependencyCount, issue.dependencies.length - dependencies.length)],
|
|
@@ -21637,10 +21640,10 @@ var MemoryStore = class {
|
|
|
21637
21640
|
reply: this.schema.capabilities.threads,
|
|
21638
21641
|
status: this.schema.capabilities.threads,
|
|
21639
21642
|
link: this.schema.capabilities.threads,
|
|
21643
|
+
delete: this.schema.capabilities.threads,
|
|
21640
21644
|
...options.threadCapabilities,
|
|
21641
21645
|
sync: false,
|
|
21642
|
-
edit: false
|
|
21643
|
-
delete: false
|
|
21646
|
+
edit: false
|
|
21644
21647
|
});
|
|
21645
21648
|
if (this.schema.capabilities.sync) refused("Memory storage has no remote synchronization.");
|
|
21646
21649
|
for (let action of this.schema.actions) if (action.kind === "custom" && !this.options.actions?.[action.id]) refused("Declare an implementation for each custom memory action.");
|
|
@@ -21953,6 +21956,28 @@ function memoryThreads(store) {
|
|
|
21953
21956
|
status: request.status
|
|
21954
21957
|
}, at);
|
|
21955
21958
|
}),
|
|
21959
|
+
deleteMessage: (root, id, value, author) => store.run(root, true, (data, at) => {
|
|
21960
|
+
if (!store.capabilities.delete) refused("This adapter does not allow deleting messages.");
|
|
21961
|
+
let request = input$4(() => parseThreadDeleteInput(value));
|
|
21962
|
+
let trusted = input$4(() => parseMessageAuthor(author));
|
|
21963
|
+
let thread = store.thread(data, id);
|
|
21964
|
+
expected(thread, request.expectedRevision);
|
|
21965
|
+
let refusal = messageDeletionRefusal(thread.messages, request.messageId, trusted.id);
|
|
21966
|
+
if (refusal === "missing") throw new WorkError({
|
|
21967
|
+
reason: "not-found",
|
|
21968
|
+
message: messageDeletionRefusals.missing
|
|
21969
|
+
});
|
|
21970
|
+
if (refusal) refused(messageDeletionRefusals[refusal]);
|
|
21971
|
+
if (thread.messages[0].id === request.messageId) {
|
|
21972
|
+
data.threads.delete(id);
|
|
21973
|
+
store.touchThreads(data, thread.itemIds, at);
|
|
21974
|
+
return null;
|
|
21975
|
+
}
|
|
21976
|
+
return save(data, {
|
|
21977
|
+
...thread,
|
|
21978
|
+
messages: withoutMessages(thread.messages, [request.messageId])
|
|
21979
|
+
}, at);
|
|
21980
|
+
}),
|
|
21956
21981
|
link: (root, id, ids, revision) => link(root, id, ids, revision, false),
|
|
21957
21982
|
unlink: (root, id, ids, revision) => link(root, id, ids, revision, true)
|
|
21958
21983
|
};
|
|
@@ -22825,6 +22850,7 @@ function parseThreadRecord(value) {
|
|
|
22825
22850
|
"creationId",
|
|
22826
22851
|
"initial",
|
|
22827
22852
|
"bindings",
|
|
22853
|
+
"deleted",
|
|
22828
22854
|
"source",
|
|
22829
22855
|
"sourceId",
|
|
22830
22856
|
"createdAt",
|
|
@@ -22838,6 +22864,7 @@ function parseThreadRecord(value) {
|
|
|
22838
22864
|
let bindings = unique(unique(array(input.bindings, "bindings", binding, MAX_BEADS_THREAD_BYTES), (entry) => entry.id, "bindings.id"), (entry) => entry.nativeId, "bindings.nativeId");
|
|
22839
22865
|
if (input.source === void 0 !== (input.sourceId === void 0)) throw new WorkValidationError("thread.metadata.source");
|
|
22840
22866
|
if (input.source !== void 0 && (!text$5(input.source, "thread.metadata.source", 512).trim() || !text$5(input.sourceId, "thread.metadata.sourceId", 512).trim())) throw new WorkValidationError("thread.metadata.source");
|
|
22867
|
+
let deleted = input.deleted === void 0 ? void 0 : unique(array(input.deleted, "deleted", (id) => identifier$1(id, "deleted"), MAX_BEADS_THREAD_BYTES), (id) => id, "deleted");
|
|
22841
22868
|
return {
|
|
22842
22869
|
version: 1,
|
|
22843
22870
|
kind,
|
|
@@ -22845,6 +22872,7 @@ function parseThreadRecord(value) {
|
|
|
22845
22872
|
creationId: identifier$1(input.creationId, "thread.metadata.creationId"),
|
|
22846
22873
|
initial,
|
|
22847
22874
|
bindings,
|
|
22875
|
+
...deleted?.length ? { deleted } : {},
|
|
22848
22876
|
createdAt: timestamp(input.createdAt, "thread.metadata.createdAt"),
|
|
22849
22877
|
updatedAt: timestamp(input.updatedAt, "thread.metadata.updatedAt"),
|
|
22850
22878
|
...input.source === void 0 ? {} : {
|
|
@@ -22854,6 +22882,14 @@ function parseThreadRecord(value) {
|
|
|
22854
22882
|
};
|
|
22855
22883
|
}
|
|
22856
22884
|
/**
|
|
22885
|
+
* Whether a thread's first message, and with it the whole thread, was deleted.
|
|
22886
|
+
* @param record - The thread's Bud record.
|
|
22887
|
+
* @returns Whether the thread no longer shows.
|
|
22888
|
+
*/
|
|
22889
|
+
function threadDeleted(record) {
|
|
22890
|
+
return record.deleted?.includes(record.initial.id) ?? false;
|
|
22891
|
+
}
|
|
22892
|
+
/**
|
|
22857
22893
|
* Leaves unpromoted creations inspectable in Beads without listing them as durable threads.
|
|
22858
22894
|
* @param value - The native issue row.
|
|
22859
22895
|
* @returns A validated promoted row, or null for a pending creation.
|
|
@@ -22942,7 +22978,8 @@ function parseThreadComments(value, id) {
|
|
|
22942
22978
|
* Validates full persisted history independently of the browser's visible message limit.
|
|
22943
22979
|
* @param row - The native row and separately validated Bud metadata.
|
|
22944
22980
|
* @param comments - All native messages within the process output byte limit.
|
|
22945
|
-
* @returns Every
|
|
22981
|
+
* @returns Every message not deleted, with validated attribution and complete ancestry; replies
|
|
22982
|
+
* to a deleted message move up to its parent.
|
|
22946
22983
|
*/
|
|
22947
22984
|
function threadMessages(row, comments) {
|
|
22948
22985
|
let bindings = new Map(row.record.bindings.map((entry) => [entry.nativeId, entry]));
|
|
@@ -22972,6 +23009,8 @@ function threadMessages(row, comments) {
|
|
|
22972
23009
|
if (!initial || JSON.stringify(initial) !== JSON.stringify(row.record.initial)) throw new WorkValidationError("thread.messages.initial");
|
|
22973
23010
|
let parents = new Map(messages.map((message) => [message.id, message.parentId]));
|
|
22974
23011
|
if (parents.size !== messages.length) throw new WorkValidationError("thread.messages.id");
|
|
23012
|
+
let deleted = row.record.deleted ?? [];
|
|
23013
|
+
if (deleted.some((id) => !parents.has(id))) throw new WorkValidationError("thread.messages.deleted");
|
|
22975
23014
|
let complete = /* @__PURE__ */ new Set();
|
|
22976
23015
|
for (let message of messages) {
|
|
22977
23016
|
let path = /* @__PURE__ */ new Set();
|
|
@@ -22983,7 +23022,7 @@ function threadMessages(row, comments) {
|
|
|
22983
23022
|
}
|
|
22984
23023
|
for (let id of path) complete.add(id);
|
|
22985
23024
|
}
|
|
22986
|
-
return messages;
|
|
23025
|
+
return withoutMessages(messages, deleted);
|
|
22987
23026
|
}
|
|
22988
23027
|
/**
|
|
22989
23028
|
* Retains source order and the truthful attribution of native replies before a new append.
|
|
@@ -23017,6 +23056,8 @@ function threadView(row, comments, loaded) {
|
|
|
23017
23056
|
let { issue, record } = row;
|
|
23018
23057
|
let count = issue.commentCount;
|
|
23019
23058
|
if (count === null || count < 1 || comments && comments.length !== count) throw new WorkValidationError("thread.messageCount");
|
|
23059
|
+
let visible = count - (record.deleted?.length ?? 0);
|
|
23060
|
+
if (visible < 1) throw new WorkValidationError("thread.messageCount");
|
|
23020
23061
|
let all = comments ? loaded ?? threadMessages(row, comments) : [record.initial];
|
|
23021
23062
|
let messages = all.slice(0, 200);
|
|
23022
23063
|
let status = issue.status === "closed" ? "resolved" : issue.labels.includes("bud-status:needs_approval") ? "needs_approval" : issue.status;
|
|
@@ -23026,9 +23067,9 @@ function threadView(row, comments, loaded) {
|
|
|
23026
23067
|
anchor: record.anchor,
|
|
23027
23068
|
itemIds: linkedItems(row),
|
|
23028
23069
|
messages,
|
|
23029
|
-
messageCount:
|
|
23070
|
+
messageCount: visible,
|
|
23030
23071
|
messagesLoaded: comments !== void 0,
|
|
23031
|
-
messagesTruncated: comments !== void 0 &&
|
|
23072
|
+
messagesTruncated: comments !== void 0 && all.length > 200,
|
|
23032
23073
|
createdAt: record.createdAt,
|
|
23033
23074
|
updatedAt: [
|
|
23034
23075
|
record.updatedAt,
|
|
@@ -23139,6 +23180,10 @@ function readThread(session, value) {
|
|
|
23139
23180
|
reason: "uncertain",
|
|
23140
23181
|
message: "This discussion has not been promoted to durable storage. Inspect its creation in Beads before trying again."
|
|
23141
23182
|
});
|
|
23183
|
+
if (threadDeleted(row.record)) return yield* new WorkError({
|
|
23184
|
+
reason: "not-found",
|
|
23185
|
+
message: "This discussion was deleted."
|
|
23186
|
+
});
|
|
23142
23187
|
let comments = yield* session.comments(id, (value) => parseThreadComments(value, id));
|
|
23143
23188
|
if (row.issue.commentCount !== null && comments.length !== row.issue.commentCount) return yield* new WorkError({
|
|
23144
23189
|
reason: "conflict",
|
|
@@ -23268,6 +23313,20 @@ function newMessage(body, author, parentId = null) {
|
|
|
23268
23313
|
});
|
|
23269
23314
|
}
|
|
23270
23315
|
/**
|
|
23316
|
+
* Checks that the requesting author may delete a message, against full storage.
|
|
23317
|
+
* @param messages - The current complete stored messages, first message first.
|
|
23318
|
+
* @param messageId - The message to delete.
|
|
23319
|
+
* @param author - The trusted server author.
|
|
23320
|
+
* @returns A refusal unless it is the author's own message, and a first message stands alone.
|
|
23321
|
+
*/
|
|
23322
|
+
function validateDeletion(messages, messageId, author) {
|
|
23323
|
+
let refusal = messageDeletionRefusal(messages, messageId, author.id);
|
|
23324
|
+
return refusal ? fail$5(new WorkError({
|
|
23325
|
+
reason: refusal === "missing" ? "not-found" : "refused",
|
|
23326
|
+
message: messageDeletionRefusals[refusal]
|
|
23327
|
+
})) : void_;
|
|
23328
|
+
}
|
|
23329
|
+
/**
|
|
23271
23330
|
* Checks reply ancestry against full storage, independent of the UI's display bound.
|
|
23272
23331
|
* @param messages - The current complete stored messages.
|
|
23273
23332
|
* @param parentId - The requested reply parent.
|
|
@@ -23571,6 +23630,37 @@ function replyItemDiscussion(session, id, input, author) {
|
|
|
23571
23630
|
});
|
|
23572
23631
|
}
|
|
23573
23632
|
/**
|
|
23633
|
+
* Deletes one of the author's replies from a native task discussion; the task's first comment
|
|
23634
|
+
* stays. The comment stays in Beads, and the discussion record hides it.
|
|
23635
|
+
* @param session - The shared native writer.
|
|
23636
|
+
* @param id - The work-item ID, without its discussion namespace.
|
|
23637
|
+
* @param input - The revision-checked deletion.
|
|
23638
|
+
* @param author - The trusted server author.
|
|
23639
|
+
* @returns The changed, validated native discussion.
|
|
23640
|
+
*/
|
|
23641
|
+
function deleteItemDiscussionMessage(session, id, input, author) {
|
|
23642
|
+
return gen(function* () {
|
|
23643
|
+
let current = yield* required$1(session, id);
|
|
23644
|
+
yield* expectedThread(current.thread, input.expectedRevision);
|
|
23645
|
+
if (input.messageId === current.data.record.initial.id) return yield* new WorkError({
|
|
23646
|
+
reason: "refused",
|
|
23647
|
+
message: "A task's first comment stays; only replies can be deleted."
|
|
23648
|
+
});
|
|
23649
|
+
yield* validateDeletion(current.messages, input.messageId, author);
|
|
23650
|
+
let next = {
|
|
23651
|
+
...current.data,
|
|
23652
|
+
record: {
|
|
23653
|
+
...current.data.record,
|
|
23654
|
+
deleted: [...current.data.record.deleted ?? [], input.messageId],
|
|
23655
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
23656
|
+
}
|
|
23657
|
+
};
|
|
23658
|
+
let json = yield* threadInput(() => payload(current.issue, next));
|
|
23659
|
+
yield* threadPayload(json, session.limits.metadataBytes);
|
|
23660
|
+
return yield* save(session, id, json, () => map$2(required$1(session, id), (result) => result.thread));
|
|
23661
|
+
});
|
|
23662
|
+
}
|
|
23663
|
+
/**
|
|
23574
23664
|
* Saves discussion-only status or linked items without mutating the work item's lifecycle.
|
|
23575
23665
|
* @param session - The shared native writer.
|
|
23576
23666
|
* @param id - The owning work item.
|
|
@@ -23747,7 +23837,7 @@ var capabilities$1 = {
|
|
|
23747
23837
|
link: true,
|
|
23748
23838
|
sync: true,
|
|
23749
23839
|
edit: false,
|
|
23750
|
-
delete:
|
|
23840
|
+
delete: true
|
|
23751
23841
|
};
|
|
23752
23842
|
var unavailableCapabilities = {
|
|
23753
23843
|
create: false,
|
|
@@ -23767,7 +23857,7 @@ function listThreads(session, filter) {
|
|
|
23767
23857
|
}, parseThreadRows);
|
|
23768
23858
|
let items = yield* listItemDiscussions(session, filter);
|
|
23769
23859
|
return yield* threadResult(() => {
|
|
23770
|
-
let threads = [...items.threads, ...rows.slice(0, MAX_THREADS).flatMap((row) => row ? [threadView(row)] : []).filter((thread) => matchesThread(thread, filter))];
|
|
23860
|
+
let threads = [...items.threads, ...rows.slice(0, MAX_THREADS).flatMap((row) => row && !threadDeleted(row.record) ? [threadView(row)] : []).filter((thread) => matchesThread(thread, filter))];
|
|
23771
23861
|
return parseThreadSnapshot({
|
|
23772
23862
|
connection: {
|
|
23773
23863
|
state: "ready",
|
|
@@ -23977,6 +24067,34 @@ var beadsStoreThreadAdapterLayer = effect(ThreadAdapter, gen(function* () {
|
|
|
23977
24067
|
}));
|
|
23978
24068
|
}));
|
|
23979
24069
|
}).pipe(mapError$2(threadFailure)),
|
|
24070
|
+
deleteMessage: (root, id, value, author) => gen(function* () {
|
|
24071
|
+
let input = yield* threadInput(() => parseThreadDeleteInput(value));
|
|
24072
|
+
let trusted = yield* threadInput(() => parseMessageAuthor(author));
|
|
24073
|
+
return yield* store.change(root, (session) => gen(function* () {
|
|
24074
|
+
let native = yield* threadInput(() => nativeDiscussionId(id));
|
|
24075
|
+
if (native) return yield* deleteItemDiscussionMessage(session, native, input, trusted);
|
|
24076
|
+
let { thread, row, messages } = yield* readThread(session, id);
|
|
24077
|
+
yield* expectedThread(thread, input.expectedRevision);
|
|
24078
|
+
yield* validateDeletion(messages, input.messageId, trusted);
|
|
24079
|
+
let record = yield* threadInput(() => parseThreadRecord({
|
|
24080
|
+
...row.record,
|
|
24081
|
+
deleted: [...row.record.deleted ?? [], input.messageId],
|
|
24082
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
24083
|
+
}));
|
|
24084
|
+
let nextMetadata = {
|
|
24085
|
+
...row.issue.metadata,
|
|
24086
|
+
bud: record
|
|
24087
|
+
};
|
|
24088
|
+
yield* threadPayload(JSON.stringify(nextMetadata), session.limits.metadataBytes);
|
|
24089
|
+
return yield* threadChange(session, (writer) => gen(function* () {
|
|
24090
|
+
yield* saveRecord(writer, row, record);
|
|
24091
|
+
if (!threadDeleted(record)) return (yield* readThread(session, id)).thread;
|
|
24092
|
+
for (let item of thread.itemIds) yield* writer.removeDependency(id, item);
|
|
24093
|
+
if (thread.status !== "resolved") yield* writer.close(id, "Deleted in Bud");
|
|
24094
|
+
return null;
|
|
24095
|
+
}));
|
|
24096
|
+
}));
|
|
24097
|
+
}).pipe(mapError$2(threadFailure)),
|
|
23980
24098
|
link: (root, id, ids, revision) => link(root, id, ids, revision, false),
|
|
23981
24099
|
unlink: (root, id, ids, revision) => link(root, id, ids, revision, true),
|
|
23982
24100
|
sync: (root, direction = "both") => gen(function* () {
|
|
@@ -38766,10 +38884,10 @@ function ProjectLiveStatus(handle) {
|
|
|
38766
38884
|
//#endregion
|
|
38767
38885
|
//#region app/client/screens.ts
|
|
38768
38886
|
var builtIns = {
|
|
38769
|
-
"bud:pages": () => import("./assets/pages-
|
|
38770
|
-
"bud:canvas": () => import("./assets/canvas-
|
|
38771
|
-
"bud:tasks": () => import("./assets/tasks-
|
|
38772
|
-
"bud:files": () => import("./assets/files-
|
|
38887
|
+
"bud:pages": () => import("./assets/pages-BzjzeuPu.js"),
|
|
38888
|
+
"bud:canvas": () => import("./assets/canvas-V36732ER.js"),
|
|
38889
|
+
"bud:tasks": () => import("./assets/tasks-Dj7AyBJB.js"),
|
|
38890
|
+
"bud:files": () => import("./assets/files-DjSCjfU7.js")
|
|
38773
38891
|
};
|
|
38774
38892
|
/**
|
|
38775
38893
|
* Imports a screen module: a built-in by its key, any other by the URL the server gave it.
|
|
@@ -40618,7 +40736,11 @@ function adapterRegistryLayer(project, builtIns) {
|
|
|
40618
40736
|
"setStatus",
|
|
40619
40737
|
"link",
|
|
40620
40738
|
"unlink"
|
|
40621
|
-
], [
|
|
40739
|
+
], [
|
|
40740
|
+
"sync",
|
|
40741
|
+
"import",
|
|
40742
|
+
"deleteMessage"
|
|
40743
|
+
]);
|
|
40622
40744
|
let connection = yield* threads.list(root, {}).pipe(flatMap$1((value) => try_({
|
|
40623
40745
|
try: () => parseThreadSnapshot(value).connection,
|
|
40624
40746
|
catch: failure$2(`Adapter ${selected.threads.id} returned an invalid thread connection.`)
|
|
@@ -41097,6 +41219,32 @@ function createCommentsController(project, runtime) {
|
|
|
41097
41219
|
unlink({ request, params }) {
|
|
41098
41220
|
return respond(request, (project) => written(link(request, project, params.id, true)));
|
|
41099
41221
|
},
|
|
41222
|
+
deleteMessage({ request, params }) {
|
|
41223
|
+
return respond(request, (project) => written(gen(function* () {
|
|
41224
|
+
let id = yield* input$1(() => parseThreadId(params.id));
|
|
41225
|
+
let value = yield* readProjectJsonBody(request, { maxBytes: 64e3 });
|
|
41226
|
+
let parsed = yield* input$1(() => parseThreadDeleteInput(value));
|
|
41227
|
+
let store = yield* writable(project.root, "delete");
|
|
41228
|
+
if (!store.deleteMessage) return yield* new WorkError({
|
|
41229
|
+
reason: "refused",
|
|
41230
|
+
message: "This comment adapter does not support deleting messages."
|
|
41231
|
+
});
|
|
41232
|
+
let author = yield* commentAuthor(project.root);
|
|
41233
|
+
let thread = yield* store.deleteMessage(project.root, id, parsed, author);
|
|
41234
|
+
return yield* result(() => {
|
|
41235
|
+
if (thread === null) return { thread: null };
|
|
41236
|
+
let parsed = parseThread(thread);
|
|
41237
|
+
if (parsed.id !== id) throw new Error("Unexpected discussion identity.");
|
|
41238
|
+
return { thread: parsed };
|
|
41239
|
+
}, true);
|
|
41240
|
+
})));
|
|
41241
|
+
},
|
|
41242
|
+
viewer({ request }) {
|
|
41243
|
+
return respond(request, (project) => commentAuthor(project.root).pipe(map$2((author) => ({ viewer: {
|
|
41244
|
+
id: author.id,
|
|
41245
|
+
name: author.name
|
|
41246
|
+
} })), orElseSucceed(() => ({ viewer: null }))));
|
|
41247
|
+
},
|
|
41100
41248
|
sync({ request }) {
|
|
41101
41249
|
return respond(request, (project) => gen(function* () {
|
|
41102
41250
|
let value = yield* readProjectJsonBody(request, { maxBytes: 64e3 });
|
|
@@ -484,6 +484,49 @@ var MessageSchema = field("message", strict$1({
|
|
|
484
484
|
function parseMessageAuthor(value) {
|
|
485
485
|
return parseWork(MessageAuthorSchema, value, "author");
|
|
486
486
|
}
|
|
487
|
+
/** What each deletion refusal tells the person, safe to show. */
|
|
488
|
+
var messageDeletionRefusals = {
|
|
489
|
+
missing: "This message no longer exists.",
|
|
490
|
+
"not-author": "Only its author can delete a message.",
|
|
491
|
+
"has-replies": "The first message stays while the discussion has replies. Resolve the discussion instead."
|
|
492
|
+
};
|
|
493
|
+
/**
|
|
494
|
+
* Checks that someone may delete a message: only its author may, and a discussion's first message
|
|
495
|
+
* only while it is the only one, since deleting it removes the whole discussion.
|
|
496
|
+
* @param messages - The discussion's complete messages, first message first.
|
|
497
|
+
* @param messageId - The message to delete.
|
|
498
|
+
* @param authorId - Who asks, as the server derived them.
|
|
499
|
+
* @returns Why it can't be deleted, or null when it can.
|
|
500
|
+
*/
|
|
501
|
+
function messageDeletionRefusal(messages, messageId, authorId) {
|
|
502
|
+
let message = messages.find((candidate) => candidate.id === messageId);
|
|
503
|
+
if (!message) return "missing";
|
|
504
|
+
if (message.author.id !== authorId) return "not-author";
|
|
505
|
+
if (message.id === messages[0].id && messages.length > 1) return "has-replies";
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Leaves out deleted messages; replies to a deleted message move up to its parent.
|
|
510
|
+
* @param messages - The messages, in order.
|
|
511
|
+
* @param deleted - The IDs of the messages to leave out.
|
|
512
|
+
* @returns The remaining messages with complete ancestry.
|
|
513
|
+
*/
|
|
514
|
+
function withoutMessages(messages, deleted) {
|
|
515
|
+
let gone = new Set(deleted);
|
|
516
|
+
if (!gone.size) return [...messages];
|
|
517
|
+
let parents = new Map(messages.map((message) => [message.id, message.parentId]));
|
|
518
|
+
let visibleParent = (id) => {
|
|
519
|
+
while (id !== null && gone.has(id)) id = parents.get(id) ?? null;
|
|
520
|
+
return id;
|
|
521
|
+
};
|
|
522
|
+
return messages.filter((message) => !gone.has(message.id)).map((message) => {
|
|
523
|
+
let parentId = visibleParent(message.parentId);
|
|
524
|
+
return parentId === message.parentId ? message : {
|
|
525
|
+
...message,
|
|
526
|
+
parentId
|
|
527
|
+
};
|
|
528
|
+
});
|
|
529
|
+
}
|
|
487
530
|
/**
|
|
488
531
|
* Validates a message returned by storage, never a browser write request.
|
|
489
532
|
* @param value - Untrusted data from a request, adapter or response.
|
|
@@ -2014,6 +2057,11 @@ var ThreadStatusInputSchema = field("status", strict$1({
|
|
|
2014
2057
|
status: ThreadStatusSchema,
|
|
2015
2058
|
expectedRevision
|
|
2016
2059
|
}));
|
|
2060
|
+
/** A message deletion against the revision the reader saw; the server checks its author. */
|
|
2061
|
+
var ThreadDeleteInputSchema = field("delete", strict$1({
|
|
2062
|
+
messageId: identifier$1,
|
|
2063
|
+
expectedRevision
|
|
2064
|
+
}));
|
|
2017
2065
|
/** A revision-checked set of work-item links. */
|
|
2018
2066
|
var ThreadLinkInputSchema = field("link", verify(strict$1({
|
|
2019
2067
|
itemIds,
|
|
@@ -2074,6 +2122,14 @@ function parseThreadStatusInput(value) {
|
|
|
2074
2122
|
return parseWork(ThreadStatusInputSchema, value, "status");
|
|
2075
2123
|
}
|
|
2076
2124
|
/**
|
|
2125
|
+
* Validates a message deletion against the revision the reader saw.
|
|
2126
|
+
* @param value - Untrusted data from a request, adapter or response.
|
|
2127
|
+
* @returns The message ID and expected revision.
|
|
2128
|
+
*/
|
|
2129
|
+
function parseThreadDeleteInput(value) {
|
|
2130
|
+
return parseWork(ThreadDeleteInputSchema, value, "delete");
|
|
2131
|
+
}
|
|
2132
|
+
/**
|
|
2077
2133
|
* Validates a revision-checked set of work-item links.
|
|
2078
2134
|
* @param value - Untrusted data from a request, adapter or response.
|
|
2079
2135
|
* @returns The linked item IDs and expected revision.
|
|
@@ -2109,4 +2165,4 @@ function changeSchema(item) {
|
|
|
2109
2165
|
}
|
|
2110
2166
|
field("change", changeSchema(ThreadSchema));
|
|
2111
2167
|
//#endregion
|
|
2112
|
-
export {
|
|
2168
|
+
export { parseWorkSchema as A, valueByRole as C, parseMessageAuthor as D, parseMessage as E, parseWork as F, record$1 as I, strict$1 as L, WorkValidationError as M, identifier$1 as N, withoutMessages as O, nonempty as P, parseWorkValue as S, messageDeletionRefusals as T, parseWorkMutationResult as _, parseThreadFilter as a, parseWorkSnapshot as b, parseThreadReplyInput as c, parseSectionData as d, parseWorkActionInput as f, parseWorkItemRef as g, parseWorkItem as h, parseThreadDeleteInput as i, statusCategory as j, fieldByRole as k, parseThreadSnapshot as l, parseWorkDetail as m, parseThread as n, parseThreadInput as o, parseWorkCreate as p, parseThreadCapabilities as r, parseThreadLinkInput as s, MAX_THREADS as t, parseThreadStatusInput as u, parseWorkPatch as v, messageDeletionRefusal as w, parseWorkUpdate as x, parseWorkQuery as y };
|