@bpmn-ld/bpmn-ld 0.0.0-stage → 0.1.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 (319) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +125 -2
  3. package/contexts/combined.jsonld +3 -0
  4. package/contexts/core.jsonld +170 -0
  5. package/contexts/di.jsonld +37 -0
  6. package/dist/analyze/analyze.d.ts +3 -0
  7. package/dist/analyze/analyze.js +11 -0
  8. package/dist/analyze/findings.d.ts +8 -0
  9. package/dist/analyze/findings.js +1 -0
  10. package/dist/analyze/report.d.ts +3 -0
  11. package/dist/analyze/report.js +9 -0
  12. package/dist/analyze/rules/ad-hoc-completion-condition.d.ts +2 -0
  13. package/dist/analyze/rules/ad-hoc-completion-condition.js +7 -0
  14. package/dist/analyze/rules/cannot-reach-end.d.ts +2 -0
  15. package/dist/analyze/rules/cannot-reach-end.js +28 -0
  16. package/dist/analyze/rules/compensation-association.d.ts +2 -0
  17. package/dist/analyze/rules/compensation-association.js +13 -0
  18. package/dist/analyze/rules/dead-end.d.ts +2 -0
  19. package/dist/analyze/rules/dead-end.js +13 -0
  20. package/dist/analyze/rules/end-event.d.ts +2 -0
  21. package/dist/analyze/rules/end-event.js +8 -0
  22. package/dist/analyze/rules/error-catch.d.ts +2 -0
  23. package/dist/analyze/rules/error-catch.js +14 -0
  24. package/dist/analyze/rules/escalation-catch.d.ts +2 -0
  25. package/dist/analyze/rules/escalation-catch.js +14 -0
  26. package/dist/analyze/rules/gateway-cardinality.d.ts +3 -0
  27. package/dist/analyze/rules/gateway-cardinality.js +17 -0
  28. package/dist/analyze/rules/index.d.ts +4 -0
  29. package/dist/analyze/rules/index.js +21 -0
  30. package/dist/analyze/rules/instantiating-gateway.d.ts +2 -0
  31. package/dist/analyze/rules/instantiating-gateway.js +14 -0
  32. package/dist/analyze/rules/non-interrupting-start.d.ts +2 -0
  33. package/dist/analyze/rules/non-interrupting-start.js +14 -0
  34. package/dist/analyze/rules/reachability.d.ts +2 -0
  35. package/dist/analyze/rules/reachability.js +25 -0
  36. package/dist/analyze/rules/rule.d.ts +7 -0
  37. package/dist/analyze/rules/rule.js +1 -0
  38. package/dist/analyze/rules/signal-catch.d.ts +2 -0
  39. package/dist/analyze/rules/signal-catch.js +12 -0
  40. package/dist/analyze/rules/split-join-pairing.d.ts +3 -0
  41. package/dist/analyze/rules/split-join-pairing.js +50 -0
  42. package/dist/analyze/rules/start-event.d.ts +2 -0
  43. package/dist/analyze/rules/start-event.js +8 -0
  44. package/dist/analyze/rules/task-message-flow.d.ts +2 -0
  45. package/dist/analyze/rules/task-message-flow.js +19 -0
  46. package/dist/analyze/scope-graph.d.ts +56 -0
  47. package/dist/analyze/scope-graph.js +89 -0
  48. package/dist/analyze-cli.d.ts +1 -0
  49. package/dist/analyze-cli.js +5 -0
  50. package/dist/analyze-run.d.ts +12 -0
  51. package/dist/analyze-run.js +85 -0
  52. package/dist/bpmn-ld-cli.d.ts +1 -0
  53. package/dist/bpmn-ld-cli.js +90 -0
  54. package/dist/build-cli.d.ts +1 -0
  55. package/dist/build-cli.js +2 -0
  56. package/dist/build.d.ts +3 -0
  57. package/dist/build.js +38 -0
  58. package/dist/check-xml/check.d.ts +3 -0
  59. package/dist/check-xml/check.js +11 -0
  60. package/dist/check-xml/lint.d.ts +2 -0
  61. package/dist/check-xml/lint.js +47 -0
  62. package/dist/check-xml/moddle.d.ts +6 -0
  63. package/dist/check-xml/moddle.js +23 -0
  64. package/dist/check-xml/types.d.ts +20 -0
  65. package/dist/check-xml/types.js +1 -0
  66. package/dist/check-xml/xsd.d.ts +2 -0
  67. package/dist/check-xml/xsd.js +25 -0
  68. package/dist/check-xml-cli.d.ts +1 -0
  69. package/dist/check-xml-cli.js +5 -0
  70. package/dist/check-xml-run.d.ts +10 -0
  71. package/dist/check-xml-run.js +77 -0
  72. package/dist/convert/analyze-output.d.ts +3 -0
  73. package/dist/convert/analyze-output.js +8 -0
  74. package/dist/convert/convert.d.ts +19 -0
  75. package/dist/convert/convert.js +96 -0
  76. package/dist/convert/errors.d.ts +5 -0
  77. package/dist/convert/errors.js +8 -0
  78. package/dist/convert/extensions.d.ts +23 -0
  79. package/dist/convert/extensions.js +103 -0
  80. package/dist/convert/mapping-check.d.ts +15 -0
  81. package/dist/convert/mapping-check.js +66 -0
  82. package/dist/convert/mapping.d.ts +52 -0
  83. package/dist/convert/mapping.js +273 -0
  84. package/dist/convert/namespaces.d.ts +2 -0
  85. package/dist/convert/namespaces.js +19 -0
  86. package/dist/convert/validate-output.d.ts +2 -0
  87. package/dist/convert/validate-output.js +28 -0
  88. package/dist/convert/warnings.d.ts +9 -0
  89. package/dist/convert/warnings.js +67 -0
  90. package/dist/convert/xml-reader.d.ts +11 -0
  91. package/dist/convert/xml-reader.js +84 -0
  92. package/dist/convert-cli.d.ts +1 -0
  93. package/dist/convert-cli.js +5 -0
  94. package/dist/convert-run.d.ts +10 -0
  95. package/dist/convert-run.js +125 -0
  96. package/dist/coverage-cli.d.ts +1 -0
  97. package/dist/coverage-cli.js +8 -0
  98. package/dist/coverage-mappings.d.ts +3 -0
  99. package/dist/coverage-mappings.js +34 -0
  100. package/dist/coverage-scope.d.ts +11 -0
  101. package/dist/coverage-scope.js +187 -0
  102. package/dist/coverage-xsd.d.ts +15 -0
  103. package/dist/coverage-xsd.js +55 -0
  104. package/dist/coverage.d.ts +35 -0
  105. package/dist/coverage.js +152 -0
  106. package/dist/docs-cli.d.ts +1 -0
  107. package/dist/docs-cli.js +18 -0
  108. package/dist/docs.d.ts +8 -0
  109. package/dist/docs.js +136 -0
  110. package/dist/eval/checks.d.ts +19 -0
  111. package/dist/eval/checks.js +252 -0
  112. package/dist/eval/regenerate.d.ts +10 -0
  113. package/dist/eval/regenerate.js +48 -0
  114. package/dist/eval-regenerate-cli.d.ts +1 -0
  115. package/dist/eval-regenerate-cli.js +16 -0
  116. package/dist/export/data.d.ts +10 -0
  117. package/dist/export/data.js +127 -0
  118. package/dist/export/diagram.d.ts +8 -0
  119. package/dist/export/diagram.js +112 -0
  120. package/dist/export/element.d.ts +27 -0
  121. package/dist/export/element.js +175 -0
  122. package/dist/export/export.d.ts +22 -0
  123. package/dist/export/export.js +124 -0
  124. package/dist/export/extensions.d.ts +18 -0
  125. package/dist/export/extensions.js +122 -0
  126. package/dist/export/layout-export.d.ts +4 -0
  127. package/dist/export/layout-export.js +25 -0
  128. package/dist/export/mapping-check.d.ts +39 -0
  129. package/dist/export/mapping-check.js +175 -0
  130. package/dist/export/normalize.d.ts +3 -0
  131. package/dist/export/normalize.js +67 -0
  132. package/dist/export-cli.d.ts +1 -0
  133. package/dist/export-cli.js +5 -0
  134. package/dist/export-run.d.ts +10 -0
  135. package/dist/export-run.js +157 -0
  136. package/dist/index.d.ts +10 -0
  137. package/dist/index.js +10 -0
  138. package/dist/install-skill-run.d.ts +8 -0
  139. package/dist/install-skill-run.js +30 -0
  140. package/dist/install-skill.d.ts +23 -0
  141. package/dist/install-skill.js +76 -0
  142. package/dist/layout/collaboration.d.ts +36 -0
  143. package/dist/layout/collaboration.js +152 -0
  144. package/dist/layout/di.d.ts +15 -0
  145. package/dist/layout/di.js +52 -0
  146. package/dist/layout/extract.d.ts +2 -0
  147. package/dist/layout/extract.js +99 -0
  148. package/dist/layout/lanes.d.ts +30 -0
  149. package/dist/layout/lanes.js +112 -0
  150. package/dist/layout/layering.d.ts +7 -0
  151. package/dist/layout/layering.js +53 -0
  152. package/dist/layout/layout.d.ts +12 -0
  153. package/dist/layout/layout.js +299 -0
  154. package/dist/layout/model.d.ts +52 -0
  155. package/dist/layout/model.js +10 -0
  156. package/dist/layout/placement.d.ts +25 -0
  157. package/dist/layout/placement.js +256 -0
  158. package/dist/layout/standalone.d.ts +8 -0
  159. package/dist/layout/standalone.js +50 -0
  160. package/dist/layout-cli.d.ts +1 -0
  161. package/dist/layout-cli.js +5 -0
  162. package/dist/layout-run.d.ts +9 -0
  163. package/dist/layout-run.js +105 -0
  164. package/dist/loader.d.ts +5 -0
  165. package/dist/loader.js +7 -0
  166. package/dist/namespace.d.ts +5 -0
  167. package/dist/namespace.js +5 -0
  168. package/dist/ontology.d.ts +19 -0
  169. package/dist/ontology.js +94 -0
  170. package/dist/paths.d.ts +1 -0
  171. package/dist/paths.js +2 -0
  172. package/dist/render/geometry.d.ts +40 -0
  173. package/dist/render/geometry.js +81 -0
  174. package/dist/render/renderer.d.ts +31 -0
  175. package/dist/render/renderer.js +369 -0
  176. package/dist/render/symbols.d.ts +10 -0
  177. package/dist/render/symbols.js +26 -0
  178. package/dist/render/text.d.ts +20 -0
  179. package/dist/render/text.js +28 -0
  180. package/dist/render-cli.d.ts +1 -0
  181. package/dist/render-cli.js +7 -0
  182. package/dist/render-run.d.ts +9 -0
  183. package/dist/render-run.js +161 -0
  184. package/dist/render.d.ts +4 -0
  185. package/dist/render.js +4 -0
  186. package/dist/schema-overrides.d.ts +14 -0
  187. package/dist/schema-overrides.js +36 -0
  188. package/dist/schema-validate.d.ts +5 -0
  189. package/dist/schema-validate.js +8 -0
  190. package/dist/schema.d.ts +6 -0
  191. package/dist/schema.js +114 -0
  192. package/dist/shacl.d.ts +15 -0
  193. package/dist/shacl.js +37 -0
  194. package/dist/validate-cli.d.ts +1 -0
  195. package/dist/validate-cli.js +5 -0
  196. package/dist/validate-run.d.ts +8 -0
  197. package/dist/validate-run.js +58 -0
  198. package/dist/validate.d.ts +11 -0
  199. package/dist/validate.js +94 -0
  200. package/dist/versioning-cli.d.ts +1 -0
  201. package/dist/versioning-cli.js +18 -0
  202. package/dist/versioning.d.ts +17 -0
  203. package/dist/versioning.js +45 -0
  204. package/ontology/core.ttl +993 -0
  205. package/ontology/di.ttl +167 -0
  206. package/package.json +76 -4
  207. package/schema/bpmn-ld.schema.json +15230 -0
  208. package/shapes/core.shapes.ttl +398 -0
  209. package/skills/bpmn-ld/SKILL.md +154 -0
  210. package/skills/bpmn-ld/evals/models/order-approval-di.jsonld +296 -0
  211. package/skills/bpmn-ld/evals/models/refund-di.bpmn +82 -0
  212. package/skills/bpmn-ld/evals/models/refund.bpmn +18 -0
  213. package/skills/bpmn-ld/evals/outputs/explain-collaboration.md +22 -0
  214. package/skills/bpmn-ld/evals/outputs/explain-order-approval.md +24 -0
  215. package/skills/bpmn-ld/evals/outputs/explain-refund.md +17 -0
  216. package/skills/bpmn-ld/evals/outputs/generate-activity-compensation.jsonld +39 -0
  217. package/skills/bpmn-ld/evals/outputs/generate-ad-hoc-sub-process.jsonld +52 -0
  218. package/skills/bpmn-ld/evals/outputs/generate-boundary-event.jsonld +27 -0
  219. package/skills/bpmn-ld/evals/outputs/generate-collaboration.jsonld +49 -0
  220. package/skills/bpmn-ld/evals/outputs/generate-conditions.jsonld +93 -0
  221. package/skills/bpmn-ld/evals/outputs/generate-diagram-properties.jsonld +340 -0
  222. package/skills/bpmn-ld/evals/outputs/generate-error.jsonld +217 -0
  223. package/skills/bpmn-ld/evals/outputs/generate-escalation.jsonld +231 -0
  224. package/skills/bpmn-ld/evals/outputs/generate-event-definition-ref.jsonld +67 -0
  225. package/skills/bpmn-ld/evals/outputs/generate-event-sub-process.jsonld +54 -0
  226. package/skills/bpmn-ld/evals/outputs/generate-exclusive-choice.jsonld +32 -0
  227. package/skills/bpmn-ld/evals/outputs/generate-instantiating-gateway.jsonld +24 -0
  228. package/skills/bpmn-ld/evals/outputs/generate-lane-partition.jsonld +46 -0
  229. package/skills/bpmn-ld/evals/outputs/generate-link-event.expect.json +11 -0
  230. package/skills/bpmn-ld/evals/outputs/generate-link-event.jsonld +57 -0
  231. package/skills/bpmn-ld/evals/outputs/generate-loop.jsonld +30 -0
  232. package/skills/bpmn-ld/evals/outputs/generate-message.jsonld +61 -0
  233. package/skills/bpmn-ld/evals/outputs/generate-parallel-branches.jsonld +25 -0
  234. package/skills/bpmn-ld/evals/outputs/generate-process-attributes.jsonld +49 -0
  235. package/skills/bpmn-ld/evals/outputs/generate-script-task.jsonld +18 -0
  236. package/skills/bpmn-ld/evals/outputs/generate-signal.jsonld +178 -0
  237. package/skills/bpmn-ld/evals/outputs/generate-sub-process.jsonld +31 -0
  238. package/skills/bpmn-ld/evals/outputs/generate-targeted-compensation.jsonld +40 -0
  239. package/skills/bpmn-ld/evals/outputs/generate-task-implementation.jsonld +18 -0
  240. package/skills/bpmn-ld/evals/outputs/generate-task-message-references.jsonld +49 -0
  241. package/skills/bpmn-ld/evals/outputs/generate-transaction.jsonld +48 -0
  242. package/skills/bpmn-ld/evals/outputs/modify-order-approval.jsonld +376 -0
  243. package/skills/bpmn-ld/evals/outputs/modify-refund.jsonld +373 -0
  244. package/skills/bpmn-ld/evals/outputs/order-approval.jsonld +29 -0
  245. package/skills/bpmn-ld/evals/outputs/refine-order-approval.jsonld +377 -0
  246. package/skills/bpmn-ld/evals/prompts/explain-collaboration.md +3 -0
  247. package/skills/bpmn-ld/evals/prompts/explain-order-approval.md +3 -0
  248. package/skills/bpmn-ld/evals/prompts/explain-refund.md +3 -0
  249. package/skills/bpmn-ld/evals/prompts/generate-activity-compensation.md +1 -0
  250. package/skills/bpmn-ld/evals/prompts/generate-ad-hoc-sub-process.md +1 -0
  251. package/skills/bpmn-ld/evals/prompts/generate-boundary-event.md +1 -0
  252. package/skills/bpmn-ld/evals/prompts/generate-collaboration.md +1 -0
  253. package/skills/bpmn-ld/evals/prompts/generate-conditions.md +1 -0
  254. package/skills/bpmn-ld/evals/prompts/generate-diagram-properties.md +1 -0
  255. package/skills/bpmn-ld/evals/prompts/generate-error.md +1 -0
  256. package/skills/bpmn-ld/evals/prompts/generate-escalation.md +1 -0
  257. package/skills/bpmn-ld/evals/prompts/generate-event-definition-ref.md +1 -0
  258. package/skills/bpmn-ld/evals/prompts/generate-event-sub-process.md +1 -0
  259. package/skills/bpmn-ld/evals/prompts/generate-exclusive-choice.md +1 -0
  260. package/skills/bpmn-ld/evals/prompts/generate-instantiating-gateway.md +1 -0
  261. package/skills/bpmn-ld/evals/prompts/generate-lane-partition.md +1 -0
  262. package/skills/bpmn-ld/evals/prompts/generate-link-event.md +1 -0
  263. package/skills/bpmn-ld/evals/prompts/generate-loop.md +1 -0
  264. package/skills/bpmn-ld/evals/prompts/generate-message.md +1 -0
  265. package/skills/bpmn-ld/evals/prompts/generate-parallel-branches.md +1 -0
  266. package/skills/bpmn-ld/evals/prompts/generate-process-attributes.md +1 -0
  267. package/skills/bpmn-ld/evals/prompts/generate-script-task.md +1 -0
  268. package/skills/bpmn-ld/evals/prompts/generate-signal.md +1 -0
  269. package/skills/bpmn-ld/evals/prompts/generate-sub-process.md +1 -0
  270. package/skills/bpmn-ld/evals/prompts/generate-targeted-compensation.md +1 -0
  271. package/skills/bpmn-ld/evals/prompts/generate-task-implementation.md +1 -0
  272. package/skills/bpmn-ld/evals/prompts/generate-task-message-references.md +1 -0
  273. package/skills/bpmn-ld/evals/prompts/generate-transaction.md +1 -0
  274. package/skills/bpmn-ld/evals/prompts/modify-order-approval.md +4 -0
  275. package/skills/bpmn-ld/evals/prompts/modify-refund.md +4 -0
  276. package/skills/bpmn-ld/evals/prompts/order-approval.md +1 -0
  277. package/skills/bpmn-ld/evals/prompts/refine-order-approval.md +8 -0
  278. package/skills/bpmn-ld/examples/positive/activity-compensation.jsonld +39 -0
  279. package/skills/bpmn-ld/examples/positive/ad-hoc-sub-process.jsonld +52 -0
  280. package/skills/bpmn-ld/examples/positive/boundary-event.jsonld +27 -0
  281. package/skills/bpmn-ld/examples/positive/collaboration.jsonld +49 -0
  282. package/skills/bpmn-ld/examples/positive/conditions.jsonld +93 -0
  283. package/skills/bpmn-ld/examples/positive/diagram-properties.jsonld +340 -0
  284. package/skills/bpmn-ld/examples/positive/error.jsonld +208 -0
  285. package/skills/bpmn-ld/examples/positive/escalation.jsonld +231 -0
  286. package/skills/bpmn-ld/examples/positive/event-definition-ref.jsonld +142 -0
  287. package/skills/bpmn-ld/examples/positive/event-sub-process.jsonld +54 -0
  288. package/skills/bpmn-ld/examples/positive/exclusive-choice.jsonld +32 -0
  289. package/skills/bpmn-ld/examples/positive/instantiating-gateway.jsonld +24 -0
  290. package/skills/bpmn-ld/examples/positive/lane-partition.jsonld +46 -0
  291. package/skills/bpmn-ld/examples/positive/link-event.expect.json +10 -0
  292. package/skills/bpmn-ld/examples/positive/link-event.jsonld +138 -0
  293. package/skills/bpmn-ld/examples/positive/loop.jsonld +30 -0
  294. package/skills/bpmn-ld/examples/positive/message.jsonld +60 -0
  295. package/skills/bpmn-ld/examples/positive/order-approval.jsonld +29 -0
  296. package/skills/bpmn-ld/examples/positive/parallel-branches.jsonld +25 -0
  297. package/skills/bpmn-ld/examples/positive/process-attributes.jsonld +49 -0
  298. package/skills/bpmn-ld/examples/positive/script-task.jsonld +18 -0
  299. package/skills/bpmn-ld/examples/positive/signal.jsonld +150 -0
  300. package/skills/bpmn-ld/examples/positive/sub-process.jsonld +31 -0
  301. package/skills/bpmn-ld/examples/positive/targeted-compensation.jsonld +40 -0
  302. package/skills/bpmn-ld/examples/positive/task-implementation.jsonld +18 -0
  303. package/skills/bpmn-ld/examples/positive/task-message-references.jsonld +151 -0
  304. package/skills/bpmn-ld/examples/positive/transaction.jsonld +48 -0
  305. package/skills/bpmn-ld/references/clarification-and-refinement.md +60 -0
  306. package/skills/bpmn-ld/references/common-mistakes.md +110 -0
  307. package/skills/bpmn-ld/references/explain-and-document.md +36 -0
  308. package/skills/bpmn-ld/references/modify-existing-model.md +49 -0
  309. package/skills/bpmn-ld/references/patterns.md +292 -0
  310. package/skills/bpmn-ld/references/render-output.md +1 -0
  311. package/skills/bpmn-ld/references/rule-catalogue.md +183 -0
  312. package/skills/bpmn-ld/references/vocabulary-summary.md +65 -0
  313. package/skills/bpmn-ld/template.jsonld +14 -0
  314. package/templates/combined.jsonld +3 -0
  315. package/templates/core.jsonld +170 -0
  316. package/templates/core.shapes.ttl +398 -0
  317. package/templates/core.ttl +993 -0
  318. package/templates/di.jsonld +37 -0
  319. package/templates/di.ttl +167 -0
