@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.
Files changed (72) hide show
  1. package/bin/restart.ts +48 -23
  2. package/bin/restartExitWatchdog.ts +99 -0
  3. package/bin/run.ts +16 -10
  4. package/components/status/crossThread.ts +69 -44
  5. package/components/status/types.ts +1 -0
  6. package/dist/bin/restart.js +39 -21
  7. package/dist/bin/restart.js.map +1 -1
  8. package/dist/bin/restartExitWatchdog.d.ts +5 -0
  9. package/dist/bin/restartExitWatchdog.js +100 -0
  10. package/dist/bin/restartExitWatchdog.js.map +1 -0
  11. package/dist/bin/run.d.ts +2 -0
  12. package/dist/bin/run.js +17 -10
  13. package/dist/bin/run.js.map +1 -1
  14. package/dist/components/status/crossThread.d.ts +1 -0
  15. package/dist/components/status/crossThread.js +57 -41
  16. package/dist/components/status/crossThread.js.map +1 -1
  17. package/dist/components/status/types.d.ts +1 -0
  18. package/dist/resources/DatabaseTransaction.d.ts +46 -1
  19. package/dist/resources/DatabaseTransaction.js +173 -9
  20. package/dist/resources/DatabaseTransaction.js.map +1 -1
  21. package/dist/resources/LMDBTransaction.d.ts +2 -0
  22. package/dist/resources/LMDBTransaction.js +7 -1
  23. package/dist/resources/LMDBTransaction.js.map +1 -1
  24. package/dist/resources/Resource.js +5 -4
  25. package/dist/resources/Resource.js.map +1 -1
  26. package/dist/resources/ResourceInterface.d.ts +20 -0
  27. package/dist/resources/ResourceInterface.js.map +1 -1
  28. package/dist/resources/Table.js +56 -9
  29. package/dist/resources/Table.js.map +1 -1
  30. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +11 -0
  31. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +153 -51
  32. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  33. package/dist/resources/transaction.js +4 -2
  34. package/dist/resources/transaction.js.map +1 -1
  35. package/dist/server/itc/serverHandlers.js +2 -1
  36. package/dist/server/itc/serverHandlers.js.map +1 -1
  37. package/dist/server/threads/manageThreads.d.ts +2 -0
  38. package/dist/server/threads/manageThreads.js +37 -5
  39. package/dist/server/threads/manageThreads.js.map +1 -1
  40. package/dist/utility/processManagement/processManagement.js +21 -3
  41. package/dist/utility/processManagement/processManagement.js.map +1 -1
  42. package/npm-shrinkwrap.json +2 -2
  43. package/package.json +1 -1
  44. package/resources/DESIGN.md +19 -18
  45. package/resources/DatabaseTransaction.ts +187 -17
  46. package/resources/LMDBTransaction.ts +7 -1
  47. package/resources/Resource.ts +6 -5
  48. package/resources/ResourceInterface.ts +20 -0
  49. package/resources/Table.ts +60 -8
  50. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +153 -54
  51. package/resources/transaction.ts +5 -3
  52. package/schema.graphql +8 -3
  53. package/server/DESIGN.md +6 -0
  54. package/server/itc/serverHandlers.js +2 -1
  55. package/server/threads/manageThreads.js +34 -5
  56. package/studio/web/assets/{Chat-BvOlYIhe.js → Chat-BNHY84-Z.js} +1 -1
  57. package/studio/web/assets/{FloatingChat-fCJiJCH2.js → FloatingChat-BbztUci0.js} +3 -3
  58. package/studio/web/assets/{apiToken-bKwPv0uN.js → apiToken-D0x_S8Wq.js} +1 -1
  59. package/studio/web/assets/{applications-BZ47njJ_.js → applications-BU83fSEG.js} +1 -1
  60. package/studio/web/assets/{index-DF1fSnXV.js → index-BQB9R8Ug.js} +4 -4
  61. package/studio/web/assets/{index.lazy-DWt8ubrr.js → index.lazy-CtErm5q7.js} +3 -3
  62. package/studio/web/assets/{notifications-B5ZBOni1.js → notifications-B8rDagJM.js} +1 -1
  63. package/studio/web/assets/{notifications-gVx0lA8H.js → notifications-CPwYKDDH.js} +1 -1
  64. package/studio/web/assets/{profile-D7g0x28A.js → profile-D0hB1xdm.js} +1 -1
  65. package/studio/web/assets/{regions-DRkhlkiD.js → regions-y_jxsBpN.js} +1 -1
  66. package/studio/web/assets/{setComponentFile-DQkkobG5.js → setComponentFile-DSHbURTI.js} +1 -1
  67. package/studio/web/assets/{setup-nf-EOheW.js → setup-J5SrM1Kp.js} +1 -1
  68. package/studio/web/assets/{status-B2tOM3RF.js → status-IoJkDzlx.js} +1 -1
  69. package/studio/web/assets/{swagger-ui-react-UktCouQ0.js → swagger-ui-react-BlgSS25O.js} +1 -1
  70. package/studio/web/assets/{useEntityRestURL-alqAn3dT.js → useEntityRestURL-qfahJnvr.js} +1 -1
  71. package/studio/web/index.html +1 -1
  72. 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), capped to bound search cost.
