@matdata/yasgui 5.5.0 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/ts/src/ConfigExportImport.js +2 -0
- package/build/ts/src/ConfigExportImport.js.map +1 -1
- package/build/ts/src/PersistentConfig.d.ts +3 -0
- package/build/ts/src/PersistentConfig.js +7 -0
- package/build/ts/src/PersistentConfig.js.map +1 -1
- package/build/ts/src/Tab.d.ts +0 -1
- package/build/ts/src/Tab.js +54 -82
- package/build/ts/src/Tab.js.map +1 -1
- package/build/ts/src/TabSettingsModal.js +145 -22
- package/build/ts/src/TabSettingsModal.js.map +1 -1
- package/build/ts/src/defaults.js +1 -1
- package/build/ts/src/defaults.js.map +1 -1
- package/build/ts/src/index.d.ts +9 -6
- package/build/ts/src/index.js +4 -1
- package/build/ts/src/index.js.map +1 -1
- package/build/ts/src/version.d.ts +1 -1
- package/build/ts/src/version.js +1 -1
- package/build/yasgui.min.css +1 -1
- package/build/yasgui.min.css.map +3 -3
- package/build/yasgui.min.js +117 -117
- package/build/yasgui.min.js.map +3 -3
- package/package.json +1 -1
- package/src/ConfigExportImport.ts +2 -0
- package/src/PersistentConfig.ts +10 -0
- package/src/Tab.ts +107 -110
- package/src/TabSettingsModal.ts +175 -26
- package/src/defaults.ts +1 -1
- package/src/endpointSelect.scss +12 -0
- package/src/index.ts +27 -10
- package/src/version.ts +1 -1
package/package.json
CHANGED
|
@@ -305,6 +305,8 @@ export function parseFromTurtle(turtle: string): Partial<PersistedJson> {
|
|
|
305
305
|
withCredentials: false,
|
|
306
306
|
adjustQueryBeforeRequest: false,
|
|
307
307
|
basicAuth: undefined,
|
|
308
|
+
bearerAuth: undefined,
|
|
309
|
+
apiKeyAuth: undefined,
|
|
308
310
|
},
|
|
309
311
|
yasr: {
|
|
310
312
|
settings: {},
|
package/src/PersistentConfig.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface PersistedJson {
|
|
|
14
14
|
endpointConfigs?: EndpointConfig[]; // New endpoint-based storage with auth
|
|
15
15
|
theme?: "light" | "dark";
|
|
16
16
|
orientation?: "vertical" | "horizontal";
|
|
17
|
+
showSnippetsBar?: boolean;
|
|
17
18
|
}
|
|
18
19
|
function getDefaults(): PersistedJson {
|
|
19
20
|
return {
|
|
@@ -166,6 +167,15 @@ export default class PersistentConfig {
|
|
|
166
167
|
this.toStorage();
|
|
167
168
|
}
|
|
168
169
|
|
|
170
|
+
public getShowSnippetsBar(): boolean | undefined {
|
|
171
|
+
return this.persistedJson.showSnippetsBar;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
public setShowSnippetsBar(show: boolean) {
|
|
175
|
+
this.persistedJson.showSnippetsBar = show;
|
|
176
|
+
this.toStorage();
|
|
177
|
+
}
|
|
178
|
+
|
|
169
179
|
// New endpoint configuration methods
|
|
170
180
|
public getEndpointConfigs(): EndpointConfig[] {
|
|
171
181
|
return this.persistedJson.endpointConfigs || [];
|
package/src/Tab.ts
CHANGED
|
@@ -20,9 +20,11 @@ const VERTICAL_LAYOUT_ICON = `<svg viewBox="0 0 24 24" class="svgImg">
|
|
|
20
20
|
<rect x="2" y="2" width="20" height="8" stroke="currentColor" stroke-width="2" fill="none"/>
|
|
21
21
|
<rect x="2" y="12" width="20" height="10" stroke="currentColor" stroke-width="2" fill="none"/>
|
|
22
22
|
</svg>`;
|
|
23
|
+
|
|
23
24
|
export interface PersistedJsonYasr extends YasrPersistentConfig {
|
|
24
25
|
responseSummary: Parser.ResponseSummary;
|
|
25
26
|
}
|
|
27
|
+
|
|
26
28
|
export interface PersistedJson {
|
|
27
29
|
name: string;
|
|
28
30
|
id: string;
|
|
@@ -37,6 +39,7 @@ export interface PersistedJson {
|
|
|
37
39
|
requestConfig: YasguiRequestConfig;
|
|
38
40
|
orientation?: "vertical" | "horizontal";
|
|
39
41
|
}
|
|
42
|
+
|
|
40
43
|
export interface Tab {
|
|
41
44
|
on(event: string | symbol, listener: (...args: any[]) => void): this;
|
|
42
45
|
|
|
@@ -59,6 +62,7 @@ export interface Tab {
|
|
|
59
62
|
on(event: "autocompletionClose", listener: (tab: Tab) => void): this;
|
|
60
63
|
emit(event: "autocompletionClose", tab: Tab): boolean;
|
|
61
64
|
}
|
|
65
|
+
|
|
62
66
|
export class Tab extends EventEmitter {
|
|
63
67
|
private persistentJson: PersistedJson;
|
|
64
68
|
public yasgui: Yasgui;
|
|
@@ -73,6 +77,7 @@ export class Tab extends EventEmitter {
|
|
|
73
77
|
private settingsModal?: TabSettingsModal;
|
|
74
78
|
private currentOrientation: "vertical" | "horizontal";
|
|
75
79
|
private orientationToggleButton?: HTMLButtonElement;
|
|
80
|
+
|
|
76
81
|
constructor(yasgui: Yasgui, conf: PersistedJson) {
|
|
77
82
|
super();
|
|
78
83
|
if (!conf || conf.id === undefined) throw new Error("Expected a valid configuration to initialize tab with");
|
|
@@ -80,15 +85,19 @@ export class Tab extends EventEmitter {
|
|
|
80
85
|
this.persistentJson = conf;
|
|
81
86
|
this.currentOrientation = this.yasgui.config.orientation || "vertical";
|
|
82
87
|
}
|
|
88
|
+
|
|
83
89
|
public name() {
|
|
84
90
|
return this.persistentJson.name;
|
|
85
91
|
}
|
|
92
|
+
|
|
86
93
|
public getPersistedJson() {
|
|
87
94
|
return this.persistentJson;
|
|
88
95
|
}
|
|
96
|
+
|
|
89
97
|
public getId() {
|
|
90
98
|
return this.persistentJson.id;
|
|
91
99
|
}
|
|
100
|
+
|
|
92
101
|
private draw() {
|
|
93
102
|
if (this.rootEl) return; //aready drawn
|
|
94
103
|
this.rootEl = document.createElement("div");
|
|
@@ -128,10 +137,12 @@ export class Tab extends EventEmitter {
|
|
|
128
137
|
this.initYasr();
|
|
129
138
|
this.yasgui._setPanel(this.persistentJson.id, this.rootEl);
|
|
130
139
|
}
|
|
140
|
+
|
|
131
141
|
public hide() {
|
|
132
142
|
removeClass(this.rootEl, "active");
|
|
133
143
|
this.detachKeyboardListeners();
|
|
134
144
|
}
|
|
145
|
+
|
|
135
146
|
public show() {
|
|
136
147
|
this.draw();
|
|
137
148
|
addClass(this.rootEl, "active");
|
|
@@ -200,9 +211,11 @@ export class Tab extends EventEmitter {
|
|
|
200
211
|
private detachKeyboardListeners() {
|
|
201
212
|
document.removeEventListener("keydown", this.handleKeyDown);
|
|
202
213
|
}
|
|
214
|
+
|
|
203
215
|
public select() {
|
|
204
216
|
this.yasgui.selectTabId(this.persistentJson.id);
|
|
205
217
|
}
|
|
218
|
+
|
|
206
219
|
public close() {
|
|
207
220
|
this.detachKeyboardListeners();
|
|
208
221
|
if (this.yasqe) this.yasqe.abortQuery();
|
|
@@ -222,12 +235,14 @@ export class Tab extends EventEmitter {
|
|
|
222
235
|
this.yasgui.tabElements.get(this.persistentJson.id).delete();
|
|
223
236
|
delete this.yasgui._tabs[this.persistentJson.id];
|
|
224
237
|
}
|
|
238
|
+
|
|
225
239
|
public getQuery() {
|
|
226
240
|
if (!this.yasqe) {
|
|
227
241
|
throw new Error("Cannot get value from uninitialized editor");
|
|
228
242
|
}
|
|
229
243
|
return this.yasqe?.getValue();
|
|
230
244
|
}
|
|
245
|
+
|
|
231
246
|
public setQuery(query: string) {
|
|
232
247
|
if (!this.yasqe) {
|
|
233
248
|
throw new Error("Cannot set value for uninitialized editor");
|
|
@@ -237,9 +252,11 @@ export class Tab extends EventEmitter {
|
|
|
237
252
|
this.emit("change", this, this.persistentJson);
|
|
238
253
|
return this;
|
|
239
254
|
}
|
|
255
|
+
|
|
240
256
|
public getRequestConfig() {
|
|
241
257
|
return this.persistentJson.requestConfig;
|
|
242
258
|
}
|
|
259
|
+
|
|
243
260
|
private initControlbar() {
|
|
244
261
|
this.initOrientationToggle();
|
|
245
262
|
this.initEndpointSelectField();
|
|
@@ -313,12 +330,15 @@ export class Tab extends EventEmitter {
|
|
|
313
330
|
}
|
|
314
331
|
}
|
|
315
332
|
}
|
|
333
|
+
|
|
316
334
|
public getYasqe() {
|
|
317
335
|
return this.yasqe;
|
|
318
336
|
}
|
|
337
|
+
|
|
319
338
|
public getYasr() {
|
|
320
339
|
return this.yasr;
|
|
321
340
|
}
|
|
341
|
+
|
|
322
342
|
private initTabSettingsMenu() {
|
|
323
343
|
if (!this.controlBarEl) throw new Error("Need to initialize wrapper elements before drawing tab settings");
|
|
324
344
|
this.settingsModal = new TabSettingsModal(this, this.controlBarEl);
|
|
@@ -397,26 +417,11 @@ export class Tab extends EventEmitter {
|
|
|
397
417
|
});
|
|
398
418
|
}
|
|
399
419
|
|
|
400
|
-
private checkEndpointForCors(endpoint: string) {
|
|
401
|
-
if (this.yasgui.config.corsProxy && !(endpoint in Yasgui.corsEnabled)) {
|
|
402
|
-
const askUrl = new URL(endpoint);
|
|
403
|
-
askUrl.searchParams.append("query", "ASK {?x ?y ?z}");
|
|
404
|
-
fetch(askUrl.toString())
|
|
405
|
-
.then(() => {
|
|
406
|
-
Yasgui.corsEnabled[endpoint] = true;
|
|
407
|
-
})
|
|
408
|
-
.catch((e) => {
|
|
409
|
-
// CORS error throws `TypeError: NetworkError when attempting to fetch resource.`
|
|
410
|
-
Yasgui.corsEnabled[endpoint] = e instanceof TypeError ? false : true;
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
420
|
public setEndpoint(endpoint: string, endpointHistory?: string[]) {
|
|
415
421
|
if (endpoint) endpoint = endpoint.trim();
|
|
416
422
|
if (endpointHistory && !eq(endpointHistory, this.yasgui.persistentConfig.getEndpointHistory())) {
|
|
417
423
|
this.yasgui.emit("endpointHistoryChange", this.yasgui, endpointHistory);
|
|
418
424
|
}
|
|
419
|
-
this.checkEndpointForCors(endpoint); //little cost in checking this as we're caching the check results
|
|
420
425
|
|
|
421
426
|
if (this.persistentJson.requestConfig.endpoint !== endpoint) {
|
|
422
427
|
this.persistentJson.requestConfig.endpoint = endpoint;
|
|
@@ -433,9 +438,11 @@ export class Tab extends EventEmitter {
|
|
|
433
438
|
}
|
|
434
439
|
return this;
|
|
435
440
|
}
|
|
441
|
+
|
|
436
442
|
public getEndpoint(): string {
|
|
437
443
|
return getAsValue(this.persistentJson.requestConfig.endpoint, this.yasgui);
|
|
438
444
|
}
|
|
445
|
+
|
|
439
446
|
/**
|
|
440
447
|
* Updates the position of the Tab's contextmenu
|
|
441
448
|
* Useful for when being scrolled
|
|
@@ -443,21 +450,26 @@ export class Tab extends EventEmitter {
|
|
|
443
450
|
public updateContextMenu(): void {
|
|
444
451
|
this.getTabListEl().redrawContextMenu();
|
|
445
452
|
}
|
|
453
|
+
|
|
446
454
|
public getShareableLink(baseURL?: string): string {
|
|
447
455
|
return shareLink.createShareLink(baseURL || window.location.href, this);
|
|
448
456
|
}
|
|
457
|
+
|
|
449
458
|
public getShareObject() {
|
|
450
459
|
return shareLink.createShareConfig(this);
|
|
451
460
|
}
|
|
461
|
+
|
|
452
462
|
private getTabListEl(): TabListEl {
|
|
453
463
|
return this.yasgui.tabElements.get(this.persistentJson.id);
|
|
454
464
|
}
|
|
465
|
+
|
|
455
466
|
public setName(newName: string) {
|
|
456
467
|
this.getTabListEl().rename(newName);
|
|
457
468
|
this.persistentJson.name = newName;
|
|
458
469
|
this.emit("change", this, this.persistentJson);
|
|
459
470
|
return this;
|
|
460
471
|
}
|
|
472
|
+
|
|
461
473
|
public hasResults() {
|
|
462
474
|
return !!this.yasr?.results;
|
|
463
475
|
}
|
|
@@ -465,10 +477,12 @@ export class Tab extends EventEmitter {
|
|
|
465
477
|
public getName() {
|
|
466
478
|
return this.persistentJson.name;
|
|
467
479
|
}
|
|
480
|
+
|
|
468
481
|
public query(): Promise<any> {
|
|
469
482
|
if (!this.yasqe) return Promise.reject(new Error("No yasqe editor initialized"));
|
|
470
483
|
return this.yasqe.query();
|
|
471
484
|
}
|
|
485
|
+
|
|
472
486
|
public setRequestConfig(requestConfig: Partial<YasguiRequestConfig>) {
|
|
473
487
|
this.persistentJson.requestConfig = {
|
|
474
488
|
...this.persistentJson.requestConfig,
|
|
@@ -490,10 +504,29 @@ export class Tab extends EventEmitter {
|
|
|
490
504
|
if (!endpointConfig || !endpointConfig.authentication) return undefined;
|
|
491
505
|
|
|
492
506
|
// Convert endpoint auth to requestConfig format
|
|
493
|
-
|
|
507
|
+
const auth = endpointConfig.authentication;
|
|
508
|
+
if (auth.type === "basic") {
|
|
509
|
+
return {
|
|
510
|
+
type: "basic" as const,
|
|
511
|
+
config: {
|
|
512
|
+
username: auth.username,
|
|
513
|
+
password: auth.password,
|
|
514
|
+
},
|
|
515
|
+
};
|
|
516
|
+
} else if (auth.type === "bearer") {
|
|
494
517
|
return {
|
|
495
|
-
|
|
496
|
-
|
|
518
|
+
type: "bearer" as const,
|
|
519
|
+
config: {
|
|
520
|
+
token: auth.token,
|
|
521
|
+
},
|
|
522
|
+
};
|
|
523
|
+
} else if (auth.type === "apiKey") {
|
|
524
|
+
return {
|
|
525
|
+
type: "apiKey" as const,
|
|
526
|
+
config: {
|
|
527
|
+
headerName: auth.headerName,
|
|
528
|
+
apiKey: auth.apiKey,
|
|
529
|
+
},
|
|
497
530
|
};
|
|
498
531
|
}
|
|
499
532
|
|
|
@@ -539,6 +572,8 @@ export class Tab extends EventEmitter {
|
|
|
539
572
|
persistenceId: null, //yasgui handles persistent storing
|
|
540
573
|
consumeShareLink: null, //not handled by this tab, but by parent yasgui instance
|
|
541
574
|
createShareableLink: () => this.getShareableLink(),
|
|
575
|
+
// Use global showSnippetsBar setting if it exists
|
|
576
|
+
showSnippetsBar: this.yasgui.config.showSnippetsBar !== false,
|
|
542
577
|
requestConfig: () => {
|
|
543
578
|
const processedReqConfig: YasguiRequestConfig = {
|
|
544
579
|
//setting defaults
|
|
@@ -562,24 +597,18 @@ export class Tab extends EventEmitter {
|
|
|
562
597
|
};
|
|
563
598
|
|
|
564
599
|
// Inject authentication from endpoint-based storage
|
|
565
|
-
// Only inject endpoint-based auth if
|
|
600
|
+
// Only inject endpoint-based auth if the corresponding auth type is not already set
|
|
566
601
|
const endpointAuth = this.getAuthForCurrentEndpoint();
|
|
567
|
-
if (endpointAuth
|
|
568
|
-
processedReqConfig.basicAuth
|
|
602
|
+
if (endpointAuth) {
|
|
603
|
+
if (endpointAuth.type === "basic" && typeof processedReqConfig.basicAuth === "undefined") {
|
|
604
|
+
processedReqConfig.basicAuth = endpointAuth.config;
|
|
605
|
+
} else if (endpointAuth.type === "bearer" && typeof processedReqConfig.bearerAuth === "undefined") {
|
|
606
|
+
processedReqConfig.bearerAuth = endpointAuth.config;
|
|
607
|
+
} else if (endpointAuth.type === "apiKey" && typeof processedReqConfig.apiKeyAuth === "undefined") {
|
|
608
|
+
processedReqConfig.apiKeyAuth = endpointAuth.config;
|
|
609
|
+
}
|
|
569
610
|
}
|
|
570
611
|
|
|
571
|
-
if (this.yasgui.config.corsProxy && !Yasgui.corsEnabled[this.getEndpoint()]) {
|
|
572
|
-
return {
|
|
573
|
-
...processedReqConfig,
|
|
574
|
-
args: [
|
|
575
|
-
...(Array.isArray(processedReqConfig.args) ? processedReqConfig.args : []),
|
|
576
|
-
{ name: "endpoint", value: this.getEndpoint() },
|
|
577
|
-
{ name: "method", value: this.persistentJson.requestConfig.method },
|
|
578
|
-
],
|
|
579
|
-
method: "POST",
|
|
580
|
-
endpoint: this.yasgui.config.corsProxy,
|
|
581
|
-
} as PlainRequestConfig;
|
|
582
|
-
}
|
|
583
612
|
return processedReqConfig as PlainRequestConfig;
|
|
584
613
|
},
|
|
585
614
|
};
|
|
@@ -608,6 +637,7 @@ export class Tab extends EventEmitter {
|
|
|
608
637
|
// Add Ctrl+Click handler for URIs
|
|
609
638
|
this.attachYasqeMouseHandler();
|
|
610
639
|
}
|
|
640
|
+
|
|
611
641
|
private destroyYasqe() {
|
|
612
642
|
// As Yasqe extends of CM instead of eventEmitter, it doesn't expose the removeAllListeners function, so we should unregister all events manually
|
|
613
643
|
this.yasqe?.off("blur", this.handleYasqeBlur);
|
|
@@ -622,12 +652,14 @@ export class Tab extends EventEmitter {
|
|
|
622
652
|
this.yasqe?.destroy();
|
|
623
653
|
this.yasqe = undefined;
|
|
624
654
|
}
|
|
655
|
+
|
|
625
656
|
handleYasqeBlur = (yasqe: Yasqe) => {
|
|
626
657
|
this.persistentJson.yasqe.value = yasqe.getValue();
|
|
627
658
|
// Capture prefixes from query if auto-capture is enabled
|
|
628
659
|
this.settingsModal?.capturePrefixesFromQuery();
|
|
629
660
|
this.emit("change", this, this.persistentJson);
|
|
630
661
|
};
|
|
662
|
+
|
|
631
663
|
handleYasqeQuery = (yasqe: Yasqe) => {
|
|
632
664
|
//the blur event might not have fired (e.g. when pressing ctrl-enter). So, we'd like to persist the query as well if needed
|
|
633
665
|
if (yasqe.getValue() !== this.persistentJson.yasqe.value) {
|
|
@@ -636,6 +668,7 @@ export class Tab extends EventEmitter {
|
|
|
636
668
|
}
|
|
637
669
|
this.emit("query", this);
|
|
638
670
|
};
|
|
671
|
+
|
|
639
672
|
handleYasqeQueryAbort = () => {
|
|
640
673
|
this.emit("queryAbort", this);
|
|
641
674
|
// Hide loading indicator in Yasr
|
|
@@ -643,6 +676,7 @@ export class Tab extends EventEmitter {
|
|
|
643
676
|
this.yasr.hideLoading();
|
|
644
677
|
}
|
|
645
678
|
};
|
|
679
|
+
|
|
646
680
|
handleYasqeQueryBefore = () => {
|
|
647
681
|
this.emit("queryBefore", this);
|
|
648
682
|
// Show loading indicator in Yasr
|
|
@@ -650,16 +684,20 @@ export class Tab extends EventEmitter {
|
|
|
650
684
|
this.yasr.showLoading();
|
|
651
685
|
}
|
|
652
686
|
};
|
|
687
|
+
|
|
653
688
|
handleYasqeResize = (_yasqe: Yasqe, newSize: string) => {
|
|
654
689
|
this.persistentJson.yasqe.editorHeight = newSize;
|
|
655
690
|
this.emit("change", this, this.persistentJson);
|
|
656
691
|
};
|
|
692
|
+
|
|
657
693
|
handleAutocompletionShown = (_yasqe: Yasqe, widget: string) => {
|
|
658
694
|
this.emit("autocompletionShown", this, widget);
|
|
659
695
|
};
|
|
696
|
+
|
|
660
697
|
handleAutocompletionClose = (_yasqe: Yasqe) => {
|
|
661
698
|
this.emit("autocompletionClose", this);
|
|
662
699
|
};
|
|
700
|
+
|
|
663
701
|
handleQueryResponse = (_yasqe: Yasqe, response: any, duration: number) => {
|
|
664
702
|
this.emit("queryResponse", this);
|
|
665
703
|
if (!this.yasr) throw new Error("Resultset visualizer not initialized. Cannot draw results");
|
|
@@ -736,7 +774,8 @@ WHERE {
|
|
|
736
774
|
} LIMIT 1000`;
|
|
737
775
|
|
|
738
776
|
// Execute query in background without changing editor content
|
|
739
|
-
|
|
777
|
+
// Note: void operator is intentional - errors are handled in the catch block of executeBackgroundQuery
|
|
778
|
+
void this.executeBackgroundQuery(constructQuery);
|
|
740
779
|
};
|
|
741
780
|
|
|
742
781
|
private async executeBackgroundQuery(query: string) {
|
|
@@ -747,75 +786,21 @@ WHERE {
|
|
|
747
786
|
this.yasr.showLoading();
|
|
748
787
|
this.emit("queryBefore", this);
|
|
749
788
|
|
|
750
|
-
//
|
|
751
|
-
const
|
|
752
|
-
const config = typeof requestConfig === "function" ? requestConfig(this.yasqe) : requestConfig;
|
|
753
|
-
|
|
754
|
-
if (!config.endpoint) {
|
|
755
|
-
throw new Error("No endpoint configured");
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
const endpoint = typeof config.endpoint === "function" ? config.endpoint(this.yasqe) : config.endpoint;
|
|
759
|
-
const method = typeof config.method === "function" ? config.method(this.yasqe) : config.method || "POST";
|
|
760
|
-
const headers = typeof config.headers === "function" ? config.headers(this.yasqe) : config.headers || {};
|
|
761
|
-
|
|
762
|
-
// Prepare request
|
|
763
|
-
const searchParams = new URLSearchParams();
|
|
764
|
-
searchParams.append("query", query);
|
|
765
|
-
|
|
766
|
-
// Add any additional args
|
|
767
|
-
if (config.args && Array.isArray(config.args)) {
|
|
768
|
-
config.args.forEach((arg: any) => {
|
|
769
|
-
if (arg.name && arg.value) {
|
|
770
|
-
searchParams.append(arg.name, arg.value);
|
|
771
|
-
}
|
|
772
|
-
});
|
|
773
|
-
}
|
|
789
|
+
// Track query execution time
|
|
790
|
+
const startTime = Date.now();
|
|
774
791
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
792
|
+
// Use yasqe's executeQuery with custom query and accept header
|
|
793
|
+
const queryResponse = await Yasqe.Sparql.executeQuery(
|
|
794
|
+
this.yasqe,
|
|
795
|
+
undefined, // Use default config
|
|
796
|
+
{
|
|
797
|
+
customQuery: query,
|
|
798
|
+
customAccept: "text/turtle",
|
|
780
799
|
},
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
let url = endpoint;
|
|
784
|
-
if (method === "POST") {
|
|
785
|
-
fetchOptions.headers = {
|
|
786
|
-
...fetchOptions.headers,
|
|
787
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
788
|
-
};
|
|
789
|
-
fetchOptions.body = searchParams.toString();
|
|
790
|
-
} else {
|
|
791
|
-
const urlObj = new URL(endpoint);
|
|
792
|
-
searchParams.forEach((value, key) => {
|
|
793
|
-
urlObj.searchParams.append(key, value);
|
|
794
|
-
});
|
|
795
|
-
url = urlObj.toString();
|
|
796
|
-
}
|
|
800
|
+
);
|
|
797
801
|
|
|
798
|
-
const startTime = Date.now();
|
|
799
|
-
const response = await fetch(url, fetchOptions);
|
|
800
802
|
const duration = Date.now() - startTime;
|
|
801
803
|
|
|
802
|
-
if (!response.ok) {
|
|
803
|
-
throw new Error(`Query failed: ${response.statusText}`);
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
const result = await response.text();
|
|
807
|
-
|
|
808
|
-
// Create a query response object similar to what Yasqe produces
|
|
809
|
-
// This includes headers so the Parser can detect the content type
|
|
810
|
-
const queryResponse = {
|
|
811
|
-
ok: response.ok,
|
|
812
|
-
status: response.status,
|
|
813
|
-
statusText: response.statusText,
|
|
814
|
-
headers: response.headers,
|
|
815
|
-
type: response.type,
|
|
816
|
-
content: result,
|
|
817
|
-
};
|
|
818
|
-
|
|
819
804
|
// Set the response in Yasr
|
|
820
805
|
this.yasr.setResponse(queryResponse, duration);
|
|
821
806
|
|
|
@@ -831,10 +816,17 @@ WHERE {
|
|
|
831
816
|
console.error("Background query failed:", error);
|
|
832
817
|
if (this.yasr) {
|
|
833
818
|
this.yasr.hideLoading();
|
|
834
|
-
// Set error response
|
|
819
|
+
// Set error response with detailed HTTP status if available
|
|
820
|
+
const errorObj: any = error;
|
|
821
|
+
let errorText = error instanceof Error ? error.message : String(error);
|
|
822
|
+
|
|
835
823
|
this.yasr.setResponse(
|
|
836
824
|
{
|
|
837
|
-
error:
|
|
825
|
+
error: {
|
|
826
|
+
status: errorObj.status,
|
|
827
|
+
statusText: errorObj.statusText || (error instanceof Error ? error.name : undefined),
|
|
828
|
+
text: errorText,
|
|
829
|
+
},
|
|
838
830
|
},
|
|
839
831
|
0,
|
|
840
832
|
);
|
|
@@ -910,6 +902,7 @@ WHERE {
|
|
|
910
902
|
this.emit("change", this, this.persistentJson);
|
|
911
903
|
});
|
|
912
904
|
}
|
|
905
|
+
|
|
913
906
|
destroy() {
|
|
914
907
|
this.removeAllListeners();
|
|
915
908
|
this.settingsModal?.destroy();
|
|
@@ -919,6 +912,7 @@ WHERE {
|
|
|
919
912
|
this.yasr = undefined;
|
|
920
913
|
this.destroyYasqe();
|
|
921
914
|
}
|
|
915
|
+
|
|
922
916
|
public static getDefaults(yasgui?: Yasgui): PersistedJson {
|
|
923
917
|
return {
|
|
924
918
|
yasqe: {
|
|
@@ -948,11 +942,21 @@ const safeEndpoint = (endpoint: string): string => {
|
|
|
948
942
|
|
|
949
943
|
function getCorsErrorRenderer(tab: Tab) {
|
|
950
944
|
return async (error: Parser.ErrorSummary): Promise<HTMLElement | undefined> => {
|
|
945
|
+
// Only show CORS/mixed-content warning for actual network failures (no status code)
|
|
946
|
+
// AND when querying HTTP from HTTPS
|
|
951
947
|
if (!error.status) {
|
|
952
|
-
// Only show this custom error if
|
|
953
948
|
const shouldReferToHttp =
|
|
954
949
|
new URL(tab.getEndpoint()).protocol === "http:" && window.location.protocol === "https:";
|
|
955
|
-
|
|
950
|
+
|
|
951
|
+
// Check if this looks like a network error (not just missing status)
|
|
952
|
+
const isNetworkError =
|
|
953
|
+
!error.text ||
|
|
954
|
+
error.text.indexOf("Request has been terminated") >= 0 ||
|
|
955
|
+
error.text.indexOf("Failed to fetch") >= 0 ||
|
|
956
|
+
error.text.indexOf("NetworkError") >= 0 ||
|
|
957
|
+
error.text.indexOf("Network request failed") >= 0;
|
|
958
|
+
|
|
959
|
+
if (shouldReferToHttp && isNetworkError) {
|
|
956
960
|
const errorEl = document.createElement("div");
|
|
957
961
|
const errorSpan = document.createElement("p");
|
|
958
962
|
errorSpan.innerHTML = `You are trying to query an HTTP endpoint (<a href="${safeEndpoint(
|
|
@@ -961,14 +965,7 @@ function getCorsErrorRenderer(tab: Tab) {
|
|
|
961
965
|
tab.getEndpoint(),
|
|
962
966
|
)}</a>) from an HTTP<strong>S</strong> website (<a href="${safeEndpoint(window.location.href)}">${safeEndpoint(
|
|
963
967
|
window.location.href,
|
|
964
|
-
)}</a>).<br>This
|
|
965
|
-
if (tab.yasgui.config.nonSslDomain) {
|
|
966
|
-
const errorLink = document.createElement("p");
|
|
967
|
-
errorLink.innerHTML = `As a workaround, you can use the HTTP version of Yasgui instead: <a href="${tab.getShareableLink(
|
|
968
|
-
tab.yasgui.config.nonSslDomain,
|
|
969
|
-
)}" target="_blank">${tab.yasgui.config.nonSslDomain}</a>`;
|
|
970
|
-
errorSpan.appendChild(errorLink);
|
|
971
|
-
}
|
|
968
|
+
)}</a>).<br>This can be blocked in modern browsers, see <a target="_blank" rel="noopener noreferrer" href="https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy">https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy</a>. See also <a href="https://yasgui-doc.matdata.eu/docs/user-guide#querying-local-endpoints">the YasGUI documentation</a> for possible workarounds.`;
|
|
972
969
|
errorEl.appendChild(errorSpan);
|
|
973
970
|
return errorEl;
|
|
974
971
|
}
|