@@ -0,0 +1,152 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { BPMN_NAMESPACE, BPMNDI_NAMESPACE } from "./namespace.js";
4
+ import { declaredClasses, declaredProperties, domainAlternatives, loadOntology, superClasses } from "./ontology.js";
5
+ import { loadXsdTypes } from "./coverage-xsd.js";
6
+ import { REAL_SCOPE } from "./coverage-scope.js";
7
+ import { BUILT_IN_PROPERTIES, CLASS_ALIASES, PROPERTY_ALIASES } from "./coverage-mappings.js";
8
+ const PREFIXES = [
9
+ [BPMN_NAMESPACE, "bpmn:"],
10
+ [BPMNDI_NAMESPACE, "bpmndi:"],
11
+ ];
12
+ const compact = (iri) => {
13
+ for (const [namespace, prefix] of PREFIXES)
14
+ if (iri.startsWith(namespace))
15
+ return prefix + iri.slice(namespace.length);
16
+ return iri;
17
+ };
18
+ const semantic = (type) => type.file === "Semantic.xsd";
19
+ const stripPrefix = (name) => name.replace(/^t(?=[A-Z])/, "");
20
+ const idName = (type) => (semantic(type) ? stripPrefix(type.name) : type.name);
21
+ const localClassName = (type) => CLASS_ALIASES[type.name] ?? idName(type);
22
+ const namespaceOf = (type) => (semantic(type) ? BPMN_NAMESPACE : BPMNDI_NAMESPACE);
23
+ const groupOf = (groups, name) => Object.entries(groups).find(([, members]) => members.includes(name))?.[0];
24
+ const emptyCounts = () => ({ covered: 0, gap: 0, "out-of-scope": 0 });
25
+ const propertyCovered = (store, classIri, property) => {
26
+ const ancestors = new Set([classIri, ...superClasses(store, classIri)]);
27
+ const statements = domainAlternatives(store, property);
28
+ return statements.length === 0 || statements.some((alternatives) => alternatives.some((c) => ancestors.has(c)));
29
+ };
30
+ const reasonOf = (scope, group) => {
31
+ const reason = scope.outOfScopeReasons?.[group];
32
+ return reason ? { reason } : {};
33
+ };
34
+ export const checkCoverage = (store, types, scope) => {
35
+ const classes = declaredClasses(store);
36
+ const properties = declaredProperties(store);
37
+ const items = [];
38
+ const unclassified = [];
39
+ for (const type of types) {
40
+ const local = idName(type);
41
+ const classIri = namespaceOf(type) + localClassName(type);
42
+ const xsd = `${type.file}#${type.name}`;
43
+ if (!scope.inScope.includes(type.name)) {
44
+ const group = groupOf(scope.outOfScopeTypes, type.name);
45
+ if (group)
46
+ items.push({ id: local, kind: "class", xsd, ontology: null, status: "out-of-scope", group, ...reasonOf(scope, group) });
47
+ else
48
+ unclassified.push(xsd);
49
+ continue;
50
+ }
51
+ const covered = classes.has(classIri);
52
+ items.push({ id: local, kind: "class", xsd, ontology: compact(classIri), status: covered ? "covered" : "gap" });
53
+ for (const member of type.members) {
54
+ const memberXsd = `${xsd}/${member.kind === "attribute" ? "@" : ""}${member.name}`;
55
+ const id = `${local}.${member.name}`;
56
+ const builtIn = BUILT_IN_PROPERTIES[member.name];
57
+ if (builtIn) {
58
+ items.push({ id, kind: "property", xsd: memberXsd, ontology: builtIn, status: "covered" });
59
+ continue;
60
+ }
61
+ const alias = PROPERTY_ALIASES[`${local}.${member.name}`] ?? PROPERTY_ALIASES[member.name] ?? member.name;
62
+ const candidates = [BPMN_NAMESPACE + alias, BPMNDI_NAMESPACE + alias].filter((iri) => properties.has(iri));
63
+ const hit = candidates.find((iri) => propertyCovered(store, classIri, iri));
64
+ if (hit) {
65
+ items.push({ id, kind: "property", xsd: memberXsd, ontology: compact(hit), status: "covered" });
66
+ continue;
67
+ }
68
+ const group = groupOf(scope.outOfScopeProperties, member.name);
69
+ if (group)
70
+ items.push({ id, kind: "property", xsd: memberXsd, ontology: null, status: "out-of-scope", group, ...reasonOf(scope, group) });
71
+ else
72
+ items.push({ id, kind: "property", xsd: memberXsd, ontology: null, status: "gap" });
73
+ }
74
+ }
75
+ items.sort((a, b) => (a.kind === b.kind ? a.id.localeCompare(b.id) : a.kind === "class" ? -1 : 1));
76
+ unclassified.sort();
77
+ const summary = { classes: emptyCounts(), properties: emptyCounts(), total: emptyCounts() };
78
+ for (const item of items) {
79
+ summary[item.kind === "class" ? "classes" : "properties"][item.status]++;
80
+ summary.total[item.status]++;
81
+ }
82
+ return { summary, unclassified, items };
83
+ };
84
+ const table = (rows, columns, cell) => rows.length
85
+ ? [
86
+ `| ${columns.join(" | ")} |`,
87
+ `| ${columns.map(() => "---").join(" | ")} |`,
88
+ ...rows.map((item) => `| ${cell(item).join(" | ")} |`),
89
+ ].join("\n")
90
+ : "_None._";
91
+ export const renderMarkdown = (report) => {
92
+ const { summary, items } = report;
93
+ const inScope = items.filter((i) => i.status !== "out-of-scope");
94
+ const gaps = items.filter((i) => i.status === "gap");
95
+ const covered = items.filter((i) => i.status === "covered");
96
+ const out = items.filter((i) => i.status === "out-of-scope");
97
+ const groups = [...new Set(out.map((i) => i.group))].sort();
98
+ const lines = [
99
+ "# BPMN coverage report",
100
+ "",
101
+ "Generated by `npm run coverage` from the normative BPMN 2.0.2 XSDs in `reference/bpmn-xsd/` and the ontology in `ontology/`. Do not edit by hand.",
102
+ "",
103
+ "## Summary",
104
+ "",
105
+ "| Kind | Covered | Gaps | Out of scope |",
106
+ "| --- | ---: | ---: | ---: |",
107
+ ...["classes", "properties", "total"].map((k) => `| ${k} | ${summary[k].covered} | ${summary[k].gap} | ${summary[k]["out-of-scope"]} |`),
108
+ "",
109
+ `In scope: ${inScope.length} items, ${summary.total.covered} covered and ${summary.total.gap} gaps.`,
110
+ "",
111
+ `## Gaps (${gaps.length})`,
112
+ "",
113
+ table(gaps, ["Kind", "Item", "XSD"], (i) => [i.kind, `\`${i.id}\``, `\`${i.xsd}\``]),
114
+ "",
115
+ `## Covered (${covered.length})`,
116
+ "",
117
+ table(covered, ["Kind", "Item", "XSD", "Ontology"], (i) => [i.kind, `\`${i.id}\``, `\`${i.xsd}\``, `\`${i.ontology}\``]),
118
+ "",
119
+ `## Out of scope (${out.length})`,
120
+ "",
121
+ ...groups.flatMap((group) => {
122
+ const members = out.filter((i) => i.group === group);
123
+ const reason = members.find((i) => i.reason)?.reason;
124
+ return [
125
+ `### ${group} (${members.length})`,
126
+ "",
127
+ ...(reason ? [reason, ""] : []),
128
+ members.map((i) => `\`${i.kind === "class" ? i.xsd : i.id}\``).join(", "),
129
+ "",
130
+ ];
131
+ }),
132
+ ];
133
+ if (report.unclassified.length) {
134
+ lines.push(`## Unclassified (${report.unclassified.length})`, "", report.unclassified.map((u) => `\`${u}\``).join(", "), "");
135
+ }
136
+ return lines.join("\n").replace(/\n+$/, "\n");
137
+ };
138
+ export const renderJson = (report) => `${JSON.stringify(report, null, 2)}\n`;
139
+ export const generateReports = (root) => {
140
+ const report = checkCoverage(loadOntology(), loadXsdTypes(root), REAL_SCOPE);
141
+ return { report, markdown: renderMarkdown(report), json: renderJson(report) };
142
+ };
143
+ export const REPORT_MARKDOWN = "reports/coverage.md";
144
+ export const REPORT_JSON = "reports/coverage.json";
145
+ export const writeReports = (root) => {
146
+ const { report, markdown, json } = generateReports(root);
147
+ mkdirSync(join(root, "reports"), { recursive: true });
148
+ writeFileSync(join(root, REPORT_MARKDOWN), markdown);
149
+ writeFileSync(join(root, REPORT_JSON), json);
150
+ return report;
151
+ };
152
+ export const readReport = (root, file) => readFileSync(join(root, file), "utf8");
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ import { checkVocabulary, writeVocabulary } from "./docs.js";
2
+ const args = process.argv.slice(2);
3
+ const check = args.includes("--check");
4
+ const path = args.find((arg) => !arg.startsWith("--"));
5
+ if (check) {
6
+ const result = checkVocabulary(path);
7
+ if (result.ok) {
8
+ console.log(result.message);
9
+ }
10
+ else {
11
+ console.error(result.message);
12
+ process.exitCode = 1;
13
+ }
14
+ }
15
+ else {
16
+ writeVocabulary(path);
17
+ console.log(`wrote ${path ?? "docs/reference/vocabulary.md"}`);
18
+ }
package/dist/docs.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import type { Store } from "n3";
2
+ export declare const VOCABULARY_PATH = "docs/reference/vocabulary.md";
3
+ export declare const renderVocabulary: (store?: Store) => string;
4
+ export declare const writeVocabulary: (path?: string, store?: Store) => void;
5
+ export declare const checkVocabulary: (path?: string, store?: Store) => {
6
+ ok: boolean;
7
+ message: string;
8
+ };
package/dist/docs.js ADDED
@@ -0,0 +1,136 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { BPMN_NAMESPACE, BPMNDI_NAMESPACE } from "./namespace.js";
4
+ import { ROOT } from "./paths.js";
5
+ import { rangeAlternatives, declaredClasses, declaredIndividuals, declaredProperties, domainAlternatives, isSubClassOf, loadOntology, typesOf, } from "./ontology.js";
6
+ export const VOCABULARY_PATH = "docs/reference/vocabulary.md";
7
+ const RDFS = "http://www.w3.org/2000/01/rdf-schema#";
8
+ const OWL_OBJECT_PROPERTY = "http://www.w3.org/2002/07/owl#ObjectProperty";
9
+ const XSD = "http://www.w3.org/2001/XMLSchema#";
10
+ const MODULES = [
11
+ { title: "Core", prefix: "bpmn", namespace: BPMN_NAMESPACE },
12
+ { title: "Diagram interchange", prefix: "bpmndi", namespace: BPMNDI_NAMESPACE },
13
+ ];
14
+ const compact = (iri) => {
15
+ const module = MODULES.find((m) => iri.startsWith(m.namespace));
16
+ if (module)
17
+ return `${module.prefix}:${iri.slice(module.namespace.length)}`;
18
+ return iri.startsWith(XSD) ? `xsd:${iri.slice(XSD.length)}` : iri;
19
+ };
20
+ const byCompact = (a, b) => compact(a).localeCompare(compact(b));
21
+ const cell = (text) => text.replace(/\|/g, "\\|").replace(/\s+/g, " ").trim();
22
+ const comment = (store, iri) => store.getObjects(iri, `${RDFS}comment`, null).map((o) => o.value).sort().join(" ") || "No definition.";
23
+ const code = (iri) => `\`${compact(iri)}\``;
24
+ const list = (iris) => (iris.length ? [...iris].sort(byCompact).map(code).join(", ") : "none");
25
+ const directSuperClasses = (store, cls) => store.getObjects(cls, `${RDFS}subClassOf`, null).filter((o) => o.termType === "NamedNode").map((o) => o.value);
26
+ const ancestorsOf = (store, cls) => {
27
+ const result = new Set();
28
+ const walk = (current) => {
29
+ for (const parent of directSuperClasses(store, current)) {
30
+ if (!result.has(parent)) {
31
+ result.add(parent);
32
+ walk(parent);
33
+ }
34
+ }
35
+ };
36
+ walk(cls);
37
+ return [...result];
38
+ };
39
+ const domainText = (store, property) => {
40
+ const alternatives = domainAlternatives(store, property);
41
+ return alternatives.length
42
+ ? alternatives.map((members) => [...members].sort(byCompact).map(code).join(" or ")).join("; and ")
43
+ : "unspecified";
44
+ };
45
+ const rangeText = (store, property) => {
46
+ const ranges = rangeAlternatives(store, property);
47
+ return ranges.length ? ranges.map((members) => [...members].sort(byCompact).map(code).join(" or ")).sort().join(", ") : "unspecified";
48
+ };
49
+ const appliesTo = (store, property, cls) => domainAlternatives(store, property).some((members) => members.some((member) => isSubClassOf(store, cls, member)));
50
+ const renderClass = (store, cls, properties) => {
51
+ const subClasses = [...declaredClasses(store)].filter((c) => directSuperClasses(store, c).includes(cls));
52
+ const applicable = properties.filter((p) => appliesTo(store, p, cls));
53
+ const lines = [
54
+ `#### ${code(cls)}`,
55
+ "",
56
+ comment(store, cls),
57
+ "",
58
+ `- Superclasses: ${list(directSuperClasses(store, cls))}`,
59
+ `- All ancestors: ${list(ancestorsOf(store, cls))}`,
60
+ `- Subclasses: ${list(subClasses)}`,
61
+ "",
62
+ ];
63
+ if (applicable.length) {
64
+ lines.push("| Property | Range | Definition |", "| --- | --- | --- |");
65
+ for (const property of applicable) {
66
+ lines.push(`| ${code(property)} | ${rangeText(store, property)} | ${cell(comment(store, property))} |`);
67
+ }
68
+ lines.push("");
69
+ }
70
+ else {
71
+ lines.push("No properties apply.", "");
72
+ }
73
+ return lines;
74
+ };
75
+ const renderProperty = (store, property) => {
76
+ const kind = typesOf(store, property).includes(OWL_OBJECT_PROPERTY) ? "object" : "datatype";
77
+ return [
78
+ `#### ${code(property)}`,
79
+ "",
80
+ comment(store, property),
81
+ "",
82
+ `- Kind: ${kind}`,
83
+ `- Domain: ${domainText(store, property)}`,
84
+ `- Range: ${rangeText(store, property)}`,
85
+ "",
86
+ ];
87
+ };
88
+ const renderIndividual = (store, individual) => {
89
+ const classes = typesOf(store, individual).filter((t) => declaredClasses(store).has(t));
90
+ return [`#### ${code(individual)}`, "", comment(store, individual), "", `- Type: ${list(classes)}`, ""];
91
+ };
92
+ export const renderVocabulary = (store = loadOntology()) => {
93
+ const classes = declaredClasses(store);
94
+ const properties = declaredProperties(store);
95
+ const individuals = declaredIndividuals(store);
96
+ const lines = [
97
+ "# Vocabulary reference",
98
+ "",
99
+ "Generated from the ontology by `npm run docs`. Do not edit by hand.",
100
+ "",
101
+ ...MODULES.map((m) => `- \`${m.prefix}:\` expands to \`${m.namespace}\``),
102
+ "",
103
+ ];
104
+ for (const module of MODULES) {
105
+ const inModule = (iri) => iri.startsWith(module.namespace);
106
+ const moduleClasses = [...classes].filter(inModule).sort(byCompact);
107
+ const moduleProperties = [...properties].filter(inModule).sort(byCompact);
108
+ const moduleIndividuals = [...individuals].filter(inModule).sort(byCompact);
109
+ const applicableProperties = [...properties].sort(byCompact);
110
+ lines.push(`## ${module.title} module (\`${module.prefix}:\`)`, "");
111
+ lines.push("### Classes", "");
112
+ for (const cls of moduleClasses)
113
+ lines.push(...renderClass(store, cls, applicableProperties));
114
+ lines.push("### Properties", "");
115
+ for (const property of moduleProperties)
116
+ lines.push(...renderProperty(store, property));
117
+ if (moduleIndividuals.length) {
118
+ lines.push("### Named individuals", "");
119
+ for (const individual of moduleIndividuals)
120
+ lines.push(...renderIndividual(store, individual));
121
+ }
122
+ }
123
+ return `${lines.join("\n").replace(/\n+$/, "")}\n`;
124
+ };
125
+ export const writeVocabulary = (path = join(ROOT, VOCABULARY_PATH), store) => {
126
+ mkdirSync(dirname(path), { recursive: true });
127
+ writeFileSync(path, renderVocabulary(store));
128
+ };
129
+ export const checkVocabulary = (path = join(ROOT, VOCABULARY_PATH), store) => {
130
+ const expected = renderVocabulary(store);
131
+ const actual = existsSync(path) ? readFileSync(path, "utf8") : null;
132
+ if (actual === expected)
133
+ return { ok: true, message: `${path} is up to date` };
134
+ const reason = actual === null ? "is missing" : "differs from the ontology";
135
+ return { ok: false, message: `${path} ${reason}; run npm run docs and commit the result` };
136
+ };
@@ -0,0 +1,19 @@
1
+ export interface Check {
2
+ name: string;
3
+ run: (file: string) => Promise<string[]>;
4
+ }
5
+ export interface Failure {
6
+ check: string;
7
+ message: string;
8
+ }
9
+ export interface FileResult {
10
+ file: string;
11
+ failures: Failure[];
12
+ }
13
+ export declare const CHECKS: Check[];
14
+ export declare const EXPLANATION_CHECKS: Check[];
15
+ export declare const MODIFICATION_CHECKS: Check[];
16
+ export declare const evaluateFile: (file: string, checks?: Check[]) => Promise<FileResult>;
17
+ export declare const evaluateOutputs: (dir: string, checks?: Check[]) => Promise<FileResult[]>;
18
+ export declare const evaluateExplanations: (dir: string, checks?: Check[]) => Promise<FileResult[]>;
19
+ export declare const evaluateModifications: (dir: string, checks?: Check[]) => Promise<FileResult[]>;
@@ -0,0 +1,252 @@
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import { basename, dirname, extname, join, resolve } from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ import { convert } from "../convert/convert.js";
5
+ import { analyze } from "../analyze/analyze.js";
6
+ import { checkXml } from "../check-xml/check.js";
7
+ import { layoutAndExport } from "../export/layout-export.js";
8
+ import { ExportInputError } from "../export/export.js";
9
+ import { layoutDocument, LayoutInputError } from "../layout/layout.js";
10
+ import { compileSchema, validateWithSchema } from "../schema-validate.js";
11
+ import { validateShacl } from "../shacl.js";
12
+ import { validateExample } from "../validate.js";
13
+ const WARNING_PREFIX = "warning ";
14
+ const severityPrefix = (severity) => (severity === "error" ? "" : WARNING_PREFIX);
15
+ const validateSchema = compileSchema();
16
+ const structural = {
17
+ name: "structural",
18
+ run: async (file) => (await validateExample(file)).errors,
19
+ };
20
+ const schema = {
21
+ name: "schema",
22
+ run: async (file) => validateWithSchema(JSON.parse(readFileSync(file, "utf8")), validateSchema),
23
+ };
24
+ const shacl = {
25
+ name: "shacl",
26
+ run: async (file) => {
27
+ const result = await validateShacl(file);
28
+ const messages = result.violations.map((v) => `${v.focusNode} violates ${v.sourceShape}`);
29
+ if (!result.conforms && messages.length === 0)
30
+ messages.push("SHACL validation failed");
31
+ return messages;
32
+ },
33
+ };
34
+ const analyzer = {
35
+ name: "analyzer",
36
+ run: async (file) => {
37
+ const findings = await analyze(JSON.parse(readFileSync(file, "utf8")), pathToFileURL(resolve(file)).href);
38
+ return findings.map((f) => `${severityPrefix(f.severity)}${f.rule} ${f.node ?? "-"}: ${f.message}`);
39
+ },
40
+ };
41
+ const layout = {
42
+ name: "layout",
43
+ run: async (file) => {
44
+ try {
45
+ layoutDocument(JSON.parse(readFileSync(file, "utf8")));
46
+ return [];
47
+ }
48
+ catch (error) {
49
+ if (error instanceof LayoutInputError)
50
+ return error.errors;
51
+ throw error;
52
+ }
53
+ },
54
+ };
55
+ const exportXml = async (file) => (await layoutAndExport(JSON.parse(readFileSync(file, "utf8")), { documentUrl: pathToFileURL(resolve(file)).href })).xml;
56
+ const exported = {
57
+ name: "export",
58
+ run: async (file) => {
59
+ try {
60
+ await exportXml(file);
61
+ return [];
62
+ }
63
+ catch (error) {
64
+ if (error instanceof ExportInputError)
65
+ return error.errors;
66
+ throw error;
67
+ }
68
+ },
69
+ };
70
+ const reports = new Map();
71
+ const externalReport = (file) => {
72
+ const key = `${resolve(file)}\n${readFileSync(file, "utf8")}`;
73
+ let report = reports.get(key);
74
+ if (!report) {
75
+ report = exportXml(file).then(checkXml);
76
+ reports.set(key, report);
77
+ }
78
+ return report;
79
+ };
80
+ const external = (name) => ({
81
+ name,
82
+ run: async (file) => {
83
+ const result = (await externalReport(file)).checks[name];
84
+ return result.issues.map((issue) => `${severityPrefix(issue.severity)}${issue.rule ? `${issue.rule}: ` : ""}${issue.message}`);
85
+ },
86
+ });
87
+ export const CHECKS = [structural, schema, shacl, analyzer, layout, exported, external("xsd"), external("moddle"), external("lint")];
88
+ const MODEL_LINE = /^Model:\s*(\S+)\s*$/m;
89
+ const collectNames = (value, names) => {
90
+ if (Array.isArray(value))
91
+ value.forEach((item) => collectNames(item, names));
92
+ else if (value && typeof value === "object")
93
+ for (const [key, child] of Object.entries(value)) {
94
+ if (key === "name" && typeof child === "string")
95
+ names.add(child);
96
+ else
97
+ collectNames(child, names);
98
+ }
99
+ };
100
+ const modelNames = (modelFile) => {
101
+ const text = readFileSync(modelFile, "utf8");
102
+ const document = extname(modelFile) === ".bpmn" ? convert(text, { fileName: modelFile }).document : JSON.parse(text);
103
+ const names = new Set();
104
+ collectNames(document, names);
105
+ return [...names];
106
+ };
107
+ const normalize = (text) => text.replace(/\s+/g, " ").toLowerCase();
108
+ const mentionsElements = {
109
+ name: "mentions-elements",
110
+ run: async (file) => {
111
+ const evalsDir = dirname(dirname(file));
112
+ const prompt = readFileSync(join(evalsDir, "prompts", `${basename(file, ".md")}.md`), "utf8");
113
+ const model = prompt.match(MODEL_LINE)?.[1];
114
+ if (!model)
115
+ return ["prompt has no Model line"];
116
+ const explanation = normalize(readFileSync(file, "utf8"));
117
+ return modelNames(resolve(dirname(evalsDir), model))
118
+ .filter((name) => !explanation.includes(normalize(name)))
119
+ .map((name) => `does not mention "${name}"`);
120
+ },
121
+ };
122
+ export const EXPLANATION_CHECKS = [mentionsElements];
123
+ const ALLOWED_LINE = /^Allowed-changes:[ \t]*(.*)$/m;
124
+ const DI_TYPES = new Set(["BPMNDiagram", "BPMNPlane", "BPMNShape", "BPMNEdge", "BPMNLabel", "Bounds", "Point"]);
125
+ const loadDocument = (modelFile) => {
126
+ const text = readFileSync(modelFile, "utf8");
127
+ return extname(modelFile) === ".bpmn" ? convert(text, { fileName: modelFile }).document : JSON.parse(text);
128
+ };
129
+ const isNode = (value) => !!value && typeof value === "object" && !Array.isArray(value);
130
+ const isDi = (node) => DI_TYPES.has(String(node.type));
131
+ const indexNodes = (value, elements, di) => {
132
+ if (Array.isArray(value))
133
+ value.forEach((item) => indexNodes(item, elements, di));
134
+ else if (isNode(value)) {
135
+ if (typeof value.id === "string")
136
+ (isDi(value) ? di : elements).set(value.id, value);
137
+ for (const [key, child] of Object.entries(value))
138
+ if (key !== "@context")
139
+ indexNodes(child, elements, di);
140
+ }
141
+ };
142
+ const ownProperties = (node) => {
143
+ const strip = (value) => {
144
+ if (Array.isArray(value))
145
+ return value.map(strip).filter((item) => item !== undefined);
146
+ if (isNode(value))
147
+ return typeof value.id === "string" ? undefined : Object.fromEntries(Object.entries(value).map(([k, v]) => [k, strip(v)]));
148
+ return value;
149
+ };
150
+ return JSON.stringify(Object.fromEntries(Object.entries(node).filter(([key]) => key !== "id").map(([key, value]) => [key, strip(value)]).sort(([a], [b]) => a.localeCompare(b))));
151
+ };
152
+ const modificationContext = (file) => {
153
+ const evalsDir = dirname(dirname(file));
154
+ const prompt = readFileSync(join(evalsDir, "prompts", `${basename(file, ".jsonld")}.md`), "utf8");
155
+ const model = prompt.match(MODEL_LINE)?.[1];
156
+ if (!model)
157
+ throw new Error("prompt has no Model line");
158
+ const allowedText = prompt.match(ALLOWED_LINE)?.[1];
159
+ if (allowedText === undefined)
160
+ throw new Error("prompt has no Allowed-changes line");
161
+ const allowed = new Set(allowedText.split(",").map((id) => id.trim()).filter(Boolean));
162
+ const before = { elements: new Map(), di: new Map() };
163
+ const after = { elements: new Map(), di: new Map() };
164
+ indexNodes(loadDocument(resolve(dirname(evalsDir), model)), before.elements, before.di);
165
+ indexNodes(JSON.parse(readFileSync(file, "utf8")), after.elements, after.di);
166
+ return { allowed, before, after };
167
+ };
168
+ const unchangedElements = {
169
+ name: "unchanged-elements",
170
+ run: async (file) => {
171
+ const { allowed, before, after } = modificationContext(file);
172
+ const failures = [];
173
+ for (const [id, node] of before.elements) {
174
+ if (allowed.has(id))
175
+ continue;
176
+ const next = after.elements.get(id);
177
+ if (!next)
178
+ failures.push(`element "${id}" was removed`);
179
+ else if (ownProperties(node) !== ownProperties(next))
180
+ failures.push(`element "${id}" changed`);
181
+ }
182
+ for (const id of after.elements.keys())
183
+ if (!before.elements.has(id) && !allowed.has(id))
184
+ failures.push(`element "${id}" was added`);
185
+ return failures;
186
+ },
187
+ };
188
+ const unchangedDi = {
189
+ name: "unchanged-di",
190
+ run: async (file) => {
191
+ const { allowed, before, after } = modificationContext(file);
192
+ const failures = [];
193
+ for (const [id, node] of before.di) {
194
+ if (node.type !== "BPMNShape" && node.type !== "BPMNEdge")
195
+ continue;
196
+ const next = after.di.get(id);
197
+ const element = String(node.bpmnElement);
198
+ if (!next) {
199
+ if (!allowed.has(element))
200
+ failures.push(`diagram element "${id}" was removed`);
201
+ continue;
202
+ }
203
+ const key = node.type === "BPMNShape" ? "bounds" : "waypoints";
204
+ if (JSON.stringify(node[key]) !== JSON.stringify(next[key]))
205
+ failures.push(`diagram element "${id}" changed its ${key}`);
206
+ }
207
+ return failures;
208
+ },
209
+ };
210
+ export const MODIFICATION_CHECKS = [unchangedElements, unchangedDi];
211
+ const loadExpected = (file) => {
212
+ const expectFile = join(dirname(file), `${basename(file, extname(file))}.expect.json`);
213
+ return existsSync(expectFile) ? JSON.parse(readFileSync(expectFile, "utf8")) : {};
214
+ };
215
+ export const evaluateFile = async (file, checks = CHECKS) => {
216
+ const failures = [];
217
+ const expected = loadExpected(file);
218
+ for (const [name, messages] of Object.entries(expected))
219
+ for (const message of messages)
220
+ if (!message.startsWith(WARNING_PREFIX))
221
+ failures.push({ check: name, message: `error findings cannot be recorded as expected: ${message}` });
222
+ for (const check of checks) {
223
+ try {
224
+ const remaining = [...(expected[check.name] ?? [])];
225
+ for (const message of await check.run(file)) {
226
+ const index = remaining.indexOf(message);
227
+ if (index >= 0)
228
+ remaining.splice(index, 1);
229
+ else
230
+ failures.push({ check: check.name, message });
231
+ }
232
+ for (const message of remaining)
233
+ failures.push({ check: check.name, message: `expected finding missing: ${message}` });
234
+ }
235
+ catch (error) {
236
+ failures.push({ check: check.name, message: error.message });
237
+ }
238
+ }
239
+ return { file, failures };
240
+ };
241
+ export const evaluateOutputs = async (dir, checks = CHECKS) => Promise.all(readdirSync(dir)
242
+ .filter((f) => f.endsWith(".jsonld"))
243
+ .sort()
244
+ .map((f) => evaluateFile(join(dir, f), checks)));
245
+ export const evaluateExplanations = async (dir, checks = EXPLANATION_CHECKS) => Promise.all(readdirSync(dir)
246
+ .filter((f) => f.startsWith("explain-") && f.endsWith(".md"))
247
+ .sort()
248
+ .map((f) => evaluateFile(join(dir, f), checks)));
249
+ export const evaluateModifications = async (dir, checks = MODIFICATION_CHECKS) => Promise.all(readdirSync(dir)
250
+ .filter((f) => (f.startsWith("modify-") || f.startsWith("refine-")) && f.endsWith(".jsonld"))
251
+ .sort()
252
+ .map((f) => evaluateFile(join(dir, f), checks)));
@@ -0,0 +1,10 @@
1
+ export declare const EXPLAIN_PREFIX = "explain-";
2
+ export declare const outputExtension: (name: string) => string;
3
+ export type Generator = (prompt: string) => Promise<string>;
4
+ export interface RegenerationEntry {
5
+ name: string;
6
+ status: "unchanged" | "changed" | "new";
7
+ diff: string;
8
+ }
9
+ export declare const lineDiff: (before: string, after: string) => string;
10
+ export declare const regenerate: (dir: string, generate: Generator) => Promise<RegenerationEntry[]>;
@@ -0,0 +1,48 @@
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ export const EXPLAIN_PREFIX = "explain-";
4
+ export const outputExtension = (name) => (name.startsWith(EXPLAIN_PREFIX) ? ".md" : ".jsonld");
5
+ export const lineDiff = (before, after) => {
6
+ const a = before.split("\n");
7
+ const b = after.split("\n");
8
+ const lcs = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
9
+ for (let i = a.length - 1; i >= 0; i--)
10
+ for (let j = b.length - 1; j >= 0; j--)
11
+ lcs[i][j] = a[i] === b[j] ? lcs[i + 1][j + 1] + 1 : Math.max(lcs[i + 1][j], lcs[i][j + 1]);
12
+ const lines = [];
13
+ let i = 0;
14
+ let j = 0;
15
+ while (i < a.length && j < b.length) {
16
+ if (a[i] === b[j])
17
+ (i++, j++);
18
+ else if (lcs[i + 1][j] >= lcs[i][j + 1])
19
+ lines.push(`-${a[i++]}`);
20
+ else
21
+ lines.push(`+${b[j++]}`);
22
+ }
23
+ while (i < a.length)
24
+ lines.push(`-${a[i++]}`);
25
+ while (j < b.length)
26
+ lines.push(`+${b[j++]}`);
27
+ return lines.join("\n");
28
+ };
29
+ export const regenerate = async (dir, generate) => {
30
+ const outputs = join(dir, "outputs");
31
+ mkdirSync(outputs, { recursive: true });
32
+ const report = [];
33
+ for (const file of readdirSync(join(dir, "prompts")).filter((f) => f.endsWith(".md")).sort()) {
34
+ const name = file.replace(/\.md$/, "");
35
+ const target = join(outputs, `${name}${outputExtension(name)}`);
36
+ const generated = await generate(readFileSync(join(dir, "prompts", file), "utf8"));
37
+ const existed = existsSync(target);
38
+ const previous = existed ? readFileSync(target, "utf8") : "";
39
+ writeFileSync(target, generated);
40
+ if (!existed)
41
+ report.push({ name, status: "new", diff: lineDiff("", generated) });
42
+ else if (previous === generated)
43
+ report.push({ name, status: "unchanged", diff: "" });
44
+ else
45
+ report.push({ name, status: "changed", diff: lineDiff(previous, generated) });
46
+ }
47
+ return report;
48
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { regenerate } from "./eval/regenerate.js";
3
+ import { ROOT } from "./paths.js";
4
+ const dir = process.argv[2] ?? `${ROOT}skills/bpmn-ld/evals`;
5
+ const command = process.env.BPMN_LD_EVAL_GENERATOR ?? "claude -p";
6
+ const generate = async (prompt) => {
7
+ const run = spawnSync(command, { input: prompt, encoding: "utf8", shell: true, maxBuffer: 64 * 1024 * 1024 });
8
+ if (run.status !== 0)
9
+ throw new Error(`generator "${command}" failed: ${run.stderr}`);
10
+ return run.stdout;
11
+ };
12
+ for (const entry of await regenerate(dir, generate)) {
13
+ console.log(`${entry.status.padEnd(9)}${entry.name}`);
14
+ if (entry.diff)
15
+ console.log(entry.diff);
16
+ }