@matdata/yasqe 5.9.0 → 5.10.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.
@@ -89,15 +89,40 @@
89
89
  }
90
90
  .yasqe_sharePopup {
91
91
  position: absolute;
92
- padding: 4px;
92
+ padding: 12px;
93
93
  margin-left: 0px;
94
94
  background-color: #fff;
95
95
  border: 1px solid #e3e3e3;
96
- border-radius: 2px;
97
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
98
- width: 600px;
96
+ border-radius: 4px;
97
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
98
+ width: auto;
99
+ min-width: 300px;
100
+ max-width: 400px;
99
101
  height: auto;
100
102
  display: flex;
103
+ flex-direction: column;
104
+ gap: 8px;
105
+
106
+ .yasqe_sharePopup_title {
107
+ font-weight: 600;
108
+ font-size: 14px;
109
+ color: #333;
110
+ margin-bottom: 4px;
111
+ }
112
+
113
+ .yasqe_sharePopup_buttons {
114
+ display: flex;
115
+ flex-direction: column;
116
+ gap: 6px;
117
+ }
118
+
119
+ .yasqe_shareBtn {
120
+ width: 100%;
121
+ text-align: left;
122
+ justify-content: flex-start;
123
+ padding: 8px 12px;
124
+ font-size: 13px;
125
+ }
101
126
 
102
127
  .inputWrapper {
103
128
  flex-grow: 100;
@@ -129,6 +154,13 @@
129
154
  textarea {
130
155
  width: 100%;
131
156
  }
157
+
158
+ .shortlinkErr {
159
+ color: #d32f2f;
160
+ font-size: 12px;
161
+ padding: 8px;
162
+ display: block;
163
+ }
132
164
  }
133
165
  .yasqe_queryButton {
134
166
  display: inline-block;
@@ -440,3 +472,74 @@
440
472
  }
441
473
  }
442
474
  }
475
+
476
+ // Toast notification styles - scoped with yasqe_ prefix
477
+ // These are appended to document.body and must be outside .yasqe selector
478
+ // The yasqe_ prefix prevents naming conflicts with other components
479
+ .yasqe_toast {
480
+ position: fixed;
481
+ bottom: 20px;
482
+ right: 20px;
483
+ background-color: #323232;
484
+ color: #fff;
485
+ padding: 12px 20px;
486
+ border-radius: 4px;
487
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
488
+ z-index: 10000;
489
+ font-size: 14px;
490
+ max-width: 400px;
491
+ animation: yasqe_toast-fadein 0.3s ease-in;
492
+ display: flex;
493
+ align-items: center;
494
+ gap: 12px;
495
+
496
+ &.yasqe_toast-warning {
497
+ background-color: #ffc107;
498
+ color: #000;
499
+ padding: 14px 20px;
500
+ box-shadow: 0 3px 10px rgba(255, 193, 7, 0.4);
501
+
502
+ .yasqe_toast-icon {
503
+ display: flex;
504
+ align-items: center;
505
+ flex-shrink: 0;
506
+
507
+ svg {
508
+ width: 24px;
509
+ height: 24px;
510
+ fill: #000;
511
+ }
512
+ }
513
+ }
514
+
515
+ .yasqe_toast-message {
516
+ flex: 1;
517
+ }
518
+
519
+ &.yasqe_toast-fadeout {
520
+ animation: yasqe_toast-fadeout 0.3s ease-out;
521
+ opacity: 0;
522
+ }
523
+ }
524
+
525
+ @keyframes yasqe_toast-fadein {
526
+ from {
527
+ opacity: 0;
528
+ transform: translateY(20px);
529
+ }
530
+ to {
531
+ opacity: 1;
532
+ transform: translateY(0);
533
+ }
534
+ }
535
+
536
+ @keyframes yasqe_toast-fadeout {
537
+ from {
538
+ opacity: 1;
539
+ transform: translateY(0);
540
+ }
541
+ to {
542
+ opacity: 0;
543
+ transform: translateY(20px);
544
+ }
545
+ }
package/src/sparql.ts CHANGED
@@ -360,38 +360,250 @@ export function getAcceptHeader(yasqe: Yasqe, _config: Config["requestConfig"])
360
360
  }
