@nice2dev/erp-adapter 0.1.0 → 1.0.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/CHANGELOG.md +48 -1
- package/dist/ErpDataAdapter.d.ts +9 -0
- package/dist/ErpDataAdapter.d.ts.map +1 -1
- package/dist/ErpFileAdapter.d.ts +20 -0
- package/dist/ErpFileAdapter.d.ts.map +1 -1
- package/dist/ErpOfflineQueue.d.ts +66 -0
- package/dist/ErpOfflineQueue.d.ts.map +1 -0
- package/dist/ErpOptimisticStore.d.ts +41 -0
- package/dist/ErpOptimisticStore.d.ts.map +1 -0
- package/dist/ErpRateLimiter.d.ts +42 -0
- package/dist/ErpRateLimiter.d.ts.map +1 -0
- package/dist/__tests__/ErpAuthAdapter.test.d.ts +2 -0
- package/dist/__tests__/ErpAuthAdapter.test.d.ts.map +1 -0
- package/dist/__tests__/ErpDataAdapter.test.d.ts +2 -0
- package/dist/__tests__/ErpDataAdapter.test.d.ts.map +1 -0
- package/dist/__tests__/ErpExportAdapter.test.d.ts +2 -0
- package/dist/__tests__/ErpExportAdapter.test.d.ts.map +1 -0
- package/dist/__tests__/ErpFileAdapter.test.d.ts +2 -0
- package/dist/__tests__/ErpFileAdapter.test.d.ts.map +1 -0
- package/dist/__tests__/ErpOfflineQueue.test.d.ts +2 -0
- package/dist/__tests__/ErpOfflineQueue.test.d.ts.map +1 -0
- package/dist/__tests__/ErpOptimisticStore.test.d.ts +2 -0
- package/dist/__tests__/ErpOptimisticStore.test.d.ts.map +1 -0
- package/dist/__tests__/ErpRateLimiter.test.d.ts +2 -0
- package/dist/__tests__/ErpRateLimiter.test.d.ts.map +1 -0
- package/dist/__tests__/middleware.test.d.ts +2 -0
- package/dist/__tests__/middleware.test.d.ts.map +1 -0
- package/dist/__tests__/registries.test.d.ts +2 -0
- package/dist/__tests__/registries.test.d.ts.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +1249 -113
- package/dist/index.mjs.map +1 -1
- package/dist/middleware.d.ts +44 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/useCollaborativeDataGrid.d.ts +132 -0
- package/dist/useCollaborativeDataGrid.d.ts.map +1 -0
- package/dist/useEntityPresence.d.ts +136 -0
- package/dist/useEntityPresence.d.ts.map +1 -0
- package/dist/useSignalRLiveData.d.ts +121 -0
- package/dist/useSignalRLiveData.d.ts.map +1 -0
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
import { useState as O, useRef as ae, useCallback as m, useEffect as W, useMemo as de } from "react";
|
|
2
|
+
class Ve {
|
|
2
3
|
constructor(e) {
|
|
3
4
|
this.cfg = typeof e == "string" ? { baseUrl: e } : e;
|
|
4
5
|
}
|
|
5
|
-
async request(e,
|
|
6
|
-
const
|
|
6
|
+
async request(e, t) {
|
|
7
|
+
const n = this.cfg.fetch ?? globalThis.fetch, a = {
|
|
7
8
|
"Content-Type": "application/json",
|
|
8
9
|
...this.cfg.headers
|
|
9
10
|
};
|
|
10
11
|
if (this.cfg.tokenFactory) {
|
|
11
|
-
const
|
|
12
|
-
|
|
12
|
+
const s = await this.cfg.tokenFactory();
|
|
13
|
+
a.Authorization = `Bearer ${s}`;
|
|
13
14
|
}
|
|
14
|
-
const
|
|
15
|
-
if (!
|
|
16
|
-
return
|
|
15
|
+
const r = await n(`${this.cfg.baseUrl}${e}`, { ...t, headers: { ...a, ...t == null ? void 0 : t.headers } });
|
|
16
|
+
if (!r.ok) throw new Error(`ERP request failed: ${r.status} ${r.statusText}`);
|
|
17
|
+
return r.json();
|
|
17
18
|
}
|
|
18
19
|
/** Load paged/filtered/sorted data. */
|
|
19
20
|
async load(e) {
|
|
20
|
-
var
|
|
21
|
-
const
|
|
22
|
-
(e == null ? void 0 : e.skip) != null &&
|
|
23
|
-
const
|
|
24
|
-
return this.request(
|
|
21
|
+
var a, r;
|
|
22
|
+
const t = new URLSearchParams();
|
|
23
|
+
(e == null ? void 0 : e.skip) != null && t.set("skip", String(e.skip)), (e == null ? void 0 : e.take) != null && t.set("take", String(e.take)), e != null && e.search && t.set("search", e.search), (a = e == null ? void 0 : e.sort) != null && a.length && t.set("sort", JSON.stringify(e.sort)), (r = e == null ? void 0 : e.filters) != null && r.length && t.set("filters", JSON.stringify(e.filters));
|
|
24
|
+
const n = t.toString();
|
|
25
|
+
return this.request(n ? `?${n}` : "");
|
|
25
26
|
}
|
|
26
27
|
/** Get single entity by id. */
|
|
27
28
|
async getById(e) {
|
|
@@ -32,8 +33,8 @@ class y {
|
|
|
32
33
|
return this.request("", { method: "POST", body: JSON.stringify(e) });
|
|
33
34
|
}
|
|
34
35
|
/** Update entity. */
|
|
35
|
-
async update(e,
|
|
36
|
-
return this.request(`/${e}`, { method: "PUT", body: JSON.stringify(
|
|
36
|
+
async update(e, t) {
|
|
37
|
+
return this.request(`/${e}`, { method: "PUT", body: JSON.stringify(t) });
|
|
37
38
|
}
|
|
38
39
|
/** Delete entity. */
|
|
39
40
|
async remove(e) {
|
|
@@ -43,40 +44,792 @@ class y {
|
|
|
43
44
|
async removeBatch(e) {
|
|
44
45
|
return this.request("/batch-delete", { method: "POST", body: JSON.stringify({ ids: e }) });
|
|
45
46
|
}
|
|
47
|
+
/** Batch create — send multiple items in a single request. */
|
|
48
|
+
async createBatch(e) {
|
|
49
|
+
return this.request("/batch", { method: "POST", body: JSON.stringify({ items: e }) });
|
|
50
|
+
}
|
|
51
|
+
/** Batch update — send multiple items with ids in a single request. */
|
|
52
|
+
async updateBatch(e) {
|
|
53
|
+
return this.request("/batch", { method: "PUT", body: JSON.stringify({ items: e }) });
|
|
54
|
+
}
|
|
55
|
+
/** Partial update (PATCH) a single entity. */
|
|
56
|
+
async patch(e, t) {
|
|
57
|
+
return this.request(`/${e}`, { method: "PATCH", body: JSON.stringify(t) });
|
|
58
|
+
}
|
|
46
59
|
}
|
|
47
|
-
async function
|
|
60
|
+
async function Ce(i) {
|
|
48
61
|
const e = await import("@microsoft/signalr");
|
|
49
|
-
let
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
},
|
|
53
|
-
accessTokenFactory:
|
|
62
|
+
let t = "disconnected";
|
|
63
|
+
const n = /* @__PURE__ */ new Set(), a = (u) => {
|
|
64
|
+
t = u, n.forEach((f) => f(u));
|
|
65
|
+
}, s = new e.HubConnectionBuilder().withUrl(i.hubUrl, {
|
|
66
|
+
accessTokenFactory: i.accessTokenFactory ? () => i.accessTokenFactory() : void 0
|
|
54
67
|
}).withAutomaticReconnect().build();
|
|
55
|
-
return
|
|
68
|
+
return s.onreconnecting(() => a("reconnecting")), s.onreconnected(() => a("connected")), s.onclose(() => a("disconnected")), {
|
|
56
69
|
get status() {
|
|
57
|
-
return
|
|
70
|
+
return t;
|
|
58
71
|
},
|
|
59
72
|
async start() {
|
|
60
|
-
|
|
73
|
+
a("connecting"), await s.start(), a("connected");
|
|
61
74
|
},
|
|
62
75
|
async stop() {
|
|
63
|
-
await
|
|
76
|
+
await s.stop(), a("disconnected");
|
|
77
|
+
},
|
|
78
|
+
on(u, f) {
|
|
79
|
+
s.on(u, f);
|
|
80
|
+
},
|
|
81
|
+
off(u, f) {
|
|
82
|
+
s.off(u, f);
|
|
83
|
+
},
|
|
84
|
+
invoke(u, ...f) {
|
|
85
|
+
return s.invoke(u, ...f);
|
|
86
|
+
},
|
|
87
|
+
onStatusChange(u) {
|
|
88
|
+
return n.add(u), () => n.delete(u);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function he(i) {
|
|
93
|
+
const {
|
|
94
|
+
adapter: e,
|
|
95
|
+
entityName: t,
|
|
96
|
+
keyField: n,
|
|
97
|
+
initialData: a = [],
|
|
98
|
+
onBeforeChange: r,
|
|
99
|
+
onAfterChange: s,
|
|
100
|
+
onConflict: o,
|
|
101
|
+
subscribeMethod: u = "SubscribeToEntity",
|
|
102
|
+
unsubscribeMethod: f = "UnsubscribeFromEntity",
|
|
103
|
+
changeEventName: b = "EntityChanged",
|
|
104
|
+
flashChanges: y = !0,
|
|
105
|
+
flashDuration: V = 500,
|
|
106
|
+
optimisticUpdates: C = !0
|
|
107
|
+
} = i, [q, L] = O(a), [Q, G] = O(e.status), [X, U] = O(/* @__PURE__ */ new Set()), [H, R] = O(/* @__PURE__ */ new Set()), [_, Z] = O(null), J = ae(/* @__PURE__ */ new Map()), I = ae(/* @__PURE__ */ new Map()), Y = m(
|
|
108
|
+
(d, v) => {
|
|
109
|
+
const { operation: E, key: S, data: g, changes: N } = v;
|
|
110
|
+
switch (E) {
|
|
111
|
+
case "insert":
|
|
112
|
+
return !g || d.some((A) => A[n] === S) ? d : [...d, g];
|
|
113
|
+
case "update":
|
|
114
|
+
return d.map((A) => {
|
|
115
|
+
if (A[n] !== S) return A;
|
|
116
|
+
const K = J.current.get(S);
|
|
117
|
+
if (K && o) {
|
|
118
|
+
const te = o(
|
|
119
|
+
{ ...A, ...K.changes },
|
|
120
|
+
g ?? { ...A, ...N },
|
|
121
|
+
v
|
|
122
|
+
);
|
|
123
|
+
return te === "local" ? { ...A, ...K.changes } : te === "remote" ? (J.current.delete(S), g ?? { ...A, ...N }) : te === "merge" ? { ...A, ...N, ...K.changes } : te;
|
|
124
|
+
}
|
|
125
|
+
return g || { ...A, ...N };
|
|
126
|
+
});
|
|
127
|
+
case "delete":
|
|
128
|
+
return d.filter((A) => A[n] !== S);
|
|
129
|
+
default:
|
|
130
|
+
return d;
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
[n, o]
|
|
134
|
+
), $ = m(
|
|
135
|
+
(d) => {
|
|
136
|
+
if (!y) return;
|
|
137
|
+
const v = I.current.get(d);
|
|
138
|
+
v && clearTimeout(v), U((S) => /* @__PURE__ */ new Set([...S, d]));
|
|
139
|
+
const E = setTimeout(() => {
|
|
140
|
+
U((S) => {
|
|
141
|
+
const g = new Set(S);
|
|
142
|
+
return g.delete(d), g;
|
|
143
|
+
}), I.current.delete(d);
|
|
144
|
+
}, V);
|
|
145
|
+
I.current.set(d, E);
|
|
146
|
+
},
|
|
147
|
+
[y, V]
|
|
148
|
+
), B = m(
|
|
149
|
+
async (d) => {
|
|
150
|
+
d.entityName === t && (r && !await r(d) || (L((v) => {
|
|
151
|
+
const E = Y(v, d);
|
|
152
|
+
return s == null || s(d, E), E;
|
|
153
|
+
}), $(d.key)));
|
|
154
|
+
},
|
|
155
|
+
[t, r, Y, s, $]
|
|
156
|
+
), P = m(
|
|
157
|
+
(d) => {
|
|
158
|
+
B(d);
|
|
159
|
+
},
|
|
160
|
+
[B]
|
|
161
|
+
), oe = m(
|
|
162
|
+
(d, v) => {
|
|
163
|
+
if (!C) return;
|
|
164
|
+
const E = q.find((S) => S[n] === d);
|
|
165
|
+
E && (J.current.set(d, { original: E, changes: v }), R((S) => /* @__PURE__ */ new Set([...S, d])));
|
|
166
|
+
},
|
|
167
|
+
[q, n, C]
|
|
168
|
+
), j = m((d) => {
|
|
169
|
+
J.current.delete(d), R((v) => {
|
|
170
|
+
const E = new Set(v);
|
|
171
|
+
return E.delete(d), E;
|
|
172
|
+
});
|
|
173
|
+
}, []), F = m((d) => {
|
|
174
|
+
const v = J.current.get(d);
|
|
175
|
+
v && (L(
|
|
176
|
+
(E) => E.map(
|
|
177
|
+
(S) => S[n] === d ? v.original : S
|
|
178
|
+
)
|
|
179
|
+
), J.current.delete(d), R((E) => {
|
|
180
|
+
const S = new Set(E);
|
|
181
|
+
return S.delete(d), S;
|
|
182
|
+
}));
|
|
183
|
+
}, [n]), ee = m(async () => {
|
|
184
|
+
if (e.status === "connected")
|
|
185
|
+
try {
|
|
186
|
+
await e.invoke(u, t), Z(null);
|
|
187
|
+
} catch (d) {
|
|
188
|
+
Z(d instanceof Error ? d : new Error(String(d)));
|
|
189
|
+
}
|
|
190
|
+
}, [e, u, t]), ne = m(async () => {
|
|
191
|
+
if (e.status === "connected")
|
|
192
|
+
try {
|
|
193
|
+
await e.invoke(f, t);
|
|
194
|
+
} catch {
|
|
195
|
+
}
|
|
196
|
+
}, [e, f, t]);
|
|
197
|
+
return W(() => {
|
|
198
|
+
const d = e.onStatusChange((v) => {
|
|
199
|
+
G(v), v === "connected" && ee();
|
|
200
|
+
});
|
|
201
|
+
return e.on(b, B), e.status === "connected" && ee(), () => {
|
|
202
|
+
d(), e.off(b, B), ne(), I.current.forEach((v) => clearTimeout(v)), I.current.clear();
|
|
203
|
+
};
|
|
204
|
+
}, [e, b, B, ee, ne]), W(() => {
|
|
205
|
+
L(a);
|
|
206
|
+
}, [a]), {
|
|
207
|
+
data: q,
|
|
208
|
+
status: Q,
|
|
209
|
+
flashedKeys: X,
|
|
210
|
+
pendingKeys: H,
|
|
211
|
+
error: _,
|
|
212
|
+
applyRemoteUpdate: P,
|
|
213
|
+
registerPendingChange: oe,
|
|
214
|
+
confirmPendingChange: j,
|
|
215
|
+
rollbackPendingChange: F,
|
|
216
|
+
resubscribe: ee
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function Pe(i) {
|
|
220
|
+
const {
|
|
221
|
+
adapter: e,
|
|
222
|
+
entityName: t,
|
|
223
|
+
keyField: n,
|
|
224
|
+
fetchMethod: a = "GetEntities",
|
|
225
|
+
insertMethod: r = "InsertEntity",
|
|
226
|
+
updateMethod: s = "UpdateEntity",
|
|
227
|
+
deleteMethod: o = "DeleteEntity"
|
|
228
|
+
} = i;
|
|
229
|
+
return {
|
|
230
|
+
/** Fetch all entities from the hub. */
|
|
231
|
+
async fetchAll(u) {
|
|
232
|
+
return e.invoke(a, t, u);
|
|
64
233
|
},
|
|
65
|
-
|
|
66
|
-
|
|
234
|
+
/** Insert a new entity via the hub. */
|
|
235
|
+
async insert(u) {
|
|
236
|
+
return e.invoke(r, t, u);
|
|
67
237
|
},
|
|
68
|
-
|
|
69
|
-
|
|
238
|
+
/** Update an existing entity via the hub. */
|
|
239
|
+
async update(u, f) {
|
|
240
|
+
return e.invoke(s, t, u, f);
|
|
70
241
|
},
|
|
71
|
-
|
|
72
|
-
|
|
242
|
+
/** Delete an entity via the hub. */
|
|
243
|
+
async remove(u) {
|
|
244
|
+
return e.invoke(o, t, u);
|
|
73
245
|
},
|
|
74
|
-
|
|
75
|
-
|
|
246
|
+
/** Get the key field name. */
|
|
247
|
+
getKeyField() {
|
|
248
|
+
return n;
|
|
76
249
|
}
|
|
77
250
|
};
|
|
78
251
|
}
|
|
79
|
-
|
|
252
|
+
function Te(i, e, t) {
|
|
253
|
+
const n = new Map(i.map((a) => [a[t], a]));
|
|
254
|
+
for (const a of e)
|
|
255
|
+
switch (a.operation) {
|
|
256
|
+
case "insert":
|
|
257
|
+
a.data && !n.has(a.key) && n.set(a.key, a.data);
|
|
258
|
+
break;
|
|
259
|
+
case "update":
|
|
260
|
+
if (n.has(a.key)) {
|
|
261
|
+
const r = n.get(a.key);
|
|
262
|
+
n.set(
|
|
263
|
+
a.key,
|
|
264
|
+
a.data ?? { ...r, ...a.changes }
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
break;
|
|
268
|
+
case "delete":
|
|
269
|
+
n.delete(a.key);
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
return Array.from(n.values());
|
|
273
|
+
}
|
|
274
|
+
function pe(i) {
|
|
275
|
+
const {
|
|
276
|
+
adapter: e,
|
|
277
|
+
entityType: t,
|
|
278
|
+
entityId: n,
|
|
279
|
+
currentUser: a,
|
|
280
|
+
initialStatus: r = "viewing",
|
|
281
|
+
heartbeatInterval: s = 3e4,
|
|
282
|
+
idleTimeout: o = 6e4,
|
|
283
|
+
awayTimeout: u = 3e5,
|
|
284
|
+
joinMethod: f = "JoinEntityView",
|
|
285
|
+
leaveMethod: b = "LeaveEntityView",
|
|
286
|
+
updateMethod: y = "UpdateEntityPresence",
|
|
287
|
+
presenceEventName: V = "EntityPresenceChanged",
|
|
288
|
+
joinedEventName: C = "EntityUserJoined",
|
|
289
|
+
leftEventName: q = "EntityUserLeft",
|
|
290
|
+
onPresenceChange: L,
|
|
291
|
+
onEditConflict: Q
|
|
292
|
+
} = i, [G, X] = O([]), [U, H] = O(r), [R, _] = O(), [Z, J] = O(e.status), I = ae(Date.now()), Y = ae(null), $ = ae(null), B = ae(null), P = Array.isArray(n) ? n : [n], oe = `${t}:${P.sort().join(",")}`, j = m(
|
|
293
|
+
async (c, k, w) => {
|
|
294
|
+
if (e.status === "connected")
|
|
295
|
+
try {
|
|
296
|
+
await e.invoke(y, {
|
|
297
|
+
entityType: t,
|
|
298
|
+
entityIds: P,
|
|
299
|
+
userId: a.id,
|
|
300
|
+
status: c,
|
|
301
|
+
editingField: k,
|
|
302
|
+
selection: w,
|
|
303
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
304
|
+
});
|
|
305
|
+
} catch {
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
[e, y, t, P, a.id]
|
|
309
|
+
), F = m(
|
|
310
|
+
(c, k) => {
|
|
311
|
+
H(c), _(k), j(c, k), I.current = Date.now();
|
|
312
|
+
},
|
|
313
|
+
[j]
|
|
314
|
+
), ee = m(
|
|
315
|
+
(c) => {
|
|
316
|
+
const k = G.find(
|
|
317
|
+
(w) => w.user.id !== a.id && w.status === "editing" && w.editingField === c
|
|
318
|
+
);
|
|
319
|
+
k && (Q == null || Q(k.user, c)), F("editing", c);
|
|
320
|
+
},
|
|
321
|
+
[G, a.id, Q, F]
|
|
322
|
+
), ne = m(() => {
|
|
323
|
+
F("viewing", void 0);
|
|
324
|
+
}, [F]), d = m(
|
|
325
|
+
(c) => {
|
|
326
|
+
j(U, R, c), I.current = Date.now();
|
|
327
|
+
},
|
|
328
|
+
[j, U, R]
|
|
329
|
+
), v = m(
|
|
330
|
+
(c) => {
|
|
331
|
+
const k = G.find(
|
|
332
|
+
(w) => w.user.id !== a.id && w.status === "editing" && w.editingField === c
|
|
333
|
+
);
|
|
334
|
+
return (k == null ? void 0 : k.user) ?? null;
|
|
335
|
+
},
|
|
336
|
+
[G, a.id]
|
|
337
|
+
), E = m(
|
|
338
|
+
(c) => {
|
|
339
|
+
const k = G.find((w) => w.status === "editing" && w.editingField === c);
|
|
340
|
+
return (k == null ? void 0 : k.user) ?? null;
|
|
341
|
+
},
|
|
342
|
+
[G]
|
|
343
|
+
), S = m(async () => {
|
|
344
|
+
if (e.status === "connected")
|
|
345
|
+
try {
|
|
346
|
+
const c = await e.invoke("GetEntityPresence", t, P);
|
|
347
|
+
Array.isArray(c) && (X(c), L == null || L(c));
|
|
348
|
+
} catch {
|
|
349
|
+
}
|
|
350
|
+
}, [e, t, P, L]), g = m(() => {
|
|
351
|
+
I.current = Date.now(), $.current && clearTimeout($.current), B.current && clearTimeout(B.current), (U === "idle" || U === "away") && F(R ? "editing" : "viewing", R), $.current = setTimeout(() => {
|
|
352
|
+
F("idle", R);
|
|
353
|
+
}, o), B.current = setTimeout(() => {
|
|
354
|
+
F("away", R);
|
|
355
|
+
}, u);
|
|
356
|
+
}, [U, R, o, u, F]), N = m(
|
|
357
|
+
(c) => {
|
|
358
|
+
c.entityType !== t || !c.entityIds.some((w) => P.includes(w)) || (X(c.presence.filter((w) => w.user.id !== a.id)), L == null || L(c.presence));
|
|
359
|
+
},
|
|
360
|
+
[t, P, a.id, L]
|
|
361
|
+
), A = m(
|
|
362
|
+
(c) => {
|
|
363
|
+
c.entityType !== t || !c.entityIds.some((w) => P.includes(w)) || c.presence.user.id === a.id || X((w) => {
|
|
364
|
+
const se = w.findIndex((re) => re.user.id === c.presence.user.id);
|
|
365
|
+
if (se >= 0) {
|
|
366
|
+
const re = [...w];
|
|
367
|
+
return re[se] = c.presence, re;
|
|
368
|
+
}
|
|
369
|
+
return [...w, c.presence];
|
|
370
|
+
});
|
|
371
|
+
},
|
|
372
|
+
[t, P, a.id]
|
|
373
|
+
), K = m(
|
|
374
|
+
(c) => {
|
|
375
|
+
c.entityType !== t || !c.entityIds.some((w) => P.includes(w)) || X((w) => w.filter((se) => se.user.id !== c.userId));
|
|
376
|
+
},
|
|
377
|
+
[t, P]
|
|
378
|
+
);
|
|
379
|
+
W(() => {
|
|
380
|
+
const c = e.onStatusChange(J);
|
|
381
|
+
e.on(V, N), e.on(C, A), e.on(q, K);
|
|
382
|
+
const k = async () => {
|
|
383
|
+
if (e.status === "connected")
|
|
384
|
+
try {
|
|
385
|
+
await e.invoke(f, {
|
|
386
|
+
entityType: t,
|
|
387
|
+
entityIds: P,
|
|
388
|
+
user: a,
|
|
389
|
+
status: U
|
|
390
|
+
}), await S();
|
|
391
|
+
} catch {
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
return e.status === "connected" && k(), () => {
|
|
395
|
+
c(), e.off(V, N), e.off(C, A), e.off(q, K), e.status === "connected" && e.invoke(b, {
|
|
396
|
+
entityType: t,
|
|
397
|
+
entityIds: P,
|
|
398
|
+
userId: a.id
|
|
399
|
+
}).catch(() => {
|
|
400
|
+
});
|
|
401
|
+
};
|
|
402
|
+
}, [
|
|
403
|
+
e,
|
|
404
|
+
oe,
|
|
405
|
+
f,
|
|
406
|
+
b,
|
|
407
|
+
V,
|
|
408
|
+
C,
|
|
409
|
+
q,
|
|
410
|
+
N,
|
|
411
|
+
A,
|
|
412
|
+
K,
|
|
413
|
+
S,
|
|
414
|
+
a,
|
|
415
|
+
U,
|
|
416
|
+
t,
|
|
417
|
+
P
|
|
418
|
+
]), W(() => (Y.current = setInterval(() => {
|
|
419
|
+
j(U, R);
|
|
420
|
+
}, s), () => {
|
|
421
|
+
Y.current && clearInterval(Y.current);
|
|
422
|
+
}), [s, j, U, R]), W(() => {
|
|
423
|
+
const c = ["mousemove", "keydown", "mousedown", "touchstart", "scroll"];
|
|
424
|
+
return c.forEach((k) => window.addEventListener(k, g, { passive: !0 })), $.current = setTimeout(() => {
|
|
425
|
+
F("idle", R);
|
|
426
|
+
}, o), B.current = setTimeout(() => {
|
|
427
|
+
F("away", R);
|
|
428
|
+
}, u), () => {
|
|
429
|
+
c.forEach((k) => window.removeEventListener(k, g)), $.current && clearTimeout($.current), B.current && clearTimeout(B.current);
|
|
430
|
+
};
|
|
431
|
+
}, [g, o, u, R, F]);
|
|
432
|
+
const te = G.filter((c) => c.status === "viewing" || c.status === "idle").map((c) => c.user), le = G.filter((c) => c.status === "editing").map((c) => c.user), ue = le.length > 0;
|
|
433
|
+
return {
|
|
434
|
+
presence: G,
|
|
435
|
+
viewers: te,
|
|
436
|
+
editors: le,
|
|
437
|
+
myStatus: U,
|
|
438
|
+
setMyStatus: F,
|
|
439
|
+
startEditing: ee,
|
|
440
|
+
stopEditing: ne,
|
|
441
|
+
updateSelection: d,
|
|
442
|
+
connectionStatus: Z,
|
|
443
|
+
isFieldLocked: v,
|
|
444
|
+
hasActiveEditors: ue,
|
|
445
|
+
getFieldEditor: E,
|
|
446
|
+
refresh: S
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
function fe(i) {
|
|
450
|
+
const e = [
|
|
451
|
+
"#3b82f6",
|
|
452
|
+
// blue
|
|
453
|
+
"#ef4444",
|
|
454
|
+
// red
|
|
455
|
+
"#22c55e",
|
|
456
|
+
// green
|
|
457
|
+
"#f59e0b",
|
|
458
|
+
// amber
|
|
459
|
+
"#8b5cf6",
|
|
460
|
+
// violet
|
|
461
|
+
"#ec4899",
|
|
462
|
+
// pink
|
|
463
|
+
"#06b6d4",
|
|
464
|
+
// cyan
|
|
465
|
+
"#84cc16",
|
|
466
|
+
// lime
|
|
467
|
+
"#f97316",
|
|
468
|
+
// orange
|
|
469
|
+
"#14b8a6"
|
|
470
|
+
// teal
|
|
471
|
+
];
|
|
472
|
+
let t = 0;
|
|
473
|
+
for (let n = 0; n < i.length; n++)
|
|
474
|
+
t = i.charCodeAt(n) + ((t << 5) - t);
|
|
475
|
+
return e[Math.abs(t) % e.length];
|
|
476
|
+
}
|
|
477
|
+
function xe(i) {
|
|
478
|
+
switch (i) {
|
|
479
|
+
case "viewing":
|
|
480
|
+
return "Viewing";
|
|
481
|
+
case "editing":
|
|
482
|
+
return "Editing";
|
|
483
|
+
case "idle":
|
|
484
|
+
return "Idle";
|
|
485
|
+
case "away":
|
|
486
|
+
return "Away";
|
|
487
|
+
default:
|
|
488
|
+
return "Unknown";
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
function Re(i) {
|
|
492
|
+
const {
|
|
493
|
+
adapter: e,
|
|
494
|
+
entityType: t,
|
|
495
|
+
currentUser: n,
|
|
496
|
+
batchEventName: a = "BatchPresenceUpdate"
|
|
497
|
+
} = i, [r, s] = O(/* @__PURE__ */ new Map()), o = m(
|
|
498
|
+
(y) => {
|
|
499
|
+
y.entityType === t && s(new Map(Object.entries(y.presenceByEntity)));
|
|
500
|
+
},
|
|
501
|
+
[t]
|
|
502
|
+
);
|
|
503
|
+
W(() => (e.on(a, o), () => {
|
|
504
|
+
e.off(a, o);
|
|
505
|
+
}), [e, a, o]);
|
|
506
|
+
const u = m(
|
|
507
|
+
(y) => r.get(y) ?? [],
|
|
508
|
+
[r]
|
|
509
|
+
), f = m(
|
|
510
|
+
(y) => {
|
|
511
|
+
const V = r.get(y);
|
|
512
|
+
return (V == null ? void 0 : V.some((C) => C.status === "editing" && C.user.id !== n.id)) ?? !1;
|
|
513
|
+
},
|
|
514
|
+
[r, n.id]
|
|
515
|
+
), b = m(
|
|
516
|
+
(y) => {
|
|
517
|
+
const V = r.get(y);
|
|
518
|
+
return (V == null ? void 0 : V.filter((C) => C.status === "editing").map((C) => C.user)) ?? [];
|
|
519
|
+
},
|
|
520
|
+
[r]
|
|
521
|
+
);
|
|
522
|
+
return {
|
|
523
|
+
presenceByEntity: r,
|
|
524
|
+
getPresence: u,
|
|
525
|
+
isEntityBeingEdited: f,
|
|
526
|
+
getEntityEditors: b
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
function M(i) {
|
|
530
|
+
return `${String(i.rowKey)}:${i.field}`;
|
|
531
|
+
}
|
|
532
|
+
function me(i) {
|
|
533
|
+
return i ? typeof i == "number" ? i : new Date(i).getTime() : Date.now();
|
|
534
|
+
}
|
|
535
|
+
function De(i) {
|
|
536
|
+
const {
|
|
537
|
+
adapter: e,
|
|
538
|
+
entityType: t,
|
|
539
|
+
keyField: n,
|
|
540
|
+
currentUser: a,
|
|
541
|
+
initialData: r = [],
|
|
542
|
+
conflictStrategy: s = "last-write-wins",
|
|
543
|
+
mergeFunction: o,
|
|
544
|
+
onConflict: u,
|
|
545
|
+
onBeforeCommit: f,
|
|
546
|
+
onAfterCommit: b,
|
|
547
|
+
enableCellLocking: y = !0,
|
|
548
|
+
lockTimeout: V = 6e4,
|
|
549
|
+
enableCursorSharing: C = !0,
|
|
550
|
+
cursorDebounce: q = 50,
|
|
551
|
+
optimisticUpdates: L = !0,
|
|
552
|
+
flashChanges: Q = !0,
|
|
553
|
+
flashDuration: G = 500,
|
|
554
|
+
liveDataOptions: X = {}
|
|
555
|
+
} = i, [U, H] = O(/* @__PURE__ */ new Map()), [R, _] = O(/* @__PURE__ */ new Map()), [Z, J] = O([]), [I, Y] = O(null), [$, B] = O([]), [P, oe] = O([]), [j, F] = O(null), [ee, ne] = O([]), d = ae(null), v = ae(/* @__PURE__ */ new Map()), E = m((l) => (v.current.has(l) || v.current.set(l, fe(l)), v.current.get(l)), []), S = m(
|
|
556
|
+
(l, h, p) => {
|
|
557
|
+
if (!p.changes) return h;
|
|
558
|
+
const T = R.get(M({ rowKey: p.key, field: Object.keys(p.changes)[0] }));
|
|
559
|
+
if (!T) return h;
|
|
560
|
+
const D = T.timestamp, x = me(p.timestamp);
|
|
561
|
+
switch (s) {
|
|
562
|
+
case "last-write-wins":
|
|
563
|
+
return D > x ? "local" : "remote";
|
|
564
|
+
case "first-write-wins":
|
|
565
|
+
return D < x ? "local" : "remote";
|
|
566
|
+
case "merge":
|
|
567
|
+
return o ? o(l, h, Object.keys(p.changes)[0]) : "merge";
|
|
568
|
+
case "ask-user":
|
|
569
|
+
const z = {
|
|
570
|
+
cell: { rowKey: p.key, field: Object.keys(p.changes)[0] },
|
|
571
|
+
localValue: T.newValue,
|
|
572
|
+
remoteValue: p.changes[Object.keys(p.changes)[0]],
|
|
573
|
+
localUser: a,
|
|
574
|
+
remoteUser: p.changedBy ? { id: p.changedBy, name: p.changedBy } : { id: "unknown", name: "Unknown" },
|
|
575
|
+
localTimestamp: D,
|
|
576
|
+
remoteTimestamp: x
|
|
577
|
+
};
|
|
578
|
+
return J((ie) => [...ie, z]), "local";
|
|
579
|
+
// Keep local until resolved
|
|
580
|
+
case "reject":
|
|
581
|
+
return "remote";
|
|
582
|
+
default:
|
|
583
|
+
return "remote";
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
[R, s, o, a]
|
|
587
|
+
), g = he({
|
|
588
|
+
adapter: e,
|
|
589
|
+
entityName: t,
|
|
590
|
+
keyField: n,
|
|
591
|
+
initialData: r,
|
|
592
|
+
flashChanges: Q,
|
|
593
|
+
flashDuration: G,
|
|
594
|
+
optimisticUpdates: L,
|
|
595
|
+
onConflict: S,
|
|
596
|
+
...X
|
|
597
|
+
}), N = pe({
|
|
598
|
+
adapter: e,
|
|
599
|
+
entityType: t,
|
|
600
|
+
entityId: "grid",
|
|
601
|
+
// Special ID for grid-level presence
|
|
602
|
+
currentUser: a,
|
|
603
|
+
initialStatus: "viewing"
|
|
604
|
+
}), A = m(() => {
|
|
605
|
+
!C || e.status !== "connected" || e.invoke("UpdateGridSelection", {
|
|
606
|
+
entityType: t,
|
|
607
|
+
userId: a.id,
|
|
608
|
+
focusedCell: I,
|
|
609
|
+
selectedCells: $,
|
|
610
|
+
selectedRows: P,
|
|
611
|
+
editingCell: j
|
|
612
|
+
}).catch(() => {
|
|
613
|
+
});
|
|
614
|
+
}, [e, C, t, a.id, I, $, P, j]);
|
|
615
|
+
W(() => {
|
|
616
|
+
if (C)
|
|
617
|
+
return d.current && clearTimeout(d.current), d.current = setTimeout(A, q), () => {
|
|
618
|
+
d.current && clearTimeout(d.current);
|
|
619
|
+
};
|
|
620
|
+
}, [C, A, q, I, $, P, j]), W(() => {
|
|
621
|
+
if (!C) return;
|
|
622
|
+
const l = (h) => {
|
|
623
|
+
h.entityType !== t || h.userId === a.id || ne((p) => {
|
|
624
|
+
const T = p.findIndex((x) => x.user.id === h.userId), D = {
|
|
625
|
+
user: { id: h.userId, name: h.userName, avatarUrl: h.avatarUrl },
|
|
626
|
+
focusedCell: h.focusedCell,
|
|
627
|
+
selectedCells: h.selectedCells,
|
|
628
|
+
selectedRows: h.selectedRows,
|
|
629
|
+
editingCell: h.editingCell,
|
|
630
|
+
color: E(h.userId)
|
|
631
|
+
};
|
|
632
|
+
if (T >= 0) {
|
|
633
|
+
const x = [...p];
|
|
634
|
+
return x[T] = D, x;
|
|
635
|
+
}
|
|
636
|
+
return [...p, D];
|
|
637
|
+
});
|
|
638
|
+
};
|
|
639
|
+
return e.on("GridSelectionUpdated", l), () => {
|
|
640
|
+
e.off("GridSelectionUpdated", l);
|
|
641
|
+
};
|
|
642
|
+
}, [e, C, t, a.id, E]), W(() => {
|
|
643
|
+
if (!y) return;
|
|
644
|
+
const l = (p) => {
|
|
645
|
+
p.entityType === t && H((T) => {
|
|
646
|
+
const D = new Map(T);
|
|
647
|
+
return D.set(M(p.cell), { user: p.user, timestamp: p.timestamp }), D;
|
|
648
|
+
});
|
|
649
|
+
}, h = (p) => {
|
|
650
|
+
p.entityType === t && H((T) => {
|
|
651
|
+
const D = new Map(T);
|
|
652
|
+
return D.delete(M(p.cell)), D;
|
|
653
|
+
});
|
|
654
|
+
};
|
|
655
|
+
return e.on("CellLocked", l), e.on("CellUnlocked", h), () => {
|
|
656
|
+
e.off("CellLocked", l), e.off("CellUnlocked", h);
|
|
657
|
+
};
|
|
658
|
+
}, [e, y, t]), W(() => {
|
|
659
|
+
if (!y) return;
|
|
660
|
+
const l = setInterval(() => {
|
|
661
|
+
const h = Date.now();
|
|
662
|
+
H((p) => {
|
|
663
|
+
const T = new Map(p);
|
|
664
|
+
let D = !1;
|
|
665
|
+
for (const [x, z] of T)
|
|
666
|
+
h - z.timestamp > V && (T.delete(x), D = !0);
|
|
667
|
+
return D ? T : p;
|
|
668
|
+
});
|
|
669
|
+
}, 1e4);
|
|
670
|
+
return () => clearInterval(l);
|
|
671
|
+
}, [y, V]);
|
|
672
|
+
const K = m(
|
|
673
|
+
(l) => {
|
|
674
|
+
if (!y) return !1;
|
|
675
|
+
const h = U.get(M(l));
|
|
676
|
+
return h ? h.user.id !== a.id && Date.now() - h.timestamp < V : !1;
|
|
677
|
+
},
|
|
678
|
+
[y, U, a.id, V]
|
|
679
|
+
), te = m(
|
|
680
|
+
(l) => {
|
|
681
|
+
const h = U.get(M(l));
|
|
682
|
+
return !h || Date.now() - h.timestamp > V ? null : h.user;
|
|
683
|
+
},
|
|
684
|
+
[U, V]
|
|
685
|
+
), le = m(
|
|
686
|
+
(l) => K(l) ? !1 : (F(l), N.startEditing(`${l.rowKey}:${l.field}`), y && e.status === "connected" && (e.invoke("AcquireCellLock", {
|
|
687
|
+
entityType: t,
|
|
688
|
+
cell: l,
|
|
689
|
+
user: a,
|
|
690
|
+
timestamp: Date.now()
|
|
691
|
+
}).catch(() => {
|
|
692
|
+
}), H((h) => {
|
|
693
|
+
const p = new Map(h);
|
|
694
|
+
return p.set(M(l), { user: a, timestamp: Date.now() }), p;
|
|
695
|
+
})), !0),
|
|
696
|
+
[K, N, y, e, t, a]
|
|
697
|
+
), ue = m(
|
|
698
|
+
async (l, h) => {
|
|
699
|
+
const p = g.data.find((x) => x[n] === l.rowKey);
|
|
700
|
+
if (!p) return !1;
|
|
701
|
+
const T = p[l.field];
|
|
702
|
+
if (f && !await f(l, T, h))
|
|
703
|
+
return c(l), !1;
|
|
704
|
+
const D = {
|
|
705
|
+
cell: l,
|
|
706
|
+
originalValue: T,
|
|
707
|
+
newValue: h,
|
|
708
|
+
timestamp: Date.now()
|
|
709
|
+
};
|
|
710
|
+
_((x) => {
|
|
711
|
+
const z = new Map(x);
|
|
712
|
+
return z.set(M(l), D), z;
|
|
713
|
+
}), L && g.registerPendingChange(l.rowKey, { [l.field]: h });
|
|
714
|
+
try {
|
|
715
|
+
return await e.invoke("UpdateEntityField", {
|
|
716
|
+
entityType: t,
|
|
717
|
+
entityId: l.rowKey,
|
|
718
|
+
field: l.field,
|
|
719
|
+
value: h,
|
|
720
|
+
userId: a.id,
|
|
721
|
+
timestamp: D.timestamp
|
|
722
|
+
}), g.confirmPendingChange(l.rowKey), _((x) => {
|
|
723
|
+
const z = new Map(x);
|
|
724
|
+
return z.delete(M(l)), z;
|
|
725
|
+
}), b == null || b(l, h), y && (e.invoke("ReleaseCellLock", { entityType: t, cell: l }).catch(() => {
|
|
726
|
+
}), H((x) => {
|
|
727
|
+
const z = new Map(x);
|
|
728
|
+
return z.delete(M(l)), z;
|
|
729
|
+
})), F(null), N.stopEditing(), !0;
|
|
730
|
+
} catch (x) {
|
|
731
|
+
throw g.rollbackPendingChange(l.rowKey), _((z) => {
|
|
732
|
+
const ie = new Map(z);
|
|
733
|
+
return ie.delete(M(l)), ie;
|
|
734
|
+
}), x;
|
|
735
|
+
}
|
|
736
|
+
},
|
|
737
|
+
[
|
|
738
|
+
g,
|
|
739
|
+
n,
|
|
740
|
+
f,
|
|
741
|
+
L,
|
|
742
|
+
e,
|
|
743
|
+
t,
|
|
744
|
+
a.id,
|
|
745
|
+
b,
|
|
746
|
+
y,
|
|
747
|
+
N
|
|
748
|
+
]
|
|
749
|
+
), c = m(
|
|
750
|
+
(l) => {
|
|
751
|
+
F(null), N.stopEditing(), y && e.status === "connected" && (e.invoke("ReleaseCellLock", { entityType: t, cell: l }).catch(() => {
|
|
752
|
+
}), H((h) => {
|
|
753
|
+
const p = new Map(h);
|
|
754
|
+
return p.delete(M(l)), p;
|
|
755
|
+
})), _((h) => {
|
|
756
|
+
const p = new Map(h);
|
|
757
|
+
return p.delete(M(l)), p;
|
|
758
|
+
});
|
|
759
|
+
},
|
|
760
|
+
[N, y, e, t]
|
|
761
|
+
), k = m(
|
|
762
|
+
(l, h) => {
|
|
763
|
+
const p = Z.find((T) => M(T.cell) === M(l));
|
|
764
|
+
p && (h === "remote" && g.applyRemoteUpdate({
|
|
765
|
+
operation: "update",
|
|
766
|
+
entityName: t,
|
|
767
|
+
key: l.rowKey,
|
|
768
|
+
changes: { [l.field]: p.remoteValue }
|
|
769
|
+
}), J((T) => T.filter((D) => M(D.cell) !== M(l))));
|
|
770
|
+
},
|
|
771
|
+
[Z, g, t]
|
|
772
|
+
), w = m(
|
|
773
|
+
async (l) => {
|
|
774
|
+
await e.invoke("InsertEntity", t, l);
|
|
775
|
+
},
|
|
776
|
+
[e, t]
|
|
777
|
+
), se = m(
|
|
778
|
+
async (l, h) => {
|
|
779
|
+
L && g.registerPendingChange(l, h);
|
|
780
|
+
try {
|
|
781
|
+
await e.invoke("UpdateEntity", t, l, h), g.confirmPendingChange(l);
|
|
782
|
+
} catch (p) {
|
|
783
|
+
throw g.rollbackPendingChange(l), p;
|
|
784
|
+
}
|
|
785
|
+
},
|
|
786
|
+
[e, t, L, g]
|
|
787
|
+
), re = m(
|
|
788
|
+
async (l) => {
|
|
789
|
+
await e.invoke("DeleteEntity", t, l);
|
|
790
|
+
},
|
|
791
|
+
[e, t]
|
|
792
|
+
), ce = de(
|
|
793
|
+
() => ({
|
|
794
|
+
user: a,
|
|
795
|
+
focusedCell: I,
|
|
796
|
+
selectedCells: $,
|
|
797
|
+
selectedRows: P,
|
|
798
|
+
editingCell: j,
|
|
799
|
+
color: E(a.id)
|
|
800
|
+
}),
|
|
801
|
+
[a, I, $, P, j, E]
|
|
802
|
+
);
|
|
803
|
+
return {
|
|
804
|
+
data: g.data,
|
|
805
|
+
presence: N.presence,
|
|
806
|
+
userSelections: ee,
|
|
807
|
+
mySelection: ce,
|
|
808
|
+
flashedKeys: g.flashedKeys,
|
|
809
|
+
pendingEdits: R,
|
|
810
|
+
connectionStatus: g.status,
|
|
811
|
+
myPresenceStatus: N.myStatus,
|
|
812
|
+
// Selection methods
|
|
813
|
+
setFocusedCell: Y,
|
|
814
|
+
setSelectedCells: B,
|
|
815
|
+
setSelectedRows: oe,
|
|
816
|
+
// Edit methods
|
|
817
|
+
startCellEdit: le,
|
|
818
|
+
commitCellEdit: ue,
|
|
819
|
+
cancelCellEdit: c,
|
|
820
|
+
isCellLocked: K,
|
|
821
|
+
getCellEditor: te,
|
|
822
|
+
getUserColor: E,
|
|
823
|
+
// Row methods
|
|
824
|
+
insertRow: w,
|
|
825
|
+
updateRow: se,
|
|
826
|
+
deleteRow: re,
|
|
827
|
+
// Conflict handling
|
|
828
|
+
resolveConflict: k,
|
|
829
|
+
conflicts: Z
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
class Ae {
|
|
80
833
|
constructor(e) {
|
|
81
834
|
this.accessToken = null, this.refreshToken = null, this.tokenFactory = async () => {
|
|
82
835
|
if (!this.accessToken) throw new Error("Not authenticated");
|
|
@@ -87,35 +840,35 @@ class w {
|
|
|
87
840
|
getAccessToken() {
|
|
88
841
|
return this.accessToken;
|
|
89
842
|
}
|
|
90
|
-
async post(e,
|
|
91
|
-
const
|
|
843
|
+
async post(e, t) {
|
|
844
|
+
const a = await (this.cfg.fetch ?? globalThis.fetch)(`${this.cfg.baseUrl}${e}`, {
|
|
92
845
|
method: "POST",
|
|
93
846
|
headers: { "Content-Type": "application/json" },
|
|
94
|
-
body: JSON.stringify(
|
|
847
|
+
body: JSON.stringify(t)
|
|
95
848
|
});
|
|
96
|
-
if (!
|
|
97
|
-
return
|
|
849
|
+
if (!a.ok) throw new Error(`Auth request failed: ${a.status}`);
|
|
850
|
+
return a.json();
|
|
98
851
|
}
|
|
99
852
|
/** Login with username/password. */
|
|
100
853
|
async login(e) {
|
|
101
|
-
var
|
|
102
|
-
const
|
|
103
|
-
return
|
|
854
|
+
var n, a;
|
|
855
|
+
const t = await this.post("/login", e);
|
|
856
|
+
return t.token && (this.accessToken = t.token, this.refreshToken = t.refreshToken ?? null, (a = (n = this.cfg).onTokens) == null || a.call(n, t.token, t.refreshToken ?? "")), t;
|
|
104
857
|
}
|
|
105
858
|
/** Refresh the access token. */
|
|
106
859
|
async refresh() {
|
|
107
|
-
var
|
|
860
|
+
var t, n;
|
|
108
861
|
if (!this.refreshToken) throw new Error("No refresh token");
|
|
109
862
|
const e = await this.post("/refresh", { refreshToken: this.refreshToken });
|
|
110
|
-
return e.token && (this.accessToken = e.token, this.refreshToken = e.refreshToken ?? this.refreshToken, (
|
|
863
|
+
return e.token && (this.accessToken = e.token, this.refreshToken = e.refreshToken ?? this.refreshToken, (n = (t = this.cfg).onTokens) == null || n.call(t, e.token, this.refreshToken)), e;
|
|
111
864
|
}
|
|
112
865
|
/** Get current user info. */
|
|
113
866
|
async me() {
|
|
114
|
-
const
|
|
867
|
+
const t = await (this.cfg.fetch ?? globalThis.fetch)(`${this.cfg.baseUrl}/me`, {
|
|
115
868
|
headers: { Authorization: `Bearer ${this.accessToken}` }
|
|
116
869
|
});
|
|
117
|
-
if (!
|
|
118
|
-
return
|
|
870
|
+
if (!t.ok) throw new Error(`Auth /me failed: ${t.status}`);
|
|
871
|
+
return t.json();
|
|
119
872
|
}
|
|
120
873
|
/** Logout (invalidate tokens on server). */
|
|
121
874
|
async logout() {
|
|
@@ -129,7 +882,7 @@ class w {
|
|
|
129
882
|
}
|
|
130
883
|
}
|
|
131
884
|
}
|
|
132
|
-
class
|
|
885
|
+
class Le {
|
|
133
886
|
constructor(e) {
|
|
134
887
|
this.cfg = e;
|
|
135
888
|
}
|
|
@@ -138,57 +891,111 @@ class V {
|
|
|
138
891
|
return this.cfg.tokenFactory && (e.Authorization = `Bearer ${await this.cfg.tokenFactory()}`), e;
|
|
139
892
|
}
|
|
140
893
|
/** Upload a file. Returns file metadata on success. */
|
|
141
|
-
async upload(e,
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
const
|
|
894
|
+
async upload(e, t) {
|
|
895
|
+
const n = this.cfg.fetch ?? globalThis.fetch, a = new FormData();
|
|
896
|
+
a.append("file", e), t && a.append("folder", t);
|
|
897
|
+
const r = await n(this.cfg.baseUrl, {
|
|
145
898
|
method: "POST",
|
|
146
899
|
headers: await this.headers(),
|
|
147
|
-
body:
|
|
900
|
+
body: a
|
|
148
901
|
});
|
|
149
|
-
if (!
|
|
150
|
-
return
|
|
902
|
+
if (!r.ok) throw new Error(`File upload failed: ${r.status}`);
|
|
903
|
+
return r.json();
|
|
151
904
|
}
|
|
152
905
|
/** Upload multiple files. */
|
|
153
|
-
async uploadMultiple(e,
|
|
154
|
-
const
|
|
155
|
-
e.forEach((
|
|
156
|
-
const
|
|
906
|
+
async uploadMultiple(e, t) {
|
|
907
|
+
const n = this.cfg.fetch ?? globalThis.fetch, a = new FormData();
|
|
908
|
+
e.forEach((s) => a.append("files", s)), t && a.append("folder", t);
|
|
909
|
+
const r = await n(`${this.cfg.baseUrl}/batch`, {
|
|
157
910
|
method: "POST",
|
|
158
911
|
headers: await this.headers(),
|
|
159
|
-
body:
|
|
912
|
+
body: a
|
|
160
913
|
});
|
|
161
|
-
if (!
|
|
162
|
-
return
|
|
914
|
+
if (!r.ok) throw new Error(`Batch upload failed: ${r.status}`);
|
|
915
|
+
return r.json();
|
|
163
916
|
}
|
|
164
917
|
/** Get file metadata by id. */
|
|
165
918
|
async getInfo(e) {
|
|
166
|
-
const
|
|
919
|
+
const n = await (this.cfg.fetch ?? globalThis.fetch)(`${this.cfg.baseUrl}/${e}/info`, {
|
|
167
920
|
headers: { ...await this.headers(), "Content-Type": "application/json" }
|
|
168
921
|
});
|
|
169
|
-
if (!
|
|
170
|
-
return
|
|
922
|
+
if (!n.ok) throw new Error(`File info failed: ${n.status}`);
|
|
923
|
+
return n.json();
|
|
171
924
|
}
|
|
172
925
|
/** Get a download URL (or blob URL) for a file. */
|
|
173
926
|
async getDownloadUrl(e) {
|
|
174
|
-
const
|
|
927
|
+
const n = await (this.cfg.fetch ?? globalThis.fetch)(`${this.cfg.baseUrl}/${e}`, {
|
|
175
928
|
headers: await this.headers()
|
|
176
929
|
});
|
|
177
|
-
if (!
|
|
178
|
-
const
|
|
179
|
-
return URL.createObjectURL(
|
|
930
|
+
if (!n.ok) throw new Error(`File download failed: ${n.status}`);
|
|
931
|
+
const a = await n.blob();
|
|
932
|
+
return URL.createObjectURL(a);
|
|
180
933
|
}
|
|
181
934
|
/** Delete file by id. */
|
|
182
935
|
async remove(e) {
|
|
183
|
-
const
|
|
936
|
+
const n = await (this.cfg.fetch ?? globalThis.fetch)(`${this.cfg.baseUrl}/${e}`, {
|
|
184
937
|
method: "DELETE",
|
|
185
938
|
headers: { ...await this.headers(), "Content-Type": "application/json" }
|
|
186
939
|
});
|
|
187
|
-
if (!
|
|
188
|
-
return
|
|
940
|
+
if (!n.ok) throw new Error(`File delete failed: ${n.status}`);
|
|
941
|
+
return n.json();
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Upload a file with progress tracking via XMLHttpRequest.
|
|
945
|
+
* Falls back to standard fetch-based upload if XHR is unavailable.
|
|
946
|
+
*/
|
|
947
|
+
uploadWithProgress(e, t, n, a) {
|
|
948
|
+
return typeof XMLHttpRequest > "u" ? this.upload(e, n) : new Promise(async (r, s) => {
|
|
949
|
+
const o = new XMLHttpRequest(), u = new FormData();
|
|
950
|
+
if (u.append("file", e), n && u.append("folder", n), a) {
|
|
951
|
+
if (a.aborted) {
|
|
952
|
+
s(new DOMException("Aborted", "AbortError"));
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
a.addEventListener("abort", () => {
|
|
956
|
+
o.abort(), s(new DOMException("Aborted", "AbortError"));
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
o.upload.addEventListener("progress", (b) => {
|
|
960
|
+
t({
|
|
961
|
+
loaded: b.loaded,
|
|
962
|
+
total: b.total,
|
|
963
|
+
percent: b.lengthComputable ? b.loaded / b.total : NaN
|
|
964
|
+
});
|
|
965
|
+
}), o.addEventListener("load", () => {
|
|
966
|
+
if (o.status >= 200 && o.status < 300)
|
|
967
|
+
try {
|
|
968
|
+
r(JSON.parse(o.responseText));
|
|
969
|
+
} catch {
|
|
970
|
+
s(new Error("Invalid JSON response"));
|
|
971
|
+
}
|
|
972
|
+
else
|
|
973
|
+
s(new Error(`File upload failed: ${o.status}`));
|
|
974
|
+
}), o.addEventListener("error", () => s(new Error("Network error during upload"))), o.addEventListener("abort", () => s(new DOMException("Aborted", "AbortError"))), o.open("POST", this.cfg.baseUrl);
|
|
975
|
+
const f = await this.headers();
|
|
976
|
+
Object.entries(f).forEach(([b, y]) => o.setRequestHeader(b, y)), o.send(u);
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Upload multiple files with per-file progress tracking.
|
|
981
|
+
* Returns progress for each file individually.
|
|
982
|
+
*/
|
|
983
|
+
async uploadMultipleWithProgress(e, t, n, a) {
|
|
984
|
+
const r = [];
|
|
985
|
+
for (let s = 0; s < e.length; s++) {
|
|
986
|
+
if (a != null && a.aborted) throw new DOMException("Aborted", "AbortError");
|
|
987
|
+
const o = await this.uploadWithProgress(
|
|
988
|
+
e[s],
|
|
989
|
+
(u) => t(s, u),
|
|
990
|
+
n,
|
|
991
|
+
a
|
|
992
|
+
);
|
|
993
|
+
r.push(o);
|
|
994
|
+
}
|
|
995
|
+
return r;
|
|
189
996
|
}
|
|
190
997
|
}
|
|
191
|
-
class
|
|
998
|
+
class Ue {
|
|
192
999
|
constructor(e) {
|
|
193
1000
|
this.cfg = e;
|
|
194
1001
|
}
|
|
@@ -198,32 +1005,346 @@ class v {
|
|
|
198
1005
|
}
|
|
199
1006
|
/** Request a server-side export. Returns a download URL / job id. */
|
|
200
1007
|
async requestExport(e) {
|
|
201
|
-
const
|
|
1008
|
+
const n = await (this.cfg.fetch ?? globalThis.fetch)(this.cfg.baseUrl, {
|
|
202
1009
|
method: "POST",
|
|
203
1010
|
headers: await this.headers(),
|
|
204
1011
|
body: JSON.stringify(e)
|
|
205
1012
|
});
|
|
206
|
-
if (!
|
|
207
|
-
return
|
|
1013
|
+
if (!n.ok) throw new Error(`Export request failed: ${n.status}`);
|
|
1014
|
+
return n.json();
|
|
208
1015
|
}
|
|
209
1016
|
/** Download an exported file as a Blob. */
|
|
210
1017
|
async download(e) {
|
|
211
|
-
const
|
|
1018
|
+
const n = await (this.cfg.fetch ?? globalThis.fetch)(e, {
|
|
212
1019
|
headers: await this.headers()
|
|
213
1020
|
});
|
|
214
|
-
if (!
|
|
215
|
-
return
|
|
1021
|
+
if (!n.ok) throw new Error(`Export download failed: ${n.status}`);
|
|
1022
|
+
return n.blob();
|
|
216
1023
|
}
|
|
217
1024
|
/** Trigger browser download for an export. */
|
|
218
|
-
async downloadToFile(e,
|
|
219
|
-
var
|
|
220
|
-
const
|
|
221
|
-
if (!((
|
|
222
|
-
const
|
|
223
|
-
|
|
1025
|
+
async downloadToFile(e, t) {
|
|
1026
|
+
var o;
|
|
1027
|
+
const n = await this.requestExport(e);
|
|
1028
|
+
if (!((o = n.data) != null && o.downloadUrl)) throw new Error("No download URL returned");
|
|
1029
|
+
const a = await this.download(n.data.downloadUrl), r = URL.createObjectURL(a), s = document.createElement("a");
|
|
1030
|
+
s.href = r, s.download = t ?? `export.${e.format}`, document.body.appendChild(s), s.click(), document.body.removeChild(s), URL.revokeObjectURL(r);
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
function Fe(i) {
|
|
1034
|
+
return (e) => {
|
|
1035
|
+
const t = e ?? globalThis.fetch;
|
|
1036
|
+
return (a, r) => {
|
|
1037
|
+
const u = { url: typeof a == "string" ? a : a instanceof URL ? a.toString() : a.url, init: r ?? {}, meta: {} };
|
|
1038
|
+
let f = (b) => t(b.url, b.init);
|
|
1039
|
+
for (let b = i.length - 1; b >= 0; b--) {
|
|
1040
|
+
const y = i[b], V = f;
|
|
1041
|
+
f = (C) => y(C, V);
|
|
1042
|
+
}
|
|
1043
|
+
return f(u);
|
|
1044
|
+
};
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
function Oe() {
|
|
1048
|
+
return async (i, e) => {
|
|
1049
|
+
const t = performance.now(), n = (i.init.method ?? "GET").toUpperCase();
|
|
1050
|
+
try {
|
|
1051
|
+
const a = await e(i), r = (performance.now() - t).toFixed(1);
|
|
1052
|
+
return console.debug(`[ERP] ${n} ${i.url} → ${a.status} (${r}ms)`), a;
|
|
1053
|
+
} catch (a) {
|
|
1054
|
+
const r = (performance.now() - t).toFixed(1);
|
|
1055
|
+
throw console.error(`[ERP] ${n} ${i.url} FAILED (${r}ms)`, a), a;
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
function Ne(i) {
|
|
1060
|
+
const e = (i == null ? void 0 : i.maxRetries) ?? 3, t = (i == null ? void 0 : i.baseDelay) ?? 500, n = new Set((i == null ? void 0 : i.retryOn) ?? [408, 429, 500, 502, 503, 504]);
|
|
1061
|
+
return async (a, r) => {
|
|
1062
|
+
let s;
|
|
1063
|
+
for (let o = 0; o <= e; o++) {
|
|
1064
|
+
try {
|
|
1065
|
+
const f = await r(a);
|
|
1066
|
+
if (f.ok || !n.has(f.status) || o === e)
|
|
1067
|
+
return f;
|
|
1068
|
+
s = new Error(`HTTP ${f.status}`);
|
|
1069
|
+
} catch (f) {
|
|
1070
|
+
if (s = f, o === e) throw f;
|
|
1071
|
+
}
|
|
1072
|
+
const u = t * Math.pow(2, o) * (0.5 + Math.random() * 0.5);
|
|
1073
|
+
await new Promise((f) => setTimeout(f, u));
|
|
1074
|
+
}
|
|
1075
|
+
throw s;
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
function Me(i) {
|
|
1079
|
+
return async (e, t) => {
|
|
1080
|
+
const n = typeof i == "function" ? await i() : i;
|
|
1081
|
+
return e.init.headers = { ...e.init.headers, ...n }, t(e);
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
class Ie {
|
|
1085
|
+
constructor(e) {
|
|
1086
|
+
this.inFlight = 0, this.timestamps = [], this.queue = [], this.processing = !1, this.backingOff = !1, this.maxConcurrent = (e == null ? void 0 : e.maxConcurrent) ?? 6, this.maxPerSecond = (e == null ? void 0 : e.maxPerSecond) ?? 20, this.backoffDelay = (e == null ? void 0 : e.backoffDelay) ?? 2e3, this.baseFetch = (e == null ? void 0 : e.baseFetch) ?? globalThis.fetch.bind(globalThis), this.fetch = this.fetch.bind(this);
|
|
1087
|
+
}
|
|
1088
|
+
/** A fetch-compatible function that respects rate limits. */
|
|
1089
|
+
fetch(e, t) {
|
|
1090
|
+
return new Promise((n, a) => {
|
|
1091
|
+
this.queue.push({ resolve: n, reject: a, input: e, init: t }), this.drain();
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
/** Number of requests currently waiting in the queue. */
|
|
1095
|
+
get pending() {
|
|
1096
|
+
return this.queue.length;
|
|
1097
|
+
}
|
|
1098
|
+
/** Number of requests currently in flight. */
|
|
1099
|
+
get active() {
|
|
1100
|
+
return this.inFlight;
|
|
1101
|
+
}
|
|
1102
|
+
drain() {
|
|
1103
|
+
if (this.processing) return;
|
|
1104
|
+
this.processing = !0;
|
|
1105
|
+
const e = () => {
|
|
1106
|
+
if (this.queue.length === 0 || this.backingOff) {
|
|
1107
|
+
this.processing = !1;
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
if (this.inFlight >= this.maxConcurrent) {
|
|
1111
|
+
this.processing = !1;
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
const t = Date.now();
|
|
1115
|
+
if (this.timestamps = this.timestamps.filter((a) => t - a < 1e3), this.timestamps.length >= this.maxPerSecond) {
|
|
1116
|
+
const a = this.timestamps[0], r = 1e3 - (t - a) + 1;
|
|
1117
|
+
setTimeout(() => {
|
|
1118
|
+
this.processing = !1, this.drain();
|
|
1119
|
+
}, r);
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
const n = this.queue.shift();
|
|
1123
|
+
this.inFlight++, this.timestamps.push(t), this.baseFetch(n.input, n.init).then((a) => {
|
|
1124
|
+
if (this.inFlight--, a.status === 429) {
|
|
1125
|
+
this.queue.unshift(n), this.inFlight++, this.inFlight--, this.backingOff = !0, setTimeout(() => {
|
|
1126
|
+
this.backingOff = !1, this.drain();
|
|
1127
|
+
}, this.backoffDelay);
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
n.resolve(a), queueMicrotask(e);
|
|
1131
|
+
}).catch((a) => {
|
|
1132
|
+
this.inFlight--, n.reject(a), queueMicrotask(e);
|
|
1133
|
+
}), queueMicrotask(e);
|
|
1134
|
+
};
|
|
1135
|
+
e();
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
class $e {
|
|
1139
|
+
constructor(e, t = "id") {
|
|
1140
|
+
this.items = [], this.mutations = [], this.listeners = /* @__PURE__ */ new Set(), this.nextMutationId = 1, this.adapter = e, this.keyField = t;
|
|
1141
|
+
}
|
|
1142
|
+
/** Subscribe to state changes. Returns unsubscribe function. */
|
|
1143
|
+
subscribe(e) {
|
|
1144
|
+
return this.listeners.add(e), () => this.listeners.delete(e);
|
|
1145
|
+
}
|
|
1146
|
+
notify() {
|
|
1147
|
+
const e = [...this.items], t = [...this.mutations];
|
|
1148
|
+
this.listeners.forEach((n) => n(e, t));
|
|
1149
|
+
}
|
|
1150
|
+
/** Current items including optimistic (unconfirmed) changes. */
|
|
1151
|
+
getItems() {
|
|
1152
|
+
return [...this.items];
|
|
1153
|
+
}
|
|
1154
|
+
/** Pending mutations not yet confirmed by server. */
|
|
1155
|
+
getPending() {
|
|
1156
|
+
return this.mutations.filter((e) => e.status === "pending");
|
|
1157
|
+
}
|
|
1158
|
+
/** Set initial data (e.g. after load()). */
|
|
1159
|
+
setItems(e) {
|
|
1160
|
+
this.items = [...e], this.notify();
|
|
1161
|
+
}
|
|
1162
|
+
/** Load data from server and replace local state. */
|
|
1163
|
+
async load(...e) {
|
|
1164
|
+
const t = await this.adapter.load(...e);
|
|
1165
|
+
return t.success && (this.items = t.data, this.mutations = [], this.notify()), t;
|
|
1166
|
+
}
|
|
1167
|
+
/** Optimistically create an item. */
|
|
1168
|
+
async create(e) {
|
|
1169
|
+
const t = `__temp_${this.nextMutationId}`, n = { ...e, [this.keyField]: t }, r = {
|
|
1170
|
+
id: String(this.nextMutationId++),
|
|
1171
|
+
type: "create",
|
|
1172
|
+
optimisticData: n,
|
|
1173
|
+
timestamp: Date.now(),
|
|
1174
|
+
status: "pending"
|
|
1175
|
+
};
|
|
1176
|
+
this.items = [...this.items, n], this.mutations.push(r), this.notify();
|
|
1177
|
+
try {
|
|
1178
|
+
const s = await this.adapter.create(e);
|
|
1179
|
+
return s.success ? (this.items = this.items.map(
|
|
1180
|
+
(o) => o[this.keyField] === t ? s.data : o
|
|
1181
|
+
), r.status = "confirmed", r.entityId = s.data[this.keyField]) : (this.items = this.items.filter(
|
|
1182
|
+
(o) => o[this.keyField] !== t
|
|
1183
|
+
), r.status = "failed", r.error = s.error), this.notify(), s;
|
|
1184
|
+
} catch (s) {
|
|
1185
|
+
throw this.items = this.items.filter(
|
|
1186
|
+
(o) => o[this.keyField] !== t
|
|
1187
|
+
), r.status = "failed", r.error = s instanceof Error ? s.message : String(s), this.notify(), s;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
/** Optimistically update an item. */
|
|
1191
|
+
async update(e, t) {
|
|
1192
|
+
const n = this.items.findIndex(
|
|
1193
|
+
(o) => o[this.keyField] === e
|
|
1194
|
+
), a = n >= 0 ? { ...this.items[n] } : void 0, s = {
|
|
1195
|
+
id: String(this.nextMutationId++),
|
|
1196
|
+
type: "update",
|
|
1197
|
+
entityId: e,
|
|
1198
|
+
previousData: a,
|
|
1199
|
+
timestamp: Date.now(),
|
|
1200
|
+
status: "pending"
|
|
1201
|
+
};
|
|
1202
|
+
n >= 0 && (this.items = this.items.map(
|
|
1203
|
+
(o, u) => u === n ? { ...o, ...t } : o
|
|
1204
|
+
)), this.mutations.push(s), this.notify();
|
|
1205
|
+
try {
|
|
1206
|
+
const o = await this.adapter.update(e, t);
|
|
1207
|
+
return o.success ? (this.items = this.items.map(
|
|
1208
|
+
(u) => u[this.keyField] === e ? o.data : u
|
|
1209
|
+
), s.status = "confirmed") : (a && n >= 0 && (this.items = this.items.map(
|
|
1210
|
+
(u) => u[this.keyField] === e ? a : u
|
|
1211
|
+
)), s.status = "failed", s.error = o.error), this.notify(), o;
|
|
1212
|
+
} catch (o) {
|
|
1213
|
+
throw a && n >= 0 && (this.items = this.items.map(
|
|
1214
|
+
(u) => u[this.keyField] === e ? a : u
|
|
1215
|
+
)), s.status = "failed", s.error = o instanceof Error ? o.message : String(o), this.notify(), o;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
/** Optimistically delete an item. */
|
|
1219
|
+
async remove(e) {
|
|
1220
|
+
const t = this.items.findIndex(
|
|
1221
|
+
(s) => s[this.keyField] === e
|
|
1222
|
+
), n = t >= 0 ? { ...this.items[t] } : void 0, r = {
|
|
1223
|
+
id: String(this.nextMutationId++),
|
|
1224
|
+
type: "delete",
|
|
1225
|
+
entityId: e,
|
|
1226
|
+
previousData: n,
|
|
1227
|
+
timestamp: Date.now(),
|
|
1228
|
+
status: "pending"
|
|
1229
|
+
};
|
|
1230
|
+
this.items = this.items.filter(
|
|
1231
|
+
(s) => s[this.keyField] !== e
|
|
1232
|
+
), this.mutations.push(r), this.notify();
|
|
1233
|
+
try {
|
|
1234
|
+
const s = await this.adapter.remove(e);
|
|
1235
|
+
return s.success ? r.status = "confirmed" : (n && this.items.splice(t, 0, n), r.status = "failed", r.error = s.error), this.notify(), s;
|
|
1236
|
+
} catch (s) {
|
|
1237
|
+
throw n && this.items.splice(t, 0, n), r.status = "failed", r.error = s instanceof Error ? s.message : String(s), this.notify(), s;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
class Be {
|
|
1242
|
+
constructor(e) {
|
|
1243
|
+
this.queue = [], this.syncing = !1, this.nextId = 1, this.config = e ?? {}, this.baseFetch = (e == null ? void 0 : e.baseFetch) ?? globalThis.fetch.bind(globalThis), this.storageKey = (e == null ? void 0 : e.storageKey) ?? "erp_offline_", this.fetch = this.fetch.bind(this), this.restoreQueue(), this.boundOnline = () => {
|
|
1244
|
+
var t, n;
|
|
1245
|
+
(n = (t = this.config).onStatusChange) == null || n.call(t, !0), this.sync();
|
|
1246
|
+
}, this.boundOffline = () => {
|
|
1247
|
+
var t, n;
|
|
1248
|
+
(n = (t = this.config).onStatusChange) == null || n.call(t, !1);
|
|
1249
|
+
}, typeof window < "u" && (window.addEventListener("online", this.boundOnline), window.addEventListener("offline", this.boundOffline));
|
|
1250
|
+
}
|
|
1251
|
+
/** A fetch-compatible function that queues mutations when offline. */
|
|
1252
|
+
fetch(e, t) {
|
|
1253
|
+
var s, o;
|
|
1254
|
+
const n = typeof e == "string" ? e : e instanceof URL ? e.toString() : e.url, a = ((t == null ? void 0 : t.method) ?? "GET").toUpperCase();
|
|
1255
|
+
if (this.isOnline())
|
|
1256
|
+
return this.baseFetch(e, t);
|
|
1257
|
+
if (a === "GET" || a === "HEAD")
|
|
1258
|
+
return Promise.reject(new Error("Offline: GET requests are not queued"));
|
|
1259
|
+
const r = {
|
|
1260
|
+
id: `q_${this.nextId++}`,
|
|
1261
|
+
url: n,
|
|
1262
|
+
method: a,
|
|
1263
|
+
headers: { ...t == null ? void 0 : t.headers },
|
|
1264
|
+
body: typeof (t == null ? void 0 : t.body) == "string" ? t.body : void 0,
|
|
1265
|
+
timestamp: Date.now()
|
|
1266
|
+
};
|
|
1267
|
+
return this.queue.push(r), this.persistQueue(), (o = (s = this.config).onQueued) == null || o.call(s, r), Promise.resolve(new Response(
|
|
1268
|
+
JSON.stringify({ queued: !0, queueId: r.id }),
|
|
1269
|
+
{ status: 202, headers: { "Content-Type": "application/json" } }
|
|
1270
|
+
));
|
|
1271
|
+
}
|
|
1272
|
+
/** Is the browser currently online? */
|
|
1273
|
+
isOnline() {
|
|
1274
|
+
return typeof navigator > "u" || navigator.onLine;
|
|
1275
|
+
}
|
|
1276
|
+
/** Number of entries waiting to be synced. */
|
|
1277
|
+
get pendingCount() {
|
|
1278
|
+
return this.queue.length;
|
|
1279
|
+
}
|
|
1280
|
+
/** Get a snapshot of the current queue. */
|
|
1281
|
+
getQueue() {
|
|
1282
|
+
return [...this.queue];
|
|
1283
|
+
}
|
|
1284
|
+
/** Clear all pending entries without syncing. */
|
|
1285
|
+
clear() {
|
|
1286
|
+
this.queue = [], this.persistQueue();
|
|
1287
|
+
}
|
|
1288
|
+
/** Manually trigger sync (e.g. when you detect connectivity). */
|
|
1289
|
+
async sync() {
|
|
1290
|
+
var t, n;
|
|
1291
|
+
if (this.syncing || this.queue.length === 0 || !this.isOnline())
|
|
1292
|
+
return [];
|
|
1293
|
+
this.syncing = !0;
|
|
1294
|
+
const e = [];
|
|
1295
|
+
for (; this.queue.length > 0 && this.isOnline(); ) {
|
|
1296
|
+
const a = this.queue[0];
|
|
1297
|
+
try {
|
|
1298
|
+
const r = await this.baseFetch(a.url, {
|
|
1299
|
+
method: a.method,
|
|
1300
|
+
headers: a.headers,
|
|
1301
|
+
body: a.body
|
|
1302
|
+
});
|
|
1303
|
+
e.push({
|
|
1304
|
+
entry: a,
|
|
1305
|
+
success: r.ok,
|
|
1306
|
+
status: r.status,
|
|
1307
|
+
error: r.ok ? void 0 : `HTTP ${r.status}`
|
|
1308
|
+
}), this.queue.shift();
|
|
1309
|
+
} catch (r) {
|
|
1310
|
+
e.push({
|
|
1311
|
+
entry: a,
|
|
1312
|
+
success: !1,
|
|
1313
|
+
error: r instanceof Error ? r.message : String(r)
|
|
1314
|
+
});
|
|
1315
|
+
break;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
return this.persistQueue(), this.syncing = !1, e.length > 0 && ((n = (t = this.config).onSync) == null || n.call(t, e)), e;
|
|
1319
|
+
}
|
|
1320
|
+
/** Remove event listeners. */
|
|
1321
|
+
destroy() {
|
|
1322
|
+
typeof window < "u" && (window.removeEventListener("online", this.boundOnline), window.removeEventListener("offline", this.boundOffline));
|
|
1323
|
+
}
|
|
1324
|
+
persistQueue() {
|
|
1325
|
+
try {
|
|
1326
|
+
typeof localStorage < "u" && localStorage.setItem(
|
|
1327
|
+
`${this.storageKey}queue`,
|
|
1328
|
+
JSON.stringify(this.queue)
|
|
1329
|
+
);
|
|
1330
|
+
} catch {
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
restoreQueue() {
|
|
1334
|
+
try {
|
|
1335
|
+
if (typeof localStorage < "u") {
|
|
1336
|
+
const e = localStorage.getItem(`${this.storageKey}queue`);
|
|
1337
|
+
e && (this.queue = JSON.parse(e), this.nextId = this.queue.reduce(
|
|
1338
|
+
(t, n) => Math.max(t, parseInt(n.id.replace("q_", ""), 10) || 0),
|
|
1339
|
+
0
|
|
1340
|
+
) + 1);
|
|
1341
|
+
}
|
|
1342
|
+
} catch {
|
|
1343
|
+
this.queue = [];
|
|
1344
|
+
}
|
|
224
1345
|
}
|
|
225
1346
|
}
|
|
226
|
-
const
|
|
1347
|
+
const ye = [
|
|
227
1348
|
{
|
|
228
1349
|
type: "NiceAudioPlayer",
|
|
229
1350
|
label: "Audio Player",
|
|
@@ -295,7 +1416,7 @@ const c = [
|
|
|
295
1416
|
{ name: "fontSize", type: "size", label: "Font size", group: "Typography" }
|
|
296
1417
|
]
|
|
297
1418
|
}
|
|
298
|
-
],
|
|
1419
|
+
], be = [
|
|
299
1420
|
{
|
|
300
1421
|
type: "NicePixelEditor",
|
|
301
1422
|
label: "Pixel Editor",
|
|
@@ -350,7 +1471,7 @@ const c = [
|
|
|
350
1471
|
{ name: "maxWidth", type: "number", label: "Max output width", min: 100, max: 8192, group: "Output" }
|
|
351
1472
|
]
|
|
352
1473
|
}
|
|
353
|
-
],
|
|
1474
|
+
], ge = [
|
|
354
1475
|
{
|
|
355
1476
|
type: "NiceModelEditor",
|
|
356
1477
|
label: "3D Model Editor",
|
|
@@ -396,7 +1517,7 @@ const c = [
|
|
|
396
1517
|
{ name: "height", type: "size", label: "Viewport height", group: "Layout" }
|
|
397
1518
|
]
|
|
398
1519
|
}
|
|
399
|
-
],
|
|
1520
|
+
], we = [
|
|
400
1521
|
{
|
|
401
1522
|
type: "NiceLeaderboard",
|
|
402
1523
|
label: "Leaderboard",
|
|
@@ -468,7 +1589,7 @@ const c = [
|
|
|
468
1589
|
{ name: "height", type: "size", label: "Bar height", group: "Layout" }
|
|
469
1590
|
]
|
|
470
1591
|
}
|
|
471
|
-
],
|
|
1592
|
+
], ve = [
|
|
472
1593
|
{
|
|
473
1594
|
type: "NiceAddress",
|
|
474
1595
|
label: "Address Editor",
|
|
@@ -567,7 +1688,7 @@ const c = [
|
|
|
567
1688
|
{ name: "readOnly", type: "boolean", label: "Read only", defaultValue: !1 }
|
|
568
1689
|
]
|
|
569
1690
|
}
|
|
570
|
-
],
|
|
1691
|
+
], ke = [
|
|
571
1692
|
{
|
|
572
1693
|
type: "NiceLoginForm",
|
|
573
1694
|
label: "Login Form",
|
|
@@ -628,7 +1749,7 @@ const c = [
|
|
|
628
1749
|
{ name: "codeLength", type: "number", label: "Code length", min: 4, max: 8, defaultValue: 6 }
|
|
629
1750
|
]
|
|
630
1751
|
}
|
|
631
|
-
],
|
|
1752
|
+
], Se = [
|
|
632
1753
|
{
|
|
633
1754
|
type: "NiceComments",
|
|
634
1755
|
label: "Comments",
|
|
@@ -686,28 +1807,43 @@ const c = [
|
|
|
686
1807
|
{ name: "maxLength", type: "number", label: "Max content length", min: 0, defaultValue: 0 }
|
|
687
1808
|
]
|
|
688
1809
|
}
|
|
689
|
-
],
|
|
690
|
-
...
|
|
691
|
-
...
|
|
692
|
-
...
|
|
693
|
-
...
|
|
694
|
-
...
|
|
695
|
-
...
|
|
696
|
-
...
|
|
1810
|
+
], je = [
|
|
1811
|
+
...ye,
|
|
1812
|
+
...be,
|
|
1813
|
+
...ge,
|
|
1814
|
+
...we,
|
|
1815
|
+
...ve,
|
|
1816
|
+
...ke,
|
|
1817
|
+
...Se
|
|
697
1818
|
];
|
|
698
1819
|
export {
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
1820
|
+
Ae as ErpAuthAdapter,
|
|
1821
|
+
Ve as ErpDataAdapter,
|
|
1822
|
+
Ue as ErpExportAdapter,
|
|
1823
|
+
Le as ErpFileAdapter,
|
|
1824
|
+
Be as ErpOfflineQueue,
|
|
1825
|
+
$e as ErpOptimisticStore,
|
|
1826
|
+
Ie as ErpRateLimiter,
|
|
1827
|
+
je as allControlRegistries,
|
|
1828
|
+
Te as applyBatchChanges,
|
|
1829
|
+
ye as audioControlRegistry,
|
|
1830
|
+
ke as authControlRegistry,
|
|
1831
|
+
ve as businessControlRegistry,
|
|
1832
|
+
Fe as createMiddlewarePipeline,
|
|
1833
|
+
Ce as createSignalRAdapter,
|
|
1834
|
+
Pe as createSignalRDataOperations,
|
|
1835
|
+
xe as formatPresenceStatus,
|
|
1836
|
+
we as gamificationControlRegistry,
|
|
1837
|
+
fe as generateUserColor,
|
|
1838
|
+
be as graphicControlRegistry,
|
|
1839
|
+
Me as headerMiddleware,
|
|
1840
|
+
Oe as loggingMiddleware,
|
|
1841
|
+
Ne as retryMiddleware,
|
|
1842
|
+
Se as socialControlRegistry,
|
|
1843
|
+
ge as threeControlRegistry,
|
|
1844
|
+
De as useCollaborativeDataGrid,
|
|
1845
|
+
pe as useEntityPresence,
|
|
1846
|
+
Re as useMultiEntityPresence,
|
|
1847
|
+
he as useSignalRLiveData
|
|
712
1848
|
};
|
|
713
1849
|
//# sourceMappingURL=index.mjs.map
|