@matdata/yasqe 5.15.0 → 5.16.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@matdata/yasqe",
3
3
  "description": "Yet Another SPARQL Query Editor",
4
- "version": "5.15.0",
4
+ "version": "5.16.0",
5
5
  "main": "build/yasqe.min.js",
6
6
  "types": "build/ts/src/index.d.ts",
7
7
  "license": "MIT",
@@ -105,7 +105,7 @@ describe("Share Functionality", () => {
105
105
  " }",
106
106
  ' ContentType = "application/x-www-form-urlencoded"',
107
107
  ' Body = "query=SELECT"',
108
- ' OutFile = "result.json"',
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 saveBtn: HTMLButtonElement | undefined;
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 button (THIRD)
577
+ * Draw save buttons (THIRD)
574
578
  */
575
- const saveBtn = document.createElement("button");
576
- addClass(saveBtn, "yasqe_saveButton");
577
- const saveIcon = document.createElement("i");
578
- addClass(saveIcon, "fas");
579
- addClass(saveIcon, "fa-save");
580
- saveIcon.setAttribute("aria-hidden", "true");
581
- saveBtn.appendChild(saveIcon);
582
- saveBtn.onclick = () => {
583
- // Call the managed query save function if available
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
- saveBtn.title = "Save managed query (Ctrl+S)";
587
- saveBtn.setAttribute("aria-label", "Save managed query");
588
- saveBtn.style.display = "none"; // Hidden by default, shown when workspace is configured
589
- this.saveBtn = saveBtn;
590
- buttons.appendChild(saveBtn);
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 saveItem = document.createElement("button");
675
- saveItem.className = "yasqe_hamburgerMenuItem";
676
- const saveIconMenu = document.createElement("i");
677
- addClass(saveIconMenu, "fas");
678
- addClass(saveIconMenu, "fa-save");
679
- saveIconMenu.setAttribute("aria-hidden", "true");
680
- saveItem.appendChild(saveIconMenu);
681
- const saveLabel = document.createElement("span");
682
- saveLabel.textContent = "Save";
683
- saveItem.appendChild(saveLabel);
684
- saveItem.onclick = () => {
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(saveItem);
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.saveBtn) {
1557
- this.saveBtn.style.display = visible ? "inline-flex" : "none";
1594
+ if (this.saveBtnWrapper) {
1595
+ this.saveBtnWrapper.style.display = visible ? "inline-flex" : "none";
1558
1596
  }
1559
1597
  }
1560
1598
 
@@ -293,25 +293,33 @@
293
293
  }
294
294
  }
295
295
 
296
- .yasqe_saveButton {
296
+ .yasqe_saveWrapper {
297
+ position: relative;
297
298
  display: inline-flex;
298
299
  align-items: center;
299
- justify-content: center;
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
- i {
310
- font-size: 20px;
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
- &:hover {
314
- color: #337ab7;
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
- .yasqe_saveButton,
452
+ .yasqe_saveWrapper,
445
453
  .yasqe_formatButton,
446
454
  .yasqe_fullscreenButton {
447
455
  display: none !important;
package/src/sparql.ts CHANGED
@@ -520,10 +520,48 @@ export function getAsPowerShellString(yasqe: Yasqe, _config?: Config["requestCon
520
520
  lines.push(headersLines.join("\n"));
521
521
  lines.push(" }");
522
522
  }
523
- lines.push(` OutFile = "result.${fileExtension}"`);
523
+ lines.push(` OutFile = "sparql-generated.${fileExtension}"`);
524
524
  lines.push("}");
525
525
  } else if (ajaxConfig.reqMethod === "POST") {
526
- const body = queryString.stringify(ajaxConfig.args);
526
+ // Extract the query/update parameter and other parameters separately
527
+ // Determine the query parameter name first (query takes precedence over update)
528
+ const queryParamName = ajaxConfig.args.query !== undefined ? "query" : "update";
529
+ const queryParam = ajaxConfig.args[queryParamName];
530
+
531
+ const otherArgs: RequestArgs = {};
532
+ for (const key in ajaxConfig.args) {
533
+ if (key !== "query" && key !== "update") {
534
+ otherArgs[key] = ajaxConfig.args[key];
535
+ }
536
+ }
537
+
538
+ // Build the query string using here-string for easy editing
539
+ if (queryParam) {
540
+ // Handle both string and string[] cases - use first element if array
541
+ const queryText = Array.isArray(queryParam) ? queryParam[0] : queryParam;
542
+ lines.push(`$${queryParamName} = @"`);
543
+ lines.push(queryText);
544
+ lines.push(`"@`);
545
+ lines.push("");
546
+ }
547
+
548
+ // Build the body with the query variable and any other parameters
549
+ // The query must be URL-encoded for application/x-www-form-urlencoded
550
+ let bodyExpression: string;
551
+ const urlEncodeExpr = `[System.Net.WebUtility]::UrlEncode($${queryParamName})`;
552
+ if (queryParam && Object.keys(otherArgs).length > 0) {
553
+ // Both query variable and other args
554
+ const otherArgsString = queryString.stringify(otherArgs);
555
+ bodyExpression = `"${queryParamName}=$(${urlEncodeExpr})&${escapePowerShellString(otherArgsString)}"`;
556
+ } else if (queryParam) {
557
+ // Only query variable - use subexpression for URL encoding
558
+ bodyExpression = `"${queryParamName}=$(${urlEncodeExpr})"`;
559
+ } else {
560
+ // Only other args (shouldn't happen, but handle it)
561
+ const otherArgsString = queryString.stringify(otherArgs);
562
+ bodyExpression = `"${escapePowerShellString(otherArgsString)}"`;
563
+ }
564
+
527
565
  lines.push("$params = @{");
528
566
  lines.push(` Uri = "${escapePowerShellString(url)}"`);
529
567
  lines.push(` Method = "Post"`);
@@ -533,8 +571,8 @@ export function getAsPowerShellString(yasqe: Yasqe, _config?: Config["requestCon
533
571
  lines.push(" }");
534
572
  }
535
573
  lines.push(` ContentType = "application/x-www-form-urlencoded"`);
536
- lines.push(` Body = "${escapePowerShellString(body)}"`);
537
- lines.push(` OutFile = "result.${fileExtension}"`);
574
+ lines.push(` Body = ${bodyExpression}`);
575
+ lines.push(` OutFile = "sparql-generated.${fileExtension}"`);
538
576
  lines.push("}");
539
577
  } else {
540
578
  // Handle other methods (PUT, DELETE, etc.)
@@ -552,7 +590,7 @@ export function getAsPowerShellString(yasqe: Yasqe, _config?: Config["requestCon
552
590
  lines.push(` ContentType = "application/x-www-form-urlencoded"`);
553
591
  lines.push(` Body = "${body.replace(/"/g, '`"')}"`);
554
592
  }
555
- lines.push(` OutFile = "result.${fileExtension}"`);
593
+ lines.push(` OutFile = "sparql-generated.${fileExtension}"`);
556
594
  lines.push("}");
557
595
  }
558
596