nolo-cli 0.8.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_executeModel-UD44KGZ4.js +203 -0
- package/action-ZJWE3Y5S.js +28 -0
- package/addContentAction-TVDBNJM5.js +26 -0
- package/addDialogAgentAction-EKH774VT.js +68 -0
- package/agentDeleteCommand-TCPEFJTF.js +327 -0
- package/agentEmailCommands-CZ3UILZ4.js +626 -0
- package/agentListCommands-SJKO2FFB.js +223 -0
- package/agentMachineCommands-PSNABISZ.js +425 -0
- package/agentPullCommand-O2MTUAFR.js +122 -0
- package/agentRecordCommands-MH2JKFHS.js +94 -0
- package/agentRunCommand-4KH6MIZR.js +110 -0
- package/agentRunControl-K4VKNEHF.js +57 -0
- package/agentRuntimeLocal-SKHJUWSS.js +104 -0
- package/agentSlice-3WXXN4VK.js +42 -0
- package/authCommand-PXFX6JDT.js +581 -0
- package/authCommands-356AYWEK.js +35 -0
- package/chunk-2EISRLRC.js +238 -0
- package/chunk-2FTTDCD4.js +675 -0
- package/chunk-2K2ECKSG.js +222 -0
- package/chunk-2PUGVR6E.js +19 -0
- package/chunk-3ASDTTEI.js +1774 -0
- package/chunk-3EUW3WEO.js +8 -0
- package/chunk-3LY4F7SV.js +9293 -0
- package/chunk-3TZDXYSF.js +71 -0
- package/chunk-3WYQEUBI.js +34 -0
- package/chunk-4CQLVGRV.js +50 -0
- package/chunk-4IJYA27C.js +342 -0
- package/chunk-4K2KKCSD.js +577 -0
- package/chunk-4SN6J65J.js +38 -0
- package/chunk-4VNS5WPM.js +42 -0
- package/chunk-4ZDM73SD.js +34 -0
- package/chunk-5CS33IBL.js +301 -0
- package/chunk-5KLGBAAR.js +386 -0
- package/chunk-5MIDPSSK.js +206 -0
- package/chunk-5MMAK75Q.js +15 -0
- package/chunk-5PJ3VWCS.js +360 -0
- package/chunk-63SLEOLK.js +175 -0
- package/chunk-63W5N6P3.js +150 -0
- package/chunk-6GANEXVO.js +231 -0
- package/chunk-6HYVJV7B.js +102 -0
- package/chunk-6WD3OOJS.js +20 -0
- package/chunk-7OOXPE3N.js +628 -0
- package/chunk-7QRKCBOP.js +1078 -0
- package/chunk-ATNTHIZI.js +13 -0
- package/chunk-AUQ747NZ.js +902 -0
- package/chunk-AVMKZHBC.js +85 -0
- package/chunk-AZPCASRE.js +36 -0
- package/chunk-BBYVOSU2.js +792 -0
- package/chunk-C3RUNRHT.js +51 -0
- package/chunk-CF2WZF4N.js +134 -0
- package/chunk-CF7PE763.js +10 -0
- package/chunk-CKOJQDKR.js +198 -0
- package/chunk-COMXDWNM.js +807 -0
- package/chunk-CPB2YM4D.js +171 -0
- package/chunk-CWMSB67P.js +34 -0
- package/chunk-DCOA4QSN.js +105 -0
- package/chunk-DFFIZ25G.js +30 -0
- package/chunk-DWLEOOGR.js +8 -0
- package/chunk-DZ3XJVET.js +231 -0
- package/chunk-EHLHSX7R.js +1433 -0
- package/chunk-ELRRLDWI.js +478 -0
- package/chunk-EXTIQUCI.js +482 -0
- package/chunk-EYEEHCU4.js +3066 -0
- package/chunk-G3SUCT4W.js +8 -0
- package/chunk-G43KEVSC.js +1447 -0
- package/chunk-GAKEBQD6.js +30 -0
- package/chunk-GBRG75BQ.js +86 -0
- package/chunk-GG4AJPSR.js +191 -0
- package/chunk-H33743XH.js +25 -0
- package/chunk-HNMXYYJR.js +12 -0
- package/chunk-I42VC5KR.js +15 -0
- package/chunk-IDZTNRP2.js +85 -0
- package/chunk-ISNCLGUZ.js +146 -0
- package/chunk-IWX4UW2E.js +548 -0
- package/chunk-JBTJAPUF.js +35 -0
- package/chunk-JEXVIMYZ.js +55 -0
- package/chunk-JRKKFRAV.js +537 -0
- package/chunk-JZPBVBIY.js +94 -0
- package/chunk-KKY4B2Q2.js +21 -0
- package/chunk-KOCA5FCS.js +4750 -0
- package/chunk-KQHGZWSV.js +63 -0
- package/chunk-KTVVPRJS.js +138 -0
- package/chunk-KUG52IAP.js +8 -0
- package/chunk-KX6YBIHG.js +8 -0
- package/chunk-LARL2V6G.js +9017 -0
- package/chunk-LCBAMY2M.js +60 -0
- package/chunk-LM6IYQ3F.js +16 -0
- package/chunk-M76ECQRD.js +13658 -0
- package/chunk-MGOJ5R6J.js +113 -0
- package/chunk-MKMWT7SA.js +27196 -0
- package/chunk-MRC4Q34M.js +188 -0
- package/chunk-OLZBQWZU.js +6 -0
- package/chunk-OPZYGHUC.js +89 -0
- package/chunk-OWOHNGCN.js +50 -0
- package/chunk-PAKN26FP.js +239 -0
- package/chunk-PCWC3JTN.js +91 -0
- package/chunk-PG5PNA7Y.js +939 -0
- package/chunk-Q2YZULBH.js +104 -0
- package/chunk-QSJ3HFEF.js +8 -0
- package/chunk-QYXLLLGL.js +4519 -0
- package/chunk-ROWQEJRP.js +139 -0
- package/chunk-S7Z2HMCN.js +25 -0
- package/chunk-SBR32ZZS.js +1901 -0
- package/chunk-SH2CFQOH.js +3391 -0
- package/chunk-SL35K7AH.js +226 -0
- package/chunk-TILFE6LV.js +381 -0
- package/chunk-TSS3XJY4.js +2052 -0
- package/chunk-U4TRT5V4.js +246 -0
- package/chunk-U6VC4MU4.js +8 -0
- package/chunk-UAD2WT54.js +64 -0
- package/chunk-UUJDGGAK.js +421 -0
- package/chunk-VF4K2QE6.js +21 -0
- package/chunk-VHBWHNVT.js +59 -0
- package/chunk-VIDPVFP3.js +20 -0
- package/chunk-VP4WJWKW.js +8882 -0
- package/chunk-W4NVOSFF.js +37 -0
- package/chunk-WKM6FH35.js +190 -0
- package/chunk-WWC7I6TA.js +8 -0
- package/chunk-X7W4TNB4.js +1550 -0
- package/chunk-XE635NI4.js +1948 -0
- package/chunk-XKIMRZO2.js +221 -0
- package/chunk-XS466O7S.js +933 -0
- package/chunk-Y5GCAMGP.js +9 -0
- package/chunk-YBWOP46I.js +3272 -0
- package/chunk-YPRZSPF3.js +148 -0
- package/chunk-YYUCRDOK.js +69 -0
- package/chunk-Z4NR32XR.js +50 -0
- package/chunk-ZIG5LJ7X.js +24765 -0
- package/chunk-ZNFJWEN4.js +56 -0
- package/chunk-ZQ2UM4KG.js +2878 -0
- package/cliExecutor-DNE7TCC4.js +36 -0
- package/clipboardy-BVE3LVEH.js +12001 -0
- package/createAgentAutomationAction-63OIP6X5.js +866 -0
- package/createDialogAction-ZLYZDYBB.js +54 -0
- package/createPageAction-AL5FSDCK.js +171 -0
- package/db-HCJ5PRB5.js +271 -0
- package/dbSlice-PR44QPXY.js +60 -0
- package/deleteFile-4SKGPHOZ.js +41 -0
- package/dialogCommands-F6YI5742.js +63 -0
- package/dialogSlice-JSBNZRLR.js +148 -0
- package/docCreateCommands-4CTO7ERT.js +219 -0
- package/docDeleteCommands-VBWERDRG.js +180 -0
- package/docReadCommands-KNP4MFYB.js +133 -0
- package/docStore-ADP5VYY4.js +98 -0
- package/docUpdateCommands-4BCBJQHA.js +317 -0
- package/fetchMessages-U6ZHN56A.js +17 -0
- package/getPricing-3E6UNPKL.js +30 -0
- package/handleSendMessageAction-IJDQ6ARA.js +185 -0
- package/index.js +188 -112943
- package/lib-FYYBQIXS.js +19839 -0
- package/localDialogRead-Z5UTAJPU.js +26 -0
- package/localFirstLog-DHNOY2NA.js +19 -0
- package/localRuntimeAuthority-TEGCOWX4.js +471 -0
- package/localRuntimeDb-LGDF6FQP.js +12 -0
- package/machineCommands-EA3M4XTN.js +779 -0
- package/machineInfo-M6KB6HZN.js +10 -0
- package/machineWsRunDispatch-NAPKE5ZA.js +110 -0
- package/memoryCommands-O4MIPIWQ.js +476 -0
- package/models-4OPOOLFS.js +9 -0
- package/offlineMarxistsAgentCommand-BCPJITJL.js +242 -0
- package/package.json +3 -25
- package/read-RIJCIVKX.js +17 -0
- package/readDocTool-GZD2N3LO.js +16 -0
- package/readlineWorkspace-KMWT4T4S.js +5509 -0
- package/removeDialogAgentAction-GJQJNDW6.js +73 -0
- package/runAgentClientLoop-U5Q6NDTJ.js +291 -0
- package/runtimeDoctorCommands-LRHLPDRC.js +289 -0
- package/setDialogExtraReferencesAction-M7OQG54N.js +60 -0
- package/setPrimaryDialogAgentAction-46UNFJDL.js +80 -0
- package/settingSlice-FVUUH4JA.js +199 -0
- package/skillSummaryMarker-42HKSXYY.js +9 -0
- package/slateToRenderMarkdown-Y5IYN73U.js +7 -0
- package/slateUtils-2M7MUTWX.js +19 -0
- package/spaceCommands-E2CFRPZJ.js +454 -0
- package/spaceSlice-L3R6IMXE.js +147 -0
- package/streamAgentChatTurn-UBDAOQWE.js +4528 -0
- package/tableCommands-GOFP7QKA.js +1286 -0
- package/toolApiClient-QI3YBNWB.js +26 -0
- package/tools-TMJXZTCA.js +85 -0
- package/tuiSummaryLlmCaller-RV3THLZF.js +78 -0
- package/understanding-GQKNDDWM.js +271 -0
- package/updateCommands-VOSPKXFF.js +31 -0
- package/updateDialogTitleAction-ZTWRDXI2.js +188 -0
|
@@ -0,0 +1,807 @@
|
|
|
1
|
+
import {
|
|
2
|
+
resolveFileCategory
|
|
3
|
+
} from "./chunk-AZPCASRE.js";
|
|
4
|
+
import {
|
|
5
|
+
loadFileFromIndexedDb,
|
|
6
|
+
removeAction,
|
|
7
|
+
saveFileToIndexedDb
|
|
8
|
+
} from "./chunk-2EISRLRC.js";
|
|
9
|
+
import {
|
|
10
|
+
fileKey,
|
|
11
|
+
getFileIdFromKey,
|
|
12
|
+
ulid
|
|
13
|
+
} from "./chunk-SH2CFQOH.js";
|
|
14
|
+
import {
|
|
15
|
+
readAction,
|
|
16
|
+
readRequestManager,
|
|
17
|
+
resolveAgentReadServers
|
|
18
|
+
} from "./chunk-ELRRLDWI.js";
|
|
19
|
+
import {
|
|
20
|
+
asyncThunkCreator,
|
|
21
|
+
buildCreateSlice,
|
|
22
|
+
compareRemoteRecordsByComparableTime,
|
|
23
|
+
createEntityAdapter,
|
|
24
|
+
getRuntimeServerContext,
|
|
25
|
+
noloDeleteRequest,
|
|
26
|
+
pickBestSettledRemoteRecord,
|
|
27
|
+
planAuthorityReadServers,
|
|
28
|
+
resolveAuthorityReplicationServers,
|
|
29
|
+
resolveRecordAuthority,
|
|
30
|
+
resolveUploadReplicationServers,
|
|
31
|
+
scheduleConfiguredPatchReplication,
|
|
32
|
+
scheduleExistingRecordReplication,
|
|
33
|
+
scheduleUploadReplication,
|
|
34
|
+
scheduleWriteReplication,
|
|
35
|
+
shouldReplaceLocalWithRemoteRecord,
|
|
36
|
+
shouldReplicateLocalRecord,
|
|
37
|
+
uploadToCurrentServer
|
|
38
|
+
} from "./chunk-EYEEHCU4.js";
|
|
39
|
+
import {
|
|
40
|
+
fetchFromClientDb,
|
|
41
|
+
fetchFromServer,
|
|
42
|
+
logger,
|
|
43
|
+
normalizeTimeFields
|
|
44
|
+
} from "./chunk-QYXLLLGL.js";
|
|
45
|
+
import {
|
|
46
|
+
isTombstoneRecord
|
|
47
|
+
} from "./chunk-GBRG75BQ.js";
|
|
48
|
+
import {
|
|
49
|
+
API_ENDPOINTS
|
|
50
|
+
} from "./chunk-LCBAMY2M.js";
|
|
51
|
+
import {
|
|
52
|
+
isRecord
|
|
53
|
+
} from "./chunk-KUG52IAP.js";
|
|
54
|
+
|
|
55
|
+
// packages/database/actions/readAndWait.ts
|
|
56
|
+
var hashTokenScope = (currentToken) => {
|
|
57
|
+
const token = currentToken || "";
|
|
58
|
+
if (!token) return "anonymous";
|
|
59
|
+
let hash = 0;
|
|
60
|
+
for (let index = 0; index < token.length; index += 1) {
|
|
61
|
+
hash = (hash << 5) - hash + token.charCodeAt(index) | 0;
|
|
62
|
+
}
|
|
63
|
+
return `token:${Math.abs(hash).toString(36)}`;
|
|
64
|
+
};
|
|
65
|
+
var buildReadAndWaitRequestKey = (dbKey, currentToken) => `${dbKey}\0auth:${hashTokenScope(currentToken)}`;
|
|
66
|
+
var isRemoteDataNewer = (remoteData, localData) => {
|
|
67
|
+
return compareRemoteRecordsByComparableTime(remoteData, localData) > 0;
|
|
68
|
+
};
|
|
69
|
+
var syncLocalDataToServer = async (replicationContext, dbKey, localData) => {
|
|
70
|
+
try {
|
|
71
|
+
scheduleExistingRecordReplication({
|
|
72
|
+
currentServer: replicationContext.currentServer,
|
|
73
|
+
syncServers: replicationContext.syncServers,
|
|
74
|
+
dbKey,
|
|
75
|
+
localData,
|
|
76
|
+
state: replicationContext.state
|
|
77
|
+
});
|
|
78
|
+
} catch {
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
var processRemoteData = async (db, dbKey, remotePromises, localData, replicationContext) => {
|
|
82
|
+
try {
|
|
83
|
+
const settledResults = await Promise.allSettled(remotePromises);
|
|
84
|
+
const remoteResult = pickBestSettledRemoteRecord({
|
|
85
|
+
settledResults,
|
|
86
|
+
isBetterCandidate: (current, latest) => compareRemoteRecordsByComparableTime(current, latest) > 0
|
|
87
|
+
});
|
|
88
|
+
const validRemoteData = remoteResult ? remoteResult.data : null;
|
|
89
|
+
if (shouldReplaceLocalWithRemoteRecord({
|
|
90
|
+
localData,
|
|
91
|
+
remoteData: validRemoteData,
|
|
92
|
+
isRemoteNewer: isRemoteDataNewer
|
|
93
|
+
})) {
|
|
94
|
+
await db.put(dbKey, validRemoteData);
|
|
95
|
+
return validRemoteData;
|
|
96
|
+
}
|
|
97
|
+
if (localData) {
|
|
98
|
+
if (shouldReplicateLocalRecord({
|
|
99
|
+
localData,
|
|
100
|
+
remoteData: validRemoteData,
|
|
101
|
+
remoteTargetCount: remotePromises.length
|
|
102
|
+
})) {
|
|
103
|
+
void syncLocalDataToServer(replicationContext, dbKey, localData);
|
|
104
|
+
}
|
|
105
|
+
return localData;
|
|
106
|
+
}
|
|
107
|
+
throw new Error("Failed to fetch data from all sources");
|
|
108
|
+
} catch (err) {
|
|
109
|
+
if (localData) {
|
|
110
|
+
return localData;
|
|
111
|
+
}
|
|
112
|
+
throw err;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
var readAndWaitAction = async (payload, thunkApi) => {
|
|
116
|
+
const dbKey = typeof payload === "string" ? payload : payload.dbKey;
|
|
117
|
+
const preferredServerOrigin = typeof payload === "string" ? void 0 : payload.preferredServerOrigin;
|
|
118
|
+
const { db } = thunkApi.extra;
|
|
119
|
+
if (!db) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
"Database instance is not available in thunk extra argument."
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
const state = thunkApi.getState();
|
|
125
|
+
const {
|
|
126
|
+
currentToken,
|
|
127
|
+
remoteServers: configuredServers,
|
|
128
|
+
currentServer,
|
|
129
|
+
currentUserId,
|
|
130
|
+
syncServers,
|
|
131
|
+
userAuthorityRegistry
|
|
132
|
+
} = getRuntimeServerContext(state, preferredServerOrigin);
|
|
133
|
+
const allServers = resolveAgentReadServers({ dbKey, configuredServers });
|
|
134
|
+
const isLoggedIn = !!currentToken;
|
|
135
|
+
const executeReadAndWait = async () => {
|
|
136
|
+
const localData = await fetchFromClientDb(db, dbKey);
|
|
137
|
+
const authority = resolveRecordAuthority({
|
|
138
|
+
dbKey,
|
|
139
|
+
record: localData,
|
|
140
|
+
currentUserId,
|
|
141
|
+
currentServer,
|
|
142
|
+
userAuthorityRegistry
|
|
143
|
+
});
|
|
144
|
+
const readServers = planAuthorityReadServers({
|
|
145
|
+
allServers,
|
|
146
|
+
authorityServer: preferredServerOrigin ?? authority.authorityServer,
|
|
147
|
+
serverOrigin: authority.serverOrigin
|
|
148
|
+
});
|
|
149
|
+
const hasPreferredAuthorityServer = !!preferredServerOrigin || !!authority.authorityServer;
|
|
150
|
+
const preferredAuthorityServer = hasPreferredAuthorityServer ? readServers[0] : null;
|
|
151
|
+
if (readServers.length === 0) {
|
|
152
|
+
if (localData) {
|
|
153
|
+
return { ...localData, dbKey };
|
|
154
|
+
}
|
|
155
|
+
throw new Error(
|
|
156
|
+
`Failed to fetch data for key "${dbKey}" because network is offline and no local data is available.`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
if (preferredAuthorityServer) {
|
|
160
|
+
try {
|
|
161
|
+
const preferredRemoteData = await fetchFromServer(
|
|
162
|
+
preferredAuthorityServer,
|
|
163
|
+
dbKey,
|
|
164
|
+
isLoggedIn ? currentToken : void 0
|
|
165
|
+
);
|
|
166
|
+
if (preferredRemoteData) {
|
|
167
|
+
await db.put(dbKey, preferredRemoteData);
|
|
168
|
+
return { ...preferredRemoteData, dbKey };
|
|
169
|
+
}
|
|
170
|
+
} catch {
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const remainingReadServers = preferredAuthorityServer ? readServers.filter((server) => server !== preferredAuthorityServer) : readServers;
|
|
174
|
+
const remotePromises = remainingReadServers.map(
|
|
175
|
+
(server) => fetchFromServer(server, dbKey, isLoggedIn ? currentToken : void 0)
|
|
176
|
+
);
|
|
177
|
+
const chosenData = await processRemoteData(
|
|
178
|
+
db,
|
|
179
|
+
dbKey,
|
|
180
|
+
remotePromises,
|
|
181
|
+
localData,
|
|
182
|
+
{ currentServer, syncServers, state }
|
|
183
|
+
);
|
|
184
|
+
return { ...chosenData, dbKey };
|
|
185
|
+
};
|
|
186
|
+
const inFlightKey = buildReadAndWaitRequestKey(dbKey, currentToken);
|
|
187
|
+
const existing = readRequestManager.getInFlight(inFlightKey);
|
|
188
|
+
if (existing) return existing;
|
|
189
|
+
let inFlightPromise;
|
|
190
|
+
inFlightPromise = executeReadAndWait().finally(() => {
|
|
191
|
+
readRequestManager.clearInFlight(inFlightKey, inFlightPromise);
|
|
192
|
+
});
|
|
193
|
+
readRequestManager.setInFlight(inFlightKey, inFlightPromise);
|
|
194
|
+
return inFlightPromise;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// packages/database/actions/actionToast.ts
|
|
198
|
+
var registeredToast = null;
|
|
199
|
+
var actionToast = {
|
|
200
|
+
success: (message) => registeredToast?.success(message),
|
|
201
|
+
error: (message) => {
|
|
202
|
+
if (registeredToast) registeredToast.error(message);
|
|
203
|
+
else console.warn(message);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// packages/database/actions/write.ts
|
|
208
|
+
var SPACE_MEMBER_PREFIX = "space-member-";
|
|
209
|
+
var getMemberUserIdFromSpaceMemberKey = (dbKey) => {
|
|
210
|
+
if (!dbKey.startsWith(SPACE_MEMBER_PREFIX)) return null;
|
|
211
|
+
const rest = dbKey.slice(SPACE_MEMBER_PREFIX.length);
|
|
212
|
+
const lastDash = rest.lastIndexOf("-");
|
|
213
|
+
if (lastDash <= 0) return null;
|
|
214
|
+
return rest.slice(0, lastDash);
|
|
215
|
+
};
|
|
216
|
+
var saveToClientDb = async (clientDb, dbKey, data) => {
|
|
217
|
+
if (!clientDb) {
|
|
218
|
+
logger.error({ dbKey }, "Client database is undefined in saveToClientDb");
|
|
219
|
+
throw new Error("Client database instance is required");
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
await clientDb.put(dbKey, data);
|
|
223
|
+
logger.debug({ dbKey }, "Data saved successfully to local database.");
|
|
224
|
+
} catch (err) {
|
|
225
|
+
logger.error({ err, dbKey }, "Failed to save data to local database");
|
|
226
|
+
throw new Error(`Local database put failed for ${dbKey}: ${err.message}`);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
var writeAction = async (writeConfig, thunkApi) => {
|
|
230
|
+
const { db: clientDb } = thunkApi.extra;
|
|
231
|
+
if (!clientDb) {
|
|
232
|
+
throw new Error("Client database instance is required in writeAction");
|
|
233
|
+
}
|
|
234
|
+
const state = thunkApi.getState();
|
|
235
|
+
const { currentServer, syncServers, currentUserId } = getRuntimeServerContext(state);
|
|
236
|
+
const { data, customKey } = writeConfig;
|
|
237
|
+
const userId = writeConfig.userId || currentUserId;
|
|
238
|
+
const isSpaceMemberRecord = customKey.startsWith(SPACE_MEMBER_PREFIX);
|
|
239
|
+
const recordUserId = isSpaceMemberRecord ? data.userId || getMemberUserIdFromSpaceMemberKey(customKey) || userId : userId;
|
|
240
|
+
if (!data || !customKey) {
|
|
241
|
+
const errorMsg = "Invalid arguments for writeAction: data and customKey are required.";
|
|
242
|
+
logger.error({ writeConfig }, errorMsg);
|
|
243
|
+
actionToast.error(errorMsg);
|
|
244
|
+
throw new Error(errorMsg);
|
|
245
|
+
}
|
|
246
|
+
const VALID_TYPES = [
|
|
247
|
+
"msg" /* MSG */,
|
|
248
|
+
"page" /* DOC */,
|
|
249
|
+
"dialog" /* DIALOG */,
|
|
250
|
+
"notification" /* NOTIFICATION */,
|
|
251
|
+
"token" /* TOKEN */,
|
|
252
|
+
"transaction" /* TRANSACTION */,
|
|
253
|
+
"space" /* SPACE */,
|
|
254
|
+
"setting" /* SETTING */,
|
|
255
|
+
"table" /* TABLE */,
|
|
256
|
+
"table_row" /* TABLE_ROW */,
|
|
257
|
+
"email" /* EMAIL */
|
|
258
|
+
];
|
|
259
|
+
if (!data.type || !VALID_TYPES.includes(data.type)) {
|
|
260
|
+
logger.warn(
|
|
261
|
+
`Invalid data type "${data.type}" for writeAction with key ${customKey}. Proceeding anyway.`
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
try {
|
|
265
|
+
const willSaveData = normalizeTimeFields({
|
|
266
|
+
...data,
|
|
267
|
+
dbKey: customKey,
|
|
268
|
+
userId: recordUserId
|
|
269
|
+
});
|
|
270
|
+
await saveToClientDb(clientDb, customKey, willSaveData);
|
|
271
|
+
const servers = resolveAuthorityReplicationServers({
|
|
272
|
+
currentServer,
|
|
273
|
+
syncServers,
|
|
274
|
+
dbKey: customKey,
|
|
275
|
+
record: willSaveData,
|
|
276
|
+
state
|
|
277
|
+
});
|
|
278
|
+
const serverWriteConfig = {
|
|
279
|
+
data: willSaveData,
|
|
280
|
+
customKey,
|
|
281
|
+
userId: recordUserId
|
|
282
|
+
};
|
|
283
|
+
if (servers.length > 0) {
|
|
284
|
+
logger.debug(
|
|
285
|
+
`[writeAction] Initiating background sync for key: ${customKey} to ${servers.length} servers.`
|
|
286
|
+
);
|
|
287
|
+
scheduleWriteReplication(servers, serverWriteConfig, state);
|
|
288
|
+
} else {
|
|
289
|
+
logger.warn(
|
|
290
|
+
{ customKey },
|
|
291
|
+
"[writeAction] No available servers, data only saved locally."
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
return willSaveData;
|
|
295
|
+
} catch (error) {
|
|
296
|
+
const errorMessage = `Write action failed for ${customKey}: ${error?.message || "Unknown error"}`;
|
|
297
|
+
logger.error("[writeAction] Error:", error);
|
|
298
|
+
actionToast.error(`Failed to save data for ${customKey}.`);
|
|
299
|
+
throw new Error(errorMessage);
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// packages/core/timestamp.ts
|
|
304
|
+
function toTimestampMs(value) {
|
|
305
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
306
|
+
if (typeof value === "string") {
|
|
307
|
+
const parsed = Date.parse(value);
|
|
308
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
309
|
+
}
|
|
310
|
+
return 0;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// packages/database/actions/patch.ts
|
|
314
|
+
var deepMerge = (target, source) => {
|
|
315
|
+
const output = { ...target };
|
|
316
|
+
for (const key in source) {
|
|
317
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
318
|
+
if (source[key] === null && key in output) {
|
|
319
|
+
delete output[key];
|
|
320
|
+
} else if (isRecord(source[key])) {
|
|
321
|
+
output[key] = deepMerge(output[key] || {}, source[key]);
|
|
322
|
+
} else {
|
|
323
|
+
output[key] = source[key];
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return output;
|
|
328
|
+
};
|
|
329
|
+
var inferNextUpdatedAt = (currentData) => {
|
|
330
|
+
const previousUpdatedAt = currentData?.updatedAt;
|
|
331
|
+
const previousCreatedAt = currentData?.createdAt;
|
|
332
|
+
const previousMetaCreatedAt = currentData?.meta?.createdAt;
|
|
333
|
+
const previousTimestamp = Math.max(
|
|
334
|
+
toTimestampMs(previousUpdatedAt),
|
|
335
|
+
toTimestampMs(previousCreatedAt),
|
|
336
|
+
toTimestampMs(previousMetaCreatedAt)
|
|
337
|
+
);
|
|
338
|
+
const nextTimestamp = Math.max(Date.now(), previousTimestamp + 1);
|
|
339
|
+
if (typeof previousUpdatedAt === "number" || typeof previousCreatedAt === "number" || typeof previousMetaCreatedAt === "number") {
|
|
340
|
+
return nextTimestamp;
|
|
341
|
+
}
|
|
342
|
+
if (typeof previousUpdatedAt === "string" || typeof previousCreatedAt === "string") {
|
|
343
|
+
return new Date(nextTimestamp).toISOString();
|
|
344
|
+
}
|
|
345
|
+
return void 0;
|
|
346
|
+
};
|
|
347
|
+
var patchAction = async ({
|
|
348
|
+
dbKey,
|
|
349
|
+
changes,
|
|
350
|
+
preferredServerOrigin
|
|
351
|
+
}, thunkApi) => {
|
|
352
|
+
const { db } = thunkApi.extra;
|
|
353
|
+
if (!db) {
|
|
354
|
+
const errorMsg = "Database instance is not available.";
|
|
355
|
+
actionToast.error(errorMsg);
|
|
356
|
+
throw new Error(errorMsg);
|
|
357
|
+
}
|
|
358
|
+
if (!dbKey || !changes || typeof changes !== "object") {
|
|
359
|
+
const errorMsg = "Patch action requires a valid dbKey and changes object.";
|
|
360
|
+
actionToast.error(errorMsg);
|
|
361
|
+
throw new Error(errorMsg);
|
|
362
|
+
}
|
|
363
|
+
const state = thunkApi.getState();
|
|
364
|
+
const { currentServer, syncServers: configuredSyncServers } = getRuntimeServerContext(state);
|
|
365
|
+
try {
|
|
366
|
+
const currentData = await db.get(dbKey);
|
|
367
|
+
if (!currentData) {
|
|
368
|
+
throw new Error(
|
|
369
|
+
`Cannot apply patch: Data not found locally for key: ${dbKey}.`
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
const patchChanges = Object.prototype.hasOwnProperty.call(changes, "updatedAt") ? changes : {
|
|
373
|
+
...changes,
|
|
374
|
+
...inferNextUpdatedAt(currentData) !== void 0 ? { updatedAt: inferNextUpdatedAt(currentData) } : {}
|
|
375
|
+
};
|
|
376
|
+
const newData = deepMerge(currentData, patchChanges);
|
|
377
|
+
const persistedData = newData && typeof newData === "object" ? { ...newData, dbKey } : { dbKey };
|
|
378
|
+
await db.put(dbKey, persistedData);
|
|
379
|
+
scheduleConfiguredPatchReplication({
|
|
380
|
+
currentServer,
|
|
381
|
+
syncServers: configuredSyncServers,
|
|
382
|
+
preferredServerOrigin,
|
|
383
|
+
dbKey,
|
|
384
|
+
changes: patchChanges,
|
|
385
|
+
state
|
|
386
|
+
});
|
|
387
|
+
return persistedData;
|
|
388
|
+
} catch (error) {
|
|
389
|
+
const errorMessage = `Failed to update data for ${dbKey}.`;
|
|
390
|
+
actionToast.error(errorMessage);
|
|
391
|
+
throw new Error(error.message || errorMessage);
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
// packages/database/actions/purge.ts
|
|
396
|
+
var purgeAction = async (payload, thunkApi) => {
|
|
397
|
+
const { db: clientDb } = thunkApi.extra;
|
|
398
|
+
const dbKey = typeof payload === "string" ? payload : payload.dbKey;
|
|
399
|
+
const preferredServerOrigin = typeof payload === "string" ? void 0 : payload.preferredServerOrigin;
|
|
400
|
+
if (!clientDb) {
|
|
401
|
+
throw new Error("Client database is undefined in purgeAction");
|
|
402
|
+
}
|
|
403
|
+
const state = thunkApi.getState();
|
|
404
|
+
const { currentServer, syncServers } = getRuntimeServerContext(state);
|
|
405
|
+
const localData = await fetchFromClientDb(clientDb, dbKey);
|
|
406
|
+
if (localData && !isTombstoneRecord(localData)) {
|
|
407
|
+
throw new Error(
|
|
408
|
+
`purgeAction refused: record ${dbKey} is not tombstoned. Use removeAction instead.`
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
await clientDb.del(dbKey);
|
|
412
|
+
const servers = resolveAuthorityReplicationServers({
|
|
413
|
+
currentServer,
|
|
414
|
+
syncServers,
|
|
415
|
+
preferredServerOrigin,
|
|
416
|
+
dbKey,
|
|
417
|
+
state
|
|
418
|
+
});
|
|
419
|
+
await Promise.all(
|
|
420
|
+
servers.map(async (server) => {
|
|
421
|
+
const ok = await noloDeleteRequest(
|
|
422
|
+
server,
|
|
423
|
+
dbKey,
|
|
424
|
+
{ type: "single", force: true },
|
|
425
|
+
state
|
|
426
|
+
);
|
|
427
|
+
if (!ok) {
|
|
428
|
+
console.warn("[purgeAction] server force-delete failed", { dbKey, server });
|
|
429
|
+
}
|
|
430
|
+
})
|
|
431
|
+
);
|
|
432
|
+
return { dbKey, servers };
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
// packages/database/actions/upsert.ts
|
|
436
|
+
var upsertAction = async (upsertConfig, thunkApi) => {
|
|
437
|
+
const { dbKey, data } = upsertConfig;
|
|
438
|
+
if (!dbKey || !data || typeof data !== "object") {
|
|
439
|
+
const errorMsg = "upsertAction \u53C2\u6570\u65E0\u6548\uFF1AdbKey \u548C data \u5BF9\u8C61\u662F\u5FC5\u9700\u7684\u3002";
|
|
440
|
+
actionToast.error(errorMsg);
|
|
441
|
+
throw new Error(errorMsg);
|
|
442
|
+
}
|
|
443
|
+
try {
|
|
444
|
+
const existingData = await readAction({ dbKey }, thunkApi);
|
|
445
|
+
let finalResult;
|
|
446
|
+
if (existingData && Object.keys(existingData).length > 0) {
|
|
447
|
+
finalResult = await patchAction({ dbKey, changes: data }, thunkApi);
|
|
448
|
+
} else {
|
|
449
|
+
finalResult = await writeAction({ data, customKey: dbKey }, thunkApi);
|
|
450
|
+
}
|
|
451
|
+
return finalResult;
|
|
452
|
+
} catch (error) {
|
|
453
|
+
const errorMessage = `Upsert \u534F\u8C03\u64CD\u4F5C\u5931\u8D25 (dbKey: ${dbKey}): ${error.message || "\u672A\u77E5\u9519\u8BEF"}`;
|
|
454
|
+
actionToast.error("\u6570\u636E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
|
|
455
|
+
throw new Error(errorMessage, { cause: error });
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
// packages/database/actions/upload.ts
|
|
460
|
+
var saveToClientDb2 = async (clientDb, dbKey, metadata) => {
|
|
461
|
+
if (!clientDb) {
|
|
462
|
+
logger.error({ dbKey }, "Client database is undefined in saveToClientDb");
|
|
463
|
+
throw new Error("Client database instance is required");
|
|
464
|
+
}
|
|
465
|
+
try {
|
|
466
|
+
await clientDb.put(dbKey, metadata);
|
|
467
|
+
logger.debug(
|
|
468
|
+
{ dbKey },
|
|
469
|
+
"File metadata saved successfully to local database."
|
|
470
|
+
);
|
|
471
|
+
} catch (err) {
|
|
472
|
+
logger.error(
|
|
473
|
+
{ err, dbKey },
|
|
474
|
+
"Failed to save file metadata to local database"
|
|
475
|
+
);
|
|
476
|
+
throw new Error(`Local database put failed for ${dbKey}: ${err.message}`);
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
var uploadFileAction = async (uploadConfig, thunkApi) => {
|
|
480
|
+
const { db: clientDb } = thunkApi.extra;
|
|
481
|
+
const state = thunkApi.getState();
|
|
482
|
+
const { currentServer, syncServers, currentUserId } = getRuntimeServerContext(state);
|
|
483
|
+
const { file, customKey } = uploadConfig;
|
|
484
|
+
const userId = uploadConfig.userId || currentUserId;
|
|
485
|
+
if (!file) {
|
|
486
|
+
const errorMsg = "Invalid arguments for uploadFileAction: file is required.";
|
|
487
|
+
logger.error({ uploadConfig }, errorMsg);
|
|
488
|
+
throw new Error(errorMsg);
|
|
489
|
+
}
|
|
490
|
+
try {
|
|
491
|
+
const fileId = ulid();
|
|
492
|
+
const fileExtension = file.name.split(".").pop() || "";
|
|
493
|
+
const fileName = `${fileId}${fileExtension ? "." + fileExtension : ""}`;
|
|
494
|
+
let finalDbKey = customKey;
|
|
495
|
+
if (!finalDbKey || !finalDbKey.startsWith("file-")) {
|
|
496
|
+
const actualUserId = userId || "unknown";
|
|
497
|
+
if (actualUserId === "unknown") {
|
|
498
|
+
console.warn("[uploadFileAction] User ID is unknown during upload. Key will be file-unknown.");
|
|
499
|
+
}
|
|
500
|
+
finalDbKey = fileKey.single(actualUserId, fileId);
|
|
501
|
+
}
|
|
502
|
+
const fileMetadata = normalizeTimeFields({
|
|
503
|
+
id: fileId,
|
|
504
|
+
title: file.name,
|
|
505
|
+
originalName: file.name,
|
|
506
|
+
fileName,
|
|
507
|
+
filePath: "",
|
|
508
|
+
size: file.size,
|
|
509
|
+
mimeType: file.type || "application/octet-stream",
|
|
510
|
+
type: "file" /* FILE */,
|
|
511
|
+
fileCategory: resolveFileCategory({
|
|
512
|
+
mimeType: file.type,
|
|
513
|
+
fileName: file.name
|
|
514
|
+
}),
|
|
515
|
+
dbKey: finalDbKey,
|
|
516
|
+
userId
|
|
517
|
+
});
|
|
518
|
+
await saveToClientDb2(clientDb, finalDbKey, fileMetadata);
|
|
519
|
+
try {
|
|
520
|
+
await saveFileToIndexedDb(fileId, file);
|
|
521
|
+
} catch (err) {
|
|
522
|
+
logger.warn(
|
|
523
|
+
{ err, fileId },
|
|
524
|
+
"[uploadFileAction] Failed to cache file locally."
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
const tenantId = userId || "default";
|
|
528
|
+
const uploadReplicationConfig = {
|
|
529
|
+
file,
|
|
530
|
+
metadata: fileMetadata,
|
|
531
|
+
customKey: finalDbKey,
|
|
532
|
+
userId
|
|
533
|
+
};
|
|
534
|
+
const uploadServers = resolveUploadReplicationServers({
|
|
535
|
+
currentServer,
|
|
536
|
+
syncServers,
|
|
537
|
+
tenantId,
|
|
538
|
+
uploadConfig: uploadReplicationConfig,
|
|
539
|
+
state
|
|
540
|
+
});
|
|
541
|
+
const primaryUploadServer = uploadServers[0] ?? currentServer;
|
|
542
|
+
const primaryUploadSucceeded = await uploadToCurrentServer({
|
|
543
|
+
currentServer: primaryUploadServer,
|
|
544
|
+
uploadConfig: uploadReplicationConfig,
|
|
545
|
+
state
|
|
546
|
+
});
|
|
547
|
+
if (primaryUploadServer && !primaryUploadSucceeded) {
|
|
548
|
+
throw new Error(`Primary upload failed on authority server ${primaryUploadServer}`);
|
|
549
|
+
}
|
|
550
|
+
const serversToUse = scheduleUploadReplication({
|
|
551
|
+
currentServer,
|
|
552
|
+
syncServers,
|
|
553
|
+
tenantId,
|
|
554
|
+
uploadConfig: uploadReplicationConfig,
|
|
555
|
+
state,
|
|
556
|
+
excludeServers: primaryUploadServer ? [primaryUploadServer] : []
|
|
557
|
+
});
|
|
558
|
+
if (!primaryUploadServer && !serversToUse.length) {
|
|
559
|
+
logger.warn(
|
|
560
|
+
{ finalDbKey, fileName, tenantId },
|
|
561
|
+
"[uploadFileAction] No replication servers available, file metadata only saved locally."
|
|
562
|
+
);
|
|
563
|
+
return fileMetadata;
|
|
564
|
+
}
|
|
565
|
+
logger.debug(
|
|
566
|
+
{ primaryUploadServer, currentServer, serversToUse, tenantId },
|
|
567
|
+
`[uploadFileAction] Uploaded primary copy for ${fileName} and scheduled background sync to ${serversToUse.length} additional servers.`
|
|
568
|
+
);
|
|
569
|
+
return fileMetadata;
|
|
570
|
+
} catch (error) {
|
|
571
|
+
const errorMessage = `Upload action failed for ${customKey}: ${error?.message || "Unknown error"}`;
|
|
572
|
+
logger.error({ error }, "[uploadFileAction] Error");
|
|
573
|
+
throw new Error(errorMessage);
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
// packages/database/actions/fileContent.ts
|
|
578
|
+
var resolveLocalFileId = (fileId) => {
|
|
579
|
+
if (fileId.startsWith("file-")) {
|
|
580
|
+
return getFileIdFromKey(fileId) || fileId;
|
|
581
|
+
}
|
|
582
|
+
return fileId;
|
|
583
|
+
};
|
|
584
|
+
var readFileContentAction = async ({
|
|
585
|
+
fileId,
|
|
586
|
+
useServerFallback = true
|
|
587
|
+
}, thunkApi) => {
|
|
588
|
+
if (!fileId || typeof fileId !== "string") {
|
|
589
|
+
throw new Error("readFileContentAction requires a valid fileId string.");
|
|
590
|
+
}
|
|
591
|
+
const localId = resolveLocalFileId(fileId);
|
|
592
|
+
const localRecord = await loadFileFromIndexedDb(localId);
|
|
593
|
+
if (localRecord) {
|
|
594
|
+
return {
|
|
595
|
+
fileId: localId,
|
|
596
|
+
blob: localRecord.blob,
|
|
597
|
+
source: "local"
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
if (!useServerFallback) {
|
|
601
|
+
throw new Error(
|
|
602
|
+
`Local file not found for id "${fileId}", and server fallback is disabled.`
|
|
603
|
+
);
|
|
604
|
+
}
|
|
605
|
+
const state = thunkApi.getState();
|
|
606
|
+
const { currentServer, remoteServers: serversToTry } = getRuntimeServerContext(state);
|
|
607
|
+
if (!currentServer) {
|
|
608
|
+
throw new Error(
|
|
609
|
+
`No current server configured. Cannot fetch remote file for id "${fileId}".`
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
let lastError = "";
|
|
613
|
+
for (const server of serversToTry) {
|
|
614
|
+
const url = `${server}${API_ENDPOINTS.DATABASE}/file/content/${fileId}`;
|
|
615
|
+
console.debug("[readFileContentAction] trying server:", url);
|
|
616
|
+
try {
|
|
617
|
+
const res = await fetch(url);
|
|
618
|
+
if (!res.ok) {
|
|
619
|
+
lastError = `HTTP ${res.status} from ${server}`;
|
|
620
|
+
console.debug("[readFileContentAction] server returned:", lastError);
|
|
621
|
+
continue;
|
|
622
|
+
}
|
|
623
|
+
const blob = await res.blob();
|
|
624
|
+
if (typeof indexedDB !== "undefined") {
|
|
625
|
+
void saveFileToIndexedDb(localId, blob).catch((err) => {
|
|
626
|
+
console.warn(
|
|
627
|
+
"[readFileContentAction] Failed to cache remote file into IndexedDB:",
|
|
628
|
+
err
|
|
629
|
+
);
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
return {
|
|
633
|
+
fileId: localId,
|
|
634
|
+
blob,
|
|
635
|
+
source: "remote"
|
|
636
|
+
};
|
|
637
|
+
} catch (err) {
|
|
638
|
+
lastError = err?.message || "Network error";
|
|
639
|
+
console.debug(
|
|
640
|
+
"[readFileContentAction] fetch error from",
|
|
641
|
+
server,
|
|
642
|
+
lastError
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
throw new Error(
|
|
647
|
+
`Failed to fetch remote file content from all servers for id "${fileId}". Last error: ${lastError}`
|
|
648
|
+
);
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
// packages/database/dbSlice.ts
|
|
652
|
+
var dbAdapter = createEntityAdapter({
|
|
653
|
+
selectId: (entity) => entity.dbKey
|
|
654
|
+
});
|
|
655
|
+
var {
|
|
656
|
+
selectById,
|
|
657
|
+
selectEntities,
|
|
658
|
+
selectAll,
|
|
659
|
+
selectIds,
|
|
660
|
+
selectTotal
|
|
661
|
+
} = dbAdapter.getSelectors((state) => state.db);
|
|
662
|
+
var initialState = dbAdapter.getInitialState({});
|
|
663
|
+
var createSliceWithThunks = buildCreateSlice({
|
|
664
|
+
creators: { asyncThunk: asyncThunkCreator }
|
|
665
|
+
});
|
|
666
|
+
var dbSlice = createSliceWithThunks({
|
|
667
|
+
name: "db",
|
|
668
|
+
initialState,
|
|
669
|
+
reducers: (create) => ({
|
|
670
|
+
// Async Thunks
|
|
671
|
+
// 惰性加载 readAction:esbuild 把大型 read.ts 排到同 chunk 后部时,
|
|
672
|
+
// `var readAction` 在 create.asyncThunk(readAction) 执行时仍是 undefined,
|
|
673
|
+
// 之后所有 dispatch(read) 抛 "payloadCreator is not a function"。
|
|
674
|
+
// 动态 import 在调用期解析,避开 TDZ/求值顺序问题(同下方 share)。
|
|
675
|
+
read: create.asyncThunk(
|
|
676
|
+
async (payload, thunkApi) => {
|
|
677
|
+
const { readAction: readAction2 } = await import("./read-RIJCIVKX.js");
|
|
678
|
+
return readAction2(payload, thunkApi);
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
fulfilled: (state, action) => {
|
|
682
|
+
if (action.payload && Object.keys(action.payload).length > 0) {
|
|
683
|
+
dbAdapter.upsertOne(state, action.payload);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
),
|
|
688
|
+
readAndWait: create.asyncThunk(readAndWaitAction, {
|
|
689
|
+
fulfilled: (state, action) => {
|
|
690
|
+
if (action.payload && Object.keys(action.payload).length > 0) {
|
|
691
|
+
dbAdapter.upsertOne(state, action.payload);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}),
|
|
695
|
+
remove: create.asyncThunk(removeAction, {
|
|
696
|
+
fulfilled: (state, action) => {
|
|
697
|
+
const { dbKey } = action.payload;
|
|
698
|
+
if (dbKey) dbAdapter.removeOne(state, dbKey);
|
|
699
|
+
}
|
|
700
|
+
}),
|
|
701
|
+
purge: create.asyncThunk(purgeAction, {
|
|
702
|
+
fulfilled: (state, action) => {
|
|
703
|
+
const { dbKey } = action.payload;
|
|
704
|
+
if (dbKey) dbAdapter.removeOne(state, dbKey);
|
|
705
|
+
}
|
|
706
|
+
}),
|
|
707
|
+
write: create.asyncThunk(writeAction, {
|
|
708
|
+
fulfilled: (state, action) => {
|
|
709
|
+
if (action.payload && action.payload.dbKey && Object.keys(action.payload).length > 0) {
|
|
710
|
+
dbAdapter.upsertOne(state, action.payload);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}),
|
|
714
|
+
patch: create.asyncThunk(patchAction, {
|
|
715
|
+
fulfilled: (state, action) => {
|
|
716
|
+
const { payload } = action;
|
|
717
|
+
if (payload && payload.dbKey && Object.keys(payload).length > 0) {
|
|
718
|
+
dbAdapter.upsertOne(state, payload);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}),
|
|
722
|
+
upsert: create.asyncThunk(upsertAction, {
|
|
723
|
+
fulfilled: (state, action) => {
|
|
724
|
+
if (action.payload && action.payload.dbKey && Object.keys(action.payload).length > 0) {
|
|
725
|
+
dbAdapter.upsertOne(state, action.payload);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}),
|
|
729
|
+
// 文件上传(avatar / Slate / Space 等统一走这里)
|
|
730
|
+
upload: create.asyncThunk(uploadFileAction, {
|
|
731
|
+
fulfilled: (state, action) => {
|
|
732
|
+
const payload = action.payload;
|
|
733
|
+
if (payload && payload.dbKey && Object.keys(payload).length > 0) {
|
|
734
|
+
dbAdapter.upsertOne(state, payload);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}),
|
|
738
|
+
// 读取文件内容(优先本地 IndexedDB,无状态副作用)
|
|
739
|
+
readFileContent: create.asyncThunk(readFileContentAction, {
|
|
740
|
+
// fulfilled 时不修改 db state;由调用方通过 unwrap() 拿返回值使用
|
|
741
|
+
}),
|
|
742
|
+
// 惰性加载 shareResourceAction:静态 import 会形成
|
|
743
|
+
// dbSlice -> share/action -> settings/settingSlice -> settingPersistence
|
|
744
|
+
// -> settings/dbActionThunks -> database/dbActionThunks -> dbSlice 的循环依赖。
|
|
745
|
+
// 分包后 chunk 求值顺序一旦从环内进入 dbSlice,上面的 `readAction` 等
|
|
746
|
+
// `export const` 还处于未初始化状态,thunk 会以 undefined 作为 payloadCreator
|
|
747
|
+
// 建成,之后 dispatch(read(...)) 抛 "payloadCreator is not a function"。
|
|
748
|
+
// 这里改成动态 import,运行期才解析,彻底断开这条边。
|
|
749
|
+
share: create.asyncThunk(
|
|
750
|
+
async (config, thunkApi) => {
|
|
751
|
+
const { shareResourceAction } = await import("./action-ZJWE3Y5S.js");
|
|
752
|
+
return shareResourceAction(config, thunkApi);
|
|
753
|
+
}
|
|
754
|
+
),
|
|
755
|
+
// SSR 预取:服务端直接注入实体到 db slice,供首屏 hydrate 使用
|
|
756
|
+
upsertSSREntity: create.reducer((state, action) => {
|
|
757
|
+
if (action.payload && action.payload.dbKey) {
|
|
758
|
+
dbAdapter.upsertOne(state, action.payload);
|
|
759
|
+
}
|
|
760
|
+
}),
|
|
761
|
+
// Undo an in-memory optimistic entity without writing a tombstone or
|
|
762
|
+
// scheduling remote deletion. Durable deletes must continue to use remove.
|
|
763
|
+
removeCachedEntity: create.reducer((state, action) => {
|
|
764
|
+
if (action.payload) dbAdapter.removeOne(state, action.payload);
|
|
765
|
+
})
|
|
766
|
+
})
|
|
767
|
+
});
|
|
768
|
+
var {
|
|
769
|
+
remove,
|
|
770
|
+
purge,
|
|
771
|
+
read,
|
|
772
|
+
readAndWait,
|
|
773
|
+
write,
|
|
774
|
+
patch,
|
|
775
|
+
upsert,
|
|
776
|
+
upload,
|
|
777
|
+
readFileContent,
|
|
778
|
+
share,
|
|
779
|
+
upsertSSREntity,
|
|
780
|
+
removeCachedEntity
|
|
781
|
+
} = dbSlice.actions;
|
|
782
|
+
var dbSlice_default = dbSlice.reducer;
|
|
783
|
+
|
|
784
|
+
export {
|
|
785
|
+
readAndWaitAction,
|
|
786
|
+
toTimestampMs,
|
|
787
|
+
uploadFileAction,
|
|
788
|
+
dbAdapter,
|
|
789
|
+
selectById,
|
|
790
|
+
selectEntities,
|
|
791
|
+
selectAll,
|
|
792
|
+
selectIds,
|
|
793
|
+
selectTotal,
|
|
794
|
+
remove,
|
|
795
|
+
purge,
|
|
796
|
+
read,
|
|
797
|
+
readAndWait,
|
|
798
|
+
write,
|
|
799
|
+
patch,
|
|
800
|
+
upsert,
|
|
801
|
+
upload,
|
|
802
|
+
readFileContent,
|
|
803
|
+
share,
|
|
804
|
+
upsertSSREntity,
|
|
805
|
+
removeCachedEntity,
|
|
806
|
+
dbSlice_default
|
|
807
|
+
};
|