@matdata/yasqe 5.15.0 → 5.17.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/__tests__/share-test.js +30 -1
- package/build/ts/src/__tests__/share-test.js.map +1 -1
- package/build/ts/src/index.d.ts +5 -1
- package/build/ts/src/index.js +58 -27
- package/build/ts/src/index.js.map +1 -1
- package/build/ts/src/sparql.d.ts +2 -0
- package/build/ts/src/sparql.js +60 -13
- package/build/ts/src/sparql.js.map +1 -1
- package/build/yasqe.min.css +1 -1
- package/build/yasqe.min.css.map +3 -3
- package/build/yasqe.min.js +73 -73
- package/build/yasqe.min.js.map +3 -3
- package/package.json +1 -1
- package/src/__tests__/share-test.ts +32 -1
- package/src/index.ts +68 -30
- package/src/scss/buttons.scss +23 -15
- package/src/sparql.ts +73 -13
package/package.json
CHANGED
|
@@ -105,7 +105,7 @@ describe("Share Functionality", () => {
|
|
|
105
105
|
" }",
|
|
106
106
|
' ContentType = "application/x-www-form-urlencoded"',
|
|
107
107
|
' Body = "query=SELECT"',
|
|
108
|
-
' OutFile = "
|
|
108
|
+
' OutFile = "sparql-generated.json"',
|
|
109
109
|
"}",
|
|
110
110
|
"",
|
|
111
111
|
"Invoke-WebRequest @params",
|
|
@@ -117,6 +117,37 @@ describe("Share Functionality", () => {
|
|
|
117
117
|
expect(psString).to.include("Headers");
|
|
118
118
|
expect(psString).to.include("OutFile");
|
|
119
119
|
expect(psString).to.include("Accept");
|
|
120
|
+
expect(psString).to.include("sparql-generated");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should format PowerShell commands with here-string for query", () => {
|
|
124
|
+
const query = "SELECT * WHERE { ?s ?p ?o }";
|
|
125
|
+
const lines = [
|
|
126
|
+
'$query = @"',
|
|
127
|
+
query,
|
|
128
|
+
'"@',
|
|
129
|
+
"",
|
|
130
|
+
"$params = @{",
|
|
131
|
+
' Uri = "https://example.com/sparql"',
|
|
132
|
+
' Method = "Post"',
|
|
133
|
+
" Headers = @{",
|
|
134
|
+
' "Accept" = "application/sparql-results+json"',
|
|
135
|
+
" }",
|
|
136
|
+
' ContentType = "application/x-www-form-urlencoded"',
|
|
137
|
+
' Body = "query=$([System.Net.WebUtility]::UrlEncode($query))"',
|
|
138
|
+
' OutFile = "sparql-generated.json"',
|
|
139
|
+
"}",
|
|
140
|
+
"",
|
|
141
|
+
"Invoke-WebRequest @params",
|
|
142
|
+
];
|
|
143
|
+
const psString = lines.join("\n");
|
|
144
|
+
|
|
145
|
+
expect(psString).to.include('$query = @"');
|
|
146
|
+
expect(psString).to.include('"@');
|
|
147
|
+
expect(psString).to.include(query);
|
|
148
|
+
expect(psString).to.include('Body = "query=$([System.Net.WebUtility]::UrlEncode($query))"');
|
|
149
|
+
expect(psString).to.not.include('Body = "query=`$query"'); // Should NOT escape the variable
|
|
150
|
+
expect(psString).to.include("sparql-generated");
|
|
120
151
|
});
|
|
121
152
|
|
|
122
153
|
it("should format wget commands with proper line breaks", () => {
|
package/src/index.ts
CHANGED
|
@@ -46,6 +46,10 @@ export interface Yasqe {
|
|
|
46
46
|
off(eventName: "autocompletionClose", handler: (instance: Yasqe) => void): void;
|
|
47
47
|
on(eventName: "resize", handler: (instance: Yasqe, newSize: string) => void): void;
|
|
48
48
|
off(eventName: "resize", handler: (instance: Yasqe, newSize: string) => void): void;
|
|
49
|
+
on(eventName: "saveManagedQuery", handler: () => void): void;
|
|
50
|
+
off(eventName: "saveManagedQuery", handler: () => void): void;
|
|
51
|
+
on(eventName: "downloadRqFile", handler: () => void): void;
|
|
52
|
+
off(eventName: "downloadRqFile", handler: () => void): void;
|
|
49
53
|
on(eventName: string, handler: () => void): void;
|
|
50
54
|
}
|
|
51
55
|
|
|
@@ -59,7 +63,7 @@ export class Yasqe extends CodeMirror {
|
|
|
59
63
|
private abortController: AbortController | undefined;
|
|
60
64
|
private queryStatus: "valid" | "error" | undefined;
|
|
61
65
|
private queryBtn: HTMLButtonElement | undefined;
|
|
62
|
-
private
|
|
66
|
+
private saveBtnWrapper: HTMLDivElement | undefined;
|
|
63
67
|
private fullscreenBtn: HTMLButtonElement | undefined;
|
|
64
68
|
private hamburgerBtn: HTMLButtonElement | undefined;
|
|
65
69
|
private hamburgerMenu: HTMLDivElement | undefined;
|
|
@@ -570,24 +574,42 @@ export class Yasqe extends CodeMirror {
|
|
|
570
574
|
}
|
|
571
575
|
|
|
572
576
|
/**
|
|
573
|
-
* Draw save
|
|
577
|
+
* Draw save buttons (THIRD)
|
|
574
578
|
*/
|
|
575
|
-
const
|
|
576
|
-
addClass(
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
579
|
+
const saveBtnWrapper = document.createElement("div");
|
|
580
|
+
addClass(saveBtnWrapper, "yasqe_saveWrapper");
|
|
581
|
+
saveBtnWrapper.style.display = "none"; // Hidden by default, shown when workspace is configured
|
|
582
|
+
this.saveBtnWrapper = saveBtnWrapper;
|
|
583
|
+
|
|
584
|
+
const saveManagedBtn = document.createElement("button");
|
|
585
|
+
addClass(saveManagedBtn, "yasqe_saveManagedButton");
|
|
586
|
+
const saveManagedIcon = document.createElement("i");
|
|
587
|
+
addClass(saveManagedIcon, "fas");
|
|
588
|
+
addClass(saveManagedIcon, "fa-database");
|
|
589
|
+
saveManagedIcon.setAttribute("aria-hidden", "true");
|
|
590
|
+
saveManagedBtn.appendChild(saveManagedIcon);
|
|
591
|
+
saveManagedBtn.title = "Save as managed query";
|
|
592
|
+
saveManagedBtn.setAttribute("aria-label", "Save as managed query");
|
|
593
|
+
saveManagedBtn.onclick = () => {
|
|
584
594
|
this.emit("saveManagedQuery");
|
|
585
595
|
};
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
596
|
+
saveBtnWrapper.appendChild(saveManagedBtn);
|
|
597
|
+
|
|
598
|
+
const saveRqBtn = document.createElement("button");
|
|
599
|
+
addClass(saveRqBtn, "yasqe_saveRqButton");
|
|
600
|
+
const saveRqIcon = document.createElement("i");
|
|
601
|
+
addClass(saveRqIcon, "fas");
|
|
602
|
+
addClass(saveRqIcon, "fa-file-download");
|
|
603
|
+
saveRqIcon.setAttribute("aria-hidden", "true");
|
|
604
|
+
saveRqBtn.appendChild(saveRqIcon);
|
|
605
|
+
saveRqBtn.title = "Save as .rq file";
|
|
606
|
+
saveRqBtn.setAttribute("aria-label", "Save as .rq file");
|
|
607
|
+
saveRqBtn.onclick = () => {
|
|
608
|
+
this.emit("downloadRqFile");
|
|
609
|
+
};
|
|
610
|
+
saveBtnWrapper.appendChild(saveRqBtn);
|
|
611
|
+
|
|
612
|
+
buttons.appendChild(saveBtnWrapper);
|
|
591
613
|
|
|
592
614
|
/**
|
|
593
615
|
* Draw format btn (FOURTH)
|
|
@@ -671,21 +693,37 @@ export class Yasqe extends CodeMirror {
|
|
|
671
693
|
this.hamburgerMenu.appendChild(shareItem);
|
|
672
694
|
}
|
|
673
695
|
|
|
674
|
-
const
|
|
675
|
-
|
|
676
|
-
const
|
|
677
|
-
addClass(
|
|
678
|
-
addClass(
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
696
|
+
const saveManagedItem = document.createElement("button");
|
|
697
|
+
saveManagedItem.className = "yasqe_hamburgerMenuItem";
|
|
698
|
+
const saveManagedIconMenu = document.createElement("i");
|
|
699
|
+
addClass(saveManagedIconMenu, "fas");
|
|
700
|
+
addClass(saveManagedIconMenu, "fa-database");
|
|
701
|
+
saveManagedIconMenu.setAttribute("aria-hidden", "true");
|
|
702
|
+
saveManagedItem.appendChild(saveManagedIconMenu);
|
|
703
|
+
const saveManagedLabel = document.createElement("span");
|
|
704
|
+
saveManagedLabel.textContent = "Save as managed query";
|
|
705
|
+
saveManagedItem.appendChild(saveManagedLabel);
|
|
706
|
+
saveManagedItem.onclick = () => {
|
|
685
707
|
this.closeHamburgerMenu();
|
|
686
708
|
this.emit("saveManagedQuery");
|
|
687
709
|
};
|
|
688
|
-
this.hamburgerMenu.appendChild(
|
|
710
|
+
this.hamburgerMenu.appendChild(saveManagedItem);
|
|
711
|
+
|
|
712
|
+
const saveRqItem = document.createElement("button");
|
|
713
|
+
saveRqItem.className = "yasqe_hamburgerMenuItem";
|
|
714
|
+
const saveRqIconMenu = document.createElement("i");
|
|
715
|
+
addClass(saveRqIconMenu, "fas");
|
|
716
|
+
addClass(saveRqIconMenu, "fa-file-download");
|
|
717
|
+
saveRqIconMenu.setAttribute("aria-hidden", "true");
|
|
718
|
+
saveRqItem.appendChild(saveRqIconMenu);
|
|
719
|
+
const saveRqLabel = document.createElement("span");
|
|
720
|
+
saveRqLabel.textContent = "Save as .rq file";
|
|
721
|
+
saveRqItem.appendChild(saveRqLabel);
|
|
722
|
+
saveRqItem.onclick = () => {
|
|
723
|
+
this.closeHamburgerMenu();
|
|
724
|
+
this.emit("downloadRqFile");
|
|
725
|
+
};
|
|
726
|
+
this.hamburgerMenu.appendChild(saveRqItem);
|
|
689
727
|
|
|
690
728
|
if (this.config.showFormatButton) {
|
|
691
729
|
const formatItem = document.createElement("button");
|
|
@@ -1553,8 +1591,8 @@ export class Yasqe extends CodeMirror {
|
|
|
1553
1591
|
}
|
|
1554
1592
|
|
|
1555
1593
|
public setSaveButtonVisible(visible: boolean) {
|
|
1556
|
-
if (this.
|
|
1557
|
-
this.
|
|
1594
|
+
if (this.saveBtnWrapper) {
|
|
1595
|
+
this.saveBtnWrapper.style.display = visible ? "inline-flex" : "none";
|
|
1558
1596
|
}
|
|
1559
1597
|
}
|
|
1560
1598
|
|
package/src/scss/buttons.scss
CHANGED
|
@@ -293,25 +293,33 @@
|
|
|
293
293
|
}
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
.
|
|
296
|
+
.yasqe_saveWrapper {
|
|
297
|
+
position: relative;
|
|
297
298
|
display: inline-flex;
|
|
298
299
|
align-items: center;
|
|
299
|
-
|
|
300
|
-
border: none;
|
|
301
|
-
background: none;
|
|
302
|
-
cursor: pointer;
|
|
303
|
-
padding: 6px;
|
|
300
|
+
vertical-align: middle;
|
|
304
301
|
margin-left: 8px;
|
|
305
|
-
height: 36px;
|
|
306
|
-
width: 36px;
|
|
307
|
-
color: var(--yasgui-text-secondary, #505050);
|
|
308
302
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
303
|
+
.yasqe_saveManagedButton,
|
|
304
|
+
.yasqe_saveRqButton {
|
|
305
|
+
display: inline-flex;
|
|
306
|
+
align-items: center;
|
|
307
|
+
justify-content: center;
|
|
308
|
+
border: none;
|
|
309
|
+
background: none;
|
|
310
|
+
cursor: pointer;
|
|
311
|
+
padding: 6px;
|
|
312
|
+
height: 36px;
|
|
313
|
+
width: 36px;
|
|
314
|
+
color: var(--yasgui-text-secondary, #505050);
|
|
312
315
|
|
|
313
|
-
|
|
314
|
-
|
|
316
|
+
i {
|
|
317
|
+
font-size: 20px;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
&:hover {
|
|
321
|
+
color: #337ab7;
|
|
322
|
+
}
|
|
315
323
|
}
|
|
316
324
|
}
|
|
317
325
|
|
|
@@ -441,7 +449,7 @@
|
|
|
441
449
|
@media (max-width: 768px) {
|
|
442
450
|
.yasqe_buttons {
|
|
443
451
|
.yasqe_share,
|
|
444
|
-
.
|
|
452
|
+
.yasqe_saveWrapper,
|
|
445
453
|
.yasqe_formatButton,
|
|
446
454
|
.yasqe_fullscreenButton {
|
|
447
455
|
display: none !important;
|
package/src/sparql.ts
CHANGED
|
@@ -174,6 +174,13 @@ export function getAjaxConfig(
|
|
|
174
174
|
export interface ExecuteQueryOptions {
|
|
175
175
|
customQuery?: string;
|
|
176
176
|
customAccept?: string;
|
|
177
|
+
/** Optional external abort signal, useful for plugin-driven background fetches. */
|
|
178
|
+
signal?: AbortSignal;
|
|
179
|
+
/**
|
|
180
|
+
* Execute without emitting Yasqe query lifecycle events.
|
|
181
|
+
* Useful for background/plugin-driven queries that should not update main UI state.
|
|
182
|
+
*/
|
|
183
|
+
silent?: boolean;
|
|
177
184
|
}
|
|
178
185
|
|
|
179
186
|
export async function executeQuery(
|
|
@@ -182,13 +189,21 @@ export async function executeQuery(
|
|
|
182
189
|
options?: ExecuteQueryOptions,
|
|
183
190
|
): Promise<any> {
|
|
184
191
|
const queryStart = Date.now();
|
|
192
|
+
const silent = !!options?.silent;
|
|
185
193
|
try {
|
|
186
|
-
yasqe.emit("queryBefore", yasqe, config);
|
|
194
|
+
if (!silent) yasqe.emit("queryBefore", yasqe, config);
|
|
187
195
|
const populatedConfig = getAjaxConfig(yasqe, config);
|
|
188
196
|
if (!populatedConfig) {
|
|
189
197
|
return; // Nothing to query
|
|
190
198
|
}
|
|
191
199
|
const abortController = new AbortController();
|
|
200
|
+
if (options?.signal) {
|
|
201
|
+
if (options.signal.aborted) {
|
|
202
|
+
abortController.abort();
|
|
203
|
+
} else {
|
|
204
|
+
options.signal.addEventListener("abort", () => abortController.abort(), { once: true });
|
|
205
|
+
}
|
|
206
|
+
}
|
|
192
207
|
|
|
193
208
|
// Use custom accept header if provided, otherwise use the default
|
|
194
209
|
const acceptHeader = options?.customAccept || populatedConfig.accept;
|
|
@@ -256,17 +271,22 @@ export async function executeQuery(
|
|
|
256
271
|
populatedConfig.url = url.toString();
|
|
257
272
|
}
|
|
258
273
|
const request = new Request(populatedConfig.url, fetchOptions);
|
|
259
|
-
yasqe.emit("query", request, abortController);
|
|
274
|
+
if (!silent) yasqe.emit("query", request, abortController);
|
|
260
275
|
const response = await fetch(request);
|
|
261
276
|
|
|
262
277
|
// Await the response content and merge with the `Response` object
|
|
278
|
+
const content = await response.text();
|
|
263
279
|
const queryResponse = {
|
|
264
280
|
ok: response.ok,
|
|
265
281
|
status: response.status,
|
|
266
282
|
statusText: response.statusText,
|
|
267
283
|
headers: response.headers,
|
|
268
284
|
type: response.type,
|
|
269
|
-
content
|
|
285
|
+
content,
|
|
286
|
+
// Compatibility aliases for plugins that expect fetch-like or axios-like response objects.
|
|
287
|
+
data: content,
|
|
288
|
+
json: async () => JSON.parse(content),
|
|
289
|
+
text: async () => content,
|
|
270
290
|
};
|
|
271
291
|
|
|
272
292
|
if (!response.ok) {
|
|
@@ -278,8 +298,10 @@ export async function executeQuery(
|
|
|
278
298
|
throw error;
|
|
279
299
|
}
|
|
280
300
|
|
|
281
|
-
|
|
282
|
-
|
|
301
|
+
if (!silent) {
|
|
302
|
+
yasqe.emit("queryResponse", queryResponse, Date.now() - queryStart);
|
|
303
|
+
yasqe.emit("queryResults", queryResponse.content, Date.now() - queryStart);
|
|
304
|
+
}
|
|
283
305
|
return queryResponse;
|
|
284
306
|
} catch (e) {
|
|
285
307
|
if (e instanceof Error && e.message === "Aborted") {
|
|
@@ -292,12 +314,12 @@ export async function executeQuery(
|
|
|
292
314
|
if (e.message.includes("Failed to fetch") || e.message.includes("NetworkError")) {
|
|
293
315
|
enhancedError.message = `${e.message}. The server may have returned an error response (check browser dev tools), but CORS headers are preventing JavaScript from accessing it. Ensure the endpoint returns proper CORS headers even for error responses (Access-Control-Allow-Origin, etc.).`;
|
|
294
316
|
}
|
|
295
|
-
yasqe.emit("queryResponse", enhancedError, Date.now() - queryStart);
|
|
317
|
+
if (!silent) yasqe.emit("queryResponse", enhancedError, Date.now() - queryStart);
|
|
296
318
|
} else {
|
|
297
|
-
yasqe.emit("queryResponse", e, Date.now() - queryStart);
|
|
319
|
+
if (!silent) yasqe.emit("queryResponse", e, Date.now() - queryStart);
|
|
298
320
|
}
|
|
299
321
|
}
|
|
300
|
-
yasqe.emit("error", e);
|
|
322
|
+
if (!silent) yasqe.emit("error", e);
|
|
301
323
|
throw e;
|
|
302
324
|
}
|
|
303
325
|
}
|
|
@@ -520,10 +542,48 @@ export function getAsPowerShellString(yasqe: Yasqe, _config?: Config["requestCon
|
|
|
520
542
|
lines.push(headersLines.join("\n"));
|
|
521
543
|
lines.push(" }");
|
|
522
544
|
}
|
|
523
|
-
lines.push(` OutFile = "
|
|
545
|
+
lines.push(` OutFile = "sparql-generated.${fileExtension}"`);
|
|
524
546
|
lines.push("}");
|
|
525
547
|
} else if (ajaxConfig.reqMethod === "POST") {
|
|
526
|
-
|
|
548
|
+
// Extract the query/update parameter and other parameters separately
|
|
549
|
+
// Determine the query parameter name first (query takes precedence over update)
|
|
550
|
+
const queryParamName = ajaxConfig.args.query !== undefined ? "query" : "update";
|
|
551
|
+
const queryParam = ajaxConfig.args[queryParamName];
|
|
552
|
+
|
|
553
|
+
const otherArgs: RequestArgs = {};
|
|
554
|
+
for (const key in ajaxConfig.args) {
|
|
555
|
+
if (key !== "query" && key !== "update") {
|
|
556
|
+
otherArgs[key] = ajaxConfig.args[key];
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Build the query string using here-string for easy editing
|
|
561
|
+
if (queryParam) {
|
|
562
|
+
// Handle both string and string[] cases - use first element if array
|
|
563
|
+
const queryText = Array.isArray(queryParam) ? queryParam[0] : queryParam;
|
|
564
|
+
lines.push(`$${queryParamName} = @"`);
|
|
565
|
+
lines.push(queryText);
|
|
566
|
+
lines.push(`"@`);
|
|
567
|
+
lines.push("");
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// Build the body with the query variable and any other parameters
|
|
571
|
+
// The query must be URL-encoded for application/x-www-form-urlencoded
|
|
572
|
+
let bodyExpression: string;
|
|
573
|
+
const urlEncodeExpr = `[System.Net.WebUtility]::UrlEncode($${queryParamName})`;
|
|
574
|
+
if (queryParam && Object.keys(otherArgs).length > 0) {
|
|
575
|
+
// Both query variable and other args
|
|
576
|
+
const otherArgsString = queryString.stringify(otherArgs);
|
|
577
|
+
bodyExpression = `"${queryParamName}=$(${urlEncodeExpr})&${escapePowerShellString(otherArgsString)}"`;
|
|
578
|
+
} else if (queryParam) {
|
|
579
|
+
// Only query variable - use subexpression for URL encoding
|
|
580
|
+
bodyExpression = `"${queryParamName}=$(${urlEncodeExpr})"`;
|
|
581
|
+
} else {
|
|
582
|
+
// Only other args (shouldn't happen, but handle it)
|
|
583
|
+
const otherArgsString = queryString.stringify(otherArgs);
|
|
584
|
+
bodyExpression = `"${escapePowerShellString(otherArgsString)}"`;
|
|
585
|
+
}
|
|
586
|
+
|
|
527
587
|
lines.push("$params = @{");
|
|
528
588
|
lines.push(` Uri = "${escapePowerShellString(url)}"`);
|
|
529
589
|
lines.push(` Method = "Post"`);
|
|
@@ -533,8 +593,8 @@ export function getAsPowerShellString(yasqe: Yasqe, _config?: Config["requestCon
|
|
|
533
593
|
lines.push(" }");
|
|
534
594
|
}
|
|
535
595
|
lines.push(` ContentType = "application/x-www-form-urlencoded"`);
|
|
536
|
-
lines.push(` Body =
|
|
537
|
-
lines.push(` OutFile = "
|
|
596
|
+
lines.push(` Body = ${bodyExpression}`);
|
|
597
|
+
lines.push(` OutFile = "sparql-generated.${fileExtension}"`);
|
|
538
598
|
lines.push("}");
|
|
539
599
|
} else {
|
|
540
600
|
// Handle other methods (PUT, DELETE, etc.)
|
|
@@ -552,7 +612,7 @@ export function getAsPowerShellString(yasqe: Yasqe, _config?: Config["requestCon
|
|
|
552
612
|
lines.push(` ContentType = "application/x-www-form-urlencoded"`);
|
|
553
613
|
lines.push(` Body = "${body.replace(/"/g, '`"')}"`);
|
|
554
614
|
}
|
|
555
|
-
lines.push(` OutFile = "
|
|
615
|
+
lines.push(` OutFile = "sparql-generated.${fileExtension}"`);
|
|
556
616
|
lines.push("}");
|
|
557
617
|
}
|
|
558
618
|
|