@contractspec/module.workspace 0.0.0-canary-20260113162409

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 (170) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -0
  3. package/dist/ai/prompts/code-generation.d.ts +24 -0
  4. package/dist/ai/prompts/code-generation.d.ts.map +1 -0
  5. package/dist/ai/prompts/code-generation.js +134 -0
  6. package/dist/ai/prompts/code-generation.js.map +1 -0
  7. package/dist/ai/prompts/spec-creation.d.ts +28 -0
  8. package/dist/ai/prompts/spec-creation.d.ts.map +1 -0
  9. package/dist/ai/prompts/spec-creation.js +102 -0
  10. package/dist/ai/prompts/spec-creation.js.map +1 -0
  11. package/dist/analysis/deps/graph.d.ts +34 -0
  12. package/dist/analysis/deps/graph.d.ts.map +1 -0
  13. package/dist/analysis/deps/graph.js +85 -0
  14. package/dist/analysis/deps/graph.js.map +1 -0
  15. package/dist/analysis/deps/parse-imports.d.ts +17 -0
  16. package/dist/analysis/deps/parse-imports.d.ts.map +1 -0
  17. package/dist/analysis/deps/parse-imports.js +31 -0
  18. package/dist/analysis/deps/parse-imports.js.map +1 -0
  19. package/dist/analysis/diff/deep-diff.d.ts +33 -0
  20. package/dist/analysis/diff/deep-diff.d.ts.map +1 -0
  21. package/dist/analysis/diff/deep-diff.js +114 -0
  22. package/dist/analysis/diff/deep-diff.js.map +1 -0
  23. package/dist/analysis/diff/semantic.d.ts +11 -0
  24. package/dist/analysis/diff/semantic.d.ts.map +1 -0
  25. package/dist/analysis/diff/semantic.js +97 -0
  26. package/dist/analysis/diff/semantic.js.map +1 -0
  27. package/dist/analysis/example-scan.d.ts +15 -0
  28. package/dist/analysis/example-scan.d.ts.map +1 -0
  29. package/dist/analysis/example-scan.js +116 -0
  30. package/dist/analysis/example-scan.js.map +1 -0
  31. package/dist/analysis/feature-extractor.js +203 -0
  32. package/dist/analysis/feature-extractor.js.map +1 -0
  33. package/dist/analysis/feature-scan.d.ts +15 -0
  34. package/dist/analysis/feature-scan.d.ts.map +1 -0
  35. package/dist/analysis/feature-scan.js +56 -0
  36. package/dist/analysis/feature-scan.js.map +1 -0
  37. package/dist/analysis/grouping.d.ts +79 -0
  38. package/dist/analysis/grouping.d.ts.map +1 -0
  39. package/dist/analysis/grouping.js +115 -0
  40. package/dist/analysis/grouping.js.map +1 -0
  41. package/dist/analysis/impact/classifier.d.ts +19 -0
  42. package/dist/analysis/impact/classifier.d.ts.map +1 -0
  43. package/dist/analysis/impact/classifier.js +135 -0
  44. package/dist/analysis/impact/classifier.js.map +1 -0
  45. package/dist/analysis/impact/index.js +2 -0
  46. package/dist/analysis/impact/rules.d.ts +35 -0
  47. package/dist/analysis/impact/rules.d.ts.map +1 -0
  48. package/dist/analysis/impact/rules.js +154 -0
  49. package/dist/analysis/impact/rules.js.map +1 -0
  50. package/dist/analysis/impact/types.d.ts +95 -0
  51. package/dist/analysis/impact/types.d.ts.map +1 -0
  52. package/dist/analysis/index.js +18 -0
  53. package/dist/analysis/snapshot/index.js +2 -0
  54. package/dist/analysis/snapshot/normalizer.d.ts +36 -0
  55. package/dist/analysis/snapshot/normalizer.d.ts.map +1 -0
  56. package/dist/analysis/snapshot/normalizer.js +67 -0
  57. package/dist/analysis/snapshot/normalizer.js.map +1 -0
  58. package/dist/analysis/snapshot/snapshot.d.ts +18 -0
  59. package/dist/analysis/snapshot/snapshot.d.ts.map +1 -0
  60. package/dist/analysis/snapshot/snapshot.js +163 -0
  61. package/dist/analysis/snapshot/snapshot.js.map +1 -0
  62. package/dist/analysis/snapshot/types.d.ts +74 -0
  63. package/dist/analysis/snapshot/types.d.ts.map +1 -0
  64. package/dist/analysis/spec-parser.d.ts +11 -0
  65. package/dist/analysis/spec-parser.d.ts.map +1 -0
  66. package/dist/analysis/spec-parser.js +89 -0
  67. package/dist/analysis/spec-parser.js.map +1 -0
  68. package/dist/analysis/spec-parsing-utils.d.ts +26 -0
  69. package/dist/analysis/spec-parsing-utils.d.ts.map +1 -0
  70. package/dist/analysis/spec-parsing-utils.js +98 -0
  71. package/dist/analysis/spec-parsing-utils.js.map +1 -0
  72. package/dist/analysis/spec-scan.d.ts +20 -0
  73. package/dist/analysis/spec-scan.d.ts.map +1 -0
  74. package/dist/analysis/spec-scan.js +141 -0
  75. package/dist/analysis/spec-scan.js.map +1 -0
  76. package/dist/analysis/utils/matchers.js +77 -0
  77. package/dist/analysis/utils/matchers.js.map +1 -0
  78. package/dist/analysis/utils/variables.js +45 -0
  79. package/dist/analysis/utils/variables.js.map +1 -0
  80. package/dist/analysis/validate/index.js +1 -0
  81. package/dist/analysis/validate/spec-structure.d.ts +29 -0
  82. package/dist/analysis/validate/spec-structure.d.ts.map +1 -0
  83. package/dist/analysis/validate/spec-structure.js +455 -0
  84. package/dist/analysis/validate/spec-structure.js.map +1 -0
  85. package/dist/formatter.d.ts +42 -0
  86. package/dist/formatter.d.ts.map +1 -0
  87. package/dist/formatter.js +163 -0
  88. package/dist/formatter.js.map +1 -0
  89. package/dist/formatters/index.js +2 -0
  90. package/dist/formatters/spec-markdown.d.ts +31 -0
  91. package/dist/formatters/spec-markdown.d.ts.map +1 -0
  92. package/dist/formatters/spec-markdown.js +263 -0
  93. package/dist/formatters/spec-markdown.js.map +1 -0
  94. package/dist/formatters/spec-to-docblock.d.ts +14 -0
  95. package/dist/formatters/spec-to-docblock.d.ts.map +1 -0
  96. package/dist/formatters/spec-to-docblock.js +48 -0
  97. package/dist/formatters/spec-to-docblock.js.map +1 -0
  98. package/dist/index.d.ts +42 -0
  99. package/dist/index.js +39 -0
  100. package/dist/templates/app-config.d.ts +7 -0
  101. package/dist/templates/app-config.d.ts.map +1 -0
  102. package/dist/templates/app-config.js +107 -0
  103. package/dist/templates/app-config.js.map +1 -0
  104. package/dist/templates/data-view.d.ts +7 -0
  105. package/dist/templates/data-view.d.ts.map +1 -0
  106. package/dist/templates/data-view.js +69 -0
  107. package/dist/templates/data-view.js.map +1 -0
  108. package/dist/templates/event.d.ts +11 -0
  109. package/dist/templates/event.d.ts.map +1 -0
  110. package/dist/templates/event.js +41 -0
  111. package/dist/templates/event.js.map +1 -0
  112. package/dist/templates/experiment.d.ts +7 -0
  113. package/dist/templates/experiment.d.ts.map +1 -0
  114. package/dist/templates/experiment.js +88 -0
  115. package/dist/templates/experiment.js.map +1 -0
  116. package/dist/templates/handler.d.ts +20 -0
  117. package/dist/templates/handler.d.ts.map +1 -0
  118. package/dist/templates/handler.js +96 -0
  119. package/dist/templates/handler.js.map +1 -0
  120. package/dist/templates/integration-utils.js +105 -0
  121. package/dist/templates/integration-utils.js.map +1 -0
  122. package/dist/templates/integration.d.ts +7 -0
  123. package/dist/templates/integration.d.ts.map +1 -0
  124. package/dist/templates/integration.js +62 -0
  125. package/dist/templates/integration.js.map +1 -0
  126. package/dist/templates/knowledge.d.ts +7 -0
  127. package/dist/templates/knowledge.d.ts.map +1 -0
  128. package/dist/templates/knowledge.js +69 -0
  129. package/dist/templates/knowledge.js.map +1 -0
  130. package/dist/templates/migration.d.ts +7 -0
  131. package/dist/templates/migration.d.ts.map +1 -0
  132. package/dist/templates/migration.js +61 -0
  133. package/dist/templates/migration.js.map +1 -0
  134. package/dist/templates/operation.d.ts +11 -0
  135. package/dist/templates/operation.d.ts.map +1 -0
  136. package/dist/templates/operation.js +101 -0
  137. package/dist/templates/operation.js.map +1 -0
  138. package/dist/templates/presentation.d.ts +11 -0
  139. package/dist/templates/presentation.d.ts.map +1 -0
  140. package/dist/templates/presentation.js +79 -0
  141. package/dist/templates/presentation.js.map +1 -0
  142. package/dist/templates/telemetry.d.ts +7 -0
  143. package/dist/templates/telemetry.d.ts.map +1 -0
  144. package/dist/templates/telemetry.js +90 -0
  145. package/dist/templates/telemetry.js.map +1 -0
  146. package/dist/templates/utils.d.ts +27 -0
  147. package/dist/templates/utils.d.ts.map +1 -0
  148. package/dist/templates/utils.js +39 -0
  149. package/dist/templates/utils.js.map +1 -0
  150. package/dist/templates/workflow-runner.d.ts +16 -0
  151. package/dist/templates/workflow-runner.d.ts.map +1 -0
  152. package/dist/templates/workflow-runner.js +49 -0
  153. package/dist/templates/workflow-runner.js.map +1 -0
  154. package/dist/templates/workflow.d.ts +11 -0
  155. package/dist/templates/workflow.d.ts.map +1 -0
  156. package/dist/templates/workflow.js +68 -0
  157. package/dist/templates/workflow.js.map +1 -0
  158. package/dist/types/analysis-types.d.ts +199 -0
  159. package/dist/types/analysis-types.d.ts.map +1 -0
  160. package/dist/types/generation-types.d.ts +87 -0
  161. package/dist/types/generation-types.d.ts.map +1 -0
  162. package/dist/types/generation-types.js +21 -0
  163. package/dist/types/generation-types.js.map +1 -0
  164. package/dist/types/llm-types.d.ts +138 -0
  165. package/dist/types/llm-types.d.ts.map +1 -0
  166. package/dist/types/rulesync-types.d.ts +24 -0
  167. package/dist/types/rulesync-types.d.ts.map +1 -0
  168. package/dist/types/spec-types.d.ts +343 -0
  169. package/dist/types/spec-types.d.ts.map +1 -0
  170. package/package.json +63 -0
