nolo-cli 0.159.0-alpha.1 → 0.159.0-alpha.3
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-JISIM4PS.js → _executeModel-SHA5BWRT.js} +17 -18
- package/{agentDeleteCommand-2NMZDFL5.js → agentDeleteCommand-VU3BA264.js} +16 -17
- package/{agentEmailCommands-VQ2QGKL4.js → agentEmailCommands-WUANEGTM.js} +18 -19
- package/{agentListCommands-3CJ4GF6C.js → agentListCommands-3YNO6UP4.js} +5 -5
- package/{agentMachineCommands-BMOEPV5C.js → agentMachineCommands-ONGZXXQW.js} +6 -6
- package/{agentRecordCommands-XRB7BLWV.js → agentRecordCommands-DXXA2RET.js} +17 -18
- package/{agentRunCommand-VKCTAW55.js → agentRunCommand-WY77TJ72.js} +18 -19
- package/{agentRuntimeLocal-7JDS3OAL.js → agentRuntimeLocal-NHC5DQPJ.js} +5 -5
- package/{agentSlice-ST6QQ5FD.js → agentSlice-MIOEV36T.js} +1 -1
- package/{appDeleteCommands-UVWHW4TJ.js → appDeleteCommands-VQYCXZRF.js} +5 -5
- package/{appDeployCommands-67JPR4JU.js → appDeployCommands-OKSJDVEY.js} +5 -5
- package/{appDeployStatusCommands-UTC6IR5V.js → appDeployStatusCommands-UFGNZS6G.js} +5 -5
- package/{appGetCommands-KLLIGWNC.js → appGetCommands-3HNO5WLF.js} +5 -5
- package/{appListCommands-AZKNWJUR.js → appListCommands-H33BIZHJ.js} +5 -5
- package/{chunk-B5MSFO6R.js → chunk-2NROMJBM.js} +1 -1
- package/chunk-6MH6PWVJ.js +2685 -0
- package/{chunk-HXSN43Y5.js → chunk-BFI6JGF6.js} +10 -10
- package/{chunk-JKXXZUO4.js → chunk-C47WM5SD.js} +1 -1
- package/{chunk-5MM7NNO6.js → chunk-CNWKLQRL.js} +8 -3
- package/{chunk-FUNTOSV3.js → chunk-CSCSMGQI.js} +3 -3
- package/{chunk-4JOZECCA.js → chunk-CVSKYIXX.js} +2 -2
- package/{chunk-DD7BZWJ6.js → chunk-DWBY333O.js} +3 -3
- package/{chunk-FVZVJA54.js → chunk-E46SJRWA.js} +3 -3
- package/{chunk-JPSTJHGJ.js → chunk-FBSDWUOJ.js} +2 -2
- package/{chunk-MNRZCEYU.js → chunk-FZYTJOAQ.js} +14 -14
- package/{chunk-R6ABE4UC.js → chunk-GCLZBOB6.js} +9 -9
- package/{chunk-OVQFMCVI.js → chunk-GSKVOMBU.js} +2 -2
- package/{chunk-J4S44CSW.js → chunk-NNE5NIW2.js} +2 -2
- package/{chunk-IULXURBL.js → chunk-NT3LMOSE.js} +4 -4
- package/{chunk-RGYDJ352.js → chunk-QO436IJ5.js} +2 -2
- package/{chunk-U6TTTB6V.js → chunk-QYAYO5JA.js} +1 -1
- package/{chunk-XEF4HW56.js → chunk-RJBHRIEL.js} +9 -9
- package/{chunk-MGSNS22D.js → chunk-TCQPBZUL.js} +2 -736
- package/{chunk-IE7G4BF5.js → chunk-TMYAT2MD.js} +3 -3
- package/{chunk-PIYYH2LE.js → chunk-V4AK4IVZ.js} +2 -2
- package/{chunk-4CYTNZG3.js → chunk-XBLZ5IFE.js} +7 -7
- package/{chunk-GJNYJH7O.js → chunk-ZAB4R7LU.js} +13 -13
- package/{chunk-GB552UJS.js → chunk-ZJG7YU3I.js} +1 -1
- package/{commandRegistry-ROBNQFWA.js → commandRegistry-DAM7RV6E.js} +40 -40
- package/{contentThunks-EWOJGG2I.js → contentThunks-WLKR5KQ6.js} +6 -6
- package/{createAgentAutomationAction-I43ICGXU.js → createAgentAutomationAction-AI2HZHLE.js} +6 -6
- package/{createDialogAction-IIAHRXND.js → createDialogAction-AU4IWDR3.js} +7 -7
- package/{createPageAction-OVK7PG4L.js → createPageAction-CAXS6ZUJ.js} +7 -8
- package/{dialogCommands-K4OVKDYD.js → dialogCommands-2QMR63AY.js} +5 -5
- package/{dialogSlice-U22CFPRN.js → dialogSlice-QEP5EUON.js} +5 -5
- package/{dist-NJC2W7I4.js → dist-Q2JSMAKA.js} +7 -361
- package/{docCreateCommands-GI377ZFI.js → docCreateCommands-EG3KFJGO.js} +5 -5
- package/{docDeleteCommands-NBQNTVHB.js → docDeleteCommands-MAM2BLCP.js} +5 -5
- package/{docReadCommands-6SKEW6AO.js → docReadCommands-XVX2VLVM.js} +5 -5
- package/{docStore-SS2QUBG6.js → docStore-WTXUKAQB.js} +1 -1
- package/{docUpdateCommands-NJ7JD6VA.js → docUpdateCommands-JURMHPS5.js} +5 -5
- package/{handleSendMessageAction-YVPRHPQR.js → handleSendMessageAction-7MAFLFOD.js} +10 -10
- package/index.js +4 -4
- package/{localRuntimeAdapter-CC7ZPJRV.js → localRuntimeAdapter-DSO3Y5ZS.js} +16 -17
- package/{machineCommands-XEWIUHTY.js → machineCommands-DWILXGTZ.js} +17 -18
- package/{machineWsRunDispatch-JYEWCXGP.js → machineWsRunDispatch-NJJZ34JJ.js} +17 -18
- package/package.json +6 -2
- package/{readlineWorkspace-7MZPTN5U.js → readlineWorkspace-STK57SSQ.js} +21 -22
- package/{runAgentClientLoop-MLEVVWUH.js → runAgentClientLoop-6OD2CTYG.js} +15 -16
- package/{spaceCommands-UM3ZQKJU.js → spaceCommands-WPCHZKEY.js} +5 -5
- package/{streamAgentChatTurn-J7HYND6V.js → streamAgentChatTurn-BOATI5H3.js} +17 -18
- package/{tools-3CEH74XY.js → tools-FPKQX2J6.js} +11 -12
- package/{tuiSummaryLlmCaller-VSNIIH63.js → tuiSummaryLlmCaller-PEYFTAVL.js} +3 -3
- package/{updateDialogTitleAction-C557RTYG.js → updateDialogTitleAction-WAH5KADA.js} +7 -7
- package/chunk-IA7TRNNR.js +0 -11
- package/chunk-NJTXRGCJ.js +0 -7018
- package/clipboardy-GCO3GT3I.js +0 -11793
|
@@ -0,0 +1,2685 @@
|
|
|
1
|
+
import {
|
|
2
|
+
deleteDialog,
|
|
3
|
+
runtime_default,
|
|
4
|
+
toast
|
|
5
|
+
} from "./chunk-FZYTJOAQ.js";
|
|
6
|
+
import {
|
|
7
|
+
getCurrentSpaceId,
|
|
8
|
+
getCurrentSpaceIdRaw,
|
|
9
|
+
updateCurrentSpaceIfMatch
|
|
10
|
+
} from "./chunk-ZJ7UHBJS.js";
|
|
11
|
+
import {
|
|
12
|
+
formatISO
|
|
13
|
+
} from "./chunk-ZRAU6T47.js";
|
|
14
|
+
import {
|
|
15
|
+
require_react
|
|
16
|
+
} from "./chunk-YDH3FDFQ.js";
|
|
17
|
+
import {
|
|
18
|
+
UNCATEGORIZED_ID,
|
|
19
|
+
addContentAction,
|
|
20
|
+
checkSpaceMembership,
|
|
21
|
+
localSpaceAuthorityPatchStamp
|
|
22
|
+
} from "./chunk-M2P3OGXZ.js";
|
|
23
|
+
import {
|
|
24
|
+
createSpaceKey,
|
|
25
|
+
normalizeSpaceId
|
|
26
|
+
} from "./chunk-UAD2WT54.js";
|
|
27
|
+
import {
|
|
28
|
+
patch,
|
|
29
|
+
read,
|
|
30
|
+
readAndWait,
|
|
31
|
+
remove,
|
|
32
|
+
uploadFileAction,
|
|
33
|
+
upsertSSREntity,
|
|
34
|
+
write
|
|
35
|
+
} from "./chunk-ZVQHBIWV.js";
|
|
36
|
+
import {
|
|
37
|
+
resolveFileCategory
|
|
38
|
+
} from "./chunk-3PVYUPXK.js";
|
|
39
|
+
import {
|
|
40
|
+
DEVICE_LOCAL_OWNER_ID,
|
|
41
|
+
getRuntimeServerContext,
|
|
42
|
+
isDeviceLocalSpaceBody,
|
|
43
|
+
resolveReplicationServers,
|
|
44
|
+
scheduleDeleteReplication,
|
|
45
|
+
scheduleWriteReplication
|
|
46
|
+
} from "./chunk-2SGAI64X.js";
|
|
47
|
+
import {
|
|
48
|
+
createAsyncThunk
|
|
49
|
+
} from "./chunk-WRCZXJ4N.js";
|
|
50
|
+
import {
|
|
51
|
+
getIsDesktopApp
|
|
52
|
+
} from "./chunk-6Q7UTJKR.js";
|
|
53
|
+
import {
|
|
54
|
+
createClientLogger
|
|
55
|
+
} from "./chunk-QQ56R7KS.js";
|
|
56
|
+
import {
|
|
57
|
+
selectIdentityUserId
|
|
58
|
+
} from "./chunk-PXYBUFDB.js";
|
|
59
|
+
import {
|
|
60
|
+
buildTombstoneRecord
|
|
61
|
+
} from "./chunk-D2MDMY2M.js";
|
|
62
|
+
import {
|
|
63
|
+
isSystemAdmin
|
|
64
|
+
} from "./chunk-Y6ORVLSS.js";
|
|
65
|
+
import {
|
|
66
|
+
asRecordOrEmpty
|
|
67
|
+
} from "./chunk-KX6YBIHG.js";
|
|
68
|
+
import {
|
|
69
|
+
isAbortError
|
|
70
|
+
} from "./chunk-7DPNBQSV.js";
|
|
71
|
+
import {
|
|
72
|
+
extractUserId
|
|
73
|
+
} from "./chunk-M2EJHDJP.js";
|
|
74
|
+
import {
|
|
75
|
+
SEPARATOR,
|
|
76
|
+
createKey,
|
|
77
|
+
fileKey,
|
|
78
|
+
isAgentKey,
|
|
79
|
+
isPageKey,
|
|
80
|
+
metaKey,
|
|
81
|
+
rowKey,
|
|
82
|
+
splitKey,
|
|
83
|
+
ulid
|
|
84
|
+
} from "./chunk-5EMSBTQ6.js";
|
|
85
|
+
import {
|
|
86
|
+
asTrimmedNonEmptyStringArray
|
|
87
|
+
} from "./chunk-I42VC5KR.js";
|
|
88
|
+
import {
|
|
89
|
+
asOptionalPositiveFiniteNumber
|
|
90
|
+
} from "./chunk-U6VC4MU4.js";
|
|
91
|
+
import {
|
|
92
|
+
asOptionalFiniteNumber
|
|
93
|
+
} from "./chunk-WWC7I6TA.js";
|
|
94
|
+
import {
|
|
95
|
+
isRecord
|
|
96
|
+
} from "./chunk-KUG52IAP.js";
|
|
97
|
+
import {
|
|
98
|
+
asTrimmedString
|
|
99
|
+
} from "./chunk-3EUW3WEO.js";
|
|
100
|
+
import {
|
|
101
|
+
asOptionalTrimmedString
|
|
102
|
+
} from "./chunk-G3SUCT4W.js";
|
|
103
|
+
import {
|
|
104
|
+
__toESM
|
|
105
|
+
} from "./chunk-4VNS5WPM.js";
|
|
106
|
+
|
|
107
|
+
// packages/render/table/tableStore.ts
|
|
108
|
+
var import_react = __toESM(require_react(), 1);
|
|
109
|
+
import { ulid as ulid3 } from "ulid";
|
|
110
|
+
|
|
111
|
+
// packages/core/logger.ts
|
|
112
|
+
import pino from "pino";
|
|
113
|
+
var logger = pino({
|
|
114
|
+
level: process.env.NODE_ENV === "production" ? "info" : "debug"
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// packages/render/table/fetchAndCacheTableRows.ts
|
|
118
|
+
var TABLE_ROWS_FETCH_TIMEOUT_MS = 5e3;
|
|
119
|
+
var getRowTimestamp = (row) => {
|
|
120
|
+
if (!row || typeof row !== "object") return 0;
|
|
121
|
+
return asOptionalPositiveFiniteNumber(Date.parse(row.updatedAt ?? "")) ?? asOptionalPositiveFiniteNumber(Date.parse(row.createdAt ?? "")) ?? 0;
|
|
122
|
+
};
|
|
123
|
+
var shouldReplaceMergedRow = (nextRow, currentRow) => {
|
|
124
|
+
const nextTs = getRowTimestamp(nextRow);
|
|
125
|
+
const currentTs = getRowTimestamp(currentRow);
|
|
126
|
+
if (nextTs !== currentTs) return nextTs > currentTs;
|
|
127
|
+
return Boolean(nextRow?.deletedAt) && !currentRow?.deletedAt;
|
|
128
|
+
};
|
|
129
|
+
var mergeTableRows = (...rowLists) => {
|
|
130
|
+
const merged = /* @__PURE__ */ new Map();
|
|
131
|
+
for (const rowList of rowLists) {
|
|
132
|
+
for (const row of rowList) {
|
|
133
|
+
const dbKey = row?.dbKey;
|
|
134
|
+
if (!dbKey) continue;
|
|
135
|
+
const existing = merged.get(dbKey);
|
|
136
|
+
if (!existing || shouldReplaceMergedRow(row, existing)) {
|
|
137
|
+
merged.set(dbKey, row);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return Array.from(merged.values());
|
|
142
|
+
};
|
|
143
|
+
var TABLE_SYNC_ENVELOPE = "table-sync-v1";
|
|
144
|
+
var getLatestTableMeta = (snapshots) => {
|
|
145
|
+
let latestMeta = null;
|
|
146
|
+
for (const snapshot of snapshots) {
|
|
147
|
+
const tableMeta = snapshot.tableMeta;
|
|
148
|
+
if (!tableMeta || typeof tableMeta !== "object") continue;
|
|
149
|
+
if (!latestMeta || shouldReplaceMergedRow(tableMeta, latestMeta)) {
|
|
150
|
+
latestMeta = tableMeta;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return latestMeta;
|
|
154
|
+
};
|
|
155
|
+
var loadLocalTableRows = async (db, tenantId, tableId) => {
|
|
156
|
+
if (!db || typeof db.iterator !== "function") {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
const rows = [];
|
|
160
|
+
const { gte, lte } = rowKey.range(tenantId, tableId);
|
|
161
|
+
try {
|
|
162
|
+
for await (const [, value] of db.iterator({ gte, lte })) {
|
|
163
|
+
if (value?.type === "table_row" /* TABLE_ROW */) {
|
|
164
|
+
rows.push(value);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} catch {
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
return rows;
|
|
171
|
+
};
|
|
172
|
+
var fetchTableRowsFromServer = async (server, tenantId, tableId, headers, fetchImpl = fetch) => {
|
|
173
|
+
const controller = new AbortController();
|
|
174
|
+
const timeoutId = setTimeout(
|
|
175
|
+
() => controller.abort(),
|
|
176
|
+
TABLE_ROWS_FETCH_TIMEOUT_MS
|
|
177
|
+
);
|
|
178
|
+
try {
|
|
179
|
+
const res = await fetchImpl(`${server}/rpc/listTableRows`, {
|
|
180
|
+
method: "POST",
|
|
181
|
+
headers,
|
|
182
|
+
credentials: "include",
|
|
183
|
+
body: JSON.stringify({
|
|
184
|
+
tenantId,
|
|
185
|
+
tableId,
|
|
186
|
+
includeDeleted: true,
|
|
187
|
+
envelope: TABLE_SYNC_ENVELOPE
|
|
188
|
+
}),
|
|
189
|
+
signal: controller.signal
|
|
190
|
+
});
|
|
191
|
+
if (!res.ok) {
|
|
192
|
+
let msg = `\u52A0\u8F7D\u8868 ${tableId} \u884C\u5931\u8D25\uFF08${res.status}\uFF09`;
|
|
193
|
+
try {
|
|
194
|
+
const err2 = await res.json();
|
|
195
|
+
if (err2 && typeof err2.message === "string") {
|
|
196
|
+
msg = err2.message;
|
|
197
|
+
}
|
|
198
|
+
} catch {
|
|
199
|
+
}
|
|
200
|
+
throw new Error(msg);
|
|
201
|
+
}
|
|
202
|
+
const data = await res.json();
|
|
203
|
+
if (Array.isArray(data)) {
|
|
204
|
+
return {
|
|
205
|
+
rows: data,
|
|
206
|
+
deletedRows: [],
|
|
207
|
+
tableMeta: null,
|
|
208
|
+
complete: false
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
if (!data || typeof data !== "object" || !Array.isArray(data.rows)) {
|
|
212
|
+
throw new Error("\u670D\u52A1\u5668\u8FD4\u56DE\u683C\u5F0F\u9519\u8BEF\uFF1A\u9884\u671F\u4E3A\u6570\u7EC4");
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
rows: data.rows,
|
|
216
|
+
deletedRows: Array.isArray(data.deletedRows) ? data.deletedRows : [],
|
|
217
|
+
tableMeta: data.tableMeta ?? null,
|
|
218
|
+
complete: data.complete === true
|
|
219
|
+
};
|
|
220
|
+
} catch (err2) {
|
|
221
|
+
if (isAbortError(err2)) {
|
|
222
|
+
throw new Error(
|
|
223
|
+
`\u52A0\u8F7D\u8868 ${tableId} \u884C\u8D85\u65F6\uFF1A${server} \u5728 ${TABLE_ROWS_FETCH_TIMEOUT_MS / 1e3}s \u5185\u65E0\u54CD\u5E94`
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
throw err2;
|
|
227
|
+
} finally {
|
|
228
|
+
clearTimeout(timeoutId);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
var cacheMergedTableRows = async (db, mergedRows) => {
|
|
232
|
+
if (!db) return;
|
|
233
|
+
await Promise.all(
|
|
234
|
+
mergedRows.map(async (mergedRow) => {
|
|
235
|
+
if (!mergedRow?.dbKey) return;
|
|
236
|
+
try {
|
|
237
|
+
const localRow = await db.get(mergedRow.dbKey).catch(() => null);
|
|
238
|
+
if (!localRow) {
|
|
239
|
+
await db.put(mergedRow.dbKey, mergedRow);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const serverTs = new Date(
|
|
243
|
+
mergedRow.updatedAt ?? mergedRow.createdAt ?? 0
|
|
244
|
+
).getTime();
|
|
245
|
+
const localTs = new Date(
|
|
246
|
+
localRow.updatedAt ?? localRow.createdAt ?? 0
|
|
247
|
+
).getTime();
|
|
248
|
+
const shouldOverwrite = serverTs > localTs || serverTs === localTs && Boolean(mergedRow.deletedAt) && !Boolean(localRow.deletedAt);
|
|
249
|
+
if (shouldOverwrite) {
|
|
250
|
+
await db.put(mergedRow.dbKey, mergedRow);
|
|
251
|
+
}
|
|
252
|
+
} catch {
|
|
253
|
+
}
|
|
254
|
+
})
|
|
255
|
+
);
|
|
256
|
+
};
|
|
257
|
+
var clearStaleLocalRows = async (db, localRows, authoritativeRows) => {
|
|
258
|
+
if (!db || typeof db.del !== "function") return;
|
|
259
|
+
const authoritativeKeys = new Set(
|
|
260
|
+
authoritativeRows.map((row) => row?.dbKey).filter((dbKey) => typeof dbKey === "string" && dbKey.length > 0)
|
|
261
|
+
);
|
|
262
|
+
await Promise.all(
|
|
263
|
+
localRows.map(async (row) => {
|
|
264
|
+
const dbKey = row?.dbKey;
|
|
265
|
+
if (typeof dbKey !== "string" || authoritativeKeys.has(dbKey)) return;
|
|
266
|
+
try {
|
|
267
|
+
await db.del(dbKey);
|
|
268
|
+
} catch {
|
|
269
|
+
}
|
|
270
|
+
})
|
|
271
|
+
);
|
|
272
|
+
};
|
|
273
|
+
var keepRemoteRowForPartialMerge = (row, localRowsByKey) => {
|
|
274
|
+
const dbKey = row?.dbKey;
|
|
275
|
+
if (typeof dbKey !== "string" || !dbKey) return false;
|
|
276
|
+
const localRow = localRowsByKey.get(dbKey);
|
|
277
|
+
if (!localRow) return true;
|
|
278
|
+
const remoteDeleted = Boolean(row?.deletedAt);
|
|
279
|
+
const localDeleted = Boolean(localRow?.deletedAt);
|
|
280
|
+
return remoteDeleted === localDeleted;
|
|
281
|
+
};
|
|
282
|
+
var fetchAndCacheTableRows = async ({
|
|
283
|
+
db,
|
|
284
|
+
tenantId,
|
|
285
|
+
tableId,
|
|
286
|
+
token,
|
|
287
|
+
remoteServers = [],
|
|
288
|
+
fetchImpl = fetch
|
|
289
|
+
}) => {
|
|
290
|
+
const headers = {
|
|
291
|
+
"Content-Type": "application/json"
|
|
292
|
+
};
|
|
293
|
+
if (typeof token === "string" && token.trim()) {
|
|
294
|
+
headers.Authorization = `Bearer ${token.trim()}`;
|
|
295
|
+
}
|
|
296
|
+
const isWebBrowser = typeof window !== "undefined" && !getIsDesktopApp();
|
|
297
|
+
const serversToQuery = isWebBrowser && remoteServers.length > 0 ? [remoteServers[0]] : remoteServers;
|
|
298
|
+
const localRows = await loadLocalTableRows(db, tenantId, tableId);
|
|
299
|
+
const remoteResults = await Promise.allSettled(
|
|
300
|
+
serversToQuery.map(
|
|
301
|
+
(server) => fetchTableRowsFromServer(server, tenantId, tableId, headers, fetchImpl)
|
|
302
|
+
)
|
|
303
|
+
);
|
|
304
|
+
const fulfilledRemoteSnapshots = remoteResults.filter(
|
|
305
|
+
(result) => result.status === "fulfilled"
|
|
306
|
+
).map((result) => result.value);
|
|
307
|
+
if (fulfilledRemoteSnapshots.length === 0 && localRows.length === 0) {
|
|
308
|
+
const firstFailure = remoteResults.find(
|
|
309
|
+
(result) => result.status === "rejected"
|
|
310
|
+
);
|
|
311
|
+
throw new Error(firstFailure?.reason?.message || "\u52A0\u8F7D\u8868\u884C\u5931\u8D25");
|
|
312
|
+
}
|
|
313
|
+
const allRemoteSnapshotsComplete = serversToQuery.length > 0 && remoteResults.length === serversToQuery.length && fulfilledRemoteSnapshots.length === serversToQuery.length && fulfilledRemoteSnapshots.every((snapshot) => snapshot.complete);
|
|
314
|
+
const localRowsByKey = new Map(
|
|
315
|
+
localRows.filter((row) => typeof row?.dbKey === "string" && row.dbKey.length > 0).map((row) => [row.dbKey, row])
|
|
316
|
+
);
|
|
317
|
+
const remoteRowLists = fulfilledRemoteSnapshots.map((snapshot) => {
|
|
318
|
+
const snapshotRows = [...snapshot.rows, ...snapshot.deletedRows];
|
|
319
|
+
return allRemoteSnapshotsComplete ? snapshotRows : snapshotRows.filter((row) => keepRemoteRowForPartialMerge(row, localRowsByKey));
|
|
320
|
+
});
|
|
321
|
+
const authoritativeRemoteRows = remoteRowLists.flat();
|
|
322
|
+
const tableDeleted = allRemoteSnapshotsComplete && Boolean(getLatestTableMeta(fulfilledRemoteSnapshots)?.deletedAt);
|
|
323
|
+
if (allRemoteSnapshotsComplete) {
|
|
324
|
+
await clearStaleLocalRows(db, localRows, authoritativeRemoteRows);
|
|
325
|
+
}
|
|
326
|
+
const localRowsForMerge = allRemoteSnapshotsComplete ? localRows.filter(
|
|
327
|
+
(row) => authoritativeRemoteRows.some((remoteRow) => remoteRow?.dbKey === row?.dbKey)
|
|
328
|
+
) : localRows;
|
|
329
|
+
const mergedRows = mergeTableRows(localRowsForMerge, ...remoteRowLists);
|
|
330
|
+
await cacheMergedTableRows(db, mergedRows);
|
|
331
|
+
return tableDeleted ? [] : mergedRows.filter((row) => !row?.deletedAt);
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// packages/render/table/tableColumnCore.ts
|
|
335
|
+
var ok = (value) => ({ ok: true, value });
|
|
336
|
+
var err = (error) => ({ ok: false, error });
|
|
337
|
+
var hasOwn = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
|
|
338
|
+
var buildMeta = (meta, columns, updatedAt) => ({
|
|
339
|
+
meta: { ...meta, columns, updatedAt },
|
|
340
|
+
metaChanges: { columns, updatedAt },
|
|
341
|
+
noop: false
|
|
342
|
+
});
|
|
343
|
+
var reorderList = (list, from, to) => {
|
|
344
|
+
const next = [...list];
|
|
345
|
+
const [moved] = next.splice(from, 1);
|
|
346
|
+
next.splice(to, 0, moved);
|
|
347
|
+
return next;
|
|
348
|
+
};
|
|
349
|
+
function addColumnToMeta(meta, input, deps) {
|
|
350
|
+
const { columnName } = input;
|
|
351
|
+
if (!columnName.trim()) {
|
|
352
|
+
return err("\u5B57\u6BB5\u540D\u4E0D\u80FD\u4E3A\u7A7A");
|
|
353
|
+
}
|
|
354
|
+
if (meta.columns.some((c) => c.name === columnName)) {
|
|
355
|
+
return err(`\u5B57\u6BB5 ${columnName} \u5DF2\u5B58\u5728`);
|
|
356
|
+
}
|
|
357
|
+
const newColumn = {
|
|
358
|
+
id: deps.id,
|
|
359
|
+
name: columnName,
|
|
360
|
+
label: columnName
|
|
361
|
+
};
|
|
362
|
+
return ok(buildMeta(meta, [...meta.columns, newColumn], deps.nowIso));
|
|
363
|
+
}
|
|
364
|
+
function deleteColumnFromMeta(meta, rows, input, deps) {
|
|
365
|
+
const { columnName } = input;
|
|
366
|
+
const { nowIso } = deps;
|
|
367
|
+
if (!meta.columns.some((c) => c.name === columnName)) {
|
|
368
|
+
return err(`\u5B57\u6BB5 ${columnName} \u4E0D\u5B58\u5728`);
|
|
369
|
+
}
|
|
370
|
+
const newColumns = meta.columns.filter((c) => c.name !== columnName);
|
|
371
|
+
const rowPatches = rows.filter((row) => hasOwn(row, columnName)).map((row) => ({
|
|
372
|
+
dbKey: row.dbKey,
|
|
373
|
+
changes: { [columnName]: null, updatedAt: nowIso }
|
|
374
|
+
}));
|
|
375
|
+
const newRows = rows.map((row) => {
|
|
376
|
+
if (!hasOwn(row, columnName)) {
|
|
377
|
+
return row;
|
|
378
|
+
}
|
|
379
|
+
const { [columnName]: _removed, ...rest } = row;
|
|
380
|
+
return { ...rest, updatedAt: nowIso };
|
|
381
|
+
});
|
|
382
|
+
return ok({
|
|
383
|
+
...buildMeta(meta, newColumns, nowIso),
|
|
384
|
+
rows: newRows,
|
|
385
|
+
rowPatches
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
function reorderColumnInMeta(meta, input, deps) {
|
|
389
|
+
const { fromIndex, toIndex } = input;
|
|
390
|
+
const columnCount = meta.columns.length;
|
|
391
|
+
if (fromIndex < 0 || fromIndex >= columnCount || toIndex < 0 || toIndex >= columnCount) {
|
|
392
|
+
return err("\u5217\u7D22\u5F15\u8D85\u51FA\u8303\u56F4");
|
|
393
|
+
}
|
|
394
|
+
if (fromIndex === toIndex) {
|
|
395
|
+
return ok({
|
|
396
|
+
meta,
|
|
397
|
+
metaChanges: { columns: meta.columns, updatedAt: meta.updatedAt },
|
|
398
|
+
noop: true
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
return ok(
|
|
402
|
+
buildMeta(meta, reorderList(meta.columns, fromIndex, toIndex), deps.nowIso)
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
function renameColumnLabelInMeta(meta, input, deps) {
|
|
406
|
+
const { columnId } = input;
|
|
407
|
+
const label = input.label.trim();
|
|
408
|
+
if (!label) {
|
|
409
|
+
return err("\u5B57\u6BB5\u663E\u793A\u540D\u4E0D\u80FD\u4E3A\u7A7A");
|
|
410
|
+
}
|
|
411
|
+
if (!meta.columns.some((c) => c.id === columnId)) {
|
|
412
|
+
return err("\u8981\u91CD\u547D\u540D\u7684\u5B57\u6BB5\u4E0D\u5B58\u5728");
|
|
413
|
+
}
|
|
414
|
+
const newColumns = meta.columns.map(
|
|
415
|
+
(c) => c.id === columnId ? { ...c, label } : c
|
|
416
|
+
);
|
|
417
|
+
return ok(buildMeta(meta, newColumns, deps.nowIso));
|
|
418
|
+
}
|
|
419
|
+
function updateColumnWidthInMeta(meta, input, deps) {
|
|
420
|
+
const { columnId, width } = input;
|
|
421
|
+
if (!meta.columns.some((c) => c.id === columnId)) {
|
|
422
|
+
return err("\u8981\u8C03\u6574\u5BBD\u5EA6\u7684\u5B57\u6BB5\u4E0D\u5B58\u5728");
|
|
423
|
+
}
|
|
424
|
+
const normalizedWidth = typeof width === "number" && width > 0 ? Math.round(width) : void 0;
|
|
425
|
+
const newColumns = meta.columns.map(
|
|
426
|
+
(c) => c.id === columnId ? { ...c, width: normalizedWidth } : c
|
|
427
|
+
);
|
|
428
|
+
return ok(buildMeta(meta, newColumns, deps.nowIso));
|
|
429
|
+
}
|
|
430
|
+
function addColumnOptionInMeta(meta, input, deps) {
|
|
431
|
+
const { columnId } = input;
|
|
432
|
+
const option = input.option.trim();
|
|
433
|
+
if (!option) {
|
|
434
|
+
return err("\u9009\u9879\u540D\u4E0D\u80FD\u4E3A\u7A7A");
|
|
435
|
+
}
|
|
436
|
+
const column = meta.columns.find((c) => c.id === columnId);
|
|
437
|
+
if (!column) {
|
|
438
|
+
return err("\u8981\u65B0\u589E\u9009\u9879\u7684\u5B57\u6BB5\u4E0D\u5B58\u5728");
|
|
439
|
+
}
|
|
440
|
+
if ((column.options ?? []).some((o) => o.trim() === option)) {
|
|
441
|
+
return ok({
|
|
442
|
+
meta,
|
|
443
|
+
metaChanges: { columns: meta.columns, updatedAt: meta.updatedAt },
|
|
444
|
+
noop: true
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
const newColumns = meta.columns.map(
|
|
448
|
+
(c) => c.id === columnId ? { ...c, options: [...c.options ?? [], option] } : c
|
|
449
|
+
);
|
|
450
|
+
return ok(buildMeta(meta, newColumns, deps.nowIso));
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// packages/render/table/createTableAction.ts
|
|
454
|
+
import { ulid as ulid2 } from "ulid";
|
|
455
|
+
var DEFAULT_COLUMNS = [
|
|
456
|
+
{
|
|
457
|
+
name: "title",
|
|
458
|
+
label: "\u6807\u9898",
|
|
459
|
+
type: "text",
|
|
460
|
+
description: "\u8FD9\u4E00\u884C\u8BB0\u5F55\u7684\u4E3B\u9898\u6216\u540D\u79F0\u3002",
|
|
461
|
+
isPrimary: true,
|
|
462
|
+
required: true
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: "note",
|
|
466
|
+
label: "\u5907\u6CE8",
|
|
467
|
+
type: "text",
|
|
468
|
+
description: "\u5BF9\u8BE5\u6761\u76EE\u7684\u8865\u5145\u8BF4\u660E\u3002",
|
|
469
|
+
required: false
|
|
470
|
+
}
|
|
471
|
+
];
|
|
472
|
+
var normalizeColumns = (inputColumns) => {
|
|
473
|
+
const base = Array.isArray(inputColumns) ? inputColumns : [];
|
|
474
|
+
const normalized = base.map((c) => {
|
|
475
|
+
if (!c || typeof c.name !== "string") return null;
|
|
476
|
+
const name = c.name.trim();
|
|
477
|
+
if (!name) return null;
|
|
478
|
+
const col = {
|
|
479
|
+
id: asOptionalTrimmedString(c.id) ?? ulid2(),
|
|
480
|
+
name
|
|
481
|
+
};
|
|
482
|
+
const label = asOptionalTrimmedString(c.label);
|
|
483
|
+
if (label) col.label = label;
|
|
484
|
+
if (typeof c.type === "string") {
|
|
485
|
+
col.type = c.type;
|
|
486
|
+
}
|
|
487
|
+
const description = asOptionalTrimmedString(c.description);
|
|
488
|
+
if (description) col.description = description;
|
|
489
|
+
if (typeof c.isPrimary === "boolean") {
|
|
490
|
+
col.isPrimary = c.isPrimary;
|
|
491
|
+
}
|
|
492
|
+
if (typeof c.required === "boolean") {
|
|
493
|
+
col.required = c.required;
|
|
494
|
+
}
|
|
495
|
+
if (Array.isArray(c.options)) {
|
|
496
|
+
const opts = asTrimmedNonEmptyStringArray(c.options);
|
|
497
|
+
if (opts.length) {
|
|
498
|
+
col.options = opts;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return col;
|
|
502
|
+
}).filter(Boolean);
|
|
503
|
+
if (normalized.length > 0) {
|
|
504
|
+
if (!normalized.some((c) => c.isPrimary)) {
|
|
505
|
+
normalized[0].isPrimary = true;
|
|
506
|
+
}
|
|
507
|
+
return normalized;
|
|
508
|
+
}
|
|
509
|
+
return DEFAULT_COLUMNS.map((c) => ({
|
|
510
|
+
...c,
|
|
511
|
+
id: ulid2()
|
|
512
|
+
}));
|
|
513
|
+
};
|
|
514
|
+
var normalizeStringArray = (value) => {
|
|
515
|
+
if (!Array.isArray(value)) return void 0;
|
|
516
|
+
const items = [...new Set(asTrimmedNonEmptyStringArray(value))];
|
|
517
|
+
return items.length ? items : void 0;
|
|
518
|
+
};
|
|
519
|
+
var normalizePublicIntake = (input) => {
|
|
520
|
+
if (!isRecord(input)) return void 0;
|
|
521
|
+
const allowedFields = normalizeStringArray(input.allowedFields) ?? [];
|
|
522
|
+
if (input.enabled !== true) {
|
|
523
|
+
return { enabled: false, allowedFields };
|
|
524
|
+
}
|
|
525
|
+
if (allowedFields.length === 0) {
|
|
526
|
+
throw new Error("publicIntake.allowedFields \u81F3\u5C11\u9700\u8981\u4E00\u4E2A\u5B57\u6BB5\u3002");
|
|
527
|
+
}
|
|
528
|
+
const slug = asOptionalTrimmedString(input.slug);
|
|
529
|
+
const honeypotField = asOptionalTrimmedString(input.honeypotField);
|
|
530
|
+
return {
|
|
531
|
+
enabled: true,
|
|
532
|
+
...slug ? { slug } : {},
|
|
533
|
+
...normalizeStringArray(input.appIds) ? { appIds: normalizeStringArray(input.appIds) } : {},
|
|
534
|
+
allowedFields,
|
|
535
|
+
...normalizeStringArray(input.requiredFields) ? { requiredFields: normalizeStringArray(input.requiredFields) } : {},
|
|
536
|
+
...honeypotField ? { honeypotField } : {}
|
|
537
|
+
};
|
|
538
|
+
};
|
|
539
|
+
var createTableAction = async ({
|
|
540
|
+
spaceId: customSpaceId,
|
|
541
|
+
title: customTitle,
|
|
542
|
+
purpose: customPurpose,
|
|
543
|
+
description: customDescription,
|
|
544
|
+
tags: customTags,
|
|
545
|
+
publicIntake,
|
|
546
|
+
categoryId,
|
|
547
|
+
columns,
|
|
548
|
+
withDefaultRows = true
|
|
549
|
+
} = {}, { dispatch, getState }) => {
|
|
550
|
+
const state3 = getState();
|
|
551
|
+
const userId = selectIdentityUserId(state3);
|
|
552
|
+
if (!userId) throw new Error("User ID not found.");
|
|
553
|
+
const spaceId = customSpaceId ?? getCurrentSpaceId();
|
|
554
|
+
const tableId = ulid2();
|
|
555
|
+
const now = /* @__PURE__ */ new Date();
|
|
556
|
+
const nowIso = formatISO(now);
|
|
557
|
+
const dbKey = metaKey(userId, tableId);
|
|
558
|
+
const defaultTitle = runtime_default.t("space:newTable", {
|
|
559
|
+
defaultValue: "\u65B0\u5EFA\u8868\u683C"
|
|
560
|
+
});
|
|
561
|
+
const title = asOptionalTrimmedString(customTitle) ?? defaultTitle;
|
|
562
|
+
const purpose = asOptionalTrimmedString(customPurpose);
|
|
563
|
+
const description = asOptionalTrimmedString(customDescription);
|
|
564
|
+
const tags = Array.isArray(customTags) && customTags.length ? asTrimmedNonEmptyStringArray(customTags) : void 0;
|
|
565
|
+
const finalPublicIntake = normalizePublicIntake(publicIntake);
|
|
566
|
+
const finalColumns = normalizeColumns(columns);
|
|
567
|
+
const tableMeta = {
|
|
568
|
+
dbKey,
|
|
569
|
+
tenantId: userId,
|
|
570
|
+
tableId,
|
|
571
|
+
spaceId: spaceId ?? null,
|
|
572
|
+
displayName: title,
|
|
573
|
+
purpose,
|
|
574
|
+
description,
|
|
575
|
+
tags,
|
|
576
|
+
schemaVersion: 1,
|
|
577
|
+
columns: finalColumns,
|
|
578
|
+
views: [],
|
|
579
|
+
// 后续可以在 UI 中新增视图
|
|
580
|
+
triggers: [],
|
|
581
|
+
// 后续可以在 UI/配置中新增触发器
|
|
582
|
+
aiConfig: void 0,
|
|
583
|
+
...finalPublicIntake ? { publicIntake: finalPublicIntake } : {},
|
|
584
|
+
createdAt: nowIso,
|
|
585
|
+
updatedAt: nowIso,
|
|
586
|
+
type: "table" /* TABLE */
|
|
587
|
+
};
|
|
588
|
+
await dispatch(
|
|
589
|
+
write({
|
|
590
|
+
data: tableMeta,
|
|
591
|
+
customKey: dbKey
|
|
592
|
+
})
|
|
593
|
+
).unwrap();
|
|
594
|
+
if (withDefaultRows) {
|
|
595
|
+
const defaultRows = [
|
|
596
|
+
{
|
|
597
|
+
title: "\u793A\u4F8B\u4E00",
|
|
598
|
+
note: "\u4F60\u53EF\u4EE5\u5728\u8FD9\u91CC\u8BB0\u5F55\u4EFB\u4F55\u5185\u5BB9\uFF0C\u4F8B\u5982\u4EFB\u52A1\u3001\u60F3\u6CD5\u6216\u914D\u7F6E\u9879\u3002"
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
title: "\u793A\u4F8B\u4E8C",
|
|
602
|
+
note: "\u53CC\u51FB\u5355\u5143\u683C\u5F00\u59CB\u7F16\u8F91\uFF0C\u53F3\u4E0A\u89D2\u53EF\u4EE5\u6DFB\u52A0\u5B57\u6BB5\u548C\u884C\u3002"
|
|
603
|
+
}
|
|
604
|
+
];
|
|
605
|
+
await Promise.all(
|
|
606
|
+
defaultRows.map(async (values) => {
|
|
607
|
+
const { dbKey: rowKeyStr, rowId } = rowKey.create(userId, tableId);
|
|
608
|
+
const row = {
|
|
609
|
+
dbKey: rowKeyStr,
|
|
610
|
+
tenantId: userId,
|
|
611
|
+
tableId,
|
|
612
|
+
rowId,
|
|
613
|
+
createdAt: nowIso,
|
|
614
|
+
updatedAt: nowIso,
|
|
615
|
+
type: "table_row" /* TABLE_ROW */,
|
|
616
|
+
...values
|
|
617
|
+
};
|
|
618
|
+
await dispatch(
|
|
619
|
+
write({
|
|
620
|
+
data: row,
|
|
621
|
+
customKey: rowKeyStr
|
|
622
|
+
})
|
|
623
|
+
).unwrap();
|
|
624
|
+
})
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
if (spaceId) {
|
|
628
|
+
await dispatch(
|
|
629
|
+
addContentToSpace({
|
|
630
|
+
spaceId,
|
|
631
|
+
contentKey: dbKey,
|
|
632
|
+
type: "table" /* TABLE */,
|
|
633
|
+
title,
|
|
634
|
+
categoryId
|
|
635
|
+
})
|
|
636
|
+
).unwrap();
|
|
637
|
+
}
|
|
638
|
+
if (typeof window !== "undefined") {
|
|
639
|
+
window.dispatchEvent(new Event("nolo-user-data-updated"));
|
|
640
|
+
}
|
|
641
|
+
return dbKey;
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
// packages/render/table/deleteTableAction.ts
|
|
645
|
+
var parseMetaKey = (dbKey) => {
|
|
646
|
+
const parts = dbKey.split(SEPARATOR);
|
|
647
|
+
if (parts[0] !== "meta" || parts.length < 3) {
|
|
648
|
+
throw new Error(`\u975E\u6CD5\u8868 key\uFF1A${dbKey}`);
|
|
649
|
+
}
|
|
650
|
+
const tenantId = parts[1];
|
|
651
|
+
const tableId = parts.slice(2).join(SEPARATOR);
|
|
652
|
+
return { tenantId, tableId };
|
|
653
|
+
};
|
|
654
|
+
var collectEntriesByPrefix = async (db, prefix) => {
|
|
655
|
+
const entries = [];
|
|
656
|
+
for await (const [key, value] of db.iterator({
|
|
657
|
+
gte: prefix,
|
|
658
|
+
lte: prefix + "\uFFFF"
|
|
659
|
+
})) {
|
|
660
|
+
entries.push([key, value]);
|
|
661
|
+
}
|
|
662
|
+
return entries;
|
|
663
|
+
};
|
|
664
|
+
var belongsToTable = (value, tenantId, tableId) => Boolean(
|
|
665
|
+
value && typeof value === "object" && value.tenantId === tenantId && value.tableId === tableId
|
|
666
|
+
);
|
|
667
|
+
var indexValueReferencesRows = (value, rowDbKeys, rowIds) => {
|
|
668
|
+
if (typeof value === "string") {
|
|
669
|
+
return rowDbKeys.has(value) || rowIds.has(value);
|
|
670
|
+
}
|
|
671
|
+
if (!value || typeof value !== "object") return false;
|
|
672
|
+
const candidate = value;
|
|
673
|
+
return typeof candidate.dbKey === "string" && rowDbKeys.has(candidate.dbKey) || typeof candidate.rowDbKey === "string" && rowDbKeys.has(candidate.rowDbKey) || typeof candidate.rowId === "string" && rowIds.has(candidate.rowId);
|
|
674
|
+
};
|
|
675
|
+
var deleteTableAction = async ({ dbKey }, {
|
|
676
|
+
dispatch,
|
|
677
|
+
getState,
|
|
678
|
+
extra
|
|
679
|
+
}) => {
|
|
680
|
+
const { db } = extra;
|
|
681
|
+
const state3 = getState();
|
|
682
|
+
const { currentServer, syncServers } = getRuntimeServerContext(state3);
|
|
683
|
+
const { tenantId, tableId } = parseMetaKey(dbKey);
|
|
684
|
+
const nowIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
685
|
+
const rowPrefix = createKey("row", tenantId, tableId, "");
|
|
686
|
+
const rowEntries = (await collectEntriesByPrefix(db, rowPrefix)).filter(
|
|
687
|
+
([, value]) => belongsToTable(value, tenantId, tableId)
|
|
688
|
+
);
|
|
689
|
+
const rowDbKeys = new Set(rowEntries.map(([key]) => key));
|
|
690
|
+
const rowIds = new Set(
|
|
691
|
+
rowEntries.map(([, value]) => value.rowId).filter((rowId) => typeof rowId === "string")
|
|
692
|
+
);
|
|
693
|
+
const idxPrefix = createKey("idx", tenantId, tableId, "");
|
|
694
|
+
const idxEntries = await collectEntriesByPrefix(db, idxPrefix);
|
|
695
|
+
const idxKeys = idxEntries.filter(
|
|
696
|
+
([, value]) => belongsToTable(value, tenantId, tableId) || indexValueReferencesRows(value, rowDbKeys, rowIds)
|
|
697
|
+
).map(([key]) => key);
|
|
698
|
+
const viewPrefix = createKey("view", tenantId, tableId, "");
|
|
699
|
+
const viewEntries = await collectEntriesByPrefix(db, viewPrefix);
|
|
700
|
+
const viewKeys = viewEntries.filter(([, value]) => belongsToTable(value, tenantId, tableId)).map(([key]) => key);
|
|
701
|
+
const metaRecord = typeof db.get === "function" ? await db.get(dbKey).catch(() => null) : null;
|
|
702
|
+
const keysToDelete = Array.from(
|
|
703
|
+
/* @__PURE__ */ new Set([
|
|
704
|
+
...idxKeys,
|
|
705
|
+
...viewKeys
|
|
706
|
+
])
|
|
707
|
+
);
|
|
708
|
+
const putOps = [
|
|
709
|
+
...metaRecord && typeof metaRecord === "object" ? [
|
|
710
|
+
{
|
|
711
|
+
type: "put",
|
|
712
|
+
key: dbKey,
|
|
713
|
+
value: buildTombstoneRecord(metaRecord, nowIso)
|
|
714
|
+
}
|
|
715
|
+
] : [],
|
|
716
|
+
...rowEntries.map(([key, value]) => ({
|
|
717
|
+
type: "put",
|
|
718
|
+
key,
|
|
719
|
+
value: buildTombstoneRecord(value, nowIso)
|
|
720
|
+
}))
|
|
721
|
+
];
|
|
722
|
+
if (keysToDelete.length > 0 || putOps.length > 0) {
|
|
723
|
+
const ops = [
|
|
724
|
+
...putOps,
|
|
725
|
+
...keysToDelete.map((key) => ({
|
|
726
|
+
type: "del",
|
|
727
|
+
key
|
|
728
|
+
}))
|
|
729
|
+
];
|
|
730
|
+
await db.batch(ops);
|
|
731
|
+
}
|
|
732
|
+
scheduleDeleteReplication({
|
|
733
|
+
currentServer,
|
|
734
|
+
syncServers,
|
|
735
|
+
dbKey,
|
|
736
|
+
deleteOptions: { type: "table" },
|
|
737
|
+
state: state3
|
|
738
|
+
});
|
|
739
|
+
return dbKey;
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
// packages/render/table/tableStore.ts
|
|
743
|
+
async function safeDispatch(dispatch, action) {
|
|
744
|
+
if (!dispatch) return void 0;
|
|
745
|
+
const res = await dispatch(action);
|
|
746
|
+
if (res && typeof res.unwrap === "function") {
|
|
747
|
+
return await res.unwrap();
|
|
748
|
+
}
|
|
749
|
+
return res?.payload ?? res;
|
|
750
|
+
}
|
|
751
|
+
var createInitialState = () => ({
|
|
752
|
+
currentTable: null,
|
|
753
|
+
expectedTableKey: null,
|
|
754
|
+
isLoading: false,
|
|
755
|
+
isInitialized: false,
|
|
756
|
+
error: null,
|
|
757
|
+
rows: [],
|
|
758
|
+
focusContext: null
|
|
759
|
+
});
|
|
760
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
761
|
+
var version = 0;
|
|
762
|
+
var state = createInitialState();
|
|
763
|
+
var notify = () => {
|
|
764
|
+
for (const listener of listeners) {
|
|
765
|
+
try {
|
|
766
|
+
listener();
|
|
767
|
+
} catch {
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
var bump = () => {
|
|
772
|
+
version += 1;
|
|
773
|
+
notify();
|
|
774
|
+
};
|
|
775
|
+
var setState = (updater) => {
|
|
776
|
+
state = typeof updater === "function" ? updater(state) : updater;
|
|
777
|
+
bump();
|
|
778
|
+
};
|
|
779
|
+
function getTableState() {
|
|
780
|
+
return state;
|
|
781
|
+
}
|
|
782
|
+
function createTableCommand(name, fn) {
|
|
783
|
+
const command = (args) => {
|
|
784
|
+
return async (dispatch, getState, extra) => {
|
|
785
|
+
try {
|
|
786
|
+
const payload = await fn(args, { dispatch, getState, extra });
|
|
787
|
+
return {
|
|
788
|
+
type: `${name}/fulfilled`,
|
|
789
|
+
payload,
|
|
790
|
+
meta: { arg: args },
|
|
791
|
+
unwrap: async () => payload
|
|
792
|
+
};
|
|
793
|
+
} catch (err2) {
|
|
794
|
+
const message = err2?.message || String(err2);
|
|
795
|
+
return {
|
|
796
|
+
type: `${name}/rejected`,
|
|
797
|
+
payload: message,
|
|
798
|
+
error: { message },
|
|
799
|
+
unwrap: async () => {
|
|
800
|
+
throw err2 instanceof Error ? err2 : new Error(message);
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
};
|
|
806
|
+
command.fulfilled = {
|
|
807
|
+
type: `${name}/fulfilled`,
|
|
808
|
+
match: (action) => action?.type === `${name}/fulfilled`
|
|
809
|
+
};
|
|
810
|
+
command.rejected = {
|
|
811
|
+
type: `${name}/rejected`,
|
|
812
|
+
match: (action) => action?.type === `${name}/rejected`
|
|
813
|
+
};
|
|
814
|
+
command.pending = {
|
|
815
|
+
type: `${name}/pending`,
|
|
816
|
+
match: (action) => action?.type === `${name}/pending`
|
|
817
|
+
};
|
|
818
|
+
command.run = (args, context = {}) => fn(args, context);
|
|
819
|
+
return command;
|
|
820
|
+
}
|
|
821
|
+
var initTable = createTableCommand(
|
|
822
|
+
"table/initTable",
|
|
823
|
+
async (args, { dispatch } = {}) => {
|
|
824
|
+
const { tenantId, tableId } = args;
|
|
825
|
+
const dbKey = metaKey(tenantId, tableId);
|
|
826
|
+
setState((s) => ({
|
|
827
|
+
...s,
|
|
828
|
+
isLoading: true,
|
|
829
|
+
error: null,
|
|
830
|
+
isInitialized: false,
|
|
831
|
+
currentTable: null,
|
|
832
|
+
// 从第一行起就声明「预期表」:读取期间 currentTable 为 null,
|
|
833
|
+
// 但只有本表的 loadTableRows 结果有权写入(见 isCurrentTableTarget)。
|
|
834
|
+
expectedTableKey: { tenantId, tableId },
|
|
835
|
+
rows: []
|
|
836
|
+
}));
|
|
837
|
+
try {
|
|
838
|
+
if (!dispatch) {
|
|
839
|
+
throw new Error("dispatch is required to initialize table");
|
|
840
|
+
}
|
|
841
|
+
const readAction = await dispatch(readAndWait(dbKey));
|
|
842
|
+
const isMatch = typeof readAndWait?.fulfilled?.match === "function" && readAndWait.fulfilled.match(readAction);
|
|
843
|
+
const meta = isMatch && readAction.payload ? readAction.payload : readAction?.payload || readAction;
|
|
844
|
+
if (meta && typeof meta === "object" && ("columns" in meta || "displayName" in meta || "tableId" in meta)) {
|
|
845
|
+
setState((s) => {
|
|
846
|
+
if (!isCurrentTableTarget(s, tenantId, tableId)) return s;
|
|
847
|
+
return {
|
|
848
|
+
...s,
|
|
849
|
+
isLoading: false,
|
|
850
|
+
isInitialized: true,
|
|
851
|
+
currentTable: meta,
|
|
852
|
+
error: null
|
|
853
|
+
};
|
|
854
|
+
});
|
|
855
|
+
return meta;
|
|
856
|
+
}
|
|
857
|
+
const msg = readAction.payload?.message || `\u65E0\u6CD5\u52A0\u8F7D\u8868 ${tableId}`;
|
|
858
|
+
setState((s) => {
|
|
859
|
+
if (!isCurrentTableTarget(s, tenantId, tableId)) return s;
|
|
860
|
+
return {
|
|
861
|
+
...s,
|
|
862
|
+
isLoading: false,
|
|
863
|
+
isInitialized: true,
|
|
864
|
+
currentTable: null,
|
|
865
|
+
error: msg,
|
|
866
|
+
rows: []
|
|
867
|
+
};
|
|
868
|
+
});
|
|
869
|
+
throw new Error(msg);
|
|
870
|
+
} catch (e) {
|
|
871
|
+
const msg = e?.message || `\u521D\u59CB\u5316\u8868 ${tableId} \u65F6\u51FA\u9519`;
|
|
872
|
+
setState((s) => {
|
|
873
|
+
if (!isCurrentTableTarget(s, tenantId, tableId)) return s;
|
|
874
|
+
return {
|
|
875
|
+
...s,
|
|
876
|
+
isLoading: false,
|
|
877
|
+
isInitialized: true,
|
|
878
|
+
currentTable: null,
|
|
879
|
+
error: msg,
|
|
880
|
+
rows: []
|
|
881
|
+
};
|
|
882
|
+
});
|
|
883
|
+
throw e;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
);
|
|
887
|
+
var sameTableTarget = (target, tenantId, tableId) => !!target && target.tenantId === tenantId && target.tableId === tableId;
|
|
888
|
+
var isCurrentTableTarget = (s, tenantId, tableId) => {
|
|
889
|
+
if (s.expectedTableKey) {
|
|
890
|
+
return sameTableTarget(s.expectedTableKey, tenantId, tableId);
|
|
891
|
+
}
|
|
892
|
+
if (s.currentTable) {
|
|
893
|
+
return sameTableTarget(s.currentTable, tenantId, tableId);
|
|
894
|
+
}
|
|
895
|
+
return true;
|
|
896
|
+
};
|
|
897
|
+
var loadTableRows = createTableCommand(
|
|
898
|
+
"table/loadTableRows",
|
|
899
|
+
async (args, { getState, extra } = {}) => {
|
|
900
|
+
const { tenantId, tableId } = args;
|
|
901
|
+
const db = extra?.db;
|
|
902
|
+
setState((s) => {
|
|
903
|
+
if (isCurrentTableTarget(s, tenantId, tableId)) {
|
|
904
|
+
return { ...s, error: null };
|
|
905
|
+
}
|
|
906
|
+
return s;
|
|
907
|
+
});
|
|
908
|
+
try {
|
|
909
|
+
const stateObj = getState ? getState() : {};
|
|
910
|
+
const { currentToken: token, remoteServers } = getRuntimeServerContext(stateObj);
|
|
911
|
+
const rows = await fetchAndCacheTableRows({
|
|
912
|
+
db,
|
|
913
|
+
tenantId,
|
|
914
|
+
tableId,
|
|
915
|
+
token,
|
|
916
|
+
remoteServers
|
|
917
|
+
});
|
|
918
|
+
setState((s) => {
|
|
919
|
+
if (isCurrentTableTarget(s, tenantId, tableId)) {
|
|
920
|
+
return { ...s, rows };
|
|
921
|
+
}
|
|
922
|
+
return s;
|
|
923
|
+
});
|
|
924
|
+
return rows;
|
|
925
|
+
} catch (e) {
|
|
926
|
+
const msg = e?.message || "\u52A0\u8F7D\u8868\u884C\u5931\u8D25";
|
|
927
|
+
try {
|
|
928
|
+
const stateObj = getState ? getState() : {};
|
|
929
|
+
const { remoteServers } = getRuntimeServerContext(stateObj);
|
|
930
|
+
logger.warn(
|
|
931
|
+
{
|
|
932
|
+
tenantId,
|
|
933
|
+
tableId,
|
|
934
|
+
remoteServers: remoteServers?.length ?? 0,
|
|
935
|
+
err: msg
|
|
936
|
+
},
|
|
937
|
+
"loadTableRows failed"
|
|
938
|
+
);
|
|
939
|
+
} catch {
|
|
940
|
+
}
|
|
941
|
+
setState((s) => {
|
|
942
|
+
if (isCurrentTableTarget(s, tenantId, tableId)) {
|
|
943
|
+
return { ...s, error: msg, rows: [] };
|
|
944
|
+
}
|
|
945
|
+
return s;
|
|
946
|
+
});
|
|
947
|
+
throw e;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
);
|
|
951
|
+
var addRow = createTableCommand(
|
|
952
|
+
"table/addRow",
|
|
953
|
+
async (args, { dispatch } = {}) => {
|
|
954
|
+
const { tenantId, tableId, values } = args;
|
|
955
|
+
setState((s) => {
|
|
956
|
+
if (s.currentTable && s.currentTable.tenantId === tenantId && s.currentTable.tableId === tableId) {
|
|
957
|
+
return { ...s, error: null };
|
|
958
|
+
}
|
|
959
|
+
return s;
|
|
960
|
+
});
|
|
961
|
+
try {
|
|
962
|
+
const { dbKey, rowId } = rowKey.create(tenantId, tableId);
|
|
963
|
+
const nowIso = formatISO(/* @__PURE__ */ new Date());
|
|
964
|
+
const row = {
|
|
965
|
+
dbKey,
|
|
966
|
+
tenantId,
|
|
967
|
+
tableId,
|
|
968
|
+
rowId,
|
|
969
|
+
createdAt: nowIso,
|
|
970
|
+
updatedAt: nowIso,
|
|
971
|
+
type: "table_row" /* TABLE_ROW */,
|
|
972
|
+
...values
|
|
973
|
+
};
|
|
974
|
+
if (dispatch) {
|
|
975
|
+
await safeDispatch(
|
|
976
|
+
dispatch,
|
|
977
|
+
write({
|
|
978
|
+
data: row,
|
|
979
|
+
customKey: dbKey
|
|
980
|
+
})
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
setState((s) => {
|
|
984
|
+
const meta = s.currentTable;
|
|
985
|
+
if (meta && row?.tenantId === meta.tenantId && row?.tableId === meta.tableId) {
|
|
986
|
+
return { ...s, rows: [...s.rows, row] };
|
|
987
|
+
}
|
|
988
|
+
return s;
|
|
989
|
+
});
|
|
990
|
+
return row;
|
|
991
|
+
} catch (e) {
|
|
992
|
+
const msg = e?.message || "\u65B0\u589E\u8868\u884C\u5931\u8D25";
|
|
993
|
+
setState((s) => {
|
|
994
|
+
if (s.currentTable && s.currentTable.tenantId === tenantId && s.currentTable.tableId === tableId) {
|
|
995
|
+
return { ...s, error: msg };
|
|
996
|
+
}
|
|
997
|
+
return s;
|
|
998
|
+
});
|
|
999
|
+
throw e;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
);
|
|
1003
|
+
var deleteRow = createTableCommand(
|
|
1004
|
+
"table/deleteRow",
|
|
1005
|
+
async (dbKey, { dispatch, getState, extra } = {}) => {
|
|
1006
|
+
try {
|
|
1007
|
+
let row = state.rows.find((item) => item?.dbKey === dbKey);
|
|
1008
|
+
if (!row && extra?.db && typeof extra.db.get === "function") {
|
|
1009
|
+
try {
|
|
1010
|
+
row = await extra.db.get(dbKey);
|
|
1011
|
+
} catch {
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
if (!row && dispatch) {
|
|
1015
|
+
try {
|
|
1016
|
+
const readAction = await safeDispatch(dispatch, readAndWait(dbKey));
|
|
1017
|
+
row = readAction?.payload || readAction;
|
|
1018
|
+
} catch {
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
if (!row) {
|
|
1022
|
+
throw new Error(`\u5F53\u524D\u8868\u4E2D\u627E\u4E0D\u5230\u8981\u5220\u9664\u7684\u884C\uFF1A${dbKey}`);
|
|
1023
|
+
}
|
|
1024
|
+
const nowIso = formatISO(/* @__PURE__ */ new Date());
|
|
1025
|
+
const tombstoneRow = {
|
|
1026
|
+
...row,
|
|
1027
|
+
deletedAt: nowIso,
|
|
1028
|
+
updatedAt: nowIso,
|
|
1029
|
+
type: "table_row" /* TABLE_ROW */
|
|
1030
|
+
};
|
|
1031
|
+
if (extra?.db && typeof extra.db.put === "function") {
|
|
1032
|
+
await extra.db.put(dbKey, tombstoneRow);
|
|
1033
|
+
}
|
|
1034
|
+
if (dispatch) {
|
|
1035
|
+
dispatch(upsertSSREntity(tombstoneRow));
|
|
1036
|
+
}
|
|
1037
|
+
const stateObj = getState ? getState() : {};
|
|
1038
|
+
const { currentServer, syncServers } = getRuntimeServerContext(stateObj);
|
|
1039
|
+
const servers = resolveReplicationServers(currentServer, syncServers);
|
|
1040
|
+
scheduleWriteReplication(
|
|
1041
|
+
servers,
|
|
1042
|
+
{
|
|
1043
|
+
data: tombstoneRow,
|
|
1044
|
+
customKey: dbKey
|
|
1045
|
+
},
|
|
1046
|
+
stateObj
|
|
1047
|
+
);
|
|
1048
|
+
setState((s) => ({
|
|
1049
|
+
...s,
|
|
1050
|
+
rows: s.rows.filter((r) => r.dbKey !== dbKey)
|
|
1051
|
+
}));
|
|
1052
|
+
return dbKey;
|
|
1053
|
+
} catch (e) {
|
|
1054
|
+
const msg = e?.message || "\u5220\u9664\u8868\u884C\u5931\u8D25";
|
|
1055
|
+
setState((s) => ({ ...s, error: msg }));
|
|
1056
|
+
throw e;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
);
|
|
1060
|
+
var createTable = createTableCommand(
|
|
1061
|
+
"table/createTable",
|
|
1062
|
+
async (args, context = {}) => {
|
|
1063
|
+
return await createTableAction(args, context);
|
|
1064
|
+
}
|
|
1065
|
+
);
|
|
1066
|
+
var deleteTable = createTableCommand(
|
|
1067
|
+
"table/deleteTable",
|
|
1068
|
+
async (args, context = {}) => {
|
|
1069
|
+
setState((s) => ({ ...s, error: null }));
|
|
1070
|
+
try {
|
|
1071
|
+
const deletedKey = await deleteTableAction(args, context);
|
|
1072
|
+
setState((s) => {
|
|
1073
|
+
if (s.currentTable?.dbKey === deletedKey) {
|
|
1074
|
+
return {
|
|
1075
|
+
...s,
|
|
1076
|
+
currentTable: null,
|
|
1077
|
+
rows: [],
|
|
1078
|
+
isInitialized: false
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
return s;
|
|
1082
|
+
});
|
|
1083
|
+
return deletedKey;
|
|
1084
|
+
} catch (e) {
|
|
1085
|
+
const msg = e?.message || "\u5220\u9664\u8868\u65F6\u53D1\u751F\u672A\u77E5\u9519\u8BEF";
|
|
1086
|
+
setState((s) => ({ ...s, error: msg }));
|
|
1087
|
+
throw e;
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
);
|
|
1091
|
+
var addColumn = createTableCommand(
|
|
1092
|
+
"table/addColumn",
|
|
1093
|
+
async (args, { dispatch } = {}) => {
|
|
1094
|
+
const { tenantId, tableId, columnName } = args;
|
|
1095
|
+
const meta = state.currentTable;
|
|
1096
|
+
if (!meta || meta.tenantId !== tenantId || meta.tableId !== tableId) {
|
|
1097
|
+
throw new Error("\u5F53\u524D\u6CA1\u6709\u52A0\u8F7D\u5BF9\u5E94\u7684\u8868\u5B9A\u4E49");
|
|
1098
|
+
}
|
|
1099
|
+
const result = addColumnToMeta(
|
|
1100
|
+
meta,
|
|
1101
|
+
{ columnName },
|
|
1102
|
+
{ id: ulid3(), nowIso: formatISO(/* @__PURE__ */ new Date()) }
|
|
1103
|
+
);
|
|
1104
|
+
if (!result.ok) {
|
|
1105
|
+
throw new Error(result.error);
|
|
1106
|
+
}
|
|
1107
|
+
setState((s) => ({ ...s, error: null }));
|
|
1108
|
+
try {
|
|
1109
|
+
if (dispatch) {
|
|
1110
|
+
await safeDispatch(
|
|
1111
|
+
dispatch,
|
|
1112
|
+
patch({
|
|
1113
|
+
dbKey: meta.dbKey,
|
|
1114
|
+
changes: result.value.metaChanges
|
|
1115
|
+
})
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
setState((s) => ({
|
|
1119
|
+
...s,
|
|
1120
|
+
currentTable: result.value.meta,
|
|
1121
|
+
isInitialized: true
|
|
1122
|
+
}));
|
|
1123
|
+
return result.value.meta;
|
|
1124
|
+
} catch (e) {
|
|
1125
|
+
const msg = e?.message || "\u6DFB\u52A0\u5B57\u6BB5\u5931\u8D25";
|
|
1126
|
+
setState((s) => ({ ...s, error: msg }));
|
|
1127
|
+
throw e;
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
);
|
|
1131
|
+
var deleteColumn = createTableCommand(
|
|
1132
|
+
"table/deleteColumn",
|
|
1133
|
+
async (args, { dispatch } = {}) => {
|
|
1134
|
+
const { tenantId, tableId, columnName } = args;
|
|
1135
|
+
const meta = state.currentTable;
|
|
1136
|
+
if (!meta || meta.tenantId !== tenantId || meta.tableId !== tableId) {
|
|
1137
|
+
throw new Error("\u5F53\u524D\u6CA1\u6709\u52A0\u8F7D\u5BF9\u5E94\u7684\u8868\u5B9A\u4E49");
|
|
1138
|
+
}
|
|
1139
|
+
const result = deleteColumnFromMeta(
|
|
1140
|
+
meta,
|
|
1141
|
+
state.rows,
|
|
1142
|
+
{ columnName },
|
|
1143
|
+
{ nowIso: formatISO(/* @__PURE__ */ new Date()) }
|
|
1144
|
+
);
|
|
1145
|
+
if (!result.ok) {
|
|
1146
|
+
throw new Error(result.error);
|
|
1147
|
+
}
|
|
1148
|
+
const { meta: nextMeta, metaChanges, rows: newRows, rowPatches } = result.value;
|
|
1149
|
+
setState((s) => ({ ...s, error: null }));
|
|
1150
|
+
try {
|
|
1151
|
+
if (dispatch) {
|
|
1152
|
+
await Promise.all(
|
|
1153
|
+
rowPatches.map((p) => safeDispatch(dispatch, patch(p)))
|
|
1154
|
+
);
|
|
1155
|
+
await safeDispatch(
|
|
1156
|
+
dispatch,
|
|
1157
|
+
patch({ dbKey: meta.dbKey, changes: metaChanges })
|
|
1158
|
+
);
|
|
1159
|
+
}
|
|
1160
|
+
setState((s) => ({
|
|
1161
|
+
...s,
|
|
1162
|
+
currentTable: nextMeta,
|
|
1163
|
+
rows: newRows,
|
|
1164
|
+
isInitialized: true
|
|
1165
|
+
}));
|
|
1166
|
+
return { meta: nextMeta, rows: newRows };
|
|
1167
|
+
} catch (e) {
|
|
1168
|
+
const msg = e?.message || "\u5220\u9664\u5B57\u6BB5\u5931\u8D25";
|
|
1169
|
+
setState((s) => ({ ...s, error: msg }));
|
|
1170
|
+
throw e;
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
);
|
|
1174
|
+
var reorderColumn = createTableCommand(
|
|
1175
|
+
"table/reorderColumn",
|
|
1176
|
+
async (args, { dispatch } = {}) => {
|
|
1177
|
+
const { tenantId, tableId, fromIndex, toIndex } = args;
|
|
1178
|
+
const meta = state.currentTable;
|
|
1179
|
+
if (!meta || meta.tenantId !== tenantId || meta.tableId !== tableId) {
|
|
1180
|
+
throw new Error("\u5F53\u524D\u6CA1\u6709\u52A0\u8F7D\u5BF9\u5E94\u7684\u8868\u5B9A\u4E49");
|
|
1181
|
+
}
|
|
1182
|
+
const result = reorderColumnInMeta(
|
|
1183
|
+
meta,
|
|
1184
|
+
{ fromIndex, toIndex },
|
|
1185
|
+
{ nowIso: formatISO(/* @__PURE__ */ new Date()) }
|
|
1186
|
+
);
|
|
1187
|
+
if (!result.ok) {
|
|
1188
|
+
throw new Error(result.error);
|
|
1189
|
+
}
|
|
1190
|
+
if (result.value.noop) {
|
|
1191
|
+
return meta;
|
|
1192
|
+
}
|
|
1193
|
+
const { meta: nextMeta, metaChanges } = result.value;
|
|
1194
|
+
setState((s) => ({ ...s, error: null }));
|
|
1195
|
+
try {
|
|
1196
|
+
if (dispatch) {
|
|
1197
|
+
await safeDispatch(
|
|
1198
|
+
dispatch,
|
|
1199
|
+
patch({ dbKey: meta.dbKey, changes: metaChanges })
|
|
1200
|
+
);
|
|
1201
|
+
}
|
|
1202
|
+
setState((s) => ({
|
|
1203
|
+
...s,
|
|
1204
|
+
currentTable: nextMeta,
|
|
1205
|
+
isInitialized: true
|
|
1206
|
+
}));
|
|
1207
|
+
return nextMeta;
|
|
1208
|
+
} catch (e) {
|
|
1209
|
+
const msg = e?.message || "\u8C03\u6574\u5217\u987A\u5E8F\u5931\u8D25";
|
|
1210
|
+
setState((s) => ({ ...s, error: msg }));
|
|
1211
|
+
throw e;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
);
|
|
1215
|
+
var renameColumnLabel = createTableCommand(
|
|
1216
|
+
"table/renameColumnLabel",
|
|
1217
|
+
async (args, { dispatch } = {}) => {
|
|
1218
|
+
const { tenantId, tableId, columnId, newLabel } = args;
|
|
1219
|
+
const meta = state.currentTable;
|
|
1220
|
+
if (!meta || meta.tenantId !== tenantId || meta.tableId !== tableId) {
|
|
1221
|
+
throw new Error("\u5F53\u524D\u6CA1\u6709\u52A0\u8F7D\u5BF9\u5E94\u7684\u8868\u5B9A\u4E49");
|
|
1222
|
+
}
|
|
1223
|
+
const result = renameColumnLabelInMeta(
|
|
1224
|
+
meta,
|
|
1225
|
+
{ columnId, label: newLabel },
|
|
1226
|
+
{ nowIso: formatISO(/* @__PURE__ */ new Date()) }
|
|
1227
|
+
);
|
|
1228
|
+
if (!result.ok) {
|
|
1229
|
+
throw new Error(result.error);
|
|
1230
|
+
}
|
|
1231
|
+
const { meta: nextMeta, metaChanges } = result.value;
|
|
1232
|
+
setState((s) => ({ ...s, error: null }));
|
|
1233
|
+
try {
|
|
1234
|
+
if (dispatch) {
|
|
1235
|
+
await safeDispatch(
|
|
1236
|
+
dispatch,
|
|
1237
|
+
patch({ dbKey: meta.dbKey, changes: metaChanges })
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
setState((s) => ({
|
|
1241
|
+
...s,
|
|
1242
|
+
currentTable: nextMeta,
|
|
1243
|
+
isInitialized: true
|
|
1244
|
+
}));
|
|
1245
|
+
return nextMeta;
|
|
1246
|
+
} catch (e) {
|
|
1247
|
+
const msg = e?.message || "\u91CD\u547D\u540D\u5B57\u6BB5\u663E\u793A\u540D\u5931\u8D25";
|
|
1248
|
+
setState((s) => ({ ...s, error: msg }));
|
|
1249
|
+
throw e;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
);
|
|
1253
|
+
var updateColumnWidth = createTableCommand(
|
|
1254
|
+
"table/updateColumnWidth",
|
|
1255
|
+
async (args, { dispatch } = {}) => {
|
|
1256
|
+
const { tenantId, tableId, columnId, width } = args;
|
|
1257
|
+
const meta = state.currentTable;
|
|
1258
|
+
if (!meta || meta.tenantId !== tenantId || meta.tableId !== tableId) {
|
|
1259
|
+
throw new Error("\u5F53\u524D\u6CA1\u6709\u52A0\u8F7D\u5BF9\u5E94\u7684\u8868\u5B9A\u4E49");
|
|
1260
|
+
}
|
|
1261
|
+
const result = updateColumnWidthInMeta(
|
|
1262
|
+
meta,
|
|
1263
|
+
{ columnId, width },
|
|
1264
|
+
{ nowIso: formatISO(/* @__PURE__ */ new Date()) }
|
|
1265
|
+
);
|
|
1266
|
+
if (!result.ok) {
|
|
1267
|
+
throw new Error(result.error);
|
|
1268
|
+
}
|
|
1269
|
+
const { meta: nextMeta, metaChanges } = result.value;
|
|
1270
|
+
setState((s) => ({ ...s, error: null }));
|
|
1271
|
+
try {
|
|
1272
|
+
if (dispatch) {
|
|
1273
|
+
await safeDispatch(
|
|
1274
|
+
dispatch,
|
|
1275
|
+
patch({ dbKey: meta.dbKey, changes: metaChanges })
|
|
1276
|
+
);
|
|
1277
|
+
}
|
|
1278
|
+
setState((s) => ({
|
|
1279
|
+
...s,
|
|
1280
|
+
currentTable: nextMeta,
|
|
1281
|
+
isInitialized: true
|
|
1282
|
+
}));
|
|
1283
|
+
return nextMeta;
|
|
1284
|
+
} catch (e) {
|
|
1285
|
+
const msg = e?.message || "\u66F4\u65B0\u5B57\u6BB5\u5BBD\u5EA6\u5931\u8D25";
|
|
1286
|
+
setState((s) => ({ ...s, error: msg }));
|
|
1287
|
+
throw e;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
);
|
|
1291
|
+
var addColumnOption = createTableCommand(
|
|
1292
|
+
"table/addColumnOption",
|
|
1293
|
+
async (args, { dispatch } = {}) => {
|
|
1294
|
+
const { tenantId, tableId, columnId, option } = args;
|
|
1295
|
+
const meta = state.currentTable;
|
|
1296
|
+
if (!meta || meta.tenantId !== tenantId || meta.tableId !== tableId) {
|
|
1297
|
+
throw new Error("\u5F53\u524D\u6CA1\u6709\u52A0\u8F7D\u5BF9\u5E94\u7684\u8868\u5B9A\u4E49");
|
|
1298
|
+
}
|
|
1299
|
+
const result = addColumnOptionInMeta(
|
|
1300
|
+
meta,
|
|
1301
|
+
{ columnId, option },
|
|
1302
|
+
{ nowIso: formatISO(/* @__PURE__ */ new Date()) }
|
|
1303
|
+
);
|
|
1304
|
+
if (!result.ok) {
|
|
1305
|
+
throw new Error(result.error);
|
|
1306
|
+
}
|
|
1307
|
+
if (result.value.noop) {
|
|
1308
|
+
return meta;
|
|
1309
|
+
}
|
|
1310
|
+
const { meta: nextMeta, metaChanges } = result.value;
|
|
1311
|
+
setState((s) => ({ ...s, error: null }));
|
|
1312
|
+
try {
|
|
1313
|
+
if (dispatch) {
|
|
1314
|
+
await safeDispatch(
|
|
1315
|
+
dispatch,
|
|
1316
|
+
patch({ dbKey: meta.dbKey, changes: metaChanges })
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
setState((s) => ({
|
|
1320
|
+
...s,
|
|
1321
|
+
currentTable: nextMeta,
|
|
1322
|
+
isInitialized: true
|
|
1323
|
+
}));
|
|
1324
|
+
return nextMeta;
|
|
1325
|
+
} catch (e) {
|
|
1326
|
+
const msg = e?.message || "\u65B0\u589E\u9009\u9879\u5931\u8D25";
|
|
1327
|
+
setState((s) => ({ ...s, error: msg }));
|
|
1328
|
+
throw e;
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
);
|
|
1332
|
+
var renameTable = createTableCommand(
|
|
1333
|
+
"table/renameTable",
|
|
1334
|
+
async (args, { dispatch } = {}) => {
|
|
1335
|
+
const { tenantId, tableId, newName } = args;
|
|
1336
|
+
const trimmedName = newName.trim();
|
|
1337
|
+
if (!trimmedName) {
|
|
1338
|
+
throw new Error("\u8868\u540D\u4E0D\u80FD\u4E3A\u7A7A");
|
|
1339
|
+
}
|
|
1340
|
+
const meta = state.currentTable;
|
|
1341
|
+
if (!meta || meta.tenantId !== tenantId || meta.tableId !== tableId) {
|
|
1342
|
+
throw new Error("\u5F53\u524D\u6CA1\u6709\u52A0\u8F7D\u5BF9\u5E94\u7684\u8868\u5B9A\u4E49");
|
|
1343
|
+
}
|
|
1344
|
+
const nowIso = formatISO(/* @__PURE__ */ new Date());
|
|
1345
|
+
setState((s) => ({ ...s, error: null }));
|
|
1346
|
+
try {
|
|
1347
|
+
if (dispatch) {
|
|
1348
|
+
await safeDispatch(
|
|
1349
|
+
dispatch,
|
|
1350
|
+
patch({
|
|
1351
|
+
dbKey: meta.dbKey,
|
|
1352
|
+
changes: {
|
|
1353
|
+
displayName: trimmedName,
|
|
1354
|
+
updatedAt: nowIso
|
|
1355
|
+
}
|
|
1356
|
+
})
|
|
1357
|
+
);
|
|
1358
|
+
}
|
|
1359
|
+
const nextMeta = {
|
|
1360
|
+
...meta,
|
|
1361
|
+
displayName: trimmedName,
|
|
1362
|
+
updatedAt: nowIso
|
|
1363
|
+
};
|
|
1364
|
+
setState((s) => {
|
|
1365
|
+
if (!isCurrentTableTarget(s, tenantId, tableId) || !sameTableTarget(s.currentTable, tenantId, tableId)) return s;
|
|
1366
|
+
return {
|
|
1367
|
+
...s,
|
|
1368
|
+
currentTable: { ...s.currentTable, displayName: trimmedName, updatedAt: nowIso },
|
|
1369
|
+
isInitialized: true
|
|
1370
|
+
};
|
|
1371
|
+
});
|
|
1372
|
+
return nextMeta;
|
|
1373
|
+
} catch (e) {
|
|
1374
|
+
const msg = e?.message || "\u91CD\u547D\u540D\u8868\u5931\u8D25";
|
|
1375
|
+
setState((s) => sameTableTarget(s.expectedTableKey ?? s.currentTable, tenantId, tableId) ? { ...s, error: msg } : s);
|
|
1376
|
+
throw e;
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
);
|
|
1380
|
+
var updateTableIcon = createTableCommand(
|
|
1381
|
+
"table/updateTableIcon",
|
|
1382
|
+
async (args, { dispatch } = {}) => {
|
|
1383
|
+
const { tenantId, tableId, icon } = args;
|
|
1384
|
+
const meta = state.currentTable;
|
|
1385
|
+
if (!meta || meta.tenantId !== tenantId || meta.tableId !== tableId) {
|
|
1386
|
+
throw new Error("\u5F53\u524D\u6CA1\u6709\u52A0\u8F7D\u5BF9\u5E94\u7684\u8868\u5B9A\u4E49");
|
|
1387
|
+
}
|
|
1388
|
+
const nowIso = formatISO(/* @__PURE__ */ new Date());
|
|
1389
|
+
setState((s) => ({ ...s, error: null }));
|
|
1390
|
+
try {
|
|
1391
|
+
if (dispatch) {
|
|
1392
|
+
await safeDispatch(
|
|
1393
|
+
dispatch,
|
|
1394
|
+
patch({
|
|
1395
|
+
dbKey: meta.dbKey,
|
|
1396
|
+
changes: {
|
|
1397
|
+
icon: icon ?? null,
|
|
1398
|
+
updatedAt: nowIso
|
|
1399
|
+
}
|
|
1400
|
+
})
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1403
|
+
const nextMeta = {
|
|
1404
|
+
...meta,
|
|
1405
|
+
icon: icon ?? null,
|
|
1406
|
+
updatedAt: nowIso
|
|
1407
|
+
};
|
|
1408
|
+
setState((s) => {
|
|
1409
|
+
if (!isCurrentTableTarget(s, tenantId, tableId) || !sameTableTarget(s.currentTable, tenantId, tableId)) return s;
|
|
1410
|
+
return {
|
|
1411
|
+
...s,
|
|
1412
|
+
currentTable: { ...s.currentTable, icon: icon ?? null, updatedAt: nowIso }
|
|
1413
|
+
};
|
|
1414
|
+
});
|
|
1415
|
+
return nextMeta;
|
|
1416
|
+
} catch (e) {
|
|
1417
|
+
const msg = e?.message || "\u66F4\u65B0\u8868\u683C\u56FE\u6807\u5931\u8D25";
|
|
1418
|
+
setState((s) => sameTableTarget(s.expectedTableKey ?? s.currentTable, tenantId, tableId) ? { ...s, error: msg } : s);
|
|
1419
|
+
throw e;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
);
|
|
1423
|
+
var updateCell = createTableCommand(
|
|
1424
|
+
"table/updateCell",
|
|
1425
|
+
async (args, { dispatch } = {}) => {
|
|
1426
|
+
const { dbKey, columnName, value } = args;
|
|
1427
|
+
setState((s) => ({ ...s, error: null }));
|
|
1428
|
+
try {
|
|
1429
|
+
const nowIso = formatISO(/* @__PURE__ */ new Date());
|
|
1430
|
+
if (dispatch) {
|
|
1431
|
+
await safeDispatch(
|
|
1432
|
+
dispatch,
|
|
1433
|
+
patch({
|
|
1434
|
+
dbKey,
|
|
1435
|
+
changes: {
|
|
1436
|
+
[columnName]: value,
|
|
1437
|
+
updatedAt: nowIso
|
|
1438
|
+
}
|
|
1439
|
+
})
|
|
1440
|
+
);
|
|
1441
|
+
}
|
|
1442
|
+
setState((s) => ({
|
|
1443
|
+
...s,
|
|
1444
|
+
rows: s.rows.map((r) => {
|
|
1445
|
+
if (r.dbKey === dbKey) {
|
|
1446
|
+
return {
|
|
1447
|
+
...r,
|
|
1448
|
+
[columnName]: value,
|
|
1449
|
+
updatedAt: nowIso
|
|
1450
|
+
};
|
|
1451
|
+
}
|
|
1452
|
+
return r;
|
|
1453
|
+
})
|
|
1454
|
+
}));
|
|
1455
|
+
return { dbKey, columnName, value, updatedAt: nowIso };
|
|
1456
|
+
} catch (e) {
|
|
1457
|
+
const msg = e?.message || "\u66F4\u65B0\u5355\u5143\u683C\u5931\u8D25";
|
|
1458
|
+
setState((s) => ({ ...s, error: msg }));
|
|
1459
|
+
throw e;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
);
|
|
1463
|
+
|
|
1464
|
+
// packages/create/space/content/deleteContentFromSpaceAction.ts
|
|
1465
|
+
var nextSpaceUpdatedAt = (value) => {
|
|
1466
|
+
const previousTimestamp = typeof value === "number" ? value : typeof value === "string" ? Date.parse(value) || 0 : 0;
|
|
1467
|
+
const nextTimestamp = Math.max(Date.now(), previousTimestamp + 1);
|
|
1468
|
+
return typeof value === "string" ? new Date(nextTimestamp).toISOString() : nextTimestamp;
|
|
1469
|
+
};
|
|
1470
|
+
var findContentReference = (spaceData, requestedContentKey) => {
|
|
1471
|
+
const contents = spaceData.contents ?? {};
|
|
1472
|
+
const directMatch = contents[requestedContentKey];
|
|
1473
|
+
if (directMatch) {
|
|
1474
|
+
return { entryKey: requestedContentKey, contentInfo: directMatch };
|
|
1475
|
+
}
|
|
1476
|
+
for (const [entryKey, item] of Object.entries(contents)) {
|
|
1477
|
+
if (!item) continue;
|
|
1478
|
+
if (item.contentKey === requestedContentKey) {
|
|
1479
|
+
return { entryKey, contentInfo: item };
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
return null;
|
|
1483
|
+
};
|
|
1484
|
+
var ENTITY_DELETE_STRATEGIES = {
|
|
1485
|
+
dialog: async (key, { dispatch }) => {
|
|
1486
|
+
await dispatch(deleteDialog(key)).unwrap();
|
|
1487
|
+
},
|
|
1488
|
+
page: async (key, { dispatch, userId, sourceServerOrigin }) => {
|
|
1489
|
+
const isOwnerByKey = extractUserId(key) === userId;
|
|
1490
|
+
if (isOwnerByKey || isSystemAdmin(userId)) {
|
|
1491
|
+
await dispatch(remove({ dbKey: key, preferredServerOrigin: sourceServerOrigin }));
|
|
1492
|
+
}
|
|
1493
|
+
},
|
|
1494
|
+
table: async (key, { dispatch }) => {
|
|
1495
|
+
await dispatch(deleteTable({ dbKey: key })).unwrap();
|
|
1496
|
+
},
|
|
1497
|
+
file: async (key, { thunkAPI }) => {
|
|
1498
|
+
const { deleteFileAction } = await import("./deleteFile-KYWEMNFX.js");
|
|
1499
|
+
await deleteFileAction(key, thunkAPI);
|
|
1500
|
+
},
|
|
1501
|
+
image: async (key, { thunkAPI }) => {
|
|
1502
|
+
const { deleteFileAction } = await import("./deleteFile-KYWEMNFX.js");
|
|
1503
|
+
await deleteFileAction(key, thunkAPI);
|
|
1504
|
+
},
|
|
1505
|
+
agent: async (key, { dispatch, userId, sourceServerOrigin }) => {
|
|
1506
|
+
const parts = splitKey(key);
|
|
1507
|
+
const isPublic = parts[1] === "pub";
|
|
1508
|
+
const isOwnerByKey = parts[1] === userId;
|
|
1509
|
+
if (!isPublic) {
|
|
1510
|
+
if (isOwnerByKey || isSystemAdmin(userId)) {
|
|
1511
|
+
await dispatch(remove({ dbKey: key, preferredServerOrigin: sourceServerOrigin }));
|
|
1512
|
+
}
|
|
1513
|
+
} else {
|
|
1514
|
+
const agentData = await dispatch(read({
|
|
1515
|
+
dbKey: key,
|
|
1516
|
+
preferredServerOrigin: sourceServerOrigin
|
|
1517
|
+
})).unwrap();
|
|
1518
|
+
if (agentData) {
|
|
1519
|
+
const isCreator = agentData.userId === userId;
|
|
1520
|
+
const isAdmin = isSystemAdmin(userId);
|
|
1521
|
+
if (isCreator || isAdmin) {
|
|
1522
|
+
await dispatch(remove({ dbKey: key, preferredServerOrigin: sourceServerOrigin }));
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1528
|
+
var deleteContentFromSpaceAction = async (input, thunkAPI) => {
|
|
1529
|
+
const { contentKey, sourceServerOrigin } = input;
|
|
1530
|
+
const spaceId = normalizeSpaceId(input.spaceId);
|
|
1531
|
+
const { dispatch, getState } = thunkAPI;
|
|
1532
|
+
const accountUserId = selectIdentityUserId(getState());
|
|
1533
|
+
const spaceKey = createSpaceKey.space(spaceId);
|
|
1534
|
+
const spaceData = await dispatch(read({
|
|
1535
|
+
dbKey: spaceKey,
|
|
1536
|
+
preferredServerOrigin: sourceServerOrigin
|
|
1537
|
+
})).unwrap();
|
|
1538
|
+
if (!spaceData) throw new Error("\u7A7A\u95F4\u4E0D\u5B58\u5728");
|
|
1539
|
+
checkSpaceMembership(spaceData, accountUserId);
|
|
1540
|
+
const contentReference = findContentReference(spaceData, contentKey);
|
|
1541
|
+
if (!contentReference) {
|
|
1542
|
+
return { contentKey, spaceId, updatedSpaceData: spaceData };
|
|
1543
|
+
}
|
|
1544
|
+
const { entryKey, contentInfo } = contentReference;
|
|
1545
|
+
const entityKey = String(contentInfo.contentKey || contentKey || entryKey);
|
|
1546
|
+
const contentDeletes = {
|
|
1547
|
+
[entryKey]: null
|
|
1548
|
+
};
|
|
1549
|
+
if (entityKey !== entryKey) {
|
|
1550
|
+
contentDeletes[entityKey] = null;
|
|
1551
|
+
}
|
|
1552
|
+
const updatedSpaceData = await dispatch(
|
|
1553
|
+
patch({
|
|
1554
|
+
dbKey: spaceKey,
|
|
1555
|
+
preferredServerOrigin: sourceServerOrigin,
|
|
1556
|
+
changes: {
|
|
1557
|
+
contents: contentDeletes,
|
|
1558
|
+
updatedAt: nextSpaceUpdatedAt(spaceData.updatedAt),
|
|
1559
|
+
...localSpaceAuthorityPatchStamp(spaceData)
|
|
1560
|
+
}
|
|
1561
|
+
})
|
|
1562
|
+
).unwrap();
|
|
1563
|
+
const userId = isDeviceLocalSpaceBody(spaceData) ? DEVICE_LOCAL_OWNER_ID : String(accountUserId ?? "");
|
|
1564
|
+
let entityRemoveError = null;
|
|
1565
|
+
const contentType = String(contentInfo.type || "").toLowerCase();
|
|
1566
|
+
try {
|
|
1567
|
+
const strategy = ENTITY_DELETE_STRATEGIES[contentType];
|
|
1568
|
+
if (strategy) {
|
|
1569
|
+
await strategy(entityKey, { dispatch, userId, thunkAPI, sourceServerOrigin });
|
|
1570
|
+
} else if (isPageKey(entityKey)) {
|
|
1571
|
+
await ENTITY_DELETE_STRATEGIES.page(entityKey, {
|
|
1572
|
+
dispatch,
|
|
1573
|
+
userId,
|
|
1574
|
+
thunkAPI,
|
|
1575
|
+
sourceServerOrigin
|
|
1576
|
+
});
|
|
1577
|
+
} else if (isAgentKey(entityKey)) {
|
|
1578
|
+
await ENTITY_DELETE_STRATEGIES.agent(entityKey, {
|
|
1579
|
+
dispatch,
|
|
1580
|
+
userId,
|
|
1581
|
+
thunkAPI,
|
|
1582
|
+
sourceServerOrigin
|
|
1583
|
+
});
|
|
1584
|
+
}
|
|
1585
|
+
} catch (err2) {
|
|
1586
|
+
console.error(`[deleteContent] Failed to delete entity ${entityKey}:`, err2);
|
|
1587
|
+
entityRemoveError = err2.message || "Unknown error";
|
|
1588
|
+
}
|
|
1589
|
+
return {
|
|
1590
|
+
contentKey,
|
|
1591
|
+
spaceId,
|
|
1592
|
+
updatedSpaceData,
|
|
1593
|
+
entityRemoveError
|
|
1594
|
+
};
|
|
1595
|
+
};
|
|
1596
|
+
|
|
1597
|
+
// packages/create/space/content/moveContentAction.ts
|
|
1598
|
+
var logger2 = createClientLogger("move-content");
|
|
1599
|
+
var moveContentAction = async (input, thunkAPI) => {
|
|
1600
|
+
const {
|
|
1601
|
+
contentKey,
|
|
1602
|
+
sourceSpaceId,
|
|
1603
|
+
targetSpaceId,
|
|
1604
|
+
targetCategoryId: rawTargetCategoryId
|
|
1605
|
+
} = input;
|
|
1606
|
+
const { dispatch, getState } = thunkAPI;
|
|
1607
|
+
const state3 = getState();
|
|
1608
|
+
const userId = selectIdentityUserId(state3);
|
|
1609
|
+
const now = Date.now();
|
|
1610
|
+
logger2.info({ ...input, userId }, "Initiating moveContentAction");
|
|
1611
|
+
if (sourceSpaceId === targetSpaceId) {
|
|
1612
|
+
logger2.error("Source and target space IDs are the same.");
|
|
1613
|
+
throw new Error(
|
|
1614
|
+
"\u6E90\u7A7A\u95F4\u548C\u76EE\u6807\u7A7A\u95F4\u4E0D\u80FD\u76F8\u540C\u3002\u5982\u9700\u4FEE\u6539\u5206\u7C7B\uFF0C\u8BF7\u4F7F\u7528\u66F4\u65B0\u5206\u7C7B\u64CD\u4F5C\u3002"
|
|
1615
|
+
);
|
|
1616
|
+
}
|
|
1617
|
+
if (!contentKey || typeof contentKey !== "string" || contentKey.trim() === "") {
|
|
1618
|
+
logger2.error("Invalid contentKey provided.");
|
|
1619
|
+
throw new Error("\u65E0\u6548\u7684\u5185\u5BB9 Key\u3002");
|
|
1620
|
+
}
|
|
1621
|
+
let sourceSpaceData = null;
|
|
1622
|
+
let targetSpaceData = null;
|
|
1623
|
+
let contentDataFromSource = null;
|
|
1624
|
+
let finalSourceData = null;
|
|
1625
|
+
let finalTargetData = null;
|
|
1626
|
+
let overallError = void 0;
|
|
1627
|
+
try {
|
|
1628
|
+
const sourceSpaceKey = createSpaceKey.space(sourceSpaceId);
|
|
1629
|
+
const targetSpaceKey = createSpaceKey.space(targetSpaceId);
|
|
1630
|
+
logger2.info(
|
|
1631
|
+
{ sourceSpaceKey, targetSpaceKey },
|
|
1632
|
+
"Reading source and target space data."
|
|
1633
|
+
);
|
|
1634
|
+
const [sourceResult, targetResult] = await Promise.allSettled([
|
|
1635
|
+
dispatch(read({
|
|
1636
|
+
dbKey: sourceSpaceKey
|
|
1637
|
+
})).unwrap(),
|
|
1638
|
+
dispatch(read({
|
|
1639
|
+
dbKey: targetSpaceKey
|
|
1640
|
+
})).unwrap()
|
|
1641
|
+
]);
|
|
1642
|
+
if (sourceResult.status === "rejected" || !sourceResult.value) {
|
|
1643
|
+
const reason = sourceResult.status === "rejected" ? sourceResult.reason : "Data is null";
|
|
1644
|
+
throw new Error(
|
|
1645
|
+
`\u65E0\u6CD5\u52A0\u8F7D\u6E90\u7A7A\u95F4 (${sourceSpaceId}): ${reason?.message || reason}`
|
|
1646
|
+
);
|
|
1647
|
+
}
|
|
1648
|
+
sourceSpaceData = sourceResult.value;
|
|
1649
|
+
if (targetResult.status === "rejected" || !targetResult.value) {
|
|
1650
|
+
const reason = targetResult.status === "rejected" ? targetResult.reason : "Data is null";
|
|
1651
|
+
throw new Error(
|
|
1652
|
+
`\u65E0\u6CD5\u52A0\u8F7D\u76EE\u6807\u7A7A\u95F4 (${targetSpaceId}): ${reason?.message || reason}`
|
|
1653
|
+
);
|
|
1654
|
+
}
|
|
1655
|
+
targetSpaceData = targetResult.value;
|
|
1656
|
+
logger2.info("Checking permissions for source and target spaces.");
|
|
1657
|
+
checkSpaceMembership(sourceSpaceData, userId);
|
|
1658
|
+
checkSpaceMembership(targetSpaceData, userId);
|
|
1659
|
+
if (!sourceSpaceData.contents || !sourceSpaceData.contents[contentKey]) {
|
|
1660
|
+
logger2.error(
|
|
1661
|
+
{ contentKey, sourceSpaceId },
|
|
1662
|
+
"Content key not found in source space contents."
|
|
1663
|
+
);
|
|
1664
|
+
throw new Error(
|
|
1665
|
+
`\u5185\u5BB9 (${contentKey}) \u4E0D\u5728\u6E90\u7A7A\u95F4 (${sourceSpaceId}) \u4E2D\u3002`
|
|
1666
|
+
);
|
|
1667
|
+
}
|
|
1668
|
+
contentDataFromSource = sourceSpaceData.contents[contentKey];
|
|
1669
|
+
if (targetSpaceData.contents && targetSpaceData.contents[contentKey]) {
|
|
1670
|
+
logger2.warn(
|
|
1671
|
+
{ contentKey, targetSpaceId },
|
|
1672
|
+
"Content key already exists in target space. Overwriting."
|
|
1673
|
+
);
|
|
1674
|
+
}
|
|
1675
|
+
let categoryIdForTargetStorage;
|
|
1676
|
+
if (rawTargetCategoryId && rawTargetCategoryId !== "" && rawTargetCategoryId !== UNCATEGORIZED_ID) {
|
|
1677
|
+
if (targetSpaceData.categories?.[rawTargetCategoryId]) {
|
|
1678
|
+
categoryIdForTargetStorage = rawTargetCategoryId;
|
|
1679
|
+
} else {
|
|
1680
|
+
logger2.warn(
|
|
1681
|
+
`Target category ${rawTargetCategoryId} not found in target space ${targetSpaceId}. Content will be uncategorized.`
|
|
1682
|
+
);
|
|
1683
|
+
categoryIdForTargetStorage = void 0;
|
|
1684
|
+
}
|
|
1685
|
+
} else {
|
|
1686
|
+
categoryIdForTargetStorage = void 0;
|
|
1687
|
+
}
|
|
1688
|
+
const contentReferenceForTarget = {
|
|
1689
|
+
...contentDataFromSource,
|
|
1690
|
+
updatedAt: now
|
|
1691
|
+
};
|
|
1692
|
+
delete contentReferenceForTarget.categoryId;
|
|
1693
|
+
if (categoryIdForTargetStorage !== void 0) {
|
|
1694
|
+
contentReferenceForTarget.categoryId = categoryIdForTargetStorage;
|
|
1695
|
+
}
|
|
1696
|
+
const sourcePatchChanges = {
|
|
1697
|
+
contents: {
|
|
1698
|
+
[contentKey]: null
|
|
1699
|
+
// 从源移除引用 (null value in patch typically means delete key)
|
|
1700
|
+
},
|
|
1701
|
+
updatedAt: now,
|
|
1702
|
+
...localSpaceAuthorityPatchStamp(sourceSpaceData)
|
|
1703
|
+
};
|
|
1704
|
+
const targetPatchChanges = {
|
|
1705
|
+
contents: {
|
|
1706
|
+
[contentKey]: contentReferenceForTarget
|
|
1707
|
+
// 向目标添加(或覆盖)引用
|
|
1708
|
+
},
|
|
1709
|
+
updatedAt: now,
|
|
1710
|
+
...localSpaceAuthorityPatchStamp(targetSpaceData)
|
|
1711
|
+
};
|
|
1712
|
+
logger2.info("Patching source and target space data.");
|
|
1713
|
+
await Promise.all([
|
|
1714
|
+
dispatch(
|
|
1715
|
+
patch({ dbKey: sourceSpaceKey, changes: sourcePatchChanges })
|
|
1716
|
+
).unwrap(),
|
|
1717
|
+
dispatch(
|
|
1718
|
+
patch({ dbKey: targetSpaceKey, changes: targetPatchChanges })
|
|
1719
|
+
).unwrap()
|
|
1720
|
+
]);
|
|
1721
|
+
logger2.info("Successfully patched both source and target spaces.");
|
|
1722
|
+
} catch (error) {
|
|
1723
|
+
logger2.error(
|
|
1724
|
+
{ error: error.message, stack: error.stack },
|
|
1725
|
+
"Error during move content action execution."
|
|
1726
|
+
);
|
|
1727
|
+
overallError = error.message || "\u79FB\u52A8\u5185\u5BB9\u65F6\u53D1\u751F\u672A\u77E5\u9519\u8BEF";
|
|
1728
|
+
} finally {
|
|
1729
|
+
logger2.info("Attempting to re-read final space data.");
|
|
1730
|
+
const sourceSpaceKey = createSpaceKey.space(sourceSpaceId);
|
|
1731
|
+
const targetSpaceKey = createSpaceKey.space(targetSpaceId);
|
|
1732
|
+
const [finalSourceResult, finalTargetResult] = await Promise.allSettled([
|
|
1733
|
+
dispatch(read({
|
|
1734
|
+
dbKey: sourceSpaceKey
|
|
1735
|
+
})).unwrap(),
|
|
1736
|
+
dispatch(read({
|
|
1737
|
+
dbKey: targetSpaceKey
|
|
1738
|
+
})).unwrap()
|
|
1739
|
+
]);
|
|
1740
|
+
if (finalSourceResult.status === "fulfilled") {
|
|
1741
|
+
finalSourceData = finalSourceResult.value;
|
|
1742
|
+
} else {
|
|
1743
|
+
logger2.error(
|
|
1744
|
+
{ sourceSpaceId, reason: finalSourceResult.reason },
|
|
1745
|
+
"Failed to re-read final source space data."
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1748
|
+
if (finalTargetResult.status === "fulfilled") {
|
|
1749
|
+
finalTargetData = finalTargetResult.value;
|
|
1750
|
+
} else {
|
|
1751
|
+
logger2.error(
|
|
1752
|
+
{ targetSpaceId, reason: finalTargetResult.reason },
|
|
1753
|
+
"Failed to re-read final target space data."
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1756
|
+
logger2.info(
|
|
1757
|
+
{
|
|
1758
|
+
hasError: !!overallError,
|
|
1759
|
+
sourceReadSuccess: !!finalSourceData,
|
|
1760
|
+
targetReadSuccess: !!finalTargetData
|
|
1761
|
+
},
|
|
1762
|
+
"Move content action finished."
|
|
1763
|
+
);
|
|
1764
|
+
const result = {
|
|
1765
|
+
sourceSpaceId,
|
|
1766
|
+
updatedSourceSpaceData: finalSourceData,
|
|
1767
|
+
targetSpaceId,
|
|
1768
|
+
updatedTargetSpaceData: finalTargetData,
|
|
1769
|
+
error: overallError
|
|
1770
|
+
// 如果过程中有错误,则传递错误信息
|
|
1771
|
+
};
|
|
1772
|
+
return result;
|
|
1773
|
+
}
|
|
1774
|
+
};
|
|
1775
|
+
|
|
1776
|
+
// packages/create/space/content/spaceContentPatch.ts
|
|
1777
|
+
var notifyUserDataUpdated = () => {
|
|
1778
|
+
if (typeof window !== "undefined" && typeof window.dispatchEvent === "function" && typeof window.Event === "function") {
|
|
1779
|
+
window.dispatchEvent(new window.Event("nolo-user-data-updated"));
|
|
1780
|
+
}
|
|
1781
|
+
};
|
|
1782
|
+
var loadSpaceContentOrThrow = async (dispatch, spaceId, contentKey, sourceServerOrigin) => {
|
|
1783
|
+
if (!contentKey || typeof contentKey !== "string" || contentKey.trim() === "") {
|
|
1784
|
+
throw new Error("Invalid contentKey provided.");
|
|
1785
|
+
}
|
|
1786
|
+
const spaceKey = createSpaceKey.space(spaceId);
|
|
1787
|
+
let spaceData = null;
|
|
1788
|
+
try {
|
|
1789
|
+
spaceData = await dispatch(
|
|
1790
|
+
read({ dbKey: spaceKey, preferredServerOrigin: sourceServerOrigin })
|
|
1791
|
+
).unwrap();
|
|
1792
|
+
} catch (readError) {
|
|
1793
|
+
throw new Error(`\u65E0\u6CD5\u52A0\u8F7D\u7A7A\u95F4\u6570\u636E: ${spaceId}`);
|
|
1794
|
+
}
|
|
1795
|
+
if (!spaceData) {
|
|
1796
|
+
throw new Error("Space not found");
|
|
1797
|
+
}
|
|
1798
|
+
const content = spaceData.contents?.[contentKey];
|
|
1799
|
+
if (!content) {
|
|
1800
|
+
throw new Error("Content not found in space");
|
|
1801
|
+
}
|
|
1802
|
+
return { spaceKey, spaceData, content };
|
|
1803
|
+
};
|
|
1804
|
+
var patchIndividualContentRecord = async (dispatch, contentKey, changes, sourceServerOrigin, failMessagePrefix) => {
|
|
1805
|
+
let hasIndividualRecord = false;
|
|
1806
|
+
try {
|
|
1807
|
+
const existing = await dispatch(
|
|
1808
|
+
read({ dbKey: contentKey, preferredServerOrigin: sourceServerOrigin })
|
|
1809
|
+
).unwrap();
|
|
1810
|
+
hasIndividualRecord = Boolean(existing);
|
|
1811
|
+
} catch {
|
|
1812
|
+
hasIndividualRecord = false;
|
|
1813
|
+
}
|
|
1814
|
+
if (hasIndividualRecord) {
|
|
1815
|
+
try {
|
|
1816
|
+
await dispatch(
|
|
1817
|
+
patch({
|
|
1818
|
+
dbKey: contentKey,
|
|
1819
|
+
changes,
|
|
1820
|
+
preferredServerOrigin: sourceServerOrigin
|
|
1821
|
+
})
|
|
1822
|
+
).unwrap();
|
|
1823
|
+
notifyUserDataUpdated();
|
|
1824
|
+
} catch (contentPatchError) {
|
|
1825
|
+
throw new Error(
|
|
1826
|
+
`${failMessagePrefix}: ${contentPatchError?.message || "\u672A\u77E5\u9519\u8BEF"}`
|
|
1827
|
+
);
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
};
|
|
1831
|
+
|
|
1832
|
+
// packages/create/space/content/updateContentTitleAction.ts
|
|
1833
|
+
var buildContentRecordTitleChanges = (content, title, updatedAt) => {
|
|
1834
|
+
const changes = { title, updatedAt };
|
|
1835
|
+
if (content.type === "app") {
|
|
1836
|
+
changes.name = title;
|
|
1837
|
+
}
|
|
1838
|
+
return changes;
|
|
1839
|
+
};
|
|
1840
|
+
var updateContentTitleAction = async (input, thunkAPI) => {
|
|
1841
|
+
const { spaceId, contentKey, title, skillSummary, sourceServerOrigin } = input;
|
|
1842
|
+
const { dispatch } = thunkAPI;
|
|
1843
|
+
if (title === void 0 || title === null || typeof title !== "string") {
|
|
1844
|
+
throw new Error("Invalid title provided.");
|
|
1845
|
+
}
|
|
1846
|
+
if (title.trim() === "") {
|
|
1847
|
+
throw new Error("Title cannot be empty.");
|
|
1848
|
+
}
|
|
1849
|
+
const { spaceKey, spaceData, content } = await loadSpaceContentOrThrow(
|
|
1850
|
+
dispatch,
|
|
1851
|
+
spaceId,
|
|
1852
|
+
contentKey,
|
|
1853
|
+
sourceServerOrigin
|
|
1854
|
+
);
|
|
1855
|
+
const trimmedTitle = title.trim();
|
|
1856
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1857
|
+
const changes = {
|
|
1858
|
+
contents: {
|
|
1859
|
+
[contentKey]: {
|
|
1860
|
+
...spaceData.contents[contentKey],
|
|
1861
|
+
title: trimmedTitle,
|
|
1862
|
+
updatedAt: now,
|
|
1863
|
+
...skillSummary !== void 0 ? { skillSummary } : {}
|
|
1864
|
+
}
|
|
1865
|
+
},
|
|
1866
|
+
updatedAt: now
|
|
1867
|
+
};
|
|
1868
|
+
let updatedSpaceData;
|
|
1869
|
+
try {
|
|
1870
|
+
updatedSpaceData = await dispatch(
|
|
1871
|
+
patch({
|
|
1872
|
+
dbKey: spaceKey,
|
|
1873
|
+
changes,
|
|
1874
|
+
preferredServerOrigin: sourceServerOrigin
|
|
1875
|
+
})
|
|
1876
|
+
).unwrap();
|
|
1877
|
+
} catch (patchError) {
|
|
1878
|
+
throw new Error(`\u66F4\u65B0\u5185\u5BB9\u6807\u9898\u5931\u8D25: ${patchError.message || "\u672A\u77E5\u9519\u8BEF"}`);
|
|
1879
|
+
}
|
|
1880
|
+
await patchIndividualContentRecord(
|
|
1881
|
+
dispatch,
|
|
1882
|
+
contentKey,
|
|
1883
|
+
buildContentRecordTitleChanges(content, trimmedTitle, now),
|
|
1884
|
+
sourceServerOrigin,
|
|
1885
|
+
"\u6807\u9898\u5DF2\u5199\u5165\u7A7A\u95F4\uFF0C\u4F46\u540C\u6B65\u72EC\u7ACB\u8BB0\u5F55\u5931\u8D25"
|
|
1886
|
+
);
|
|
1887
|
+
if (contentKey.startsWith("meta-")) {
|
|
1888
|
+
const parts = contentKey.split(SEPARATOR);
|
|
1889
|
+
if (parts.length >= 3) {
|
|
1890
|
+
const tableId = parts.slice(2).join(SEPARATOR);
|
|
1891
|
+
const tenantId = parts[1];
|
|
1892
|
+
try {
|
|
1893
|
+
await dispatch(
|
|
1894
|
+
renameTable({
|
|
1895
|
+
tenantId,
|
|
1896
|
+
tableId,
|
|
1897
|
+
newName: trimmedTitle
|
|
1898
|
+
})
|
|
1899
|
+
).unwrap();
|
|
1900
|
+
} catch (tableError) {
|
|
1901
|
+
console.error("Failed to sync title to TableMeta:", tableError);
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
return { spaceId, updatedSpaceData };
|
|
1906
|
+
};
|
|
1907
|
+
|
|
1908
|
+
// packages/create/space/content/updateContentPinnedAction.ts
|
|
1909
|
+
var updateContentPinnedAction = async (input, thunkAPI) => {
|
|
1910
|
+
const { spaceId, contentKey, pinned, sourceServerOrigin } = input;
|
|
1911
|
+
const { dispatch } = thunkAPI;
|
|
1912
|
+
if (typeof pinned !== "boolean") {
|
|
1913
|
+
throw new Error("Invalid pinned value provided.");
|
|
1914
|
+
}
|
|
1915
|
+
if (!spaceId) {
|
|
1916
|
+
if (!contentKey || typeof contentKey !== "string" || contentKey.trim() === "") {
|
|
1917
|
+
throw new Error("Invalid contentKey provided.");
|
|
1918
|
+
}
|
|
1919
|
+
await patchIndividualContentRecord(
|
|
1920
|
+
dispatch,
|
|
1921
|
+
contentKey,
|
|
1922
|
+
{ pinned },
|
|
1923
|
+
sourceServerOrigin,
|
|
1924
|
+
"\u66F4\u65B0\u7F6E\u9876\u72B6\u6001\u5931\u8D25"
|
|
1925
|
+
);
|
|
1926
|
+
return { spaceId: null, updatedSpaceData: null };
|
|
1927
|
+
}
|
|
1928
|
+
const { spaceKey, spaceData } = await loadSpaceContentOrThrow(
|
|
1929
|
+
dispatch,
|
|
1930
|
+
spaceId,
|
|
1931
|
+
contentKey,
|
|
1932
|
+
sourceServerOrigin
|
|
1933
|
+
);
|
|
1934
|
+
const changes = {
|
|
1935
|
+
contents: {
|
|
1936
|
+
[contentKey]: { ...spaceData.contents[contentKey], pinned }
|
|
1937
|
+
},
|
|
1938
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1939
|
+
};
|
|
1940
|
+
let updatedSpaceData;
|
|
1941
|
+
try {
|
|
1942
|
+
updatedSpaceData = await dispatch(
|
|
1943
|
+
patch({ dbKey: spaceKey, changes, preferredServerOrigin: sourceServerOrigin })
|
|
1944
|
+
).unwrap();
|
|
1945
|
+
} catch (patchError) {
|
|
1946
|
+
throw new Error(`\u66F4\u65B0\u5185\u5BB9\u7F6E\u9876\u72B6\u6001\u5931\u8D25: ${patchError.message || "\u672A\u77E5\u9519\u8BEF"}`);
|
|
1947
|
+
}
|
|
1948
|
+
await patchIndividualContentRecord(
|
|
1949
|
+
dispatch,
|
|
1950
|
+
contentKey,
|
|
1951
|
+
{ pinned },
|
|
1952
|
+
sourceServerOrigin,
|
|
1953
|
+
"\u7F6E\u9876\u72B6\u6001\u5DF2\u5199\u5165\u7A7A\u95F4\uFF0C\u4F46\u540C\u6B65\u72EC\u7ACB\u8BB0\u5F55\u5931\u8D25"
|
|
1954
|
+
);
|
|
1955
|
+
return { spaceId, updatedSpaceData };
|
|
1956
|
+
};
|
|
1957
|
+
|
|
1958
|
+
// packages/create/space/content/updateContentCategoryAction.ts
|
|
1959
|
+
var updateContentCategoryAction = async (input, thunkAPI) => {
|
|
1960
|
+
const { spaceId, contentKey, categoryId: targetContainerId } = input;
|
|
1961
|
+
const { dispatch, getState } = thunkAPI;
|
|
1962
|
+
const state3 = getState();
|
|
1963
|
+
const userId = selectIdentityUserId(state3);
|
|
1964
|
+
if (!userId) {
|
|
1965
|
+
throw new Error("User is not logged in.");
|
|
1966
|
+
}
|
|
1967
|
+
if (!contentKey || typeof contentKey !== "string" || contentKey.trim() === "") {
|
|
1968
|
+
throw new Error("Invalid contentKey provided.");
|
|
1969
|
+
}
|
|
1970
|
+
if (typeof targetContainerId !== "string") {
|
|
1971
|
+
throw new Error("Invalid target container ID provided (must be a string).");
|
|
1972
|
+
}
|
|
1973
|
+
const spaceKey = createSpaceKey.space(spaceId);
|
|
1974
|
+
let spaceData = null;
|
|
1975
|
+
try {
|
|
1976
|
+
spaceData = await dispatch(read({
|
|
1977
|
+
dbKey: spaceKey
|
|
1978
|
+
})).unwrap();
|
|
1979
|
+
} catch (readError) {
|
|
1980
|
+
console.error(
|
|
1981
|
+
`[updateContentCategoryAction] Failed to read space data for key ${spaceKey}:`,
|
|
1982
|
+
readError
|
|
1983
|
+
);
|
|
1984
|
+
throw new Error(
|
|
1985
|
+
`\u65E0\u6CD5\u52A0\u8F7D\u7A7A\u95F4\u6570\u636E: ${spaceId}, \u539F\u56E0: ${readError.message || "\u672A\u77E5\u9519\u8BEF"}`
|
|
1986
|
+
);
|
|
1987
|
+
}
|
|
1988
|
+
if (!spaceData) {
|
|
1989
|
+
throw new Error(`\u7A7A\u95F4\u4E0D\u5B58\u5728: ${spaceId}`);
|
|
1990
|
+
}
|
|
1991
|
+
try {
|
|
1992
|
+
checkSpaceMembership(spaceData, userId);
|
|
1993
|
+
} catch (permissionError) {
|
|
1994
|
+
throw new Error(`\u6743\u9650\u4E0D\u8DB3\uFF0C\u65E0\u6CD5\u4FEE\u6539\u5185\u5BB9\u5206\u7C7B: ${permissionError.message}`);
|
|
1995
|
+
}
|
|
1996
|
+
const currentContent = spaceData.contents?.[contentKey];
|
|
1997
|
+
if (!currentContent) {
|
|
1998
|
+
console.warn(
|
|
1999
|
+
`[updateContentCategoryAction] Content ${contentKey} not found in space ${spaceId}.`
|
|
2000
|
+
);
|
|
2001
|
+
throw new Error("Content not found");
|
|
2002
|
+
}
|
|
2003
|
+
if (targetContainerId !== UNCATEGORIZED_ID) {
|
|
2004
|
+
if (!spaceData.categories[targetContainerId]) {
|
|
2005
|
+
console.warn(
|
|
2006
|
+
`[updateContentCategoryAction] Target category ${targetContainerId} not found or invalid in space ${spaceId}. Cannot move content.`
|
|
2007
|
+
);
|
|
2008
|
+
throw new Error("Target category not found or is invalid");
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
2011
|
+
const now = Date.now();
|
|
2012
|
+
const categoryIdValueForPatch = targetContainerId === UNCATEGORIZED_ID ? null : targetContainerId;
|
|
2013
|
+
const currentCategoryId = currentContent.categoryId;
|
|
2014
|
+
let needsUpdate = true;
|
|
2015
|
+
if (categoryIdValueForPatch === null && currentCategoryId === void 0) {
|
|
2016
|
+
needsUpdate = false;
|
|
2017
|
+
} else if (categoryIdValueForPatch === currentCategoryId) {
|
|
2018
|
+
needsUpdate = false;
|
|
2019
|
+
}
|
|
2020
|
+
let changes = null;
|
|
2021
|
+
if (needsUpdate) {
|
|
2022
|
+
changes = {
|
|
2023
|
+
contents: {
|
|
2024
|
+
// 使用动态键更新指定的 contentKey
|
|
2025
|
+
[contentKey]: {
|
|
2026
|
+
// --- 修改: 使用计算出的 categoryIdValueForPatch ---
|
|
2027
|
+
categoryId: categoryIdValueForPatch,
|
|
2028
|
+
// null 或 目标分类 ID
|
|
2029
|
+
updatedAt: now
|
|
2030
|
+
// 更新内容的 updatedAt
|
|
2031
|
+
}
|
|
2032
|
+
},
|
|
2033
|
+
updatedAt: now
|
|
2034
|
+
// 更新顶层的 updatedAt
|
|
2035
|
+
};
|
|
2036
|
+
} else {
|
|
2037
|
+
console.log(
|
|
2038
|
+
`[updateContentCategoryAction] No category change needed for ${contentKey}. Skipping patch.`
|
|
2039
|
+
);
|
|
2040
|
+
return { spaceId, updatedSpaceData: spaceData };
|
|
2041
|
+
}
|
|
2042
|
+
let updatedSpaceData;
|
|
2043
|
+
try {
|
|
2044
|
+
updatedSpaceData = await dispatch(
|
|
2045
|
+
patch({ dbKey: spaceKey, changes })
|
|
2046
|
+
// 使用非空断言,因为已检查
|
|
2047
|
+
).unwrap();
|
|
2048
|
+
} catch (patchError) {
|
|
2049
|
+
console.error(
|
|
2050
|
+
`[updateContentCategoryAction] Failed to patch space data for key ${spaceKey}:`,
|
|
2051
|
+
patchError
|
|
2052
|
+
);
|
|
2053
|
+
throw new Error(`\u66F4\u65B0\u5185\u5BB9\u5206\u7C7B\u5931\u8D25: ${patchError.message || "\u672A\u77E5\u9519\u8BEF"}`);
|
|
2054
|
+
}
|
|
2055
|
+
return { spaceId, updatedSpaceData };
|
|
2056
|
+
};
|
|
2057
|
+
|
|
2058
|
+
// packages/create/space/content/deleteMultipleContentAction.ts
|
|
2059
|
+
var logger3 = createClientLogger("delete-multiple-content");
|
|
2060
|
+
var deleteMultipleContentAction = async (input, thunkAPI) => {
|
|
2061
|
+
const { contentKeys, spaceId } = input;
|
|
2062
|
+
if (!contentKeys || contentKeys.length === 0) {
|
|
2063
|
+
logger3.warn("deleteMultipleContentAction called with no contentKeys.");
|
|
2064
|
+
return { spaceId, updatedSpaceData: null };
|
|
2065
|
+
}
|
|
2066
|
+
const { dispatch, getState } = thunkAPI;
|
|
2067
|
+
const state3 = getState();
|
|
2068
|
+
const userId = selectIdentityUserId(state3);
|
|
2069
|
+
logger3.info(
|
|
2070
|
+
{ count: contentKeys.length, spaceId, userId },
|
|
2071
|
+
"Initiating deleteMultipleContentAction"
|
|
2072
|
+
);
|
|
2073
|
+
const spaceKey = createSpaceKey.space(spaceId);
|
|
2074
|
+
const spaceData = await dispatch(
|
|
2075
|
+
read({
|
|
2076
|
+
dbKey: spaceKey
|
|
2077
|
+
})
|
|
2078
|
+
).unwrap();
|
|
2079
|
+
if (!spaceData) {
|
|
2080
|
+
logger3.error({ spaceKey }, "Space data not found for batch delete");
|
|
2081
|
+
throw new Error("\u7A7A\u95F4\u4E0D\u5B58\u5728");
|
|
2082
|
+
}
|
|
2083
|
+
if (!userId || !spaceData.members.includes(userId)) {
|
|
2084
|
+
logger3.warn({ userId, spaceId }, "Unauthorized attempt for batch delete");
|
|
2085
|
+
throw new Error("\u65E0\u6743\u4FEE\u6539\u6B64\u7A7A\u95F4");
|
|
2086
|
+
}
|
|
2087
|
+
const changes = {
|
|
2088
|
+
contents: contentKeys.reduce(
|
|
2089
|
+
(acc, key) => {
|
|
2090
|
+
acc[key] = null;
|
|
2091
|
+
return acc;
|
|
2092
|
+
},
|
|
2093
|
+
{}
|
|
2094
|
+
)
|
|
2095
|
+
};
|
|
2096
|
+
try {
|
|
2097
|
+
await dispatch(patch({ dbKey: spaceKey, changes })).unwrap();
|
|
2098
|
+
logger3.info(
|
|
2099
|
+
{ spaceKey, count: contentKeys.length },
|
|
2100
|
+
"Successfully batched patch to remove content references"
|
|
2101
|
+
);
|
|
2102
|
+
} catch (patchError) {
|
|
2103
|
+
logger3.error(
|
|
2104
|
+
{ error: patchError, spaceKey },
|
|
2105
|
+
"Failed to batch patch space data. Aborting."
|
|
2106
|
+
);
|
|
2107
|
+
throw new Error(`\u66F4\u65B0\u7A7A\u95F4\u5F15\u7528\u5931\u8D25: ${patchError.message}`);
|
|
2108
|
+
}
|
|
2109
|
+
const removePromises = contentKeys.map(
|
|
2110
|
+
(key) => dispatch(remove(key)).unwrap()
|
|
2111
|
+
);
|
|
2112
|
+
const results = await Promise.allSettled(removePromises);
|
|
2113
|
+
results.forEach((result, index) => {
|
|
2114
|
+
if (result.status === "fulfilled") {
|
|
2115
|
+
logger3.info({ contentKey: contentKeys[index] }, "Entity removed");
|
|
2116
|
+
} else {
|
|
2117
|
+
logger3.error(
|
|
2118
|
+
{ contentKey: contentKeys[index], error: result.reason },
|
|
2119
|
+
"Failed to remove an entity"
|
|
2120
|
+
);
|
|
2121
|
+
}
|
|
2122
|
+
});
|
|
2123
|
+
const finalSpaceData = await dispatch(
|
|
2124
|
+
read({
|
|
2125
|
+
dbKey: spaceKey
|
|
2126
|
+
})
|
|
2127
|
+
).unwrap();
|
|
2128
|
+
logger3.info({ spaceId }, "Finished batch delete and refetched space data.");
|
|
2129
|
+
return {
|
|
2130
|
+
spaceId,
|
|
2131
|
+
updatedSpaceData: finalSpaceData
|
|
2132
|
+
};
|
|
2133
|
+
};
|
|
2134
|
+
|
|
2135
|
+
// packages/create/space/content/spaceFileVersions.ts
|
|
2136
|
+
var normalizeFileName = (name) => typeof name === "string" ? name.trim().normalize("NFC") : "";
|
|
2137
|
+
var versionsOf = (entryKey, entry) => {
|
|
2138
|
+
if (Array.isArray(entry.versions) && entry.versions.length > 0) {
|
|
2139
|
+
return entry.versions.filter((v) => v && typeof v.fileKey === "string");
|
|
2140
|
+
}
|
|
2141
|
+
return [
|
|
2142
|
+
{
|
|
2143
|
+
fileKey: entry.contentKey || entryKey,
|
|
2144
|
+
sha256: entry.sha256 || "",
|
|
2145
|
+
size: typeof entry.fileSize === "number" ? entry.fileSize : 0,
|
|
2146
|
+
uploadedAt: typeof entry.createdAt === "number" ? entry.createdAt : 0
|
|
2147
|
+
}
|
|
2148
|
+
];
|
|
2149
|
+
};
|
|
2150
|
+
var findSameNameFileEntries = (contents, originalName) => {
|
|
2151
|
+
const target = normalizeFileName(originalName);
|
|
2152
|
+
if (!target || !contents) return [];
|
|
2153
|
+
return Object.entries(contents).filter(([, value]) => Boolean(value)).map(([entryKey, value]) => ({ entryKey, entry: value })).filter(
|
|
2154
|
+
({ entry }) => entry.type === "file" && normalizeFileName(entry.originalName ?? entry.title) === target
|
|
2155
|
+
);
|
|
2156
|
+
};
|
|
2157
|
+
var sortVersions = (versions) => [...versions].sort(
|
|
2158
|
+
(a, b) => a.uploadedAt - b.uploadedAt || a.fileKey.localeCompare(b.fileKey)
|
|
2159
|
+
);
|
|
2160
|
+
var mergeVersions = (groups) => {
|
|
2161
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
2162
|
+
for (const { entryKey, entry } of groups) {
|
|
2163
|
+
for (const v of versionsOf(entryKey, entry)) {
|
|
2164
|
+
const prev = byKey.get(v.fileKey);
|
|
2165
|
+
if (!prev || !prev.sha256 && v.sha256) byKey.set(v.fileKey, v);
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
return sortVersions([...byKey.values()]);
|
|
2169
|
+
};
|
|
2170
|
+
var planSpaceFileUpload = (contents, originalName, sha256) => {
|
|
2171
|
+
const same = findSameNameFileEntries(contents, originalName);
|
|
2172
|
+
if (same.length === 0) return { kind: "new_file" };
|
|
2173
|
+
const primary = pickPrimary(same);
|
|
2174
|
+
const current = primary.entry.contentKey || primary.entryKey;
|
|
2175
|
+
const versions = mergeVersions(same);
|
|
2176
|
+
const currentVersion = versions.find((v) => v.fileKey === current);
|
|
2177
|
+
if (currentVersion?.sha256 && currentVersion.sha256 === sha256) {
|
|
2178
|
+
return { kind: "unchanged", entryKey: primary.entryKey, currentKey: current };
|
|
2179
|
+
}
|
|
2180
|
+
const older = versions.find((v) => v.sha256 && v.sha256 === sha256);
|
|
2181
|
+
if (older) {
|
|
2182
|
+
return { kind: "restore_existing", entryKey: primary.entryKey, versionKey: older.fileKey };
|
|
2183
|
+
}
|
|
2184
|
+
return { kind: "new_version", entryKey: primary.entryKey };
|
|
2185
|
+
};
|
|
2186
|
+
var pickPrimary = (same) => [...same].sort((a, b) => {
|
|
2187
|
+
const at = Math.max(...versionsOf(a.entryKey, a.entry).map((v) => v.uploadedAt));
|
|
2188
|
+
const bt = Math.max(...versionsOf(b.entryKey, b.entry).map((v) => v.uploadedAt));
|
|
2189
|
+
return bt - at || b.entryKey.localeCompare(a.entryKey);
|
|
2190
|
+
})[0];
|
|
2191
|
+
var buildVersionedEntryPatch = (input) => {
|
|
2192
|
+
const { same, extraVersion, now } = input;
|
|
2193
|
+
const versions = mergeVersions(
|
|
2194
|
+
extraVersion ? [...same, { entryKey: extraVersion.fileKey, entry: { versions: [extraVersion] } }] : same
|
|
2195
|
+
);
|
|
2196
|
+
const currentKey = input.currentKey ?? versions[versions.length - 1].fileKey;
|
|
2197
|
+
const current = versions.find((v) => v.fileKey === currentKey);
|
|
2198
|
+
if (!current) throw new Error(`Version ${currentKey} not found for this file.`);
|
|
2199
|
+
const template = same.length > 0 ? pickPrimary(same).entry : {};
|
|
2200
|
+
const patch2 = {};
|
|
2201
|
+
for (const { entryKey } of same) {
|
|
2202
|
+
if (entryKey !== currentKey) patch2[entryKey] = null;
|
|
2203
|
+
}
|
|
2204
|
+
patch2[currentKey] = {
|
|
2205
|
+
...template,
|
|
2206
|
+
contentKey: currentKey,
|
|
2207
|
+
currentVersionKey: currentKey,
|
|
2208
|
+
fileSize: current.size,
|
|
2209
|
+
versions,
|
|
2210
|
+
updatedAt: now,
|
|
2211
|
+
...input.restoredFrom ? { restoredFromVersionKey: input.restoredFrom } : {}
|
|
2212
|
+
};
|
|
2213
|
+
return patch2;
|
|
2214
|
+
};
|
|
2215
|
+
var sha256Hex = async (file) => {
|
|
2216
|
+
const digest = await crypto.subtle.digest("SHA-256", await file.arrayBuffer());
|
|
2217
|
+
return Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2218
|
+
};
|
|
2219
|
+
|
|
2220
|
+
// packages/create/space/content/uploadAndAddFileToSpaceAction.ts
|
|
2221
|
+
var uploadQueues = /* @__PURE__ */ new Map();
|
|
2222
|
+
var serialize = (key, task) => {
|
|
2223
|
+
const prev = uploadQueues.get(key) ?? Promise.resolve();
|
|
2224
|
+
const next = prev.catch(() => void 0).then(task);
|
|
2225
|
+
uploadQueues.set(key, next);
|
|
2226
|
+
void next.finally(() => {
|
|
2227
|
+
if (uploadQueues.get(key) === next) uploadQueues.delete(key);
|
|
2228
|
+
}).catch(() => void 0);
|
|
2229
|
+
return next;
|
|
2230
|
+
};
|
|
2231
|
+
var readSpaceOrNull = async (dispatch, spaceId) => {
|
|
2232
|
+
try {
|
|
2233
|
+
return await dispatch(read({ dbKey: createSpaceKey.space(spaceId) })).unwrap() ?? null;
|
|
2234
|
+
} catch {
|
|
2235
|
+
return null;
|
|
2236
|
+
}
|
|
2237
|
+
};
|
|
2238
|
+
var reconcileSameNameEntries = async (thunkAPI, spaceId, originalName, options = {}) => {
|
|
2239
|
+
let updated = await reconcileOnce(thunkAPI, spaceId, originalName, options);
|
|
2240
|
+
for (let attempt = 0; attempt < 2 && updated; attempt += 1) {
|
|
2241
|
+
const same = findSameNameFileEntries(updated.contents, originalName);
|
|
2242
|
+
const keys = new Set(mergeVersions(same).map((v) => v.fileKey));
|
|
2243
|
+
const missingOwn = options.extraVersion && !keys.has(options.extraVersion.fileKey);
|
|
2244
|
+
if (same.length <= 1 && !missingOwn) break;
|
|
2245
|
+
const fresh = await readSpaceOrNull(thunkAPI.dispatch, spaceId);
|
|
2246
|
+
const freshSame = findSameNameFileEntries(fresh?.contents, originalName);
|
|
2247
|
+
const freshKeys = new Set(mergeVersions(freshSame).map((v) => v.fileKey));
|
|
2248
|
+
const stillMissing = options.extraVersion && !freshKeys.has(options.extraVersion.fileKey);
|
|
2249
|
+
if (freshSame.length <= 1 && !stillMissing) {
|
|
2250
|
+
updated = fresh;
|
|
2251
|
+
break;
|
|
2252
|
+
}
|
|
2253
|
+
updated = await reconcileOnce(thunkAPI, spaceId, originalName, {
|
|
2254
|
+
...stillMissing ? { extraVersion: options.extraVersion } : {}
|
|
2255
|
+
});
|
|
2256
|
+
}
|
|
2257
|
+
return updated;
|
|
2258
|
+
};
|
|
2259
|
+
var reconcileOnce = async (thunkAPI, spaceId, originalName, options = {}) => {
|
|
2260
|
+
const { dispatch, getState } = thunkAPI;
|
|
2261
|
+
const spaceData = await readSpaceOrNull(dispatch, spaceId);
|
|
2262
|
+
if (!spaceData) return null;
|
|
2263
|
+
checkSpaceMembership(spaceData, selectIdentityUserId(getState()));
|
|
2264
|
+
const same = findSameNameFileEntries(spaceData.contents, originalName);
|
|
2265
|
+
if (same.length === 0 && !options.extraVersion) return spaceData;
|
|
2266
|
+
const contentsPatch = buildVersionedEntryPatch({ same, now: Date.now(), ...options });
|
|
2267
|
+
return await dispatch(
|
|
2268
|
+
patch({
|
|
2269
|
+
dbKey: createSpaceKey.space(spaceId),
|
|
2270
|
+
changes: {
|
|
2271
|
+
contents: contentsPatch,
|
|
2272
|
+
updatedAt: Date.now(),
|
|
2273
|
+
...localSpaceAuthorityPatchStamp(spaceData)
|
|
2274
|
+
}
|
|
2275
|
+
})
|
|
2276
|
+
).unwrap();
|
|
2277
|
+
};
|
|
2278
|
+
var uploadAndAddFileToSpaceAction = async (payload, thunkAPI) => {
|
|
2279
|
+
const { spaceId, file } = payload;
|
|
2280
|
+
const userId = selectIdentityUserId(thunkAPI.getState());
|
|
2281
|
+
if (!userId) {
|
|
2282
|
+
throw new Error("Cannot upload a file to a space without an authenticated user.");
|
|
2283
|
+
}
|
|
2284
|
+
return serialize(
|
|
2285
|
+
`${spaceId}\0${file.name}`,
|
|
2286
|
+
() => uploadOnce(payload, thunkAPI, userId)
|
|
2287
|
+
);
|
|
2288
|
+
};
|
|
2289
|
+
var uploadOnce = async (payload, thunkAPI, userId) => {
|
|
2290
|
+
const { spaceId, file, categoryId } = payload;
|
|
2291
|
+
const { dispatch } = thunkAPI;
|
|
2292
|
+
try {
|
|
2293
|
+
const sha256 = await sha256Hex(file);
|
|
2294
|
+
const existingSpace = await readSpaceOrNull(dispatch, spaceId);
|
|
2295
|
+
const plan = existingSpace ? planSpaceFileUpload(existingSpace.contents, file.name, sha256) : { kind: "new_file" };
|
|
2296
|
+
if (plan.kind === "unchanged") {
|
|
2297
|
+
return {
|
|
2298
|
+
spaceId,
|
|
2299
|
+
updatedSpaceData: existingSpace,
|
|
2300
|
+
contentKey: plan.currentKey,
|
|
2301
|
+
fileId: plan.currentKey.split("-").slice(2).join("-"),
|
|
2302
|
+
outcome: "unchanged"
|
|
2303
|
+
};
|
|
2304
|
+
}
|
|
2305
|
+
if (plan.kind === "restore_existing") {
|
|
2306
|
+
const updated = await reconcileSameNameEntries(thunkAPI, spaceId, file.name, {
|
|
2307
|
+
currentKey: plan.versionKey,
|
|
2308
|
+
restoredFrom: plan.entryKey
|
|
2309
|
+
});
|
|
2310
|
+
return {
|
|
2311
|
+
spaceId,
|
|
2312
|
+
updatedSpaceData: updated ?? existingSpace,
|
|
2313
|
+
contentKey: plan.versionKey,
|
|
2314
|
+
fileId: plan.versionKey.split("-").slice(2).join("-"),
|
|
2315
|
+
outcome: "restored_existing"
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2318
|
+
const id = ulid();
|
|
2319
|
+
const dbKey = fileKey.single(userId, id);
|
|
2320
|
+
const contentType = "file" /* FILE */;
|
|
2321
|
+
const fileCategory = resolveFileCategory({
|
|
2322
|
+
mimeType: file.type,
|
|
2323
|
+
fileName: file.name
|
|
2324
|
+
});
|
|
2325
|
+
const fileMetadata = await uploadFileAction(
|
|
2326
|
+
{ file, customKey: dbKey, userId },
|
|
2327
|
+
thunkAPI
|
|
2328
|
+
);
|
|
2329
|
+
if (!fileMetadata) {
|
|
2330
|
+
throw new Error("Upload failed, no metadata returned");
|
|
2331
|
+
}
|
|
2332
|
+
const contentKey = fileMetadata.dbKey || dbKey;
|
|
2333
|
+
const title = file.name;
|
|
2334
|
+
const fileSize = asOptionalFiniteNumber(file.size);
|
|
2335
|
+
const version3 = {
|
|
2336
|
+
fileKey: contentKey,
|
|
2337
|
+
sha256,
|
|
2338
|
+
size: fileSize ?? 0,
|
|
2339
|
+
uploadedAt: Date.now()
|
|
2340
|
+
};
|
|
2341
|
+
const addAsNewFile = async () => {
|
|
2342
|
+
const result2 = await addContentAction({
|
|
2343
|
+
spaceId,
|
|
2344
|
+
contentKey,
|
|
2345
|
+
title,
|
|
2346
|
+
type: contentType,
|
|
2347
|
+
fileCategory,
|
|
2348
|
+
mimeType: file.type || void 0,
|
|
2349
|
+
fileSize,
|
|
2350
|
+
originalName: file.name,
|
|
2351
|
+
categoryId
|
|
2352
|
+
}, thunkAPI);
|
|
2353
|
+
return result2;
|
|
2354
|
+
};
|
|
2355
|
+
const result = plan.kind === "new_file" ? await addAsNewFile() : { spaceId, updatedSpaceData: existingSpace };
|
|
2356
|
+
await dispatch(
|
|
2357
|
+
patch({
|
|
2358
|
+
dbKey: contentKey,
|
|
2359
|
+
changes: {
|
|
2360
|
+
title,
|
|
2361
|
+
spaceId,
|
|
2362
|
+
fileCategory,
|
|
2363
|
+
mimeType: file.type || void 0,
|
|
2364
|
+
fileSize,
|
|
2365
|
+
originalName: file.name
|
|
2366
|
+
}
|
|
2367
|
+
})
|
|
2368
|
+
).unwrap();
|
|
2369
|
+
const reconciled = await reconcileSameNameEntries(thunkAPI, spaceId, file.name, {
|
|
2370
|
+
extraVersion: version3,
|
|
2371
|
+
currentKey: contentKey
|
|
2372
|
+
}).catch((error) => {
|
|
2373
|
+
if (plan.kind === "new_version") throw error;
|
|
2374
|
+
console.warn("[uploadAndAddFileToSpace] version reconcile failed", error);
|
|
2375
|
+
return null;
|
|
2376
|
+
});
|
|
2377
|
+
if (typeof window !== "undefined") {
|
|
2378
|
+
window.dispatchEvent(new Event("nolo-user-data-updated"));
|
|
2379
|
+
}
|
|
2380
|
+
return {
|
|
2381
|
+
...result,
|
|
2382
|
+
...reconciled ? { updatedSpaceData: reconciled } : {},
|
|
2383
|
+
contentKey,
|
|
2384
|
+
fileId: fileMetadata.id,
|
|
2385
|
+
outcome: plan.kind === "new_file" ? "new_file" : "new_version"
|
|
2386
|
+
};
|
|
2387
|
+
} catch (error) {
|
|
2388
|
+
console.error("Upload and add file error:", error);
|
|
2389
|
+
throw error;
|
|
2390
|
+
}
|
|
2391
|
+
};
|
|
2392
|
+
var restoreSpaceFileVersionAction = async (input, thunkAPI) => {
|
|
2393
|
+
const { spaceId, contentKey, versionKey } = input;
|
|
2394
|
+
const spaceData = await readSpaceOrNull(thunkAPI.dispatch, spaceId);
|
|
2395
|
+
if (!spaceData) throw new Error(`\u65E0\u6CD5\u52A0\u8F7D\u7A7A\u95F4\u6570\u636E: ${spaceId}`);
|
|
2396
|
+
const entry = spaceData.contents?.[contentKey];
|
|
2397
|
+
if (!entry || entry.type !== "file" /* FILE */) throw new Error("File not found in space");
|
|
2398
|
+
const versions = Array.isArray(entry.versions) ? entry.versions : [];
|
|
2399
|
+
if (!versions.some((v) => v.fileKey === versionKey)) {
|
|
2400
|
+
throw new Error("Version not found for this file");
|
|
2401
|
+
}
|
|
2402
|
+
const name = entry.originalName ?? entry.title;
|
|
2403
|
+
const updated = await serialize(
|
|
2404
|
+
`${spaceId}\0${name}`,
|
|
2405
|
+
() => reconcileSameNameEntries(thunkAPI, spaceId, name, {
|
|
2406
|
+
currentKey: versionKey,
|
|
2407
|
+
restoredFrom: contentKey
|
|
2408
|
+
})
|
|
2409
|
+
);
|
|
2410
|
+
return { spaceId, updatedSpaceData: updated ?? spaceData, contentKey: versionKey };
|
|
2411
|
+
};
|
|
2412
|
+
|
|
2413
|
+
// packages/create/space/spaceCollapsedState.ts
|
|
2414
|
+
var SPACE_COLLAPSE_STORAGE_PREFIX = "space-collapsed-categories:";
|
|
2415
|
+
var normalizeCollapsedCategories = (value) => {
|
|
2416
|
+
const entries = Object.entries(asRecordOrEmpty(value)).filter(
|
|
2417
|
+
([key, collapsed]) => typeof key === "string" && typeof collapsed === "boolean"
|
|
2418
|
+
);
|
|
2419
|
+
return Object.fromEntries(entries);
|
|
2420
|
+
};
|
|
2421
|
+
var storageKeyForSpace = (spaceId) => `${SPACE_COLLAPSE_STORAGE_PREFIX}${spaceId}`;
|
|
2422
|
+
var readStoredCollapsedCategories = (spaceId, storage) => {
|
|
2423
|
+
if (!spaceId || !storage) return {};
|
|
2424
|
+
try {
|
|
2425
|
+
const raw = storage.getItem(storageKeyForSpace(spaceId));
|
|
2426
|
+
if (!raw) return { [UNCATEGORIZED_ID]: false };
|
|
2427
|
+
return normalizeCollapsedCategories(JSON.parse(raw));
|
|
2428
|
+
} catch (error) {
|
|
2429
|
+
console.warn("[Space] \u8BFB\u53D6\u5206\u7C7B\u6298\u53E0\u72B6\u6001\u5931\u8D25:", error);
|
|
2430
|
+
return {};
|
|
2431
|
+
}
|
|
2432
|
+
};
|
|
2433
|
+
var writeStoredCollapsedCategories = (spaceId, collapsedCategories, storage) => {
|
|
2434
|
+
if (!spaceId || !storage) return;
|
|
2435
|
+
try {
|
|
2436
|
+
storage.setItem(
|
|
2437
|
+
storageKeyForSpace(spaceId),
|
|
2438
|
+
JSON.stringify(normalizeCollapsedCategories(collapsedCategories))
|
|
2439
|
+
);
|
|
2440
|
+
} catch (error) {
|
|
2441
|
+
console.warn("[Space] \u4FDD\u5B58\u5206\u7C7B\u6298\u53E0\u72B6\u6001\u5931\u8D25:", error);
|
|
2442
|
+
}
|
|
2443
|
+
};
|
|
2444
|
+
|
|
2445
|
+
// packages/create/space/spaceUiStore.ts
|
|
2446
|
+
var import_react2 = __toESM(require_react(), 1);
|
|
2447
|
+
var DEFAULT_COLLAPSED_CATEGORIES = {
|
|
2448
|
+
[UNCATEGORIZED_ID]: false
|
|
2449
|
+
};
|
|
2450
|
+
var FAVORITES_COLLAPSED_STORAGE_KEY = "nolo-sidebar-favorites-collapsed";
|
|
2451
|
+
var readStoredFavoritesCollapsed = () => {
|
|
2452
|
+
try {
|
|
2453
|
+
if (typeof window === "undefined") return false;
|
|
2454
|
+
return window.localStorage.getItem(FAVORITES_COLLAPSED_STORAGE_KEY) === "1";
|
|
2455
|
+
} catch {
|
|
2456
|
+
return false;
|
|
2457
|
+
}
|
|
2458
|
+
};
|
|
2459
|
+
var createInitialState2 = () => ({
|
|
2460
|
+
favoritesCollapsed: readStoredFavoritesCollapsed(),
|
|
2461
|
+
collapsedCategories: {},
|
|
2462
|
+
collapsedCategoriesSpaceId: null
|
|
2463
|
+
});
|
|
2464
|
+
var state2 = createInitialState2();
|
|
2465
|
+
var listeners2 = /* @__PURE__ */ new Set();
|
|
2466
|
+
var version2 = 0;
|
|
2467
|
+
var notify2 = () => {
|
|
2468
|
+
version2 += 1;
|
|
2469
|
+
for (const listener of listeners2) {
|
|
2470
|
+
try {
|
|
2471
|
+
listener();
|
|
2472
|
+
} catch {
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
};
|
|
2476
|
+
function getCollapsedCategories() {
|
|
2477
|
+
return state2.collapsedCategories;
|
|
2478
|
+
}
|
|
2479
|
+
function setCollapsedCategories(categories, spaceId) {
|
|
2480
|
+
state2 = {
|
|
2481
|
+
...state2,
|
|
2482
|
+
collapsedCategories: categories,
|
|
2483
|
+
collapsedCategoriesSpaceId: spaceId
|
|
2484
|
+
};
|
|
2485
|
+
notify2();
|
|
2486
|
+
}
|
|
2487
|
+
function toggleCategoryCollapse(categoryId) {
|
|
2488
|
+
const current = state2.collapsedCategories[categoryId] ?? (DEFAULT_COLLAPSED_CATEGORIES[categoryId] ?? true);
|
|
2489
|
+
state2 = {
|
|
2490
|
+
...state2,
|
|
2491
|
+
collapsedCategories: {
|
|
2492
|
+
...state2.collapsedCategories,
|
|
2493
|
+
[categoryId]: !current
|
|
2494
|
+
}
|
|
2495
|
+
};
|
|
2496
|
+
notify2();
|
|
2497
|
+
}
|
|
2498
|
+
function expandCategoryInCollapsed(categoryId, spaceId) {
|
|
2499
|
+
if (!categoryId || categoryId === UNCATEGORIZED_ID) return;
|
|
2500
|
+
state2 = {
|
|
2501
|
+
...state2,
|
|
2502
|
+
collapsedCategories: {
|
|
2503
|
+
...state2.collapsedCategories,
|
|
2504
|
+
[categoryId]: false
|
|
2505
|
+
}
|
|
2506
|
+
};
|
|
2507
|
+
if (typeof window !== "undefined") {
|
|
2508
|
+
writeStoredCollapsedCategories(
|
|
2509
|
+
spaceId,
|
|
2510
|
+
state2.collapsedCategories,
|
|
2511
|
+
window.localStorage
|
|
2512
|
+
);
|
|
2513
|
+
}
|
|
2514
|
+
notify2();
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2517
|
+
// packages/create/space/content/contentThunks.ts
|
|
2518
|
+
var syncCurrentSpaceIfNormalizedMatch = (spaceId, updatedSpaceData) => {
|
|
2519
|
+
const normalizedSpaceId = normalizeSpaceId(spaceId);
|
|
2520
|
+
const rawSpaceId = getCurrentSpaceIdRaw();
|
|
2521
|
+
const normalizedCurrentSpaceId = rawSpaceId ? normalizeSpaceId(rawSpaceId) : null;
|
|
2522
|
+
if (normalizedCurrentSpaceId === normalizedSpaceId && updatedSpaceData) {
|
|
2523
|
+
updateCurrentSpaceIfMatch(spaceId, updatedSpaceData);
|
|
2524
|
+
}
|
|
2525
|
+
};
|
|
2526
|
+
var addContentToSpace = createAsyncThunk(
|
|
2527
|
+
"space/addContentToSpace",
|
|
2528
|
+
async (input, thunkAPI) => {
|
|
2529
|
+
const result = await addContentAction(input, thunkAPI);
|
|
2530
|
+
const rawCategoryId = asTrimmedString(input.categoryId);
|
|
2531
|
+
const expandCategoryId = rawCategoryId && rawCategoryId !== UNCATEGORIZED_ID ? rawCategoryId : null;
|
|
2532
|
+
if (expandCategoryId) {
|
|
2533
|
+
const collapsedCategories = {
|
|
2534
|
+
...getCollapsedCategories(),
|
|
2535
|
+
[expandCategoryId]: false
|
|
2536
|
+
};
|
|
2537
|
+
if (typeof window !== "undefined") {
|
|
2538
|
+
writeStoredCollapsedCategories(
|
|
2539
|
+
result.spaceId,
|
|
2540
|
+
collapsedCategories,
|
|
2541
|
+
window.localStorage
|
|
2542
|
+
);
|
|
2543
|
+
}
|
|
2544
|
+
expandCategoryInCollapsed(expandCategoryId, result.spaceId);
|
|
2545
|
+
syncCurrentSpaceIfNormalizedMatch(
|
|
2546
|
+
result.spaceId,
|
|
2547
|
+
result.updatedSpaceData
|
|
2548
|
+
);
|
|
2549
|
+
return { ...result, expandCategoryId, collapsedCategories };
|
|
2550
|
+
}
|
|
2551
|
+
syncCurrentSpaceIfNormalizedMatch(result.spaceId, result.updatedSpaceData);
|
|
2552
|
+
return { ...result, expandCategoryId: null };
|
|
2553
|
+
}
|
|
2554
|
+
);
|
|
2555
|
+
var moveContentToSpace = createAsyncThunk(
|
|
2556
|
+
"space/moveContentToSpace",
|
|
2557
|
+
async (arg, thunkAPI) => {
|
|
2558
|
+
const payload = await moveContentAction(arg, thunkAPI);
|
|
2559
|
+
const {
|
|
2560
|
+
sourceSpaceId,
|
|
2561
|
+
updatedSourceSpaceData,
|
|
2562
|
+
targetSpaceId,
|
|
2563
|
+
updatedTargetSpaceData
|
|
2564
|
+
} = payload;
|
|
2565
|
+
if (getCurrentSpaceIdRaw() === sourceSpaceId && updatedSourceSpaceData) {
|
|
2566
|
+
updateCurrentSpaceIfMatch(sourceSpaceId, updatedSourceSpaceData);
|
|
2567
|
+
}
|
|
2568
|
+
if (getCurrentSpaceIdRaw() === targetSpaceId && updatedTargetSpaceData) {
|
|
2569
|
+
updateCurrentSpaceIfMatch(targetSpaceId, updatedTargetSpaceData);
|
|
2570
|
+
}
|
|
2571
|
+
return payload;
|
|
2572
|
+
}
|
|
2573
|
+
);
|
|
2574
|
+
var deleteContentFromSpace = createAsyncThunk(
|
|
2575
|
+
"space/deleteContentFromSpace",
|
|
2576
|
+
async (arg, thunkAPI) => {
|
|
2577
|
+
const payload = await deleteContentFromSpaceAction(arg, thunkAPI);
|
|
2578
|
+
syncCurrentSpaceIfNormalizedMatch(
|
|
2579
|
+
payload.spaceId,
|
|
2580
|
+
payload.updatedSpaceData
|
|
2581
|
+
);
|
|
2582
|
+
return payload;
|
|
2583
|
+
}
|
|
2584
|
+
);
|
|
2585
|
+
var deleteMultipleContent = createAsyncThunk(
|
|
2586
|
+
"space/deleteMultipleContent",
|
|
2587
|
+
async (arg, thunkAPI) => {
|
|
2588
|
+
const payload = await deleteMultipleContentAction(arg, thunkAPI);
|
|
2589
|
+
syncCurrentSpaceIfNormalizedMatch(
|
|
2590
|
+
payload.spaceId,
|
|
2591
|
+
payload.updatedSpaceData
|
|
2592
|
+
);
|
|
2593
|
+
return payload;
|
|
2594
|
+
}
|
|
2595
|
+
);
|
|
2596
|
+
var uploadAndAddFileToSpace = createAsyncThunk(
|
|
2597
|
+
"space/uploadAndAddFileToSpace",
|
|
2598
|
+
async (arg, thunkAPI) => {
|
|
2599
|
+
const payload = await uploadAndAddFileToSpaceAction(arg, thunkAPI);
|
|
2600
|
+
if (getCurrentSpaceIdRaw() === payload.spaceId) {
|
|
2601
|
+
updateCurrentSpaceIfMatch(payload.spaceId, payload.updatedSpaceData);
|
|
2602
|
+
}
|
|
2603
|
+
return payload;
|
|
2604
|
+
}
|
|
2605
|
+
);
|
|
2606
|
+
var restoreSpaceFileVersion = createAsyncThunk(
|
|
2607
|
+
"space/restoreSpaceFileVersion",
|
|
2608
|
+
async (arg, thunkAPI) => {
|
|
2609
|
+
const payload = await restoreSpaceFileVersionAction(arg, thunkAPI);
|
|
2610
|
+
if (getCurrentSpaceIdRaw() === payload.spaceId) {
|
|
2611
|
+
updateCurrentSpaceIfMatch(payload.spaceId, payload.updatedSpaceData);
|
|
2612
|
+
}
|
|
2613
|
+
return payload;
|
|
2614
|
+
}
|
|
2615
|
+
);
|
|
2616
|
+
var updateContentTitle = createAsyncThunk(
|
|
2617
|
+
"space/updateContentTitle",
|
|
2618
|
+
async (arg, thunkAPI) => {
|
|
2619
|
+
try {
|
|
2620
|
+
const payload = await updateContentTitleAction(arg, thunkAPI);
|
|
2621
|
+
if (getCurrentSpaceIdRaw() === payload.spaceId) {
|
|
2622
|
+
updateCurrentSpaceIfMatch(payload.spaceId, payload.updatedSpaceData);
|
|
2623
|
+
}
|
|
2624
|
+
return payload;
|
|
2625
|
+
} catch (error) {
|
|
2626
|
+
const message = error?.message || "\u6807\u9898\u4FDD\u5B58\u5931\u8D25";
|
|
2627
|
+
if (!message.includes("\u65E0\u6CD5\u52A0\u8F7D\u7A7A\u95F4\u6570\u636E")) {
|
|
2628
|
+
toast.error(message);
|
|
2629
|
+
}
|
|
2630
|
+
throw error;
|
|
2631
|
+
}
|
|
2632
|
+
}
|
|
2633
|
+
);
|
|
2634
|
+
var updateContentPinned = createAsyncThunk(
|
|
2635
|
+
"space/updateContentPinned",
|
|
2636
|
+
async (arg, thunkAPI) => {
|
|
2637
|
+
try {
|
|
2638
|
+
const payload = await updateContentPinnedAction(arg, thunkAPI);
|
|
2639
|
+
if (payload.updatedSpaceData && payload.spaceId && getCurrentSpaceIdRaw() === payload.spaceId) {
|
|
2640
|
+
updateCurrentSpaceIfMatch(payload.spaceId, payload.updatedSpaceData);
|
|
2641
|
+
}
|
|
2642
|
+
return payload;
|
|
2643
|
+
} catch (error) {
|
|
2644
|
+
toast.error(
|
|
2645
|
+
error?.message || "\u7F6E\u9876\u72B6\u6001\u66F4\u65B0\u5931\u8D25"
|
|
2646
|
+
);
|
|
2647
|
+
throw error;
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
);
|
|
2651
|
+
var updateContentCategory = createAsyncThunk(
|
|
2652
|
+
"space/updateContentCategory",
|
|
2653
|
+
async (arg, thunkAPI) => {
|
|
2654
|
+
const payload = await updateContentCategoryAction(arg, thunkAPI);
|
|
2655
|
+
if (getCurrentSpaceIdRaw() === payload.spaceId) {
|
|
2656
|
+
updateCurrentSpaceIfMatch(payload.spaceId, payload.updatedSpaceData);
|
|
2657
|
+
}
|
|
2658
|
+
return payload;
|
|
2659
|
+
}
|
|
2660
|
+
);
|
|
2661
|
+
|
|
2662
|
+
export {
|
|
2663
|
+
fetchAndCacheTableRows,
|
|
2664
|
+
getTableState,
|
|
2665
|
+
loadTableRows,
|
|
2666
|
+
addRow,
|
|
2667
|
+
createTable,
|
|
2668
|
+
deleteTable,
|
|
2669
|
+
readStoredCollapsedCategories,
|
|
2670
|
+
writeStoredCollapsedCategories,
|
|
2671
|
+
DEFAULT_COLLAPSED_CATEGORIES,
|
|
2672
|
+
getCollapsedCategories,
|
|
2673
|
+
setCollapsedCategories,
|
|
2674
|
+
toggleCategoryCollapse,
|
|
2675
|
+
expandCategoryInCollapsed,
|
|
2676
|
+
addContentToSpace,
|
|
2677
|
+
moveContentToSpace,
|
|
2678
|
+
deleteContentFromSpace,
|
|
2679
|
+
deleteMultipleContent,
|
|
2680
|
+
uploadAndAddFileToSpace,
|
|
2681
|
+
restoreSpaceFileVersion,
|
|
2682
|
+
updateContentTitle,
|
|
2683
|
+
updateContentPinned,
|
|
2684
|
+
updateContentCategory
|
|
2685
|
+
};
|