ondc-code-generator 0.8.9 → 1.0.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.
Files changed (95) hide show
  1. package/alpha/possible-json-paths.json +34134 -0
  2. package/alpha/table/page/index.html +11487 -0
  3. package/alpha/table/page/style.css +449 -0
  4. package/alpha/table/rag-table-docs/confirm.md +63 -0
  5. package/alpha/table/rag-table-docs/init.md +81 -0
  6. package/alpha/table/rag-table-docs/on_confirm.md +164 -0
  7. package/alpha/table/rag-table-docs/on_init.md +146 -0
  8. package/alpha/table/rag-table-docs/on_search.md +163 -0
  9. package/alpha/table/rag-table-docs/on_select.md +99 -0
  10. package/alpha/table/rag-table-docs/on_status.md +153 -0
  11. package/alpha/table/rag-table-docs/on_update.md +164 -0
  12. package/alpha/table/rag-table-docs/raw_table.json +11198 -0
  13. package/alpha/table/rag-table-docs/search.md +128 -0
  14. package/alpha/table/rag-table-docs/select.md +70 -0
  15. package/alpha/table/rag-table-docs/status.md +44 -0
  16. package/alpha/table/rag-table-docs/update.md +51 -0
  17. package/alpha/table/readme.md +1312 -0
  18. package/alpha/table/validPaths.json +34134 -0
  19. package/alpha/table.zip +0 -0
  20. package/dist/bin/cli.js +4 -0
  21. package/dist/generator/config-compiler.d.ts +16 -2
  22. package/dist/generator/config-compiler.js +57 -19
  23. package/dist/generator/generators/classes/abstract-generator.d.ts +2 -0
  24. package/dist/generator/generators/documentation/md-generator.d.ts +12 -5
  25. package/dist/generator/generators/documentation/md-generator.js +23 -28
  26. package/dist/generator/generators/documentation/templates/index.mustache +162 -26
  27. package/dist/generator/generators/documentation/templates/style.css +387 -142
  28. package/dist/generator/generators/go/go-generator.js +1 -1
  29. package/dist/generator/generators/javascript/js-generator.js +1 -1
  30. package/dist/generator/generators/python/py-generator.js +1 -1
  31. package/dist/generator/generators/rag/rag-generator.d.ts +48 -0
  32. package/dist/generator/generators/rag/rag-generator.js +185 -0
  33. package/dist/generator/generators/rag/rag-table-generator.d.ts +55 -0
  34. package/dist/generator/generators/rag/rag-table-generator.js +269 -0
  35. package/dist/generator/generators/typescript/ts-generator.js +1 -1
  36. package/dist/index.test.js +12 -3
  37. package/dist/types/build.d.ts +4 -0
  38. package/dist/types/compiler-types.d.ts +3 -1
  39. package/dist/types/compiler-types.js +2 -0
  40. package/package.json +1 -1
  41. package/alpha/docs/page/index.html +0 -6137
  42. package/alpha/docs/page/style.css +0 -204
  43. package/alpha/docs/readme.md +0 -5939
  44. package/alpha/docs/validPaths.json +0 -14351
  45. package/alpha/page/index.html +0 -6137
  46. package/alpha/page/style.css +0 -204
  47. package/alpha/readme.md +0 -5939
  48. package/alpha/validationpkg/examples/search.json +0 -143
  49. package/alpha/validationpkg/examples_output/search/case-001/output.json +0 -12
  50. package/alpha/validationpkg/go.mod +0 -8
  51. package/alpha/validationpkg/go.sum +0 -4
  52. package/alpha/validationpkg/jsonvalidations/cancel.go +0 -1289
  53. package/alpha/validationpkg/jsonvalidations/confirm.go +0 -9121
  54. package/alpha/validationpkg/jsonvalidations/init.go +0 -4864
  55. package/alpha/validationpkg/jsonvalidations/issue.go +0 -4868
  56. package/alpha/validationpkg/jsonvalidations/on_cancel.go +0 -7111
  57. package/alpha/validationpkg/jsonvalidations/on_confirm.go +0 -8903
  58. package/alpha/validationpkg/jsonvalidations/on_init.go +0 -4445
  59. package/alpha/validationpkg/jsonvalidations/on_issue.go +0 -2828
  60. package/alpha/validationpkg/jsonvalidations/on_issue_status.go +0 -1938
  61. package/alpha/validationpkg/jsonvalidations/on_search.go +0 -3356
  62. package/alpha/validationpkg/jsonvalidations/on_status.go +0 -8129
  63. package/alpha/validationpkg/jsonvalidations/on_track.go +0 -1415
  64. package/alpha/validationpkg/jsonvalidations/on_update.go +0 -8700
  65. package/alpha/validationpkg/jsonvalidations/search.go +0 -3585
  66. package/alpha/validationpkg/jsonvalidations/status.go +0 -1073
  67. package/alpha/validationpkg/jsonvalidations/track.go +0 -1073
  68. package/alpha/validationpkg/jsonvalidations/update.go +0 -3012
  69. package/alpha/validationpkg/main-validator.go +0 -196
  70. package/alpha/validationpkg/main-validator_test.go +0 -165
  71. package/alpha/validationpkg/storageutils/api_save_utils.go +0 -83
  72. package/alpha/validationpkg/storageutils/cancel.go +0 -30
  73. package/alpha/validationpkg/storageutils/confirm.go +0 -30
  74. package/alpha/validationpkg/storageutils/index.go +0 -132
  75. package/alpha/validationpkg/storageutils/init.go +0 -30
  76. package/alpha/validationpkg/storageutils/issue.go +0 -30
  77. package/alpha/validationpkg/storageutils/on_cancel.go +0 -30
  78. package/alpha/validationpkg/storageutils/on_confirm.go +0 -30
  79. package/alpha/validationpkg/storageutils/on_init.go +0 -30
  80. package/alpha/validationpkg/storageutils/on_issue.go +0 -30
  81. package/alpha/validationpkg/storageutils/on_issue_status.go +0 -30
  82. package/alpha/validationpkg/storageutils/on_search.go +0 -30
  83. package/alpha/validationpkg/storageutils/on_status.go +0 -30
  84. package/alpha/validationpkg/storageutils/on_track.go +0 -30
  85. package/alpha/validationpkg/storageutils/on_update.go +0 -30
  86. package/alpha/validationpkg/storageutils/save_utils.go +0 -75
  87. package/alpha/validationpkg/storageutils/search.go +0 -30
  88. package/alpha/validationpkg/storageutils/status.go +0 -30
  89. package/alpha/validationpkg/storageutils/track.go +0 -30
  90. package/alpha/validationpkg/storageutils/update.go +0 -30
  91. package/alpha/validationpkg/validationutils/json_normalizer.go +0 -152
  92. package/alpha/validationpkg/validationutils/json_path_utils.go +0 -173
  93. package/alpha/validationpkg/validationutils/storage-interface.go +0 -107
  94. package/alpha/validationpkg/validationutils/test-config.go +0 -69
  95. package/alpha/validationpkg/validationutils/validation_utils.go +0 -429