@@ -0,0 +1,114 @@
1
+ //#region src/analysis/diff/deep-diff.ts
2
+ /**
3
+ * Compute deep differences between two IO schemas.
4
+ */
5
+ function computeIoDiff(base, head, options = {}) {
6
+ const diffs = [];
7
+ diffs.push(...computeFieldsDiff(base.input, head.input, "io.input", options));
8
+ diffs.push(...computeFieldsDiff(base.output, head.output, "io.output", options));
9
+ return options.breakingOnly ? diffs.filter((d) => d.type === "breaking") : diffs;
10
+ }
11
+ /**
12
+ * Compute differences between two field maps.
13
+ */
14
+ function computeFieldsDiff(baseFields, headFields, pathPrefix, options = {}) {
15
+ const diffs = [];
16
+ const baseNames = new Set(Object.keys(baseFields));
17
+ const headNames = new Set(Object.keys(headFields));
18
+ for (const name of baseNames) if (!headNames.has(name)) {
19
+ const baseField = baseFields[name];
20
+ diffs.push({
21
+ type: "breaking",
22
+ path: `${pathPrefix}.${name}`,
23
+ oldValue: baseField,
24
+ newValue: void 0,
25
+ description: `Field '${name}' was removed`
26
+ });
27
+ }
28
+ for (const name of headNames) if (!baseNames.has(name)) {
29
+ const headField = headFields[name];
30
+ const isBreaking = headField?.required === true;
31
+ diffs.push({
32
+ type: isBreaking ? "breaking" : "added",
33
+ path: `${pathPrefix}.${name}`,
34
+ oldValue: void 0,
35
+ newValue: headField,
36
+ description: isBreaking ? `Required field '${name}' was added` : `Optional field '${name}' was added`
37
+ });
38
+ }
39
+ for (const name of baseNames) if (headNames.has(name)) {
40
+ const baseField = baseFields[name];
41
+ const headField = headFields[name];
42
+ if (baseField && headField) diffs.push(...computeFieldDiff(baseField, headField, `${pathPrefix}.${name}`, options));
43
+ }
44
+ return diffs;
45
+ }
46
+ /**
47
+ * Compute differences between two field definitions.
48
+ */
49
+ function computeFieldDiff(base, head, path, _options = {}) {
50
+ const diffs = [];
51
+ if (base.type !== head.type) diffs.push({
52
+ type: "breaking",
53
+ path: `${path}.type`,
54
+ oldValue: base.type,
55
+ newValue: head.type,
56
+ description: `Field type changed from '${base.type}' to '${head.type}'`
57
+ });
58
+ if (base.required !== head.required) {
59
+ const isBreaking = !base.required && head.required;
60
+ diffs.push({
61
+ type: isBreaking ? "breaking" : "changed",
62
+ path: `${path}.required`,
63
+ oldValue: base.required,
64
+ newValue: head.required,
65
+ description: isBreaking ? `Field '${base.name}' changed from optional to required` : `Field '${base.name}' changed from required to optional`
66
+ });
67
+ }
68
+ if (base.nullable !== head.nullable) {
69
+ const isBreaking = base.nullable && !head.nullable;
70
+ diffs.push({
71
+ type: isBreaking ? "breaking" : "changed",
72
+ path: `${path}.nullable`,
73
+ oldValue: base.nullable,
74
+ newValue: head.nullable,
75
+ description: isBreaking ? `Field '${base.name}' is no longer nullable` : `Field '${base.name}' is now nullable`
76
+ });
77
+ }
78
+ if (base.type === "enum" && head.type === "enum") {
79
+ const baseValues = new Set(base.enumValues ?? []);
80
+ const headValues = new Set(head.enumValues ?? []);
81
+ for (const value of baseValues) if (!headValues.has(value)) diffs.push({
82
+ type: "breaking",
83
+ path: `${path}.enumValues`,
84
+ oldValue: base.enumValues,
85
+ newValue: head.enumValues,
86
+ description: `Enum value '${value}' was removed`
87
+ });
88
+ for (const value of headValues) if (!baseValues.has(value)) diffs.push({
89
+ type: "added",
90
+ path: `${path}.enumValues`,
91
+ oldValue: base.enumValues,
92
+ newValue: head.enumValues,
93
+ description: `Enum value '${value}' was added`
94
+ });
95
+ }
96
+ if (base.type === "object" && head.type === "object" && base.properties && head.properties) diffs.push(...computeFieldsDiff(base.properties, head.properties, path, _options));
97
+ if (base.type === "array" && head.type === "array" && base.items && head.items) diffs.push(...computeFieldDiff(base.items, head.items, `${path}.items`, _options));
98
+ return diffs;
99
+ }
100
+ /**
101
+ * Classify a diff as breaking based on context.
102
+ */
103
+ function isBreakingChange(diff, context) {
104
+ if (context === "output") return diff.type === "breaking" || diff.type === "removed";
105
+ if (context === "input") {
106
+ if (diff.type === "added" && diff.description?.includes("Required field")) return true;
107
+ return diff.type === "breaking";
108
+ }
109
+ return diff.type === "breaking";
110
+ }
111
+
112
+ //#endregion
113
+ export { computeFieldDiff, computeFieldsDiff, computeIoDiff, isBreakingChange };
114
+ //# sourceMappingURL=deep-diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deep-diff.js","names":[],"sources":["../../../src/analysis/diff/deep-diff.ts"],"sourcesContent":["/**\n * Deep diff engine for IO schema comparison.\n *\n * Compares input/output schemas field-by-field to detect\n * breaking and non-breaking changes.\n */\n\nimport type { FieldSnapshot, IoSnapshot } from '../snapshot/types';\nimport type { SemanticDiffItem } from '../../types/analysis-types';\n\n/**\n * Deep diff options.\n */\nexport interface DeepDiffOptions {\n /** Only report breaking changes */\n breakingOnly?: boolean;\n /** Path prefix for nested diffs */\n pathPrefix?: string;\n}\n\n/**\n * Compute deep differences between two IO schemas.\n */\nexport function computeIoDiff(\n base: IoSnapshot,\n head: IoSnapshot,\n options: DeepDiffOptions = {}\n): SemanticDiffItem[] {\n const diffs: SemanticDiffItem[] = [];\n\n // Compare input schemas\n diffs.push(...computeFieldsDiff(base.input, head.input, 'io.input', options));\n\n // Compare output schemas\n diffs.push(\n ...computeFieldsDiff(base.output, head.output, 'io.output', options)\n );\n\n return options.breakingOnly\n ? diffs.filter((d) => d.type === 'breaking')\n : diffs;\n}\n\n/**\n * Compute differences between two field maps.\n */\nexport function computeFieldsDiff(\n baseFields: Record<string, FieldSnapshot>,\n headFields: Record<string, FieldSnapshot>,\n pathPrefix: string,\n options: DeepDiffOptions = {}\n): SemanticDiffItem[] {\n const diffs: SemanticDiffItem[] = [];\n const baseNames = new Set(Object.keys(baseFields));\n const headNames = new Set(Object.keys(headFields));\n\n // Check for removed fields\n for (const name of baseNames) {\n if (!headNames.has(name)) {\n const baseField = baseFields[name];\n diffs.push({\n type: 'breaking',\n path: `${pathPrefix}.${name}`,\n oldValue: baseField,\n newValue: undefined,\n description: `Field '${name}' was removed`,\n });\n }\n }\n\n // Check for added fields\n for (const name of headNames) {\n if (!baseNames.has(name)) {\n const headField = headFields[name];\n const isBreaking = headField?.required === true;\n diffs.push({\n type: isBreaking ? 'breaking' : 'added',\n path: `${pathPrefix}.${name}`,\n oldValue: undefined,\n newValue: headField,\n description: isBreaking\n ? `Required field '${name}' was added`\n : `Optional field '${name}' was added`,\n });\n }\n }\n\n // Check for changed fields\n for (const name of baseNames) {\n if (headNames.has(name)) {\n const baseField = baseFields[name];\n const headField = headFields[name];\n if (baseField && headField) {\n diffs.push(\n ...computeFieldDiff(\n baseField,\n headField,\n `${pathPrefix}.${name}`,\n options\n )\n );\n }\n }\n }\n\n return diffs;\n}\n\n/**\n * Compute differences between two field definitions.\n */\nexport function computeFieldDiff(\n base: FieldSnapshot,\n head: FieldSnapshot,\n path: string,\n _options: DeepDiffOptions = {}\n): SemanticDiffItem[] {\n const diffs: SemanticDiffItem[] = [];\n\n // Type change is always breaking\n if (base.type !== head.type) {\n diffs.push({\n type: 'breaking',\n path: `${path}.type`,\n oldValue: base.type,\n newValue: head.type,\n description: `Field type changed from '${base.type}' to '${head.type}'`,\n });\n }\n\n // Required change\n if (base.required !== head.required) {\n const isBreaking = !base.required && head.required; // Optional -> Required is breaking\n diffs.push({\n type: isBreaking ? 'breaking' : 'changed',\n path: `${path}.required`,\n oldValue: base.required,\n newValue: head.required,\n description: isBreaking\n ? `Field '${base.name}' changed from optional to required`\n : `Field '${base.name}' changed from required to optional`,\n });\n }\n\n // Nullable change\n if (base.nullable !== head.nullable) {\n const isBreaking = base.nullable && !head.nullable; // Nullable -> Non-nullable is breaking\n diffs.push({\n type: isBreaking ? 'breaking' : 'changed',\n path: `${path}.nullable`,\n oldValue: base.nullable,\n newValue: head.nullable,\n description: isBreaking\n ? `Field '${base.name}' is no longer nullable`\n : `Field '${base.name}' is now nullable`,\n });\n }\n\n // Enum values change\n if (base.type === 'enum' && head.type === 'enum') {\n const baseValues = new Set(base.enumValues ?? []);\n const headValues = new Set(head.enumValues ?? []);\n\n // Removed enum values are breaking\n for (const value of baseValues) {\n if (!headValues.has(value)) {\n diffs.push({\n type: 'breaking',\n path: `${path}.enumValues`,\n oldValue: base.enumValues,\n newValue: head.enumValues,\n description: `Enum value '${value}' was removed`,\n });\n }\n }\n\n // Added enum values are non-breaking\n for (const value of headValues) {\n if (!baseValues.has(value)) {\n diffs.push({\n type: 'added',\n path: `${path}.enumValues`,\n oldValue: base.enumValues,\n newValue: head.enumValues,\n description: `Enum value '${value}' was added`,\n });\n }\n }\n }\n\n // Nested object fields\n if (\n base.type === 'object' &&\n head.type === 'object' &&\n base.properties &&\n head.properties\n ) {\n diffs.push(\n ...computeFieldsDiff(base.properties, head.properties, path, _options)\n );\n }\n\n // Array items\n if (\n base.type === 'array' &&\n head.type === 'array' &&\n base.items &&\n head.items\n ) {\n diffs.push(\n ...computeFieldDiff(base.items, head.items, `${path}.items`, _options)\n );\n }\n\n return diffs;\n}\n\n/**\n * Classify a diff as breaking based on context.\n */\nexport function isBreakingChange(\n diff: SemanticDiffItem,\n context: 'input' | 'output'\n): boolean {\n // In output context, removing/changing fields is always breaking\n if (context === 'output') {\n return diff.type === 'breaking' || diff.type === 'removed';\n }\n\n // In input context, adding required fields is breaking\n if (context === 'input') {\n if (diff.type === 'added' && diff.description?.includes('Required field')) {\n return true;\n }\n return diff.type === 'breaking';\n }\n\n return diff.type === 'breaking';\n}\n"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { SemanticDiffItem, SemanticDiffOptions } from "../../types/analysis-types.js";
2
+
3
+ //#region src/analysis/diff/semantic.d.ts
4
+
5
+ /**
6
+ * Compute semantic differences between two spec sources.
7
+ */
8
+ declare function computeSemanticDiff(aCode: string, aPath: string, bCode: string, bPath: string, options?: SemanticDiffOptions): SemanticDiffItem[];
9
+ //#endregion
10
+ export { computeSemanticDiff };
11
+ //# sourceMappingURL=semantic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic.d.ts","names":[],"sources":["../../../src/analysis/diff/semantic.ts"],"sourcesContent":[],"mappings":";;;;;;;iBAegB,mBAAA,uEAKL,sBACR"}
@@ -0,0 +1,97 @@
1
+ import { scanSpecSource } from "../spec-scan.js";
2
+
3
+ //#region src/analysis/diff/semantic.ts
4
+ /**
5
+ * Compute semantic differences between two spec sources.
6
+ */
7
+ function computeSemanticDiff(aCode, aPath, bCode, bPath, options = {}) {
8
+ const a = scanSpecSource(aCode, aPath);
9
+ const b = scanSpecSource(bCode, bPath);
10
+ const diffs = [];
11
+ compareScalar(diffs, "specType", a.specType, b.specType, {
12
+ breaking: true,
13
+ label: "Spec type"
14
+ });
15
+ compareScalar(diffs, "key", a.key, b.key, {
16
+ breaking: true,
17
+ label: "Key"
18
+ });
19
+ compareScalar(diffs, "version", a.version, b.version, {
20
+ breaking: true,
21
+ label: "Version"
22
+ });
23
+ compareScalar(diffs, "kind", a.kind, b.kind, {
24
+ breaking: true,
25
+ label: "Kind"
26
+ });
27
+ compareScalar(diffs, "stability", a.stability, b.stability, {
28
+ breaking: isStabilityDowngrade(a, b),
29
+ label: "Stability"
30
+ });
31
+ compareArray(diffs, "owners", a.owners ?? [], b.owners ?? [], { label: "Owners" });
32
+ compareArray(diffs, "tags", a.tags ?? [], b.tags ?? [], { label: "Tags" });
33
+ compareStructuralHints(diffs, a, b);
34
+ return options.breakingOnly ? diffs.filter((d) => d.type === "breaking") : diffs;
35
+ }
36
+ function compareScalar(diffs, path, a, b, config) {
37
+ if (a === b) return;
38
+ diffs.push({
39
+ type: config.breaking ? "breaking" : "changed",
40
+ path,
41
+ oldValue: a,
42
+ newValue: b,
43
+ description: `${config.label} changed`
44
+ });
45
+ }
46
+ function compareArray(diffs, path, a, b, config) {
47
+ const aSorted = [...a].sort();
48
+ const bSorted = [...b].sort();
49
+ if (JSON.stringify(aSorted) === JSON.stringify(bSorted)) return;
50
+ diffs.push({
51
+ type: "changed",
52
+ path,
53
+ oldValue: aSorted,
54
+ newValue: bSorted,
55
+ description: `${config.label} changed`
56
+ });
57
+ }
58
+ function isStabilityDowngrade(a, b) {
59
+ const order = {
60
+ experimental: 0,
61
+ beta: 1,
62
+ stable: 2,
63
+ deprecated: 3
64
+ };
65
+ const aValue = a.stability ? order[a.stability] ?? 0 : 0;
66
+ return (b.stability ? order[b.stability] ?? 0 : 0) > aValue;
67
+ }
68
+ function compareStructuralHints(diffs, a, b) {
69
+ compareScalar(diffs, "hasMeta", a.hasMeta, b.hasMeta, {
70
+ breaking: a.specType === "operation" || b.specType === "operation",
71
+ label: "meta section presence"
72
+ });
73
+ compareScalar(diffs, "hasIo", a.hasIo, b.hasIo, {
74
+ breaking: a.specType === "operation" || b.specType === "operation",
75
+ label: "io section presence"
76
+ });
77
+ compareScalar(diffs, "hasPolicy", a.hasPolicy, b.hasPolicy, {
78
+ breaking: a.specType === "operation" || b.specType === "operation",
79
+ label: "policy section presence"
80
+ });
81
+ compareScalar(diffs, "hasPayload", a.hasPayload, b.hasPayload, {
82
+ breaking: a.specType === "event" || b.specType === "event",
83
+ label: "payload section presence"
84
+ });
85
+ compareScalar(diffs, "hasContent", a.hasContent, b.hasContent, {
86
+ breaking: a.specType === "presentation" || b.specType === "presentation",
87
+ label: "content section presence"
88
+ });
89
+ compareScalar(diffs, "hasDefinition", a.hasDefinition, b.hasDefinition, {
90
+ breaking: a.specType === "workflow" || b.specType === "workflow",
91
+ label: "definition section presence"
92
+ });
93
+ }
94
+
95
+ //#endregion
96
+ export { computeSemanticDiff };
97
+ //# sourceMappingURL=semantic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semantic.js","names":[],"sources":["../../../src/analysis/diff/semantic.ts"],"sourcesContent":["/**\n * Semantic diff computation for contract specs.\n * Extracted from cli-contractspec/src/commands/diff/semantic.ts\n */\n\nimport type {\n SemanticDiffItem,\n SemanticDiffOptions,\n SpecScanResult,\n} from '../../types/analysis-types';\nimport { scanSpecSource } from '../spec-scan';\n\n/**\n * Compute semantic differences between two spec sources.\n */\nexport function computeSemanticDiff(\n aCode: string,\n aPath: string,\n bCode: string,\n bPath: string,\n options: SemanticDiffOptions = {}\n): SemanticDiffItem[] {\n const a = scanSpecSource(aCode, aPath);\n const b = scanSpecSource(bCode, bPath);\n\n const diffs: SemanticDiffItem[] = [];\n\n compareScalar(diffs, 'specType', a.specType, b.specType, {\n breaking: true,\n label: 'Spec type',\n });\n\n compareScalar(diffs, 'key', a.key, b.key, {\n breaking: true,\n label: 'Key',\n });\n compareScalar(diffs, 'version', a.version, b.version, {\n breaking: true,\n label: 'Version',\n });\n compareScalar(diffs, 'kind', a.kind, b.kind, {\n breaking: true,\n label: 'Kind',\n });\n\n compareScalar(diffs, 'stability', a.stability, b.stability, {\n breaking: isStabilityDowngrade(a, b),\n label: 'Stability',\n });\n\n compareArray(diffs, 'owners', a.owners ?? [], b.owners ?? [], {\n label: 'Owners',\n });\n compareArray(diffs, 'tags', a.tags ?? [], b.tags ?? [], { label: 'Tags' });\n\n compareStructuralHints(diffs, a, b);\n\n const filtered = options.breakingOnly\n ? diffs.filter((d) => d.type === 'breaking')\n : diffs;\n\n return filtered;\n}\n\nfunction compareScalar(\n diffs: SemanticDiffItem[],\n path: string,\n a: unknown,\n b: unknown,\n config: { breaking: boolean; label: string }\n) {\n if (a === b) return;\n diffs.push({\n type: config.breaking ? 'breaking' : 'changed',\n path,\n oldValue: a,\n newValue: b,\n description: `${config.label} changed`,\n });\n}\n\nfunction compareArray(\n diffs: SemanticDiffItem[],\n path: string,\n a: string[],\n b: string[],\n config: { label: string }\n) {\n const aSorted = [...a].sort();\n const bSorted = [...b].sort();\n if (JSON.stringify(aSorted) === JSON.stringify(bSorted)) return;\n\n diffs.push({\n type: 'changed',\n path,\n oldValue: aSorted,\n newValue: bSorted,\n description: `${config.label} changed`,\n });\n}\n\nfunction isStabilityDowngrade(a: SpecScanResult, b: SpecScanResult): boolean {\n const order: Record<string, number> = {\n experimental: 0,\n beta: 1,\n stable: 2,\n deprecated: 3,\n };\n const aValue = a.stability ? (order[a.stability] ?? 0) : 0;\n const bValue = b.stability ? (order[b.stability] ?? 0) : 0;\n // Moving toward deprecated is effectively a breaking signal for consumers.\n return bValue > aValue;\n}\n\nfunction compareStructuralHints(\n diffs: SemanticDiffItem[],\n a: SpecScanResult,\n b: SpecScanResult\n) {\n // For operations these sections are usually required; missing them is breaking.\n compareScalar(diffs, 'hasMeta', a.hasMeta, b.hasMeta, {\n breaking: a.specType === 'operation' || b.specType === 'operation',\n label: 'meta section presence',\n });\n compareScalar(diffs, 'hasIo', a.hasIo, b.hasIo, {\n breaking: a.specType === 'operation' || b.specType === 'operation',\n label: 'io section presence',\n });\n compareScalar(diffs, 'hasPolicy', a.hasPolicy, b.hasPolicy, {\n breaking: a.specType === 'operation' || b.specType === 'operation',\n label: 'policy section presence',\n });\n compareScalar(diffs, 'hasPayload', a.hasPayload, b.hasPayload, {\n breaking: a.specType === 'event' || b.specType === 'event',\n label: 'payload section presence',\n });\n compareScalar(diffs, 'hasContent', a.hasContent, b.hasContent, {\n breaking: a.specType === 'presentation' || b.specType === 'presentation',\n label: 'content section presence',\n });\n compareScalar(diffs, 'hasDefinition', a.hasDefinition, b.hasDefinition, {\n breaking: a.specType === 'workflow' || b.specType === 'workflow',\n label: 'definition section presence',\n });\n}\n"],"mappings":";;;;;;AAeA,SAAgB,oBACd,OACA,OACA,OACA,OACA,UAA+B,EAAE,EACb;CACpB,MAAM,IAAI,eAAe,OAAO,MAAM;CACtC,MAAM,IAAI,eAAe,OAAO,MAAM;CAEtC,MAAM,QAA4B,EAAE;AAEpC,eAAc,OAAO,YAAY,EAAE,UAAU,EAAE,UAAU;EACvD,UAAU;EACV,OAAO;EACR,CAAC;AAEF,eAAc,OAAO,OAAO,EAAE,KAAK,EAAE,KAAK;EACxC,UAAU;EACV,OAAO;EACR,CAAC;AACF,eAAc,OAAO,WAAW,EAAE,SAAS,EAAE,SAAS;EACpD,UAAU;EACV,OAAO;EACR,CAAC;AACF,eAAc,OAAO,QAAQ,EAAE,MAAM,EAAE,MAAM;EAC3C,UAAU;EACV,OAAO;EACR,CAAC;AAEF,eAAc,OAAO,aAAa,EAAE,WAAW,EAAE,WAAW;EAC1D,UAAU,qBAAqB,GAAG,EAAE;EACpC,OAAO;EACR,CAAC;AAEF,cAAa,OAAO,UAAU,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAC5D,OAAO,UACR,CAAC;AACF,cAAa,OAAO,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,QAAQ,CAAC;AAE1E,wBAAuB,OAAO,GAAG,EAAE;AAMnC,QAJiB,QAAQ,eACrB,MAAM,QAAQ,MAAM,EAAE,SAAS,WAAW,GAC1C;;AAKN,SAAS,cACP,OACA,MACA,GACA,GACA,QACA;AACA,KAAI,MAAM,EAAG;AACb,OAAM,KAAK;EACT,MAAM,OAAO,WAAW,aAAa;EACrC;EACA,UAAU;EACV,UAAU;EACV,aAAa,GAAG,OAAO,MAAM;EAC9B,CAAC;;AAGJ,SAAS,aACP,OACA,MACA,GACA,GACA,QACA;CACA,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM;CAC7B,MAAM,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM;AAC7B,KAAI,KAAK,UAAU,QAAQ,KAAK,KAAK,UAAU,QAAQ,CAAE;AAEzD,OAAM,KAAK;EACT,MAAM;EACN;EACA,UAAU;EACV,UAAU;EACV,aAAa,GAAG,OAAO,MAAM;EAC9B,CAAC;;AAGJ,SAAS,qBAAqB,GAAmB,GAA4B;CAC3E,MAAM,QAAgC;EACpC,cAAc;EACd,MAAM;EACN,QAAQ;EACR,YAAY;EACb;CACD,MAAM,SAAS,EAAE,YAAa,MAAM,EAAE,cAAc,IAAK;AAGzD,SAFe,EAAE,YAAa,MAAM,EAAE,cAAc,IAAK,KAEzC;;AAGlB,SAAS,uBACP,OACA,GACA,GACA;AAEA,eAAc,OAAO,WAAW,EAAE,SAAS,EAAE,SAAS;EACpD,UAAU,EAAE,aAAa,eAAe,EAAE,aAAa;EACvD,OAAO;EACR,CAAC;AACF,eAAc,OAAO,SAAS,EAAE,OAAO,EAAE,OAAO;EAC9C,UAAU,EAAE,aAAa,eAAe,EAAE,aAAa;EACvD,OAAO;EACR,CAAC;AACF,eAAc,OAAO,aAAa,EAAE,WAAW,EAAE,WAAW;EAC1D,UAAU,EAAE,aAAa,eAAe,EAAE,aAAa;EACvD,OAAO;EACR,CAAC;AACF,eAAc,OAAO,cAAc,EAAE,YAAY,EAAE,YAAY;EAC7D,UAAU,EAAE,aAAa,WAAW,EAAE,aAAa;EACnD,OAAO;EACR,CAAC;AACF,eAAc,OAAO,cAAc,EAAE,YAAY,EAAE,YAAY;EAC7D,UAAU,EAAE,aAAa,kBAAkB,EAAE,aAAa;EAC1D,OAAO;EACR,CAAC;AACF,eAAc,OAAO,iBAAiB,EAAE,eAAe,EAAE,eAAe;EACtE,UAAU,EAAE,aAAa,cAAc,EAAE,aAAa;EACtD,OAAO;EACR,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { ExampleScanResult } from "../types/analysis-types.js";
2
+
3
+ //#region src/analysis/example-scan.d.ts
4
+
5
+ /**
6
+ * Check if a file is an example file based on naming conventions.
7
+ */
8
+ declare function isExampleFile(filePath: string): boolean;
9
+ /**
10
+ * Scan an example source file to extract metadata.
11
+ */
12
+ declare function scanExampleSource(code: string, filePath: string): ExampleScanResult;
13
+ //#endregion
14
+ export { isExampleFile, scanExampleSource };
15
+ //# sourceMappingURL=example-scan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example-scan.d.ts","names":[],"sources":["../../src/analysis/example-scan.ts"],"sourcesContent":[],"mappings":";;;;;;AAWA;AAOgB,iBAPA,aAAA,CAUb,QAAA,EAAA,MAAA,CAAiB,EAAA,OAAA;;;;iBAHJ,iBAAA,kCAGb"}
@@ -0,0 +1,116 @@
1
+ import { isStability, matchStringArrayField, matchStringField, matchStringFieldIn } from "./utils/matchers.js";
2
+
3
+ //#region src/analysis/example-scan.ts
4
+ /**
5
+ * Check if a file is an example file based on naming conventions.
6
+ */
7
+ function isExampleFile(filePath) {
8
+ return filePath.includes("/example.") || filePath.endsWith(".example.ts");
9
+ }
10
+ /**
11
+ * Scan an example source file to extract metadata.
12
+ */
13
+ function scanExampleSource(code, filePath) {
14
+ const key = matchStringField(code, "key") ?? extractKeyFromFilePath(filePath);
15
+ const version = matchStringField(code, "version") ?? void 0;
16
+ const title = matchStringField(code, "title") ?? void 0;
17
+ const description = matchStringField(code, "description") ?? void 0;
18
+ const summary = matchStringField(code, "summary") ?? void 0;
19
+ const kind = matchStringField(code, "kind") ?? void 0;
20
+ const visibility = matchStringField(code, "visibility") ?? void 0;
21
+ const domain = matchStringField(code, "domain") ?? void 0;
22
+ const stabilityRaw = matchStringField(code, "stability");
23
+ return {
24
+ filePath,
25
+ key,
26
+ version,
27
+ title,
28
+ description,
29
+ summary,
30
+ kind,
31
+ visibility,
32
+ domain,
33
+ stability: isStability(stabilityRaw) ? stabilityRaw : void 0,
34
+ owners: matchStringArrayField(code, "owners"),
35
+ tags: matchStringArrayField(code, "tags"),
36
+ docs: extractDocs(code),
37
+ surfaces: extractSurfaces(code),
38
+ entrypoints: extractEntrypoints(code)
39
+ };
40
+ }
41
+ /**
42
+ * Extract docs section from source code.
43
+ */
44
+ function extractDocs(code) {
45
+ const docsMatch = code.match(/docs\s*:\s*\{([\s\S]*?)\}/);
46
+ if (!docsMatch?.[1]) return void 0;
47
+ const docsContent = docsMatch[1];
48
+ return {
49
+ rootDocId: matchStringFieldIn(docsContent, "rootDocId") ?? void 0,
50
+ goalDocId: matchStringFieldIn(docsContent, "goalDocId") ?? void 0,
51
+ usageDocId: matchStringFieldIn(docsContent, "usageDocId") ?? void 0
52
+ };
53
+ }
54
+ /**
55
+ * Extract surfaces section from source code.
56
+ */
57
+ function extractSurfaces(code) {
58
+ const surfaces = {
59
+ templates: false,
60
+ sandbox: {
61
+ enabled: false,
62
+ modes: []
63
+ },
64
+ studio: {
65
+ enabled: false,
66
+ installable: false
67
+ },
68
+ mcp: { enabled: false }
69
+ };
70
+ surfaces.templates = /surfaces\s*:\s*\{[\s\S]*?templates\s*:\s*true/.test(code);
71
+ const sandboxMatch = code.match(/sandbox\s*:\s*\{\s*enabled\s*:\s*(true|false)\s*,\s*modes\s*:\s*\[([^\]]*)\]/);
72
+ if (sandboxMatch) {
73
+ surfaces.sandbox.enabled = sandboxMatch[1] === "true";
74
+ if (sandboxMatch[2]) surfaces.sandbox.modes = Array.from(sandboxMatch[2].matchAll(/['"]([^'"]+)['"]/g)).map((m) => m[1]).filter((v) => typeof v === "string");
75
+ }
76
+ const studioMatch = code.match(/studio\s*:\s*\{\s*enabled\s*:\s*(true|false)\s*,\s*installable\s*:\s*(true|false)/);
77
+ if (studioMatch) {
78
+ surfaces.studio.enabled = studioMatch[1] === "true";
79
+ surfaces.studio.installable = studioMatch[2] === "true";
80
+ }
81
+ const mcpMatch = code.match(/mcp\s*:\s*\{\s*enabled\s*:\s*(true|false)/);
82
+ if (mcpMatch) surfaces.mcp.enabled = mcpMatch[1] === "true";
83
+ return surfaces;
84
+ }
85
+ /**
86
+ * Extract entrypoints section from source code.
87
+ */
88
+ function extractEntrypoints(code) {
89
+ const entrypoints = { packageName: "" };
90
+ const entrypointsMatch = code.match(/entrypoints\s*:\s*\{([\s\S]*?)\}(?=\s*[,}])/);
91
+ if (!entrypointsMatch?.[1]) return entrypoints;
92
+ const content = entrypointsMatch[1];
93
+ entrypoints.packageName = matchStringFieldIn(content, "packageName") ?? "unknown";
94
+ entrypoints.feature = matchStringFieldIn(content, "feature") ?? void 0;
95
+ entrypoints.blueprint = matchStringFieldIn(content, "blueprint") ?? void 0;
96
+ entrypoints.contracts = matchStringFieldIn(content, "contracts") ?? void 0;
97
+ entrypoints.presentations = matchStringFieldIn(content, "presentations") ?? void 0;
98
+ entrypoints.handlers = matchStringFieldIn(content, "handlers") ?? void 0;
99
+ entrypoints.ui = matchStringFieldIn(content, "ui") ?? void 0;
100
+ entrypoints.docs = matchStringFieldIn(content, "docs") ?? void 0;
101
+ return entrypoints;
102
+ }
103
+ /**
104
+ * Extract key from file path as fallback.
105
+ */
106
+ function extractKeyFromFilePath(filePath) {
107
+ const parts = filePath.split("/");
108
+ const examplesIndex = parts.findIndex((p) => p === "examples");
109
+ const exampleName = parts[examplesIndex + 1];
110
+ if (examplesIndex !== -1 && exampleName !== void 0) return exampleName;
111
+ return (parts.pop() ?? filePath).replace(/\.example\.[jt]s$/, "").replace(/[^a-zA-Z0-9-]/g, "-");
112
+ }
113
+
114
+ //#endregion
115
+ export { isExampleFile, scanExampleSource };
116
+ //# sourceMappingURL=example-scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example-scan.js","names":[],"sources":["../../src/analysis/example-scan.ts"],"sourcesContent":["import {\n isStability,\n matchStringArrayField,\n matchStringField,\n matchStringFieldIn,\n} from './utils/matchers';\nimport type { ExampleScanResult } from '../types/analysis-types';\n\n/**\n * Check if a file is an example file based on naming conventions.\n */\nexport function isExampleFile(filePath: string): boolean {\n return filePath.includes('/example.') || filePath.endsWith('.example.ts');\n}\n\n/**\n * Scan an example source file to extract metadata.\n */\nexport function scanExampleSource(\n code: string,\n filePath: string\n): ExampleScanResult {\n const key = matchStringField(code, 'key') ?? extractKeyFromFilePath(filePath);\n const versionRaw = matchStringField(code, 'version');\n const version = versionRaw ?? undefined;\n const title = matchStringField(code, 'title') ?? undefined;\n const description = matchStringField(code, 'description') ?? undefined;\n const summary = matchStringField(code, 'summary') ?? undefined;\n const kind = matchStringField(code, 'kind') ?? undefined;\n const visibility = matchStringField(code, 'visibility') ?? undefined;\n const domain = matchStringField(code, 'domain') ?? undefined;\n const stabilityRaw = matchStringField(code, 'stability');\n const stability = isStability(stabilityRaw) ? stabilityRaw : undefined;\n const owners = matchStringArrayField(code, 'owners');\n const tags = matchStringArrayField(code, 'tags');\n\n // Extract docs\n const docs = extractDocs(code);\n\n // Extract surfaces\n const surfaces = extractSurfaces(code);\n\n // Extract entrypoints\n const entrypoints = extractEntrypoints(code);\n\n return {\n filePath,\n key,\n version,\n title,\n description,\n summary,\n kind,\n visibility,\n domain,\n stability,\n owners,\n tags,\n docs,\n surfaces,\n entrypoints,\n };\n}\n\n/**\n * Extract docs section from source code.\n */\nfunction extractDocs(code: string): ExampleScanResult['docs'] {\n const docsMatch = code.match(/docs\\s*:\\s*\\{([\\s\\S]*?)\\}/);\n if (!docsMatch?.[1]) return undefined;\n\n const docsContent = docsMatch[1];\n return {\n rootDocId: matchStringFieldIn(docsContent, 'rootDocId') ?? undefined,\n goalDocId: matchStringFieldIn(docsContent, 'goalDocId') ?? undefined,\n usageDocId: matchStringFieldIn(docsContent, 'usageDocId') ?? undefined,\n };\n}\n\n/**\n * Extract surfaces section from source code.\n */\nfunction extractSurfaces(code: string): ExampleScanResult['surfaces'] {\n const surfaces: ExampleScanResult['surfaces'] = {\n templates: false,\n sandbox: { enabled: false, modes: [] },\n studio: { enabled: false, installable: false },\n mcp: { enabled: false },\n };\n\n // Check templates directly in the surfaces section\n surfaces.templates = /surfaces\\s*:\\s*\\{[\\s\\S]*?templates\\s*:\\s*true/.test(\n code\n );\n\n // Check sandbox - look for the sandbox object and extract its content\n const sandboxMatch = code.match(\n /sandbox\\s*:\\s*\\{\\s*enabled\\s*:\\s*(true|false)\\s*,\\s*modes\\s*:\\s*\\[([^\\]]*)\\]/\n );\n if (sandboxMatch) {\n surfaces.sandbox.enabled = sandboxMatch[1] === 'true';\n if (sandboxMatch[2]) {\n surfaces.sandbox.modes = Array.from(\n sandboxMatch[2].matchAll(/['\"]([^'\"]+)['\"]/g)\n )\n .map((m) => m[1])\n .filter((v): v is string => typeof v === 'string');\n }\n }\n\n // Check studio - look for studio object\n const studioMatch = code.match(\n /studio\\s*:\\s*\\{\\s*enabled\\s*:\\s*(true|false)\\s*,\\s*installable\\s*:\\s*(true|false)/\n );\n if (studioMatch) {\n surfaces.studio.enabled = studioMatch[1] === 'true';\n surfaces.studio.installable = studioMatch[2] === 'true';\n }\n\n // Check mcp\n const mcpMatch = code.match(/mcp\\s*:\\s*\\{\\s*enabled\\s*:\\s*(true|false)/);\n if (mcpMatch) {\n surfaces.mcp.enabled = mcpMatch[1] === 'true';\n }\n\n return surfaces;\n}\n\n/**\n * Extract entrypoints section from source code.\n */\nfunction extractEntrypoints(code: string): ExampleScanResult['entrypoints'] {\n const entrypoints: ExampleScanResult['entrypoints'] = {\n packageName: '',\n };\n\n const entrypointsMatch = code.match(\n /entrypoints\\s*:\\s*\\{([\\s\\S]*?)\\}(?=\\s*[,}])/\n );\n if (!entrypointsMatch?.[1]) return entrypoints;\n\n const content = entrypointsMatch[1];\n\n entrypoints.packageName =\n matchStringFieldIn(content, 'packageName') ?? 'unknown';\n entrypoints.feature = matchStringFieldIn(content, 'feature') ?? undefined;\n entrypoints.blueprint = matchStringFieldIn(content, 'blueprint') ?? undefined;\n entrypoints.contracts = matchStringFieldIn(content, 'contracts') ?? undefined;\n entrypoints.presentations =\n matchStringFieldIn(content, 'presentations') ?? undefined;\n entrypoints.handlers = matchStringFieldIn(content, 'handlers') ?? undefined;\n entrypoints.ui = matchStringFieldIn(content, 'ui') ?? undefined;\n entrypoints.docs = matchStringFieldIn(content, 'docs') ?? undefined;\n\n return entrypoints;\n}\n\n/**\n * Extract key from file path as fallback.\n */\nfunction extractKeyFromFilePath(filePath: string): string {\n // Try to get package name from path\n const parts = filePath.split('/');\n const examplesIndex = parts.findIndex((p) => p === 'examples');\n const exampleName = parts[examplesIndex + 1];\n if (examplesIndex !== -1 && exampleName !== undefined) {\n return exampleName;\n }\n // Fallback to filename\n const fileName = parts.pop() ?? filePath;\n return fileName\n .replace(/\\.example\\.[jt]s$/, '')\n .replace(/[^a-zA-Z0-9-]/g, '-');\n}\n"],"mappings":"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"}
@@ -0,0 +1,203 @@
1
+ import { Node, Project, SyntaxKind } from "ts-morph";
2
+
3
+ //#region src/analysis/feature-extractor.ts
4
+ /**
5
+ * Extract specs referenced in a feature.
6
+ * Uses ts-morph for robust parsing of object structures.
7
+ */
8
+ function extractFeatureRefs(code) {
9
+ const result = {
10
+ operations: [],
11
+ events: [],
12
+ presentations: [],
13
+ experiments: [],
14
+ capabilities: {
15
+ provides: [],
16
+ requires: []
17
+ },
18
+ opToPresentationLinks: [],
19
+ presentationsTargets: []
20
+ };
21
+ const sourceFile = new Project({ useInMemoryFileSystem: true }).createSourceFile("feature.ts", code);
22
+ const extractRefs = (obj, propName, allowKeyOnly = false) => {
23
+ const refs = [];
24
+ const prop = obj.getProperty(propName);
25
+ if (prop && Node.isPropertyAssignment(prop)) {
26
+ const init = prop.getInitializer();
27
+ if (init && Node.isArrayLiteralExpression(init)) {
28
+ for (const elem of init.getElements()) if (Node.isObjectLiteralExpression(elem)) {
29
+ const keyText = getTextFromProp(elem, "key");
30
+ const verText = getTextFromProp(elem, "version");
31
+ if (keyText && verText) refs.push({
32
+ key: keyText,
33
+ version: verText
34
+ });
35
+ else if (keyText && allowKeyOnly) refs.push({
36
+ key: keyText,
37
+ version: "1.0.0"
38
+ });
39
+ }
40
+ }
41
+ }
42
+ return refs;
43
+ };
44
+ const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind.CallExpression);
45
+ for (const call of callExpressions) if ([
46
+ "defineFeature",
47
+ "defineAppConfig",
48
+ "defineAppBlueprint"
49
+ ].includes(call.getExpression().getText())) {
50
+ const args = call.getArguments();
51
+ if (args.length > 0 && Node.isObjectLiteralExpression(args[0])) {
52
+ const obj = args[0];
53
+ result.operations.push(...extractRefs(obj, "operations"));
54
+ result.events.push(...extractRefs(obj, "events"));
55
+ result.presentations.push(...extractRefs(obj, "presentations"));
56
+ result.experiments.push(...extractRefs(obj, "experiments"));
57
+ const capsProp = obj.getProperty("capabilities");
58
+ if (capsProp && Node.isPropertyAssignment(capsProp)) {
59
+ const capsObj = capsProp.getInitializer();
60
+ if (capsObj && Node.isObjectLiteralExpression(capsObj)) {
61
+ result.capabilities.provides.push(...extractRefs(capsObj, "provides"));
62
+ result.capabilities.requires.push(...extractRefs(capsObj, "requires", true));
63
+ }
64
+ }
65
+ const linksProp = obj.getProperty("opToPresentation");
66
+ if (linksProp && Node.isPropertyAssignment(linksProp)) {
67
+ const linksArr = linksProp.getInitializer();
68
+ if (linksArr && Node.isArrayLiteralExpression(linksArr)) linksArr.getElements().forEach((link) => {
69
+ if (Node.isObjectLiteralExpression(link)) {
70
+ const opProp = link.getProperty("op");
71
+ const presProp = link.getProperty("pres");
72
+ let opRef;
73
+ let presRef;
74
+ if (opProp && Node.isPropertyAssignment(opProp)) {
75
+ const val = opProp.getInitializer();
76
+ if (val && Node.isObjectLiteralExpression(val)) {
77
+ const key = getTextFromProp(val, "key");
78
+ const version = getTextFromProp(val, "version");
79
+ if (key && version) opRef = {
80
+ key,
81
+ version
82
+ };
83
+ }
84
+ }
85
+ if (presProp && Node.isPropertyAssignment(presProp)) {
86
+ const val = presProp.getInitializer();
87
+ if (val && Node.isObjectLiteralExpression(val)) {
88
+ const key = getTextFromProp(val, "key");
89
+ const version = getTextFromProp(val, "version");
90
+ if (key && version) presRef = {
91
+ key,
92
+ version
93
+ };
94
+ }
95
+ }
96
+ if (opRef && presRef) result.opToPresentationLinks.push({
97
+ op: opRef,
98
+ pres: presRef
99
+ });
100
+ }
101
+ });
102
+ }
103
+ extractLinks(obj, result.opToPresentationLinks);
104
+ const targetsProp = obj.getProperty("presentationsTargets");
105
+ if (targetsProp && Node.isPropertyAssignment(targetsProp)) {
106
+ const targetsArr = targetsProp.getInitializer();
107
+ if (targetsArr && Node.isArrayLiteralExpression(targetsArr)) targetsArr.getElements().forEach((targetBlock) => {
108
+ if (Node.isObjectLiteralExpression(targetBlock)) {
109
+ const key = getTextFromProp(targetBlock, "key");
110
+ const version = getTextFromProp(targetBlock, "version");
111
+ const targetsList = getTargetsList(targetBlock, "targets");
112
+ if (key && version && targetsList) result.presentationsTargets.push({
113
+ key,
114
+ version,
115
+ targets: targetsList
116
+ });
117
+ }
118
+ });
119
+ }
120
+ extractPresentationTargets(obj, result.presentationsTargets);
121
+ }
122
+ }
123
+ return result;
124
+ }
125
+ function extractLinks(obj, opToPresentationLinks) {
126
+ const interactionsProp = obj.getProperty("interactions");
127
+ if (interactionsProp && Node.isPropertyAssignment(interactionsProp)) {
128
+ const interactionsArr = interactionsProp.getInitializer();
129
+ if (interactionsArr && Node.isArrayLiteralExpression(interactionsArr)) interactionsArr.getElements().forEach((interaction) => {
130
+ if (Node.isObjectLiteralExpression(interaction)) {
131
+ const triggerProp = interaction.getProperty("trigger");
132
+ const triggerObj = triggerProp && Node.isPropertyAssignment(triggerProp) && triggerProp.getInitializer();
133
+ const actionProp = interaction.getProperty("action");
134
+ const actionObj = actionProp && Node.isPropertyAssignment(actionProp) && actionProp.getInitializer();
135
+ if (triggerObj && Node.isObjectLiteralExpression(triggerObj) && actionObj && Node.isObjectLiteralExpression(actionObj)) {
136
+ const triggerType = getTextFromProp(triggerObj, "type");
137
+ const actionType = getTextFromProp(actionObj, "type");
138
+ if (triggerType === "presentation" && actionType === "operation") {
139
+ const presRef = extractNestedRef(triggerObj, "presentation");
140
+ const opRef = extractNestedRef(actionObj, "operation");
141
+ if (presRef && opRef) opToPresentationLinks.push({
142
+ op: opRef,
143
+ pres: presRef
144
+ });
145
+ }
146
+ }
147
+ }
148
+ });
149
+ }
150
+ }
151
+ function extractNestedRef(obj, propName) {
152
+ const prop = obj.getProperty(propName);
153
+ if (prop && Node.isPropertyAssignment(prop)) {
154
+ const val = prop.getInitializer();
155
+ if (val && Node.isObjectLiteralExpression(val)) {
156
+ const key = getTextFromProp(val, "key");
157
+ const version = getTextFromProp(val, "version");
158
+ if (key && version) return {
159
+ key,
160
+ version
161
+ };
162
+ }
163
+ }
164
+ }
165
+ function extractPresentationTargets(obj, presentationsTargets) {
166
+ const presentationsProp = obj.getProperty("presentations");
167
+ if (presentationsProp && Node.isPropertyAssignment(presentationsProp)) {
168
+ const presentationsArr = presentationsProp.getInitializer();
169
+ if (presentationsArr && Node.isArrayLiteralExpression(presentationsArr)) {
170
+ for (const elem of presentationsArr.getElements()) if (Node.isObjectLiteralExpression(elem)) {
171
+ const keyText = getTextFromProp(elem, "key");
172
+ const verText = getTextFromProp(elem, "version");
173
+ const targetsList = getTargetsList(elem, "targets");
174
+ if (keyText && verText && targetsList) presentationsTargets.push({
175
+ key: keyText,
176
+ version: verText,
177
+ targets: targetsList
178
+ });
179
+ }
180
+ }
181
+ }
182
+ }
183
+ function getTextFromProp(obj, propName) {
184
+ const prop = obj.getProperty(propName);
185
+ if (prop && Node.isPropertyAssignment(prop)) {
186
+ const init = prop.getInitializer();
187
+ if (init && Node.isStringLiteral(init)) return init.getLiteralText();
188
+ }
189
+ }
190
+ function getTargetsList(obj, propName) {
191
+ const prop = obj.getProperty(propName);
192
+ if (prop && Node.isPropertyAssignment(prop)) {
193
+ const init = prop.getInitializer();
194
+ if (init && Node.isArrayLiteralExpression(init)) return init.getElements().map((e) => {
195
+ if (Node.isStringLiteral(e)) return e.getLiteralText();
196
+ return { target: e.getText() };
197
+ });
198
+ }
199
+ }
200
+
201
+ //#endregion
202
+ export { extractFeatureRefs };
203
+ //# sourceMappingURL=feature-extractor.js.map