@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,13 @@
1
+ export const compensationAssociationRule = {
2
+ id: "compensation-activity-without-association",
3
+ severity: "warning",
4
+ check: (scope) => scope.nodes
5
+ .filter((node) => node.forCompensation)
6
+ .filter((node) => !scope.associations.some((association) => association.target === node.id && scope.nodes.some((source) => source.id === association.source && source.compensationBoundary)))
7
+ .map((node) => ({
8
+ rule: "compensation-activity-without-association",
9
+ severity: "warning",
10
+ node: node.id,
11
+ message: "compensation activity is not the target of an association from a compensation boundary event, so no compensation event triggers it",
12
+ })),
13
+ };
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "./rule.ts";
2
+ export declare const deadEndRule: Rule;
@@ -0,0 +1,13 @@
1
+ import { hasType } from "../scope-graph.js";
2
+ export const deadEndRule = {
3
+ id: "node-dead-end",
4
+ severity: "warning",
5
+ check: (scope) => {
6
+ if (scope.adHoc)
7
+ return [];
8
+ const sources = new Set(scope.flows.map((flow) => flow.source));
9
+ return scope.nodes
10
+ .filter((node) => !node.exempt && !hasType(node, "EndEvent") && !sources.has(node.id))
11
+ .map((node) => ({ rule: "node-dead-end", severity: "warning", node: node.id, message: "flow node has no outgoing sequence flow and is not an end event" }));
12
+ },
13
+ };
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "./rule.ts";
2
+ export declare const endEventRule: Rule;
@@ -0,0 +1,8 @@
1
+ import { hasType, isPlaceholder } from "../scope-graph.js";
2
+ export const endEventRule = {
3
+ id: "end-event-missing",
4
+ severity: "warning",
5
+ check: (scope) => isPlaceholder(scope) || scope.adHoc || scope.nodes.some((node) => hasType(node, "EndEvent"))
6
+ ? []
7
+ : [{ rule: "end-event-missing", severity: "warning", node: scope.id, message: `${scope.kind === "process" ? "process" : "sub-process"} has no end event` }],
8
+ };
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "./rule.ts";
2
+ export declare const errorCatchRule: Rule;
@@ -0,0 +1,14 @@
1
+ export const errorCatchRule = {
2
+ id: "error-throw-without-catch",
3
+ severity: "warning",
4
+ check: (scope) => scope.catchesAnyError
5
+ ? []
6
+ : scope.nodes
7
+ .filter((node) => node.errorThrows.some((error) => !scope.caughtErrors.includes(error)))
8
+ .map((node) => ({
9
+ rule: "error-throw-without-catch",
10
+ severity: "warning",
11
+ node: node.id,
12
+ message: "error end event references an error that no boundary event or event sub-process start event in the document catches",
13
+ })),
14
+ };
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "./rule.ts";
2
+ export declare const escalationCatchRule: Rule;
@@ -0,0 +1,14 @@
1
+ export const escalationCatchRule = {
2
+ id: "escalation-throw-without-catch",
3
+ severity: "warning",
4
+ check: (scope) => scope.catchesAnyEscalation
5
+ ? []
6
+ : scope.nodes
7
+ .filter((node) => node.escalationThrows.some((escalation) => !scope.caughtEscalations.includes(escalation)))
8
+ .map((node) => ({
9
+ rule: "escalation-throw-without-catch",
10
+ severity: "warning",
11
+ node: node.id,
12
+ message: "escalation throw event references an escalation that no boundary event or event sub-process start event in the document catches",
13
+ })),
14
+ };
@@ -0,0 +1,3 @@
1
+ import type { Rule } from "./rule.ts";
2
+ export declare const gatewayDivergingRule: Rule;
3
+ export declare const gatewayConvergingRule: Rule;
@@ -0,0 +1,17 @@
1
+ import { bpmn } from "../scope-graph.js";
2
+ export const gatewayDivergingRule = {
3
+ id: "gateway-diverging-too-few-outgoing",
4
+ severity: "error",
5
+ check: (scope) => scope.nodes
6
+ .filter((node) => node.direction === bpmn("Diverging"))
7
+ .filter((node) => scope.flows.filter((flow) => flow.source === node.id).length < 2)
8
+ .map((node) => ({ rule: "gateway-diverging-too-few-outgoing", severity: "error", node: node.id, message: "diverging gateway has fewer than two outgoing flows" })),
9
+ };
10
+ export const gatewayConvergingRule = {
11
+ id: "gateway-converging-too-few-incoming",
12
+ severity: "error",
13
+ check: (scope) => scope.nodes
14
+ .filter((node) => node.direction === bpmn("Converging"))
15
+ .filter((node) => scope.flows.filter((flow) => flow.target === node.id).length < 2)
16
+ .map((node) => ({ rule: "gateway-converging-too-few-incoming", severity: "error", node: node.id, message: "converging gateway has fewer than two incoming flows" })),
17
+ };
@@ -0,0 +1,4 @@
1
+ import { type Severity } from "../findings.ts";
2
+ import type { Rule } from "./rule.ts";
3
+ export declare const rules: Rule[];
4
+ export declare const findingSeverities: () => Record<string, Severity>;
@@ -0,0 +1,21 @@
1
+ import { STRUCTURAL_RULE } from "../findings.js";
2
+ import { adHocCompletionConditionRule } from "./ad-hoc-completion-condition.js";
3
+ import { cannotReachEndRule } from "./cannot-reach-end.js";
4
+ import { compensationAssociationRule } from "./compensation-association.js";
5
+ import { deadEndRule } from "./dead-end.js";
6
+ import { endEventRule } from "./end-event.js";
7
+ import { errorCatchRule } from "./error-catch.js";
8
+ import { escalationCatchRule } from "./escalation-catch.js";
9
+ import { gatewayConvergingRule, gatewayDivergingRule } from "./gateway-cardinality.js";
10
+ import { instantiatingGatewayRule } from "./instantiating-gateway.js";
11
+ import { nonInterruptingStartRule } from "./non-interrupting-start.js";
12
+ import { reachabilityRule } from "./reachability.js";
13
+ import { signalCatchRule } from "./signal-catch.js";
14
+ import { startEventRule } from "./start-event.js";
15
+ import { taskMessageFlowRule } from "./task-message-flow.js";
16
+ import { splitJoinFindingIds, splitJoinPairingRule } from "./split-join-pairing.js";
17
+ export const rules = [startEventRule, endEventRule, gatewayDivergingRule, gatewayConvergingRule, reachabilityRule, deadEndRule, cannotReachEndRule, splitJoinPairingRule, nonInterruptingStartRule, instantiatingGatewayRule, adHocCompletionConditionRule, compensationAssociationRule, taskMessageFlowRule, signalCatchRule, errorCatchRule, escalationCatchRule];
18
+ export const findingSeverities = () => ({
19
+ [STRUCTURAL_RULE]: "error",
20
+ ...Object.fromEntries(rules.flatMap((rule) => (rule === splitJoinPairingRule ? splitJoinFindingIds : [rule.id]).map((id) => [id, rule.severity]))),
21
+ });
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "./rule.ts";
2
+ export declare const instantiatingGatewayRule: Rule;
@@ -0,0 +1,14 @@
1
+ import { hasType } from "../scope-graph.js";
2
+ export const instantiatingGatewayRule = {
3
+ id: "instantiating-gateway-with-incoming-flow",
4
+ severity: "error",
5
+ check: (scope) => scope.nodes
6
+ .filter((node) => node.instantiating && hasType(node, "EventBasedGateway"))
7
+ .filter((node) => scope.flows.some((flow) => flow.target === node.id))
8
+ .map((node) => ({
9
+ rule: "instantiating-gateway-with-incoming-flow",
10
+ severity: "error",
11
+ node: node.id,
12
+ message: "instantiating event-based gateway has an incoming sequence flow, but it starts the process and must have none",
13
+ })),
14
+ };
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "./rule.ts";
2
+ export declare const nonInterruptingStartRule: Rule;
@@ -0,0 +1,14 @@
1
+ export const nonInterruptingStartRule = {
2
+ id: "non-interrupting-start-outside-event-sub-process",
3
+ severity: "warning",
4
+ check: (scope) => scope.eventSubProcess
5
+ ? []
6
+ : scope.nodes
7
+ .filter((node) => node.nonInterrupting)
8
+ .map((node) => ({
9
+ rule: "non-interrupting-start-outside-event-sub-process",
10
+ severity: "warning",
11
+ node: node.id,
12
+ message: "start event is non-interrupting but is not in an event sub-process, so isInterrupting has no effect",
13
+ })),
14
+ };
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "./rule.ts";
2
+ export declare const reachabilityRule: Rule;
@@ -0,0 +1,25 @@
1
+ import { hasType } from "../scope-graph.js";
2
+ export const reachabilityRule = {
3
+ id: "node-unreachable",
4
+ severity: "warning",
5
+ check: (scope) => {
6
+ if (scope.adHoc || !scope.nodes.some((node) => hasType(node, "StartEvent")))
7
+ return [];
8
+ const reached = new Set(scope.nodes.filter((node) => node.exempt || hasType(node, "StartEvent")).map((node) => node.id));
9
+ const queue = [...reached];
10
+ const visit = (id) => {
11
+ if (!reached.has(id)) {
12
+ reached.add(id);
13
+ queue.push(id);
14
+ }
15
+ };
16
+ while (queue.length > 0) {
17
+ const current = queue.shift();
18
+ scope.flows.filter((flow) => flow.source === current && flow.target !== null).forEach((flow) => visit(flow.target));
19
+ scope.nodes.filter((node) => node.attachedTo === current).forEach((node) => visit(node.id));
20
+ }
21
+ return scope.nodes
22
+ .filter((node) => !reached.has(node.id))
23
+ .map((node) => ({ rule: "node-unreachable", severity: "warning", node: node.id, message: "flow node is not reachable from a start event" }));
24
+ },
25
+ };
@@ -0,0 +1,7 @@
1
+ import type { Finding, Severity } from "../findings.ts";
2
+ import type { Scope } from "../scope-graph.ts";
3
+ export interface Rule {
4
+ id: string;
5
+ severity: Severity;
6
+ check: (scope: Scope) => Finding[];
7
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "./rule.ts";
2
+ export declare const signalCatchRule: Rule;
@@ -0,0 +1,12 @@
1
+ export const signalCatchRule = {
2
+ id: "signal-throw-without-catch",
3
+ severity: "warning",
4
+ check: (scope) => scope.nodes
5
+ .filter((node) => node.signalThrows.some((signal) => !scope.caughtSignals.includes(signal)))
6
+ .map((node) => ({
7
+ rule: "signal-throw-without-catch",
8
+ severity: "warning",
9
+ node: node.id,
10
+ message: "signal throw event references a signal that no start, intermediate catch or boundary event in the document catches",
11
+ })),
12
+ };
@@ -0,0 +1,3 @@
1
+ import type { Rule } from "./rule.ts";
2
+ export declare const splitJoinFindingIds: string[];
3
+ export declare const splitJoinPairingRule: Rule;
@@ -0,0 +1,50 @@
1
+ import { bpmn, hasType } from "../scope-graph.js";
2
+ const MISMATCHES = [
3
+ { split: "ExclusiveGateway", join: "ParallelGateway", rule: "split-join-exclusive-to-parallel", message: "exclusive split is merged by a parallel join, which may deadlock" },
4
+ { split: "ParallelGateway", join: "ExclusiveGateway", rule: "split-join-parallel-to-exclusive", message: "parallel split is merged by an exclusive join, which may activate the flow more than once" },
5
+ ];
6
+ export const splitJoinFindingIds = MISMATCHES.map((mismatch) => mismatch.rule);
7
+ const successors = (scope) => {
8
+ const next = new Map();
9
+ for (const flow of scope.flows) {
10
+ if (flow.source !== null && flow.target !== null)
11
+ next.set(flow.source, [...(next.get(flow.source) ?? []), flow.target]);
12
+ }
13
+ return next;
14
+ };
15
+ const reachableFrom = (start, split, next) => {
16
+ const seen = new Set();
17
+ const queue = [start];
18
+ while (queue.length > 0) {
19
+ const id = queue.shift();
20
+ if (id === split || seen.has(id))
21
+ continue;
22
+ seen.add(id);
23
+ queue.push(...(next.get(id) ?? []));
24
+ }
25
+ return seen;
26
+ };
27
+ const nearestJoins = (scope, splitId, next) => {
28
+ const branches = (next.get(splitId) ?? []).map((start) => reachableFrom(start, splitId, next));
29
+ const merged = scope.nodes.filter((node) => node.direction === bpmn("Converging") && branches.filter((branch) => branch.has(node.id)).length >= 2);
30
+ const mergedIds = new Set(merged.map((node) => node.id));
31
+ const downstream = (id) => reachableFrom(id, splitId, next);
32
+ return merged.filter((node) => ![...mergedIds].some((other) => other !== node.id && downstream(other).has(node.id) && !downstream(node.id).has(other)));
33
+ };
34
+ export const splitJoinPairingRule = {
35
+ id: "split-join-pairing-mismatch",
36
+ severity: "warning",
37
+ check: (scope) => {
38
+ const next = successors(scope);
39
+ const found = new Map();
40
+ for (const split of scope.nodes.filter((node) => node.direction === bpmn("Diverging"))) {
41
+ for (const join of nearestJoins(scope, split.id, next)) {
42
+ for (const mismatch of MISMATCHES) {
43
+ if (hasType(split, mismatch.split) && hasType(join, mismatch.join))
44
+ found.set(`${mismatch.rule}|${join.id}`, { rule: mismatch.rule, message: mismatch.message, node: join.id });
45
+ }
46
+ }
47
+ }
48
+ return [...found.values()].map((entry) => ({ ...entry, severity: "warning" }));
49
+ },
50
+ };
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "./rule.ts";
2
+ export declare const startEventRule: Rule;
@@ -0,0 +1,8 @@
1
+ import { hasType, isPlaceholder } from "../scope-graph.js";
2
+ export const startEventRule = {
3
+ id: "start-event-missing",
4
+ severity: "error",
5
+ check: (scope) => isPlaceholder(scope) || scope.adHoc || scope.nodes.some((node) => node.instantiating) || scope.nodes.some((node) => hasType(node, "StartEvent"))
6
+ ? []
7
+ : [{ rule: "start-event-missing", severity: "error", node: scope.id, message: `${scope.kind === "process" ? "process" : "sub-process"} has no start event` }],
8
+ };
@@ -0,0 +1,2 @@
1
+ import type { Rule } from "./rule.ts";
2
+ export declare const taskMessageFlowRule: Rule;
@@ -0,0 +1,19 @@
1
+ import { hasType } from "../scope-graph.js";
2
+ export const taskMessageFlowRule = {
3
+ id: "task-message-without-message-flow",
4
+ severity: "warning",
5
+ check: (scope) => scope.nodes
6
+ .filter((node) => node.messageRef !== null && (hasType(node, "SendTask") || hasType(node, "ReceiveTask")))
7
+ .filter((node) => {
8
+ const sends = hasType(node, "SendTask");
9
+ return !scope.messageFlows.some((messageFlow) => (sends ? messageFlow.source : messageFlow.target) === node.id && (messageFlow.messageRef === null || messageFlow.messageRef === node.messageRef));
10
+ })
11
+ .map((node) => ({
12
+ rule: "task-message-without-message-flow",
13
+ severity: "warning",
14
+ node: node.id,
15
+ message: hasType(node, "SendTask")
16
+ ? "send task references a message but no message flow that leaves it carries that message"
17
+ : "receive task references a message but no message flow that enters it carries that message",
18
+ })),
19
+ };
@@ -0,0 +1,56 @@
1
+ import type { Store } from "n3";
2
+ import type { Node } from "../validate.ts";
3
+ export interface FlowNode {
4
+ id: string;
5
+ types: string[];
6
+ attachedTo: string | null;
7
+ direction: string | null;
8
+ exempt: boolean;
9
+ forCompensation: boolean;
10
+ compensationBoundary: boolean;
11
+ nonInterrupting: boolean;
12
+ instantiating: boolean;
13
+ messageRef: string | null;
14
+ signalThrows: string[];
15
+ errorThrows: string[];
16
+ escalationThrows: string[];
17
+ }
18
+ export interface MessageFlow {
19
+ id: string;
20
+ source: string | null;
21
+ target: string | null;
22
+ messageRef: string | null;
23
+ }
24
+ export interface SequenceFlow {
25
+ id: string;
26
+ source: string | null;
27
+ target: string | null;
28
+ }
29
+ export interface Association {
30
+ id: string;
31
+ source: string | null;
32
+ target: string | null;
33
+ }
34
+ export type ScopeKind = "process" | "subProcess";
35
+ export interface Scope {
36
+ id: string;
37
+ kind: ScopeKind;
38
+ parent: string | null;
39
+ eventSubProcess: boolean;
40
+ adHoc: boolean;
41
+ completionCondition: boolean;
42
+ participant: string | null;
43
+ nodes: FlowNode[];
44
+ flows: SequenceFlow[];
45
+ associations: Association[];
46
+ messageFlows: MessageFlow[];
47
+ caughtSignals: string[];
48
+ caughtErrors: string[];
49
+ catchesAnyError: boolean;
50
+ caughtEscalations: string[];
51
+ catchesAnyEscalation: boolean;
52
+ }
53
+ export declare const isPlaceholder: (scope: Scope) => boolean;
54
+ export declare const bpmn: (name: string) => string;
55
+ export declare const hasType: (node: FlowNode, name: string) => boolean;
56
+ export declare const buildScopes: (expanded: Node[], store?: Store) => Scope[];
@@ -0,0 +1,89 @@
1
+ import { BPMN_NAMESPACE } from "../namespace.js";
2
+ import { isSubClassOf, loadOntology } from "../ontology.js";
3
+ export const isPlaceholder = (scope) => scope.kind === "subProcess" && scope.nodes.length === 0;
4
+ export const bpmn = (name) => `${BPMN_NAMESPACE}${name}`;
5
+ export const hasType = (node, name) => node.types.includes(bpmn(name));
6
+ const valuesOf = (value) => value.flatMap((v) => ("@list" in v ? v["@list"] : [v]));
7
+ const indexNode = (node, index) => {
8
+ const id = node["@id"];
9
+ const entry = id === undefined ? undefined : (index.get(id) ?? { types: new Set(), properties: new Map() });
10
+ if (id !== undefined && entry !== undefined)
11
+ index.set(id, entry);
12
+ for (const [key, value] of Object.entries(node)) {
13
+ if (key === "@type")
14
+ value.forEach((t) => entry?.types.add(t));
15
+ else if (!key.startsWith("@")) {
16
+ const members = valuesOf(value);
17
+ entry?.properties.set(key, [...(entry.properties.get(key) ?? []), ...members]);
18
+ members.filter((m) => !("@value" in m)).forEach((m) => indexNode(m, index));
19
+ }
20
+ }
21
+ };
22
+ const hasEventDefinition = (entry, definition) => (entry.properties.get(bpmn("eventDefinitions")) ?? []).some((candidate) => (candidate["@type"] ?? []).includes(bpmn(definition)));
23
+ const definitionsOf = (entry, definition) => (entry.properties.get(bpmn("eventDefinitions")) ?? []).filter((candidate) => (candidate["@type"] ?? []).includes(bpmn(definition)));
24
+ const definitionRefs = (definition, property) => (definition[bpmn(property)] ?? []).flatMap((ref) => (typeof ref["@id"] === "string" ? [ref["@id"]] : []));
25
+ const signalRefsOf = (entry) => definitionsOf(entry, "SignalEventDefinition").flatMap((definition) => definitionRefs(definition, "signalRef"));
26
+ const errorRefsOf = (entry) => definitionsOf(entry, "ErrorEventDefinition").flatMap((definition) => definitionRefs(definition, "errorRef"));
27
+ const escalationRefsOf = (entry) => definitionsOf(entry, "EscalationEventDefinition").flatMap((definition) => definitionRefs(definition, "escalationRef"));
28
+ const EXEMPT_EVENT_DEFINITIONS = [bpmn("LinkEventDefinition"), bpmn("SignalEventDefinition")];
29
+ const flagOf = (entry, property) => (entry.properties.get(bpmn(property)) ?? []).some((m) => m["@value"] === true);
30
+ const refsOf = (entry, property) => (entry?.properties.get(bpmn(property)) ?? []).flatMap((m) => (typeof m["@id"] === "string" ? [m["@id"]] : []));
31
+ export const buildScopes = (expanded, store = loadOntology()) => {
32
+ const index = new Map();
33
+ expanded.forEach((node) => indexNode(node, index));
34
+ const isA = (entry, name) => [...entry.types].some((t) => isSubClassOf(store, t, bpmn(name)));
35
+ const isExempt = (member) => flagOf(member, "isForCompensation") ||
36
+ flagOf(member, "triggeredByEvent") ||
37
+ (isA(member, "BoundaryEvent") && hasEventDefinition(member, "CompensateEventDefinition")) ||
38
+ (isA(member, "CatchEvent") &&
39
+ !isA(member, "BoundaryEvent") &&
40
+ (member.properties.get(bpmn("eventDefinitions")) ?? []).some((definition) => (definition["@type"] ?? []).some((t) => EXEMPT_EVENT_DEFINITIONS.includes(t))));
41
+ const participants = new Map();
42
+ for (const [id, entry] of index) {
43
+ if (isA(entry, "Participant"))
44
+ refsOf(entry, "processRef").forEach((processId) => participants.set(processId, id));
45
+ }
46
+ const messageFlows = [];
47
+ for (const [id, entry] of index) {
48
+ if (isA(entry, "MessageFlow")) {
49
+ messageFlows.push({ id, source: refsOf(entry, "sourceRef")[0] ?? null, target: refsOf(entry, "targetRef")[0] ?? null, messageRef: refsOf(entry, "messageRef")[0] ?? null });
50
+ }
51
+ }
52
+ const caughtSignals = [...new Set([...index.values()].filter((entry) => isA(entry, "CatchEvent")).flatMap(signalRefsOf))];
53
+ const catchEvents = [...index.values()].filter((entry) => isA(entry, "BoundaryEvent") || isA(entry, "StartEvent"));
54
+ const caughtErrors = [...new Set(catchEvents.flatMap(errorRefsOf))];
55
+ const catchesAnyError = catchEvents.some((entry) => definitionsOf(entry, "ErrorEventDefinition").some((definition) => definitionRefs(definition, "errorRef").length === 0));
56
+ const caughtEscalations = [...new Set(catchEvents.flatMap(escalationRefsOf))];
57
+ const catchesAnyEscalation = catchEvents.some((entry) => definitionsOf(entry, "EscalationEventDefinition").some((definition) => definitionRefs(definition, "escalationRef").length === 0));
58
+ const scopes = [];
59
+ const addScope = (id, entry, kind, parent) => {
60
+ const scope = { id, kind, parent, eventSubProcess: flagOf(entry, "triggeredByEvent"), adHoc: isA(entry, "AdHocSubProcess"), completionCondition: (entry.properties.get(bpmn("completionCondition")) ?? []).length > 0, participant: participants.get(id) ?? null, nodes: [], flows: [], associations: [], messageFlows, caughtSignals, caughtErrors, catchesAnyError, caughtEscalations, catchesAnyEscalation };
61
+ scopes.push(scope);
62
+ const subProcesses = [];
63
+ for (const artifactId of refsOf(entry, "artifacts")) {
64
+ const artifact = index.get(artifactId);
65
+ if (artifact !== undefined && isA(artifact, "Association")) {
66
+ scope.associations.push({ id: artifactId, source: refsOf(artifact, "sourceRef")[0] ?? null, target: refsOf(artifact, "targetRef")[0] ?? null });
67
+ }
68
+ }
69
+ for (const memberId of refsOf(entry, "flowElements")) {
70
+ const member = index.get(memberId);
71
+ if (member === undefined)
72
+ continue;
73
+ if (isA(member, "SequenceFlow")) {
74
+ scope.flows.push({ id: memberId, source: refsOf(member, "sourceRef")[0] ?? null, target: refsOf(member, "targetRef")[0] ?? null });
75
+ }
76
+ else if (isA(member, "FlowNode")) {
77
+ scope.nodes.push({ id: memberId, types: [...member.types], attachedTo: refsOf(member, "attachedToRef")[0] ?? null, direction: refsOf(member, "gatewayDirection")[0] ?? null, exempt: isExempt(member), forCompensation: flagOf(member, "isForCompensation"), compensationBoundary: isA(member, "BoundaryEvent") && hasEventDefinition(member, "CompensateEventDefinition"), nonInterrupting: isA(member, "StartEvent") && (member.properties.get(bpmn("isInterrupting")) ?? []).some((m) => m["@value"] === false), instantiating: flagOf(member, "instantiate"), messageRef: refsOf(member, "messageRef")[0] ?? null, signalThrows: isA(member, "ThrowEvent") ? signalRefsOf(member) : [], errorThrows: isA(member, "EndEvent") ? errorRefsOf(member) : [], escalationThrows: isA(member, "ThrowEvent") ? escalationRefsOf(member) : [] });
78
+ if (isA(member, "SubProcess"))
79
+ subProcesses.push([memberId, member]);
80
+ }
81
+ }
82
+ subProcesses.forEach(([subId, sub]) => addScope(subId, sub, "subProcess", id));
83
+ };
84
+ for (const [id, entry] of index) {
85
+ if (isA(entry, "Process"))
86
+ addScope(id, entry, "process", null);
87
+ }
88
+ return scopes;
89
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { runAnalyze } from "./analyze-run.js";
2
+ const { exitCode, stdout, stderr } = await runAnalyze(process.argv.slice(2));
3
+ process.stdout.write(stdout);
4
+ process.stderr.write(stderr);
5
+ process.exitCode = exitCode;
@@ -0,0 +1,12 @@
1
+ import type { Finding } from "./analyze/findings.ts";
2
+ export declare const EXIT_OK = 0;
3
+ export declare const EXIT_FINDINGS = 1;
4
+ export declare const EXIT_INPUT = 2;
5
+ export declare const exitCodeFor: (findings: Finding[]) => number;
6
+ export interface CliResult {
7
+ exitCode: number;
8
+ stdout: string;
9
+ stderr: string;
10
+ }
11
+ export declare const USAGE = "Usage: analyze <file.jsonld> [options]\n\nAnalyzes a BPMN-LD document and reports process-level findings.\n\nOptions:\n --format <text|json> Output format (default: text)\n -h, --help Show this help\n\nExit codes:\n 0 No findings, or warnings only\n 1 At least one error finding\n 2 Usage error or unusable input (missing file, invalid JSON)\n";
12
+ export declare const runAnalyze: (argv: string[]) => Promise<CliResult>;
@@ -0,0 +1,85 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ import { analyze } from "./analyze/analyze.js";
5
+ import { formatJson, formatText } from "./analyze/report.js";
6
+ export const EXIT_OK = 0;
7
+ export const EXIT_FINDINGS = 1;
8
+ export const EXIT_INPUT = 2;
9
+ export const exitCodeFor = (findings) => (findings.some((f) => f.severity === "error") ? EXIT_FINDINGS : EXIT_OK);
10
+ export const USAGE = `Usage: analyze <file.jsonld> [options]
11
+
12
+ Analyzes a BPMN-LD document and reports process-level findings.
13
+
14
+ Options:
15
+ --format <text|json> Output format (default: text)
16
+ -h, --help Show this help
17
+
18
+ Exit codes:
19
+ 0 No findings, or warnings only
20
+ 1 At least one error finding
21
+ 2 Usage error or unusable input (missing file, invalid JSON)
22
+ `;
23
+ class UsageError extends Error {
24
+ }
25
+ const parseArgs = (argv) => {
26
+ const options = { input: "", format: "text" };
27
+ let input;
28
+ for (let i = 0; i < argv.length; i++) {
29
+ const arg = argv[i];
30
+ if (arg === "-h" || arg === "--help")
31
+ return "help";
32
+ else if (arg === "--format") {
33
+ const value = argv[++i];
34
+ if (value !== "text" && value !== "json")
35
+ throw new UsageError("--format requires text or json");
36
+ options.format = value;
37
+ }
38
+ else if (arg.startsWith("-"))
39
+ throw new UsageError(`unknown option ${arg}`);
40
+ else if (input !== undefined)
41
+ throw new UsageError(`unexpected extra argument ${arg}`);
42
+ else
43
+ input = arg;
44
+ }
45
+ if (input === undefined)
46
+ throw new UsageError("missing input file");
47
+ options.input = input;
48
+ return options;
49
+ };
50
+ const failure = (message, usage = false) => ({
51
+ exitCode: EXIT_INPUT,
52
+ stdout: "",
53
+ stderr: `error: ${message}\n${usage ? `\n${USAGE}` : ""}`,
54
+ });
55
+ export const runAnalyze = async (argv) => {
56
+ let options;
57
+ try {
58
+ options = parseArgs(argv);
59
+ }
60
+ catch (error) {
61
+ if (error instanceof UsageError)
62
+ return failure(error.message, true);
63
+ throw error;
64
+ }
65
+ if (options === "help")
66
+ return { exitCode: EXIT_OK, stdout: USAGE, stderr: "" };
67
+ let text;
68
+ try {
69
+ text = readFileSync(options.input, "utf8");
70
+ }
71
+ catch (error) {
72
+ const reason = error.code === "ENOENT" ? "no such file" : error.message;
73
+ return failure(`cannot read ${options.input}: ${reason}`);
74
+ }
75
+ let document;
76
+ try {
77
+ document = JSON.parse(text);
78
+ }
79
+ catch (error) {
80
+ return failure(`${options.input} is not valid JSON: ${error.message}`);
81
+ }
82
+ const findings = await analyze(document, pathToFileURL(resolve(options.input)).href);
83
+ const stdout = options.format === "json" ? formatJson(findings) : formatText(findings);
84
+ return { exitCode: exitCodeFor(findings), stdout, stderr: "" };
85
+ };
@@ -0,0 +1 @@
1
+ export {};