Binary file
package/dist/bin/cli.js CHANGED
@@ -126,6 +126,10 @@ function getSupportedLanguage(lang) {
126
126
  return SupportedLanguages.Golang;
127
127
  case "md":
128
128
  return SupportedLanguages.Markdown;
129
+ case "rag":
130
+ return SupportedLanguages.RAG;
131
+ case "rag_table":
132
+ return SupportedLanguages.RAG_TABLE;
129
133
  default:
130
134
  throw new Error(`Unsupported language: ${lang}. Supported languages are: ${getValidLanguageOptions()}`);
131
135
  }
@@ -15,15 +15,29 @@ export declare class ConfigCompiler {
15
15
  errorDefinitions: ErrorDefinition[] | undefined;
16
16
  generatorConfig: CodeGeneratorConfig | undefined;
17
17
  language: SupportedLanguages;
18
+ domain?: string | string[];
19
+ version?: string;
18
20
  private SchemaExtractionService;
19
21
  constructor(language: SupportedLanguages);
20
22
  initialize: (buildYaml: string, generatorConfig?: Partial<CodeGeneratorConfig>) => Promise<void>;
21
23
  performValidations: (valConfig: ValidationConfig) => Promise<void>;
22
24
  withMinimalValidations: (valConfig: ValidationConfig) => Promise<void>;
23
- generateCode: (valConfig: ValidationConfig, codeName?: string, minimal?: boolean, outputPath?: string, absolutePath?: boolean, goPackageName?: string) => Promise<void>;
25
+ generateCode: (valConfig: ValidationConfig, codeGenConfig?: CodeGenConfig | string) => Promise<void>;
24
26
  generateL0Schema: (outputPath?: string, type?: "json" | "typescript", absolutePath?: boolean) => Promise<void>;
25
27
  generateValidPaths: () => Promise<Record<string, string[]>>;
26
- generateValidationFromBuild: (codeName: string, outputPath: string, absolutePath?: boolean, goPackageName?: string) => Promise<void>;
28
+ generateValidationFromBuild: (codeGenConfig: CodeGenConfig | string, outputPath?: string, absolutePath?: boolean, goPackageName?: string) => Promise<void>;
27
29
  extractPayloadsFromBuild: (outputPath: string) => Promise<void>;
28
30
  }
