@bud.sh/app 0.5.0 → 0.6.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/README.md +41 -31
- package/dist/client/assets/bud/design.js +1 -1
- package/dist/client/assets/canvas-Bih9EdKJ.js +1 -0
- package/dist/client/assets/difference-worker-Bzg-rizy.js +8 -0
- package/dist/client/assets/dist-CdE5wBkg.js +5 -0
- package/dist/client/assets/files-1dLuoM2f.js +1 -0
- package/dist/client/assets/i18n-CkO6VFI0.js +1 -0
- package/dist/client/assets/index-Cn2mxroU.css +2 -0
- package/dist/client/assets/{index-ChANGtl-.js → index-DtISWZp9.js} +2 -2
- package/dist/client/assets/pages-BJ-tNJ4J.js +1 -0
- package/dist/client/assets/{project-branches-DO897zM6.js → project-branches-Ce3rXrkc.js} +1 -1
- package/dist/client/assets/project-comments-CPUONfs8.js +1 -0
- package/dist/client/assets/project-thread-links-C-TGkq59.js +1 -0
- package/dist/client/assets/{project-threads-Lay351p4.js → project-threads-Bv7IDBgi.js} +105 -105
- package/dist/client/assets/project-work-DAE9-olg.js +1 -0
- package/dist/client/assets/{task-design-Avb2RO4w.js → task-design-CwySIAOl.js} +3 -3
- package/dist/client/assets/tasks-KWsCLi_h.js +3 -0
- package/dist/client/assets/wasm-inline-DSUyFn1L.js +1 -0
- package/dist/client/assets/workspace-CVdcfaaB.js +2 -0
- package/dist/client/index.html +3 -3
- package/dist/hosted/assets/{canvas-V36732ER.js → canvas-AII16n9i.js} +97 -6
- package/dist/hosted/assets/{comment-data-CF2ZV_3k.js → comment-data-BXfMElfx.js} +2004 -402
- package/dist/hosted/assets/dist-BCSTeNf4.js +4785 -0
- package/dist/hosted/assets/{files-DjSCjfU7.js → files-D_1yeAVO.js} +10 -5
- package/dist/hosted/assets/{pages-BzjzeuPu.js → pages-CN_SbKWA.js} +3 -3
- package/dist/hosted/assets/{project-branches-BT97J9i7.js → project-branches-Bxp0DCwg.js} +1 -1
- package/dist/hosted/assets/{project-thread-links-BRLgByEe.js → project-thread-links-KqQoKBLE.js} +76 -4
- package/dist/{node-host/assets/screen-CZXWJEds.js → hosted/assets/screen-DpAcDUKR.js} +29 -1
- package/dist/hosted/assets/tasks-D66LlwxM.js +1423 -0
- package/dist/hosted/assets/wasm-inline-Dl9tqabx.js +4 -0
- package/dist/hosted/hosted.js +7885 -6219
- package/dist/node/assets/Stream-CAm1eJ2n.js +16604 -0
- package/dist/{server/assets/bridge-B_Blt55E.js → node/assets/bridge-DQSBYHmw.js} +124 -290
- package/dist/node/assets/config-9_jWwNBh.js +1067 -0
- package/dist/node/assets/load-config.server-CMVtRVmO.js +261 -0
- package/dist/node/assets/module-3GgtT4Xs.js +308 -0
- package/dist/node/assets/server-DTQFuoX2.js +846 -0
- package/dist/node/assets/src-D-kG1GxB.js +2 -0
- package/dist/node/assets/{src-CerrKjID.js → src-Dwe9APUx.js} +93 -3
- package/dist/node/assets/src-DysjfbM8.js +8592 -0
- package/dist/node/bridge.js +1 -845
- package/dist/node/cli.js +206 -0
- package/dist/node/design.js +811 -13
- package/dist/node/index.js +2 -739
- package/dist/node/plugins.js +5711 -0
- package/dist/node/server.js +1 -6746
- package/dist/node/vite.js +121 -0
- package/dist/{server/assets/canvas-Dc1mYCwQ.js → node-host/assets/canvas-BeDoufy1.js} +97 -7
- package/dist/node-host/assets/{comment-data-DyIZNpfG.js → comment-data-DE2T9yTj.js} +1980 -376
- package/dist/node-host/assets/dist-BCSTeNf4.js +4785 -0
- package/dist/node-host/assets/{files-Dh2TiQZq.js → files-Dl-PTS09.js} +11 -6
- package/dist/node-host/assets/{pages-BxpjbYhs.js → pages-DL2sk6_U.js} +3 -3
- package/dist/node-host/assets/{project-branches-B7HUGiVK.js → project-branches-DGp5ELct.js} +1 -1
- package/dist/{server/assets/project-thread-links-CBOpf6zk.js → node-host/assets/project-thread-links-CgBV3Jcm.js} +76 -5
- package/dist/{hosted/assets/screen-CKp2C7kx.js → node-host/assets/screen-B55yyVfb.js} +29 -1
- package/dist/node-host/assets/tasks-DZRNkoxe.js +1425 -0
- package/dist/node-host/assets/wasm-inline-Dl9tqabx.js +4 -0
- package/dist/node-host/node-host.js +6127 -4438
- package/dist/server/assets/{branch-previews-ClkoJ0lM.js → branch-previews-DedrpnVR.js} +1555 -673
- package/dist/{node-host/assets/canvas-TRUfxfL5.js → server/assets/canvas-CJWQ-jjv.js} +98 -6
- package/dist/server/assets/{comment-data-CsjukwAW.js → comment-data-Rg42ToTW.js} +1666 -47
- package/dist/server/assets/dist-BCSTeNf4.js +4785 -0
- package/dist/server/assets/{events-iHpLGr3k.js → events-BH_rAu7O.js} +155 -20
- package/dist/server/assets/{files-E9mSFeoc.js → files-CPJqKeMo.js} +12 -7
- package/dist/server/assets/{listener-DN5TyOjJ.js → listener-D61O6PzF.js} +708 -12
- package/dist/server/assets/{pages-BBBXyJWV.js → pages-KJY_Ba92.js} +4 -4
- package/dist/server/assets/{project-branches-B0YIK6Ur.js → project-branches-qG9ACZvo.js} +2 -2
- package/dist/{node-host/assets/project-thread-links-CauJF0Cz.js → server/assets/project-thread-links-CqK0lqCF.js} +77 -4
- package/dist/{node-host/assets/tasks-request-ZWmbc8fE.js → server/assets/project-work-DoIwxWMm.js} +87 -2
- package/dist/server/assets/run-BJ8r5jSh.js +572 -0
- package/dist/server/assets/{screen-BIQA2mr4.js → screen-CBrHPFug.js} +30 -2
- package/dist/server/assets/{server-DxK-UB_Z.js → server-Dkbp5stG.js} +1521 -793
- package/dist/server/assets/{services-BRrgXYlT.js → services-BTfig8A8.js} +234 -997
- package/dist/server/assets/tasks-Bgu5Lg9Y.js +1427 -0
- package/dist/server/assets/wasm-inline-Dl9tqabx.js +4 -0
- package/dist/server/capture.js +1 -2
- package/dist/server/migration.js +569 -12
- package/dist/server/runtime.js +7 -5
- package/dist/server/server.js +3 -3
- package/dist/types/assets/{config-DIC2cf_S.d.ts → config-CvcYmE2s.d.ts} +188 -50
- package/dist/types/assets/{contract-CAT-XFNq.d.ts → contract-B7yuuqSn.d.ts} +44 -2
- package/dist/types/assets/{items-CaVkByHk.d.ts → items-BQdaVksf.d.ts} +12 -1
- package/dist/types/assets/memory-state-BBW-FAVA.d.ts +236 -0
- package/dist/types/assets/picker-Be5Gx4WR.d.ts +9 -0
- package/dist/types/assets/{task-design-xJOhRNWh.d.ts → task-design-BDHKFHpp.d.ts} +3 -1
- package/dist/types/bridge.d.ts +1 -1
- package/dist/types/companion.d.ts +3 -3
- package/dist/types/design.d.ts +2 -2
- package/dist/types/hosted.d.ts +3 -3
- package/dist/types/index.d.ts +2 -2
- package/dist/types/node-host.d.ts +2 -2
- package/dist/types/plugins.d.ts +445 -0
- package/dist/types/screen.d.ts +2 -2
- package/dist/types/server.d.ts +2 -228
- package/dist/types/vite.d.ts +16 -0
- package/package.json +19 -3
- package/dist/client/assets/canvas-Cu6R6xxT.js +0 -1
- package/dist/client/assets/difference-worker-jfTSn-Tw.js +0 -8
- package/dist/client/assets/files-js6ElfkU.js +0 -1
- package/dist/client/assets/i18n-DxqFr2mi.js +0 -1
- package/dist/client/assets/index-CFFxP0gS.css +0 -2
- package/dist/client/assets/pages-6_lhiQmz.js +0 -1
- package/dist/client/assets/project-comments-DFFKVpia.js +0 -1
- package/dist/client/assets/project-thread-links-NLFWQRD2.js +0 -1
- package/dist/client/assets/project-work-du0Y-L5Y.js +0 -1
- package/dist/client/assets/tasks-CA-E8bbK.js +0 -1
- package/dist/client/assets/tasks-request-C55IeuiP.js +0 -1
- package/dist/client/assets/workspace-Cw5HvX5d.js +0 -2
- package/dist/hosted/assets/tasks-Dj7AyBJB.js +0 -649
- package/dist/hosted/assets/tasks-request-DZob623r.js +0 -113
- package/dist/node-host/assets/tasks-Bs1IOpeU.js +0 -651
- package/dist/server/assets/project-work-qSKFO5yj.js +0 -88
- package/dist/server/assets/tasks-CiXodsLN.js +0 -652
- package/dist/server/assets/tasks-request-DhwCzpRi.js +0 -115
- package/dist/types/assets/picker-D1Bw3ndx.d.ts +0 -14
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { A as
|
|
3
|
-
import { A as parseAnchor, B as
|
|
4
|
-
import {
|
|
5
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
1
|
+
import { $ as unwrap, $n as provide, $t as map, A as ProcessRunner, An as tap, At as andThen, Bt as context, Cn as scoped, Cr as TaggedError, Ct as isPattern, D as nodeLayer, Dt as acquireUseRelease, En as succeed, F as changes, Fn as try_, Gn as effect, Gt as flatMap, Hn as currentTimeMillis, Ht as ensuring, I as concat, It as catchIf, J as mapError$1, K as make, Kn as effectContext, L as debounce, Lt as catchTag, M as Path, N as FileSystem, Nn as timeoutOrElse, Nt as asVoid, On as suspend, P as as$1, Pt as cached, Qn as mergeAll, Qt as ignore, R as drop, Rt as catch_, St as isNonEmpty, Ut as fail, Vt as die, Wt as filterOrFail, X as runForEach, Xt as forkScoped, Yn as isLayer, Zn as merge, Zt as gen, _r as make$1, _t as Struct, bt as decodeEffect, c as previewsLayer, d as pickerPageScript, en as mapError, er as provideMerge, f as PICKER_MODULE_URL, g as Project, gt as String$1, h as gitLayer, hr as add, ht as Record, i as projectFlows, in as orElseSucceed, ir as unwrap$1, j as RuntimePolicy, jt as as, kn as sync, kt as all, ln as result, m as Git, mr as Service, nr as succeedContext, q as mapEffect, qn as effectDiscard, qt as forEach, r as branchPreviewsLayer, s as Previews, t as BranchPreviews, tr as succeed$1, tt as makeUnsafe, vt as Undefined, wn as serviceOption, wt as optional, xt as isMaxLength, y as configPlugins, yr as getOrUndefined, yt as Union, z as empty, zn as void_ } from "./branch-previews-DedrpnVR.js";
|
|
2
|
+
import { A as liveChannelLayer, B as ThreadAdapter, F as AdapterRegistry, G as sourceScreensLayer, H as WorkError, I as AdapterStartupError, L as adapterRegistryLayer, N as attachmentsLayer, P as directoryAttachmentStore, R as defineAdapter, S as storeLiveLayer, V as WorkAdapter, W as bundledScreensLayer, _ as factoryRunsLayer, b as StoreActivity, c as FileReview, d as fileSearchLayer, f as Files, i as fileSavesLayer, j as publishChanges, k as LiveChannel, l as fileReviewLayer, n as fileReviewActionsLayer, o as fileMutationsLayer, p as filesLayer, s as fileWriteLockLayer, x as storeActivityLayer, y as runExecutorLayer, z as parseThreadImport } from "./server-Dkbp5stG.js";
|
|
3
|
+
import { A as parseAnchor, B as parseThreadStatusInput, Cn as workRunSummary, D as MAX_THREADS, Dn as parseMessageAuthor, En as parseMessage, F as parseThreadInput, I as parseThreadLinkInput, L as parseThreadReplyInput, M as parseThreadDeleteInput, N as parseThreadFilter, Nn as WorkValidationError, On as withoutMessages, R as parseThreadSnapshot, Tn as messageDeletionRefusals, _n as parseWorkQuery, bn as parseWorkSyncResult, cn as parseWorkActionInput, dn as parseWorkCreate, fn as parseWorkDetail, hn as parseWorkMutationResult, j as parseThread, jn as parseWorkSchema, ln as parseWorkActivity, pn as parseWorkItem, sn as parseSectionData, un as parseWorkConnection, vn as parseWorkSnapshot, wn as messageDeletionRefusal, xn as parseWorkUpdate, yn as parseWorkSync, z as parseThreadStatus } from "./events-BH_rAu7O.js";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
6
5
|
import { join, relative, sep } from "node:path";
|
|
7
6
|
//#region ../beads/src/lib/contracts.ts
|
|
8
7
|
var MAX_BEADS = 1e3;
|
|
@@ -995,6 +994,99 @@ var Beads = class extends Service()("@bud.sh/beads/Beads") {};
|
|
|
995
994
|
*/
|
|
996
995
|
var BeadsStore = class extends Service()("@bud.sh/beads/Store") {};
|
|
997
996
|
//#endregion
|
|
997
|
+
//#region ../beads/src/lib/custom-statuses.ts
|
|
998
|
+
var categories = [
|
|
999
|
+
"active",
|
|
1000
|
+
"wip",
|
|
1001
|
+
"done",
|
|
1002
|
+
"frozen"
|
|
1003
|
+
];
|
|
1004
|
+
var statusName = /^[a-z][a-z0-9_]{0,63}$/;
|
|
1005
|
+
/**
|
|
1006
|
+
* Reads Beads' `status.custom` setting, such as `needs_approval:wip,merged:done`.
|
|
1007
|
+
* @param value - The setting; empty or absent for none.
|
|
1008
|
+
* @returns Its statuses in order, without duplicates.
|
|
1009
|
+
*/
|
|
1010
|
+
function parseCustomStatuses(value) {
|
|
1011
|
+
let statuses = [];
|
|
1012
|
+
for (let entry of (value ?? "").split(",")) {
|
|
1013
|
+
let [name = "", category = ""] = entry.trim().split(":");
|
|
1014
|
+
if (!name) continue;
|
|
1015
|
+
if (!statusName.test(name)) throw new TypeError(`Invalid custom status ${name}.`);
|
|
1016
|
+
if (category && !categories.includes(category)) throw new TypeError(`Invalid category for custom status ${name}.`);
|
|
1017
|
+
if (statuses.some((status) => status.name === name)) continue;
|
|
1018
|
+
statuses.push({
|
|
1019
|
+
name,
|
|
1020
|
+
category: category || null
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
return statuses;
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Writes statuses as Beads' `status.custom` setting.
|
|
1027
|
+
* @param statuses - The statuses in order.
|
|
1028
|
+
* @returns The setting.
|
|
1029
|
+
*/
|
|
1030
|
+
function formatCustomStatuses(statuses) {
|
|
1031
|
+
return statuses.map((status) => status.category ? `${status.name}:${status.category}` : status.name).join(",");
|
|
1032
|
+
}
|
|
1033
|
+
/**
|
|
1034
|
+
* Adds statuses to the ones a project already has, keeping theirs and their order.
|
|
1035
|
+
* @param current - The configured custom statuses.
|
|
1036
|
+
* @param added - Statuses to add; ones already present, built in or custom, are left as they are.
|
|
1037
|
+
* @param builtIn - Beads' own status names, which can't be configured again.
|
|
1038
|
+
* @returns The combined custom statuses.
|
|
1039
|
+
*/
|
|
1040
|
+
function mergeCustomStatuses(current, added, builtIn = []) {
|
|
1041
|
+
let merged = [...current];
|
|
1042
|
+
for (let status of added) {
|
|
1043
|
+
if (!statusName.test(status.name)) throw new TypeError(`Invalid custom status ${status.name}.`);
|
|
1044
|
+
if (builtIn.includes(status.name) || merged.some((entry) => entry.name === status.name)) continue;
|
|
1045
|
+
merged.push(status);
|
|
1046
|
+
}
|
|
1047
|
+
return merged;
|
|
1048
|
+
}
|
|
1049
|
+
var anchors = {
|
|
1050
|
+
active: {
|
|
1051
|
+
name: "open",
|
|
1052
|
+
after: true
|
|
1053
|
+
},
|
|
1054
|
+
wip: {
|
|
1055
|
+
name: "in_progress",
|
|
1056
|
+
after: true
|
|
1057
|
+
},
|
|
1058
|
+
frozen: {
|
|
1059
|
+
name: "deferred",
|
|
1060
|
+
after: true
|
|
1061
|
+
},
|
|
1062
|
+
done: {
|
|
1063
|
+
name: "closed",
|
|
1064
|
+
after: false
|
|
1065
|
+
}
|
|
1066
|
+
};
|
|
1067
|
+
/**
|
|
1068
|
+
* Places custom statuses beside the built-in status of their category, so a board shows a
|
|
1069
|
+
* workflow in order: in-progress ones after `in_progress`, done ones before `closed`.
|
|
1070
|
+
* @param statuses - Built-in statuses followed by custom ones, as Beads lists them.
|
|
1071
|
+
* @returns The same statuses in board order; custom ones keep their order within a category.
|
|
1072
|
+
*/
|
|
1073
|
+
function orderStatuses(statuses) {
|
|
1074
|
+
let ordered = statuses.filter((status) => !status.custom);
|
|
1075
|
+
let placed = /* @__PURE__ */ new Map();
|
|
1076
|
+
for (let status of statuses.filter((entry) => entry.custom)) {
|
|
1077
|
+
let anchor = categories.includes(status.category ?? "") ? anchors[status.category] : null;
|
|
1078
|
+
let index = anchor ? ordered.findIndex((entry) => entry.name === anchor.name) : -1;
|
|
1079
|
+
if (!anchor || index === -1) {
|
|
1080
|
+
ordered.push(status);
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
1083
|
+
let offset = placed.get(anchor.name) ?? 0;
|
|
1084
|
+
ordered.splice(anchor.after ? index + 1 + offset : index, 0, status);
|
|
1085
|
+
placed.set(anchor.name, offset + 1);
|
|
1086
|
+
}
|
|
1087
|
+
return ordered;
|
|
1088
|
+
}
|
|
1089
|
+
//#endregion
|
|
998
1090
|
//#region ../beads/src/server/cli-store.ts
|
|
999
1091
|
function output(stdout) {
|
|
1000
1092
|
return try_({
|
|
@@ -1105,7 +1197,40 @@ function writer(session) {
|
|
|
1105
1197
|
...(change.removeLabels ?? []).map((label) => `--remove-label=${label}`)
|
|
1106
1198
|
]),
|
|
1107
1199
|
setMetadata: (id, json) => asVoid(session.metadata(id, json)),
|
|
1108
|
-
importJsonl: (jsonl) => write(["import", "-"], jsonl)
|
|
1200
|
+
importJsonl: (jsonl) => write(["import", "-"], jsonl),
|
|
1201
|
+
addStatuses: (statuses) => gen(function* () {
|
|
1202
|
+
let { statuses: known } = yield* session.catalog;
|
|
1203
|
+
let builtIn = known.filter((status) => !status.custom).map((status) => status.name);
|
|
1204
|
+
let setting = yield* session.read([
|
|
1205
|
+
"config",
|
|
1206
|
+
"get",
|
|
1207
|
+
"status.custom"
|
|
1208
|
+
], (value) => {
|
|
1209
|
+
let raw = value && typeof value === "object" && "value" in value ? value.value : "";
|
|
1210
|
+
return typeof raw === "string" ? raw : "";
|
|
1211
|
+
});
|
|
1212
|
+
let merged = yield* try_({
|
|
1213
|
+
try: () => {
|
|
1214
|
+
let current = parseCustomStatuses(setting);
|
|
1215
|
+
return {
|
|
1216
|
+
current,
|
|
1217
|
+
next: mergeCustomStatuses(current, statuses, builtIn)
|
|
1218
|
+
};
|
|
1219
|
+
},
|
|
1220
|
+
catch: (cause) => new BeadsError({
|
|
1221
|
+
reason: "input",
|
|
1222
|
+
message: "Invalid custom status.",
|
|
1223
|
+
cause
|
|
1224
|
+
})
|
|
1225
|
+
});
|
|
1226
|
+
if (merged.next.length === merged.current.length) return;
|
|
1227
|
+
yield* write([
|
|
1228
|
+
"config",
|
|
1229
|
+
"set",
|
|
1230
|
+
"status.custom",
|
|
1231
|
+
formatCustomStatuses(merged.next)
|
|
1232
|
+
]);
|
|
1233
|
+
})
|
|
1109
1234
|
};
|
|
1110
1235
|
}
|
|
1111
1236
|
/**
|
|
@@ -1137,7 +1262,7 @@ var beadsWorkspaceStoreLayer = effect(BeadsStore, gen(function* () {
|
|
|
1137
1262
|
//#endregion
|
|
1138
1263
|
//#region ../beads/src/server/service.ts
|
|
1139
1264
|
var nonEmpty = String$1.check(isNonEmpty(), isMaxLength(32768), isPattern(/^[^\0]*$/));
|
|
1140
|
-
var optionsSchema
|
|
1265
|
+
var optionsSchema = Struct({
|
|
1141
1266
|
executable: optional(nonEmpty),
|
|
1142
1267
|
environment: optional(Record(String$1, Union([String$1, Undefined])))
|
|
1143
1268
|
});
|
|
@@ -1215,7 +1340,7 @@ function unsupportedCommand(error) {
|
|
|
1215
1340
|
*/
|
|
1216
1341
|
function beadsLayer(options = {}) {
|
|
1217
1342
|
return effectContext(gen(function* () {
|
|
1218
|
-
let settings = yield* decodeEffect(optionsSchema
|
|
1343
|
+
let settings = yield* decodeEffect(optionsSchema, { onExcessProperty: "error" })(options).pipe(mapError((cause) => new BeadsError({
|
|
1219
1344
|
reason: "input",
|
|
1220
1345
|
message: "Invalid Beads adapter options.",
|
|
1221
1346
|
cause
|
|
@@ -2084,7 +2209,7 @@ function beadsWorkSchema(snapshot, additional = []) {
|
|
|
2084
2209
|
label: label$2(value)
|
|
2085
2210
|
}))
|
|
2086
2211
|
} : field),
|
|
2087
|
-
statuses: [...statuses.values()].map((status) => ({
|
|
2212
|
+
statuses: orderStatuses([...statuses.values()]).map((status) => ({
|
|
2088
2213
|
value: status.name,
|
|
2089
2214
|
label: label$2(status.name),
|
|
2090
2215
|
category: category(status)
|
|
@@ -2217,7 +2342,7 @@ function text(value) {
|
|
|
2217
2342
|
function field(value, key) {
|
|
2218
2343
|
return value && typeof value === "object" && !Array.isArray(value) ? value[key] : value;
|
|
2219
2344
|
}
|
|
2220
|
-
function changed
|
|
2345
|
+
function changed(key, before, after) {
|
|
2221
2346
|
let name = fields[key] ?? label$1(key).toLowerCase();
|
|
2222
2347
|
if (key === "labels") {
|
|
2223
2348
|
let labels = (value) => Array.isArray(value) ? value.map(text).filter(Boolean) : text(value).split(",").map((v) => v.trim()).filter(Boolean);
|
|
@@ -2288,11 +2413,11 @@ function beadActivitySummary(event) {
|
|
|
2288
2413
|
let value = field(after, key);
|
|
2289
2414
|
return after && typeof after === "object" && !Array.isArray(after) && value !== void 0 && JSON.stringify(field(before, key)) !== JSON.stringify(value);
|
|
2290
2415
|
});
|
|
2291
|
-
return keys.length ? keys.map((key) => changed
|
|
2416
|
+
return keys.length ? keys.map((key) => changed(key, field(before, key), field(after, key))).join("; ") : "updated this task";
|
|
2292
2417
|
}
|
|
2293
2418
|
default: {
|
|
2294
2419
|
let key = event.type.replace(/_changed$/, "");
|
|
2295
|
-
if (Object.hasOwn(fields, key)) return changed
|
|
2420
|
+
if (Object.hasOwn(fields, key)) return changed(key, field(before, key), field(after, key));
|
|
2296
2421
|
return `recorded ${label$1(event.type).toLowerCase()}`;
|
|
2297
2422
|
}
|
|
2298
2423
|
}
|
|
@@ -2324,31 +2449,37 @@ function beadsWorkConnection(connection, schema) {
|
|
|
2324
2449
|
message: connection.message
|
|
2325
2450
|
});
|
|
2326
2451
|
}
|
|
2327
|
-
var
|
|
2452
|
+
var budKeys = [
|
|
2453
|
+
"budDiscussion",
|
|
2454
|
+
"budRun",
|
|
2455
|
+
"budContext"
|
|
2456
|
+
];
|
|
2457
|
+
function runSummary(metadata) {
|
|
2458
|
+
let run = metadata && typeof metadata === "object" && !Array.isArray(metadata) ? workRunSummary(metadata.budRun) : void 0;
|
|
2459
|
+
return run ? { run } : {};
|
|
2460
|
+
}
|
|
2328
2461
|
/**
|
|
2329
|
-
* Removes Bud's
|
|
2462
|
+
* Removes Bud's own records from task metadata.
|
|
2330
2463
|
* @param metadata - The issue's stored metadata.
|
|
2331
2464
|
* @returns The metadata a person sees and edits, or null when nothing else is stored.
|
|
2332
2465
|
*/
|
|
2333
2466
|
function taskMetadata(metadata) {
|
|
2334
2467
|
if (metadata === null || typeof metadata !== "object" || Array.isArray(metadata)) return metadata;
|
|
2335
|
-
if (!(
|
|
2336
|
-
let
|
|
2468
|
+
if (!budKeys.some((key) => key in metadata)) return metadata;
|
|
2469
|
+
let rest = Object.fromEntries(Object.entries(metadata).filter(([key]) => !budKeys.includes(key)));
|
|
2337
2470
|
return Object.keys(rest).length ? rest : null;
|
|
2338
2471
|
}
|
|
2339
2472
|
/**
|
|
2340
|
-
* Applies edited task metadata while keeping
|
|
2341
|
-
* @param edited - The metadata from a task edit, which may not set
|
|
2473
|
+
* Applies edited task metadata while keeping Bud's stored records.
|
|
2474
|
+
* @param edited - The metadata from a task edit, which may not set Bud's keys.
|
|
2342
2475
|
* @param stored - The issue's current metadata.
|
|
2343
2476
|
* @returns The metadata to write.
|
|
2344
2477
|
*/
|
|
2345
2478
|
function withDiscussionMetadata(edited, stored) {
|
|
2346
|
-
let
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
[discussionKey]: kept
|
|
2351
|
-
};
|
|
2479
|
+
let rest = Object.fromEntries(Object.entries(edited).filter(([key]) => !budKeys.includes(key)));
|
|
2480
|
+
if (stored === null || typeof stored !== "object" || Array.isArray(stored)) return rest;
|
|
2481
|
+
for (let key of budKeys) if (stored[key] !== void 0) rest[key] = stored[key];
|
|
2482
|
+
return rest;
|
|
2352
2483
|
}
|
|
2353
2484
|
/**
|
|
2354
2485
|
* Retains every native field, directed relationship, brief count and opaque revision.
|
|
@@ -2414,6 +2545,7 @@ function beadsWorkItem(issue, schema, ready) {
|
|
|
2414
2545
|
counts,
|
|
2415
2546
|
...ready === void 0 ? {} : { ready },
|
|
2416
2547
|
...issue.epicCloseable === null ? {} : { closeable: issue.epicCloseable },
|
|
2548
|
+
...runSummary(issue.metadata),
|
|
2417
2549
|
contentLoaded: issue.contentLoaded,
|
|
2418
2550
|
extra: taskMetadata(issue.metadata)
|
|
2419
2551
|
}, schema);
|
|
@@ -2859,10 +2991,48 @@ var beadsStoreWorkAdapterLayer = effect(WorkAdapter, gen(function* () {
|
|
|
2859
2991
|
}));
|
|
2860
2992
|
}).pipe(mapError(beadsWorkError));
|
|
2861
2993
|
}
|
|
2994
|
+
let beadCategories = {
|
|
2995
|
+
backlog: "active",
|
|
2996
|
+
unstarted: "active",
|
|
2997
|
+
started: "wip",
|
|
2998
|
+
blocked: "wip",
|
|
2999
|
+
deferred: "frozen",
|
|
3000
|
+
done: "done",
|
|
3001
|
+
cancelled: "done"
|
|
3002
|
+
};
|
|
3003
|
+
function addStatuses(root, statuses) {
|
|
3004
|
+
return store.change(root, (session) => session.addStatuses(statuses.map((status) => ({
|
|
3005
|
+
name: status.value,
|
|
3006
|
+
category: beadCategories[status.category]
|
|
3007
|
+
})))).pipe(mapError(beadsWorkError));
|
|
3008
|
+
}
|
|
3009
|
+
function budRecord(metadata, key) {
|
|
3010
|
+
return metadata !== null && typeof metadata === "object" && !Array.isArray(metadata) ? metadata[key] : void 0;
|
|
3011
|
+
}
|
|
3012
|
+
function readItemData(root, id, key) {
|
|
3013
|
+
return store.read(root, (session) => map(session.issue(id), (issue) => ({
|
|
3014
|
+
value: budRecord(issue.metadata, key) ?? null,
|
|
3015
|
+
revision: beadsWorkRevision(issue)
|
|
3016
|
+
}))).pipe(mapError(beadsWorkError));
|
|
3017
|
+
}
|
|
3018
|
+
function writeItemData(root, id, key, value, expectedRevision) {
|
|
3019
|
+
return store.change(root, (session) => gen(function* () {
|
|
3020
|
+
let current = yield* session.issue(id);
|
|
3021
|
+
yield* revision(current, expectedRevision);
|
|
3022
|
+
let stored = current.metadata;
|
|
3023
|
+
let metadata = stored !== null && typeof stored === "object" && !Array.isArray(stored) ? { ...stored } : {};
|
|
3024
|
+
metadata[key] = value;
|
|
3025
|
+
yield* session.setMetadata(id, JSON.stringify(metadata));
|
|
3026
|
+
return { revision: beadsWorkRevision(yield* session.issue(id)) };
|
|
3027
|
+
})).pipe(mapError(beadsWorkError));
|
|
3028
|
+
}
|
|
2862
3029
|
return {
|
|
2863
3030
|
read,
|
|
2864
3031
|
describe,
|
|
2865
3032
|
schema,
|
|
3033
|
+
readItemData,
|
|
3034
|
+
writeItemData,
|
|
3035
|
+
addStatuses,
|
|
2866
3036
|
list,
|
|
2867
3037
|
get,
|
|
2868
3038
|
section: loadSection,
|
|
@@ -3025,7 +3195,7 @@ function canonical(value) {
|
|
|
3025
3195
|
* @param count - The native message count.
|
|
3026
3196
|
* @returns An opaque content revision shared by list and detail.
|
|
3027
3197
|
*/
|
|
3028
|
-
function threadRevision
|
|
3198
|
+
function threadRevision(row, count) {
|
|
3029
3199
|
let { issue, record } = row;
|
|
3030
3200
|
return createHash("sha256").update(JSON.stringify(canonical({
|
|
3031
3201
|
id: issue.id,
|
|
@@ -3154,7 +3324,7 @@ function threadView(row, comments, loaded) {
|
|
|
3154
3324
|
issue.updatedAt,
|
|
3155
3325
|
...all.map((message) => message.createdAt)
|
|
3156
3326
|
].reduce((latest, at) => Date.parse(at) > Date.parse(latest) ? at : latest),
|
|
3157
|
-
revision: threadRevision
|
|
3327
|
+
revision: threadRevision(row, count),
|
|
3158
3328
|
metadata: {
|
|
3159
3329
|
creationId: record.creationId,
|
|
3160
3330
|
...record.source === void 0 ? {} : {
|
|
@@ -3303,7 +3473,8 @@ function threadChange(session, operation, identity) {
|
|
|
3303
3473
|
reopen: (id, reason) => tracked(session.reopen(id, reason)),
|
|
3304
3474
|
update: (id, change) => tracked(session.update(id, change)),
|
|
3305
3475
|
setMetadata: (id, json) => tracked(session.setMetadata(id, json)),
|
|
3306
|
-
importJsonl: (jsonl) => tracked(session.importJsonl(jsonl))
|
|
3476
|
+
importJsonl: (jsonl) => tracked(session.importJsonl(jsonl)),
|
|
3477
|
+
addStatuses: (statuses) => tracked(session.addStatuses(statuses))
|
|
3307
3478
|
}).pipe(mapError((cause) => confirmed ? new WorkError({
|
|
3308
3479
|
reason: "uncertain",
|
|
3309
3480
|
message: `The discussion may have changed. Refresh and inspect Beads before trying again.${identity ? ` Creation identity: ${identity}.` : ""}`,
|
|
@@ -3772,7 +3943,7 @@ function changeItemDiscussion(session, id, expectedRevision, change) {
|
|
|
3772
3943
|
}
|
|
3773
3944
|
//#endregion
|
|
3774
3945
|
//#region ../beads/src/server/thread-import.ts
|
|
3775
|
-
function identity
|
|
3946
|
+
function identity(parts) {
|
|
3776
3947
|
return createHash("sha256").update(JSON.stringify(parts)).digest("hex").slice(0, 32);
|
|
3777
3948
|
}
|
|
3778
3949
|
function same(left, right) {
|
|
@@ -3815,7 +3986,7 @@ function importThread(session, value) {
|
|
|
3815
3986
|
});
|
|
3816
3987
|
yield* validateLinks(session, input.thread.itemIds);
|
|
3817
3988
|
let { thread, source, sourceId } = input;
|
|
3818
|
-
let creationId = identity
|
|
3989
|
+
let creationId = identity([source, sourceId]);
|
|
3819
3990
|
let id = `${session.connection.project.prefix ?? "bud"}-thread-${creationId}`;
|
|
3820
3991
|
if ((yield* session.show(id, (value) => value).pipe(catchIf((cause) => cause.reason === "not-found", () => succeed(null)))) !== null) return yield* new WorkError({
|
|
3821
3992
|
reason: "conflict",
|
|
@@ -3833,7 +4004,7 @@ function importThread(session, value) {
|
|
|
3833
4004
|
createdAt: thread.createdAt,
|
|
3834
4005
|
updatedAt: thread.updatedAt,
|
|
3835
4006
|
bindings: thread.messages.map((message) => ({
|
|
3836
|
-
nativeId: identity
|
|
4007
|
+
nativeId: identity([
|
|
3837
4008
|
source,
|
|
3838
4009
|
sourceId,
|
|
3839
4010
|
message.id
|
|
@@ -3908,7 +4079,7 @@ var adapter = {
|
|
|
3908
4079
|
label: "Beads",
|
|
3909
4080
|
version: "1"
|
|
3910
4081
|
};
|
|
3911
|
-
var capabilities
|
|
4082
|
+
var capabilities = {
|
|
3912
4083
|
create: true,
|
|
3913
4084
|
reply: true,
|
|
3914
4085
|
status: true,
|
|
@@ -3941,7 +4112,7 @@ function listThreads(session, filter) {
|
|
|
3941
4112
|
state: "ready",
|
|
3942
4113
|
adapter
|
|
3943
4114
|
},
|
|
3944
|
-
capabilities
|
|
4115
|
+
capabilities,
|
|
3945
4116
|
threads: threads.slice(0, MAX_THREADS),
|
|
3946
4117
|
truncated: rows.length > 1e3 || items.truncated || threads.length > 1e3
|
|
3947
4118
|
});
|
|
@@ -4200,957 +4371,39 @@ var beadsStoreThreadAdapterLayer = effect(ThreadAdapter, gen(function* () {
|
|
|
4200
4371
|
}));
|
|
4201
4372
|
provide(beadsStoreThreadAdapterLayer, beadsWorkspaceStoreLayer);
|
|
4202
4373
|
//#endregion
|
|
4203
|
-
//#region ../comments/src/server/records.ts
|
|
4204
|
-
/**
|
|
4205
|
-
* Validates a persisted thread record of any version.
|
|
4206
|
-
* @param value - Untrusted data.
|
|
4207
|
-
* @returns The record.
|
|
4208
|
-
*/
|
|
4209
|
-
function parseStoredThread(value) {
|
|
4210
|
-
let input = object$1(value);
|
|
4211
|
-
if (input.version !== 3) return parseCommentThread(value);
|
|
4212
|
-
for (let key of Object.keys(input)) if (![
|
|
4213
|
-
"version",
|
|
4214
|
-
"id",
|
|
4215
|
-
"project",
|
|
4216
|
-
"branch",
|
|
4217
|
-
"createdCommit",
|
|
4218
|
-
"anchor",
|
|
4219
|
-
"itemIds",
|
|
4220
|
-
"comments",
|
|
4221
|
-
"status",
|
|
4222
|
-
"createdAt",
|
|
4223
|
-
"updatedAt",
|
|
4224
|
-
"metadata"
|
|
4225
|
-
].includes(key)) throw new Error(`Unsupported discussion field ${key}.`);
|
|
4226
|
-
let project = text$2(input.project, "Project", 4096);
|
|
4227
|
-
if (project !== "." && (project.startsWith("/") || project.includes("\\") || project.split("/").some((part) => !part || part === ".." || part === "."))) throw new Error("Invalid discussion project path.");
|
|
4228
|
-
let createdCommit = input.createdCommit === "" ? "" : revision(input.createdCommit);
|
|
4229
|
-
if (createdCommit === null) throw new Error("Invalid source commit.");
|
|
4230
|
-
if (!Array.isArray(input.comments)) throw new Error("A discussion needs messages.");
|
|
4231
|
-
let comments = input.comments.map(parseMessage);
|
|
4232
|
-
let parents = new Map(comments.map((message) => [message.id, message.parentId]));
|
|
4233
|
-
if (parents.size !== comments.length) throw new Error("Duplicate discussion message ID.");
|
|
4234
|
-
for (let message of comments) {
|
|
4235
|
-
let parent = message.parentId;
|
|
4236
|
-
let seen = /* @__PURE__ */ new Set([message.id]);
|
|
4237
|
-
while (parent !== null) {
|
|
4238
|
-
if (seen.has(parent) || !parents.has(parent)) throw new Error("Invalid discussion reply ancestry.");
|
|
4239
|
-
seen.add(parent);
|
|
4240
|
-
parent = parents.get(parent);
|
|
4241
|
-
}
|
|
4242
|
-
}
|
|
4243
|
-
let thread = parseThread({
|
|
4244
|
-
id: conversationId(input.id),
|
|
4245
|
-
status: input.status,
|
|
4246
|
-
anchor: input.anchor,
|
|
4247
|
-
itemIds: input.itemIds,
|
|
4248
|
-
messages: comments.slice(0, 200),
|
|
4249
|
-
messageCount: comments.length,
|
|
4250
|
-
messagesLoaded: true,
|
|
4251
|
-
messagesTruncated: comments.length > 200,
|
|
4252
|
-
createdAt: input.createdAt,
|
|
4253
|
-
updatedAt: input.updatedAt,
|
|
4254
|
-
revision: null,
|
|
4255
|
-
...input.metadata === void 0 ? {} : { metadata: input.metadata }
|
|
4256
|
-
});
|
|
4257
|
-
return {
|
|
4258
|
-
version: 3,
|
|
4259
|
-
id: thread.id,
|
|
4260
|
-
project,
|
|
4261
|
-
branch: text$2(input.branch, "Source branch", 1024),
|
|
4262
|
-
createdCommit,
|
|
4263
|
-
anchor: thread.anchor,
|
|
4264
|
-
itemIds: thread.itemIds,
|
|
4265
|
-
comments,
|
|
4266
|
-
status: thread.status,
|
|
4267
|
-
createdAt: thread.createdAt,
|
|
4268
|
-
updatedAt: thread.updatedAt,
|
|
4269
|
-
...thread.metadata === void 0 ? {} : { metadata: thread.metadata }
|
|
4270
|
-
};
|
|
4271
|
-
}
|
|
4272
|
-
/**
|
|
4273
|
-
* Tracks this record's content, independent of writes to other threads.
|
|
4274
|
-
* @param record - The validated persisted discussion.
|
|
4275
|
-
* @returns An opaque revision for compare-and-swap updates.
|
|
4276
|
-
*/
|
|
4277
|
-
function threadRevision(record) {
|
|
4278
|
-
return createHash("sha256").update(stableJson(record)).digest("hex");
|
|
4279
|
-
}
|
|
4280
|
-
/**
|
|
4281
|
-
* Turns a persisted record into the `@bud.sh/work` thread the app shows.
|
|
4282
|
-
* @param record - The validated persisted record.
|
|
4283
|
-
* @param loaded - Whether to include its messages, up to the limit, or only the first.
|
|
4284
|
-
* @returns The thread.
|
|
4285
|
-
*/
|
|
4286
|
-
function projectThread(record, loaded = true) {
|
|
4287
|
-
let thread = {
|
|
4288
|
-
id: record.id,
|
|
4289
|
-
status: record.status,
|
|
4290
|
-
anchor: record.anchor,
|
|
4291
|
-
itemIds: record.version === 3 ? record.itemIds : [],
|
|
4292
|
-
messages: record.comments.slice(0, loaded ? 200 : 1),
|
|
4293
|
-
messageCount: record.comments.length,
|
|
4294
|
-
messagesLoaded: loaded,
|
|
4295
|
-
messagesTruncated: loaded && record.comments.length > 200,
|
|
4296
|
-
createdAt: record.createdAt,
|
|
4297
|
-
updatedAt: record.updatedAt,
|
|
4298
|
-
revision: threadRevision(record),
|
|
4299
|
-
...record.version === 3 ? record.metadata === void 0 ? {} : { metadata: record.metadata } : { metadata: { git: {
|
|
4300
|
-
project: record.project,
|
|
4301
|
-
branch: record.branch,
|
|
4302
|
-
createdCommit: record.createdCommit
|
|
4303
|
-
} } }
|
|
4304
|
-
};
|
|
4305
|
-
return parseThread(thread);
|
|
4306
|
-
}
|
|
4307
|
-
/**
|
|
4308
|
-
* Promotes a legacy record without changing its capture or messages.
|
|
4309
|
-
* @param record - The current stored record.
|
|
4310
|
-
* @returns Its version 3 representation; persistence removes the old path in the same commit.
|
|
4311
|
-
*/
|
|
4312
|
-
function promoteThread(record) {
|
|
4313
|
-
if (record.version === 3) return record;
|
|
4314
|
-
return {
|
|
4315
|
-
...record,
|
|
4316
|
-
version: 3,
|
|
4317
|
-
itemIds: [],
|
|
4318
|
-
metadata: projectThread(record).metadata
|
|
4319
|
-
};
|
|
4320
|
-
}
|
|
4321
|
-
//#endregion
|
|
4322
|
-
//#region ../comments/src/server/merge.ts
|
|
4323
|
-
function changed(local, remote, base, field, hasBase = base !== void 0) {
|
|
4324
|
-
if (stableJson(local) === stableJson(remote)) return local;
|
|
4325
|
-
if (hasBase && stableJson(local) === stableJson(base)) return remote;
|
|
4326
|
-
if (hasBase && stableJson(remote) === stableJson(base)) return local;
|
|
4327
|
-
throw new CommentMergeError(`Conflicting discussion ${field}. Resolve the record in Git before syncing.`);
|
|
4328
|
-
}
|
|
4329
|
-
/**
|
|
4330
|
-
* Merges new records and mixed-version history while preserving captures.
|
|
4331
|
-
* @param local - The locally stored record.
|
|
4332
|
-
* @param remote - The validated incoming record.
|
|
4333
|
-
* @param base - The common ancestor, when one exists.
|
|
4334
|
-
* @returns The merged record, or a conflict when both sides changed the same value.
|
|
4335
|
-
*/
|
|
4336
|
-
function mergeStoredThreads(local, remote, base) {
|
|
4337
|
-
if (local.version !== 3 && remote.version !== 3 && base?.version !== 3) return mergeThreads(local, remote, base);
|
|
4338
|
-
let left = promoteThread(local);
|
|
4339
|
-
let right = promoteThread(remote);
|
|
4340
|
-
let ancestor = base && promoteThread(base);
|
|
4341
|
-
function identity(record) {
|
|
4342
|
-
let { comments, status, updatedAt, itemIds, metadata, ...identity } = record;
|
|
4343
|
-
return identity;
|
|
4344
|
-
}
|
|
4345
|
-
if (stableJson(identity(left)) !== stableJson(identity(right)) || ancestor && stableJson(identity(left)) !== stableJson(identity(ancestor))) throw new CommentMergeError("Conflicting discussion anchors. Resolve the record in Git before syncing.");
|
|
4346
|
-
let messages = /* @__PURE__ */ new Map();
|
|
4347
|
-
for (let message of [...left.comments, ...right.comments]) {
|
|
4348
|
-
let existing = messages.get(message.id);
|
|
4349
|
-
if (existing && stableJson(existing) !== stableJson(message)) throw new CommentMergeError("Conflicting versions of a message. Resolve the record in Git before syncing.");
|
|
4350
|
-
messages.set(message.id, message);
|
|
4351
|
-
}
|
|
4352
|
-
let itemIds = [.../* @__PURE__ */ new Set([
|
|
4353
|
-
...left.itemIds,
|
|
4354
|
-
...right.itemIds,
|
|
4355
|
-
...ancestor?.itemIds ?? []
|
|
4356
|
-
])].filter((id) => changed(left.itemIds.includes(id), right.itemIds.includes(id), ancestor?.itemIds.includes(id), "links")).sort();
|
|
4357
|
-
let metadata = changed(left.metadata, right.metadata, ancestor?.metadata, "metadata", ancestor !== void 0);
|
|
4358
|
-
return parseStoredThread({
|
|
4359
|
-
...left,
|
|
4360
|
-
status: changed(left.status, right.status, ancestor?.status, "status"),
|
|
4361
|
-
itemIds,
|
|
4362
|
-
updatedAt: left.updatedAt > right.updatedAt ? left.updatedAt : right.updatedAt,
|
|
4363
|
-
comments: [...messages.values()].sort((a, b) => a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id)),
|
|
4364
|
-
metadata
|
|
4365
|
-
});
|
|
4366
|
-
}
|
|
4367
|
-
//#endregion
|
|
4368
|
-
//#region ../comments/src/server/errors.ts
|
|
4369
|
-
/** Safe domain messages; nested diagnostics stay on the trusted server. */
|
|
4370
|
-
var CommentsError = class extends TaggedError("CommentsError") {};
|
|
4371
|
-
//#endregion
|
|
4372
|
-
//#region ../comments/src/server/persistence.ts
|
|
4373
|
-
var commentsRef = "refs/heads/bud/comments";
|
|
4374
|
-
var threadPath = /^comments\/v(\d+)\/threads\/([a-f0-9-]{36})\/thread\.json$/;
|
|
4375
|
-
var optionsSchema = Struct({
|
|
4376
|
-
root: String$1.check(isNonEmpty(), isPattern(/^[^\0]*$/)),
|
|
4377
|
-
remote: String$1.check(isPattern(/^[A-Za-z0-9][A-Za-z0-9._-]*$/)),
|
|
4378
|
-
routes: ArraySchema(Struct({
|
|
4379
|
-
id: String$1.check(isNonEmpty()),
|
|
4380
|
-
path: String$1.check(isNonEmpty())
|
|
4381
|
-
}))
|
|
4382
|
-
});
|
|
4383
|
-
function fromGit(cause) {
|
|
4384
|
-
if (cause._tag === "GitIdentityError") return new CommentsError({
|
|
4385
|
-
reason: "identity",
|
|
4386
|
-
message: "Set git config user.name and user.email in this project before commenting.",
|
|
4387
|
-
cause
|
|
4388
|
-
});
|
|
4389
|
-
if (cause._tag === "GitRepositoryError") return new CommentsError({
|
|
4390
|
-
reason: "repository",
|
|
4391
|
-
message: "Initialize a Git repository for this project to save comments.",
|
|
4392
|
-
cause
|
|
4393
|
-
});
|
|
4394
|
-
if (cause._tag === "GitConflictError") return new CommentsError({
|
|
4395
|
-
reason: "conflict",
|
|
4396
|
-
message: "Comments changed elsewhere. Reload the conversation and try again.",
|
|
4397
|
-
cause
|
|
4398
|
-
});
|
|
4399
|
-
return new CommentsError({
|
|
4400
|
-
reason: "storage",
|
|
4401
|
-
message: "The Git operation failed. Your existing comments remain saved. Check the repository and Git configuration.",
|
|
4402
|
-
cause
|
|
4403
|
-
});
|
|
4404
|
-
}
|
|
4405
|
-
function attempt$1(tryValue, reason = "input", message) {
|
|
4406
|
-
return try_({
|
|
4407
|
-
try: tryValue,
|
|
4408
|
-
catch: (cause) => new CommentsError({
|
|
4409
|
-
reason,
|
|
4410
|
-
message: message ?? (cause instanceof Error ? cause.message : "Invalid comment."),
|
|
4411
|
-
cause
|
|
4412
|
-
})
|
|
4413
|
-
});
|
|
4414
|
-
}
|
|
4415
|
-
function failure(reason, message) {
|
|
4416
|
-
return (cause) => new CommentsError({
|
|
4417
|
-
reason,
|
|
4418
|
-
message,
|
|
4419
|
-
cause
|
|
4420
|
-
});
|
|
4421
|
-
}
|
|
4422
|
-
/**
|
|
4423
|
-
* Captures shared persistence for legacy comments and the thread adapter.
|
|
4424
|
-
* @param options - The trusted project, remote and configured routes.
|
|
4425
|
-
* @returns Scoped operations; repository access starts only when an operation runs.
|
|
4426
|
-
*/
|
|
4427
|
-
function createCommentStorage(options) {
|
|
4428
|
-
return gen(function* () {
|
|
4429
|
-
let input = yield* decodeEffect(optionsSchema, { onExcessProperty: "error" })(options).pipe(mapError(failure("input", "Invalid comment store configuration.")));
|
|
4430
|
-
let git = yield* Git;
|
|
4431
|
-
let fs = yield* FileSystem;
|
|
4432
|
-
let paths = yield* Path;
|
|
4433
|
-
function location() {
|
|
4434
|
-
return gen(function* () {
|
|
4435
|
-
let repo = yield* git.discover(input.root).pipe(mapError(fromGit));
|
|
4436
|
-
if (repo.info.bare) return yield* new CommentsError({
|
|
4437
|
-
reason: "repository",
|
|
4438
|
-
message: "Open a source worktree before editing comments."
|
|
4439
|
-
});
|
|
4440
|
-
if ((yield* repo.symbolicRef.pipe(mapError(fromGit))) === "refs/heads/bud/comments") return yield* new CommentsError({
|
|
4441
|
-
reason: "repository",
|
|
4442
|
-
message: "Switch to your source branch before editing comments."
|
|
4443
|
-
});
|
|
4444
|
-
let root = yield* fs.realPath(input.root).pipe(mapError(failure("repository", "Unable to resolve the project directory.")));
|
|
4445
|
-
let projectId = paths.relative(repo.info.root, root).replaceAll("\\", "/") || ".";
|
|
4446
|
-
if (projectId === ".." || projectId.startsWith("../") || paths.isAbsolute(projectId)) return yield* new CommentsError({
|
|
4447
|
-
reason: "repository",
|
|
4448
|
-
message: "The project must be inside its Git repository."
|
|
4449
|
-
});
|
|
4450
|
-
return {
|
|
4451
|
-
repo,
|
|
4452
|
-
projectId
|
|
4453
|
-
};
|
|
4454
|
-
});
|
|
4455
|
-
}
|
|
4456
|
-
function headAt(repo, ref = commentsRef) {
|
|
4457
|
-
return repo.resolve(ref).pipe(mapError(fromGit));
|
|
4458
|
-
}
|
|
4459
|
-
function author(repo) {
|
|
4460
|
-
return repo.identity.pipe(mapError(fromGit), map(({ name, email }) => ({
|
|
4461
|
-
name,
|
|
4462
|
-
email,
|
|
4463
|
-
id: createHash("sha256").update(`${name}\0${email}`).digest("hex").slice(0, 16)
|
|
4464
|
-
})));
|
|
4465
|
-
}
|
|
4466
|
-
function source(repo) {
|
|
4467
|
-
return gen(function* () {
|
|
4468
|
-
return {
|
|
4469
|
-
branch: (yield* repo.symbolicRef.pipe(mapError(fromGit)))?.replace(/^refs\/heads\//, "") ?? "detached",
|
|
4470
|
-
createdCommit: (yield* headAt(repo, "HEAD")) ?? ""
|
|
4471
|
-
};
|
|
4472
|
-
});
|
|
4473
|
-
}
|
|
4474
|
-
function recordsAt(repo, head) {
|
|
4475
|
-
return gen(function* () {
|
|
4476
|
-
let records = /* @__PURE__ */ new Map();
|
|
4477
|
-
let blobs = /* @__PURE__ */ new Map();
|
|
4478
|
-
if (!head) return {
|
|
4479
|
-
threads: records,
|
|
4480
|
-
blobs
|
|
4481
|
-
};
|
|
4482
|
-
let entries = yield* repo.listTree(head, "comments").pipe(mapError(fromGit));
|
|
4483
|
-
for (let entry of entries) {
|
|
4484
|
-
let match = threadPath.exec(entry.path);
|
|
4485
|
-
if (!match) continue;
|
|
4486
|
-
if (entry.type !== "blob") return yield* new CommentsError({
|
|
4487
|
-
reason: "conflict",
|
|
4488
|
-
message: "The comments branch contains an unsupported or invalid thread."
|
|
4489
|
-
});
|
|
4490
|
-
let bytes = yield* repo.readBlob(entry.oid).pipe(mapError(fromGit));
|
|
4491
|
-
let value = yield* attempt$1(() => JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes)), "conflict", "The comments branch contains invalid JSON. Repair that record before syncing.");
|
|
4492
|
-
let thread = yield* attempt$1(() => {
|
|
4493
|
-
let thread = parseStoredThread(value);
|
|
4494
|
-
if (thread.id !== match[2] || String(thread.version) !== match[1]) throw new Error("Mismatched thread ID or storage version.");
|
|
4495
|
-
if (records.has(thread.id)) throw new Error("Duplicate thread across versions.");
|
|
4496
|
-
return thread;
|
|
4497
|
-
}, "conflict", "The comments branch contains an unsupported or invalid thread.");
|
|
4498
|
-
records.set(thread.id, thread);
|
|
4499
|
-
blobs.set(thread.id, bytes);
|
|
4500
|
-
}
|
|
4501
|
-
return {
|
|
4502
|
-
threads: records,
|
|
4503
|
-
blobs
|
|
4504
|
-
};
|
|
4505
|
-
});
|
|
4506
|
-
}
|
|
4507
|
-
function snapshot(repo, projectId) {
|
|
4508
|
-
return gen(function* () {
|
|
4509
|
-
let head = yield* headAt(repo);
|
|
4510
|
-
return {
|
|
4511
|
-
head,
|
|
4512
|
-
threads: [...(yield* recordsAt(repo, head)).threads.values()].filter((thread) => thread.project === projectId).sort((a, b) => a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id))
|
|
4513
|
-
};
|
|
4514
|
-
});
|
|
4515
|
-
}
|
|
4516
|
-
function commitRecords(repo, parent, records, message, previous) {
|
|
4517
|
-
return gen(function* () {
|
|
4518
|
-
let files = yield* attempt$1(() => records.flatMap((record) => {
|
|
4519
|
-
let validated = parseStoredThread(record);
|
|
4520
|
-
let contents = stableJson(validated);
|
|
4521
|
-
let existing = previous.records.threads.get(validated.id);
|
|
4522
|
-
if (existing && stableJson(existing) === contents) return [];
|
|
4523
|
-
let remote = previous.remote?.threads.get(validated.id);
|
|
4524
|
-
let original = remote && stableJson(remote) === contents ? previous.remote?.blobs.get(validated.id) : void 0;
|
|
4525
|
-
return [...existing && existing.version !== validated.version ? [{
|
|
4526
|
-
path: `comments/v${existing.version}/threads/${existing.id}/thread.json`,
|
|
4527
|
-
contents: null
|
|
4528
|
-
}] : [], {
|
|
4529
|
-
path: `comments/v${validated.version}/threads/${validated.id}/thread.json`,
|
|
4530
|
-
contents: original ?? `${contents}\n`
|
|
4531
|
-
}];
|
|
4532
|
-
}));
|
|
4533
|
-
return yield* repo.commitSnapshot({
|
|
4534
|
-
ref: commentsRef,
|
|
4535
|
-
expectedHead: parent,
|
|
4536
|
-
files,
|
|
4537
|
-
message,
|
|
4538
|
-
extraParents: previous.extraParent && previous.extraParent !== parent ? [previous.extraParent] : void 0
|
|
4539
|
-
}).pipe(mapError(fromGit));
|
|
4540
|
-
});
|
|
4541
|
-
}
|
|
4542
|
-
function list() {
|
|
4543
|
-
return gen(function* () {
|
|
4544
|
-
let { repo, projectId } = yield* location();
|
|
4545
|
-
return yield* snapshot(repo, projectId);
|
|
4546
|
-
});
|
|
4547
|
-
}
|
|
4548
|
-
function write(expectedHead, value) {
|
|
4549
|
-
return gen(function* () {
|
|
4550
|
-
let command = yield* attempt$1(() => {
|
|
4551
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Invalid comment change.");
|
|
4552
|
-
return parseCommentInput({
|
|
4553
|
-
...value,
|
|
4554
|
-
head: expectedHead
|
|
4555
|
-
}, input.routes, value.id);
|
|
4556
|
-
});
|
|
4557
|
-
let { repo, projectId } = yield* location();
|
|
4558
|
-
let head = yield* headAt(repo);
|
|
4559
|
-
if (head !== command.head) return yield* new CommentsError({
|
|
4560
|
-
reason: "conflict",
|
|
4561
|
-
message: "Comments changed elsewhere. Reload the conversation and try again."
|
|
4562
|
-
});
|
|
4563
|
-
let records = yield* recordsAt(repo, head);
|
|
4564
|
-
let change = command.change;
|
|
4565
|
-
let identity = yield* author(repo);
|
|
4566
|
-
let now = new Date(yield* currentTimeMillis).toISOString();
|
|
4567
|
-
let comment = change.body ? {
|
|
4568
|
-
id: randomUUID(),
|
|
4569
|
-
body: change.body,
|
|
4570
|
-
author: identity,
|
|
4571
|
-
createdAt: now,
|
|
4572
|
-
parentId: change.parentId ?? null
|
|
4573
|
-
} : void 0;
|
|
4574
|
-
let thread;
|
|
4575
|
-
if (change.id) {
|
|
4576
|
-
let previous = records.threads.get(change.id);
|
|
4577
|
-
if (!previous || previous.project !== projectId || previous.version === 3) return yield* new CommentsError({
|
|
4578
|
-
reason: "not-found",
|
|
4579
|
-
message: "This conversation no longer exists."
|
|
4580
|
-
});
|
|
4581
|
-
if (change.parentId && !previous.comments.some((comment) => comment.id === change.parentId)) return yield* new CommentsError({
|
|
4582
|
-
reason: "input",
|
|
4583
|
-
message: "Reply to a comment in this conversation."
|
|
4584
|
-
});
|
|
4585
|
-
thread = {
|
|
4586
|
-
...previous,
|
|
4587
|
-
comments: comment ? [...previous.comments, comment] : previous.comments,
|
|
4588
|
-
status: change.status ?? previous.status,
|
|
4589
|
-
updatedAt: now
|
|
4590
|
-
};
|
|
4591
|
-
} else {
|
|
4592
|
-
let { branch, createdCommit } = yield* source(repo);
|
|
4593
|
-
thread = yield* attempt$1(() => parseCommentThread({
|
|
4594
|
-
version: "type" in change.anchor && change.anchor.type === "code" ? 2 : 1,
|
|
4595
|
-
id: randomUUID(),
|
|
4596
|
-
project: projectId,
|
|
4597
|
-
branch,
|
|
4598
|
-
createdCommit,
|
|
4599
|
-
anchor: change.anchor,
|
|
4600
|
-
comments: [comment],
|
|
4601
|
-
status: "open",
|
|
4602
|
-
createdAt: now,
|
|
4603
|
-
updatedAt: now
|
|
4604
|
-
}));
|
|
4605
|
-
}
|
|
4606
|
-
yield* commitRecords(repo, head, [thread], change.id ? "bud: update conversation" : "bud: start conversation", { records });
|
|
4607
|
-
return yield* snapshot(repo, projectId);
|
|
4608
|
-
});
|
|
4609
|
-
}
|
|
4610
|
-
function sync(direction = "both") {
|
|
4611
|
-
return gen(function* () {
|
|
4612
|
-
let { repo, projectId } = yield* location();
|
|
4613
|
-
let remote = input.remote;
|
|
4614
|
-
if (!(yield* repo.remoteUrl(remote).pipe(mapError(fromGit)))) return yield* new CommentsError({
|
|
4615
|
-
reason: "remote-missing",
|
|
4616
|
-
message: `Add the Git remote “${remote}” before syncing comments.`
|
|
4617
|
-
});
|
|
4618
|
-
let remoteExists = yield* repo.remoteHead(remote, commentsRef).pipe(mapError(failure("remote-read", "Cannot reach the Git remote. Check your connection and Git credentials; local comments are saved.")));
|
|
4619
|
-
let localHead = yield* headAt(repo);
|
|
4620
|
-
let localRecords = yield* recordsAt(repo, localHead);
|
|
4621
|
-
if (remoteExists && direction !== "push") {
|
|
4622
|
-
let remoteHead = yield* repo.fetchRef(remote, commentsRef, `refs/bud/remotes/${remote}/comments`).pipe(mapError(failure("remote-fetch", "Could not fetch comments from the Git remote. Local comments are saved.")));
|
|
4623
|
-
if (!remoteHead) return yield* new CommentsError({
|
|
4624
|
-
reason: "remote-fetch",
|
|
4625
|
-
message: "Remote comments changed during fetch. Try syncing again."
|
|
4626
|
-
});
|
|
4627
|
-
let remoteRecords = yield* recordsAt(repo, remoteHead);
|
|
4628
|
-
if (localHead !== remoteHead) {
|
|
4629
|
-
if (!localHead) yield* repo.compareAndSwapRef(commentsRef, remoteHead, null).pipe(mapError(fromGit));
|
|
4630
|
-
else {
|
|
4631
|
-
let base = yield* repo.mergeBase(localHead, remoteHead).pipe(mapError(fromGit));
|
|
4632
|
-
if (base === localHead) {
|
|
4633
|
-
yield* attempt$1(() => {
|
|
4634
|
-
for (let [id, local] of localRecords.threads) {
|
|
4635
|
-
let remote = remoteRecords.threads.get(id);
|
|
4636
|
-
if (remote) mergeStoredThreads(local, remote, local);
|
|
4637
|
-
}
|
|
4638
|
-
}, "conflict");
|
|
4639
|
-
yield* repo.compareAndSwapRef(commentsRef, remoteHead, localHead).pipe(mapError(fromGit));
|
|
4640
|
-
} else if (base !== remoteHead) {
|
|
4641
|
-
let baseRecords = yield* recordsAt(repo, base);
|
|
4642
|
-
yield* commitRecords(repo, localHead, yield* attempt$1(() => [.../* @__PURE__ */ new Set([...localRecords.threads.keys(), ...remoteRecords.threads.keys()])].map((id) => {
|
|
4643
|
-
let local = localRecords.threads.get(id);
|
|
4644
|
-
let remote = remoteRecords.threads.get(id);
|
|
4645
|
-
return local && remote ? mergeStoredThreads(local, remote, baseRecords.threads.get(id)) : local ?? remote;
|
|
4646
|
-
}), "conflict"), "bud: sync conversations", {
|
|
4647
|
-
records: localRecords,
|
|
4648
|
-
remote: remoteRecords,
|
|
4649
|
-
extraParent: remoteHead
|
|
4650
|
-
});
|
|
4651
|
-
}
|
|
4652
|
-
}
|
|
4653
|
-
}
|
|
4654
|
-
}
|
|
4655
|
-
let publishHead = yield* headAt(repo);
|
|
4656
|
-
if (publishHead && direction !== "pull") {
|
|
4657
|
-
yield* recordsAt(repo, publishHead);
|
|
4658
|
-
yield* repo.pushRef(remote, commentsRef, publishHead).pipe(mapError(failure("remote-push", "Comments are saved locally. The remote rejected the push; check Git access or sync again to include newer comments.")));
|
|
4659
|
-
}
|
|
4660
|
-
return yield* snapshot(repo, projectId);
|
|
4661
|
-
});
|
|
4662
|
-
}
|
|
4663
|
-
function seed(seeds) {
|
|
4664
|
-
return gen(function* () {
|
|
4665
|
-
yield* attempt$1(() => {
|
|
4666
|
-
if (!Array.isArray(seeds) || seeds.length > 1e3) throw new Error("Invalid seed conversations.");
|
|
4667
|
-
if (new Set(seeds.map((seed) => seed.id)).size !== seeds.length) throw new Error("Duplicate seed conversation ID.");
|
|
4668
|
-
});
|
|
4669
|
-
let { repo, projectId } = yield* location();
|
|
4670
|
-
let head = yield* headAt(repo);
|
|
4671
|
-
let records = yield* recordsAt(repo, head);
|
|
4672
|
-
let missing = yield* attempt$1(() => seeds.filter((seed) => {
|
|
4673
|
-
let existing = records.threads.get(seed.id);
|
|
4674
|
-
if (existing && existing.project !== projectId) throw new Error("That seed conversation belongs to another project.");
|
|
4675
|
-
return !existing;
|
|
4676
|
-
}), "conflict");
|
|
4677
|
-
if (!missing.length) return yield* snapshot(repo, projectId);
|
|
4678
|
-
let identity = yield* author(repo);
|
|
4679
|
-
let now = new Date(yield* currentTimeMillis).toISOString();
|
|
4680
|
-
let { branch, createdCommit } = yield* source(repo);
|
|
4681
|
-
yield* commitRecords(repo, head, yield* attempt$1(() => missing.map((seed) => {
|
|
4682
|
-
if (seed.anchor.element !== null || !input.routes.some((route) => route.id === seed.anchor.pageId && route.path === seed.anchor.pagePath)) throw new Error("Seed comments need coordinates on a configured route.");
|
|
4683
|
-
return parseCommentThread({
|
|
4684
|
-
version: 1,
|
|
4685
|
-
id: seed.id,
|
|
4686
|
-
project: projectId,
|
|
4687
|
-
branch,
|
|
4688
|
-
createdCommit,
|
|
4689
|
-
anchor: seed.anchor,
|
|
4690
|
-
comments: seed.messages.map((body) => ({
|
|
4691
|
-
id: randomUUID(),
|
|
4692
|
-
body,
|
|
4693
|
-
author: identity,
|
|
4694
|
-
createdAt: now,
|
|
4695
|
-
parentId: null
|
|
4696
|
-
})),
|
|
4697
|
-
status: "open",
|
|
4698
|
-
createdAt: now,
|
|
4699
|
-
updatedAt: now
|
|
4700
|
-
});
|
|
4701
|
-
})), "bud: seed development conversations", { records });
|
|
4702
|
-
return yield* snapshot(repo, projectId);
|
|
4703
|
-
});
|
|
4704
|
-
}
|
|
4705
|
-
return {
|
|
4706
|
-
list: list(),
|
|
4707
|
-
write,
|
|
4708
|
-
sync,
|
|
4709
|
-
seed,
|
|
4710
|
-
root: input.root,
|
|
4711
|
-
paths,
|
|
4712
|
-
location,
|
|
4713
|
-
source,
|
|
4714
|
-
author,
|
|
4715
|
-
headAt,
|
|
4716
|
-
recordsAt,
|
|
4717
|
-
snapshot,
|
|
4718
|
-
commitRecords
|
|
4719
|
-
};
|
|
4720
|
-
});
|
|
4721
|
-
}
|
|
4722
|
-
Service()("@bud.sh/comments/Comments");
|
|
4723
|
-
//#endregion
|
|
4724
|
-
//#region ../comments/src/server/adapter.ts
|
|
4725
|
-
var capabilities = {
|
|
4726
|
-
create: true,
|
|
4727
|
-
reply: true,
|
|
4728
|
-
status: true,
|
|
4729
|
-
link: true,
|
|
4730
|
-
sync: true,
|
|
4731
|
-
edit: false,
|
|
4732
|
-
delete: false
|
|
4733
|
-
};
|
|
4734
|
-
var disabled = {
|
|
4735
|
-
create: false,
|
|
4736
|
-
reply: false,
|
|
4737
|
-
status: false,
|
|
4738
|
-
link: false,
|
|
4739
|
-
sync: false,
|
|
4740
|
-
edit: false,
|
|
4741
|
-
delete: false
|
|
4742
|
-
};
|
|
4743
|
-
var identity = {
|
|
4744
|
-
id: "git-comments",
|
|
4745
|
-
label: "Git comments",
|
|
4746
|
-
version: "3"
|
|
4747
|
-
};
|
|
4748
|
-
function fromComments(cause) {
|
|
4749
|
-
let reason = cause.reason === "repository" || cause.reason.startsWith("remote-") ? "unavailable" : cause.reason === "identity" ? "refused" : [
|
|
4750
|
-
"input",
|
|
4751
|
-
"not-found",
|
|
4752
|
-
"conflict"
|
|
4753
|
-
].includes(cause.reason) ? cause.reason : "storage";
|
|
4754
|
-
return new WorkError({
|
|
4755
|
-
reason,
|
|
4756
|
-
message: cause.message,
|
|
4757
|
-
cause
|
|
4758
|
-
});
|
|
4759
|
-
}
|
|
4760
|
-
function attempt(parse, reason = "input") {
|
|
4761
|
-
return try_({
|
|
4762
|
-
try: parse,
|
|
4763
|
-
catch: (cause) => new WorkError({
|
|
4764
|
-
reason,
|
|
4765
|
-
message: cause instanceof Error ? cause.message : "Invalid discussion data.",
|
|
4766
|
-
cause
|
|
4767
|
-
})
|
|
4768
|
-
});
|
|
4769
|
-
}
|
|
4770
|
-
function snapshot(records, filter) {
|
|
4771
|
-
let selected = records.filter((record) => {
|
|
4772
|
-
if (filter.itemId && (record.version !== 3 || !record.itemIds.includes(filter.itemId))) return false;
|
|
4773
|
-
let kind = record.anchor ? "type" in record.anchor ? "code" : "canvas" : null;
|
|
4774
|
-
if (filter.anchorKind && filter.anchorKind !== kind) return false;
|
|
4775
|
-
return !filter.pagePath || record.anchor && !("type" in record.anchor) && record.anchor.pagePath === filter.pagePath;
|
|
4776
|
-
});
|
|
4777
|
-
return parseThreadSnapshot({
|
|
4778
|
-
connection: {
|
|
4779
|
-
state: "ready",
|
|
4780
|
-
adapter: identity
|
|
4781
|
-
},
|
|
4782
|
-
capabilities,
|
|
4783
|
-
threads: selected.slice(0, MAX_THREADS).map((record) => projectThread(record, false)),
|
|
4784
|
-
truncated: selected.length > MAX_THREADS
|
|
4785
|
-
});
|
|
4786
|
-
}
|
|
4787
|
-
function metadataSource(value, source, sourceId) {
|
|
4788
|
-
return !!value && typeof value === "object" && !Array.isArray(value) && value.source === source && value.sourceId === sourceId;
|
|
4789
|
-
}
|
|
4790
|
-
/**
|
|
4791
|
-
* Adapts versioned Git comments to discussions without touching the source index or branch.
|
|
4792
|
-
* @param options - The trusted project root, remote and configured canvas routes.
|
|
4793
|
-
* @returns A thread service backed by temporary-index commits and compare-and-swap ref updates.
|
|
4794
|
-
*/
|
|
4795
|
-
function gitThreadAdapterLayer(options) {
|
|
4796
|
-
return effect(ThreadAdapter, gen(function* () {
|
|
4797
|
-
let store = yield* createCommentStorage(options).pipe(mapError(fromComments));
|
|
4798
|
-
let fs = yield* FileSystem;
|
|
4799
|
-
function root(value, program) {
|
|
4800
|
-
return attempt(() => {
|
|
4801
|
-
if (typeof value !== "string" || !value || value.includes("\0") || store.paths.resolve(value) !== store.paths.resolve(store.root)) throw new Error("The discussion root must match this companion’s project.");
|
|
4802
|
-
}).pipe(andThen(program));
|
|
4803
|
-
}
|
|
4804
|
-
function current() {
|
|
4805
|
-
return gen(function* () {
|
|
4806
|
-
let location = yield* store.location();
|
|
4807
|
-
let head = yield* store.headAt(location.repo);
|
|
4808
|
-
let records = yield* store.recordsAt(location.repo, head);
|
|
4809
|
-
return {
|
|
4810
|
-
...location,
|
|
4811
|
-
head,
|
|
4812
|
-
records
|
|
4813
|
-
};
|
|
4814
|
-
}).pipe(mapError(fromComments));
|
|
4815
|
-
}
|
|
4816
|
-
function record(state, id) {
|
|
4817
|
-
return gen(function* () {
|
|
4818
|
-
yield* attempt(() => conversationId(id));
|
|
4819
|
-
let record = state.records.threads.get(id);
|
|
4820
|
-
if (!record || record.project !== state.projectId) return yield* new WorkError({
|
|
4821
|
-
reason: "not-found",
|
|
4822
|
-
message: "This discussion no longer exists."
|
|
4823
|
-
});
|
|
4824
|
-
return record;
|
|
4825
|
-
});
|
|
4826
|
-
}
|
|
4827
|
-
function expected(record, revision) {
|
|
4828
|
-
let currentRevision = threadRevision(record);
|
|
4829
|
-
return currentRevision === revision ? void_ : fail(new WorkError({
|
|
4830
|
-
reason: "conflict",
|
|
4831
|
-
message: "This discussion changed. Reload it before saving your draft.",
|
|
4832
|
-
currentRevision
|
|
4833
|
-
}));
|
|
4834
|
-
}
|
|
4835
|
-
function save(state, value, message) {
|
|
4836
|
-
return gen(function* () {
|
|
4837
|
-
let next = yield* attempt(() => parseStoredThread(value));
|
|
4838
|
-
let projected = yield* attempt(() => projectThread(next), "storage");
|
|
4839
|
-
yield* store.commitRecords(state.repo, state.head, [next], message, { records: state.records }).pipe(mapError((cause) => cause.reason === "conflict" ? cause : fromComments(cause)));
|
|
4840
|
-
return projected;
|
|
4841
|
-
});
|
|
4842
|
-
}
|
|
4843
|
-
function fresh(program) {
|
|
4844
|
-
return program.pipe(retry({
|
|
4845
|
-
times: 2,
|
|
4846
|
-
while: (error) => error._tag === "CommentsError"
|
|
4847
|
-
}), mapError((error) => error._tag === "CommentsError" ? fromComments(error) : error));
|
|
4848
|
-
}
|
|
4849
|
-
function change(id, revision, update) {
|
|
4850
|
-
return fresh(gen(function* () {
|
|
4851
|
-
let state = yield* current();
|
|
4852
|
-
let previous = yield* record(state, id);
|
|
4853
|
-
yield* expected(previous, revision);
|
|
4854
|
-
let now = yield* currentTimeMillis;
|
|
4855
|
-
let at = new Date(Math.max(now, Date.parse(previous.updatedAt))).toISOString();
|
|
4856
|
-
return yield* save(state, yield* attempt(() => update(previous, at)), "bud: update discussion");
|
|
4857
|
-
}));
|
|
4858
|
-
}
|
|
4859
|
-
function link(project, id, itemIds, revision, remove) {
|
|
4860
|
-
return root(project, gen(function* () {
|
|
4861
|
-
let input = yield* attempt(() => parseThreadLinkInput({
|
|
4862
|
-
itemIds,
|
|
4863
|
-
expectedRevision: revision
|
|
4864
|
-
}));
|
|
4865
|
-
return yield* change(id, input.expectedRevision, (previous, at) => {
|
|
4866
|
-
let next = promoteThread(previous);
|
|
4867
|
-
let linked = remove ? next.itemIds.filter((id) => !input.itemIds.includes(id)) : [.../* @__PURE__ */ new Set([...next.itemIds, ...input.itemIds])];
|
|
4868
|
-
if (linked.length > 100) throw new Error("This discussion has too many linked work items.");
|
|
4869
|
-
if (!next.anchor && !linked.length) throw new Error("An unanchored discussion needs a linked work item.");
|
|
4870
|
-
return {
|
|
4871
|
-
...next,
|
|
4872
|
-
itemIds: linked,
|
|
4873
|
-
updatedAt: at
|
|
4874
|
-
};
|
|
4875
|
-
});
|
|
4876
|
-
}));
|
|
4877
|
-
}
|
|
4878
|
-
function watch(project) {
|
|
4879
|
-
return unwrap(root(project, gen(function* () {
|
|
4880
|
-
let { repo } = yield* store.location().pipe(mapError(fromComments));
|
|
4881
|
-
let head = store.headAt(repo).pipe(orElseSucceed(() => null));
|
|
4882
|
-
return concat(fromEffect(head), fs.watch(repo.info.commonDirectory, { recursive: true }).pipe(debounce("100 millis"), mapEffect(() => head))).pipe(changes, drop(1), as$1(void 0), mapError$1((cause) => new WorkError({
|
|
4883
|
-
reason: "unavailable",
|
|
4884
|
-
message: "Bud could not watch the comments ref.",
|
|
4885
|
-
cause
|
|
4886
|
-
})));
|
|
4887
|
-
})));
|
|
4888
|
-
}
|
|
4889
|
-
return {
|
|
4890
|
-
watch,
|
|
4891
|
-
list: (project, value) => root(project, gen(function* () {
|
|
4892
|
-
let filter = yield* attempt(() => parseThreadFilter(value));
|
|
4893
|
-
return yield* store.list.pipe(flatMap((result) => attempt(() => snapshot(result.threads, filter), "storage")), catchTag("CommentsError", (cause) => cause.reason === "repository" ? succeed({
|
|
4894
|
-
connection: {
|
|
4895
|
-
state: "not-initialized",
|
|
4896
|
-
adapter: null,
|
|
4897
|
-
message: cause.message
|
|
4898
|
-
},
|
|
4899
|
-
capabilities: disabled,
|
|
4900
|
-
threads: [],
|
|
4901
|
-
truncated: false
|
|
4902
|
-
}) : fail(fromComments(cause))));
|
|
4903
|
-
})),
|
|
4904
|
-
get: (project, id) => root(project, gen(function* () {
|
|
4905
|
-
let stored = yield* record(yield* current(), id);
|
|
4906
|
-
return yield* attempt(() => projectThread(stored), "storage");
|
|
4907
|
-
})),
|
|
4908
|
-
create: (project, value, author) => root(project, gen(function* () {
|
|
4909
|
-
let input = yield* attempt(() => parseThreadInput(value));
|
|
4910
|
-
let trusted = yield* attempt(() => parseMessageAuthor(author));
|
|
4911
|
-
let anchor = input.anchor;
|
|
4912
|
-
if (anchor && !("type" in anchor) && !options.routes.some((route) => route.id === anchor.pageId && route.path === anchor.pagePath)) return yield* new WorkError({
|
|
4913
|
-
reason: "input",
|
|
4914
|
-
message: "The discussion must belong to a configured route."
|
|
4915
|
-
});
|
|
4916
|
-
return yield* fresh(gen(function* () {
|
|
4917
|
-
let state = yield* current();
|
|
4918
|
-
let source = yield* store.source(state.repo).pipe(mapError(fromComments));
|
|
4919
|
-
let now = new Date(yield* currentTimeMillis).toISOString();
|
|
4920
|
-
return yield* save(state, {
|
|
4921
|
-
version: 3,
|
|
4922
|
-
id: randomUUID(),
|
|
4923
|
-
project: state.projectId,
|
|
4924
|
-
...source,
|
|
4925
|
-
anchor: input.anchor,
|
|
4926
|
-
itemIds: input.itemIds,
|
|
4927
|
-
status: "open",
|
|
4928
|
-
createdAt: now,
|
|
4929
|
-
updatedAt: now,
|
|
4930
|
-
comments: [{
|
|
4931
|
-
id: randomUUID(),
|
|
4932
|
-
body: input.body,
|
|
4933
|
-
author: trusted,
|
|
4934
|
-
parentId: null,
|
|
4935
|
-
createdAt: now
|
|
4936
|
-
}]
|
|
4937
|
-
}, "bud: start discussion");
|
|
4938
|
-
}));
|
|
4939
|
-
})),
|
|
4940
|
-
reply: (project, id, value, author) => root(project, gen(function* () {
|
|
4941
|
-
let input = yield* attempt(() => parseThreadReplyInput(value));
|
|
4942
|
-
let trusted = yield* attempt(() => parseMessageAuthor(author));
|
|
4943
|
-
return yield* change(id, input.expectedRevision, (previous, at) => {
|
|
4944
|
-
if (input.parentId && !previous.comments.some((message) => message.id === input.parentId)) throw new Error("Reply to a message in this discussion.");
|
|
4945
|
-
return {
|
|
4946
|
-
...promoteThread(previous),
|
|
4947
|
-
updatedAt: at,
|
|
4948
|
-
comments: [...previous.comments, {
|
|
4949
|
-
id: randomUUID(),
|
|
4950
|
-
body: input.body,
|
|
4951
|
-
author: trusted,
|
|
4952
|
-
parentId: input.parentId,
|
|
4953
|
-
createdAt: at
|
|
4954
|
-
}]
|
|
4955
|
-
};
|
|
4956
|
-
});
|
|
4957
|
-
})),
|
|
4958
|
-
setStatus: (project, id, status, revision) => root(project, gen(function* () {
|
|
4959
|
-
let input = yield* attempt(() => parseThreadStatusInput({
|
|
4960
|
-
status,
|
|
4961
|
-
expectedRevision: revision
|
|
4962
|
-
}));
|
|
4963
|
-
return yield* change(id, input.expectedRevision, (previous, at) => ({
|
|
4964
|
-
...promoteThread(previous),
|
|
4965
|
-
status: input.status,
|
|
4966
|
-
updatedAt: at
|
|
4967
|
-
}));
|
|
4968
|
-
})),
|
|
4969
|
-
link: (project, id, ids, revision) => link(project, id, ids, revision, false),
|
|
4970
|
-
unlink: (project, id, ids, revision) => link(project, id, ids, revision, true),
|
|
4971
|
-
sync: (project, direction = "both") => root(project, gen(function* () {
|
|
4972
|
-
let input = yield* attempt(() => parseWorkSync({ direction }));
|
|
4973
|
-
let result = yield* store.sync(input.direction).pipe(mapError(fromComments));
|
|
4974
|
-
return yield* attempt(() => snapshot(result.threads, {}), "storage");
|
|
4975
|
-
})),
|
|
4976
|
-
import: (project, value) => root(project, gen(function* () {
|
|
4977
|
-
let input = yield* attempt(() => parseThreadImport(value));
|
|
4978
|
-
return yield* fresh(gen(function* () {
|
|
4979
|
-
let state = yield* current();
|
|
4980
|
-
let existing = [...state.records.threads.values()].filter((record) => record.project === state.projectId && record.version === 3 && metadataSource(record.metadata, input.source, input.sourceId));
|
|
4981
|
-
if (existing.length > 1) return yield* new WorkError({
|
|
4982
|
-
reason: "conflict",
|
|
4983
|
-
message: "This source discussion has duplicate imports. Repair the records before migrating."
|
|
4984
|
-
});
|
|
4985
|
-
if (existing.length) return {
|
|
4986
|
-
outcome: "existing",
|
|
4987
|
-
thread: projectThread(existing[0])
|
|
4988
|
-
};
|
|
4989
|
-
let source = yield* store.source(state.repo).pipe(mapError(fromComments));
|
|
4990
|
-
let thread = input.thread;
|
|
4991
|
-
return {
|
|
4992
|
-
outcome: "created",
|
|
4993
|
-
thread: yield* save(state, {
|
|
4994
|
-
version: 3,
|
|
4995
|
-
id: randomUUID(),
|
|
4996
|
-
project: state.projectId,
|
|
4997
|
-
...source,
|
|
4998
|
-
anchor: thread.anchor,
|
|
4999
|
-
itemIds: thread.itemIds,
|
|
5000
|
-
comments: thread.messages,
|
|
5001
|
-
status: thread.status,
|
|
5002
|
-
createdAt: thread.createdAt,
|
|
5003
|
-
updatedAt: thread.updatedAt,
|
|
5004
|
-
metadata: {
|
|
5005
|
-
source: input.source,
|
|
5006
|
-
sourceId: input.sourceId,
|
|
5007
|
-
createdAt: thread.createdAt,
|
|
5008
|
-
original: thread.metadata ?? null
|
|
5009
|
-
}
|
|
5010
|
-
}, "bud: import discussion")
|
|
5011
|
-
};
|
|
5012
|
-
}));
|
|
5013
|
-
}))
|
|
5014
|
-
};
|
|
5015
|
-
}));
|
|
5016
|
-
}
|
|
5017
|
-
//#endregion
|
|
5018
|
-
//#region ../comments/src/server/migration.ts
|
|
5019
|
-
/**
|
|
5020
|
-
* Reads complete migration history under the Git runtime's existing tree and blob byte limits.
|
|
5021
|
-
* @param options - The trusted source project, remote and configured canvas routes.
|
|
5022
|
-
* @returns Complete source records and their immutable ref head, without writing any Git object or
|
|
5023
|
-
* ref.
|
|
5024
|
-
*/
|
|
5025
|
-
function readGitThreadImports(options) {
|
|
5026
|
-
return gen(function* () {
|
|
5027
|
-
let snapshot = yield* (yield* createCommentStorage(options)).list;
|
|
5028
|
-
let threads = yield* try_({
|
|
5029
|
-
try: () => snapshot.threads.map((record) => parseThreadImport({
|
|
5030
|
-
source: "bud-git",
|
|
5031
|
-
sourceId: record.id,
|
|
5032
|
-
thread: {
|
|
5033
|
-
...projectThread(record),
|
|
5034
|
-
messages: record.comments,
|
|
5035
|
-
messagesTruncated: false
|
|
5036
|
-
}
|
|
5037
|
-
})),
|
|
5038
|
-
catch: (cause) => new CommentsError({
|
|
5039
|
-
reason: "storage",
|
|
5040
|
-
message: "The Git discussion history could not be validated for migration.",
|
|
5041
|
-
cause
|
|
5042
|
-
})
|
|
5043
|
-
});
|
|
5044
|
-
return {
|
|
5045
|
-
head: snapshot.head,
|
|
5046
|
-
threads
|
|
5047
|
-
};
|
|
5048
|
-
});
|
|
5049
|
-
}
|
|
5050
|
-
//#endregion
|
|
5051
4374
|
//#region app/server/built-in-adapters.ts
|
|
5052
|
-
var memorySchema = {
|
|
5053
|
-
adapter: {
|
|
5054
|
-
id: "memory",
|
|
5055
|
-
label: "Memory",
|
|
5056
|
-
version: "1"
|
|
5057
|
-
},
|
|
5058
|
-
fields: [
|
|
5059
|
-
{
|
|
5060
|
-
id: "id",
|
|
5061
|
-
label: "Identifier",
|
|
5062
|
-
kind: "text",
|
|
5063
|
-
role: "identifier",
|
|
5064
|
-
editable: false,
|
|
5065
|
-
required: true
|
|
5066
|
-
},
|
|
5067
|
-
{
|
|
5068
|
-
id: "title",
|
|
5069
|
-
label: "Title",
|
|
5070
|
-
kind: "text",
|
|
5071
|
-
role: "title",
|
|
5072
|
-
editable: true,
|
|
5073
|
-
required: true
|
|
5074
|
-
},
|
|
5075
|
-
{
|
|
5076
|
-
id: "status",
|
|
5077
|
-
label: "Status",
|
|
5078
|
-
kind: "status",
|
|
5079
|
-
role: "status",
|
|
5080
|
-
editable: true
|
|
5081
|
-
},
|
|
5082
|
-
{
|
|
5083
|
-
id: "description",
|
|
5084
|
-
label: "Description",
|
|
5085
|
-
kind: "markdown",
|
|
5086
|
-
role: "description",
|
|
5087
|
-
editable: true
|
|
5088
|
-
},
|
|
5089
|
-
{
|
|
5090
|
-
id: "createdAt",
|
|
5091
|
-
label: "Created",
|
|
5092
|
-
kind: "datetime",
|
|
5093
|
-
role: "created",
|
|
5094
|
-
editable: false,
|
|
5095
|
-
required: true
|
|
5096
|
-
},
|
|
5097
|
-
{
|
|
5098
|
-
id: "updatedAt",
|
|
5099
|
-
label: "Updated",
|
|
5100
|
-
kind: "datetime",
|
|
5101
|
-
role: "updated",
|
|
5102
|
-
editable: false,
|
|
5103
|
-
required: true
|
|
5104
|
-
},
|
|
5105
|
-
{
|
|
5106
|
-
id: "revision",
|
|
5107
|
-
label: "Revision",
|
|
5108
|
-
kind: "text",
|
|
5109
|
-
role: "revision",
|
|
5110
|
-
editable: false,
|
|
5111
|
-
required: true
|
|
5112
|
-
}
|
|
5113
|
-
],
|
|
5114
|
-
statuses: [
|
|
5115
|
-
{
|
|
5116
|
-
value: "open",
|
|
5117
|
-
label: "Open",
|
|
5118
|
-
category: "unstarted"
|
|
5119
|
-
},
|
|
5120
|
-
{
|
|
5121
|
-
value: "in_progress",
|
|
5122
|
-
label: "In progress",
|
|
5123
|
-
category: "started"
|
|
5124
|
-
},
|
|
5125
|
-
{
|
|
5126
|
-
value: "closed",
|
|
5127
|
-
label: "Closed",
|
|
5128
|
-
category: "done"
|
|
5129
|
-
}
|
|
5130
|
-
],
|
|
5131
|
-
relations: [],
|
|
5132
|
-
actions: [],
|
|
5133
|
-
sections: [],
|
|
5134
|
-
capabilities: {
|
|
5135
|
-
create: true,
|
|
5136
|
-
update: true,
|
|
5137
|
-
sync: false,
|
|
5138
|
-
discussion: true,
|
|
5139
|
-
threads: true,
|
|
5140
|
-
history: false,
|
|
5141
|
-
activity: false,
|
|
5142
|
-
ready: false
|
|
5143
|
-
}
|
|
5144
|
-
};
|
|
5145
4375
|
/**
|
|
5146
|
-
* Supplies built-ins with the companion's exact native services,
|
|
4376
|
+
* Supplies built-ins and the project's plugin stores with the companion's exact native services,
|
|
4377
|
+
* including its shared Beads queue.
|
|
5147
4378
|
* @param project - The trusted consuming project.
|
|
5148
|
-
* @returns Fully provided definitions; only selected
|
|
4379
|
+
* @returns Fully provided definitions by plugin name; only selected ones are acquired by the
|
|
4380
|
+
* registry.
|
|
5149
4381
|
*/
|
|
5150
4382
|
function builtInAdapters(project) {
|
|
5151
4383
|
return gen(function* () {
|
|
5152
4384
|
let native = succeedContext(yield* context());
|
|
5153
|
-
let
|
|
4385
|
+
let store = (plugin, value) => try_({
|
|
4386
|
+
try: () => {
|
|
4387
|
+
if (value === void 0) return void 0;
|
|
4388
|
+
let layer = typeof value === "function" ? value({ project }) : value;
|
|
4389
|
+
if (!isLayer(layer)) throw new Error("Not an Effect layer.");
|
|
4390
|
+
return provide(layer, native);
|
|
4391
|
+
},
|
|
4392
|
+
catch: (cause) => new AdapterStartupError({
|
|
4393
|
+
message: `Plugin ${plugin.name} must give its stores as Effect layers, or functions making them.`,
|
|
4394
|
+
cause
|
|
4395
|
+
})
|
|
4396
|
+
});
|
|
4397
|
+
let plugins = {};
|
|
4398
|
+
for (let plugin of configPlugins(project.config)) {
|
|
4399
|
+
if (plugin.work === void 0 && plugin.threads === void 0) continue;
|
|
4400
|
+
plugins[plugin.name] = defineAdapter({
|
|
4401
|
+
id: plugin.name.replace(/^bud:/, ""),
|
|
4402
|
+
label: plugin.label ?? plugin.name,
|
|
4403
|
+
work: yield* store(plugin, plugin.work),
|
|
4404
|
+
threads: yield* store(plugin, plugin.threads)
|
|
4405
|
+
});
|
|
4406
|
+
}
|
|
5154
4407
|
return {
|
|
5155
4408
|
"bud:beads": defineAdapter({
|
|
5156
4409
|
id: "beads",
|
|
@@ -5158,24 +4411,7 @@ function builtInAdapters(project) {
|
|
|
5158
4411
|
work: provide(beadsStoreWorkAdapterLayer, native),
|
|
5159
4412
|
threads: provide(beadsStoreThreadAdapterLayer, native)
|
|
5160
4413
|
}),
|
|
5161
|
-
|
|
5162
|
-
id: "git-comments",
|
|
5163
|
-
label: "Git comments",
|
|
5164
|
-
threads: provide(gitThreadAdapterLayer({
|
|
5165
|
-
root: project.root,
|
|
5166
|
-
remote: project.config.remote,
|
|
5167
|
-
routes: project.info.routes.map(({ id, path }) => ({
|
|
5168
|
-
id,
|
|
5169
|
-
path
|
|
5170
|
-
}))
|
|
5171
|
-
}), native)
|
|
5172
|
-
}),
|
|
5173
|
-
"bud:memory": defineAdapter({
|
|
5174
|
-
id: "memory",
|
|
5175
|
-
label: "Memory",
|
|
5176
|
-
work: memory,
|
|
5177
|
-
threads: memory
|
|
5178
|
-
})
|
|
4414
|
+
...plugins
|
|
5179
4415
|
};
|
|
5180
4416
|
});
|
|
5181
4417
|
}
|
|
@@ -5332,6 +4568,7 @@ function domainLayer(project, options) {
|
|
|
5332
4568
|
flows: projectFlows(project.root, project.config.flows),
|
|
5333
4569
|
warm: options.warmPreviews
|
|
5334
4570
|
});
|
|
4571
|
+
let attachments = attachmentsLayer(directoryAttachmentStore(join(project.root, "node_modules/.cache/bud")));
|
|
5335
4572
|
let files = mergeAll(filesLayer({ root: project.root }), fileWriteLockLayer);
|
|
5336
4573
|
let review = fileReviewLayer({ root: project.root });
|
|
5337
4574
|
let channel = liveChannelLayer();
|
|
@@ -5342,7 +4579,7 @@ function domainLayer(project, options) {
|
|
|
5342
4579
|
pollMs: Number(process.env.BUD_BRANCH_POLL_MS) || void 0
|
|
5343
4580
|
});
|
|
5344
4581
|
let live = mergeAll(provideMerge(publishLiveChanges, mergeAll(previews, branches, files, channel)), provide(fileLiveLayer, mergeAll(provideMerge(review, files), channel)), provideMerge(storeLiveLayer(project.root), merge(storeActivityLayer, channel)), provide(relocatePins(anchors), mergeAll(previews, branches, storeActivityLayer)));
|
|
5345
|
-
return mergeAll(options.reloadPreviews ? provideMerge(reloadPreviewsOnChange, live) : live, provideMerge(fileReviewActionsLayer({ root: project.root }), provideMerge(mergeAll(review, fileMutationsLayer({ root: project.root }), fileSavesLayer({ root: project.root }), fileSearchLayer()), files)), options.development ? sourceScreensLayer(project) : bundledScreensLayer(project));
|
|
4582
|
+
return mergeAll(options.reloadPreviews ? provideMerge(reloadPreviewsOnChange, live) : live, provideMerge(fileReviewActionsLayer({ root: project.root }), provideMerge(mergeAll(review, fileMutationsLayer({ root: project.root }), fileSavesLayer({ root: project.root }), fileSearchLayer()), files)), options.development ? sourceScreensLayer(project) : bundledScreensLayer(project), attachments, provide(factoryRunsLayer(project), mergeAll(storeActivityLayer, runExecutorLayer(project), branches, attachments)));
|
|
5346
4583
|
}
|
|
5347
4584
|
/**
|
|
5348
4585
|
* Adds explicitly selected storage adapters to the companion's shared service graph.
|
|
@@ -5361,7 +4598,7 @@ function configuredApplicationLayer(project, definitions, options = {}) {
|
|
|
5361
4598
|
* @returns The registry and shared native services used by the application and migration CLI.
|
|
5362
4599
|
*/
|
|
5363
4600
|
function storageLayer(project, definitions) {
|
|
5364
|
-
return provideMerge(definitions ? adapterRegistryLayer(project, definitions) : unwrap$1(map(builtInAdapters(project), (definitions) => adapterRegistryLayer(project, definitions))),
|
|
4601
|
+
return provideMerge(definitions ? adapterRegistryLayer(project, definitions) : unwrap$1(map(builtInAdapters(project), (definitions) => adapterRegistryLayer(project, definitions))), provideMerge(beadsLayer(), mergeAll(succeed$1(Project, project), provideMerge(gitLayer(), nodeLayer()))));
|
|
5365
4602
|
}
|
|
5366
4603
|
//#endregion
|
|
5367
|
-
export {
|
|
4604
|
+
export { storageLayer as n, applicationLayer as t };
|