@harperfast/harper 5.2.4 → 5.2.6
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/bin/restart.ts +48 -23
- package/bin/restartExitWatchdog.ts +99 -0
- package/bin/run.ts +16 -10
- package/components/status/crossThread.ts +69 -44
- package/components/status/types.ts +1 -0
- package/dist/bin/restart.js +39 -21
- package/dist/bin/restart.js.map +1 -1
- package/dist/bin/restartExitWatchdog.d.ts +5 -0
- package/dist/bin/restartExitWatchdog.js +100 -0
- package/dist/bin/restartExitWatchdog.js.map +1 -0
- package/dist/bin/run.d.ts +2 -0
- package/dist/bin/run.js +17 -10
- package/dist/bin/run.js.map +1 -1
- package/dist/components/status/crossThread.d.ts +1 -0
- package/dist/components/status/crossThread.js +57 -41
- package/dist/components/status/crossThread.js.map +1 -1
- package/dist/components/status/types.d.ts +1 -0
- package/dist/resources/DatabaseTransaction.d.ts +46 -1
- package/dist/resources/DatabaseTransaction.js +173 -9
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/LMDBTransaction.d.ts +2 -0
- package/dist/resources/LMDBTransaction.js +7 -1
- package/dist/resources/LMDBTransaction.js.map +1 -1
- package/dist/resources/Resource.js +5 -4
- package/dist/resources/Resource.js.map +1 -1
- package/dist/resources/ResourceInterface.d.ts +20 -0
- package/dist/resources/ResourceInterface.js.map +1 -1
- package/dist/resources/Table.js +56 -9
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +11 -0
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +153 -51
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
- package/dist/resources/transaction.js +4 -2
- package/dist/resources/transaction.js.map +1 -1
- package/dist/server/itc/serverHandlers.js +2 -1
- package/dist/server/itc/serverHandlers.js.map +1 -1
- package/dist/server/threads/manageThreads.d.ts +2 -0
- package/dist/server/threads/manageThreads.js +37 -5
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/utility/processManagement/processManagement.js +21 -3
- package/dist/utility/processManagement/processManagement.js.map +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/resources/DESIGN.md +19 -18
- package/resources/DatabaseTransaction.ts +187 -17
- package/resources/LMDBTransaction.ts +7 -1
- package/resources/Resource.ts +6 -5
- package/resources/ResourceInterface.ts +20 -0
- package/resources/Table.ts +60 -8
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +153 -54
- package/resources/transaction.ts +5 -3
- package/schema.graphql +8 -3
- package/server/DESIGN.md +6 -0
- package/server/itc/serverHandlers.js +2 -1
- package/server/threads/manageThreads.js +34 -5
- package/studio/web/assets/{Chat-BvOlYIhe.js → Chat-BNHY84-Z.js} +1 -1
- package/studio/web/assets/{FloatingChat-fCJiJCH2.js → FloatingChat-BbztUci0.js} +3 -3
- package/studio/web/assets/{apiToken-bKwPv0uN.js → apiToken-D0x_S8Wq.js} +1 -1
- package/studio/web/assets/{applications-BZ47njJ_.js → applications-BU83fSEG.js} +1 -1
- package/studio/web/assets/{index-DF1fSnXV.js → index-BQB9R8Ug.js} +4 -4
- package/studio/web/assets/{index.lazy-DWt8ubrr.js → index.lazy-CtErm5q7.js} +3 -3
- package/studio/web/assets/{notifications-B5ZBOni1.js → notifications-B8rDagJM.js} +1 -1
- package/studio/web/assets/{notifications-gVx0lA8H.js → notifications-CPwYKDDH.js} +1 -1
- package/studio/web/assets/{profile-D7g0x28A.js → profile-D0hB1xdm.js} +1 -1
- package/studio/web/assets/{regions-DRkhlkiD.js → regions-y_jxsBpN.js} +1 -1
- package/studio/web/assets/{setComponentFile-DQkkobG5.js → setComponentFile-DSHbURTI.js} +1 -1
- package/studio/web/assets/{setup-nf-EOheW.js → setup-J5SrM1Kp.js} +1 -1
- package/studio/web/assets/{status-B2tOM3RF.js → status-IoJkDzlx.js} +1 -1
- package/studio/web/assets/{swagger-ui-react-UktCouQ0.js → swagger-ui-react-BlgSS25O.js} +1 -1
- package/studio/web/assets/{useEntityRestURL-alqAn3dT.js → useEntityRestURL-qfahJnvr.js} +1 -1
- package/studio/web/index.html +1 -1
- package/utility/processManagement/processManagement.js +19 -3
|
@@ -43,11 +43,16 @@ function dequantizeInt8(q: Int8Array, scale: number): number[] {
|
|
|
43
43
|
|
|
44
44
|
// Auto-scaled search ef, used only when an index does not explicitly configure efConstructionSearch
|
|
45
45
|
// and a query does not pass its own ef. A fixed ef makes recall decay as the graph grows (it explores
|
|
46
|
-
// a shrinking fraction of the graph), so ef grows with sqrt(node count)
|
|
47
|
-
//
|
|
48
|
-
// 5K
|
|
49
|
-
//
|
|
50
|
-
// ef
|
|
46
|
+
// a shrinking fraction of the graph), so ef grows with sqrt(node count) in two regimes, with a
|
|
47
|
+
// ceiling to bound search cost. The first regime's constants come from the original recall/latency
|
|
48
|
+
// sweep (5K-30K, 768-dim cosine, int8) and plateau at AUTO_EF_MAX from ~13K nodes; that plateau was
|
|
49
|
+
// calibrated when layers above 0 were searched at the full ef, which made large efs cost seconds.
|
|
50
|
+
// After the greedy-descent fix (#2125) ef 1024 at 5M nodes costs ~45ms, and the measured decay at a
|
|
51
|
+
// pinned 512 (set-recall 0.997 -> 0.955 -> 0.935 across 1M/2M/5M on well-built graphs, #2181) is
|
|
52
|
+
// recall left on the table, so past AUTO_EF_LARGE_REF nodes the scale resumes from that plateau and
|
|
53
|
+
// runs to AUTO_EF_CEILING. Validated against the same sweeps: the second regime resolves 1,145 at
|
|
54
|
+
// 5M, and the measured ef-1024 point there holds 0.985. Apps preferring latency pin
|
|
55
|
+
// efConstructionSearch or a per-query ef; graphs past ~tens of millions of nodes should shard.
|
|
51
56
|
const AUTO_EF_BASE = 100;
|
|
52
57
|
// The index store holds a graph node plus a primary-key mapping per record, so a key count is twice
|
|
53
58
|
// the node count. Sizes here are in nodes; this converts back for the one consumer still calibrated
|
|
@@ -58,7 +63,15 @@ const INDEX_KEYS_PER_NODE = 2;
|
|
|
58
63
|
// a live count, so the resolved ef can differ from that formula by one at a rounding boundary.
|
|
59
64
|
const AUTO_EF_REF = 500;
|
|
60
65
|
const AUTO_EF_MAX = 512;
|
|
66
|
+
// Nodes at which the second regime starts: 512 was measured sufficient through 1M (set-recall
|
|
67
|
+
// 0.997) and short from 2M up, so the resumed curve is anchored to pass through (1M, 512).
|
|
68
|
+
const AUTO_EF_LARGE_REF = 1_000_000;
|
|
69
|
+
const AUTO_EF_CEILING = 2048;
|
|
61
70
|
function autoScaleEf(nodeCount: number): number {
|
|
71
|
+
if (nodeCount > AUTO_EF_LARGE_REF) {
|
|
72
|
+
const scaled = Math.round(AUTO_EF_MAX * Math.sqrt(nodeCount / AUTO_EF_LARGE_REF));
|
|
73
|
+
return Math.min(AUTO_EF_CEILING, scaled);
|
|
74
|
+
}
|
|
62
75
|
const scaled = Math.round(AUTO_EF_BASE * Math.sqrt(Math.max(1, nodeCount / AUTO_EF_REF)));
|
|
63
76
|
return Math.min(AUTO_EF_MAX, Math.max(AUTO_EF_BASE, scaled));
|
|
64
77
|
}
|
|
@@ -69,10 +82,24 @@ function autoScaleEf(nodeCount: number): number {
|
|
|
69
82
|
const ROUTING_EF = 1;
|
|
70
83
|
// Ceiling on the ef a query's own `offset + limit` can ask for. `limit` is unprivileged and set on
|
|
71
84
|
// every request, so this bounds what a caller can make one thread do synchronously: layer 0 holds
|
|
72
|
-
// `ef` candidates in a sorted array with an O(len) insert. Kept within a small multiple of
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
const LIMIT_EF_MAX =
|
|
85
|
+
// `ef` candidates in a sorted array with an O(len) insert. Kept within a small multiple of the
|
|
86
|
+
// index's own auto-scaled ceiling so the worst case stays the same order. Schema and per-query ef
|
|
87
|
+
// pins are authoritative cost ceilings; only an automatically scaled index widens from `limit`.
|
|
88
|
+
const LIMIT_EF_MAX = 2 * AUTO_EF_CEILING;
|
|
89
|
+
// Auto-scaled construction ef, used only when an index does not explicitly configure efConstruction.
|
|
90
|
+
// At a constant efConstruction, edge quality erodes as the graph grows until true neighbours become
|
|
91
|
+
// unreachable at ANY search ef; the cap bounds per-insert cost. Measurements and policy in
|
|
92
|
+
// DESIGN.md ("efConstruction and the search-ef ceiling both auto-scale with the graph") and #2180.
|
|
93
|
+
const AUTO_EFC_REF = 250_000;
|
|
94
|
+
// Validated to 447 (the 5M point, where the resulting graph held 0.985 set-recall at ef 1024); the
|
|
95
|
+
// headroom to 1024 is the same sqrt curve extrapolated, binding at ~26M nodes. Build cost grows
|
|
96
|
+
// with the curve (N^1.5 total under sqrt scaling), which is why the cap stays finite: past ~tens of
|
|
97
|
+
// millions of nodes, sharded medium graphs beat one huge graph on both build and query cost.
|
|
98
|
+
const AUTO_EFC_MAX = 1024;
|
|
99
|
+
function autoScaleEfConstruction(nodeCount: number): number {
|
|
100
|
+
const scaled = Math.round(AUTO_EF_BASE * Math.sqrt(Math.max(1, nodeCount / AUTO_EFC_REF)));
|
|
101
|
+
return Math.min(AUTO_EFC_MAX, Math.max(AUTO_EF_BASE, scaled));
|
|
102
|
+
}
|
|
76
103
|
// How long a resolved graph size is reused before it is looked up again (see approximateNodeCount).
|
|
77
104
|
// ef moves with the square root of the count and is capped, so a slightly stale size is immaterial;
|
|
78
105
|
// this only has to be short enough that a table growing from empty picks up a larger ef promptly.
|
|
@@ -182,22 +209,22 @@ export class HierarchicalNavigableSmallWorld {
|
|
|
182
209
|
// a value of 1 is extremely aggressive.
|
|
183
210
|
optimizeRouting = 0.5;
|
|
184
211
|
nodesVisitedCount = 0;
|
|
185
|
-
// Visit-budget multiplier for predicate-aware traversal (#1241).
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
// ~ef/s visits; a multiplier of 24 fills down to ~4% selectivity before the budget bites, keeping
|
|
192
|
-
// recall at or above post-filtering across the range the query planner routes to traversal. Raising
|
|
193
|
-
// it is nearly free for condition-derived filters (they fill and self-terminate); it mainly trades
|
|
194
|
-
// latency for recall on selective *function* predicates. Per-query override via the search options.
|
|
212
|
+
// Visit-budget multiplier for predicate-aware traversal (#1241). Under-filled filtered searches
|
|
213
|
+
// stop after the resolved budget ef * filterExpansion visits; automatic search ef contributes at
|
|
214
|
+
// most AUTO_EF_MAX, while explicit schema/query ef remains authoritative. A filter that fills its
|
|
215
|
+
// candidate list terminates naturally before this bound. Raising the multiplier mainly trades
|
|
216
|
+
// latency for recall on selective function predicates; 24 fills to roughly 4% selectivity before
|
|
217
|
+
// the budget binds. Per-query override via the search options.
|
|
195
218
|
filterExpansion = 24;
|
|
196
219
|
|
|
197
220
|
idIncrementer: BigInt64Array | undefined;
|
|
198
221
|
distance: (a: number[], b: number[]) => number;
|
|
199
222
|
int8 = true; // store vectors as int8-quantized bins by default; opt out with `quantization: "none"`
|
|
200
|
-
efSearchConfigured = false; // whether the schema
|
|
223
|
+
efSearchConfigured = false; // whether the schema pins search ef directly or through efConstruction
|
|
224
|
+
efConstructionConfigured = false; // whether the schema set an explicit efConstruction; if not, it auto-scales with N
|
|
225
|
+
private lastLoggedEfConstruction = 0;
|
|
226
|
+
private idIncrementerRetryAt = 0;
|
|
227
|
+
private idIncrementerFailureLogged = false;
|
|
201
228
|
// Caches the Int8Array-converted clone of a frozen (decoded-from-disk) int8 node, keyed by the
|
|
202
229
|
// frozen node the object store hands back. WeakMap so entries are collected when the store evicts
|
|
203
230
|
// the frozen node — without it, every cache hit on a frozen node would re-slice and re-clone.
|
|
@@ -212,8 +239,9 @@ export class HierarchicalNavigableSmallWorld {
|
|
|
212
239
|
this.indexStore.encoder.useFloat32 = FLOAT32_OPTIONS.ALWAYS;
|
|
213
240
|
}
|
|
214
241
|
this.int8 = options?.quantization !== 'none';
|
|
215
|
-
// Respect an explicitly-configured
|
|
242
|
+
// Respect an explicitly-configured ef (efConstruction seeds the search ef too); otherwise auto-scale both.
|
|
216
243
|
this.efSearchConfigured = options?.efConstructionSearch !== undefined || options?.efConstruction !== undefined;
|
|
244
|
+
this.efConstructionConfigured = options?.efConstruction !== undefined;
|
|
217
245
|
this.distance =
|
|
218
246
|
options?.distance === 'euclidean'
|
|
219
247
|
? euclideanDistance
|
|
@@ -258,24 +286,8 @@ export class HierarchicalNavigableSmallWorld {
|
|
|
258
286
|
// that won't collide with the node ids, so we can't have a collision with internal
|
|
259
287
|
if (!nodeId) {
|
|
260
288
|
if (!vector) return; // didn't exist before, doesn't exist now, nothing to do
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
for (const key of this.indexStore.getKeys({
|
|
264
|
-
reverse: true,
|
|
265
|
-
limit: 1,
|
|
266
|
-
start: Infinity,
|
|
267
|
-
end: 0,
|
|
268
|
-
transaction: options.transaction,
|
|
269
|
-
})) {
|
|
270
|
-
if (typeof key === 'number') largestNodeId = key;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
this.idIncrementer = new BigInt64Array([BigInt(largestNodeId) + 1n]);
|
|
274
|
-
this.idIncrementer = new BigInt64Array(
|
|
275
|
-
this.indexStore.getUserSharedBuffer('next-id', this.idIncrementer.buffer)
|
|
276
|
-
);
|
|
277
|
-
}
|
|
278
|
-
nodeId = Number(Atomics.add(this.idIncrementer, 0, 1n));
|
|
289
|
+
this.ensureIdIncrementer(options);
|
|
290
|
+
nodeId = Number(Atomics.add(this.idIncrementer!, 0, 1n));
|
|
279
291
|
this.indexStore.put(safeKey, nodeId, options);
|
|
280
292
|
}
|
|
281
293
|
const updatedNodes = new Map<number, Node>();
|
|
@@ -375,9 +387,25 @@ export class HierarchicalNavigableSmallWorld {
|
|
|
375
387
|
connections[i] = [];
|
|
376
388
|
}
|
|
377
389
|
|
|
378
|
-
//
|
|
390
|
+
// An update-only worker may not have attached the id counter yet. The healthy path is one
|
|
391
|
+
// atomic load; a failed attach falls back to the memoized seek and retries after its TTL.
|
|
392
|
+
let efConstruction = this.efConstruction;
|
|
393
|
+
if (!this.efConstructionConfigured) {
|
|
394
|
+
// Graph size only tunes a heuristic; a write must never fail because it could not be resolved.
|
|
395
|
+
try {
|
|
396
|
+
efConstruction = autoScaleEfConstruction(this.resolveConstructionNodeCount(options));
|
|
397
|
+
} catch (error) {
|
|
398
|
+
logger.debug?.('could not resolve the HNSW construction node count; using the base ef', error);
|
|
399
|
+
}
|
|
400
|
+
if (efConstruction > this.efConstruction && this.lastLoggedEfConstruction !== efConstruction) {
|
|
401
|
+
// once per resolved value per process: makes replica-divergent build quality and the
|
|
402
|
+
// build-cost ramp diagnosable (the resolved value is otherwise surfaced nowhere)
|
|
403
|
+
this.lastLoggedEfConstruction = efConstruction;
|
|
404
|
+
logger.debug?.(`HNSW construction ef auto-scaled to ${efConstruction}`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
379
407
|
for (let l = Math.min(level, currentLevel); l >= 0; l--) {
|
|
380
|
-
let neighbors = this.searchLayer(vector, entryPointId, entryPoint,
|
|
408
|
+
let neighbors = this.searchLayer(vector, entryPointId, entryPoint, efConstruction, l, options);
|
|
381
409
|
neighbors = neighbors.slice(0, this.M << 1) as SearchResults;
|
|
382
410
|
|
|
383
411
|
if (neighbors.length === 0 && l === 0) {
|
|
@@ -531,8 +559,7 @@ export class HierarchicalNavigableSmallWorld {
|
|
|
531
559
|
}
|
|
532
560
|
}
|
|
533
561
|
this.indexStore.remove(nodeId, options);
|
|
534
|
-
//
|
|
535
|
-
// and a re-insert of this primary key gets a fresh node rather than the deleted node's id.
|
|
562
|
+
// A re-insert of this primary key must get a fresh node rather than the deleted node's id.
|
|
536
563
|
this.indexStore.remove(safeKey, options);
|
|
537
564
|
}
|
|
538
565
|
const needsReindexing = new Map();
|
|
@@ -717,19 +744,84 @@ export class HierarchicalNavigableSmallWorld {
|
|
|
717
744
|
* measurements behind both. The memo is the only gate on how often the size is resolved; nothing on
|
|
718
745
|
* the query path may bypass it, or the O(1) lookup becomes per-query work again.
|
|
719
746
|
*/
|
|
720
|
-
private approximateNodeCount(): number {
|
|
747
|
+
private approximateNodeCount(options?: any): number {
|
|
721
748
|
const now = Date.now();
|
|
722
749
|
if (this.nodeCountAt > 0 && now - this.nodeCountAt < NODE_COUNT_TTL) return this.nodeCount;
|
|
723
|
-
this.nodeCount = this.resolveNodeCount();
|
|
750
|
+
this.nodeCount = this.resolveNodeCount(options);
|
|
724
751
|
this.nodeCountAt = now;
|
|
725
752
|
return this.nodeCount;
|
|
726
753
|
}
|
|
727
754
|
|
|
755
|
+
private resolveConstructionNodeCount(options?: any): number {
|
|
756
|
+
if (this.idIncrementer) return this.resolveNodeCount();
|
|
757
|
+
const now = Date.now();
|
|
758
|
+
if (now >= this.idIncrementerRetryAt) {
|
|
759
|
+
try {
|
|
760
|
+
this.ensureIdIncrementer(options);
|
|
761
|
+
this.idIncrementerRetryAt = 0;
|
|
762
|
+
this.idIncrementerFailureLogged = false;
|
|
763
|
+
return this.resolveNodeCount();
|
|
764
|
+
} catch (error) {
|
|
765
|
+
this.idIncrementerRetryAt = now + NODE_COUNT_TTL;
|
|
766
|
+
if (!this.idIncrementerFailureLogged) {
|
|
767
|
+
this.idIncrementerFailureLogged = true;
|
|
768
|
+
logger.warn?.('could not attach the shared HNSW id counter; using a memoized node count', error);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return this.approximateNodeCount(options);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Create-or-attach the shared id counter, seeded from a one-time reverse seek to the largest node
|
|
777
|
+
* id. getUserSharedBuffer returns the existing shared buffer when another worker created it
|
|
778
|
+
* first, so the seed only matters for whoever wins the race.
|
|
779
|
+
*/
|
|
780
|
+
private ensureIdIncrementer(options?: any): void {
|
|
781
|
+
if (this.idIncrementer) return;
|
|
782
|
+
let largestNodeId = 0;
|
|
783
|
+
for (const key of this.indexStore.getKeys({
|
|
784
|
+
reverse: true,
|
|
785
|
+
limit: 1,
|
|
786
|
+
start: Infinity,
|
|
787
|
+
end: 0,
|
|
788
|
+
transaction: options?.transaction,
|
|
789
|
+
})) {
|
|
790
|
+
if (typeof key === 'number') largestNodeId = key;
|
|
791
|
+
}
|
|
792
|
+
// Never install the counter until the shared attach succeeds: assigning the private seed
|
|
793
|
+
// array first would, on an attach failure, leave THIS process allocating ids nobody else can
|
|
794
|
+
// see — cross-worker id collisions. Left unset, the next write simply retries the ensure.
|
|
795
|
+
const seed = new BigInt64Array([BigInt(largestNodeId) + 1n]);
|
|
796
|
+
try {
|
|
797
|
+
const sharedBuffer = this.indexStore.getUserSharedBuffer('next-id', seed.buffer);
|
|
798
|
+
if (
|
|
799
|
+
!sharedBuffer ||
|
|
800
|
+
sharedBuffer.byteLength < BigInt64Array.BYTES_PER_ELEMENT ||
|
|
801
|
+
sharedBuffer.byteLength % BigInt64Array.BYTES_PER_ELEMENT !== 0
|
|
802
|
+
) {
|
|
803
|
+
throw new Error('Shared HNSW id counter buffer is unusable');
|
|
804
|
+
}
|
|
805
|
+
this.idIncrementer = new BigInt64Array(sharedBuffer);
|
|
806
|
+
} catch (error) {
|
|
807
|
+
// Reuse the transactional seed seek as the degraded count instead of seeking a second time.
|
|
808
|
+
this.nodeCount = largestNodeId + 1;
|
|
809
|
+
this.nodeCountAt = Date.now();
|
|
810
|
+
throw error;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
728
814
|
/** O(1) node count — the shared id counter, else a single reverse seek to the largest node id. */
|
|
729
|
-
private resolveNodeCount(): number {
|
|
815
|
+
private resolveNodeCount(options?: any): number {
|
|
730
816
|
if (this.idIncrementer) return Number(Atomics.load(this.idIncrementer, 0));
|
|
731
817
|
try {
|
|
732
|
-
for (const key of this.indexStore.getKeys({
|
|
818
|
+
for (const key of this.indexStore.getKeys({
|
|
819
|
+
reverse: true,
|
|
820
|
+
limit: 1,
|
|
821
|
+
start: Infinity,
|
|
822
|
+
end: 0,
|
|
823
|
+
transaction: options?.transaction,
|
|
824
|
+
})) {
|
|
733
825
|
if (typeof key === 'number') return key + 1;
|
|
734
826
|
}
|
|
735
827
|
} catch (error) {
|
|
@@ -1026,8 +1118,8 @@ export class HierarchicalNavigableSmallWorld {
|
|
|
1026
1118
|
if (!Array.isArray(target)) throw new ClientError('The target vector must be an array');
|
|
1027
1119
|
|
|
1028
1120
|
const options = context.transaction; // should have a nested RocksDB transaction
|
|
1029
|
-
// Resolve search ef: per-query ef wins; else
|
|
1030
|
-
//
|
|
1121
|
+
// Resolve search ef: per-query ef wins; else use the schema-pinned value (from either ef option);
|
|
1122
|
+
// otherwise auto-scale with the graph size so recall holds as the table grows.
|
|
1031
1123
|
let effectiveEf = this.efConstructionSearch;
|
|
1032
1124
|
const explicitEf = ef !== undefined && ef > 0;
|
|
1033
1125
|
if (explicitEf) effectiveEf = ef;
|
|
@@ -1040,22 +1132,29 @@ export class HierarchicalNavigableSmallWorld {
|
|
|
1040
1132
|
// to cover the request, up to LIMIT_EF_MAX — `ef` sizes a synchronous traversal that holds every
|
|
1041
1133
|
// admitted candidate in a sorted array with an O(len) insert, so an unbounded one lets a plain
|
|
1042
1134
|
// `limit` stall the thread. Past the ceiling the result set is still short, as it was before.
|
|
1043
|
-
//
|
|
1044
|
-
//
|
|
1135
|
+
// Schema and per-query `ef` pins are authoritative cost ceilings. Only an automatically scaled
|
|
1136
|
+
// index widens toward LIMIT_EF_MAX to cover a larger bounded request.
|
|
1045
1137
|
// The ceiling is the only bound: clamping to the graph size as well would need a count exact as
|
|
1046
1138
|
// of this query — the memo reads low while a table grows, truncating the very limit this
|
|
1047
1139
|
// honours — and an ef above the node count is free, the traversal ending at the graph, not ef.
|
|
1048
|
-
if (minResults !== undefined && !explicitEf && minResults > effectiveEf) {
|
|
1140
|
+
if (minResults !== undefined && !explicitEf && !this.efSearchConfigured && minResults > effectiveEf) {
|
|
1049
1141
|
effectiveEf = Math.max(effectiveEf, Math.min(minResults, LIMIT_EF_MAX));
|
|
1050
1142
|
}
|
|
1051
1143
|
// Predicate-aware traversal budget (#1241): matches accrue slower than visits under a selective
|
|
1052
1144
|
// filter, so bound layer-0 work at ef * filterExpansion nodes. Only built when a filter is active.
|
|
1053
1145
|
// Deliberately `resolvedEf`, not the limit-widened ef: this budget is what stops a selective
|
|
1054
1146
|
// filter crawling the whole graph, and multiplying it by a caller's limit would turn a filtered
|
|
1055
|
-
// vector query into a record-loading scan.
|
|
1147
|
+
// vector query into a record-loading scan. When the ef came from the auto-scale (neither a
|
|
1148
|
+
// per-query ef nor a schema-configured one), its budget contribution is additionally capped at
|
|
1149
|
+
// AUTO_EF_MAX: every budgeted visit is a synchronous record load + predicate evaluation, so the
|
|
1150
|
+
// second-regime search ef (up to AUTO_EF_CEILING) must not silently quadruple the filtered
|
|
1151
|
+
// worst case — the recall decision and the filtered-scan budget are separate decisions. Callers
|
|
1152
|
+
// wanting a deeper filtered search set an explicit ef or filterExpansion, and own the cost.
|
|
1056
1153
|
const filterState: FilterState | undefined = filter
|
|
1057
1154
|
? {
|
|
1058
|
-
maxVisits:
|
|
1155
|
+
maxVisits:
|
|
1156
|
+
(explicitEf || this.efSearchConfigured ? resolvedEf : Math.min(resolvedEf, AUTO_EF_MAX)) *
|
|
1157
|
+
(filterExpansion && filterExpansion > 0 ? filterExpansion : this.filterExpansion),
|
|
1059
1158
|
nodesVisited: 0,
|
|
1060
1159
|
filterEvaluations: 0,
|
|
1061
1160
|
}
|
package/resources/transaction.ts
CHANGED
|
@@ -2,9 +2,9 @@ import type { Context } from './ResourceInterface.ts';
|
|
|
2
2
|
import { _assignPackageExport } from '../globals.js';
|
|
3
3
|
import {
|
|
4
4
|
DatabaseTransaction,
|
|
5
|
+
isJoinableScope,
|
|
5
6
|
isReleasedTransaction,
|
|
6
7
|
type Transaction,
|
|
7
|
-
TRANSACTION_STATE,
|
|
8
8
|
} from './DatabaseTransaction.ts';
|
|
9
9
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
10
10
|
|
|
@@ -41,11 +41,13 @@ export function transaction<T>(
|
|
|
41
41
|
if (typeof callback !== 'function') {
|
|
42
42
|
throw new TypeError('Callback function must be provided to transaction');
|
|
43
43
|
}
|
|
44
|
-
if (context?.transaction
|
|
44
|
+
if (isJoinableScope(context?.transaction) && typeof callback === 'function') {
|
|
45
45
|
return callback(context.transaction); // nothing to be done, already in open transaction
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
// scopeOwned: onComplete/onError below guarantee this instance a final commit or an abort, which is
|
|
49
|
+
// what lets a mid-scope commit rotate it instead of leaving later writes to commit themselves.
|
|
50
|
+
const transaction = new DatabaseTransaction({ scopeOwned: true });
|
|
49
51
|
context.transaction = transaction;
|
|
50
52
|
if (context.timestamp) transaction.timestamp = context.timestamp;
|
|
51
53
|
if (context.replicatedConfirmation) transaction.replicatedConfirmation = context.replicatedConfirmation;
|
package/schema.graphql
CHANGED
|
@@ -188,7 +188,9 @@ directive @indexed(
|
|
|
188
188
|
distance: String
|
|
189
189
|
"""
|
|
190
190
|
Construction effort/recall parameter (HNSW). Higher values improve recall at
|
|
191
|
-
the cost of build time and memory.
|
|
191
|
+
the cost of build time and memory. When omitted, it auto-scales with graph
|
|
192
|
+
size. Setting it pins both the build-side value and the search-side default;
|
|
193
|
+
changing it rebuilds the index.
|
|
192
194
|
"""
|
|
193
195
|
efConstruction: Int
|
|
194
196
|
"""
|
|
@@ -205,8 +207,11 @@ directive @indexed(
|
|
|
205
207
|
"""
|
|
206
208
|
mL: Int
|
|
207
209
|
"""
|
|
208
|
-
Search-time effort/recall parameter
|
|
209
|
-
|
|
210
|
+
Search-time effort/recall parameter (HNSW): the candidate-list size used when
|
|
211
|
+
querying. Larger values typically yield better accuracy at the cost of query
|
|
212
|
+
latency. When both this and efConstruction are omitted, it auto-scales with
|
|
213
|
+
graph size, continuing past one million nodes up to 2048. Pin it when query
|
|
214
|
+
latency matters more than recall on large tables.
|
|
210
215
|
"""
|
|
211
216
|
efConstructionSearch: Int
|
|
212
217
|
) on FIELD_DEFINITION
|
package/server/DESIGN.md
CHANGED
|
@@ -68,6 +68,12 @@ A request entering `http.ts` does **not** go through Fastify. The two `handleApp
|
|
|
68
68
|
| `threads/itc.js` | Inter-thread comms primitives. |
|
|
69
69
|
| `transactionLogCooling.ts` | Main-thread timer that cools transaction-log mmaps. |
|
|
70
70
|
|
|
71
|
+
Process-wide shutdown begins by calling `beginProcessShutdown()` in `threads/manageThreads.js`.
|
|
72
|
+
Once set, this terminal state prevents every worker replacement path and makes new `startWorker()`
|
|
73
|
+
calls fail with `ERR_HARPER_PROCESS_SHUTTING_DOWN`; scoped worker-type restarts do not set it.
|
|
74
|
+
`shutdownWorkersNow()` remains an immediate teardown: its worker shutdown messages are best-effort,
|
|
75
|
+
and it force-terminates the remaining worker set rather than waiting for application drain hooks.
|
|
76
|
+
|
|
71
77
|
> Workers receive `workerData.noServerStart = true` — never start the server inside a worker.
|
|
72
78
|
>
|
|
73
79
|
> `threadServer.listenOnDomainSocket()` skips a listener only when its path exceeds the platform's
|
|
@@ -11,7 +11,7 @@ const harperBridge =
|
|
|
11
11
|
require('../../dataLayer/harperBridge/harperBridge.ts').default ||
|
|
12
12
|
require('../../dataLayer/harperBridge/harperBridge.ts');
|
|
13
13
|
const process = require('process');
|
|
14
|
-
const { isMainThread, workerData } = require('worker_threads');
|
|
14
|
+
const { isMainThread, threadId, workerData } = require('node:worker_threads');
|
|
15
15
|
const { resetDatabases, closeDatabase } = require('../../resources/databases.ts');
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -187,6 +187,7 @@ async function componentStatusRequestHandler(event) {
|
|
|
187
187
|
message: {
|
|
188
188
|
requestId: event.message.requestId,
|
|
189
189
|
statuses: statusArray,
|
|
190
|
+
threadId,
|
|
190
191
|
workerIndex: workerIndex,
|
|
191
192
|
isMainThread: isMainThread,
|
|
192
193
|
},
|
|
@@ -46,6 +46,7 @@ const chokidar = require('chokidar');
|
|
|
46
46
|
const isBun = typeof globalThis.Bun !== 'undefined';
|
|
47
47
|
const MB = 1024 * 1024;
|
|
48
48
|
const workers = []; // these are our child workers that we are managing
|
|
49
|
+
let processShuttingDown = false;
|
|
49
50
|
const connectedPorts = []; // these are all known connected worker ports (siblings, children, parents)
|
|
50
51
|
const MAX_UNEXPECTED_RESTARTS = 50;
|
|
51
52
|
// Threads get 10s to die before they're forced. In dev (`harper dev`) we widen this: a reload's old
|
|
@@ -144,11 +145,13 @@ module.exports = {
|
|
|
144
145
|
broadcastWithAcknowledgement,
|
|
145
146
|
getWorkerIndex,
|
|
146
147
|
getWorkerCount,
|
|
148
|
+
getEligibleBroadcastRecipientThreadIds,
|
|
147
149
|
getTicketKeys,
|
|
148
150
|
setMainIsWorker,
|
|
149
151
|
setTerminateTimeout,
|
|
150
152
|
extendShutdownDeadline,
|
|
151
153
|
restoreShutdownDeadline,
|
|
154
|
+
beginProcessShutdown,
|
|
152
155
|
registerWorkerDataProvider,
|
|
153
156
|
onThreadExit,
|
|
154
157
|
registerProcessGroup,
|
|
@@ -192,6 +195,18 @@ function getWorkerIndex() {
|
|
|
192
195
|
function getWorkerCount() {
|
|
193
196
|
return workerData ? workerData.workerCount : isMainWorker ? 1 : undefined;
|
|
194
197
|
}
|
|
198
|
+
function isEligibleBroadcastRecipient(port) {
|
|
199
|
+
return !port.isJobWorker;
|
|
200
|
+
}
|
|
201
|
+
function getEligibleBroadcastRecipientThreadIds() {
|
|
202
|
+
const recipientThreadIds = new Set();
|
|
203
|
+
for (const port of connectedPorts) {
|
|
204
|
+
if (isEligibleBroadcastRecipient(port) && port.threadId !== undefined) {
|
|
205
|
+
recipientThreadIds.add(port.threadId);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return recipientThreadIds;
|
|
209
|
+
}
|
|
195
210
|
function setMainIsWorker(isWorker) {
|
|
196
211
|
isMainWorker = isWorker;
|
|
197
212
|
module.exports.threadsHaveStarted();
|
|
@@ -205,6 +220,7 @@ let workerCount = 1; // should be assigned when workers are created
|
|
|
205
220
|
const RESERVED_WORKER_DATA_KEYS = [
|
|
206
221
|
'addPorts',
|
|
207
222
|
'addThreadIds',
|
|
223
|
+
'addPortIsJobWorkers',
|
|
208
224
|
'workerIndex',
|
|
209
225
|
'workerCount',
|
|
210
226
|
'name',
|
|
@@ -310,6 +326,11 @@ listenersByType.set(THREAD_INFO, null);
|
|
|
310
326
|
listenersByType.set(PROCESS_GROUP_TERMINATION_CONFIRMED, null);
|
|
311
327
|
|
|
312
328
|
function startWorker(path, options = {}) {
|
|
329
|
+
if (processShuttingDown) {
|
|
330
|
+
const error = new Error('Cannot start a worker while the Harper process is shutting down');
|
|
331
|
+
error.code = 'ERR_HARPER_PROCESS_SHUTTING_DOWN';
|
|
332
|
+
throw error;
|
|
333
|
+
}
|
|
313
334
|
// Take a percentage of total memory to determine the max memory for each thread. The percentage is based
|
|
314
335
|
// on the thread count. Generally, it is unrealistic to efficiently use the majority of total memory for a single
|
|
315
336
|
// NodeJS worker since it would lead to massive swap space usage with other processes and there is significant
|
|
@@ -383,6 +404,7 @@ function startWorker(path, options = {}) {
|
|
|
383
404
|
...collectProvidedWorkerData(options),
|
|
384
405
|
addPorts: portsToSend,
|
|
385
406
|
addThreadIds: channelsToConnect.map((channel) => channel.existingPort.threadId),
|
|
407
|
+
addPortIsJobWorkers: channelsToConnect.map((channel) => channel.existingPort.isJobWorker === true),
|
|
386
408
|
workerIndex: options.workerIndex,
|
|
387
409
|
workerCount: (workerCount = options.threadCount),
|
|
388
410
|
name: options.name,
|
|
@@ -420,7 +442,7 @@ function startWorker(path, options = {}) {
|
|
|
420
442
|
});
|
|
421
443
|
worker.on('exit', (_code) => {
|
|
422
444
|
workers.splice(workers.indexOf(worker), 1);
|
|
423
|
-
if (!worker.wasShutdown && options.autoRestart !== false) {
|
|
445
|
+
if (!processShuttingDown && !worker.wasShutdown && options.autoRestart !== false) {
|
|
424
446
|
// if this wasn't an intentional shutdown, restart now (unless we have tried too many times)
|
|
425
447
|
if (worker.unexpectedRestarts < MAX_UNEXPECTED_RESTARTS) {
|
|
426
448
|
options.unexpectedRestarts = worker.unexpectedRestarts + 1;
|
|
@@ -454,6 +476,7 @@ async function restartWorkers(
|
|
|
454
476
|
startReplacementThreads = true
|
|
455
477
|
) {
|
|
456
478
|
if (isMainThread) {
|
|
479
|
+
if (processShuttingDown && startReplacementThreads) return;
|
|
457
480
|
try {
|
|
458
481
|
// we do this because it is possible for a component to chdir to itself, get re-deployed and then the cwd
|
|
459
482
|
// inode link is invalid and it can cause a lot of problems. But process.cwd() still returns the path, for
|
|
@@ -491,6 +514,8 @@ async function restartWorkers(
|
|
|
491
514
|
// listenOnPorts() treat a dedicated listener's EADDRINUSE as an external conflict.
|
|
492
515
|
const canPreStartReplacement = process.platform !== 'win32' && process.platform !== 'darwin' && !isBun;
|
|
493
516
|
for (let worker of workers.slice(0)) {
|
|
517
|
+
// Terminal shutdown: stop replacing workers mid-loop — the guard for every replacement start below.
|
|
518
|
+
if (processShuttingDown && startReplacementThreads) break;
|
|
494
519
|
if ((name && worker.name !== name) || worker.wasShutdown) continue; // filter by type, if specified
|
|
495
520
|
const overlapping = OVERLAPPING_RESTART_TYPES.indexOf(worker.name) > -1;
|
|
496
521
|
if (overlapping && startReplacementThreads && canPreStartReplacement) {
|
|
@@ -575,7 +600,7 @@ async function restartWorkers(
|
|
|
575
600
|
// Overlapping types we couldn't pre-start (Windows/Bun): start the replacement now that the old
|
|
576
601
|
// worker is releasing its port. server.close() stops accepting immediately, so the port frees up
|
|
577
602
|
// well before the replacement finishes booting and binds.
|
|
578
|
-
if (overlapping && startReplacementThreads && !canPreStartReplacement) worker.startCopy();
|
|
603
|
+
if (overlapping && startReplacementThreads && !canPreStartReplacement && !processShuttingDown) worker.startCopy();
|
|
579
604
|
let whenDone = new Promise((resolve) => {
|
|
580
605
|
// in case the exit inside the thread doesn't timeout, force it from the outside
|
|
581
606
|
const armTerminate = (delay) =>
|
|
@@ -612,7 +637,7 @@ async function restartWorkers(
|
|
|
612
637
|
const index = waitingToFinish.indexOf(whenDone);
|
|
613
638
|
if (index > -1) waitingToFinish.splice(index, 1);
|
|
614
639
|
// non-overlapping types have no advance replacement, so start it once the old one is gone
|
|
615
|
-
if (!overlapping && startReplacementThreads) worker.startCopy();
|
|
640
|
+
if (!overlapping && startReplacementThreads && !processShuttingDown) worker.startCopy();
|
|
616
641
|
resolve();
|
|
617
642
|
});
|
|
618
643
|
});
|
|
@@ -635,7 +660,11 @@ async function restartWorkers(
|
|
|
635
660
|
function shutdownWorkers(name) {
|
|
636
661
|
return restartWorkers(name, Infinity, false);
|
|
637
662
|
}
|
|
663
|
+
function beginProcessShutdown() {
|
|
664
|
+
processShuttingDown = true;
|
|
665
|
+
}
|
|
638
666
|
async function shutdownWorkersNow(name) {
|
|
667
|
+
if (name == null) beginProcessShutdown();
|
|
639
668
|
shutdownWorkers(name); // set the state of all the workers to shut down. this should finish the important stuff synchronously
|
|
640
669
|
if (isBun) {
|
|
641
670
|
// worker.terminate() triggers a NAPI segfault in Bun; ask workers to self-exit instead
|
|
@@ -714,7 +743,7 @@ function broadcastWithAcknowledgement(message, timeout = DEFAULT_ACK_TIMEOUT_MS)
|
|
|
714
743
|
// schema-change gossip. Including them causes a deadlock: the broadcast waits for
|
|
715
744
|
// the job worker's ACK while the job worker's event loop is busy waiting for the
|
|
716
745
|
// same broadcast to complete (re-entrant schema change triggered by the job op).
|
|
717
|
-
if (port
|
|
746
|
+
if (!isEligibleBroadcastRecipient(port)) continue;
|
|
718
747
|
try {
|
|
719
748
|
let requestId = nextId++;
|
|
720
749
|
const ackHandler = () => {
|
|
@@ -842,7 +871,7 @@ if (parentPort && workerData?.addPorts) {
|
|
|
842
871
|
for (let i = 0, l = workerData.addPorts.length; i < l; i++) {
|
|
843
872
|
let port = workerData.addPorts[i];
|
|
844
873
|
port.threadId = workerData.addThreadIds[i];
|
|
845
|
-
addPort(port);
|
|
874
|
+
addPort(port, false, workerData.addPortIsJobWorkers?.[i]);
|
|
846
875
|
}
|
|
847
876
|
setInterval(() => {
|
|
848
877
|
// post our memory usage as a resource report, reporting our memory usage
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as e,t}from"./rolldown-runtime-B0Z9INg1.js";import{C as n,S as r,_ as i,b as a,c as o,f as s,g as c,h as l,i as u,l as d,m as f,n as p,o as m,p as h,r as g,s as _,u as v,v as ee,x as te,y}from"./vendor-core-CddkYGh5.js";import{i as b,t as x}from"./button-DfBmT4rc.js";import{H as ne,L as re,k as ie,z as ae}from"./vendor-tanstack-FsM5XZNE.js";import{a as oe}from"./vendor-datadog-XDrpA25D.js";import{r as se}from"./vendor-react-BhazFIp8.js";import{Rt as S}from"./vendor-ui-BLIveqaL.js";import{t as C}from"./createLucideIcon-BMkDbMrz.js";import{l as ce,t as le}from"./react-VeAY02s6.js";import{i as ue,n as de,r as fe,t as pe}from"./x-D39AGKMb.js";import{t as me}from"./chevron-right-CY-tr2sG.js";import{c as he,f as ge,g as _e,h as ve,i as ye,l as be,m as xe,p as Se,r as Ce,t as we,u as Te}from"./setComponentFile-
|
|
1
|
+
import{a as e,t}from"./rolldown-runtime-B0Z9INg1.js";import{C as n,S as r,_ as i,b as a,c as o,f as s,g as c,h as l,i as u,l as d,m as f,n as p,o as m,p as h,r as g,s as _,u as v,v as ee,x as te,y}from"./vendor-core-CddkYGh5.js";import{i as b,t as x}from"./button-DfBmT4rc.js";import{H as ne,L as re,k as ie,z as ae}from"./vendor-tanstack-FsM5XZNE.js";import{a as oe}from"./vendor-datadog-XDrpA25D.js";import{r as se}from"./vendor-react-BhazFIp8.js";import{Rt as S}from"./vendor-ui-BLIveqaL.js";import{t as C}from"./createLucideIcon-BMkDbMrz.js";import{l as ce,t as le}from"./react-VeAY02s6.js";import{i as ue,n as de,r as fe,t as pe}from"./x-D39AGKMb.js";import{t as me}from"./chevron-right-CY-tr2sG.js";import{c as he,f as ge,g as _e,h as ve,i as ye,l as be,m as xe,p as Se,r as Ce,t as we,u as Te}from"./setComponentFile-DSHbURTI.js";import{r as Ee}from"./queryClient-BLhQvmWC.js";import{n as De}from"./setLocalStorage-CD_L8p_D.js";import{t as Oe}from"./useLocalStorage-DetQlhBe.js";import{o as ke}from"./pollUnlessForbidden-DIDXGBc5.js";import{$n as Ae,At as je,Gt as Me,H as Ne,Kn as Pe,Nt as Fe,Ot as Ie,Un as Le,V as Re,Vn as ze,Wn as Be,Y as Ve,Yn as He,_t as Ue,ar as We,c as Ge,ct as Ke,d as qe,dn as Je,er as Ye,f as Xe,jt as Ze,kt as Qe,m as $e,or as et,rr as tt,ut as nt}from"./index-BQB9R8Ug.js";import{t as rt}from"./useEntityRestURL-qfahJnvr.js";import{n as it}from"./getAnalytics-rTxZLIM5.js";var at=C(`between-horizontal-start`,[[`rect`,{width:`13`,height:`7`,x:`8`,y:`3`,rx:`1`,key:`pkso9a`}],[`path`,{d:`m2 9 3 3-3 3`,key:`1agib5`}],[`rect`,{width:`13`,height:`7`,x:`8`,y:`14`,rx:`1`,key:`1q5fc1`}]]),ot=C(`book`,[[`path`,{d:`M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20`,key:`k3hazp`}]]),st=C(`chart-area`,[[`path`,{d:`M3 3v16a2 2 0 0 0 2 2h16`,key:`c24i48`}],[`path`,{d:`M7 11.207a.5.5 0 0 1 .146-.353l2-2a.5.5 0 0 1 .708 0l3.292 3.292a.5.5 0 0 0 .708 0l4.292-4.292a.5.5 0 0 1 .854.353V16a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1z`,key:`q0gr47`}]]),ct=C(`circle-x`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m15 9-6 6`,key:`1uzhvr`}],[`path`,{d:`m9 9 6 6`,key:`z0biqf`}]]),lt=C(`file-pen`,[[`path`,{d:`M12.659 22H18a2 2 0 0 0 2-2V8a2.4 2.4 0 0 0-.706-1.706l-3.588-3.588A2.4 2.4 0 0 0 14 2H6a2 2 0 0 0-2 2v9.34`,key:`o6klzx`}],[`path`,{d:`M14 2v5a1 1 0 0 0 1 1h5`,key:`wfsgrz`}],[`path`,{d:`M10.378 12.622a1 1 0 0 1 3 3.003L8.36 20.637a2 2 0 0 1-.854.506l-2.867.837a.5.5 0 0 1-.62-.62l.836-2.869a2 2 0 0 1 .506-.853z`,key:`zhnas1`}]]),ut=C(`logs`,[[`path`,{d:`M3 5h1`,key:`1mv5vm`}],[`path`,{d:`M3 12h1`,key:`lp3yf2`}],[`path`,{d:`M3 19h1`,key:`w6f3n9`}],[`path`,{d:`M8 5h1`,key:`1nxr5w`}],[`path`,{d:`M8 12h1`,key:`1con00`}],[`path`,{d:`M8 19h1`,key:`k7p10e`}],[`path`,{d:`M13 5h8`,key:`a7qcls`}],[`path`,{d:`M13 12h8`,key:`h98zly`}],[`path`,{d:`M13 19h8`,key:`c3s6r1`}]]),dt=C(`message-square-heart`,[[`path`,{d:`M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z`,key:`18887p`}],[`path`,{d:`M7.5 9.5c0 .687.265 1.383.697 1.844l3.009 3.264a1.14 1.14 0 0 0 .407.314 1 1 0 0 0 .783-.004 1.14 1.14 0 0 0 .398-.31l3.008-3.264A2.77 2.77 0 0 0 16.5 9.5 2.5 2.5 0 0 0 12 8a2.5 2.5 0 0 0-4.5 1.5`,key:`1faxuh`}]]),ft=C(`send`,[[`path`,{d:`M14.536 21.686a.5.5 0 0 0 .937-.024l6.5-19a.496.496 0 0 0-.635-.635l-19 6.5a.5.5 0 0 0-.024.937l7.93 3.18a2 2 0 0 1 1.112 1.11z`,key:`1ffxy3`}],[`path`,{d:`m21.854 2.147-10.94 10.939`,key:`12cjpa`}]]),pt=C(`wrench`,[[`path`,{d:`M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z`,key:`1ngwbx`}]]);async function mt(){await b.delete(`/Chat/Messages/`)}var w=e(oe(),1),T=se();function ht({setMessages:e}){let[t,n]=(0,w.useState)(!1),r=(0,w.useCallback)(async()=>{if(!t){n(!0);try{await mt(),e([])}catch(e){console.error(`Failed to clear chat:`,e)}finally{n(!1)}}},[t,e]);return(0,T.jsxs)(`button`,{type:`button`,className:`clear-chat-button gap-1`,onClick:r,disabled:t,title:`Clear chat`,children:[t?(0,T.jsx)(Ae,{className:`animate-spin`,size:18}):(0,T.jsx)(Be,{size:18}),`Clear`]})}async function gt(){let{data:e}=await b.get(`/Chat/Messages/`);return e}var _t=`vercel.ai.error`,vt=Symbol.for(_t),yt,bt,E=class e extends (bt=Error,yt=vt,bt){constructor({name:e,message:t,cause:n}){super(t),this[yt]=!0,this.name=e,this.cause=n}static isInstance(t){return e.hasMarker(t,_t)}static hasMarker(e,t){let n=Symbol.for(t);return typeof e==`object`&&!!e&&n in e&&typeof e[n]==`boolean`&&e[n]===!0}};function xt(e){return e==null?`unknown error`:typeof e==`string`?e:e instanceof Error?e.toString():JSON.stringify(e)}var St=`AI_InvalidArgumentError`,Ct=`vercel.ai.error.${St}`,wt=Symbol.for(Ct),Tt,Et,Dt=class extends (Et=E,Tt=wt,Et){constructor({message:e,cause:t,argument:n}){super({name:St,message:e,cause:t}),this[Tt]=!0,this.argument=n}static isInstance(e){return E.hasMarker(e,Ct)}},Ot=`AI_JSONParseError`,kt=`vercel.ai.error.${Ot}`,At=Symbol.for(kt),jt,Mt,Nt=class extends (Mt=E,jt=At,Mt){constructor({text:e,cause:t}){super({name:Ot,message:`JSON parsing failed: Text: ${e}.
|
|
2
2
|
Error message: ${xt(t)}`,cause:t}),this[jt]=!0,this.text=e}static isInstance(e){return E.hasMarker(e,kt)}},Pt=`AI_TypeValidationError`,Ft=`vercel.ai.error.${Pt}`,It=Symbol.for(Ft),Lt,Rt,D=class e extends (Rt=E,Lt=It,Rt){constructor({value:e,cause:t,context:n}){let r=`Type validation failed`;if(n?.field&&(r+=` for ${n.field}`),n?.entityName||n?.entityId){r+=` (`;let e=[];n.entityName&&e.push(n.entityName),n.entityId&&e.push(`id: "${n.entityId}"`),r+=e.join(`, `),r+=`)`}super({name:Pt,message:`${r}: Value: ${JSON.stringify(e)}.
|
|
3
3
|
Error message: ${xt(t)}`,cause:t}),this[Lt]=!0,this.value=e,this.context=n}static isInstance(e){return E.hasMarker(e,Ft)}static wrap({value:t,cause:n,context:r}){return e.isInstance(n)&&n.value===t&&n.context?.field===r?.field&&n.context?.entityName===r?.entityName&&n.context?.entityId===r?.entityId?n:new e({value:t,cause:n,context:r})}},zt=class extends Error{constructor(e,t){super(e),this.name=`ParseError`,this.type=t.type,this.field=t.field,this.value=t.value,this.line=t.line}},Bt=10,Vt=13,O=32;function Ht(e){}function Ut(e){if(typeof e==`function`)throw TypeError("`config` must be an object, got a function instead. Did you mean `createParser({onEvent: fn})`?");let{onEvent:t=Ht,onError:n=Ht,onRetry:r=Ht,onComment:i,maxBufferSize:a}=e,o=[],s=0,c=!0,l,u=``,d=0,f,p=!1;function m(e){if(p)throw Error("Cannot feed parser: it was terminated after exceeding the configured max buffer size. Call `reset()` to resume parsing.");if(c&&(c=!1,e.charCodeAt(0)===239&&e.charCodeAt(1)===187&&e.charCodeAt(2)===191&&(e=e.slice(3))),o.length===0){let t=g(e);t!==``&&(o.push(t),s=t.length),h();return}if(e.indexOf(`
|
|
4
4
|
`)===-1&&e.indexOf(`\r`)===-1){o.push(e),s+=e.length,h();return}o.push(e);let t=o.join(``);o.length=0,s=0;let n=g(t);n!==``&&(o.push(n),s=n.length),h()}function h(){a!==void 0&&(s+u.length<=a||(p=!0,o.length=0,s=0,l=void 0,u=``,d=0,f=void 0,n(new zt(`Buffered data exceeded max buffer size of ${a} characters`,{type:`max-buffer-size-exceeded`}))))}function g(e){let n=0;if(e.indexOf(`\r`)===-1){let r=e.indexOf(`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/Chat-
|
|
2
|
-
import{a as e}from"./rolldown-runtime-B0Z9INg1.js";import{a as t,i as n}from"./vendor-datadog-XDrpA25D.js";import{r}from"./vendor-react-BhazFIp8.js";import{a as i,c as a,i as o,l as s,n as c,o as l,r as u,s as d,t as f}from"./react-VeAY02s6.js";import{n as p}from"./setLocalStorage-CD_L8p_D.js";import{t as m}from"./useLocalStorage-DetQlhBe.js";import{At as h,kn as g}from"./index-
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/Chat-BNHY84-Z.js","assets/rolldown-runtime-B0Z9INg1.js","assets/vendor-core-CddkYGh5.js","assets/button-DfBmT4rc.js","assets/vendor-datadog-XDrpA25D.js","assets/vendor-react-BhazFIp8.js","assets/vendor-ui-BLIveqaL.js","assets/vendor-tanstack-FsM5XZNE.js","assets/createLucideIcon-BMkDbMrz.js","assets/react-VeAY02s6.js","assets/x-D39AGKMb.js","assets/chevron-right-CY-tr2sG.js","assets/setComponentFile-DSHbURTI.js","assets/pollUnlessForbidden-DIDXGBc5.js","assets/authStore-B3IhXrn6.js","assets/index-BQB9R8Ug.js","assets/useAuth-DiJUZS1C.js","assets/card-ByrPS_wa.js","assets/select-CKgJgpkn.js","assets/setLocalStorage-CD_L8p_D.js","assets/useLocalStorage-DetQlhBe.js","assets/getRegistrationInfo--NLLrics.js","assets/table-BvFflPWi.js","assets/errorText-NpBmvk9I.js","assets/queryClient-BLhQvmWC.js","assets/textarea-a3RnTY31.js","assets/tokenization-CWuvR2gh.js","assets/index-BO3NaEw8.css","assets/useEntityRestURL-qfahJnvr.js","assets/getAnalytics-rTxZLIM5.js","assets/Chat-B-7kW9XZ.css"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{a as e}from"./rolldown-runtime-B0Z9INg1.js";import{a as t,i as n}from"./vendor-datadog-XDrpA25D.js";import{r}from"./vendor-react-BhazFIp8.js";import{a as i,c as a,i as o,l as s,n as c,o as l,r as u,s as d,t as f}from"./react-VeAY02s6.js";import{n as p}from"./setLocalStorage-CD_L8p_D.js";import{t as m}from"./useLocalStorage-DetQlhBe.js";import{At as h,kn as g}from"./index-BQB9R8Ug.js";var _=e(t(),1);function v(e,t){if(typeof e==`function`)return e(t);e!=null&&(e.current=t)}function y(...e){return t=>{let n=!1,r=e.map(e=>{let r=v(e,t);return!n&&typeof r==`function`&&(n=!0),r});if(n)return()=>{for(let t=0;t<r.length;t++){let n=r[t];typeof n==`function`?n():v(e[t],null)}}}}function b(...e){return _.useCallback(y(...e),e)}var x=r(),S=class extends _.Component{getSnapshotBeforeUpdate(e){let t=this.props.childRef.current;if(o(t)&&e.isPresent&&!this.props.isPresent&&this.props.pop!==!1){let e=t.offsetParent,n=o(e)&&e.offsetWidth||0,r=o(e)&&e.offsetHeight||0,i=getComputedStyle(t),a=this.props.sizeRef.current;a.height=parseFloat(i.height),a.width=parseFloat(i.width),a.top=t.offsetTop,a.left=t.offsetLeft,a.right=n-a.width-a.left,a.bottom=r-a.height-a.top,a.direction=i.direction}return null}componentDidUpdate(){}render(){return this.props.children}};function C({children:e,isPresent:t,anchorX:n,anchorY:r,root:i,pop:a}){let o=(0,_.useId)(),s=(0,_.useRef)(null),c=(0,_.useRef)({width:0,height:0,top:0,left:0,right:0,bottom:0,direction:`ltr`}),{nonce:l}=(0,_.useContext)(u),d=b(s,a===!1?void 0:e.props?.ref??e?.ref);return(0,_.useInsertionEffect)(()=>{let{width:e,height:u,top:d,left:f,right:p,bottom:m,direction:h}=c.current;if(t||a===!1||!s.current||!e||!u)return;let g=h===`rtl`,_=n===`left`?g?`right: ${p}`:`left: ${f}`:g?`left: ${f}`:`right: ${p}`,v=r===`bottom`?`bottom: ${m}`:`top: ${d}`;s.current.dataset.motionPopId=o;let y=document.createElement(`style`);l&&(y.nonce=l);let b=i??document.head;return b.appendChild(y),y.sheet&&y.sheet.insertRule(`
|
|
3
3
|
[data-motion-pop-id="${o}"] {
|
|
4
4
|
position: absolute !important;
|
|
5
5
|
width: ${e}px !important;
|
|
@@ -7,7 +7,7 @@ import{a as e}from"./rolldown-runtime-B0Z9INg1.js";import{a as t,i as n}from"./v
|
|
|
7
7
|
${_}px !important;
|
|
8
8
|
${v}px !important;
|
|
9
9
|
}
|
|
10
|
-
`),()=>{s.current?.removeAttribute(`data-motion-pop-id`),b.contains(y)&&b.removeChild(y)}},[t]),(0,x.jsx)(S,{isPresent:t,childRef:s,sizeRef:c,pop:a,children:a===!1?e:_.cloneElement(e,{ref:d})})}var w=({children:e,initial:t,isPresent:n,onExitComplete:r,custom:a,presenceAffectsLayout:o,mode:s,anchorX:c,anchorY:u,root:f})=>{let p=d(T),m=(0,_.useId)(),h=(0,_.useRef)(n),g=(0,_.useRef)(r);l(()=>{h.current=n,g.current=r});let v=!0,y=(0,_.useMemo)(()=>(v=!1,{id:m,initial:t,isPresent:n,custom:a,onExitComplete:e=>{p.set(e,!0);for(let e of p.values())if(!e)return;r&&r()},register:e=>(p.set(e,!1),()=>{p.delete(e),!h.current&&!p.size&&g.current?.()})}),[n,p,r]);return o&&v&&(y={...y}),(0,_.useMemo)(()=>{p.forEach((e,t)=>p.set(t,!1))},[n]),_.useEffect(()=>{!n&&!p.size&&r&&r()},[n]),e=(0,x.jsx)(C,{pop:s===`popLayout`,isPresent:n,anchorX:c,anchorY:u,root:f,children:e}),(0,x.jsx)(i.Provider,{value:y,children:e})};function T(){return new Map}var E=e=>e.key||``;function D(e){let t=[];return _.Children.forEach(e,e=>{(0,_.isValidElement)(e)&&t.push(e)}),t}var O=({children:e,custom:t,initial:n=!0,onExitComplete:r,presenceAffectsLayout:i=!0,mode:o=`sync`,propagate:s=!1,anchorX:u=`left`,anchorY:f=`top`,root:p})=>{let[m,h]=c(s),g=(0,_.useMemo)(()=>D(e),[e]),v=s&&!m?[]:g.map(E),y=(0,_.useRef)(!0),b=(0,_.useRef)(g),S=d(()=>new Map),C=(0,_.useRef)(new Set),[T,O]=(0,_.useState)(g),[k,A]=(0,_.useState)(g);l(()=>{s&&!m&&!k.length&&h?.()},[m,s,k.length,h]),l(()=>{y.current=!1,b.current=g;for(let e=0;e<k.length;e++){let t=E(k[e]);v.includes(t)?(S.delete(t),C.current.delete(t)):S.get(t)!==!0&&S.set(t,!1)}},[k,v.length,v.join(`-`)]);let j=[];if(g!==T){let e=[...g];for(let t=0;t<k.length;t++){let n=k[t],r=E(n);v.includes(r)||(e.splice(t,0,n),j.push(n))}return o===`wait`&&j.length&&(e=j),A(D(e)),O(g),null}let{forceRender:M}=(0,_.useContext)(a);return(0,x.jsx)(x.Fragment,{children:k.map(e=>{let a=E(e),c=s&&!m?!1:g===k||v.includes(a);return(0,x.jsx)(w,{isPresent:c,initial:!y.current||n?void 0:!1,custom:t,presenceAffectsLayout:i,mode:o,root:p,onExitComplete:c?void 0:()=>{if(C.current.has(a))return;if(S.has(a))C.current.add(a),S.set(a,!0);else return;let e=!0;S.forEach(t=>{t||(e=!1)}),e&&(M?.(),A(b.current),s&&h?.(),r&&r())},anchorX:u,anchorY:f,children:e},a)})})},k=(0,_.lazy)(()=>n(()=>import(`./Chat-
|
|
10
|
+
`),()=>{s.current?.removeAttribute(`data-motion-pop-id`),b.contains(y)&&b.removeChild(y)}},[t]),(0,x.jsx)(S,{isPresent:t,childRef:s,sizeRef:c,pop:a,children:a===!1?e:_.cloneElement(e,{ref:d})})}var w=({children:e,initial:t,isPresent:n,onExitComplete:r,custom:a,presenceAffectsLayout:o,mode:s,anchorX:c,anchorY:u,root:f})=>{let p=d(T),m=(0,_.useId)(),h=(0,_.useRef)(n),g=(0,_.useRef)(r);l(()=>{h.current=n,g.current=r});let v=!0,y=(0,_.useMemo)(()=>(v=!1,{id:m,initial:t,isPresent:n,custom:a,onExitComplete:e=>{p.set(e,!0);for(let e of p.values())if(!e)return;r&&r()},register:e=>(p.set(e,!1),()=>{p.delete(e),!h.current&&!p.size&&g.current?.()})}),[n,p,r]);return o&&v&&(y={...y}),(0,_.useMemo)(()=>{p.forEach((e,t)=>p.set(t,!1))},[n]),_.useEffect(()=>{!n&&!p.size&&r&&r()},[n]),e=(0,x.jsx)(C,{pop:s===`popLayout`,isPresent:n,anchorX:c,anchorY:u,root:f,children:e}),(0,x.jsx)(i.Provider,{value:y,children:e})};function T(){return new Map}var E=e=>e.key||``;function D(e){let t=[];return _.Children.forEach(e,e=>{(0,_.isValidElement)(e)&&t.push(e)}),t}var O=({children:e,custom:t,initial:n=!0,onExitComplete:r,presenceAffectsLayout:i=!0,mode:o=`sync`,propagate:s=!1,anchorX:u=`left`,anchorY:f=`top`,root:p})=>{let[m,h]=c(s),g=(0,_.useMemo)(()=>D(e),[e]),v=s&&!m?[]:g.map(E),y=(0,_.useRef)(!0),b=(0,_.useRef)(g),S=d(()=>new Map),C=(0,_.useRef)(new Set),[T,O]=(0,_.useState)(g),[k,A]=(0,_.useState)(g);l(()=>{s&&!m&&!k.length&&h?.()},[m,s,k.length,h]),l(()=>{y.current=!1,b.current=g;for(let e=0;e<k.length;e++){let t=E(k[e]);v.includes(t)?(S.delete(t),C.current.delete(t)):S.get(t)!==!0&&S.set(t,!1)}},[k,v.length,v.join(`-`)]);let j=[];if(g!==T){let e=[...g];for(let t=0;t<k.length;t++){let n=k[t],r=E(n);v.includes(r)||(e.splice(t,0,n),j.push(n))}return o===`wait`&&j.length&&(e=j),A(D(e)),O(g),null}let{forceRender:M}=(0,_.useContext)(a);return(0,x.jsx)(x.Fragment,{children:k.map(e=>{let a=E(e),c=s&&!m?!1:g===k||v.includes(a);return(0,x.jsx)(w,{isPresent:c,initial:!y.current||n?void 0:!1,custom:t,presenceAffectsLayout:i,mode:o,root:p,onExitComplete:c?void 0:()=>{if(C.current.has(a))return;if(S.has(a))C.current.add(a),S.set(a,!0);else return;let e=!0;S.forEach(t=>{t||(e=!1)}),e&&(M?.(),A(b.current),s&&h?.(),r&&r())},anchorX:u,anchorY:f,children:e},a)})})},k=(0,_.lazy)(()=>n(()=>import(`./Chat-BNHY84-Z.js`).then(e=>({default:e.Chat})),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30]))),A=56,j=24;function M(){let[e,t]=(0,_.useState)(!1),[n,r]=(0,_.useState)(!1),[i,a]=(0,_.useState)(!1),[o,c]=h(`ApplicationChatOpen`,!1),[l,u]=m(p.ApplicationChatPosition,{x:-40,y:-40}),[d,v]=m(p.ApplicationChatWidth,600),y=(0,_.useRef)(null);(0,_.useEffect)(()=>{let e=()=>{let e=window.innerWidth<768;t(e),!e&&d>window.innerWidth&&v(window.innerWidth)};return e(),window.addEventListener(`resize`,e),()=>window.removeEventListener(`resize`,e)},[d,v]);let b=(0,_.useCallback)(e=>{e.preventDefault(),a(!0)},[]);(0,_.useEffect)(()=>{if(!i)return;let e=e=>{let t=window.innerWidth-e.clientX,n=Math.min(Math.max(t,300),window.innerWidth);v(n)},t=()=>{a(!1)};return window.addEventListener(`mousemove`,e),window.addEventListener(`mouseup`,t),()=>{window.removeEventListener(`mousemove`,e),window.removeEventListener(`mouseup`,t)}},[i,v]);let S=(0,_.useCallback)(()=>{n||c(e=>!e)},[n]),C=(0,_.useCallback)(()=>{c(!1)},[]);return(0,_.useEffect)(()=>{if(o){let e=e=>{e.key===`Escape`&&C()};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)}},[o,C]),(0,x.jsxs)(`div`,{className:`fixed inset-0 pointer-events-none z-50`,ref:y,children:[(0,x.jsx)(O,{children:o&&(0,x.jsxs)(f.div,{initial:e?{opacity:0,y:`100%`}:{opacity:0,x:`100%`},animate:e?{opacity:1,y:0}:{opacity:1,x:0},exit:e?{opacity:0,y:`100%`}:{opacity:0,x:`100%`},transition:{type:`spring`,damping:25,stiffness:200},style:e?{}:{top:0,bottom:0,right:0,width:d,maxWidth:`100vw`},className:`
|
|
11
11
|
pointer-events-auto
|
|
12
12
|
fixed bg-background shadow-2xl border-l border-border overflow-visible
|
|
13
13
|
${e?`inset-0 w-full h-full rounded-none`:`h-full`}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as e}from"./rolldown-runtime-B0Z9INg1.js";import{i as t,t as n}from"./button-DfBmT4rc.js";import{I as r}from"./vendor-tanstack-FsM5XZNE.js";import{a as i}from"./vendor-datadog-XDrpA25D.js";import{r as a}from"./vendor-react-BhazFIp8.js";import{a as o,i as s,n as c,r as l,t as u}from"./card-ByrPS_wa.js";import{Kt as d,ir as f,rn as p,sr as m}from"./index-
|
|
1
|
+
import{a as e}from"./rolldown-runtime-B0Z9INg1.js";import{i as t,t as n}from"./button-DfBmT4rc.js";import{I as r}from"./vendor-tanstack-FsM5XZNE.js";import{a as i}from"./vendor-datadog-XDrpA25D.js";import{r as a}from"./vendor-react-BhazFIp8.js";import{a as o,i as s,n as c,r as l,t as u}from"./card-ByrPS_wa.js";import{Kt as d,ir as f,rn as p,sr as m}from"./index-BQB9R8Ug.js";async function h(){let{data:e}=await t.post(`/Admin/ApiToken`,{});return e}function g(){return r({mutationFn:h,gcTime:0})}var _=e(i(),1),v=a();function y(){let{mutate:e,isPending:t,reset:r}=g(),[i,a]=(0,_.useState)(null),h=d();return(0,v.jsxs)(`div`,{className:`max-w-2xl`,children:[(0,v.jsx)(`h1`,{className:`text-2xl font-light`,children:`API Token`}),(0,v.jsxs)(`p`,{className:`mt-2 text-sm text-muted-foreground`,children:[`Generate a short-lived token for programmatic API access. It authenticates as you, with your permissions. Send it as a bearer token:`,` `,(0,v.jsx)(`code`,{className:`rounded bg-muted px-1 py-0.5 text-xs`,children:`Authorization: Bearer <token>`})]}),(0,v.jsxs)(n,{className:`mt-4`,variant:`submit`,onClick:()=>{e(void 0,{onSuccess:e=>{a(e),r()}})},disabled:t,children:[(0,v.jsx)(f,{}),t?`Generating…`:`Generate token`]}),i&&(0,v.jsxs)(u,{className:`mt-6`,children:[(0,v.jsxs)(s,{children:[(0,v.jsx)(o,{children:`Your API token`}),(0,v.jsxs)(l,{children:[`Copy it now — it won't be shown again. Expires `,new Date(i.expiresAt).toLocaleString(),`.`]})]}),(0,v.jsxs)(c,{className:`flex items-center gap-2`,children:[(0,v.jsx)(p,{readOnly:!0,value:i.operationToken,className:`font-mono text-xs`,onClick:()=>h(i.operationToken)}),(0,v.jsx)(n,{type:`button`,size:`icon`,variant:`ghost`,className:`shrink-0`,"aria-label":`Copy token`,onClick:()=>h(i.operationToken),children:(0,v.jsx)(m,{})})]})]})]})}export{y as ApiTokenIndex};
|