31
+ export type CodeGenConfig = {
32
+ codeName: string;
33
+ goPkgName?: string;
34
+ domain?: string | string[];
35
+ version?: string;
36
+ /** Skip full path/enum validations and run minimal checks only. Default: false */
37
+ minimal?: boolean;
38
+ /** Base output directory. Default: "./" */
39
+ outputPath?: string;
40
+ /** Treat outputPath as absolute (skip prepending "generated/<codeName>"). Default: false */
41
+ absolutePath?: boolean;
42
+ };
29
43
  export {};
@@ -13,6 +13,8 @@ import { PythonGenerator } from "./generators/python/py-generator.js";
13
13
  import { JavascriptGenerator } from "./generators/javascript/js-generator.js";
14
14
  import { GoGenerator } from "./generators/go/go-generator.js";
15
15
  import { MarkdownDocGenerator } from "./generators/documentation/md-generator.js";
16
+ import { RagGenerator } from "./generators/rag/rag-generator.js";
17
+ import { RagTableGenerator } from "./generators/rag/rag-table-generator.js";
16
18
  const __filename = fileURLToPath(import.meta.url);
17
19
  const __dirname = path.dirname(__filename);
18
20
  const defaultConfig = {
@@ -32,9 +34,10 @@ export class ConfigCompiler {
32
34
  this.SchemaExtractionService.extractPossiblePaths(this.jsonSchemas);
33
35
  const errors = this.buildData["x-errorcodes"];
34
36
  this.errorDefinitions = errors.code;
37
+ this.domain = this.buildData.info?.domain;
38
+ this.version = this.buildData.info?.version;
35
39
  };
36
40
  this.performValidations = async (valConfig) => {
37
- // try {
38
41
  if (!this.buildData)
39
42
  throw new Error("Build data not initialized");
40
43
  if (!this.jsonSchemas)
@@ -44,22 +47,19 @@ export class ConfigCompiler {
44
47
  if (!this.errorDefinitions)
45
48
  throw new Error("Error definitions not initialized");
46
49
  await new ConfigValidator("", valConfig, this.possibleJsonPaths, this.errorDefinitions).validate();
47
- // } catch (e) {
48
- // logger.error(e);
49
- // throw new Error(e as any);
50
- // }
51
50
  };
52
51
  this.withMinimalValidations = async (valConfig) => {
53
- // try {
54
52
  await new ConfigValidator("", valConfig, {}, [], {
55
53
  minimal: true,
56
54
  }).validate();
57
- // } catch (e) {
58
- // logger.error(e);
59
- // throw new Error("validation failed");
60
55
  };
61
- // };
62
- this.generateCode = async (valConfig, codeName = "L1-Validations", minimal = false, outputPath = "./", absolutePath = false, goPackageName = "validationpkg") => {
56
+ this.generateCode = async (valConfig, codeGenConfig = "L1-Validations") => {
57
+ // Accept either the new CodeGenConfig object or the legacy positional
58
+ // string (codeName) so existing call-sites continue to work.
59
+ const config = typeof codeGenConfig === "string"
60
+ ? { codeName: codeGenConfig }
61
+ : codeGenConfig;
62
+ const { codeName = "L1-Validations", goPkgName = "validationpkg", minimal = false, outputPath = "./", absolutePath = false, domain = this.domain, version = this.version, } = config;
63
63
  try {
64
64
  console.log("[ CODE GENERATION ] Starting code generation...");
65
65
  valConfig = JSON.parse(JSON.stringify(valConfig));
@@ -80,26 +80,52 @@ export class ConfigCompiler {
80
80
  case SupportedLanguages.Typescript:
81
81
  await new TypescriptGenerator(valConfig, this.errorDefinitions ?? [], targetPath).generateCode({
82
82
  codeName: codeName,
83
+ domain: domain,
84
+ version: version,
83
85
  });
84
86
  break;
85
87
  case SupportedLanguages.Python:
86
88
  await new PythonGenerator(valConfig, this.errorDefinitions ?? [], targetPath).generateCode({
87
89
  codeName: codeName,
90
+ domain: domain,
91
+ version: version,
88
92
  });
89
93
  break;
90
94
  case SupportedLanguages.Javascript:
91
95
  await new JavascriptGenerator(valConfig, this.errorDefinitions ?? [], targetPath).generateCode({
92
96
  codeName: codeName,
97
+ domain: domain,
98
+ version: version,
93
99
  });
94
100
  break;
95
101
  case SupportedLanguages.Golang:
96
102
  await new GoGenerator(valConfig, this.errorDefinitions ?? [], targetPath).generateCode({
97
103
  codeName: codeName,
98
- goPkgName: goPackageName,
104
+ goPkgName: goPkgName,
105
+ domain: domain,
106
+ version: version,
99
107
  });
100
108
  break;
101
109
  case SupportedLanguages.Markdown:
102
- await new MarkdownDocGenerator(valConfig, this.errorDefinitions ?? [], targetPath).generateCode();
110
+ await new MarkdownDocGenerator(valConfig, this.errorDefinitions ?? [], targetPath).generateCode({
111
+ codeName: codeName,
112
+ domain: domain,
113
+ version: version,
114
+ });
115
+ break;
116
+ case SupportedLanguages.RAG:
117
+ await new RagGenerator(valConfig, this.errorDefinitions ?? [], targetPath).generateCode({
118
+ codeName: codeName,
119
+ domain: domain,
120
+ version: version,
121
+ });
122
+ break;
123
+ case SupportedLanguages.RAG_TABLE:
124
+ await new RagTableGenerator(valConfig, this.errorDefinitions ?? [], targetPath).generateCode({
125
+ codeName: codeName,
126
+ domain: domain,
127
+ version: version,
128
+ });
103
129
  break;
104
130
  default:
105
131
  throw new Error("Language not supported");
@@ -144,19 +170,31 @@ export class ConfigCompiler {
144
170
  this.generateValidPaths = async () => {
145
171
  if (!this.possibleJsonPaths)
146
172
  throw new Error("Possible paths not initialized");
147
- // writeFileSync(
148
- // "./validPaths.json",
149
- // JSON.stringify(this.possibleJsonPaths, null, 2)
150
- // );
151
173
  return this.possibleJsonPaths;
152
174
  };
153
- this.generateValidationFromBuild = async (codeName, outputPath, absolutePath = false, goPackageName = "validationpkg") => {
175
+ this.generateValidationFromBuild = async (codeGenConfig, outputPath, absolutePath, goPackageName) => {
154
176
  if (!this.buildData)
155
177
  throw new Error("Build data not initialized");
156
178
  const valConfig = this.buildData["x-validations"];
157
179
  if (!valConfig)
158
180
  throw new Error("No validation config found in build data");
159
- await this.generateCode(valConfig, codeName, false, outputPath, absolutePath, goPackageName);
181
+ // Support legacy positional call: (codeName, outputPath, absolutePath, goPkgName)
182
+ const config = typeof codeGenConfig === "string"
183
+ ? {
184
+ codeName: codeGenConfig,
185
+ outputPath: outputPath ?? "./",
186
+ absolutePath: absolutePath ?? false,
187
+ goPkgName: goPackageName ?? "validationpkg",
188
+ domain: this.buildData.info?.domain,
189
+ version: this.buildData.info?.version,
190
+ }
191
+ : {
192
+ ...codeGenConfig,
193
+ domain: codeGenConfig.domain ?? this.buildData?.info?.domain,
194
+ version: codeGenConfig.version ??
195
+ this.buildData?.info?.version,
196
+ };
197
+ await this.generateCode(valConfig, config);
160
198
  };
161
199
  this.extractPayloadsFromBuild = async (outputPath) => {
162
200
  if (!this.buildData)
@@ -3,6 +3,8 @@ import { ErrorDefinition } from "../../../types/error-codes";
3
3
  export type CodeGeneratorProps = {
4
4
  codeName: string;
5
5
  goPkgName?: string;
6
+ domain?: string | string[];
7
+ version?: string;
6
8
  };
7
9
  export declare abstract class CodeGenerator {
8
10
  validationConfig: ValidationConfig;
@@ -1,10 +1,17 @@
1
- import { TestObject } from "../../../types/config-types.js";
2
- import { CodeGenerator } from "../classes/abstract-generator.js";
1
+ import { CodeGenerator, CodeGeneratorProps } from "../classes/abstract-generator.js";
2
+ /**
3
+ * MarkdownDocGenerator — generates a readme.md and a styled index.html
4
+ * for a validation config, reusing RagTableGenerator for the table layout.
5
+ *
6
+ * Output:
7
+ * ./readme.md — concatenated GFM table markdown for all actions
8
+ * ./page/index.html — the same content rendered to HTML with a stylesheet
9
+ * ./page/style.css — stylesheet
10
+ */
3
11
  export declare class MarkdownDocGenerator extends CodeGenerator {
4
12
  generateSessionDataCode(): Promise<void>;
5
13
  generateUnitTestingCode(): Promise<void>;
14
+ codeConfig: CodeGeneratorProps | undefined;
6
15
  generateValidationCode: () => Promise<void>;
7
- generateCode: () => Promise<void>;
8
- generateMarkdownForTest: (testObject: TestObject) => string;
9
- generateHtmlCode: (markdownData: string) => Promise<string>;
16
+ generateCode: (codeConfig: CodeGeneratorProps) => Promise<void>;
10
17
  }
@@ -1,55 +1,50 @@
1
- import { ConfigSyntax, TestObjectSyntax } from "../../../constants/syntax.js";
2
- import { CodeGenerator } from "../classes/abstract-generator.js";
3
- import { markdownMessageGenerator } from "./markdown-message-generator.js";
1
+ import { ConfigSyntax } from "../../../constants/syntax.js";
2
+ import { CodeGenerator, } from "../classes/abstract-generator.js";
4
3
  import { writeFileWithFsExtra } from "../../../utils/fs-utils.js";
5
4
  import { marked } from "marked";
6
5
  import Mustache from "mustache";
7
6
  import { readFileSync } from "fs";
8
7
  import path from "path";
9
8
  import { fileURLToPath } from "url";
10
- import { addTabToMarkdown } from "../../../utils/general-utils/string-utils.js";
9
+ import { RagTableGenerator } from "../rag/rag-table-generator.js";
11
10
  const __filename = fileURLToPath(import.meta.url);
12
11
  const __dirname = path.dirname(__filename);
12
+ /**
13
+ * MarkdownDocGenerator — generates a readme.md and a styled index.html
14
+ * for a validation config, reusing RagTableGenerator for the table layout.
15
+ *
16
+ * Output:
17
+ * ./readme.md — concatenated GFM table markdown for all actions
18
+ * ./page/index.html — the same content rendered to HTML with a stylesheet
19
+ * ./page/style.css — stylesheet
20
+ */
13
21
  export class MarkdownDocGenerator extends CodeGenerator {
14
22
  constructor() {
15
23
  super(...arguments);
16
24
  this.generateValidationCode = async () => {
17
25
  const testConfig = this.validationConfig[ConfigSyntax.Tests];
26
+ // Reuse RagTableGenerator's table builder — same config, same error codes
27
+ const tableGen = new RagTableGenerator(this.validationConfig, this.errorCodes, this.rootPath);
28
+ // Build one GFM table section per action and concatenate
18
29
  let finalMarkdown = "";
19
- for (const key in testConfig) {
20
- const testObjects = testConfig[key];
21
- const betaConfig = {
22
- [TestObjectSyntax.Name]: key,
23
- [TestObjectSyntax.Return]: testObjects,
24
- };
25
- const md = this.generateMarkdownForTest(betaConfig);
26
- finalMarkdown += `\n\n${md}`;
30
+ for (const action of Object.keys(testConfig)) {
31
+ const section = tableGen.buildActionMarkdown(action, action, // use action name as codeName heading inside the doc
32
+ testConfig[action], this.codeConfig);
33
+ finalMarkdown += `\n\n${section}`;
27
34
  }
35
+ finalMarkdown = finalMarkdown.trimStart();
28
36
  const cssData = readFileSync(path.resolve(__dirname, "./templates/style.css"), "utf-8");
29
37
  const htmlTemplate = readFileSync(path.resolve(__dirname, "./templates/index.mustache"), "utf-8");
30
38
  writeFileWithFsExtra(this.rootPath, "./readme.md", finalMarkdown);
31
39
  writeFileWithFsExtra(this.rootPath, "./page/index.html", Mustache.render(htmlTemplate, {
32
- content: await this.generateHtmlCode(finalMarkdown),
40
+ content: await marked(finalMarkdown),
33
41
  }));
34
42
  writeFileWithFsExtra(this.rootPath, "./page/style.css", cssData);
35
43
  };
36
- this.generateCode = async () => {
44
+ this.generateCode = async (codeConfig) => {
45
+ this.codeConfig = codeConfig; // store for use in generateValidationCode
37
46
  await this.generateValidationCode();
38
47
  };
39
- this.generateMarkdownForTest = (testObject) => {
40
- const ret = testObject[TestObjectSyntax.Return];
41
- if (typeof ret === "string") {
42
- const skip = testObject[TestObjectSyntax.Continue];
43
- return markdownMessageGenerator(ret, testObject, testObject[TestObjectSyntax.Name], skip ? [skip] : undefined);
44
- }
45
- const subMardowns = ret.map((r) => {
46
- return this.generateMarkdownForTest(r);
47
- });
48
- return `- **${testObject[TestObjectSyntax.Name]}** : All the following sub conditions must pass as per the api requirement\n\n${addTabToMarkdown(subMardowns.join("\n\n"))}`;
49
- };
50
- this.generateHtmlCode = async (markdownData) => {
51
- return await marked(markdownData);
52
- };
53
48
  }
54
49
  generateSessionDataCode() {
55
50
  throw new Error("Method not implemented.");
@@ -1,36 +1,172 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
3
  <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>API SERVICE VALIDATIONS</title>
7
- <!-- Link to the CSS file -->
8
- <link rel="stylesheet" href="./style.css">
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>API Validations</title>
7
+ <link rel="stylesheet" href="./style.css">
9
8
  </head>
10
9
  <body>
11
- <div id="content">
12
- <h2>List of validations of api</h2>
13
- {{{content}}}
10
+
11
+ <header class="top-bar">
12
+ <span class="logo">&#9724; ONDC Validations</span>
13
+ <div class="toolbar">
14
+ <div class="search-wrap">
15
+ <svg class="search-icon" viewBox="0 0 20 20" fill="none">
16
+ <circle cx="8.5" cy="8.5" r="5.5" stroke="currentColor" stroke-width="1.8"/>
17
+ <path d="M13 13l3.5 3.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
18
+ </svg>
19
+ <input id="search" type="text" placeholder="Search tests, descriptions, error codes&hellip;" autocomplete="off">
20
+ <button id="clear-search" class="clear-btn hidden">&times;</button>
14
21
  </div>
15
- </body>
22
+ <div class="filter-group" id="type-filter">
23
+ <button class="pill active" data-type="all">All</button>
24
+ <button class="pill" data-type="group">GRP - Groups</button>
25
+ <button class="pill" data-type="leaf">LF - Tests</button>
26
+ </div>
27
+ </div>
28
+ </header>
29
+
30
+ <div class="layout">
31
+ <nav id="sidebar">
32
+ <p class="nav-heading">Actions</p>
33
+ <!-- populated by JS -->
34
+ </nav>
35
+
36
+ <main id="content">
37
+ {{{content}}}
38
+ </main>
39
+ </div>
40
+
41
+ <div id="match-count" class="hidden"></div>
42
+
16
43
  <script>
17
- document.addEventListener('DOMContentLoaded', function() {
18
- // Select all list items that have a nested <ul> (collapsible items)
19
- const collapsibleItems = document.querySelectorAll('li > ul');
20
-
21
- collapsibleItems.forEach(function(nestedList) {
22
- const parentLi = nestedList.parentElement;
23
- parentLi.classList.add('collapsible', 'collapsed');
24
-
25
- // Add click event listener to the <p> inside the list item
26
- const clickableArea = parentLi.querySelector('p');
27
- clickableArea.addEventListener('click', function(e) {
28
- // Prevent click events on links inside the <p>
29
- if (e.target.tagName !== 'A') {
30
- parentLi.classList.toggle('collapsed');
31
- }
32
- });
33
- });
44
+ (function () {
45
+
46
+ /* ── 1. Strip YAML front-matter bleed-through ─────────────────────────
47
+ marked renders --- as <hr> and the key:value block as <h2> */
48
+ document.querySelectorAll('#content hr').forEach(hr => {
49
+ const sib = hr.nextElementSibling;
50
+ if (sib && sib.tagName === 'H2' &&
51
+ /action:|codeName:|numTests:|generated:/.test(sib.textContent)) {
52
+ const trailingHr = sib.nextElementSibling;
53
+ if (trailingHr && trailingHr.tagName === 'HR') trailingHr.remove();
54
+ sib.remove();
55
+ hr.remove();
56
+ }
57
+ });
58
+
59
+ /* ── 2. Build sidebar nav from h1 headings ────────────────────────── */
60
+ const nav = document.getElementById('sidebar');
61
+ document.querySelectorAll('#content h1').forEach((h1, i) => {
62
+ const id = 'section-' + i;
63
+ h1.id = id;
64
+ const link = document.createElement('a');
65
+ link.href = '#' + id;
66
+ const match = h1.textContent.match(/`([^`]+)`/);
67
+ link.textContent = match ? match[1] : h1.textContent.slice(0, 28);
68
+ link.addEventListener('click', () => {
69
+ nav.querySelectorAll('a').forEach(a => a.classList.remove('active'));
70
+ link.classList.add('active');
34
71
  });
72
+ nav.appendChild(link);
73
+ });
74
+
75
+ /* ── 3. Tag every table body row with data attributes ─────────────── */
76
+ document.querySelectorAll('table tbody tr').forEach(row => {
77
+ const typeCell = row.cells[1];
78
+ if (!typeCell) return;
79
+ row.dataset.rowtype = typeCell.textContent.includes('GRP') ? 'group' : 'leaf';
80
+ row.dataset.text = row.textContent.toLowerCase();
81
+ /* cache original inner HTML of each cell for highlight restore */
82
+ Array.from(row.cells).forEach(td => { td.dataset.raw = td.innerHTML; });
83
+ });
84
+
85
+ /* ── 4. Shared filter function ────────────────────────────────────── */
86
+ const searchInput = document.getElementById('search');
87
+ const clearBtn = document.getElementById('clear-search');
88
+ const countEl = document.getElementById('match-count');
89
+ let activeType = 'all';
90
+
91
+ function escapeRe(s) {
92
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
93
+ }
94
+
95
+ function applyFilters() {
96
+ const q = searchInput.value.trim().toLowerCase();
97
+ clearBtn.classList.toggle('hidden', q === '');
98
+ let visible = 0, total = 0;
99
+
100
+ document.querySelectorAll('table tbody tr').forEach(row => {
101
+ const typeOk = activeType === 'all' || row.dataset.rowtype === activeType;
102
+ const textOk = q === '' || (row.dataset.text || '').includes(q);
103
+ const show = typeOk && textOk;
104
+ row.style.display = show ? '' : 'none';
105
+ if (show) visible++;
106
+ total++;
107
+
108
+ /* inline search highlight */
109
+ Array.from(row.cells).forEach(td => {
110
+ if (!td.dataset.raw) return;
111
+ if (q === '') {
112
+ td.innerHTML = td.dataset.raw;
113
+ } else {
114
+ td.innerHTML = td.dataset.raw.replace(
115
+ new RegExp('(' + escapeRe(q) + ')', 'gi'),
116
+ '<mark>$1</mark>'
117
+ );
118
+ }
119
+ });
120
+ });
121
+
122
+ const filtered = q !== '' || activeType !== 'all';
123
+ countEl.textContent = filtered ? visible + ' / ' + total + ' rows shown' : '';
124
+ countEl.classList.toggle('hidden', !filtered);
125
+ }
126
+
127
+ searchInput.addEventListener('input', applyFilters);
128
+ clearBtn.addEventListener('click', () => {
129
+ searchInput.value = '';
130
+ applyFilters();
131
+ searchInput.focus();
132
+ });
133
+
134
+ /* ── 5. Type filter pills ─────────────────────────────────────────── */
135
+ document.querySelectorAll('#type-filter .pill').forEach(btn => {
136
+ btn.addEventListener('click', () => {
137
+ document.querySelectorAll('#type-filter .pill')
138
+ .forEach(b => b.classList.remove('active'));
139
+ btn.classList.add('active');
140
+ activeType = btn.dataset.type;
141
+ applyFilters();
142
+ });
143
+ });
144
+
145
+ /* ── 6. Keyboard shortcut: / to focus search ─────────────────────── */
146
+ document.addEventListener('keydown', e => {
147
+ if (e.key === '/' && document.activeElement !== searchInput) {
148
+ e.preventDefault();
149
+ searchInput.focus();
150
+ }
151
+ if (e.key === 'Escape') {
152
+ searchInput.value = '';
153
+ applyFilters();
154
+ searchInput.blur();
155
+ }
156
+ });
157
+
158
+ /* ── 7. Highlight active section on scroll ────────────────────────── */
159
+ const observer = new IntersectionObserver(entries => {
160
+ entries.forEach(e => {
161
+ if (e.isIntersecting) {
162
+ nav.querySelectorAll('a').forEach(a =>
163
+ a.classList.toggle('active', a.getAttribute('href') === '#' + e.target.id));
164
+ }
165
+ });
166
+ }, { threshold: 0.5 });
167
+ document.querySelectorAll('#content h1').forEach(h => observer.observe(h));
168
+
169
+ })();
35
170
  </script>
171
+ </body>
36
172
  </html>