361
361
  return acceptHeader;
362
362
  }
363
- export function getAsCurlString(yasqe: Yasqe, _config?: Config["requestConfig"]) {
364
- let ajaxConfig = getAjaxConfig(yasqe, getRequestConfigSettings(yasqe, _config));
365
- if (!ajaxConfig) return "";
366
- let url = ajaxConfig.url;
367
- if (ajaxConfig.url.indexOf("http") !== 0) {
368
- //this is either a relative or absolute url, which is not supported by CURL.
369
- //Add domain, schema, etc etc
370
- url = `${window.location.protocol}//${window.location.host}`;
371
- if (ajaxConfig.url.indexOf("/") === 0) {
372
- //its an absolute path
373
- url += ajaxConfig.url;
363
+ /**
364
+ * Helper to normalize URL for command-line tools
365
+ */
366
+ function normalizeUrl(url: string): string {
367
+ if (url.indexOf("http") !== 0) {
368
+ // Relative or absolute URL - add domain, schema, etc
369
+ let fullUrl = `${window.location.protocol}//${window.location.host}`;
370
+ if (url.indexOf("/") === 0) {
371
+ // Absolute path
372
+ fullUrl += url;
374
373
  } else {
375
- //relative, so append current location to url first
376
- url += window.location.pathname + ajaxConfig.url;
374
+ // Relative path - ensure proper path joining
375
+ let basePath = window.location.pathname;
376
+ // If pathname does not end with "/", treat it as a file and use its directory
377
+ if (!basePath.endsWith("/")) {
378
+ const lastSlashIndex = basePath.lastIndexOf("/");
379
+ basePath = lastSlashIndex >= 0 ? basePath.substring(0, lastSlashIndex + 1) : "/";
380
+ }
381
+ fullUrl += basePath + url;
382
+ }
383
+ return fullUrl;
384
+ }
385
+ return url;
386
+ }
387
+
388
+ /**
389
+ * Check if ajax config contains authentication credentials
390
+ */
391
+ export function hasAuthenticationCredentials(ajaxConfig: PopulatedAjaxConfig): boolean {
392
+ if (!ajaxConfig) return false;
393
+
394
+ // Check for Authorization header (Bearer, Basic, OAuth2)
395
+ if (ajaxConfig.headers && ajaxConfig.headers["Authorization"]) {
396
+ return true;
397
+ }
398
+
399
+ // Check for API Key headers - use more specific patterns to avoid false positives
400
+ if (ajaxConfig.headers) {
401
+ for (const headerName in ajaxConfig.headers) {
402
+ const lowerHeader = headerName.toLowerCase();
403
+ // Match common authentication header patterns with stricter rules to avoid false positives
404
+ // Only match headers that are clearly authentication-related
405
+ if (
406
+ lowerHeader.startsWith("x-api-key") ||
407
+ lowerHeader.startsWith("x-auth-") ||
408
+ lowerHeader === "apikey" ||
409
+ lowerHeader === "api-key" ||
410
+ // Only match token headers that end with "token" or have "auth" in the middle
411
+ (lowerHeader.endsWith("-token") && lowerHeader.startsWith("x-")) ||
412
+ (lowerHeader.includes("-auth-") && lowerHeader.includes("token"))
413
+ ) {
414
+ return true;
415
+ }
377
416
  }
378
417
  }
418
+
419
+ return false;
420
+ }
421
+
422
+ /**
423
+ * Escape single quotes for shell commands by replacing ' with '\''
424
+ */
425
+ function escapeShellString(str: string): string {
426
+ return str.replace(/'/g, "'\\''");
427
+ }
428
+
429
+ /**
430
+ * Generate cURL command string
431
+ */
432
+ export function getAsCurlString(yasqe: Yasqe, _config?: Config["requestConfig"]) {
433
+ let ajaxConfig = getAjaxConfig(yasqe, getRequestConfigSettings(yasqe, _config));
434
+ if (!ajaxConfig) return "";
435
+
436
+ let url = normalizeUrl(ajaxConfig.url);
379
437
  const segments: string[] = ["curl"];
380
438
 
381
439
  if (ajaxConfig.reqMethod === "GET") {
382
440
  url += `?${queryString.stringify(ajaxConfig.args)}`;
383
- segments.push(url);
441
+ segments.push(`'${escapeShellString(url)}'`);
384
442
  } else if (ajaxConfig.reqMethod === "POST") {
385
- segments.push(url);
386
- segments.push("--data", queryString.stringify(ajaxConfig.args));
443
+ segments.push(`'${escapeShellString(url)}'`);
444
+ const data = queryString.stringify(ajaxConfig.args);
445
+ segments.push("--data", `'${escapeShellString(data)}'`);
387
446
  } else {
388
- // I don't expect to get here but let's be sure
389
447
  console.warn("Unexpected request-method", ajaxConfig.reqMethod);
390
- segments.push(url);
448
+ segments.push(`'${escapeShellString(url)}'`);
391
449
  }
450
+
392
451
  segments.push("-X", ajaxConfig.reqMethod);
452
+
453
+ // Add Accept header if present
454
+ if (ajaxConfig.accept) {
455
+ segments.push("-H", `'Accept: ${escapeShellString(ajaxConfig.accept)}'`);
456
+ }
457
+
458
+ for (const header in ajaxConfig.headers) {
459
+ segments.push("-H", `'${escapeShellString(header)}: ${escapeShellString(ajaxConfig.headers[header])}'`);
460
+ }
461
+
462
+ return segments.join(" \\\n ");
463
+ }
464
+
465
+ /**
466
+ * Escape PowerShell string by handling special characters
467
+ */
468
+ function escapePowerShellString(str: string): string {
469
+ // Escape backtick, double quote, and dollar sign
470
+ return str.replace(/`/g, "``").replace(/"/g, '`"').replace(/\$/g, "`$");
471
+ }
472
+
473
+ /**
474
+ * Generate PowerShell command string
475
+ */
476
+ export function getAsPowerShellString(yasqe: Yasqe, _config?: Config["requestConfig"]): string {
477
+ let ajaxConfig = getAjaxConfig(yasqe, getRequestConfigSettings(yasqe, _config));
478
+ if (!ajaxConfig) return "";
479
+
480
+ let url = normalizeUrl(ajaxConfig.url);
481
+ const lines: string[] = [];
482
+
483
+ // Determine output file extension based on Accept header
484
+ const acceptHeader = ajaxConfig.accept;
485
+ let fileExtension = "json"; // default
486
+ if (acceptHeader) {
487
+ if (acceptHeader.includes("text/turtle") || acceptHeader.includes("application/x-turtle")) {
488
+ fileExtension = "ttl";
489
+ } else if (acceptHeader.includes("application/rdf+xml")) {
490
+ fileExtension = "xml";
491
+ } else if (acceptHeader.includes("application/n-triples")) {
492
+ fileExtension = "nt";
493
+ } else if (acceptHeader.includes("application/ld+json")) {
494
+ fileExtension = "jsonld";
495
+ } else if (acceptHeader.includes("text/csv")) {
496
+ fileExtension = "csv";
497
+ } else if (acceptHeader.includes("text/tab-separated-values")) {
498
+ fileExtension = "tsv";
499
+ }
500
+ }
501
+
502
+ // Build headers object, including Accept header
503
+ const headersLines: string[] = [];
504
+ if (acceptHeader) {
505
+ headersLines.push(` "Accept" = "${escapePowerShellString(acceptHeader)}"`);
506
+ }
507
+ for (const header in ajaxConfig.headers) {
508
+ headersLines.push(
509
+ ` "${escapePowerShellString(header)}" = "${escapePowerShellString(ajaxConfig.headers[header])}"`,
510
+ );
511
+ }
512
+
513
+ if (ajaxConfig.reqMethod === "GET") {
514
+ url += `?${queryString.stringify(ajaxConfig.args)}`;
515
+ lines.push("$params = @{");
516
+ lines.push(` Uri = "${escapePowerShellString(url)}"`);
517
+ lines.push(` Method = "Get"`);
518
+ if (headersLines.length > 0) {
519
+ lines.push(" Headers = @{");
520
+ lines.push(headersLines.join("\n"));
521
+ lines.push(" }");
522
+ }
523
+ lines.push(` OutFile = "result.${fileExtension}"`);
524
+ lines.push("}");
525
+ } else if (ajaxConfig.reqMethod === "POST") {
526
+ const body = queryString.stringify(ajaxConfig.args);
527
+ lines.push("$params = @{");
528
+ lines.push(` Uri = "${escapePowerShellString(url)}"`);
529
+ lines.push(` Method = "Post"`);
530
+ if (headersLines.length > 0) {
531
+ lines.push(" Headers = @{");
532
+ lines.push(headersLines.join("\n"));
533
+ lines.push(" }");
534
+ }
535
+ lines.push(` ContentType = "application/x-www-form-urlencoded"`);
536
+ lines.push(` Body = "${escapePowerShellString(body)}"`);
537
+ lines.push(` OutFile = "result.${fileExtension}"`);
538
+ lines.push("}");
539
+ } else {
540
+ // Handle other methods (PUT, DELETE, etc.)
541
+ console.warn("Unexpected request-method for PowerShell", ajaxConfig.reqMethod);
542
+ const body = queryString.stringify(ajaxConfig.args);
543
+ lines.push("$params = @{");
544
+ lines.push(` Uri = "${url}"`);
545
+ lines.push(` Method = "${ajaxConfig.reqMethod}"`);
546
+ if (headersLines.length > 0) {
547
+ lines.push(" Headers = @{");
548
+ lines.push(headersLines.join("\n"));
549
+ lines.push(" }");
550
+ }
551
+ if (body) {
552
+ lines.push(` ContentType = "application/x-www-form-urlencoded"`);
553
+ lines.push(` Body = "${body.replace(/"/g, '`"')}"`);
554
+ }
555
+ lines.push(` OutFile = "result.${fileExtension}"`);
556
+ lines.push("}");
557
+ }
558
+
559
+ lines.push("");
560
+ lines.push("Invoke-WebRequest @params");
561
+
562
+ return lines.join("\n");
563
+ }
564
+
565
+ /**
566
+ * Generate wget command string
567
+ */
568
+ export function getAsWgetString(yasqe: Yasqe, _config?: Config["requestConfig"]): string {
569
+ let ajaxConfig = getAjaxConfig(yasqe, getRequestConfigSettings(yasqe, _config));
570
+ if (!ajaxConfig) return "";
571
+
572
+ let url = normalizeUrl(ajaxConfig.url);
573
+ const segments: string[] = ["wget"];
574
+
575
+ if (ajaxConfig.reqMethod === "GET") {
576
+ url += `?${queryString.stringify(ajaxConfig.args)}`;
577
+ segments.push(`'${escapeShellString(url)}'`);
578
+ } else if (ajaxConfig.reqMethod === "POST") {
579
+ segments.push(`'${escapeShellString(url)}'`);
580
+ const data = queryString.stringify(ajaxConfig.args);
581
+ segments.push("--post-data", `'${escapeShellString(data)}'`);
582
+ } else {
583
+ // Handle other methods
584
+ console.warn("Unexpected request-method for wget", ajaxConfig.reqMethod);
585
+ segments.push(`'${escapeShellString(url)}'`);
586
+ const data = queryString.stringify(ajaxConfig.args);
587
+ if (data) {
588
+ segments.push("--post-data", `'${escapeShellString(data)}'`);
589
+ }
590
+ }
591
+
592
+ // Only add --method for non-GET requests
593
+ if (ajaxConfig.reqMethod !== "GET") {
594
+ segments.push("--method", ajaxConfig.reqMethod);
595
+ }
596
+
597
+ // Add Accept header if present
598
+ if (ajaxConfig.accept) {
599
+ segments.push("--header", `'Accept: ${escapeShellString(ajaxConfig.accept)}'`);
600
+ }
601
+
393
602
  for (const header in ajaxConfig.headers) {
394
- segments.push(`-H '${header}: ${ajaxConfig.headers[header]}'`);
603
+ segments.push("--header", `'${escapeShellString(header)}: ${escapeShellString(ajaxConfig.headers[header])}'`);
395
604
  }
396
- return segments.join(" ");
605
+
606
+ segments.push("-O -");
607
+
608
+ return segments.join(" \\\n ");
397
609
  }