@stackql/provider-utils 0.1.4 → 0.1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackql/provider-utils",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Utilities for building StackQL providers from OpenAPI specifications.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -126,7 +126,8 @@ async function createDocsForService(yamlFilePath, providerName, serviceName, ser
126
126
  let dereferencedAPI;
127
127
 
128
128
  try {
129
- dereferencedAPI = await deno_openapi_dereferencer.dereferenceApi(api, "$", ignorePaths);
129
+ // dereferencedAPI = await deno_openapi_dereferencer.dereferenceApi(api, "$", ignorePaths);
130
+ const dereferencedAPI = await SwaggerParser.dereference(api);
130
131
  dereferencedAPI = await deno_openapi_dereferencer.flattenAllOf(dereferencedAPI);
131
132
  } catch (error) {
132
133
  console.error("error in dereferencing or flattening:", error);
@@ -23,41 +23,29 @@ export function getIndefiniteArticle(resourceName) {
23
23
  return article;
24
24
  }
25
25
 
26
- export function cleanDescription(description) {
27
- if (!description) return '';
28
-
29
- // Replace <a> tags with markdown equivalent
30
- description = description.replace(/<a\s+(?:[^>]*?\s+)?href="([^"]*)"(?:[^>]*?)>(.*?)<\/a>/gi, '[$2]($1)');
31
-
32
- // Remove <p> tags and replace them with a single space
33
- description = description.replace(/<\/?p>/gi, ' ');
34
-
35
- // Replace <br> tags with a single space
36
- description = description.replace(/<br\s*\/?>/gi, ' ');
37
-
38
- // Replace <code> and <pre> tags with markdown code blocks
39
- description = description.replace(/<(code|pre)>(.*?)<\/\1>/gi, '`$2`');
40
-
41
- // Convert <ul> and <li> tags into a comma-separated list
42
- description = description.replace(/<\/?ul>/gi, '');
43
- description = description.replace(/<li>(.*?)<\/li>/gi, '$1, ');
44
-
45
- // Remove <name>, <td>, <tr>, and <table> tags
46
- description = description.replace(/<\/?(name|td|tr|table)>/gi, '');
47
-
48
- // Replace multiple spaces with a single space
49
- description = description.replace(/\s+/g, ' ');
50
-
51
- // Escape pipe characters to prevent breaking markdown tables
52
- description = description.replace(/\|/g, '\\|');
53
-
54
- // Remove any trailing commas, spaces, and line breaks
55
- description = description.replace(/,s*$/, '').trim();
56
-
57
- description = description.replace(/</g, '{');
58
- description = description.replace(/>/g, '}');
59
-
60
- return description;
26
+ export function sanitizeHtml(text) {
27
+ return text
28
+ // Replace "<" unless it's followed by "a", "/a", "b", "/b", "strong", or "/strong"
29
+ .replace(/<(?!\/?(?:a|b|strong)\b)/gi, '&lt;')
30
+ // Replace ">" unless it's preceded by "</a", "<a ...>", "</b", "<b ...>", "</strong", or "<strong ...>"
31
+ .replace(/(?<!<\/?(?:a|b|strong)[^>]*)>/gi, '&gt;')
32
+ // Add quotes around unquoted href values (within <a ...>)
33
+ .replace(/(<a\b[^>]*?)href=([^"' \t\r\n>]+)/gi, '$1href="$2"')
34
+ // Wrap backticked text with <code>
35
+ .replace(/`([^`]+)`/g, '<code>$1</code>')
36
+ // Replace { and }
37
+ .replace(/{/g, '&#123;')
38
+ .replace(/}/g, '&#125;')
39
+ // Escape backslash
40
+ .replace(/\\/g, '\\\\')
41
+ // Replace " with &quot; UNLESS inside <code>...</code> OR inside an href="...".
42
+ // The alternation matches either a whole <code>...</code> block, an href="...",
43
+ // or a bare " to replace.
44
+ .replace(/(<code>[\s\S]*?<\/code>)|(\bhref="[^"]*")|"/gi, (m, code, href) => {
45
+ if (code) return code; // keep code blocks untouched
46
+ if (href) return href; // keep href="..." quotes untouched
47
+ return '&quot;'; // everything else: replace "
48
+ });
61
49
  }
62
50
 
63
51
  export function getSqlMethodsWithOrderedFields(resourceData, dereferencedAPI, sqlVerb) {
@@ -249,47 +237,13 @@ function formatProperties(respProps) {
249
237
  // Store formatted property details
250
238
  allProperties[propName] = {
251
239
  type: typeString,
252
- description: escapeHtml(fullDescription),
240
+ // description: escapeHtml(fullDescription),
241
+ description: fullDescription
253
242
  };
254
243
  }
255
244
  return allProperties;
256
245
  }
257
246
 
258
- function escapeHtml(text) {
259
- if (!text) return '';
260
- return text
261
- .replace(/&/g, '&amp;')
262
- .replace(/</g, '&lt;')
263
- .replace(/>/g, '&gt;')
264
- .replace(/"/g, '&quot;')
265
- .replace(/'/g, '&#039;');
266
- }
267
-
268
- export function sanitizeHtml(text) {
269
- return text
270
- // Replace "<" unless it's followed by "a", "/a", "b", "/b", "strong", or "/strong"
271
- .replace(/<(?!\/?(?:a|b|strong)\b)/gi, '&lt;')
272
- // Replace ">" unless it's preceded by "</a", "<a ...>", "</b", "<b ...>", "</strong", or "<strong ...>"
273
- .replace(/(?<!<\/?(?:a|b|strong)[^>]*)>/gi, '&gt;')
274
- // Add quotes around unquoted href values (within <a ...>)
275
- .replace(/(<a\b[^>]*?)href=([^"' \t\r\n>]+)/gi, '$1href="$2"')
276
- // Wrap backticked text with <code>
277
- .replace(/`([^`]+)`/g, '<code>$1</code>')
278
- // Replace { and }
279
- .replace(/{/g, '&#123;')
280
- .replace(/}/g, '&#125;')
281
- // Escape backslash
282
- .replace(/\\/g, '\\\\')
283
- // Replace " with &quot; UNLESS inside <code>...</code> OR inside an href="...".
284
- // The alternation matches either a whole <code>...</code> block, an href="...",
285
- // or a bare " to replace.
286
- .replace(/(<code>[\s\S]*?<\/code>)|(\bhref="[^"]*")|"/gi, (m, code, href) => {
287
- if (code) return code; // keep code blocks untouched
288
- if (href) return href; // keep href="..." quotes untouched
289
- return '&quot;'; // everything else: replace "
290
- });
291
- }
292
-
293
247
  function getRequiredServerVars(dereferencedAPI) {
294
248
  const serverVars = {};
295
249
 
@@ -389,7 +343,7 @@ function getHttpOperationInfo(dereferencedAPI, path, httpVerb, mediaType, openAP
389
343
  }
390
344
 
391
345
  // Get operation description and replace curly braces with HTML entities
392
- const opDescription = (dereferencedAPI.paths[path][httpVerb].description || '').replace(/\{/g, '&#123;').replace(/\}/g, '&#125;');
346
+ const opDescription = (dereferencedAPI.paths[path][httpVerb].description || ''); // .replace(/\{/g, '&#123;').replace(/\}/g, '&#125;');
393
347
 
394
348
  // Extract request body if it exists
395
349
  let requestBody = {};
@@ -498,26 +452,26 @@ function getHttpOperationParams(dereferencedAPI, path, httpVerb) {
498
452
 
499
453
  let additionalDescriptionParts = [];
500
454
 
501
- for (const [fieldName, fieldValue] of Object.entries(param.schema)) {
502
- if (fieldName === 'type' || fieldName === 'format' || fieldName === 'description' || fieldName === 'pattern') {
503
- continue;
504
- }
505
-
506
- let formattedValue;
507
- if (Array.isArray(fieldValue)) {
508
- formattedValue = `[${fieldValue.join(', ')}]`;
509
- } else if (typeof fieldValue === 'object' && fieldValue !== null) {
510
- formattedValue = JSON.stringify(fieldValue);
511
- } else {
512
- formattedValue = String(fieldValue);
513
- }
514
-
515
- // if (fieldName === 'pattern') {
516
- // additionalDescriptionParts.push(`pattern: <code>${formattedValue}</code>`);
517
- // } else {
518
- // additionalDescriptionParts.push(`${fieldName}: ${formattedValue}`);
519
- // }
520
- }
455
+ // for (const [fieldName, fieldValue] of Object.entries(param.schema)) {
456
+ // if (fieldName === 'type' || fieldName === 'format' || fieldName === 'description' || fieldName === 'pattern') {
457
+ // continue;
458
+ // }
459
+
460
+ // let formattedValue;
461
+ // if (Array.isArray(fieldValue)) {
462
+ // formattedValue = `[${fieldValue.join(', ')}]`;
463
+ // } else if (typeof fieldValue === 'object' && fieldValue !== null) {
464
+ // formattedValue = JSON.stringify(fieldValue);
465
+ // } else {
466
+ // formattedValue = String(fieldValue);
467
+ // }
468
+
469
+ // if (fieldName === 'pattern') {
470
+ // additionalDescriptionParts.push(`pattern: <code>${formattedValue}</code>`);
471
+ // } else {
472
+ // additionalDescriptionParts.push(`${fieldName}: ${formattedValue}`);
473
+ // }
474
+ // }
521
475
 
522
476
  // Add any fields from the parameter itself that might contain metadata
523
477
  for (const [fieldName, fieldValue] of Object.entries(param)) {
@@ -551,7 +505,7 @@ function getHttpOperationParams(dereferencedAPI, path, httpVerb) {
551
505
  // Create the parameter details object
552
506
  const paramDetails = {
553
507
  type: typeString,
554
- description: description // Apply escapeHtml here if needed
508
+ description: description
555
509
  };
556
510
 
557
511
  // Add to the appropriate category based on required flag
@@ -1,6 +1,7 @@
1
1
  // src/docgen/resource/examples/delete-example.js
2
2
  import {
3
3
  getSqlMethodsWithOrderedFields,
4
+ sanitizeHtml,
4
5
  } from '../../helpers.js';
5
6
 
6
7
  export function createDeleteExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI) {
@@ -29,8 +30,9 @@ export function createDeleteExamples(providerName, serviceName, resourceName, re
29
30
  content += '<TabItem value="' + methodName + '">\n\n';
30
31
 
31
32
  // Add method description
32
- content += methodDetails.opDescription || methodDetails.respDescription || 'No description available.';
33
-
33
+ const opDescription = methodDetails.opDescription || 'No description available.';
34
+ content += sanitizeHtml(opDescription);
35
+
34
36
  // Create SQL example
35
37
  content += '\n\n```sql\nDELETE FROM ' + providerName + '.' + serviceName + '.' + resourceName;
36
38
 
@@ -1,6 +1,7 @@
1
1
  // src/docgen/resource/examples/exec-example.js
2
2
  import {
3
3
  getSqlMethodsWithOrderedFields,
4
+ sanitizeHtml
4
5
  } from '../../helpers.js';
5
6
 
6
7
  export function createExecExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI) {
@@ -29,7 +30,8 @@ export function createExecExamples(providerName, serviceName, resourceName, reso
29
30
  content += '<TabItem value="' + methodName + '">\n\n';
30
31
 
31
32
  // Add method description
32
- content += methodDetails.opDescription || methodDetails.respDescription || 'No description available.';
33
+ const opDescription = methodDetails.opDescription || methodDetails.respDescription || 'No description available.';
34
+ content += sanitizeHtml(opDescription);
33
35
 
34
36
  // Create SQL example
35
37
  content += '\n\n```sql\nEXEC ' + providerName + '.' + serviceName + '.' + resourceName + '.' + methodName + ' \n';
@@ -1,6 +1,7 @@
1
1
  // src/docgen/resource/examples/insert-example.js
2
2
  import {
3
3
  getSqlMethodsWithOrderedFields,
4
+ sanitizeHtml
4
5
  } from '../../helpers.js';
5
6
 
6
7
  export function createInsertExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI) {
@@ -31,7 +32,11 @@ export function createInsertExamples(providerName, serviceName, resourceName, re
31
32
  // Create each method tab content
32
33
  Object.entries(insertMethods).forEach(([methodName, methodDetails]) => {
33
34
  content += '<TabItem value="' + methodName + '">\n\n';
34
- content += methodDetails.opDescription || 'No description available.';
35
+
36
+ // Add method description
37
+ const opDescription = methodDetails.opDescription || 'No description available.';
38
+ content += sanitizeHtml(opDescription);
39
+ // content += methodDetails.opDescription || 'No description available.';
35
40
 
36
41
  // Create SQL example
37
42
  content += '\n\n```sql\nINSERT INTO ' + providerName + '.' + serviceName + '.' + resourceName + ' (\n';
@@ -107,22 +112,6 @@ export function createInsertExamples(providerName, serviceName, resourceName, re
107
112
  content += '\n;\n```\n</TabItem>\n';
108
113
  });
109
114
 
110
-
111
- // {
112
- // "code": {
113
- // "type": "string",
114
- // "description": " (example: 000123)"
115
- // },
116
- // "createdOn": {
117
- // "type": "integer (int64)",
118
- // "description": "Timestamp that specifies when the statement execution started.‌ The timestamp is expressed in milliseconds since the epoch.‌"
119
- // },
120
- // "data": {
121
- // "type": "array",
122
- // "description": "Result set data."
123
- // },
124
- // }
125
-
126
115
  // Create manifest tab
127
116
  content += '<TabItem value="manifest">\n\n';
128
117
  content += '```yaml\n# Description fields are for documentation purposes\n- name: ' + resourceName + '\n props:\n';
@@ -1,6 +1,7 @@
1
1
  // src/docgen/resource/examples/select-example.js
2
2
  import {
3
3
  getSqlMethodsWithOrderedFields,
4
+ sanitizeHtml
4
5
  } from '../../helpers.js';
5
6
 
6
7
  export function createSelectExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI) {
@@ -27,8 +28,11 @@ export function createSelectExamples(providerName, serviceName, resourceName, re
27
28
  // Create each tab content
28
29
  Object.entries(selectMethods).forEach(([methodName, methodDetails]) => {
29
30
  content += '<TabItem value="' + methodName + '">\n\n';
30
- content += methodDetails.opDescription || 'No description available.';
31
-
31
+ // content += methodDetails.opDescription || 'No description available.';
32
+ // Add method description
33
+ const opDescription = methodDetails.opDescription || methodDetails.respDescription || 'No description available.';
34
+ content += sanitizeHtml(opDescription);
35
+
32
36
  // Create SQL example
33
37
  content += '\n\n```sql\nSELECT\n';
34
38
 
@@ -1,6 +1,7 @@
1
1
  // src/docgen/resource/examples/update-example.js
2
2
  import {
3
3
  getSqlMethodsWithOrderedFields,
4
+ sanitizeHtml
4
5
  } from '../../helpers.js';
5
6
 
6
7
  export function createUpdateExamples(providerName, serviceName, resourceName, resourceData, dereferencedAPI, isReplace = false) {
@@ -33,8 +34,11 @@ export function createUpdateExamples(providerName, serviceName, resourceName, re
33
34
  Object.entries(updateMethods).forEach(([methodName, methodDetails]) => {
34
35
  content += '<TabItem value="' + methodName + '">\n\n';
35
36
 
37
+ // // Add method description
38
+ // content += methodDetails.opDescription || methodDetails.respDescription || 'No description available.';
36
39
  // Add method description
37
- content += methodDetails.opDescription || methodDetails.respDescription || 'No description available.';
40
+ const opDescription = methodDetails.opDescription || 'No description available.';
41
+ content += sanitizeHtml(opDescription);
38
42
 
39
43
  // Create SQL example
40
44
  content += '\n\n```sql\n';
@@ -1,6 +1,7 @@
1
1
  // src/docgen/resource/fields.js
2
2
  import {
3
3
  getSqlMethodsWithOrderedFields,
4
+ sanitizeHtml,
4
5
  } from '../helpers.js';
5
6
 
6
7
  const mdCodeAnchor = "`";
@@ -58,7 +59,7 @@ export function createFieldsSection(resourceData, dereferencedAPI) {
58
59
  content += `\n<tr>
59
60
  <td><CopyableCode code="${propName}" /></td>
60
61
  <td><code>${propData.type}</code></td>
61
- <td>${propData.description}</td>
62
+ <td>${sanitizeHtml(propData.description)}</td>
62
63
  </tr>`;
63
64
  }
64
65
 
@@ -1,6 +1,5 @@
1
1
  // src/docgen/resource/methods.js
2
2
  import {
3
- cleanDescription,
4
3
  getSqlMethodsWithOrderedFields,
5
4
  sanitizeHtml
6
5
  } from '../helpers.js';