47
- // Constants from a recall/latency-vs-N sweep (768-dim cosine, int8): ef≈400 holds ~0.8 recall@10 from
48
- // 5K30K, and the recall/latency tradeoff is steep (ef 800 at 30K 0.92 recall but ~2s p50), so the
49
- // cap deliberately favors latency apps wanting higher recall set efConstructionSearch or a per-query
50
- // ef. Tune as graph build quality / larger-N data improves.
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
- // AUTO_EF_MAX so the worst case stays the same order as the index's own auto-scaled ceiling; a
74
- // caller who genuinely wants more sets an explicit `ef` and owns the cost.
75
- const LIMIT_EF_MAX = 4 * AUTO_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). When a filter is selective enough
186
- // that layer-0 results never fill `ef`, the "closest candidate worse than worst result" stop rule
187
- // never triggers, so maxVisits = ef * filterExpansion caps how many nodes an under-filled filtered
188
- // search visits before returning what it has (approximate, like all ANN). It does NOT bound a filter
189
- // that fills `ef` that terminates naturally. Default 24 (not the 8 the design sketch assumed):
190
- // this HNSW visits a large fraction of the graph per query, so filling `ef` at selectivity `s` needs
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 set an explicit search ef; if not, search ef auto-scales with N
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 search ef (or efConstruction, which seeds it); otherwise auto-scale.
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
- if (!this.idIncrementer) {
262
- let largestNodeId = 0;
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
- // Connect the new element to neighbors at its level and below
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, this.efConstruction, l, options);
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
- // Remove the safeKey→nodeId mapping so the key count used by autoScaleEf stays accurate
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({ reverse: true, limit: 1, start: Infinity, end: 0 })) {
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 an explicitly-configured efConstructionSearch;
1030
- // else auto-scale with the graph size so recall holds as the table grows.
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
- // A per-query `ef` is left authoritative: it is an explicit cost ceiling, and a caller who sets
1044
- // one has said what they are willing to spend.
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: resolvedEf * (filterExpansion && filterExpansion > 0 ? filterExpansion : this.filterExpansion),
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
  }
@@ -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?.open === TRANSACTION_STATE.OPEN && typeof callback === 'function') {
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
- const transaction = new DatabaseTransaction();
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 used during construction (implementation-
209
- specific). Larger values typically yield better accuracy.
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.isJobWorker) continue;
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-DQkkobG5.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-DF1fSnXV.js";import{t as rt}from"./useEntityRestURL-alqAn3dT.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}.
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-BvOlYIhe.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-DQkkobG5.js","assets/pollUnlessForbidden-DIDXGBc5.js","assets/authStore-B3IhXrn6.js","assets/index-DF1fSnXV.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-alqAn3dT.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-DF1fSnXV.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(`
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-BvOlYIhe.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:`
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-DF1fSnXV.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};
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};