@xnetjs/react 1.0.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  useInfiniteQuery,
6
6
  useIsOffline,
7
7
  useNode
8
- } from "./chunk-Q3NDLI6Z.js";
8
+ } from "./chunk-VZMZQTR7.js";
9
9
  import {
10
10
  useCell,
11
11
  useDatabase,
@@ -14,7 +14,7 @@ import {
14
14
  useDatabaseSchema,
15
15
  useRelatedRows,
16
16
  useReverseRelations
17
- } from "./chunk-WL7OCTSM.js";
17
+ } from "./chunk-HTFAHJQK.js";
18
18
  import {
19
19
  AuthErrorScreen,
20
20
  AuthenticatingScreen,
@@ -94,7 +94,7 @@ import {
94
94
  useUndo,
95
95
  useVerification,
96
96
  useVisibleComments
97
- } from "./chunk-MEIYFD6R.js";
97
+ } from "./chunk-ANCTAEML.js";
98
98
  import {
99
99
  EMPTY_PAGE_INFO,
100
100
  computeFallbackPageInfo,
@@ -106,10 +106,10 @@ import {
106
106
  useMutate,
107
107
  useQuery,
108
108
  useSyncManager
109
- } from "./chunk-EJEGIEIC.js";
109
+ } from "./chunk-ZGP56IIJ.js";
110
110
  import {
111
111
  useUndoScope
112
- } from "./chunk-BDBDZXYS.js";
112
+ } from "./chunk-SZQNGRTO.js";
113
113
  import {
114
114
  InstrumentationContext,
115
115
  useInstrumentation
@@ -122,6 +122,7 @@ import {
122
122
  TracingContext,
123
123
  XNetContext,
124
124
  XNetProvider,
125
+ mergeEditorContributions,
125
126
  useCommand,
126
127
  useCommands,
127
128
  useContributions,
@@ -129,6 +130,7 @@ import {
129
130
  useEditorExtensions,
130
131
  useEditorExtensionsSafe,
131
132
  useImporters,
133
+ useMergedEditorContributions,
132
134
  useNodeStore,
133
135
  usePluginRegistry,
134
136
  usePluginRegistryOptional,
@@ -142,7 +144,7 @@ import {
142
144
  useView,
143
145
  useViews,
144
146
  useXNet
145
- } from "./chunk-RIN2MXZK.js";
147
+ } from "./chunk-H44ZSIH4.js";
146
148
 
147
149
  // src/hooks/useEffectiveSchema.ts
148
150
  import {
@@ -4929,8 +4931,531 @@ function useGridDatabase(databaseId, options = {}) {
4929
4931
  };
4930
4932
  }
4931
4933
 
4934
+ // src/hooks/useTimeMachine.ts
4935
+ import { CHECKPOINT_SCHEMA_IRI } from "@xnetjs/data";
4936
+ import {
4937
+ HistoryEngine,
4938
+ HistoryHorizonError,
4939
+ MemorySnapshotStorage,
4940
+ ScopeTimeline,
4941
+ SnapshotCache,
4942
+ createCheckpoint,
4943
+ listCheckpoints,
4944
+ restoreToFrontier
4945
+ } from "@xnetjs/history";
4946
+ import { useCallback as useCallback4, useEffect as useEffect7, useMemo as useMemo6, useRef as useRef5, useState as useState7 } from "react";
4947
+ function useTimeMachine(nodeId, options) {
4948
+ const { store, isReady } = useNodeStore();
4949
+ const memberKey = options?.memberIds?.join(",") ?? "";
4950
+ const members = useMemo6(() => {
4951
+ if (!nodeId) return [];
4952
+ const extra = options?.memberIds ?? [];
4953
+ return [nodeId, ...extra.filter((id) => id !== nodeId)];
4954
+ }, [nodeId, memberKey]);
4955
+ const [timeline, setTimeline] = useState7([]);
4956
+ const [checkpoints, setCheckpoints] = useState7([]);
4957
+ const [horizon, setHorizon] = useState7(null);
4958
+ const [docSnapshotCount, setDocSnapshotCount] = useState7(null);
4959
+ const [loading, setLoading] = useState7(false);
4960
+ const [error, setError] = useState7(null);
4961
+ const [rawPosition, setRawPosition] = useState7(null);
4962
+ const position = rawPosition === null ? Math.max(0, timeline.length - 1) : Math.max(0, Math.min(rawPosition, timeline.length - 1));
4963
+ const atLatest = timeline.length === 0 || position === timeline.length - 1;
4964
+ const [preview, setPreview] = useState7(null);
4965
+ const [diffs, setDiffs] = useState7([]);
4966
+ const engineRef = useRef5(null);
4967
+ const getEngines = useCallback4(() => {
4968
+ if (!store) return null;
4969
+ const storage = store.getStorageAdapter();
4970
+ if (!storage) return null;
4971
+ if (!engineRef.current || engineRef.current.storage !== storage) {
4972
+ const snapshots = new SnapshotCache(new MemorySnapshotStorage(), { interval: 50 });
4973
+ engineRef.current = {
4974
+ engine: new HistoryEngine(storage, snapshots),
4975
+ scope: new ScopeTimeline(storage),
4976
+ storage
4977
+ };
4978
+ }
4979
+ return engineRef.current;
4980
+ }, [store]);
4981
+ const reload = useCallback4(async () => {
4982
+ if (!nodeId || !isReady || !store) return;
4983
+ const engines = getEngines();
4984
+ if (!engines) return;
4985
+ setLoading(true);
4986
+ setError(null);
4987
+ try {
4988
+ const [line, nodeHorizon, named] = await Promise.all([
4989
+ engines.scope.getMergedTimeline(members),
4990
+ engines.engine.getHorizon(nodeId),
4991
+ listCheckpoints(store, nodeId)
4992
+ ]);
4993
+ setTimeline(line);
4994
+ setHorizon(nodeHorizon);
4995
+ setCheckpoints(named);
4996
+ const snapshotStorage = engines.storage;
4997
+ if (typeof snapshotStorage.getYjsSnapshots === "function") {
4998
+ const snapshots = await snapshotStorage.getYjsSnapshots(nodeId);
4999
+ setDocSnapshotCount(snapshots.length);
5000
+ } else {
5001
+ setDocSnapshotCount(null);
5002
+ }
5003
+ } catch (err) {
5004
+ setError(err instanceof Error ? err : new Error(String(err)));
5005
+ } finally {
5006
+ setLoading(false);
5007
+ }
5008
+ }, [nodeId, isReady, store, members, getEngines]);
5009
+ useEffect7(() => {
5010
+ setRawPosition(null);
5011
+ void reload();
5012
+ }, [reload]);
5013
+ useEffect7(() => {
5014
+ if (!store || !nodeId) return;
5015
+ const memberSet = new Set(members);
5016
+ const unsub = store.subscribe((event) => {
5017
+ if (memberSet.has(event.change.payload.nodeId) || event.change.payload.schemaId === CHECKPOINT_SCHEMA_IRI) {
5018
+ void reload();
5019
+ }
5020
+ });
5021
+ return unsub;
5022
+ }, [store, nodeId, members, reload]);
5023
+ useEffect7(() => {
5024
+ let cancelled = false;
5025
+ const engines = getEngines();
5026
+ if (!nodeId || !engines || timeline.length === 0) {
5027
+ setPreview(null);
5028
+ setDiffs([]);
5029
+ return;
5030
+ }
5031
+ let hash = null;
5032
+ for (let i = position; i >= 0; i--) {
5033
+ if (timeline[i].nodeId === nodeId) {
5034
+ hash = timeline[i].change.hash;
5035
+ break;
5036
+ }
5037
+ }
5038
+ if (!hash) {
5039
+ setPreview(null);
5040
+ setDiffs([]);
5041
+ return;
5042
+ }
5043
+ const target = { type: "hash", hash };
5044
+ Promise.all([
5045
+ engines.engine.materializeAt(nodeId, target),
5046
+ engines.engine.diff(nodeId, target, { type: "latest" })
5047
+ ]).then(([state, propertyDiffs]) => {
5048
+ if (cancelled) return;
5049
+ setPreview(state.node);
5050
+ setDiffs(propertyDiffs);
5051
+ }).catch((err) => {
5052
+ if (cancelled) return;
5053
+ setPreview(null);
5054
+ setDiffs([]);
5055
+ if (err instanceof HistoryHorizonError) {
5056
+ setHorizon(err.horizon);
5057
+ } else {
5058
+ setError(err instanceof Error ? err : new Error(String(err)));
5059
+ }
5060
+ });
5061
+ return () => {
5062
+ cancelled = true;
5063
+ };
5064
+ }, [nodeId, timeline, position, getEngines]);
5065
+ const setPosition = useCallback4(
5066
+ (index) => {
5067
+ const max = timeline.length - 1;
5068
+ const clamped = Math.max(0, Math.min(index, max));
5069
+ setRawPosition(clamped >= max ? null : clamped);
5070
+ },
5071
+ [timeline.length]
5072
+ );
5073
+ const stepBack = useCallback4(() => setPosition(position - 1), [setPosition, position]);
5074
+ const stepForward = useCallback4(() => setPosition(position + 1), [setPosition, position]);
5075
+ const goToLatest = useCallback4(() => setRawPosition(null), []);
5076
+ const createNamedVersion = useCallback4(
5077
+ async (name, note) => {
5078
+ if (!nodeId || !store) return null;
5079
+ const engines = getEngines();
5080
+ if (!engines) return null;
5081
+ try {
5082
+ const checkpoint = await createCheckpoint(store, engines.storage, {
5083
+ name,
5084
+ ...note !== void 0 ? { note } : {},
5085
+ nodeIds: members,
5086
+ scopeId: nodeId
5087
+ });
5088
+ await reload();
5089
+ return checkpoint;
5090
+ } catch (err) {
5091
+ setError(err instanceof Error ? err : new Error(String(err)));
5092
+ return null;
5093
+ }
5094
+ },
5095
+ [nodeId, store, members, getEngines, reload]
5096
+ );
5097
+ const positionOfCheckpoint = useCallback4(
5098
+ (checkpoint) => {
5099
+ const frontier = checkpoint.properties.frontier;
5100
+ if (!frontier) return null;
5101
+ let best = -1;
5102
+ for (let i = 0; i < timeline.length; i++) {
5103
+ const pinned = frontier[timeline[i].nodeId];
5104
+ if (pinned && pinned.hash === timeline[i].change.hash) {
5105
+ best = Math.max(best, i);
5106
+ }
5107
+ }
5108
+ return best >= 0 ? best : null;
5109
+ },
5110
+ [timeline]
5111
+ );
5112
+ const frontierAt = useCallback4(
5113
+ (index) => {
5114
+ const scope = getEngines()?.scope;
5115
+ if (!scope) return {};
5116
+ return scope.frontierAtPosition(timeline, index);
5117
+ },
5118
+ [getEngines, timeline]
5119
+ );
5120
+ const restore = useCallback4(async () => {
5121
+ if (!nodeId || !store || timeline.length === 0) return null;
5122
+ const engines = getEngines();
5123
+ if (!engines) return null;
5124
+ try {
5125
+ const frontier = engines.scope.frontierAtPosition(timeline, position);
5126
+ const result = await restoreToFrontier(store, engines.engine, frontier, members);
5127
+ setRawPosition(null);
5128
+ await reload();
5129
+ return result;
5130
+ } catch (err) {
5131
+ if (err instanceof HistoryHorizonError) setHorizon(err.horizon);
5132
+ setError(err instanceof Error ? err : new Error(String(err)));
5133
+ return null;
5134
+ }
5135
+ }, [nodeId, store, timeline, position, members, getEngines, reload]);
5136
+ return {
5137
+ timeline,
5138
+ changeCount: timeline.length,
5139
+ position,
5140
+ atLatest,
5141
+ setPosition,
5142
+ stepBack,
5143
+ stepForward,
5144
+ goToLatest,
5145
+ preview,
5146
+ diffs,
5147
+ horizon,
5148
+ docSnapshotCount,
5149
+ checkpoints,
5150
+ createNamedVersion,
5151
+ positionOfCheckpoint,
5152
+ frontierAt,
5153
+ restore,
5154
+ loading,
5155
+ error,
5156
+ reload
5157
+ };
5158
+ }
5159
+
5160
+ // src/hooks/useDraft.ts
5161
+ import { DRAFT_SCHEMA_IRI, DatabaseRowSchema as DatabaseRowSchema2, DatabaseSchema as DatabaseSchema2 } from "@xnetjs/data";
5162
+ import {
5163
+ HistoryEngine as HistoryEngine2,
5164
+ MemorySnapshotStorage as MemorySnapshotStorage2,
5165
+ SnapshotCache as SnapshotCache2,
5166
+ createDraft as createDraftNode,
5167
+ discardDraft,
5168
+ draftEntries,
5169
+ forkNodeIntoDraft,
5170
+ listDrafts,
5171
+ mergeDraft,
5172
+ refreshDraftFromMain,
5173
+ threeWayPropertyMerge
5174
+ } from "@xnetjs/history";
5175
+ import { useCallback as useCallback5, useEffect as useEffect8, useRef as useRef6, useState as useState8 } from "react";
5176
+ var baseIRI = (schemaId) => schemaId.split("@")[0];
5177
+ async function resolveMembers(store, hostId) {
5178
+ const members = [hostId];
5179
+ const host = await store.getRaw(hostId);
5180
+ if (host && baseIRI(host.schemaId) === baseIRI(DatabaseSchema2.schema["@id"])) {
5181
+ const rows = await store.list({ schemaId: DatabaseRowSchema2.schema["@id"] });
5182
+ for (const row of rows) {
5183
+ if (row.properties.database === hostId) members.push(row.id);
5184
+ }
5185
+ }
5186
+ return members;
5187
+ }
5188
+ function clonesFromEntries(draft) {
5189
+ const clones = {};
5190
+ for (const [originalId, entry] of Object.entries(draftEntries(draft))) {
5191
+ clones[originalId] = entry.cloneId;
5192
+ }
5193
+ return clones;
5194
+ }
5195
+ function useDraft(hostId) {
5196
+ const { store, isReady } = useNodeStore();
5197
+ const [drafts, setDrafts] = useState8([]);
5198
+ const [checkedOut, setCheckedOut] = useState8(null);
5199
+ const [loading, setLoading] = useState8(false);
5200
+ const [error, setError] = useState8(null);
5201
+ const engineRef = useRef6(null);
5202
+ const getEngines = useCallback5(() => {
5203
+ if (!store) return null;
5204
+ const storage = store.getStorageAdapter();
5205
+ if (!storage) return null;
5206
+ if (!engineRef.current || engineRef.current.storage !== storage) {
5207
+ engineRef.current = {
5208
+ engine: new HistoryEngine2(
5209
+ storage,
5210
+ new SnapshotCache2(new MemorySnapshotStorage2(), { interval: 50 })
5211
+ ),
5212
+ storage
5213
+ };
5214
+ }
5215
+ return engineRef.current;
5216
+ }, [store]);
5217
+ const draftIdsRef = useRef6(/* @__PURE__ */ new Set());
5218
+ const reload = useCallback5(async () => {
5219
+ if (!hostId || !isReady || !store) return;
5220
+ setLoading(true);
5221
+ setError(null);
5222
+ try {
5223
+ const open = await listDrafts(store, hostId);
5224
+ setDrafts(open);
5225
+ for (const draft of open) draftIdsRef.current.add(draft.id);
5226
+ const overlay = store.getCheckedOutDraft();
5227
+ if (overlay) {
5228
+ const draftNode = await store.getRaw(overlay.draftId);
5229
+ setCheckedOut(
5230
+ draftNode && draftNode.properties.status === "open" && draftNode.properties.target === hostId ? draftNode : null
5231
+ );
5232
+ } else {
5233
+ setCheckedOut(null);
5234
+ }
5235
+ } catch (err) {
5236
+ setError(err instanceof Error ? err : new Error(String(err)));
5237
+ } finally {
5238
+ setLoading(false);
5239
+ }
5240
+ }, [hostId, isReady, store]);
5241
+ useEffect8(() => {
5242
+ void reload();
5243
+ }, [reload]);
5244
+ useEffect8(() => {
5245
+ if (!store || !hostId) return;
5246
+ const unsubStore = store.subscribe((event) => {
5247
+ if (event.change.payload.schemaId === DRAFT_SCHEMA_IRI || draftIdsRef.current.has(event.change.payload.nodeId)) {
5248
+ void reload();
5249
+ }
5250
+ });
5251
+ const unsubOverlay = store.subscribeToDraftOverlay(() => void reload());
5252
+ return () => {
5253
+ unsubStore();
5254
+ unsubOverlay();
5255
+ };
5256
+ }, [store, hostId, reload]);
5257
+ const checkout = useCallback5(
5258
+ async (draftId) => {
5259
+ if (!hostId || !store) return false;
5260
+ const engines = getEngines();
5261
+ if (!engines) return false;
5262
+ try {
5263
+ const draft = await store.getRaw(draftId);
5264
+ if (!draft || draft.properties.status !== "open") return false;
5265
+ const members = await resolveMembers(store, hostId);
5266
+ store.setCheckedOutDraft({
5267
+ draftId,
5268
+ members,
5269
+ clones: clonesFromEntries(draft),
5270
+ // Lazy copy-on-write: the first write to an unforked member clones
5271
+ // it. Returning null declines — the write then targets the
5272
+ // original (never-fork schemas stay live by policy).
5273
+ onMissingMember: async (originalId) => {
5274
+ try {
5275
+ const entry = await forkNodeIntoDraft(store, engines.storage, draftId, originalId);
5276
+ return entry.cloneId;
5277
+ } catch {
5278
+ return null;
5279
+ }
5280
+ }
5281
+ });
5282
+ await reload();
5283
+ return true;
5284
+ } catch (err) {
5285
+ setError(err instanceof Error ? err : new Error(String(err)));
5286
+ return false;
5287
+ }
5288
+ },
5289
+ [hostId, store, getEngines, reload]
5290
+ );
5291
+ const returnToMain = useCallback5(() => {
5292
+ if (!store) return;
5293
+ store.setCheckedOutDraft(null);
5294
+ }, [store]);
5295
+ const createDraft = useCallback5(
5296
+ async (name) => {
5297
+ if (!hostId || !store) return null;
5298
+ try {
5299
+ const draft = await createDraftNode(store, { name, targetId: hostId });
5300
+ await checkout(draft.id);
5301
+ return draft;
5302
+ } catch (err) {
5303
+ setError(err instanceof Error ? err : new Error(String(err)));
5304
+ return null;
5305
+ }
5306
+ },
5307
+ [hostId, store, checkout]
5308
+ );
5309
+ const discard = useCallback5(
5310
+ async (draftId) => {
5311
+ if (!store) return false;
5312
+ const engines = getEngines();
5313
+ if (!engines) return false;
5314
+ try {
5315
+ if (store.getCheckedOutDraft()?.draftId === draftId) {
5316
+ store.setCheckedOutDraft(null);
5317
+ }
5318
+ await discardDraft(store, engines.storage, draftId);
5319
+ await reload();
5320
+ return true;
5321
+ } catch (err) {
5322
+ setError(err instanceof Error ? err : new Error(String(err)));
5323
+ return false;
5324
+ }
5325
+ },
5326
+ [store, getEngines, reload]
5327
+ );
5328
+ const merge = useCallback5(
5329
+ async (draftId) => {
5330
+ if (!store) return null;
5331
+ const engines = getEngines();
5332
+ if (!engines) return null;
5333
+ try {
5334
+ const result = await mergeDraft(store, engines.storage, engines.engine, draftId);
5335
+ if (result.status === "merged" && store.getCheckedOutDraft()?.draftId === draftId) {
5336
+ store.setCheckedOutDraft(null);
5337
+ }
5338
+ await reload();
5339
+ return result;
5340
+ } catch (err) {
5341
+ setError(err instanceof Error ? err : new Error(String(err)));
5342
+ return null;
5343
+ }
5344
+ },
5345
+ [store, getEngines, reload]
5346
+ );
5347
+ const refresh = useCallback5(
5348
+ async (draftId) => {
5349
+ if (!store) return null;
5350
+ const engines = getEngines();
5351
+ if (!engines) return null;
5352
+ try {
5353
+ const result = await refreshDraftFromMain(store, engines.storage, engines.engine, draftId);
5354
+ await reload();
5355
+ return result;
5356
+ } catch (err) {
5357
+ setError(err instanceof Error ? err : new Error(String(err)));
5358
+ return null;
5359
+ }
5360
+ },
5361
+ [store, getEngines, reload]
5362
+ );
5363
+ const setReviewRequested = useCallback5(
5364
+ async (draftId, requested) => {
5365
+ if (!store) return;
5366
+ try {
5367
+ await store.update(draftId, { properties: { reviewRequested: requested } });
5368
+ } catch (err) {
5369
+ setError(err instanceof Error ? err : new Error(String(err)));
5370
+ }
5371
+ },
5372
+ [store]
5373
+ );
5374
+ const computeReview = useCallback5(
5375
+ async (draftId) => {
5376
+ if (!store) return null;
5377
+ const engines = getEngines();
5378
+ if (!engines) return null;
5379
+ try {
5380
+ const draft = await store.getRaw(draftId);
5381
+ if (!draft) return null;
5382
+ const cards = [];
5383
+ const members = [];
5384
+ for (const [id, entry] of Object.entries(draftEntries(draft))) {
5385
+ const originalId = id;
5386
+ const cloneId = entry.cloneId;
5387
+ const [base, ours, theirs] = await Promise.all([
5388
+ engines.engine.materializeAt(originalId, {
5389
+ type: "hash",
5390
+ hash: entry.forkedAtHash
5391
+ }),
5392
+ store.getRaw(originalId),
5393
+ store.getRaw(cloneId)
5394
+ ]);
5395
+ if (!ours || !theirs) continue;
5396
+ const result = threeWayPropertyMerge(
5397
+ base.node.properties,
5398
+ ours.properties,
5399
+ theirs.properties
5400
+ );
5401
+ for (const [property, draftValue] of Object.entries(result.patch)) {
5402
+ cards.push({
5403
+ originalId,
5404
+ property,
5405
+ base: base.node.properties[property],
5406
+ main: ours.properties[property],
5407
+ draft: draftValue,
5408
+ conflict: false
5409
+ });
5410
+ }
5411
+ for (const conflict of result.conflicts) {
5412
+ cards.push({
5413
+ originalId,
5414
+ property: conflict.property,
5415
+ base: conflict.base,
5416
+ main: conflict.ours,
5417
+ draft: conflict.theirs,
5418
+ conflict: true
5419
+ });
5420
+ }
5421
+ const hasDocument = Boolean(entry.forkedAtYjsStateVector);
5422
+ let documentDiffers = false;
5423
+ if (hasDocument) {
5424
+ const [cloneBlob, mainBlob] = await Promise.all([
5425
+ engines.storage.getDocumentContent(cloneId),
5426
+ engines.storage.getDocumentContent(originalId)
5427
+ ]);
5428
+ documentDiffers = (cloneBlob?.length ?? 0) !== (mainBlob?.length ?? 0);
5429
+ }
5430
+ members.push({ originalId, cloneId, hasDocument, documentDiffers });
5431
+ }
5432
+ return { cards, members };
5433
+ } catch (err) {
5434
+ setError(err instanceof Error ? err : new Error(String(err)));
5435
+ return null;
5436
+ }
5437
+ },
5438
+ [store, getEngines]
5439
+ );
5440
+ return {
5441
+ drafts,
5442
+ checkedOut,
5443
+ createDraft,
5444
+ checkout,
5445
+ returnToMain,
5446
+ discard,
5447
+ merge,
5448
+ refresh,
5449
+ setReviewRequested,
5450
+ computeReview,
5451
+ loading,
5452
+ error,
5453
+ reload
5454
+ };
5455
+ }
5456
+
4932
5457
  // src/hooks/useGlobalUndo.ts
4933
- import { useCallback as useCallback4, useContext, useEffect as useEffect7, useReducer } from "react";
5458
+ import { useCallback as useCallback6, useContext, useEffect as useEffect9, useReducer } from "react";
4934
5459
  function readUndoContext(ctx) {
4935
5460
  if (!ctx) return { undoManager: null, nodeStore: null };
4936
5461
  return { undoManager: ctx.undoManager, nodeStore: ctx.nodeStore };
@@ -4939,17 +5464,17 @@ function useGlobalUndo() {
4939
5464
  const ctx = useContext(XNetContext);
4940
5465
  const { undoManager, nodeStore } = readUndoContext(ctx);
4941
5466
  const [, bump] = useReducer((n) => n + 1, 0);
4942
- useEffect7(() => {
5467
+ useEffect9(() => {
4943
5468
  if (!nodeStore) return;
4944
5469
  return nodeStore.subscribe(() => bump());
4945
5470
  }, [nodeStore]);
4946
- const undo = useCallback4(async () => {
5471
+ const undo = useCallback6(async () => {
4947
5472
  if (!undoManager) return false;
4948
5473
  const ok = await undoManager.undoLatest();
4949
5474
  bump();
4950
5475
  return ok;
4951
5476
  }, [undoManager]);
4952
- const redo = useCallback4(async () => {
5477
+ const redo = useCallback6(async () => {
4953
5478
  if (!undoManager) return false;
4954
5479
  const ok = await undoManager.redoLatest();
4955
5480
  bump();
@@ -4964,7 +5489,7 @@ function useGlobalUndo() {
4964
5489
  }
4965
5490
 
4966
5491
  // src/hooks/useCanCreate.ts
4967
- import { useEffect as useEffect8, useMemo as useMemo6, useRef as useRef5, useState as useState7 } from "react";
5492
+ import { useEffect as useEffect10, useMemo as useMemo7, useRef as useRef7, useState as useState9 } from "react";
4968
5493
  var INITIAL_STATE = {
4969
5494
  canCreate: false,
4970
5495
  loading: true,
@@ -4972,10 +5497,10 @@ var INITIAL_STATE = {
4972
5497
  };
4973
5498
  function useCanCreate(schemaId, properties) {
4974
5499
  const { store, isReady } = useNodeStore();
4975
- const [state, setState] = useState7(INITIAL_STATE);
4976
- const requestRef = useRef5(0);
4977
- const propertiesKey = useMemo6(() => JSON.stringify(properties ?? {}), [properties]);
4978
- useEffect8(() => {
5500
+ const [state, setState] = useState9(INITIAL_STATE);
5501
+ const requestRef = useRef7(0);
5502
+ const propertiesKey = useMemo7(() => JSON.stringify(properties ?? {}), [properties]);
5503
+ useEffect10(() => {
4979
5504
  if (!store || !isReady) {
4980
5505
  setState((prev) => ({ ...prev, loading: true }));
4981
5506
  return;
@@ -5044,7 +5569,7 @@ import {
5044
5569
  } from "@xnetjs/runtime";
5045
5570
 
5046
5571
  // src/hooks/useBilling.ts
5047
- import { useCallback as useCallback5, useContext as useContext2, useEffect as useEffect9, useMemo as useMemo7, useState as useState8 } from "react";
5572
+ import { useCallback as useCallback7, useContext as useContext2, useEffect as useEffect11, useMemo as useMemo8, useState as useState10 } from "react";
5048
5573
  var toHttpUrl = (hubUrl) => {
5049
5574
  try {
5050
5575
  const url = new URL(hubUrl);
@@ -5081,20 +5606,20 @@ function deriveBilling(state) {
5081
5606
  }
5082
5607
  function useBilling() {
5083
5608
  const context = useContext2(XNetContext);
5084
- const [state, setState] = useState8(null);
5085
- const [loading, setLoading] = useState8(
5609
+ const [state, setState] = useState10(null);
5610
+ const [loading, setLoading] = useState10(
5086
5611
  Boolean(context?.hubUrl || context?.billing?.apiBase)
5087
5612
  );
5088
- const [error, setError] = useState8(null);
5089
- const apiBase = useMemo7(() => {
5613
+ const [error, setError] = useState10(null);
5614
+ const apiBase = useMemo8(() => {
5090
5615
  if (context?.billing?.apiBase) return context.billing.apiBase.replace(/\/$/, "");
5091
5616
  return context?.hubUrl ? toHttpUrl(context.hubUrl) : null;
5092
5617
  }, [context?.billing?.apiBase, context?.hubUrl]);
5093
- const authHeaders = useCallback5(async () => {
5618
+ const authHeaders = useCallback7(async () => {
5094
5619
  const token = context?.getHubAuthToken ? await context.getHubAuthToken() : "";
5095
5620
  return token ? { Authorization: `Bearer ${token}` } : {};
5096
5621
  }, [context]);
5097
- const reload = useCallback5(async () => {
5622
+ const reload = useCallback7(async () => {
5098
5623
  if (!apiBase) {
5099
5624
  setState(null);
5100
5625
  setLoading(false);
@@ -5112,10 +5637,10 @@ function useBilling() {
5112
5637
  setLoading(false);
5113
5638
  }
5114
5639
  }, [apiBase, authHeaders]);
5115
- useEffect9(() => {
5640
+ useEffect11(() => {
5116
5641
  void reload();
5117
5642
  }, [reload]);
5118
- const redirectVia = useCallback5(
5643
+ const redirectVia = useCallback7(
5119
5644
  async (path, body) => {
5120
5645
  if (!apiBase) throw new Error("Hub URL not configured");
5121
5646
  const res = await fetch(`${apiBase}${path}`, {
@@ -5129,11 +5654,11 @@ function useBilling() {
5129
5654
  },
5130
5655
  [apiBase, authHeaders]
5131
5656
  );
5132
- const openCheckout = useCallback5(
5657
+ const openCheckout = useCallback7(
5133
5658
  (priceRef, options = {}) => redirectVia("/billing/checkout", buildCheckoutBody(priceRef, options)),
5134
5659
  [redirectVia]
5135
5660
  );
5136
- const openPortal = useCallback5(
5661
+ const openPortal = useCallback7(
5137
5662
  (returnUrl) => redirectVia("/billing/portal", returnUrl ? { returnUrl } : {}),
5138
5663
  [redirectVia]
5139
5664
  );
@@ -5239,6 +5764,7 @@ export {
5239
5764
  isReactionVisible,
5240
5765
  isSavedViewVisualPreviewEmbeddable,
5241
5766
  isTaskOverdue,
5767
+ mergeEditorContributions,
5242
5768
  mergeSavedViewFeedEnrichment,
5243
5769
  moderateThread,
5244
5770
  onboardingReducer,
@@ -5274,6 +5800,7 @@ export {
5274
5800
  useDatabaseSchema,
5275
5801
  useDemoMode,
5276
5802
  useDiff,
5803
+ useDraft,
5277
5804
  useEditorExtensions,
5278
5805
  useEditorExtensionsSafe,
5279
5806
  useEffectiveSchema,
@@ -5290,6 +5817,7 @@ export {
5290
5817
  useInfiniteQuery,
5291
5818
  useInstrumentation,
5292
5819
  useIsOffline,
5820
+ useMergedEditorContributions,
5293
5821
  useMessageRequests,
5294
5822
  useModeratedThread,
5295
5823
  useMutate,
@@ -5317,6 +5845,7 @@ export {
5317
5845
  useTaskProjectionSync,
5318
5846
  useTasks,
5319
5847
  useTelemetryReporter,
5848
+ useTimeMachine,
5320
5849
  useTracingReporter,
5321
5850
  useUndo,
5322
5851
  useVerification,