@uipath/case-tool 1.205.0-preview.281 → 1.206.0-preview.282
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/THIRD-PARTY-NOTICES.md +68 -7
- package/dist/applicationinsights-web-8m26566f.js +17209 -0
- package/dist/{authoring-kqh34zzm.js → authoring-vg90pqrn.js} +488 -213
- package/dist/bindings-v2-1da35xsc.js +27 -0
- package/dist/{browser-strategy-6atc7v8s.js → browser-strategy-627rrg7z.js} +6 -7
- package/dist/{business-rule-export-ey588rv7.js → business-rule-export-jv60r9jf.js} +3 -7
- package/dist/carry-forward-sfdnta6q.js +52 -0
- package/dist/{case-api-sme7esce.js → case-api-by95rf40.js} +38 -37
- package/dist/{case-bindings-service-cht20gqt.js → case-bindings-service-pp3c9g1e.js} +93 -45
- package/dist/{case-debug-solution-projects-qqd3h10v.js → case-debug-solution-projects-je7h4hmq.js} +3 -7
- package/dist/{case-debug-studio-web-service-nqzmwbr5.js → case-debug-studio-web-service-d83h3atd.js} +48 -45
- package/dist/{case-described-io-service-dx1f4nxe.js → case-described-io-service-tsm8r189.js} +33 -15
- package/dist/case-event-trigger-bindings-qbs8ywf5.js +17 -0
- package/dist/{case-format-service-6aew6tqc.js → case-format-service-3sab2297.js} +9 -10
- package/dist/{case-pack-service-pqq8wnfd.js → case-pack-service-txnv711n.js} +47 -42
- package/dist/case-schema-version-cw0h2nrv.js +18 -0
- package/dist/case-shape-reader-xaxmrd40.js +14 -0
- package/dist/{case-splice-service-s5gccctq.js → case-splice-service-6y35w83e.js} +139 -51
- package/dist/{case-validate-service-vq38va33.js → case-validate-service-hz0j90we.js} +234 -170
- package/dist/connector-enrichment-service-sjjmb9dy.js +56 -0
- package/dist/connector-library-1yw5r47w.js +99 -0
- package/dist/connector-lookup-service-yerhg9cd.js +20 -0
- package/dist/connector-task-shape-bgywpbqc.js +13 -0
- package/dist/{de-WLW6KWOJ-2qnsqdtw.js → de-2BEKRUMY-x4dx0nn9.js} +190 -10
- package/dist/{de-UZI7FJDO-mwvc4cxe.js → de-AI7ML6JM-km47f8k4.js} +20 -5
- package/dist/debug-service-h4jxbn1r.js +56 -0
- package/dist/{embedded-file-reader-r66242ey.js → embedded-file-reader-0228j82a.js} +1 -4
- package/dist/en-LAVDF7IQ-yzy20n6d.js +254 -0
- package/dist/en-X6T4SP2Y-xm47mf18.js +2752 -0
- package/dist/{enrichment-service-hv7ff88p.js → enrichment-service-wpprec7k.js} +36 -37
- package/dist/{es-5THCNRFN-tqwyrdgy.js → es-7ZTQTGKN-11sj0yjx.js} +190 -10
- package/dist/{es-MX-5RELIXKI-9jhqtddc.js → es-MX-7KJZPEQN-1fq0a9jr.js} +20 -5
- package/dist/{es-MX-TWZBRAXK-gqd9swzw.js → es-MX-IHGRIEWH-g63p0gb4.js} +191 -11
- package/dist/{es-4UBUTCQA-m1ytpyx2.js → es-SPNKTD5Y-vw5cbgv5.js} +20 -5
- package/dist/filter-engine-dh9s90gb.js +10 -0
- package/dist/first-party-service-ahcc6xc6.js +8 -0
- package/dist/{fr-CERONSRC-edpxhbmb.js → fr-7ZDL4MVO-pd27vwh6.js} +191 -11
- package/dist/{fr-VBM5WRC2-63z7w4mh.js → fr-AXJGUT53-hdg3pmd4.js} +20 -5
- package/dist/index-21kns9qg.js +10 -0
- package/dist/{index-fyc0k5dd.js → index-5pzp2qeb.js} +5350 -3271
- package/dist/{connector-task-spec-pzfzmfr5.js → index-9f8naxbk.js} +320 -144
- package/dist/{index-5dxh9z60.js → index-a7y7etmy.js} +6 -5
- package/dist/{index-ekwtgx7y.js → index-ec115v05.js} +309 -287
- package/dist/index-j9t2k52h.js +35 -0
- package/dist/index-myf7b4a5.js +26 -0
- package/dist/index-nj3axvya.js +159 -0
- package/dist/{index-h8cph72k.js → index-nzr4mx5p.js} +2 -22
- package/dist/index-pg9wmyrf.js +36 -0
- package/dist/index-z2qk2qn6.js +24 -0
- package/dist/index.js +19 -25
- package/dist/index.umd.min-ttg1qc0x.js +7814 -0
- package/dist/{init-entry-points-hhpv3e3f.js → init-entry-points-pc128f7z.js} +22 -35
- package/dist/input-reader-9ym8mkbc.js +10 -0
- package/dist/{ja-43QL7CGO-c3q8gre9.js → ja-C7GZV6YP-hvtbngr5.js} +20 -5
- package/dist/{ja-U2MCNHGT-gz0ap1jk.js → ja-LGAM5HKR-sgrtjqjh.js} +190 -10
- package/dist/{jmespath-e4a59c8w.js → jmespath-wwym30dp.js} +1 -1
- package/dist/{js-yaml-87cdx6bm.js → js-yaml-a2bk27p6.js} +1 -3
- package/dist/known-types-gak9by1w.js +11 -0
- package/dist/{ko-XAUYQWDR-zp81gjp4.js → ko-7B4SUZB7-c8wr20sc.js} +190 -10
- package/dist/{ko-TLL6Z6CD-wrt3n8a9.js → ko-Q24HSSL4-5vn55pte.js} +20 -5
- package/dist/localSolutionDiscovery-48d1bnkj.js +15 -0
- package/dist/{migration-diagnosis-ns5cw4e3.js → migration-diagnosis-myqqgwj0.js} +1 -4
- package/dist/{node-strategy-v442hwgd.js → node-strategy-2mqzg36t.js} +15 -18
- package/dist/node-sync-service-y8b5w1rd.js +56 -0
- package/dist/{out-target-wjgmna0r.js → out-target-eqsttq1k.js} +18 -22
- package/dist/{packager-tool-f8vwy92w.js → packager-tool-0te1tyhf.js} +186 -16
- package/dist/{packager-tool-ydyvtknp.js → packager-tool-0w4fsdzv.js} +44 -20
- package/dist/{packager-tool-8ftat1vn.js → packager-tool-17b466mr.js} +59158 -59666
- package/dist/packager-tool-1nfdj0zm.js +425 -0
- package/dist/packager-tool-1wgav5ma.js +139 -0
- package/dist/{packager-tool-b1nw50ev.js → packager-tool-265h946t.js} +256 -486
- package/dist/{packager-tool-nj4seavy.js → packager-tool-35c9npcy.js} +26 -7
- package/dist/{packager-tool-r0x3qh59.js → packager-tool-388vyja1.js} +185 -29
- package/dist/packager-tool-3m8482sw.js +78 -0
- package/dist/packager-tool-3r23qsca.js +4832 -0
- package/dist/packager-tool-3xehbdyd.js +1302 -0
- package/dist/packager-tool-4avchhz8.js +539 -0
- package/dist/{packager-tool-9kvkzc05.js → packager-tool-5w0gjcck.js} +4 -4
- package/dist/packager-tool-68648y3j.js +6 -0
- package/dist/{packager-tool-1yh8g550.js → packager-tool-6an893wm.js} +12 -3
- package/dist/{packager-tool-aagnvyba.js → packager-tool-76zhn5pa.js} +131 -432
- package/dist/packager-tool-7x1tmb8a.js +46 -0
- package/dist/{packager-tool-kyrm4e5m.js → packager-tool-9ar5829n.js} +781 -741
- package/dist/{packager-tool-4p11fvf4.js → packager-tool-b2q8m9pq.js} +10 -13
- package/dist/packager-tool-bg3tdpva.js +27 -0
- package/dist/{packager-tool-wr12ndwk.js → packager-tool-ccejfs0r.js} +252 -468
- package/dist/{packager-tool-yn33pw57.js → packager-tool-dn9d4ywm.js} +246 -225
- package/dist/{packager-tool-kdhey8gz.js → packager-tool-e02jkmrq.js} +41 -29
- package/dist/{packager-tool-sq931c23.js → packager-tool-e1tx5mqg.js} +1 -1
- package/dist/{packager-tool-qn3zkrrc.js → packager-tool-ehjw672b.js} +32 -15
- package/dist/packager-tool-gy4yjqhf.js +178 -0
- package/dist/packager-tool-h2ar8xf8.js +222 -0
- package/dist/{packager-tool-v2kcgjbf.js → packager-tool-j6nfje7t.js} +5 -4
- package/dist/packager-tool-k0ar6cn7.js +295 -0
- package/dist/packager-tool-kz7zz79p.js +71 -0
- package/dist/{packager-tool-1gwbq864.js → packager-tool-m5384a1s.js} +66 -82
- package/dist/{packager-tool-9e59z4d6.js → packager-tool-me8gd22q.js} +82 -83
- package/dist/packager-tool-mk9fmzdy.js +20 -0
- package/dist/{packager-tool-yfmby4dd.js → packager-tool-mkt1njk2.js} +630 -689
- package/dist/{packager-tool-gdtpsdn7.js → packager-tool-mm1x18d6.js} +223 -28
- package/dist/{packager-tool-mqy97y5f.js → packager-tool-p5gs9xqy.js} +14 -14
- package/dist/{packager-tool-83v804p7.js → packager-tool-psmb758n.js} +3108 -2627
- package/dist/packager-tool-q26hncjr.js +13 -0
- package/dist/packager-tool-q9fnx278.js +77 -0
- package/dist/{packager-tool-ezds6t5e.js → packager-tool-s12s1dp6.js} +11 -11
- package/dist/packager-tool-scy94jyb.js +5424 -0
- package/dist/{packager-tool-yn7r7zq2.js → packager-tool-snesn0bt.js} +177 -96
- package/dist/{packager-tool-d8e2mrkq.js → packager-tool-tgehy324.js} +56 -56
- package/dist/{packager-tool-tqn0j6f9.js → packager-tool-v11fx4dk.js} +7 -7
- package/dist/{packager-tool-67vy1fz3.js → packager-tool-v6rkp2ay.js} +24 -92
- package/dist/{packager-tool-9sd483vs.js → packager-tool-vs7gqa4r.js} +15 -15
- package/dist/packager-tool-vyavrbym.js +488 -0
- package/dist/{packager-tool-b098y4a2.js → packager-tool-wbksf242.js} +97 -97
- package/dist/{packager-tool-2sn7d8fd.js → packager-tool-x74mmjkw.js} +9 -13
- package/dist/{packager-tool-7hw344bt.js → packager-tool-xb16e3tz.js} +5 -5
- package/dist/{packager-tool-a17g0pj1.js → packager-tool-xd1wbvay.js} +10 -8
- package/dist/{packager-tool-1yfw674x.js → packager-tool-xehhk3c3.js} +10 -5
- package/dist/packager-tool-yh8mmj3p.js +123 -0
- package/dist/{packager-tool-hdxtgckw.js → packager-tool-z070caqq.js} +56 -12
- package/dist/{packager-tool-b250k6w4.js → packager-tool-z1fw9a8v.js} +41 -9
- package/dist/{packager-tool-0gctz400.js → packager-tool-z9fmqtm6.js} +4 -4
- package/dist/{packager-tool-mzf4gmph.js → packager-tool-zbx189j3.js} +11 -8
- package/dist/{packager-tool-zf8f9pmm.js → packager-tool-zrbnms9b.js} +3 -4
- package/dist/packager-tool.js +10 -11
- package/dist/prepare-uip-ad7mfs8t.js +92 -0
- package/dist/{project-artifacts-bgyyb0dm.js → project-artifacts-q0c0zghw.js} +2 -6
- package/dist/{pt-BR-USP2T7GD-pedn77m1.js → pt-BR-BIUJXIPU-2sqzhr8y.js} +20 -5
- package/dist/{pt-BR-GNQNAAZ2-xadharv4.js → pt-BR-PHLFHMXZ-n6de4s7z.js} +191 -11
- package/dist/{pt-RMIAKZB4-67enhx3m.js → pt-MHX6JGAX-xfgvdqwg.js} +20 -5
- package/dist/{pt-TO2DARY2-ftcce1bv.js → pt-N7ZTJT4W-d6c67xcn.js} +190 -10
- package/dist/{resolved-check-aqw7568y.js → resolved-check-dz8eqrf6.js} +2 -3
- package/dist/{resourcesSyncService-8ac6ncq2.js → resourcesSyncService-yrhhh62m.js} +81 -141
- package/dist/{ro-GHOBYW2S-mhnqsgaa.js → ro-3RKJNGJZ-xqj14e3d.js} +20 -5
- package/dist/{ro-WUYJV6OW-a8d9qy1n.js → ro-FLNXVYJI-ytrwabg2.js} +190 -10
- package/dist/ru-GSCT4ARZ-c2cfq4h1.js +7 -0
- package/dist/ru-WBLQ5B2U-bf20yr5y.js +7 -0
- package/dist/{rule-group-members-zje7n3kf.js → rule-group-members-p0nv1rmj.js} +1 -3
- package/dist/rule-type-declaration-s2nxpcfy.js +77 -0
- package/dist/{sdd-convert-service-8jpew7vz.js → sdd-convert-service-ffbx00k6.js} +481 -504
- package/dist/{sdd-lookup-fe900sdb.js → sdd-lookup-sah7wj5j.js} +8 -9
- package/dist/{sdd-resolve-service-qdtmxw9b.js → sdd-resolve-service-6nt10vhq.js} +28 -20
- package/dist/{sdd-resolve-siblings-8gf9zjwf.js → sdd-resolve-siblings-ap8j1c8h.js} +11 -14
- package/dist/solution-auth-a1p22hq1.js +11 -0
- package/dist/solution-discovery-r1y550sz.js +33 -0
- package/dist/{strict-case-rules-4tjcaznr.js → strict-case-rules-045kbjvv.js} +16 -28
- package/dist/{strict-entry-points-g2pd4mf4.js → strict-entry-points-40ha9afz.js} +15 -14
- package/dist/strict-sdd-audit-bp7fq2tg.js +26 -0
- package/dist/{task-selector-8bjn35dv.js → task-selector-jt60hdbt.js} +1 -3
- package/dist/{task-type-declaration-7a8fqdvs.js → task-type-declaration-yhczvj05.js} +9 -9
- package/dist/tool.js +18 -24
- package/dist/{tr-M3F5A6AU-bb0tg39m.js → tr-7AIZZWZ6-pdzp9n0p.js} +190 -10
- package/dist/{tr-GDTLECMM-k5fhmq13.js → tr-PQLUUHPC-qsb7btxn.js} +20 -5
- package/dist/{trigger-enrichment-service-agvnhdf7.js → trigger-enrichment-service-z8kxy6qr.js} +53 -57
- package/dist/trigger-entry-point-id-751v48qd.js +14 -0
- package/dist/{zh-CN-C5G4PC7C-ds9gkptg.js → zh-CN-CN2ARPFS-e0p2qrxf.js} +190 -10
- package/dist/{zh-CN-I5D7WHBD-sr5h0a98.js → zh-CN-OHNTHZRI-b0hjkfqn.js} +20 -5
- package/dist/{zh-TW-62HZIG6O-787kdwex.js → zh-TW-7RUFEVFK-qd7myw31.js} +190 -10
- package/dist/{zh-TW-2AVLM723-288pkc1z.js → zh-TW-E4D3Y6ZR-tn3bmzz5.js} +20 -5
- package/package.json +2 -2
- package/dist/canvas-notes-86fxx1t8.js +0 -19
- package/dist/case-event-trigger-bindings-g9ftnm4x.js +0 -16
- package/dist/case-schema-version-h820ne16.js +0 -20
- package/dist/case-shape-reader-dsbraetf.js +0 -13
- package/dist/connector-enrichment-service-6kskfevq.js +0 -65
- package/dist/connector-lookup-service-yyn8kkw7.js +0 -22
- package/dist/connector-task-shape-mpazkf7s.js +0 -14
- package/dist/debug-service-7q98j726.js +0 -59
- package/dist/en-EJCBA3LK-dtpdghgj.js +0 -146
- package/dist/en-M7GQYBV2-5nfmpnfz.js +0 -2018
- package/dist/filter-engine-7ep90g7f.js +0 -11
- package/dist/first-party-service-srxaftkn.js +0 -27
- package/dist/index-0d7qc2gs.js +0 -282
- package/dist/index-jmv5by9z.js +0 -666
- package/dist/index-kmp9fmhb.js +0 -25
- package/dist/index-n9qvnx4x.js +0 -29
- package/dist/index-ngs0a2he.js +0 -15
- package/dist/index-xq6ajhgq.js +0 -157
- package/dist/index.umd.min-fqefd9kn.js +0 -7814
- package/dist/input-reader-c7wz8ra2.js +0 -11
- package/dist/known-types-5b6yrsqn.js +0 -16
- package/dist/localSolutionDiscovery-yyx234ak.js +0 -13
- package/dist/node-sync-service-2ezr3akv.js +0 -57
- package/dist/packager-tool-53w8skv5.js +0 -262
- package/dist/packager-tool-6z1hsm2h.js +0 -143
- package/dist/packager-tool-93mdeqay.js +0 -18
- package/dist/packager-tool-9kf3fqtr.js +0 -23
- package/dist/packager-tool-bfgdz1zh.js +0 -73
- package/dist/packager-tool-e15kqpyh.js +0 -266
- package/dist/packager-tool-gmykn1ef.js +0 -5365
- package/dist/packager-tool-jysvpt8p.js +0 -11
- package/dist/packager-tool-pcvhcn2k.js +0 -2019
- package/dist/packager-tool-rk25wp3c.js +0 -28
- package/dist/packager-tool-sc961w0q.js +0 -16
- package/dist/packager-tool-wekqkmgn.js +0 -265
- package/dist/packager-tool-yc4kw997.js +0 -13
- package/dist/packager-tool-zgpsnxkd.js +0 -13189
- package/dist/ru-GSCT4ARZ-1sa9wbr8.js +0 -10
- package/dist/ru-WBLQ5B2U-s4em88p0.js +0 -10
- package/dist/solution-auth-30yycqtv.js +0 -14
- package/dist/solution-discovery-e9mamp6w.js +0 -38
- package/dist/strict-sdd-audit-q7tb9yj7.js +0 -25
- package/dist/trigger-entry-point-id-yhqwnkx9.js +0 -15
- /package/dist/{packager-tool-5qp9xza1.js → packager-tool-1c30ttpm.js} +0 -0
- /package/dist/{packager-tool-mknccck5.js → packager-tool-1h7abxw6.js} +0 -0
- /package/dist/{packager-tool-9y1w2wds.js → packager-tool-fcektt1g.js} +0 -0
- /package/dist/{packager-tool-k6jbedyc.js → packager-tool-hcdnc420.js} +0 -0
- /package/dist/{packager-tool-7nxppa3d.js → packager-tool-jv5byaww.js} +0 -0
- /package/dist/{packager-tool-qa9gftnj.js → packager-tool-mxgybnmt.js} +0 -0
- /package/dist/{packager-tool-87y6xw3a.js → packager-tool-nfhtwa72.js} +0 -0
- /package/dist/{packager-tool-cv05dwac.js → packager-tool-p0rdmmvk.js} +0 -0
- /package/dist/{packager-tool-21qfsbxn.js → packager-tool-yaertxsw.js} +0 -0
|
@@ -1,49 +1,68 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./packager-tool-
|
|
2
|
+
catchError2
|
|
3
|
+
} from "./packager-tool-psmb758n.js";
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
exactKey,
|
|
7
|
-
indexKey,
|
|
8
|
-
lookupOutput,
|
|
9
|
-
modelKey,
|
|
10
|
-
stageSpellings
|
|
11
|
-
} from "./packager-tool-9kvkzc05.js";
|
|
12
|
-
import {
|
|
13
|
-
outputSourceFor
|
|
14
|
-
} from "./packager-tool-ydyvtknp.js";
|
|
15
|
-
import {
|
|
16
|
-
comparableFolder
|
|
17
|
-
} from "./packager-tool-21qfsbxn.js";
|
|
18
|
-
import {
|
|
19
|
-
cellAt,
|
|
20
|
-
columnField,
|
|
21
|
-
columnIndex,
|
|
22
|
-
fenceOf,
|
|
5
|
+
withoutTrailingSlug,
|
|
23
6
|
field,
|
|
24
|
-
firstTable,
|
|
25
|
-
isBlank,
|
|
26
7
|
isDigit,
|
|
27
8
|
isLetter,
|
|
28
9
|
isLowerLetter,
|
|
29
|
-
|
|
30
|
-
namedValue,
|
|
10
|
+
trimQuotes,
|
|
31
11
|
norm,
|
|
32
|
-
pipeTables,
|
|
33
|
-
splitRowCells,
|
|
34
12
|
stripTicks,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
13
|
+
isBlank,
|
|
14
|
+
namedValue,
|
|
15
|
+
splitRowCells,
|
|
16
|
+
isSeparatorCell,
|
|
17
|
+
firstTable,
|
|
18
|
+
fenceOf,
|
|
19
|
+
pipeTables,
|
|
20
|
+
columnIndex,
|
|
21
|
+
cellAt,
|
|
22
|
+
withoutAnnotation,
|
|
23
|
+
columnField
|
|
24
|
+
} from "./packager-tool-fcektt1g.js";
|
|
25
|
+
import {
|
|
26
|
+
CASE_APP_VERSIONS_WITH_ROLES,
|
|
27
|
+
roleActionKeyOf,
|
|
28
|
+
readCaseRoles
|
|
29
|
+
} from "./packager-tool-1wgav5ma.js";
|
|
30
|
+
import {
|
|
31
|
+
resolvePersonas
|
|
32
|
+
} from "./packager-tool-yh8mmj3p.js";
|
|
33
|
+
import {
|
|
34
|
+
CONNECTOR_CALL_TASK_TYPES
|
|
35
|
+
} from "./packager-tool-7x1tmb8a.js";
|
|
36
|
+
import {
|
|
37
|
+
outputSourceFor
|
|
38
|
+
} from "./packager-tool-0w4fsdzv.js";
|
|
39
|
+
import {
|
|
40
|
+
STAGE_SELECTOR_RULES,
|
|
41
|
+
TASK_SELECTOR_RULES,
|
|
42
|
+
parseIfCell,
|
|
43
|
+
outputTypeCell,
|
|
44
|
+
canonicalExpression
|
|
45
|
+
} from "./packager-tool-1nfdj0zm.js";
|
|
38
46
|
import {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
} from "./packager-tool-
|
|
47
|
+
ledgerMissFor,
|
|
48
|
+
ledgerFoundFor
|
|
49
|
+
} from "./packager-tool-z1fw9a8v.js";
|
|
42
50
|
import {
|
|
43
|
-
|
|
44
|
-
} from "./packager-tool-
|
|
51
|
+
comparableFolder
|
|
52
|
+
} from "./packager-tool-yaertxsw.js";
|
|
53
|
+
import {
|
|
54
|
+
stageSpellings
|
|
55
|
+
} from "./packager-tool-5w0gjcck.js";
|
|
56
|
+
import {
|
|
57
|
+
isStageNode,
|
|
58
|
+
isAnyTriggerNode
|
|
59
|
+
} from "./packager-tool-35c9npcy.js";
|
|
45
60
|
|
|
46
61
|
// src/services/sdd-normalizer.ts
|
|
62
|
+
var ownBodyOf = (section) => {
|
|
63
|
+
const first = section.children[0];
|
|
64
|
+
return first === undefined ? section.body : section.body.slice(0, first.start - section.contentStart);
|
|
65
|
+
};
|
|
47
66
|
var readSddHeading = (line) => {
|
|
48
67
|
let level = 0;
|
|
49
68
|
while (level < line.length && line[level] === "#")
|
|
@@ -93,8 +112,10 @@ var readSddSections = (source) => {
|
|
|
93
112
|
});
|
|
94
113
|
const stack = [];
|
|
95
114
|
for (const section of sections) {
|
|
96
|
-
|
|
115
|
+
let top = stack.at(-1);
|
|
116
|
+
while (top !== undefined && top.level >= section.level) {
|
|
97
117
|
stack.pop();
|
|
118
|
+
top = stack.at(-1);
|
|
98
119
|
}
|
|
99
120
|
const parent = stack.at(-1);
|
|
100
121
|
if (parent !== undefined)
|
|
@@ -235,7 +256,14 @@ var SLA_RESPONSE_MAP_HEADINGS = ["SLA Response Map"];
|
|
|
235
256
|
var CASE_METADATA_HEADINGS = ["Case Metadata"];
|
|
236
257
|
var CASE_SLA_PROPERTY = "Case-Level SLA";
|
|
237
258
|
var CASE_VARIABLE_HEADINGS = ["Case Variables"];
|
|
259
|
+
var PERSONA_HEADINGS = [
|
|
260
|
+
"Personas",
|
|
261
|
+
"Case Roles",
|
|
262
|
+
"Case Roles and Permissions",
|
|
263
|
+
"Roles and Permissions"
|
|
264
|
+
];
|
|
238
265
|
var CANVAS_NOTE_HEADINGS = ["Canvas Notes", "Sticky Notes"];
|
|
266
|
+
var BUSINESS_EVENT_HEADINGS = ["Business Events"];
|
|
239
267
|
var STAGE_ENTRY_HEADINGS = ["Stage Entry Conditions"];
|
|
240
268
|
var STAGE_EXIT_HEADINGS = [
|
|
241
269
|
"Stage Exit Conditions",
|
|
@@ -254,12 +282,12 @@ var CASE_CONDITIONAL_SLA_HEADINGS = [
|
|
|
254
282
|
];
|
|
255
283
|
var STAGE_TASK_HEADINGS = ["Tasks"];
|
|
256
284
|
var VARIABLE_GROUPS = ["inputs", "outputs", "inputOutputs"];
|
|
257
|
-
var
|
|
285
|
+
var VARIABLE_PLACEMENT2 = new Map([
|
|
258
286
|
["in", ["inputs", "inputOutputs"]],
|
|
259
287
|
["out", ["outputs", "inputOutputs"]],
|
|
260
288
|
["variable", ["inputOutputs"]]
|
|
261
289
|
]);
|
|
262
|
-
var
|
|
290
|
+
var DEFAULT_GROUP2 = new Map([
|
|
263
291
|
["in", "inputs"],
|
|
264
292
|
["out", "inputOutputs"],
|
|
265
293
|
["variable", "inputOutputs"]
|
|
@@ -303,8 +331,8 @@ var bindingId = (expression) => {
|
|
|
303
331
|
if (!text.startsWith(prefix))
|
|
304
332
|
return;
|
|
305
333
|
const id = text.slice(prefix.length);
|
|
306
|
-
const
|
|
307
|
-
return id !== "" && [...id].every(
|
|
334
|
+
const isWordChar = (ch) => isDigit(ch) || ch === "_" || isLowerLetter(ch.toLowerCase());
|
|
335
|
+
return id !== "" && [...id].every(isWordChar) ? id : undefined;
|
|
308
336
|
};
|
|
309
337
|
var bindingDefault = (bindings, expression) => {
|
|
310
338
|
const id = bindingId(expression);
|
|
@@ -372,7 +400,11 @@ var parseCaseplan = (doc) => {
|
|
|
372
400
|
bindings,
|
|
373
401
|
caseExitConditions: asArray(metadata.caseExitRules),
|
|
374
402
|
caseSla: asArray(metadata.slaRules).length > 0,
|
|
375
|
-
caseSlaRules: asArray(metadata.slaRules)
|
|
403
|
+
caseSlaRules: asArray(metadata.slaRules),
|
|
404
|
+
caseRoles: readCaseRoles(doc),
|
|
405
|
+
...typeof metadata.caseAppVersion === "string" && {
|
|
406
|
+
caseAppVersion: metadata.caseAppVersion
|
|
407
|
+
}
|
|
376
408
|
};
|
|
377
409
|
for (const node of asArray(doc.nodes)) {
|
|
378
410
|
const record = asRecord(node);
|
|
@@ -488,6 +520,7 @@ var customRowsOf = (header, rows) => {
|
|
|
488
520
|
const bindingAt = columnIndex(header, ...INPUT_VALUE_COLUMNS);
|
|
489
521
|
if (bindingAt === undefined)
|
|
490
522
|
return [];
|
|
523
|
+
const typeAt = columnIndex(header, "Type");
|
|
491
524
|
const found = [];
|
|
492
525
|
for (const row of rows) {
|
|
493
526
|
const binding = unwrapCodeSpan(cellAt(row, bindingAt));
|
|
@@ -501,7 +534,12 @@ var customRowsOf = (header, rows) => {
|
|
|
501
534
|
const emptyLiteral = /^(""|'')$/.test(expression);
|
|
502
535
|
if (!emptyLiteral && (!expression || isBlank(expression)))
|
|
503
536
|
continue;
|
|
504
|
-
|
|
537
|
+
const type = typeAt === undefined ? "" : cellAt(row, typeAt).trim();
|
|
538
|
+
found.push({
|
|
539
|
+
variable,
|
|
540
|
+
expression,
|
|
541
|
+
...type !== "" && !isBlank(type) && { type }
|
|
542
|
+
});
|
|
505
543
|
}
|
|
506
544
|
return found;
|
|
507
545
|
};
|
|
@@ -615,6 +653,7 @@ var xrefArguments = (text) => {
|
|
|
615
653
|
return value.trim();
|
|
616
654
|
}).filter((part) => part !== "");
|
|
617
655
|
};
|
|
656
|
+
var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
618
657
|
var producerSegments = (raw) => {
|
|
619
658
|
if (raw.includes(XREF_OPEN))
|
|
620
659
|
return xrefArguments(raw);
|
|
@@ -625,9 +664,13 @@ var producerSegments = (raw) => {
|
|
|
625
664
|
};
|
|
626
665
|
var readProducer = (raw) => {
|
|
627
666
|
const segments = producerSegments(raw);
|
|
628
|
-
if (segments.length
|
|
667
|
+
if (segments.length < 3)
|
|
629
668
|
return;
|
|
630
|
-
|
|
669
|
+
if (segments.length > 3 && (raw.includes(XREF_OPEN) || !segments.slice(2).every((part) => IDENTIFIER.test(part)))) {
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
const [stage, task, ...path] = segments;
|
|
673
|
+
const output = path.join(".");
|
|
631
674
|
return stage && task && output ? { stage, task, output } : undefined;
|
|
632
675
|
};
|
|
633
676
|
var resourceOutputsByTask = (document) => {
|
|
@@ -711,19 +754,24 @@ var detailProducerInputs = (block) => {
|
|
|
711
754
|
};
|
|
712
755
|
|
|
713
756
|
// src/services/strict-sdd-audit/references.ts
|
|
714
|
-
var
|
|
715
|
-
|
|
757
|
+
var QUOTE_PAIRS = new Map([
|
|
758
|
+
['"', '"'],
|
|
759
|
+
["“", "”"],
|
|
760
|
+
["‘", "’"],
|
|
761
|
+
["'", "'"]
|
|
762
|
+
]);
|
|
716
763
|
var quotedArgs = (argumentList) => {
|
|
717
764
|
const args = [];
|
|
718
765
|
let at = 0;
|
|
719
766
|
while (at < argumentList.length) {
|
|
720
|
-
|
|
767
|
+
const close = QUOTE_PAIRS.get(argumentList[at]);
|
|
768
|
+
if (close === undefined) {
|
|
721
769
|
at += 1;
|
|
722
770
|
continue;
|
|
723
771
|
}
|
|
724
772
|
const from = at + 1;
|
|
725
773
|
let end = from;
|
|
726
|
-
while (end < argumentList.length &&
|
|
774
|
+
while (end < argumentList.length && argumentList[end] !== close) {
|
|
727
775
|
end += 1;
|
|
728
776
|
}
|
|
729
777
|
if (end >= argumentList.length)
|
|
@@ -831,7 +879,7 @@ var pairLeftovers = (sddKeys, planKeys) => {
|
|
|
831
879
|
return pairs.sort(([a], [b]) => a - b);
|
|
832
880
|
};
|
|
833
881
|
var pairByEvidence = (sddCount, planCount, related) => {
|
|
834
|
-
const candidates = Array.from({ length: sddCount }, (_, s) => Array.from({ length: planCount }, (
|
|
882
|
+
const candidates = Array.from({ length: sddCount }, (_, s) => Array.from({ length: planCount }, (_, p) => p).filter((p) => related(s, p)));
|
|
835
883
|
const pairs = [];
|
|
836
884
|
candidates.forEach((planAts, s) => {
|
|
837
885
|
if (planAts.length !== 1)
|
|
@@ -939,19 +987,22 @@ var conditionNameFindings = (declared, planConditions, path, label, once) => {
|
|
|
939
987
|
return findings;
|
|
940
988
|
};
|
|
941
989
|
|
|
990
|
+
// src/services/strict-sdd-audit/input-lookup.ts
|
|
991
|
+
var inputNamed = (inputs, name) => inputs.find((i) => norm(i.name) === norm(name)) ?? inputs.find((i) => typeof i.displayName === "string" && norm(i.displayName) === norm(name));
|
|
992
|
+
|
|
942
993
|
// src/services/strict-sdd-audit/wiring.ts
|
|
943
|
-
var parsesAsJson = (text) =>
|
|
994
|
+
var parsesAsJson = (text) => catchError2(() => JSON.parse(text))[0] === undefined;
|
|
944
995
|
var sameLiteral = (declared, actual) => {
|
|
945
996
|
if (declared === actual)
|
|
946
997
|
return true;
|
|
947
|
-
const parse = (text) =>
|
|
998
|
+
const parse = (text) => catchError2(() => JSON.parse(text))[1];
|
|
948
999
|
const canonical = (value) => {
|
|
949
1000
|
if (Array.isArray(value)) {
|
|
950
1001
|
return `[${value.map(canonical).join(",")}]`;
|
|
951
1002
|
}
|
|
952
1003
|
const record = asRecord(value);
|
|
953
1004
|
if (record) {
|
|
954
|
-
return `{${Object.keys(record).sort((
|
|
1005
|
+
return `{${Object.keys(record).sort((a, b) => a.localeCompare(b)).map((key) => `${JSON.stringify(key)}:${canonical(record[key])}`).join(",")}}`;
|
|
955
1006
|
}
|
|
956
1007
|
return JSON.stringify(value) ?? "null";
|
|
957
1008
|
};
|
|
@@ -1113,7 +1164,7 @@ var wiringFindings = (task, found, path, label) => {
|
|
|
1113
1164
|
const findings = [];
|
|
1114
1165
|
const inputs = asArray(found.data.inputs).map(asRecord).filter((i) => i !== undefined);
|
|
1115
1166
|
for (const declared of task.expressionInputs) {
|
|
1116
|
-
const input = inputs
|
|
1167
|
+
const input = inputNamed(inputs, declared.name);
|
|
1117
1168
|
if (!input)
|
|
1118
1169
|
continue;
|
|
1119
1170
|
const actual = input.value;
|
|
@@ -1149,6 +1200,21 @@ var referenceTokensIn = (text) => {
|
|
|
1149
1200
|
return tokens;
|
|
1150
1201
|
};
|
|
1151
1202
|
var isReferenceToken = (ch) => ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch >= "0" && ch <= "9" || ch === "_";
|
|
1203
|
+
var RESPONSE_PREFIX = "response.";
|
|
1204
|
+
var outputLabels = (output) => {
|
|
1205
|
+
const labels = [output.name, output.var];
|
|
1206
|
+
const source = output.source;
|
|
1207
|
+
if (typeof source !== "string" || !source.startsWith("="))
|
|
1208
|
+
return labels;
|
|
1209
|
+
const path = source.slice(1).trim();
|
|
1210
|
+
if (!path.split(".").every((part) => IDENTIFIER.test(part)))
|
|
1211
|
+
return labels;
|
|
1212
|
+
labels.push(path);
|
|
1213
|
+
if (path.startsWith(RESPONSE_PREFIX)) {
|
|
1214
|
+
labels.push(path.slice(RESPONSE_PREFIX.length));
|
|
1215
|
+
}
|
|
1216
|
+
return labels;
|
|
1217
|
+
};
|
|
1152
1218
|
var planOutputFor = (plan, producer) => {
|
|
1153
1219
|
for (const stage of plan.stages) {
|
|
1154
1220
|
if (norm(stage.name) !== norm(producer.stage))
|
|
@@ -1160,7 +1226,7 @@ var planOutputFor = (plan, producer) => {
|
|
|
1160
1226
|
const output = asRecord(entry);
|
|
1161
1227
|
if (output === undefined)
|
|
1162
1228
|
continue;
|
|
1163
|
-
for (const label of
|
|
1229
|
+
for (const label of outputLabels(output)) {
|
|
1164
1230
|
if (typeof label === "string" && norm(label) === norm(producer.output)) {
|
|
1165
1231
|
return output;
|
|
1166
1232
|
}
|
|
@@ -1229,7 +1295,7 @@ var producerIndex = (plan) => {
|
|
|
1229
1295
|
}
|
|
1230
1296
|
if (tokens.size === 0)
|
|
1231
1297
|
continue;
|
|
1232
|
-
for (const label of
|
|
1298
|
+
for (const label of outputLabels(output)) {
|
|
1233
1299
|
if (typeof label !== "string" || label === "")
|
|
1234
1300
|
continue;
|
|
1235
1301
|
const key = `${norm(stage.name)}|${norm(planTask.name)}|${norm(label)}`;
|
|
@@ -1385,8 +1451,8 @@ var conditionExpressionFindings = (declared, planConditions, plan, path, label)
|
|
|
1385
1451
|
if (resolved === undefined)
|
|
1386
1452
|
continue;
|
|
1387
1453
|
const expected = comparableExpression(resolved.expected);
|
|
1388
|
-
const comparable = (
|
|
1389
|
-
let normalised =
|
|
1454
|
+
const comparable = (actual) => {
|
|
1455
|
+
let normalised = actual;
|
|
1390
1456
|
for (const token of resolved.tokens) {
|
|
1391
1457
|
normalised = normalised.replaceAll(`${VARS_PREFIX}${token}`, VARIABLE_PLACEHOLDER);
|
|
1392
1458
|
}
|
|
@@ -1423,7 +1489,7 @@ var expressionShapeFindings = (task, found, plan, path, label) => {
|
|
|
1423
1489
|
const resolved = resolveXrefExpression(declared.expression, index);
|
|
1424
1490
|
if (resolved === undefined)
|
|
1425
1491
|
continue;
|
|
1426
|
-
const input = inputs
|
|
1492
|
+
const input = inputNamed(inputs, declared.name);
|
|
1427
1493
|
const actual = input?.value;
|
|
1428
1494
|
if (typeof actual !== "string" || actual.trim() === "")
|
|
1429
1495
|
continue;
|
|
@@ -1439,20 +1505,213 @@ var expressionShapeFindings = (task, found, plan, path, label) => {
|
|
|
1439
1505
|
return findings;
|
|
1440
1506
|
};
|
|
1441
1507
|
|
|
1508
|
+
// src/services/strict-sdd-audit/personas.ts
|
|
1509
|
+
var personaFindings = (sdd, plan) => {
|
|
1510
|
+
if (sdd.personas.length === 0)
|
|
1511
|
+
return [];
|
|
1512
|
+
const planStageId = new Map;
|
|
1513
|
+
for (const stage of plan.stages) {
|
|
1514
|
+
if (stage.name === undefined || stage.id === undefined)
|
|
1515
|
+
continue;
|
|
1516
|
+
planStageId.set(norm(withoutTrailingSlug(stage.name)), stage.id);
|
|
1517
|
+
}
|
|
1518
|
+
const declared = new Set(sdd.stages.map((stage) => norm(withoutTrailingSlug(stage.name))));
|
|
1519
|
+
const { roles, problems } = resolvePersonas(sdd.personas, (name) => {
|
|
1520
|
+
const key = norm(withoutTrailingSlug(name));
|
|
1521
|
+
if (!declared.has(key))
|
|
1522
|
+
return;
|
|
1523
|
+
return planStageId.get(key) ?? `${UNPLANNED_STAGE}${key}`;
|
|
1524
|
+
});
|
|
1525
|
+
const strictFor = new Map(sdd.personas.map((persona) => [
|
|
1526
|
+
persona.name.toLowerCase(),
|
|
1527
|
+
persona.templateShape
|
|
1528
|
+
]));
|
|
1529
|
+
const finding = (personaName, code, path, message) => (strictFor.get(personaName.toLowerCase()) ? error : warning)(code, path, message);
|
|
1530
|
+
const issues = problems.map((problem) => {
|
|
1531
|
+
const code = problemCode(problem.code);
|
|
1532
|
+
const path = `metadata.caseRoles[${problem.persona}]`;
|
|
1533
|
+
const message = `Persona "${problem.persona}": ${problem.detail}.`;
|
|
1534
|
+
return problem.code === "stages-on-case" || problem.code === "permission-gated" ? warning(code, path, message) : finding(problem.persona, code, path, message);
|
|
1535
|
+
});
|
|
1536
|
+
if (roles.length === 0)
|
|
1537
|
+
return issues;
|
|
1538
|
+
if (plan.caseAppVersion === undefined || !CASE_APP_VERSIONS_WITH_ROLES.includes(plan.caseAppVersion)) {
|
|
1539
|
+
issues.push((roles.some((role) => strictFor.get(role.name.toLowerCase())) ? error : warning)("STRICT_SDD_PERSONA_APP_VERSION", "metadata.caseAppVersion", `The SDD declares ${roles.length} persona(s) and caseplan metadata.caseAppVersion is "${plan.caseAppVersion ?? ""}"; roles are honoured only under "v2" or "custom", so the deployed case would carry none.`));
|
|
1540
|
+
}
|
|
1541
|
+
const byName = new Map(plan.caseRoles.map((role) => [role.name.toLowerCase(), role]));
|
|
1542
|
+
const matched = new Set;
|
|
1543
|
+
for (const persona of roles) {
|
|
1544
|
+
const role = byName.get(persona.name.toLowerCase());
|
|
1545
|
+
if (role === undefined) {
|
|
1546
|
+
issues.push(finding(persona.name, "STRICT_SDD_PERSONA_MISSING", `metadata.caseRoles[${persona.name}]`, `Persona "${persona.name}" has no entry in caseplan metadata.caseRoles, so nobody can be assigned it.`));
|
|
1547
|
+
continue;
|
|
1548
|
+
}
|
|
1549
|
+
matched.add(role);
|
|
1550
|
+
issues.push(...roleFindings(persona, role, (code, path, message) => finding(persona.name, code, path, message)));
|
|
1551
|
+
}
|
|
1552
|
+
for (const role of plan.caseRoles) {
|
|
1553
|
+
if (matched.has(role))
|
|
1554
|
+
continue;
|
|
1555
|
+
issues.push(warning("STRICT_SDD_PERSONA_EXTRA", `metadata.caseRoles[${role.name}]`, `Role "${role.name}" is in caseplan metadata.caseRoles with no matching SDD persona.`));
|
|
1556
|
+
}
|
|
1557
|
+
return issues;
|
|
1558
|
+
};
|
|
1559
|
+
var UNPLANNED_STAGE = "sdd:";
|
|
1560
|
+
var problemCode = (code) => `STRICT_SDD_PERSONA_${code.toUpperCase().replace(/-/g, "_")}`;
|
|
1561
|
+
var sameSet = (left, right) => left.length === right.length && left.every((item) => right.includes(item));
|
|
1562
|
+
var keysOf = (actions) => actions.map((action) => roleActionKeyOf(action) ?? action).join(", ");
|
|
1563
|
+
var roleFindings = (persona, role, report) => {
|
|
1564
|
+
const path = `metadata.caseRoles[${persona.name}]`;
|
|
1565
|
+
const issues = [];
|
|
1566
|
+
if (role.scope !== persona.scope) {
|
|
1567
|
+
issues.push(report("STRICT_SDD_PERSONA_SCOPE_MISMATCH", `${path}.scope`, `Persona "${persona.name}": the SDD scopes it to ${persona.scope === "case" ? "the whole case" : "stages"} and the caseplan role has scope "${role.scope}".`));
|
|
1568
|
+
return issues;
|
|
1569
|
+
}
|
|
1570
|
+
if (persona.scope === "stages" && persona.stageIds.every((id) => !id.startsWith(UNPLANNED_STAGE)) && !sameSet(persona.stageIds, role.stageIds ?? [])) {
|
|
1571
|
+
issues.push(report("STRICT_SDD_PERSONA_STAGES_MISMATCH", `${path}.stageIds`, `Persona "${persona.name}": the SDD scopes it to stage(s) ${persona.stageNames.join(", ")} and the caseplan role's stageIds select different stages.`));
|
|
1572
|
+
}
|
|
1573
|
+
if (!sameSet(persona.actions, role.actions)) {
|
|
1574
|
+
issues.push(report("STRICT_SDD_PERSONA_PERMISSIONS_MISMATCH", `${path}.actions`, `Persona "${persona.name}": the SDD grants ${keysOf(persona.actions)} and the caseplan role grants ${keysOf(role.actions) || "nothing"}.`));
|
|
1575
|
+
}
|
|
1576
|
+
return issues;
|
|
1577
|
+
};
|
|
1578
|
+
|
|
1579
|
+
// src/services/strict-sdd-audit/inputs.ts
|
|
1580
|
+
var CONNECTOR_ENVELOPE_INPUTS = new Set([
|
|
1581
|
+
"pathParameters",
|
|
1582
|
+
"queryParameters",
|
|
1583
|
+
"body"
|
|
1584
|
+
]);
|
|
1585
|
+
var resolveEnvelopeKey = (body, name) => {
|
|
1586
|
+
const named = (record, part) => Object.entries(record).find(([key]) => norm(key) === norm(part));
|
|
1587
|
+
const direct = named(body, name);
|
|
1588
|
+
if (direct !== undefined)
|
|
1589
|
+
return { value: direct[1] };
|
|
1590
|
+
const parts = name.split(".").filter((part) => part !== "");
|
|
1591
|
+
if (parts.length < 2)
|
|
1592
|
+
return;
|
|
1593
|
+
let current = body;
|
|
1594
|
+
for (const part of parts) {
|
|
1595
|
+
const record = asRecord(current);
|
|
1596
|
+
if (record === undefined)
|
|
1597
|
+
return;
|
|
1598
|
+
const hit = named(record, part);
|
|
1599
|
+
if (hit === undefined)
|
|
1600
|
+
return;
|
|
1601
|
+
current = hit[1];
|
|
1602
|
+
}
|
|
1603
|
+
return { value: current };
|
|
1604
|
+
};
|
|
1605
|
+
var carriedInput = (found, name) => {
|
|
1606
|
+
const absent = { present: false, value: undefined, valueless: false };
|
|
1607
|
+
const inputs = asArray(found.data.inputs).map(asRecord).filter((i) => i !== undefined);
|
|
1608
|
+
const direct = inputNamed(inputs, name);
|
|
1609
|
+
if (direct !== undefined) {
|
|
1610
|
+
return {
|
|
1611
|
+
present: true,
|
|
1612
|
+
value: direct.value,
|
|
1613
|
+
valueless: !("value" in direct)
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
if (typeof found.type !== "string" || !CONNECTOR_CALL_TASK_TYPES.has(found.type)) {
|
|
1617
|
+
return absent;
|
|
1618
|
+
}
|
|
1619
|
+
for (const entry of inputs) {
|
|
1620
|
+
const entryName = entry.name;
|
|
1621
|
+
if (typeof entryName !== "string" || !CONNECTOR_ENVELOPE_INPUTS.has(entryName)) {
|
|
1622
|
+
continue;
|
|
1623
|
+
}
|
|
1624
|
+
const body = asRecord(entry.body);
|
|
1625
|
+
if (body === undefined)
|
|
1626
|
+
continue;
|
|
1627
|
+
const carried = resolveEnvelopeKey(body, name) ?? (entryName === "body" && found.type === "wait-for-connector" ? ["parameters", "queryParams"].map((key) => asRecord(body[key])).filter((nested) => nested !== undefined).map((nested) => resolveEnvelopeKey(nested, name)).find((key) => key !== undefined) : undefined);
|
|
1628
|
+
if (carried !== undefined) {
|
|
1629
|
+
return { present: true, value: carried.value, valueless: false };
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
return absent;
|
|
1633
|
+
};
|
|
1634
|
+
var declaredInputNames = (task) => {
|
|
1635
|
+
const names = [];
|
|
1636
|
+
const add = (name) => {
|
|
1637
|
+
if (!names.some((existing) => norm(existing) === norm(name))) {
|
|
1638
|
+
names.push(name);
|
|
1639
|
+
}
|
|
1640
|
+
};
|
|
1641
|
+
for (const entry of task.literalInputs)
|
|
1642
|
+
add(entry.name);
|
|
1643
|
+
for (const entry of task.expressionInputs)
|
|
1644
|
+
add(entry.name);
|
|
1645
|
+
for (const entry of task.producerInputs)
|
|
1646
|
+
add(entry.name);
|
|
1647
|
+
return names;
|
|
1648
|
+
};
|
|
1649
|
+
var boundInputNames = (task) => task.boundInputs.map((entry) => entry.name);
|
|
1650
|
+
var emptyLiteralInputNames = (task) => task.literalInputs.filter((entry) => isBlank(entry.value)).map((entry) => entry.name);
|
|
1651
|
+
var envelopeFindings = (task, found, path, label) => {
|
|
1652
|
+
const findings = [];
|
|
1653
|
+
const columns = [
|
|
1654
|
+
{
|
|
1655
|
+
declared: task.required,
|
|
1656
|
+
actual: found.isRequired,
|
|
1657
|
+
column: "Required",
|
|
1658
|
+
field: "isRequired"
|
|
1659
|
+
},
|
|
1660
|
+
{
|
|
1661
|
+
declared: task.runOnlyOnce,
|
|
1662
|
+
actual: found.shouldRunOnlyOnce,
|
|
1663
|
+
column: "Run Only Once",
|
|
1664
|
+
field: "shouldRunOnlyOnce"
|
|
1665
|
+
}
|
|
1666
|
+
];
|
|
1667
|
+
for (const { declared, actual, column, field } of columns) {
|
|
1668
|
+
if (declared === undefined)
|
|
1669
|
+
continue;
|
|
1670
|
+
if (typeof actual === "boolean" && actual === declared)
|
|
1671
|
+
continue;
|
|
1672
|
+
findings.push(error("STRICT_SDD_TASK_ENVELOPE_MISMATCH", `${path}.${field}`, `${label}: the SDD Tasks table declares a "${column}" value for this task and its \`${field}\` ${typeof actual === "boolean" ? "disagrees" : "is absent"}. Set it from that column.`));
|
|
1673
|
+
}
|
|
1674
|
+
return findings;
|
|
1675
|
+
};
|
|
1676
|
+
var emptyInputFindings = (task, found, path, label) => {
|
|
1677
|
+
const declared = declaredInputNames(task);
|
|
1678
|
+
if (declared.length === 0)
|
|
1679
|
+
return [];
|
|
1680
|
+
if (Object.keys(found.data).length === 0)
|
|
1681
|
+
return [];
|
|
1682
|
+
const bound = boundInputNames(task);
|
|
1683
|
+
const emptyLiteral = emptyLiteralInputNames(task);
|
|
1684
|
+
const findings = [];
|
|
1685
|
+
const names = (list, name) => list.some((entry) => norm(entry) === norm(name));
|
|
1686
|
+
for (const name of declared) {
|
|
1687
|
+
const carried = carriedInput(found, name);
|
|
1688
|
+
const value = carried.value;
|
|
1689
|
+
const missing = !carried.present;
|
|
1690
|
+
if (!missing && !carried.valueless && (typeof value !== "string" || value.trim() !== "")) {
|
|
1691
|
+
continue;
|
|
1692
|
+
}
|
|
1693
|
+
if (!missing && (names(bound, name) || names(emptyLiteral, name))) {
|
|
1694
|
+
continue;
|
|
1695
|
+
}
|
|
1696
|
+
const declares = names(emptyLiteral, name) ? "the empty literal" : "a value";
|
|
1697
|
+
findings.push(error("STRICT_SDD_INPUT_EMPTY", `${path}.data.inputs[${name}]`, `${label}: the SDD Inputs row for "${name}" declares ${declares}, and the caseplan input ${missing ? "is absent" : carried.valueless ? "carries no `value` at all" : "is empty"}. Nothing reaches the task for it, and none of the value comparisons can run against an input that carries nothing. ${missing ? "The row exists, so the input has to exist too — add it, carrying that row's value." : carried.valueless ? "Its value has to live under `value` — data under `body` is the connector envelope, which does not apply to this task type." : "Re-read that row and set the input from it."}`));
|
|
1698
|
+
}
|
|
1699
|
+
return findings;
|
|
1700
|
+
};
|
|
1701
|
+
|
|
1442
1702
|
// src/services/strict-sdd-audit/producer-findings.ts
|
|
1443
1703
|
var producerFindings = (task, found, plan, path, label) => {
|
|
1444
1704
|
if (task.producerInputs.length === 0)
|
|
1445
1705
|
return [];
|
|
1446
1706
|
const findings = [];
|
|
1447
1707
|
const index = producerIndex(plan);
|
|
1448
|
-
const inputs = asArray(found.data.inputs).map(asRecord).filter((i) => i !== undefined);
|
|
1449
1708
|
for (const declared of task.producerInputs) {
|
|
1450
1709
|
const { stage, task: producerTask, output } = declared.producer;
|
|
1451
1710
|
const candidates = index.get(`${norm(stage)}|${norm(producerTask)}|${norm(output)}`);
|
|
1452
1711
|
if (candidates === undefined || candidates.length !== 1)
|
|
1453
1712
|
continue;
|
|
1454
|
-
const input =
|
|
1455
|
-
if (input
|
|
1713
|
+
const input = carriedInput(found, declared.name);
|
|
1714
|
+
if (!input.present)
|
|
1456
1715
|
continue;
|
|
1457
1716
|
const value = input.value;
|
|
1458
1717
|
if (typeof value !== "string" || value.trim() === "")
|
|
@@ -1562,289 +1821,212 @@ var capitalize = (text) => text.charAt(0).toUpperCase() + text.slice(1);
|
|
|
1562
1821
|
var noCategoryColumnNote = (title, header) => `section "${title}": the table has rows but no Category column ` + `(header: ${header.join(" | ") || "<none>"}) — no row can be checked ` + "for `variables` group placement, so a formal argument missing half its " + "pair passes";
|
|
1563
1822
|
var unrecognizedHeadingNote = (title, level) => `heading "${title}" (level ${level}) reads like an audited section but ` + "matches no accepted spelling — rename it to one of the canonical " + "headings, or the checks it feeds are skipped";
|
|
1564
1823
|
|
|
1565
|
-
// src/services/sdd-
|
|
1566
|
-
var
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
const
|
|
1574
|
-
const
|
|
1575
|
-
if (
|
|
1576
|
-
return
|
|
1577
|
-
|
|
1578
|
-
return
|
|
1579
|
-
};
|
|
1580
|
-
var DOTTED_READ = /^\.[A-Za-z_$]/;
|
|
1581
|
-
var deferredOutputs = new WeakMap;
|
|
1582
|
-
var markDeferredOutputs = (index, ids) => {
|
|
1583
|
-
deferredOutputs.set(index, ids);
|
|
1584
|
-
};
|
|
1585
|
-
var expandXrefs = (text, context) => text.replace(XREF_CALL, (whole, ...rest) => {
|
|
1586
|
-
const offset = rest[rest.length - 2];
|
|
1587
|
-
const captured = rest.slice(0, -2);
|
|
1588
|
-
const stage = argumentOf(captured.slice(0, 4));
|
|
1589
|
-
const task = argumentOf(captured.slice(4, 8));
|
|
1590
|
-
const output = argumentOf(captured.slice(8, 12));
|
|
1591
|
-
const found = lookupOutput(context.outputs, `${indexKey(stage)}|${indexKey(task)}|${indexKey(output)}`, task);
|
|
1592
|
-
if (found !== undefined && "id" in found) {
|
|
1593
|
-
if (deferredOutputs.get(context.outputs)?.has(found.id)) {
|
|
1594
|
-
context.unresolved.push({
|
|
1595
|
-
kind: "placeholder",
|
|
1596
|
-
where: context.where,
|
|
1597
|
-
detail: `\`${whole}\` reads an output of a task the resolved file defers, so \`vars.${found.id}\` resolves to nothing until that task is filled`
|
|
1598
|
-
});
|
|
1599
|
-
}
|
|
1600
|
-
const after = text.slice(offset + whole.length);
|
|
1601
|
-
return DOTTED_READ.test(after) ? `vars.${found.id}?` : `vars.${found.id}`;
|
|
1602
|
-
}
|
|
1603
|
-
context.unresolved.push({
|
|
1604
|
-
kind: "producer-reference",
|
|
1605
|
-
where: context.where,
|
|
1606
|
-
detail: unresolvedXrefDetail(whole, found)
|
|
1607
|
-
});
|
|
1608
|
-
return whole;
|
|
1609
|
-
});
|
|
1610
|
-
var expandOutputMarkers = (output, context) => {
|
|
1611
|
-
const expand = (value2) => typeof value2 === "string" && value2.includes("$xref") ? expandXrefs(value2, context) : value2;
|
|
1612
|
-
const value = expand(output.value);
|
|
1613
|
-
const source = expand(output.source);
|
|
1614
|
-
return value === output.value && source === output.source ? output : { ...output, value, source };
|
|
1824
|
+
// src/services/strict-sdd-audit/selector-targets.ts
|
|
1825
|
+
var idsIn = (value) => {
|
|
1826
|
+
const values = Array.isArray(value) ? value : [value];
|
|
1827
|
+
return values.filter((entry) => typeof entry === "string" && entry.trim() !== "");
|
|
1828
|
+
};
|
|
1829
|
+
var selectedIds = (rule) => Object.entries(rule ?? {}).filter(([key]) => key.startsWith("selected")).flatMap(([, value]) => idsIn(value));
|
|
1830
|
+
var hasSelector = (rule) => selectedIds(rule).length > 0 || idsIn(rule?.slaId).length > 0;
|
|
1831
|
+
var ruleTargets = (rule, resolve, same) => {
|
|
1832
|
+
const named = selectedIds(rule).map((id) => resolve.stage(id) ?? resolve.task(id)).filter((name) => name !== undefined);
|
|
1833
|
+
const owners = idsIn(rule?.slaId).map((id) => resolve.sla?.(id)).filter((name) => name !== undefined);
|
|
1834
|
+
if (named.length === 0)
|
|
1835
|
+
return { names: owners };
|
|
1836
|
+
const conflict = owners.find((owner) => !named.some((name) => same(name, owner)));
|
|
1837
|
+
return conflict === undefined ? { names: named } : { names: named, conflict };
|
|
1615
1838
|
};
|
|
1616
1839
|
|
|
1617
|
-
// src/services/sdd-
|
|
1618
|
-
var
|
|
1619
|
-
|
|
1620
|
-
const
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
for (const stage of model.stages) {
|
|
1625
|
-
for (const other of stage.tasks) {
|
|
1626
|
-
const name = exactKey(String(other.name ?? ""));
|
|
1627
|
-
if (name === mine)
|
|
1628
|
-
continue;
|
|
1629
|
-
if (indexKey(name) === folded)
|
|
1630
|
-
unique = false;
|
|
1631
|
-
if (modelKey(name) === filed)
|
|
1632
|
-
filedUnique = false;
|
|
1633
|
-
}
|
|
1840
|
+
// src/services/strict-sdd-audit/selectors.ts
|
|
1841
|
+
var sameLabel = (a, b) => norm(a) === norm(b) || normAnnotationless(a) === normAnnotationless(b);
|
|
1842
|
+
var leadingSegment = (name) => {
|
|
1843
|
+
for (const dash of [" - ", " – ", " — "]) {
|
|
1844
|
+
const at = name.indexOf(dash);
|
|
1845
|
+
if (at > 0)
|
|
1846
|
+
return name.slice(0, at);
|
|
1634
1847
|
}
|
|
1635
|
-
return
|
|
1636
|
-
names: (spelled) => exactKey(spelled) === mine || unique && indexKey(spelled) === folded,
|
|
1637
|
-
unique,
|
|
1638
|
-
filedUnique
|
|
1639
|
-
};
|
|
1848
|
+
return name;
|
|
1640
1849
|
};
|
|
1641
|
-
var
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
const
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
const
|
|
1649
|
-
const
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
return false;
|
|
1653
|
-
if (!name.includes("."))
|
|
1654
|
-
return true;
|
|
1655
|
-
const held = dotted(one);
|
|
1656
|
-
return held === undefined || indexKey(held) === indexKey(name);
|
|
1657
|
-
};
|
|
1658
|
-
const add = (name) => {
|
|
1659
|
-
if (name === "")
|
|
1660
|
-
return;
|
|
1661
|
-
const found = rows.find((one) => takes(one, name));
|
|
1662
|
-
if (found === undefined)
|
|
1663
|
-
rows.push({ row: name, aliases: [] });
|
|
1664
|
-
else if (found.row !== name && !found.aliases.includes(name)) {
|
|
1665
|
-
found.aliases.push(name);
|
|
1850
|
+
var targetsOf = (rule, selectors) => norm(rule) === norm("sla-status-change") ? selectors.slice(0, 1) : norm(rule) === norm("api-event") ? [] : selectors;
|
|
1851
|
+
var undeclaredGateFinding = (row, planRules, path, label, resolve) => {
|
|
1852
|
+
if (row.ungated !== true || row.when === undefined)
|
|
1853
|
+
return;
|
|
1854
|
+
const { rule, selectors: named } = whenRule(row.when);
|
|
1855
|
+
if (!rule || isBlank(rule))
|
|
1856
|
+
return;
|
|
1857
|
+
const selectors = targetsOf(rule, named);
|
|
1858
|
+
const belongsToRow = (candidate) => {
|
|
1859
|
+
if (norm(rule) === norm("api-event")) {
|
|
1860
|
+
return candidate?.businessEventType === named[0];
|
|
1666
1861
|
}
|
|
1862
|
+
if (selectors.length === 0)
|
|
1863
|
+
return !hasSelector(candidate);
|
|
1864
|
+
if (!hasSelector(candidate))
|
|
1865
|
+
return false;
|
|
1866
|
+
if (resolve === undefined)
|
|
1867
|
+
return false;
|
|
1868
|
+
const pointsAt = ruleTargets(candidate, resolve, sameLabel).names;
|
|
1869
|
+
if (pointsAt.length === 0)
|
|
1870
|
+
return false;
|
|
1871
|
+
return selectors.some((want) => {
|
|
1872
|
+
const denotes = resolve.label(want);
|
|
1873
|
+
return pointsAt.some((got) => sameLabel(got, want) || denotes !== undefined && got === denotes);
|
|
1874
|
+
});
|
|
1667
1875
|
};
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1687
|
-
return rows;
|
|
1688
|
-
};
|
|
1689
|
-
var emittedName = (row) => row.split(".").at(-1) ?? row;
|
|
1690
|
-
var OUTPUT_TYPES = new Map([
|
|
1691
|
-
["string", "string"],
|
|
1692
|
-
["object", "jsonSchema"],
|
|
1693
|
-
["json", "jsonSchema"],
|
|
1694
|
-
["jsonschema", "jsonSchema"]
|
|
1695
|
-
]);
|
|
1696
|
-
var readsOutput = (reference, row) => indexKey(reference) === indexKey(row) || !reference.includes(".") && indexKey(reference) === indexKey(emittedName(row));
|
|
1697
|
-
var outputTypeFor = (model, stageNames, taskName, output) => {
|
|
1698
|
-
const matcher = taskMatcher(model, taskName);
|
|
1699
|
-
for (const stage of model.stages) {
|
|
1700
|
-
for (const task of stage.tasks) {
|
|
1701
|
-
for (const row of task.producerInputs ?? []) {
|
|
1702
|
-
const producer = row.producer;
|
|
1703
|
-
if (producer === undefined || row.direct !== true || typeof row.type !== "string" || !stageNames.includes(indexKey(producer.stage)) || !matcher.names(producer.task) || !readsOutput(producer.output, output)) {
|
|
1704
|
-
continue;
|
|
1705
|
-
}
|
|
1706
|
-
const mapped = OUTPUT_TYPES.get(row.type.trim().toLowerCase().replaceAll(" ", ""));
|
|
1707
|
-
if (mapped !== undefined)
|
|
1708
|
-
return mapped;
|
|
1709
|
-
}
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
return;
|
|
1876
|
+
const gated = planRules.filter((candidate) => {
|
|
1877
|
+
if (norm(candidate?.rule) !== norm(rule))
|
|
1878
|
+
return false;
|
|
1879
|
+
if (!belongsToRow(candidate))
|
|
1880
|
+
return false;
|
|
1881
|
+
const expression = candidate?.conditionExpression;
|
|
1882
|
+
return typeof expression === "string" && !isBlank(expression);
|
|
1883
|
+
});
|
|
1884
|
+
if (gated.length === 0)
|
|
1885
|
+
return;
|
|
1886
|
+
const expressions = [
|
|
1887
|
+
...new Set(gated.map((candidate) => String(candidate?.conditionExpression)).filter((expression) => expression !== ""))
|
|
1888
|
+
];
|
|
1889
|
+
const on = selectors.length > 0 ? ` on "${selectors.join('", "')}"` : "";
|
|
1890
|
+
return error("STRICT_SDD_CONDITION_EXPRESSION_UNDECLARED", `${path}[${row.displayName}].conditionExpression`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\`${on} with no IF gate, and the caseplan's \`${rule}\` carries \`${expressions.join("`, `")}\`. A gate the SDD does not declare narrows the transition to one branch, so it fires where the row says it should fire every time. Remove the expression, or add the IF cell that justifies it.`);
|
|
1713
1891
|
};
|
|
1714
|
-
var
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
const
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
id,
|
|
1722
|
-
var: id,
|
|
1723
|
-
value: id,
|
|
1724
|
-
source: outputSourceFor(taskType, row),
|
|
1725
|
-
target: `=${id}`,
|
|
1726
|
-
elementId
|
|
1727
|
-
};
|
|
1892
|
+
var apiEventFinding = (row, rules, wanted, path, label) => {
|
|
1893
|
+
if (wanted === undefined || isBlank(wanted))
|
|
1894
|
+
return;
|
|
1895
|
+
const carried = rules.map((r) => r?.businessEventType).filter((one) => typeof one === "string");
|
|
1896
|
+
if (carried.includes(wanted))
|
|
1897
|
+
return;
|
|
1898
|
+
return error(carried.length === 0 ? "STRICT_SDD_CONDITION_SELECTOR_MISSING" : "STRICT_SDD_CONDITION_SELECTOR_MISMATCH", `${path}[${row.displayName}].rule`, carried.length === 0 ? `${label}: the SDD row ${rowLabel(row)} declares WHEN \`api-event\` on business event "${wanted}", and the caseplan's \`api-event\` names no \`businessEventType\`. The rule then listens for nothing and never fires. Set \`businessEventType\` to "${wanted}".` : `${label}: the SDD row ${rowLabel(row)} declares WHEN \`api-event\` on business event "${wanted}", and the caseplan's \`api-event\` listens for "${carried.join('", "')}". Rules match on the exact name, so it fires on a different event. Set \`businessEventType\` to "${wanted}".`);
|
|
1728
1899
|
};
|
|
1729
|
-
var
|
|
1730
|
-
|
|
1731
|
-
|
|
1900
|
+
var whenCellFinding = (row, planRules, path, label, resolve, ownRules) => {
|
|
1901
|
+
if (row.when === undefined)
|
|
1902
|
+
return;
|
|
1903
|
+
const { rule, selectors: named } = whenRule(row.when);
|
|
1904
|
+
if (!rule || isBlank(rule))
|
|
1732
1905
|
return;
|
|
1906
|
+
const wanted = targetsOf(rule, named);
|
|
1907
|
+
const matching = planRules.filter((r) => norm(r?.rule) === norm(rule));
|
|
1908
|
+
if (matching.length === 0) {
|
|
1909
|
+
return error("STRICT_SDD_CONDITION_RULE_MISMATCH", `${path}[${row.displayName}].rule`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\`, no rule on it uses that predicate. A different predicate starts the task at a different moment. Re-read that row's WHEN cell.`);
|
|
1733
1910
|
}
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
var extractOutputFor = (row, elementId, taken, type, taskType) => {
|
|
1738
|
-
const leaf = row.field.split(".").at(-1) ?? row.field;
|
|
1739
|
-
const base = leaf === "" ? leaf : `${leaf[0].toLowerCase()}${leaf.slice(1)}`;
|
|
1740
|
-
const id = dedupedId(base, taken);
|
|
1741
|
-
return {
|
|
1742
|
-
name: leaf,
|
|
1743
|
-
...type !== undefined && { type },
|
|
1744
|
-
id,
|
|
1745
|
-
var: row.variable,
|
|
1746
|
-
value: row.variable,
|
|
1747
|
-
source: outputSourceFor(taskType, row.field),
|
|
1748
|
-
target: `=${id}`,
|
|
1749
|
-
originalVar: id,
|
|
1750
|
-
elementId
|
|
1751
|
-
};
|
|
1752
|
-
};
|
|
1753
|
-
var customOutputFor = (row, type) => ({
|
|
1754
|
-
name: row.variable,
|
|
1755
|
-
...type !== undefined && { type },
|
|
1756
|
-
custom: true,
|
|
1757
|
-
var: row.variable,
|
|
1758
|
-
value: canonicalExpression(unquoteLiteral(row.expression)),
|
|
1759
|
-
source: canonicalExpression(unquoteLiteral(row.expression)),
|
|
1760
|
-
target: "",
|
|
1761
|
-
body: "",
|
|
1762
|
-
elementId: "root"
|
|
1763
|
-
});
|
|
1764
|
-
var canonicalExpression = (value) => value.startsWith("=metadata.") ? `=js:${value.slice(1)}` : value;
|
|
1765
|
-
var unquoteLiteral = (cell) => {
|
|
1766
|
-
const text = cell.trim();
|
|
1767
|
-
const quote = text[0];
|
|
1768
|
-
if (quote !== '"' && quote !== "'")
|
|
1769
|
-
return cell;
|
|
1770
|
-
if (text.length < 2 || text[text.length - 1] !== quote)
|
|
1771
|
-
return cell;
|
|
1772
|
-
const inner = text.slice(1, -1);
|
|
1773
|
-
for (let at = 0;at < inner.length; at += 1) {
|
|
1774
|
-
if (inner[at] === "\\") {
|
|
1775
|
-
at += 1;
|
|
1776
|
-
continue;
|
|
1777
|
-
}
|
|
1778
|
-
if (inner[at] === quote)
|
|
1779
|
-
return cell;
|
|
1911
|
+
if (norm(rule) === norm("api-event")) {
|
|
1912
|
+
const own = (ownRules ?? []).filter((r) => norm(r?.rule) === norm(rule));
|
|
1913
|
+
return apiEventFinding(row, own.length > 0 ? own : matching, named[0], path, label);
|
|
1780
1914
|
}
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
if (found === undefined) {
|
|
1788
|
-
return `the Inputs row reads an output (${target}) that no task in this SDD declares`;
|
|
1915
|
+
if (wanted.length === 0)
|
|
1916
|
+
return;
|
|
1917
|
+
const own = (ownRules ?? []).filter((r) => norm(r?.rule) === norm(rule));
|
|
1918
|
+
const targeted = own.length > 0 ? own : matching;
|
|
1919
|
+
if (!targeted.some(hasSelector)) {
|
|
1920
|
+
return error("STRICT_SDD_CONDITION_SELECTOR_MISSING", `${path}[${row.displayName}].rule`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\` on "${wanted.join('", "')}", and the caseplan's \`${rule}\` carries no target. The predicate then refers to nothing, so it never becomes true and the gate never opens. Set the rule's \`selected*\` field to the target the row names.`);
|
|
1789
1921
|
}
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1922
|
+
if (resolve === undefined)
|
|
1923
|
+
return;
|
|
1924
|
+
const resolved = targeted.map((candidate) => ruleTargets(candidate, resolve, sameLabel));
|
|
1925
|
+
const pointsAt = resolved.flatMap((target) => target.names);
|
|
1926
|
+
if (pointsAt.length === 0)
|
|
1927
|
+
return;
|
|
1928
|
+
const missed = wanted.filter((want) => {
|
|
1929
|
+
const denotes = resolve.label(want);
|
|
1930
|
+
return !pointsAt.some((got) => sameLabel(got, want) || denotes !== undefined && got === denotes);
|
|
1931
|
+
});
|
|
1932
|
+
const conflict = resolved.find((target) => target.conflict !== undefined);
|
|
1933
|
+
if (missed.length === 0 && conflict?.conflict !== undefined) {
|
|
1934
|
+
return error("STRICT_SDD_CONDITION_SELECTOR_MISMATCH", `${path}[${row.displayName}].rule`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\` on "${wanted.join('", "')}", and the caseplan's \`${rule}\` names "${conflict.names.join('", "')}" but its \`slaId\` is the SLA of "${conflict.conflict}". The rule watches one stage's SLA while naming another, so it fires on a deadline the row did not ask for. Point \`slaId\` at the SLA of "${wanted.join('", "')}" and drop the \`selected*\` field, which convert does not write for this rule.`);
|
|
1794
1935
|
}
|
|
1795
|
-
|
|
1936
|
+
if (missed.length === 0)
|
|
1937
|
+
return;
|
|
1938
|
+
return error("STRICT_SDD_CONDITION_SELECTOR_MISMATCH", `${path}[${row.displayName}].rule`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\` on "${missed.join('", "')}", and the caseplan's \`${rule}\` points at "${pointsAt.join('", "')}". A selector that resolves to the wrong target builds a transition the SDD never asked for and leaves the declared one absent, which no count or predicate check can see.`);
|
|
1796
1939
|
};
|
|
1797
|
-
var
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1940
|
+
var conditionRuleFindings = (declared, planConditions, path, label, resolve) => {
|
|
1941
|
+
if (declared.length === 0)
|
|
1942
|
+
return [];
|
|
1943
|
+
const conditions = planConditions.map(asRecord);
|
|
1944
|
+
const rulesOf = (of) => of.flatMap((condition) => asArray(condition?.rules).flatMap((group) => asArray(group).map(asRecord)));
|
|
1945
|
+
const planRules = rulesOf(conditions);
|
|
1946
|
+
if (planRules.length === 0)
|
|
1947
|
+
return [];
|
|
1948
|
+
const owners = bindByName(declared, conditions);
|
|
1949
|
+
const taken = new Set(owners.filter((one) => one !== undefined));
|
|
1950
|
+
const openRows = declared.map((row, at) => ({ row, at })).filter(({ at }) => owners[at] === undefined);
|
|
1951
|
+
const openConditions = conditions.filter((one) => !taken.has(one));
|
|
1952
|
+
const gated = (condition) => rulesOf([condition]).some((rule) => {
|
|
1953
|
+
const expression = rule?.conditionExpression;
|
|
1954
|
+
return typeof expression === "string" && !isBlank(expression);
|
|
1955
|
+
});
|
|
1956
|
+
const rowGated = (row) => row.ungated === true ? false : row.ifExpression !== undefined ? true : undefined;
|
|
1957
|
+
const shapeKey = (rule, isGated) => {
|
|
1958
|
+
const name = norm(rule);
|
|
1959
|
+
if (name === "" || isGated === undefined)
|
|
1960
|
+
return;
|
|
1961
|
+
return `${name}|${isGated ? "gated" : "open"}`;
|
|
1804
1962
|
};
|
|
1805
|
-
for (const
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
});
|
|
1811
|
-
}
|
|
1812
|
-
for (const row of task.expressionInputs ?? []) {
|
|
1813
|
-
offer(String(row.name ?? ""), RANK_EXPRESSION, {
|
|
1814
|
-
name: String(row.name ?? ""),
|
|
1815
|
-
value: canonicalExpression(expandXrefs(String(row.expression ?? row.value ?? ""), {
|
|
1816
|
-
outputs,
|
|
1817
|
-
where,
|
|
1818
|
-
unresolved
|
|
1819
|
-
})),
|
|
1820
|
-
elementId
|
|
1821
|
-
});
|
|
1963
|
+
for (const [rowAt, conditionAt] of pairLeftovers(openRows.map(({ row }) => shapeKey(whenRule(row.when ?? "").rule, rowGated(row))), openConditions.map((condition) => {
|
|
1964
|
+
const rules = new Set(rulesOf([condition]).map((r) => norm(r?.rule)));
|
|
1965
|
+
return rules.size === 1 ? shapeKey([...rules][0], gated(condition)) : undefined;
|
|
1966
|
+
}))) {
|
|
1967
|
+
owners[openRows[rowAt].at] = openConditions[conditionAt];
|
|
1822
1968
|
}
|
|
1823
|
-
|
|
1824
|
-
const
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1969
|
+
const unownedBy = (at) => {
|
|
1970
|
+
const others = new Set(owners.filter((one, index) => index !== at && one !== undefined));
|
|
1971
|
+
return rulesOf(conditions.filter((one) => !others.has(one)));
|
|
1972
|
+
};
|
|
1973
|
+
return declared.flatMap((row, at) => [
|
|
1974
|
+
whenCellFinding(row, planRules, path, label, resolve, owners[at] === undefined ? undefined : rulesOf([owners[at]])),
|
|
1975
|
+
undeclaredGateFinding(row, unownedBy(at), path, label, resolve)
|
|
1976
|
+
]).filter((finding) => finding !== undefined);
|
|
1977
|
+
};
|
|
1978
|
+
var CONDITION_FLAGS = [
|
|
1979
|
+
["interrupting", "isInterrupting", "Interrupting"],
|
|
1980
|
+
["marksComplete", "marksStageComplete", "Marks Stage Complete"]
|
|
1981
|
+
];
|
|
1982
|
+
var conditionShapeFindings = (declared, planConditions, path, label) => {
|
|
1983
|
+
const findings = [];
|
|
1984
|
+
const bound = bindConditions(declared, planConditions);
|
|
1985
|
+
for (const [at, row] of declared.entries()) {
|
|
1986
|
+
const { match, shaped } = bound[at];
|
|
1987
|
+
if (match === undefined)
|
|
1834
1988
|
continue;
|
|
1989
|
+
const planName = String(match.displayName ?? "");
|
|
1990
|
+
const where = shaped ? planName : row.displayName;
|
|
1991
|
+
const which = shaped ? `the condition "${planName}", the only one in this scope no row names that holds this row's rule,` : "the condition";
|
|
1992
|
+
if (row.exitType !== undefined) {
|
|
1993
|
+
const has = typeof match.type === "string" && match.type !== "";
|
|
1994
|
+
const effective = norm(has ? match.type : "exit-only");
|
|
1995
|
+
if (effective !== norm(row.exitType)) {
|
|
1996
|
+
findings.push(error("STRICT_SDD_CONDITION_EXIT_TYPE_MISMATCH", `${path}[${where}].type`, `${label}: the SDD row ${rowLabel(row)} declares Exit Type "${row.exitType}", ${which} has ${has ? `"${String(match.type)}"` : "no `type` at all, which the runtime reads as exit-only"}. The exit type decides where the case goes when the gate opens, so this sends it somewhere else${has ? "" : " — check for an invented key like `exitType` holding the value `type` should carry"}.`));
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
for (const [cell, field, title] of CONDITION_FLAGS) {
|
|
2000
|
+
const want = row[cell];
|
|
2001
|
+
const got = typeof match[field] === "boolean" ? match[field] : false;
|
|
2002
|
+
if (want === undefined)
|
|
2003
|
+
continue;
|
|
2004
|
+
if (want === got)
|
|
2005
|
+
continue;
|
|
2006
|
+
findings.push(error("STRICT_SDD_CONDITION_FLAG_MISMATCH", `${path}[${where}].${field}`, `${label}: the SDD row ${rowLabel(row)} declares ${title}=${want ? "Yes" : "No"}, ${which} has ${typeof match[field] === "boolean" ? String(got) : "no such field, which the runtime reads as false"}.`));
|
|
1835
2007
|
}
|
|
1836
|
-
const id = found.id;
|
|
1837
|
-
offer(name, RANK_PRODUCER, {
|
|
1838
|
-
name,
|
|
1839
|
-
value: row.direct === false ? expandXrefs(String(row.value ?? `=js:vars.${id}`), {
|
|
1840
|
-
outputs,
|
|
1841
|
-
where,
|
|
1842
|
-
unresolved
|
|
1843
|
-
}) : `=vars.${id}`,
|
|
1844
|
-
elementId
|
|
1845
|
-
});
|
|
1846
2008
|
}
|
|
1847
|
-
return
|
|
2009
|
+
return findings;
|
|
2010
|
+
};
|
|
2011
|
+
var undeclaredHandoffFindings = (stage, planConditions, stageLabels, path, label) => {
|
|
2012
|
+
if (stage.exitToColumn !== true)
|
|
2013
|
+
return [];
|
|
2014
|
+
if (stage.exitConditions.some((row) => row.exitToStage !== undefined)) {
|
|
2015
|
+
return [];
|
|
2016
|
+
}
|
|
2017
|
+
return planConditions.flatMap((raw) => {
|
|
2018
|
+
const condition = asRecord(raw);
|
|
2019
|
+
const target = condition?.exitToStageId;
|
|
2020
|
+
if (typeof target !== "string" || target === "")
|
|
2021
|
+
return [];
|
|
2022
|
+
const name = String(condition?.displayName ?? "");
|
|
2023
|
+
const where = name === "" ? String(condition?.id ?? "") : name;
|
|
2024
|
+
const targetLabel = stageLabels.get(target);
|
|
2025
|
+
const named = targetLabel === undefined ? `"${target}"` : `"${targetLabel}" (${target})`;
|
|
2026
|
+
return [
|
|
2027
|
+
error("STRICT_SDD_CONDITION_UNDECLARED_HANDOFF", `${path}[${where}].exitToStageId`, `${label}: the SDD's exit tables name no Exit-To Stage for this stage, so it hands off to no one, and the condition "${where}" routes the case to ${named}. Remove exitToStageId (in Case SDK source, drop \`exitToStage\` from the exitWhen options): the next stage starts from its own entry rule.`)
|
|
2028
|
+
];
|
|
2029
|
+
});
|
|
1848
2030
|
};
|
|
1849
2031
|
|
|
1850
2032
|
// src/services/strict-sdd-audit/sla-durations.ts
|
|
@@ -2016,133 +2198,6 @@ var slaDurationFindings = (sdd, plan) => {
|
|
|
2016
2198
|
return findings;
|
|
2017
2199
|
};
|
|
2018
2200
|
|
|
2019
|
-
// src/services/strict-sdd-audit/inputs.ts
|
|
2020
|
-
var AUDIT_CONNECTOR_TASK_TYPES = new Set([
|
|
2021
|
-
"execute-connector-activity",
|
|
2022
|
-
"wait-for-connector"
|
|
2023
|
-
]);
|
|
2024
|
-
var CONNECTOR_ENVELOPE_INPUTS = new Set([
|
|
2025
|
-
"pathParameters",
|
|
2026
|
-
"queryParameters",
|
|
2027
|
-
"body"
|
|
2028
|
-
]);
|
|
2029
|
-
var resolveEnvelopeKey = (body, name) => {
|
|
2030
|
-
const named = (record, part) => Object.entries(record).find(([key]) => norm(key) === norm(part));
|
|
2031
|
-
const direct = named(body, name);
|
|
2032
|
-
if (direct !== undefined)
|
|
2033
|
-
return { value: direct[1] };
|
|
2034
|
-
const parts = name.split(".").filter((part) => part !== "");
|
|
2035
|
-
if (parts.length < 2)
|
|
2036
|
-
return;
|
|
2037
|
-
let current = body;
|
|
2038
|
-
for (const part of parts) {
|
|
2039
|
-
const record = asRecord(current);
|
|
2040
|
-
if (record === undefined)
|
|
2041
|
-
return;
|
|
2042
|
-
const hit = named(record, part);
|
|
2043
|
-
if (hit === undefined)
|
|
2044
|
-
return;
|
|
2045
|
-
current = hit[1];
|
|
2046
|
-
}
|
|
2047
|
-
return { value: current };
|
|
2048
|
-
};
|
|
2049
|
-
var carriedInput = (found, name) => {
|
|
2050
|
-
const absent = { present: false, value: undefined, valueless: false };
|
|
2051
|
-
const inputs = asArray(found.data.inputs).map(asRecord).filter((i) => i !== undefined);
|
|
2052
|
-
const direct = inputs.find((i) => norm(i.name) === norm(name));
|
|
2053
|
-
if (direct !== undefined) {
|
|
2054
|
-
return {
|
|
2055
|
-
present: true,
|
|
2056
|
-
value: direct.value,
|
|
2057
|
-
valueless: !("value" in direct)
|
|
2058
|
-
};
|
|
2059
|
-
}
|
|
2060
|
-
if (typeof found.type !== "string" || !AUDIT_CONNECTOR_TASK_TYPES.has(found.type)) {
|
|
2061
|
-
return absent;
|
|
2062
|
-
}
|
|
2063
|
-
for (const entry of inputs) {
|
|
2064
|
-
const entryName = entry.name;
|
|
2065
|
-
if (typeof entryName !== "string" || !CONNECTOR_ENVELOPE_INPUTS.has(entryName)) {
|
|
2066
|
-
continue;
|
|
2067
|
-
}
|
|
2068
|
-
const body = asRecord(entry.body);
|
|
2069
|
-
if (body === undefined)
|
|
2070
|
-
continue;
|
|
2071
|
-
const carried = resolveEnvelopeKey(body, name);
|
|
2072
|
-
if (carried !== undefined) {
|
|
2073
|
-
return { present: true, value: carried.value, valueless: false };
|
|
2074
|
-
}
|
|
2075
|
-
}
|
|
2076
|
-
return absent;
|
|
2077
|
-
};
|
|
2078
|
-
var declaredInputNames = (task) => {
|
|
2079
|
-
const names = [];
|
|
2080
|
-
const add = (name) => {
|
|
2081
|
-
if (!names.some((existing) => norm(existing) === norm(name))) {
|
|
2082
|
-
names.push(name);
|
|
2083
|
-
}
|
|
2084
|
-
};
|
|
2085
|
-
for (const entry of task.literalInputs)
|
|
2086
|
-
add(entry.name);
|
|
2087
|
-
for (const entry of task.expressionInputs)
|
|
2088
|
-
add(entry.name);
|
|
2089
|
-
for (const entry of task.producerInputs)
|
|
2090
|
-
add(entry.name);
|
|
2091
|
-
return names;
|
|
2092
|
-
};
|
|
2093
|
-
var boundInputNames = (task) => task.boundInputs.map((entry) => entry.name);
|
|
2094
|
-
var emptyLiteralInputNames = (task) => task.literalInputs.filter((entry) => isBlank(entry.value)).map((entry) => entry.name);
|
|
2095
|
-
var envelopeFindings = (task, found, path, label) => {
|
|
2096
|
-
const findings = [];
|
|
2097
|
-
const columns = [
|
|
2098
|
-
{
|
|
2099
|
-
declared: task.required,
|
|
2100
|
-
actual: found.isRequired,
|
|
2101
|
-
column: "Required",
|
|
2102
|
-
field: "isRequired"
|
|
2103
|
-
},
|
|
2104
|
-
{
|
|
2105
|
-
declared: task.runOnlyOnce,
|
|
2106
|
-
actual: found.shouldRunOnlyOnce,
|
|
2107
|
-
column: "Run Only Once",
|
|
2108
|
-
field: "shouldRunOnlyOnce"
|
|
2109
|
-
}
|
|
2110
|
-
];
|
|
2111
|
-
for (const { declared, actual, column, field: field2 } of columns) {
|
|
2112
|
-
if (declared === undefined)
|
|
2113
|
-
continue;
|
|
2114
|
-
if (typeof actual === "boolean" && actual === declared)
|
|
2115
|
-
continue;
|
|
2116
|
-
findings.push(error("STRICT_SDD_TASK_ENVELOPE_MISMATCH", `${path}.${field2}`, `${label}: the SDD Tasks table declares a "${column}" value for this task and its \`${field2}\` ${typeof actual === "boolean" ? "disagrees" : "is absent"}. Set it from that column.`));
|
|
2117
|
-
}
|
|
2118
|
-
return findings;
|
|
2119
|
-
};
|
|
2120
|
-
var emptyInputFindings = (task, found, path, label) => {
|
|
2121
|
-
const declared = declaredInputNames(task);
|
|
2122
|
-
if (declared.length === 0)
|
|
2123
|
-
return [];
|
|
2124
|
-
if (Object.keys(found.data).length === 0)
|
|
2125
|
-
return [];
|
|
2126
|
-
const bound = boundInputNames(task);
|
|
2127
|
-
const emptyLiteral = emptyLiteralInputNames(task);
|
|
2128
|
-
const findings = [];
|
|
2129
|
-
const names = (list, name) => list.some((entry) => norm(entry) === norm(name));
|
|
2130
|
-
for (const name of declared) {
|
|
2131
|
-
const carried = carriedInput(found, name);
|
|
2132
|
-
const value = carried.value;
|
|
2133
|
-
const missing = !carried.present;
|
|
2134
|
-
if (!missing && !carried.valueless && (typeof value !== "string" || value.trim() !== "")) {
|
|
2135
|
-
continue;
|
|
2136
|
-
}
|
|
2137
|
-
if (!missing && (names(bound, name) || names(emptyLiteral, name))) {
|
|
2138
|
-
continue;
|
|
2139
|
-
}
|
|
2140
|
-
const declares = names(emptyLiteral, name) ? "the empty literal" : "a value";
|
|
2141
|
-
findings.push(error("STRICT_SDD_INPUT_EMPTY", `${path}.data.inputs[${name}]`, `${label}: the SDD Inputs row for "${name}" declares ${declares}, and the caseplan input ${missing ? "is absent" : carried.valueless ? "carries no `value` at all" : "is empty"}. Nothing reaches the task for it, and none of the value comparisons can run against an input that carries nothing. ${missing ? "The row exists, so the input has to exist too — add it, carrying that row's value." : carried.valueless ? "Its value has to live under `value` — data under `body` is the connector envelope, which does not apply to this task type." : "Re-read that row and set the input from it."}`));
|
|
2142
|
-
}
|
|
2143
|
-
return findings;
|
|
2144
|
-
};
|
|
2145
|
-
|
|
2146
2201
|
// src/services/strict-sdd-audit/task-inputs.ts
|
|
2147
2202
|
var PAYLOAD_SLOTS = ["body", "queryParameters", "pathParameters"];
|
|
2148
2203
|
var usesSlotInputs = (found) => {
|
|
@@ -2153,7 +2208,7 @@ var usesSlotInputs = (found) => {
|
|
|
2153
2208
|
return names.includes(norm("pathParameters")) || names.includes(norm("queryParameters"));
|
|
2154
2209
|
};
|
|
2155
2210
|
var carriesPayload = (inputs) => PAYLOAD_SLOTS.some((slot) => {
|
|
2156
|
-
const input = inputs
|
|
2211
|
+
const input = inputNamed(inputs, slot);
|
|
2157
2212
|
if (typeof input?.body === "string")
|
|
2158
2213
|
return input.body !== "";
|
|
2159
2214
|
return Object.keys(asRecord(input?.body) ?? {}).length > 0;
|
|
@@ -2163,10 +2218,10 @@ var boundInputFindings = (task, found, path, label) => {
|
|
|
2163
2218
|
return [];
|
|
2164
2219
|
const inputs = asArray(found.data.inputs).map(asRecord).filter((i) => i !== undefined);
|
|
2165
2220
|
const findings = [];
|
|
2166
|
-
const isConnector =
|
|
2221
|
+
const isConnector = CONNECTOR_CALL_TASK_TYPES.has(found.type ?? "") && usesSlotInputs(found);
|
|
2167
2222
|
let connectorRows = 0;
|
|
2168
2223
|
for (const declared of task.boundInputs) {
|
|
2169
|
-
const input = inputs
|
|
2224
|
+
const input = inputNamed(inputs, declared.name);
|
|
2170
2225
|
if (input) {
|
|
2171
2226
|
const valueless = !("value" in input);
|
|
2172
2227
|
if (!valueless && input.value !== "")
|
|
@@ -2214,6 +2269,35 @@ var customOutputFindings = (task, found, path, label) => {
|
|
|
2214
2269
|
}
|
|
2215
2270
|
return findings;
|
|
2216
2271
|
};
|
|
2272
|
+
var outputTypeFindings = (task, found, path, label) => {
|
|
2273
|
+
if (task.extractRows.length === 0 && task.customRows.length === 0) {
|
|
2274
|
+
return [];
|
|
2275
|
+
}
|
|
2276
|
+
const outputs = asArray(found.data.outputs).map(asRecord).filter((o) => o !== undefined);
|
|
2277
|
+
const declared = [
|
|
2278
|
+
...task.extractRows.map((row) => ({
|
|
2279
|
+
row: `\`${row.field} -> ${row.variable}\``,
|
|
2280
|
+
type: row.type,
|
|
2281
|
+
output: outputs.find((o) => o.custom !== true && o.source === outputSourceFor(found.type, row.field) && o.var === row.variable)
|
|
2282
|
+
})),
|
|
2283
|
+
...task.customRows.map((row) => ({
|
|
2284
|
+
row: `\`${row.variable} = ${row.expression}\``,
|
|
2285
|
+
type: row.type,
|
|
2286
|
+
output: outputs.find((o) => o.custom === true && o.var === row.variable)
|
|
2287
|
+
}))
|
|
2288
|
+
];
|
|
2289
|
+
const findings = [];
|
|
2290
|
+
for (const { row, type, output } of declared) {
|
|
2291
|
+
const wanted = outputTypeCell(type).type;
|
|
2292
|
+
if (wanted === undefined || output === undefined)
|
|
2293
|
+
continue;
|
|
2294
|
+
const actual = typeof output.type === "string" ? output.type : "";
|
|
2295
|
+
if (actual === "" || actual === wanted)
|
|
2296
|
+
continue;
|
|
2297
|
+
findings.push(error("STRICT_SDD_OUTPUT_TYPE_MISMATCH", `${path}.data.outputs[${String(output.name ?? output.var)}].type`, `${label}: the SDD row ${row} types this output "${type}", so the output's \`type\` must be "${wanted}"; the caseplan has "${actual}". The row's Type is the resource's output-schema type, which is what Studio Web and \`uip maestro case splice --described\` write.`));
|
|
2298
|
+
}
|
|
2299
|
+
return findings;
|
|
2300
|
+
};
|
|
2217
2301
|
var gatedSlaFindings = (rules, path, label) => {
|
|
2218
2302
|
if (rules.length === 0)
|
|
2219
2303
|
return [];
|
|
@@ -2232,7 +2316,7 @@ var gatedSlaFindings = (rules, path, label) => {
|
|
|
2232
2316
|
// src/services/strict-sdd-audit/case-level.ts
|
|
2233
2317
|
var variablePlacementFindings = (sdd, plan, name, key) => {
|
|
2234
2318
|
const category = sdd.variableCategories.get(key);
|
|
2235
|
-
const required =
|
|
2319
|
+
const required = VARIABLE_PLACEMENT2.get(category ?? "");
|
|
2236
2320
|
if (category === undefined || required === undefined)
|
|
2237
2321
|
return [];
|
|
2238
2322
|
const issues = [];
|
|
@@ -2247,7 +2331,7 @@ var variablePlacementFindings = (sdd, plan, name, key) => {
|
|
|
2247
2331
|
var variableDefaultFindings = (sdd, plan, name, key) => {
|
|
2248
2332
|
const declared = sdd.variableDefaults.get(key);
|
|
2249
2333
|
const category = sdd.variableCategories.get(key);
|
|
2250
|
-
const group =
|
|
2334
|
+
const group = DEFAULT_GROUP2.get(category ?? "");
|
|
2251
2335
|
if (declared === undefined || group === undefined)
|
|
2252
2336
|
return [];
|
|
2253
2337
|
const entry = plan.variableEntries[group].get(key);
|
|
@@ -2269,12 +2353,14 @@ var variableDefaultFindings = (sdd, plan, name, key) => {
|
|
|
2269
2353
|
var compareCaseLevel = (sdd, plan) => {
|
|
2270
2354
|
const issues = [
|
|
2271
2355
|
...coverageFinding("metadata.caseExitRules", "case", "exit", sdd.caseExitRows, plan.caseExitConditions),
|
|
2356
|
+
...conditionRuleFindings(sdd.caseExitConditions.filter((row) => norm(whenRule(row.when ?? "").rule) === norm("api-event")), plan.caseExitConditions, "metadata.caseExitRules", "case"),
|
|
2272
2357
|
...gatedSlaFindings(plan.caseSlaRules, "metadata.slaRules", "case"),
|
|
2273
2358
|
...conditionalSlaFindings(sdd.caseConditionalSlas, plan.caseSlaRules, "metadata.slaRules", "case", plan, sdd.stages, slaReferences(sdd))
|
|
2274
2359
|
];
|
|
2275
2360
|
if (sdd.caseSla && !plan.caseSla) {
|
|
2276
2361
|
issues.push(error("STRICT_SDD_SLA_MISSING", "metadata.slaRules", "case: the SDD declares Case-Level SLA Escalation Rules, the caseplan metadata.slaRules is empty."));
|
|
2277
2362
|
}
|
|
2363
|
+
issues.push(...personaFindings(sdd, plan));
|
|
2278
2364
|
issues.push(...escalationFindings(sdd.caseSlaEscalations, plan.caseSlaRules, "metadata.slaRules", "case", sdd.caseSlaResponses), ...xrefOutputFindings(sdd, plan), ...conditionExpressionFindings(sdd.caseExitConditions, plan.caseExitConditions, plan, "metadata.caseExitRules", "case"), ...conditionNameFindings(sdd.caseExitConditions, plan.caseExitConditions, "metadata.caseExitRules", "case", namesDeclaredOnce(sdd)));
|
|
2279
2365
|
issues.push(...slaDurationFindings(sdd, plan));
|
|
2280
2366
|
const caseDefaults = withoutConditional(plan.caseSlaRules, sdd.caseConditionalSlas, plan, sdd.stages, slaReferences(sdd));
|
|
@@ -2446,7 +2532,39 @@ var slaDurationOf = (block) => {
|
|
|
2446
2532
|
return;
|
|
2447
2533
|
};
|
|
2448
2534
|
|
|
2535
|
+
// src/services/strict-sdd-audit/document-extraction-detail.ts
|
|
2536
|
+
var readDocumentExtraction = (block) => {
|
|
2537
|
+
const read = (label) => {
|
|
2538
|
+
const raw = field(block, label);
|
|
2539
|
+
if (raw === undefined)
|
|
2540
|
+
return;
|
|
2541
|
+
const text = stripTicks(raw).trim();
|
|
2542
|
+
return text === "" || isBlank(text) ? undefined : text;
|
|
2543
|
+
};
|
|
2544
|
+
const extraction = {
|
|
2545
|
+
deployment: read("IXP Deployment"),
|
|
2546
|
+
modelName: read("Model Name"),
|
|
2547
|
+
deploymentKey: read("Deployment Key"),
|
|
2548
|
+
folderKey: read("Folder Key")
|
|
2549
|
+
};
|
|
2550
|
+
const stated = Object.entries(extraction).filter(([, value]) => value !== undefined);
|
|
2551
|
+
return stated.length === 0 ? undefined : Object.fromEntries(stated);
|
|
2552
|
+
};
|
|
2553
|
+
var extractionFields = (block) => {
|
|
2554
|
+
const extraction = readDocumentExtraction(block);
|
|
2555
|
+
return extraction === undefined ? {} : { extraction };
|
|
2556
|
+
};
|
|
2557
|
+
|
|
2449
2558
|
// src/services/strict-sdd-audit/http-request-detail.ts
|
|
2559
|
+
var HTTP_METHODS = new Set([
|
|
2560
|
+
"GET",
|
|
2561
|
+
"POST",
|
|
2562
|
+
"PUT",
|
|
2563
|
+
"PATCH",
|
|
2564
|
+
"DELETE",
|
|
2565
|
+
"HEAD",
|
|
2566
|
+
"OPTIONS"
|
|
2567
|
+
]);
|
|
2450
2568
|
var readHttpRequest = (block) => {
|
|
2451
2569
|
const read = (label) => {
|
|
2452
2570
|
const raw = field(block, label);
|
|
@@ -2500,79 +2618,6 @@ var resourceReference = (block) => {
|
|
|
2500
2618
|
};
|
|
2501
2619
|
};
|
|
2502
2620
|
|
|
2503
|
-
// src/services/sdd-condition-cell.ts
|
|
2504
|
-
var STAGE_SELECTOR_RULES = new Set([
|
|
2505
|
-
"selected-stage-completed",
|
|
2506
|
-
"selected-stage-exited"
|
|
2507
|
-
]);
|
|
2508
|
-
var TASK_SELECTOR_RULES = new Set(["selected-tasks-completed"]);
|
|
2509
|
-
var IF_PARTS = new Map([
|
|
2510
|
-
["conditionexpression", "expression"],
|
|
2511
|
-
["selected tasks", "tasks"],
|
|
2512
|
-
["selected task", "tasks"],
|
|
2513
|
-
["selected stage", "stage"],
|
|
2514
|
-
["selected stages", "stage"]
|
|
2515
|
-
]);
|
|
2516
|
-
var splitIfParts = (text) => {
|
|
2517
|
-
const pieces = [];
|
|
2518
|
-
let current = "";
|
|
2519
|
-
let quote;
|
|
2520
|
-
for (let index = 0;index < text.length; index += 1) {
|
|
2521
|
-
const char = text[index];
|
|
2522
|
-
if (quote !== undefined) {
|
|
2523
|
-
current += char;
|
|
2524
|
-
if (char === "\\") {
|
|
2525
|
-
current += text[index + 1] ?? "";
|
|
2526
|
-
index += 1;
|
|
2527
|
-
} else if (char === quote)
|
|
2528
|
-
quote = undefined;
|
|
2529
|
-
continue;
|
|
2530
|
-
}
|
|
2531
|
-
if (char === '"' || char === "'" || char === "`") {
|
|
2532
|
-
quote = char;
|
|
2533
|
-
current += char;
|
|
2534
|
-
continue;
|
|
2535
|
-
}
|
|
2536
|
-
if (char !== ";") {
|
|
2537
|
-
current += char;
|
|
2538
|
-
continue;
|
|
2539
|
-
}
|
|
2540
|
-
pieces.push(current);
|
|
2541
|
-
current = "";
|
|
2542
|
-
}
|
|
2543
|
-
pieces.push(current);
|
|
2544
|
-
return pieces;
|
|
2545
|
-
};
|
|
2546
|
-
var parseIfCell = (cell) => {
|
|
2547
|
-
const text = cell.trim();
|
|
2548
|
-
const found = {
|
|
2549
|
-
expression: ""
|
|
2550
|
-
};
|
|
2551
|
-
const parts = [];
|
|
2552
|
-
let structured = false;
|
|
2553
|
-
const partKey = (text2) => text2.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
2554
|
-
for (const piece of splitIfParts(text)) {
|
|
2555
|
-
const at = piece.indexOf(":");
|
|
2556
|
-
const key = at === -1 ? "" : partKey(piece.slice(0, at));
|
|
2557
|
-
const slot = IF_PARTS.get(key);
|
|
2558
|
-
if (slot === undefined) {
|
|
2559
|
-
parts.push(piece);
|
|
2560
|
-
continue;
|
|
2561
|
-
}
|
|
2562
|
-
structured = true;
|
|
2563
|
-
const value = piece.slice(at + 1).trim();
|
|
2564
|
-
if (slot === "expression")
|
|
2565
|
-
found.expression = value;
|
|
2566
|
-
else if (slot === "tasks")
|
|
2567
|
-
found.tasks = value;
|
|
2568
|
-
else
|
|
2569
|
-
found.stage = value;
|
|
2570
|
-
}
|
|
2571
|
-
if (!structured)
|
|
2572
|
-
return { expression: text };
|
|
2573
|
-
return found;
|
|
2574
|
-
};
|
|
2575
|
-
|
|
2576
2621
|
// src/services/strict-sdd-audit/task-detail-rows.ts
|
|
2577
2622
|
var entryConditionBlocks = (section) => {
|
|
2578
2623
|
const block = section.body;
|
|
@@ -2659,6 +2704,7 @@ var declaredCell = (row, at) => {
|
|
|
2659
2704
|
const text = unwrapCodeSpan(cellAt(row, at));
|
|
2660
2705
|
return isBlank(text) ? "" : text;
|
|
2661
2706
|
};
|
|
2707
|
+
var API_EVENT_RULE = "api-event";
|
|
2662
2708
|
var ARGUMENT_RULES = new Set([
|
|
2663
2709
|
...STAGE_SELECTOR_RULES,
|
|
2664
2710
|
...TASK_SELECTOR_RULES,
|
|
@@ -2669,6 +2715,8 @@ var labelledWhen = (when, ifCell) => {
|
|
|
2669
2715
|
if (match === null)
|
|
2670
2716
|
return { when };
|
|
2671
2717
|
const [, rule, label] = match;
|
|
2718
|
+
if (rule === API_EVENT_RULE)
|
|
2719
|
+
return { when };
|
|
2672
2720
|
if (!ARGUMENT_RULES.has(rule))
|
|
2673
2721
|
return { when: rule, label };
|
|
2674
2722
|
const parts = parseIfCell(ifCell);
|
|
@@ -2826,15 +2874,21 @@ var detailExtractRows = (block) => {
|
|
|
2826
2874
|
const bindingAt = columnIndex(header, "Binding / Value", "Binding", "Value");
|
|
2827
2875
|
if (fieldAt === undefined || bindingAt === undefined)
|
|
2828
2876
|
continue;
|
|
2877
|
+
const typeAt = columnIndex(header, "Type");
|
|
2829
2878
|
for (const row of tableRows) {
|
|
2830
|
-
const
|
|
2879
|
+
const field = stripTicks(cellAt(row, fieldAt).trim());
|
|
2831
2880
|
const binding = unwrapCodeSpan(cellAt(row, bindingAt));
|
|
2832
|
-
if (!
|
|
2881
|
+
if (!field || isBlank(field) || !binding.startsWith("->"))
|
|
2833
2882
|
continue;
|
|
2834
2883
|
const variable = binding.slice(2).trim();
|
|
2835
2884
|
if (!variable || isBlank(variable))
|
|
2836
2885
|
continue;
|
|
2837
|
-
|
|
2886
|
+
const type = typeAt === undefined ? "" : stripTicks(cellAt(row, typeAt).trim()).trim();
|
|
2887
|
+
rows.push({
|
|
2888
|
+
field,
|
|
2889
|
+
variable,
|
|
2890
|
+
...type !== "" && !isBlank(type) && { type }
|
|
2891
|
+
});
|
|
2838
2892
|
}
|
|
2839
2893
|
}
|
|
2840
2894
|
return rows;
|
|
@@ -2848,10 +2902,10 @@ var timerFields = (block) => {
|
|
|
2848
2902
|
if (!declared || isBlank(declared))
|
|
2849
2903
|
return {};
|
|
2850
2904
|
if (TIMER_VALUE_FIELDS.has(norm(declared).replaceAll(" ", ""))) {
|
|
2851
|
-
const
|
|
2905
|
+
const value = stripTicks(field(block, "Value") ?? "");
|
|
2852
2906
|
return {
|
|
2853
2907
|
timerType: declared,
|
|
2854
|
-
...
|
|
2908
|
+
...value && !isBlank(value) && { timerValue: value }
|
|
2855
2909
|
};
|
|
2856
2910
|
}
|
|
2857
2911
|
const configured = stripTicks(field(block, "Timer Configuration") ?? "");
|
|
@@ -2895,15 +2949,6 @@ var resourceNameOf = (block) => {
|
|
|
2895
2949
|
}
|
|
2896
2950
|
return resourceReference(block).name;
|
|
2897
2951
|
};
|
|
2898
|
-
var HTTP_METHODS = new Set([
|
|
2899
|
-
"GET",
|
|
2900
|
-
"POST",
|
|
2901
|
-
"PUT",
|
|
2902
|
-
"PATCH",
|
|
2903
|
-
"DELETE",
|
|
2904
|
-
"HEAD",
|
|
2905
|
-
"OPTIONS"
|
|
2906
|
-
]);
|
|
2907
2952
|
var readConnectorTail = (text, into) => {
|
|
2908
2953
|
const open = text.lastIndexOf("(");
|
|
2909
2954
|
const close = text.lastIndexOf(")");
|
|
@@ -2944,10 +2989,12 @@ var readRecipient = (block) => {
|
|
|
2944
2989
|
if (declared === undefined || isBlank(declared))
|
|
2945
2990
|
return;
|
|
2946
2991
|
const text = stripTicks(declared).trim();
|
|
2992
|
+
if (text.startsWith("="))
|
|
2993
|
+
return { kind: "expression", value: text };
|
|
2947
2994
|
const at = text.indexOf(":");
|
|
2948
2995
|
if (at === -1)
|
|
2949
2996
|
return { kind: "", value: text };
|
|
2950
|
-
const value = text.slice(at + 1)
|
|
2997
|
+
const value = stripTicks(text.slice(at + 1));
|
|
2951
2998
|
if (value === "" || isBlank(value))
|
|
2952
2999
|
return;
|
|
2953
3000
|
return { kind: norm(text.slice(0, at)), value };
|
|
@@ -2993,6 +3040,15 @@ var readTaskDetail = (section) => {
|
|
|
2993
3040
|
type: stripTicks(field(block, "Type") ?? ""),
|
|
2994
3041
|
resolvedResource: field(block, "Resolved Resource"),
|
|
2995
3042
|
identityResolved: identity.length > 0 && !identity.some(hasUnresolvedMarker),
|
|
3043
|
+
identityStated: (() => {
|
|
3044
|
+
const stated = [
|
|
3045
|
+
field(block, "Resource Identity"),
|
|
3046
|
+
field(block, "Connection ID"),
|
|
3047
|
+
field(block, "Activity Type ID"),
|
|
3048
|
+
field(block, "Action App ID")
|
|
3049
|
+
].filter((value) => !!value && !isBlank(value));
|
|
3050
|
+
return stated.length > 0 && !stated.some(hasUnresolvedMarker);
|
|
3051
|
+
})(),
|
|
2996
3052
|
folder: field(block, "Deployment Folder") ?? field(block, "Folder Path") ?? resourceReference(block).folder,
|
|
2997
3053
|
...(() => {
|
|
2998
3054
|
const skip = namedValue(unwrapCodeSpan(field(block, "Skip Condition") ?? columnField(block, "Skip Condition") ?? ""));
|
|
@@ -3022,7 +3078,8 @@ var readTaskDetail = (section) => {
|
|
|
3022
3078
|
customRows: detailCustomRows(block),
|
|
3023
3079
|
unreadTables: unreadIoTables(block),
|
|
3024
3080
|
...timerFields(block),
|
|
3025
|
-
...httpFields(block)
|
|
3081
|
+
...httpFields(block),
|
|
3082
|
+
...extractionFields(block)
|
|
3026
3083
|
};
|
|
3027
3084
|
};
|
|
3028
3085
|
var TIMER_CONFIGURATION_TYPES = new Map([
|
|
@@ -3070,8 +3127,8 @@ var literalDifference = (actual, declared) => {
|
|
|
3070
3127
|
return "does not match the literal the SDD declares";
|
|
3071
3128
|
};
|
|
3072
3129
|
var missingLiteralFields = (declared, actual) => {
|
|
3073
|
-
const [declaredError, declaredValue] =
|
|
3074
|
-
const [actualError, actualValue] =
|
|
3130
|
+
const [declaredError, declaredValue] = catchError2(() => JSON.parse(declared));
|
|
3131
|
+
const [actualError, actualValue] = catchError2(() => JSON.parse(actual));
|
|
3075
3132
|
if (declaredError || actualError)
|
|
3076
3133
|
return;
|
|
3077
3134
|
const declaredRecord = asRecord(declaredValue);
|
|
@@ -3090,7 +3147,18 @@ var missingLiteralFields = (declared, actual) => {
|
|
|
3090
3147
|
firstMissing: missing[0]
|
|
3091
3148
|
};
|
|
3092
3149
|
};
|
|
3093
|
-
var
|
|
3150
|
+
var pickedValue = (found, actual, rootBindings) => {
|
|
3151
|
+
const id = /^=bindings\.(.+)$/.exec(actual)?.[1];
|
|
3152
|
+
if (id === undefined)
|
|
3153
|
+
return;
|
|
3154
|
+
const taskCopies = Array.isArray(found.data.bindings) ? found.data.bindings.map(asRecord) : [];
|
|
3155
|
+
const binding = rootBindings.find((entry) => entry.id === id) ?? taskCopies.find((entry) => entry?.id === id);
|
|
3156
|
+
return binding?.resource !== "Connection" && typeof binding?.default === "string" ? binding.default : undefined;
|
|
3157
|
+
};
|
|
3158
|
+
var pickedFindings = (declared, picked, path, label) => sameLiteral(declared.value, picked) ? [] : [
|
|
3159
|
+
warning("STRICT_SDD_LITERAL_MISMATCH", `${path}.data.inputs[${declared.name}]`, `${label}: input "${declared.name}" picks a resource that ${literalDifference(picked, declared.value)}. Re-read the Inputs row for "${declared.name}" and resolve the picker to the resource it names.`)
|
|
3160
|
+
];
|
|
3161
|
+
var literalFindings = (task, found, rootBindings, path, label) => {
|
|
3094
3162
|
const findings = [];
|
|
3095
3163
|
for (const declared of task.literalInputs) {
|
|
3096
3164
|
const carried = carriedInput(found, declared.name);
|
|
@@ -3099,6 +3167,11 @@ var literalFindings = (task, found, path, label) => {
|
|
|
3099
3167
|
const actual = carried.value;
|
|
3100
3168
|
if (typeof actual !== "string")
|
|
3101
3169
|
continue;
|
|
3170
|
+
const picked = pickedValue(found, actual, rootBindings);
|
|
3171
|
+
if (picked !== undefined) {
|
|
3172
|
+
findings.push(...pickedFindings(declared, picked, path, label));
|
|
3173
|
+
continue;
|
|
3174
|
+
}
|
|
3102
3175
|
if (actual.startsWith("=")) {
|
|
3103
3176
|
findings.push(error("STRICT_SDD_LITERAL_MISMATCH", `${path}.data.inputs[${declared.name}]`, `${label}: input "${declared.name}" carries an expression where the SDD's Inputs row declares a literal value. Re-read that row and set the literal it gives.`));
|
|
3104
3177
|
continue;
|
|
@@ -3116,181 +3189,41 @@ var literalFindings = (task, found, path, label) => {
|
|
|
3116
3189
|
return findings;
|
|
3117
3190
|
};
|
|
3118
3191
|
|
|
3119
|
-
// src/services/strict-sdd-audit/
|
|
3120
|
-
var
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
const owners = idsIn(rule?.slaId).map((id) => resolve.sla?.(id)).filter((name) => name !== undefined);
|
|
3129
|
-
if (named.length === 0)
|
|
3130
|
-
return { names: owners };
|
|
3131
|
-
const conflict = owners.find((owner) => !named.some((name) => same(name, owner)));
|
|
3132
|
-
return conflict === undefined ? { names: named } : { names: named, conflict };
|
|
3133
|
-
};
|
|
3134
|
-
|
|
3135
|
-
// src/services/strict-sdd-audit/selectors.ts
|
|
3136
|
-
var sameLabel = (a, b) => norm(a) === norm(b) || normAnnotationless(a) === normAnnotationless(b);
|
|
3137
|
-
var leadingSegment = (name) => {
|
|
3138
|
-
for (const dash of [" - ", " – ", " — "]) {
|
|
3139
|
-
const at = name.indexOf(dash);
|
|
3140
|
-
if (at > 0)
|
|
3141
|
-
return name.slice(0, at);
|
|
3142
|
-
}
|
|
3143
|
-
return name;
|
|
3144
|
-
};
|
|
3145
|
-
var targetsOf = (rule, selectors) => norm(rule) === norm("sla-status-change") ? selectors.slice(0, 1) : selectors;
|
|
3146
|
-
var undeclaredGateFinding = (row, planRules, path, label, resolve) => {
|
|
3147
|
-
if (row.ungated !== true || row.when === undefined)
|
|
3148
|
-
return;
|
|
3149
|
-
const { rule, selectors: named } = whenRule(row.when);
|
|
3150
|
-
if (!rule || isBlank(rule))
|
|
3151
|
-
return;
|
|
3152
|
-
const selectors = targetsOf(rule, named);
|
|
3153
|
-
const belongsToRow = (candidate) => {
|
|
3154
|
-
if (selectors.length === 0)
|
|
3155
|
-
return !hasSelector(candidate);
|
|
3156
|
-
if (!hasSelector(candidate))
|
|
3157
|
-
return false;
|
|
3158
|
-
if (resolve === undefined)
|
|
3159
|
-
return false;
|
|
3160
|
-
const pointsAt = ruleTargets(candidate, resolve, sameLabel).names;
|
|
3161
|
-
if (pointsAt.length === 0)
|
|
3162
|
-
return false;
|
|
3163
|
-
return selectors.some((want) => {
|
|
3164
|
-
const denotes = resolve.label(want);
|
|
3165
|
-
return pointsAt.some((got) => sameLabel(got, want) || denotes !== undefined && got === denotes);
|
|
3166
|
-
});
|
|
3167
|
-
};
|
|
3168
|
-
const gated = planRules.filter((candidate) => {
|
|
3169
|
-
if (norm(candidate?.rule) !== norm(rule))
|
|
3170
|
-
return false;
|
|
3171
|
-
if (!belongsToRow(candidate))
|
|
3172
|
-
return false;
|
|
3173
|
-
const expression = candidate?.conditionExpression;
|
|
3174
|
-
return typeof expression === "string" && !isBlank(expression);
|
|
3175
|
-
});
|
|
3176
|
-
if (gated.length === 0)
|
|
3177
|
-
return;
|
|
3178
|
-
const expressions = [
|
|
3179
|
-
...new Set(gated.map((candidate) => String(candidate?.conditionExpression)).filter((expression) => expression !== ""))
|
|
3180
|
-
];
|
|
3181
|
-
const on = selectors.length > 0 ? ` on "${selectors.join('", "')}"` : "";
|
|
3182
|
-
return error("STRICT_SDD_CONDITION_EXPRESSION_UNDECLARED", `${path}[${row.displayName}].conditionExpression`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\`${on} with no IF gate, and the caseplan's \`${rule}\` carries \`${expressions.join("`, `")}\`. A gate the SDD does not declare narrows the transition to one branch, so it fires where the row says it should fire every time. Remove the expression, or add the IF cell that justifies it.`);
|
|
3183
|
-
};
|
|
3184
|
-
var whenCellFinding = (row, planRules, path, label, resolve, ownRules) => {
|
|
3185
|
-
if (row.when === undefined)
|
|
3186
|
-
return;
|
|
3187
|
-
const { rule, selectors: named } = whenRule(row.when);
|
|
3188
|
-
if (!rule || isBlank(rule))
|
|
3189
|
-
return;
|
|
3190
|
-
const wanted = targetsOf(rule, named);
|
|
3191
|
-
const matching = planRules.filter((r) => norm(r?.rule) === norm(rule));
|
|
3192
|
-
if (matching.length === 0) {
|
|
3193
|
-
return error("STRICT_SDD_CONDITION_RULE_MISMATCH", `${path}[${row.displayName}].rule`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\`, no rule on it uses that predicate. A different predicate starts the task at a different moment. Re-read that row's WHEN cell.`);
|
|
3194
|
-
}
|
|
3195
|
-
if (wanted.length === 0)
|
|
3196
|
-
return;
|
|
3197
|
-
const own = (ownRules ?? []).filter((r) => norm(r?.rule) === norm(rule));
|
|
3198
|
-
const targeted = own.length > 0 ? own : matching;
|
|
3199
|
-
if (!targeted.some(hasSelector)) {
|
|
3200
|
-
return error("STRICT_SDD_CONDITION_SELECTOR_MISSING", `${path}[${row.displayName}].rule`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\` on "${wanted.join('", "')}", and the caseplan's \`${rule}\` carries no target. The predicate then refers to nothing, so it never becomes true and the gate never opens. Set the rule's \`selected*\` field to the target the row names.`);
|
|
3201
|
-
}
|
|
3202
|
-
if (resolve === undefined)
|
|
3203
|
-
return;
|
|
3204
|
-
const resolved = targeted.map((candidate) => ruleTargets(candidate, resolve, sameLabel));
|
|
3205
|
-
const pointsAt = resolved.flatMap((target) => target.names);
|
|
3206
|
-
if (pointsAt.length === 0)
|
|
3207
|
-
return;
|
|
3208
|
-
const missed = wanted.filter((want) => {
|
|
3209
|
-
const denotes = resolve.label(want);
|
|
3210
|
-
return !pointsAt.some((got) => sameLabel(got, want) || denotes !== undefined && got === denotes);
|
|
3211
|
-
});
|
|
3212
|
-
const conflict = resolved.find((target) => target.conflict !== undefined);
|
|
3213
|
-
if (missed.length === 0 && conflict?.conflict !== undefined) {
|
|
3214
|
-
return error("STRICT_SDD_CONDITION_SELECTOR_MISMATCH", `${path}[${row.displayName}].rule`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\` on "${wanted.join('", "')}", and the caseplan's \`${rule}\` names "${conflict.names.join('", "')}" but its \`slaId\` is the SLA of "${conflict.conflict}". The rule watches one stage's SLA while naming another, so it fires on a deadline the row did not ask for. Point \`slaId\` at the SLA of "${wanted.join('", "')}" and drop the \`selected*\` field, which convert does not write for this rule.`);
|
|
3192
|
+
// src/services/strict-sdd-audit/recipients.ts
|
|
3193
|
+
var recipientFindings = (task, found, path, label) => {
|
|
3194
|
+
if (task.recipient === undefined)
|
|
3195
|
+
return [];
|
|
3196
|
+
const carried = found.data.recipient;
|
|
3197
|
+
if (carried === undefined) {
|
|
3198
|
+
return [
|
|
3199
|
+
error("STRICT_SDD_TASK_RECIPIENT_MISSING", `${path}.data.recipient`, `${label}: the SDD's \`**Recipient:**\` line names "${task.recipient.value}" and the caseplan task carries no \`data.recipient\`. \`case validate\` does not check this field's presence, so a dropped recipient passes the build and the task is created with no assignee. Add \`data.recipient\` as the \`{Type, Value}\` object the SDD's recipient line resolves to.`)
|
|
3200
|
+
];
|
|
3215
3201
|
}
|
|
3216
|
-
if (
|
|
3217
|
-
return;
|
|
3218
|
-
return error("STRICT_SDD_CONDITION_SELECTOR_MISMATCH", `${path}[${row.displayName}].rule`, `${label}: the SDD row ${rowLabel(row)} declares WHEN \`${rule}\` on "${missed.join('", "')}", and the caseplan's \`${rule}\` points at "${pointsAt.join('", "')}". A selector that resolves to the wrong target builds a transition the SDD never asked for and leaves the declared one absent, which no count or predicate check can see.`);
|
|
3219
|
-
};
|
|
3220
|
-
var conditionRuleFindings = (declared, planConditions, path, label, resolve) => {
|
|
3221
|
-
if (declared.length === 0)
|
|
3202
|
+
if (task.recipient.kind !== "expression")
|
|
3222
3203
|
return [];
|
|
3223
|
-
const
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3204
|
+
const recipient = asRecord(carried);
|
|
3205
|
+
if (recipient === undefined)
|
|
3206
|
+
return [];
|
|
3207
|
+
const value = recipient.Value;
|
|
3208
|
+
if (recipient.Type === 3 && typeof value === "string" && value.startsWith("=")) {
|
|
3227
3209
|
return [];
|
|
3228
|
-
const owners = bindByName(declared, conditions);
|
|
3229
|
-
const taken = new Set(owners.filter((one) => one !== undefined));
|
|
3230
|
-
const openRows = declared.map((row, at) => ({ row, at })).filter(({ at }) => owners[at] === undefined);
|
|
3231
|
-
const openConditions = conditions.filter((one) => !taken.has(one));
|
|
3232
|
-
const gated = (condition) => rulesOf([condition]).some((rule) => {
|
|
3233
|
-
const expression = rule?.conditionExpression;
|
|
3234
|
-
return typeof expression === "string" && !isBlank(expression);
|
|
3235
|
-
});
|
|
3236
|
-
const rowGated = (row) => row.ungated === true ? false : row.ifExpression !== undefined ? true : undefined;
|
|
3237
|
-
const shapeKey = (rule, isGated) => {
|
|
3238
|
-
const name = norm(rule);
|
|
3239
|
-
if (name === "" || isGated === undefined)
|
|
3240
|
-
return;
|
|
3241
|
-
return `${name}|${isGated ? "gated" : "open"}`;
|
|
3242
|
-
};
|
|
3243
|
-
for (const [rowAt, conditionAt] of pairLeftovers(openRows.map(({ row }) => shapeKey(whenRule(row.when ?? "").rule, rowGated(row))), openConditions.map((condition) => {
|
|
3244
|
-
const rules = new Set(rulesOf([condition]).map((r) => norm(r?.rule)));
|
|
3245
|
-
return rules.size === 1 ? shapeKey([...rules][0], gated(condition)) : undefined;
|
|
3246
|
-
}))) {
|
|
3247
|
-
owners[openRows[rowAt].at] = openConditions[conditionAt];
|
|
3248
3210
|
}
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
};
|
|
3253
|
-
return declared.flatMap((row, at) => [
|
|
3254
|
-
whenCellFinding(row, planRules, path, label, resolve, owners[at] === undefined ? undefined : rulesOf([owners[at]])),
|
|
3255
|
-
undeclaredGateFinding(row, unownedBy(at), path, label, resolve)
|
|
3256
|
-
]).filter((finding) => finding !== undefined);
|
|
3211
|
+
return [
|
|
3212
|
+
error("STRICT_SDD_TASK_RECIPIENT_EXPRESSION", `${path}.data.recipient`, `${label}: the SDD's \`**Recipient:**\` line is the expression "${task.recipient.value}", and the caseplan task carries \`{Type: ${String(recipient.Type)}, Value: ${JSON.stringify(value)}}\`. The engine assigns a \`Type 3\` recipient to the user or group its value evaluates to; any other Type, or a value that is not an \`=\` expression, sends the literal text to a lookup and the task reaches nobody. ${expressionFix(task.recipient.value)}`)
|
|
3213
|
+
];
|
|
3257
3214
|
};
|
|
3258
|
-
var
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
];
|
|
3262
|
-
var conditionShapeFindings = (declared, planConditions, path, label) => {
|
|
3263
|
-
const findings = [];
|
|
3264
|
-
const bound = bindConditions(declared, planConditions);
|
|
3265
|
-
for (const [at, row] of declared.entries()) {
|
|
3266
|
-
const { match, shaped } = bound[at];
|
|
3267
|
-
if (match === undefined)
|
|
3268
|
-
continue;
|
|
3269
|
-
const planName = String(match.displayName ?? "");
|
|
3270
|
-
const where = shaped ? planName : row.displayName;
|
|
3271
|
-
const which = shaped ? `the condition "${planName}", the only one in this scope no row names that holds this row's rule,` : "the condition";
|
|
3272
|
-
if (row.exitType !== undefined) {
|
|
3273
|
-
const has = typeof match.type === "string" && match.type !== "";
|
|
3274
|
-
const effective = norm(has ? match.type : "exit-only");
|
|
3275
|
-
if (effective !== norm(row.exitType)) {
|
|
3276
|
-
findings.push(error("STRICT_SDD_CONDITION_EXIT_TYPE_MISMATCH", `${path}[${where}].type`, `${label}: the SDD row ${rowLabel(row)} declares Exit Type "${row.exitType}", ${which} has ${has ? `"${String(match.type)}"` : "no `type` at all, which the runtime reads as exit-only"}. The exit type decides where the case goes when the gate opens, so this sends it somewhere else${has ? "" : " — check for an invented key like `exitType` holding the value `type` should carry"}.`));
|
|
3277
|
-
}
|
|
3278
|
-
}
|
|
3279
|
-
for (const [cell, field2, title] of CONDITION_FLAGS) {
|
|
3280
|
-
const want = row[cell];
|
|
3281
|
-
const got = typeof match[field2] === "boolean" ? match[field2] : false;
|
|
3282
|
-
if (want === undefined)
|
|
3283
|
-
continue;
|
|
3284
|
-
if (want === got)
|
|
3285
|
-
continue;
|
|
3286
|
-
findings.push(error("STRICT_SDD_CONDITION_FLAG_MISMATCH", `${path}[${where}].${field2}`, `${label}: the SDD row ${rowLabel(row)} declares ${title}=${want ? "Yes" : "No"}, ${which} has ${typeof match[field2] === "boolean" ? String(got) : "no such field, which the runtime reads as false"}.`));
|
|
3287
|
-
}
|
|
3215
|
+
var expressionFix = (sddValue) => {
|
|
3216
|
+
if (sddValue.startsWith("=") && !sddValue.includes("$xref(")) {
|
|
3217
|
+
return `Write \`{"Type": 3, "Value": ${JSON.stringify(sddValue)}}\`.`;
|
|
3288
3218
|
}
|
|
3289
|
-
|
|
3219
|
+
if (sddValue.includes("$xref(")) {
|
|
3220
|
+
return "Write `Type: 3` with the expression's `$xref(...)` replaced by the producing output's `vars.<id>`, as `sdd convert` does.";
|
|
3221
|
+
}
|
|
3222
|
+
return "Write `Type: 3` with an `=` expression for it (`=vars.<variable>`), and fix the SDD line to `Expression: =vars.<variable>` so the next convert writes it.";
|
|
3290
3223
|
};
|
|
3291
3224
|
|
|
3292
3225
|
// src/services/strict-sdd-audit/compare-task.ts
|
|
3293
|
-
var compareTask = (plan, task, found, stagePath, label, selectors, ledger,
|
|
3226
|
+
var compareTask = (plan, task, found, stagePath, label, selectors, ledger, stageSpellings = [], once = new Set) => {
|
|
3294
3227
|
const issues = [];
|
|
3295
3228
|
const taskPath = `${stagePath}.data.tasks[${found.name ?? task.name}]`;
|
|
3296
3229
|
if (task.type && found.type && norm(task.type) !== norm(found.type)) {
|
|
@@ -3309,7 +3242,7 @@ var compareTask = (plan, task, found, stagePath, label, selectors, ledger, stage
|
|
|
3309
3242
|
issues.push(error("STRICT_SDD_ENTRY_RULE_EMPTY", `${taskPath}.entryConditions`, `${label}: entryConditions carry no rules.`));
|
|
3310
3243
|
}
|
|
3311
3244
|
const isPlaceholder = Object.keys(found.data).length === 0;
|
|
3312
|
-
const miss = isPlaceholder ? ledgerMissFor(ledger,
|
|
3245
|
+
const miss = isPlaceholder ? ledgerMissFor(ledger, stageSpellings, task.name) : undefined;
|
|
3313
3246
|
if (isPlaceholder && task.identityResolved && miss !== undefined) {
|
|
3314
3247
|
issues.push(warning("STRICT_SDD_PLACEHOLDER_UNRESOLVABLE", `${taskPath}.data`, `${label}: placeholder task, and the run's registry ledger records no match for "${miss.query}"${miss.cacheFile ? ` in ${miss.cacheFile}` : ""} — so the placeholder is correct and the SDD names a resource this tenant does not have. Resolve it on a tenant that does, or leave the placeholder.`));
|
|
3315
3248
|
} else if (isPlaceholder && task.identityResolved) {
|
|
@@ -3318,13 +3251,17 @@ var compareTask = (plan, task, found, stagePath, label, selectors, ledger, stage
|
|
|
3318
3251
|
} else if (isPlaceholder) {
|
|
3319
3252
|
issues.push(warning("STRICT_SDD_PLACEHOLDER_TASK", `${taskPath}.data`, `${label}: placeholder task (empty \`data\`) — the SDD leaves its resource unresolved.`));
|
|
3320
3253
|
}
|
|
3254
|
+
if (!isPlaceholder && task.identityStated !== true && task.resourceName !== undefined && bindingId(found.data.name) !== undefined && !ledgerFoundFor(ledger, stageSpellings, task.name, {
|
|
3255
|
+
name: namedValue(task.resourceName),
|
|
3256
|
+
folder: namedValue(task.folder)
|
|
3257
|
+
})) {
|
|
3258
|
+
issues.push(warning("STRICT_SDD_RESOURCE_UNVERIFIED", `${taskPath}.data.name`, `${label}: bound to "${task.resourceName}", which the SDD names without a Resource Identity, and no registry ledger records finding it — so nothing checked that this ${task.type || "resource"} exists on the tenant. A binding to a resource that does not exist passes validate and faults at \`case debug\`. Run \`uip maestro case registry pull\`, then \`uip maestro case sdd resolve <sdd> --out tasks/registry-resolved.json\` from the caseplan's folder (validate reads the ledger only from a file of that name beside the caseplan or above it): bind it if it is found, and use a placeholder (empty \`data\`) if it is not.`));
|
|
3259
|
+
}
|
|
3321
3260
|
issues.push(...conditionRuleFindings(task.entryConditions, found.entry, `${taskPath}.entryConditions`, label, selectors), ...conditionNameFindings(task.entryConditions, found.entry, `${taskPath}.entryConditions`, label, once), ...envelopeFindings(task, found, taskPath, label));
|
|
3322
3261
|
if (isPlaceholder)
|
|
3323
3262
|
return issues;
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
}
|
|
3327
|
-
issues.push(...bindingFindings(plan, found, taskPath, label, namedValue(task.folder) || undefined), ...literalFindings(task, found, taskPath, label), ...boundInputFindings(task, found, taskPath, label), ...timerFindings(task, found, taskPath, label), ...wiringFindings(task, found, taskPath, label), ...producerFindings(task, found, plan, taskPath, label), ...emptyInputFindings(task, found, taskPath, label), ...expressionShapeFindings(task, found, plan, taskPath, label), ...outputRowFindings(task, found, taskPath, label), ...extractFindings(task, found, taskPath, label), ...customOutputFindings(task, found, taskPath, label));
|
|
3263
|
+
issues.push(...recipientFindings(task, found, taskPath, label));
|
|
3264
|
+
issues.push(...bindingFindings(plan, found, taskPath, label, namedValue(task.folder) || undefined), ...literalFindings(task, found, plan.bindings, taskPath, label), ...boundInputFindings(task, found, taskPath, label), ...timerFindings(task, found, taskPath, label), ...wiringFindings(task, found, taskPath, label), ...producerFindings(task, found, plan, taskPath, label), ...emptyInputFindings(task, found, taskPath, label), ...expressionShapeFindings(task, found, plan, taskPath, label), ...outputRowFindings(task, found, taskPath, label), ...extractFindings(task, found, taskPath, label), ...customOutputFindings(task, found, taskPath, label), ...outputTypeFindings(task, found, taskPath, label));
|
|
3328
3265
|
return issues;
|
|
3329
3266
|
};
|
|
3330
3267
|
|
|
@@ -3332,7 +3269,7 @@ var compareTask = (plan, task, found, stagePath, label, selectors, ledger, stage
|
|
|
3332
3269
|
var compareStage = (plan, stage, planStage, selectors, ledger, stagePosition, once = new Set, sddStages = [stage], references = []) => {
|
|
3333
3270
|
const stagePath = `nodes[${planStage.name ?? stage.name}]`;
|
|
3334
3271
|
const ordinal = stagePosition === undefined ? undefined : `Stage ${stagePosition + 1}`;
|
|
3335
|
-
const
|
|
3272
|
+
const stageSpellings = [
|
|
3336
3273
|
stage.name,
|
|
3337
3274
|
ordinal,
|
|
3338
3275
|
ordinal === undefined ? undefined : `${ordinal}: ${stage.name}`,
|
|
@@ -3352,7 +3289,7 @@ var compareStage = (plan, stage, planStage, selectors, ledger, stagePosition, on
|
|
|
3352
3289
|
if (stage.hasSla && stageDefaults.length > 1) {
|
|
3353
3290
|
issues.push(error("STRICT_SDD_SLA_EXTRA", `${stagePath}.data.slaRules`, `${stageLabel}: the SDD declares one Stage SLA and ${stageDefaults.length} SLA rules are present. Its escalations belong to that one SLA, so splitting them across rules leaves each with part of it and the runtime applies whichever rule it reaches — the other escalations are lost. Carry one rule with every escalation the table declares.`));
|
|
3354
3291
|
}
|
|
3355
|
-
issues.push(...conditionRuleFindings(stage.entryConditions, planStage.entry, `${stagePath}.data.entryConditions`, stageLabel, selectors), ...conditionRuleFindings(stage.exitConditions, planStage.exit, `${stagePath}.data.exitConditions`, stageLabel, selectors), ...conditionShapeFindings(stage.entryConditions, planStage.entry, `${stagePath}.data.entryConditions`, stageLabel), ...conditionShapeFindings(stage.exitConditions, planStage.exit, `${stagePath}.data.exitConditions`, stageLabel), ...conditionNameFindings(stage.entryConditions, planStage.entry, `${stagePath}.data.entryConditions`, stageLabel, once), ...conditionNameFindings(stage.exitConditions, planStage.exit, `${stagePath}.data.exitConditions`, stageLabel, once), ...conditionExpressionFindings(stage.entryConditions, planStage.entry, plan, `${stagePath}.data.entryConditions`, stageLabel), ...conditionExpressionFindings(stage.exitConditions, planStage.exit, plan, `${stagePath}.data.exitConditions`, stageLabel), ...escalationFindings(stage.slaEscalations, planStage.sla, `${stagePath}.data.slaRules`, stageLabel, stage.slaResponses));
|
|
3292
|
+
issues.push(...conditionRuleFindings(stage.entryConditions, planStage.entry, `${stagePath}.data.entryConditions`, stageLabel, selectors), ...conditionRuleFindings(stage.exitConditions, planStage.exit, `${stagePath}.data.exitConditions`, stageLabel, selectors), ...conditionShapeFindings(stage.entryConditions, planStage.entry, `${stagePath}.data.entryConditions`, stageLabel), ...conditionShapeFindings(stage.exitConditions, planStage.exit, `${stagePath}.data.exitConditions`, stageLabel), ...undeclaredHandoffFindings(stage, planStage.exit, new Map(plan.stages.flatMap((one) => one.id !== undefined && one.name !== undefined ? [[one.id, one.name]] : [])), `${stagePath}.data.exitConditions`, stageLabel), ...conditionNameFindings(stage.entryConditions, planStage.entry, `${stagePath}.data.entryConditions`, stageLabel, once), ...conditionNameFindings(stage.exitConditions, planStage.exit, `${stagePath}.data.exitConditions`, stageLabel, once), ...conditionExpressionFindings(stage.entryConditions, planStage.entry, plan, `${stagePath}.data.entryConditions`, stageLabel), ...conditionExpressionFindings(stage.exitConditions, planStage.exit, plan, `${stagePath}.data.exitConditions`, stageLabel), ...escalationFindings(stage.slaEscalations, planStage.sla, `${stagePath}.data.slaRules`, stageLabel, stage.slaResponses));
|
|
3356
3293
|
const bound = resolveTasks(stage.tasks, planStage.tasks);
|
|
3357
3294
|
const unboundRows = stage.tasks.map((_, row) => row).filter((row) => bound[row] === undefined);
|
|
3358
3295
|
const unboundTasks = planStage.tasks.map((_, index) => index).filter((index) => !bound.includes(index));
|
|
@@ -3370,7 +3307,7 @@ var compareStage = (plan, stage, planStage, selectors, ledger, stagePosition, on
|
|
|
3370
3307
|
issues.push(error("STRICT_SDD_TASK_MISSING", `${stagePath}.data.tasks[${task.name}]`, `${label} is declared in the SDD and absent from the caseplan.`));
|
|
3371
3308
|
return;
|
|
3372
3309
|
}
|
|
3373
|
-
issues.push(...compareTask(plan, task, planStage.tasks[taskIndex], stagePath, label, selectors, ledger,
|
|
3310
|
+
issues.push(...compareTask(plan, task, planStage.tasks[taskIndex], stagePath, label, selectors, ledger, stageSpellings, once));
|
|
3374
3311
|
});
|
|
3375
3312
|
planStage.tasks.forEach((extra, index) => {
|
|
3376
3313
|
if (bound.includes(index))
|
|
@@ -3415,7 +3352,7 @@ var compare = (sdd, plan, ledger) => {
|
|
|
3415
3352
|
}
|
|
3416
3353
|
};
|
|
3417
3354
|
const paired = new Set;
|
|
3418
|
-
for (const [s, p] of pairByEvidence(unbound.length, leftover.length, (
|
|
3355
|
+
for (const [s, p] of pairByEvidence(unbound.length, leftover.length, (s, p) => evidence(sdd.stages[unbound[s]], plan.stages[leftover[p]]) !== undefined)) {
|
|
3419
3356
|
const sddIndex = unbound[s];
|
|
3420
3357
|
const at = leftover[p];
|
|
3421
3358
|
const stage = sdd.stages[sddIndex];
|
|
@@ -3499,6 +3436,89 @@ class SddNameCollisionError extends Error {
|
|
|
3499
3436
|
}
|
|
3500
3437
|
}
|
|
3501
3438
|
|
|
3439
|
+
// src/services/sdd-personas-table.ts
|
|
3440
|
+
var PERSONA_NAME_COLUMNS = [
|
|
3441
|
+
"Persona",
|
|
3442
|
+
"Role",
|
|
3443
|
+
"Case Role",
|
|
3444
|
+
"Name"
|
|
3445
|
+
];
|
|
3446
|
+
var WHOLE_CASE_CELLS = new Set(["all", "case", "all stages", "whole case"]);
|
|
3447
|
+
var splitList = (cell) => cell.split(/[,;]/).map((part) => stripTicks(part.trim()).trim()).filter((part) => part !== "" && !isBlank(part));
|
|
3448
|
+
var EMPTY = { personas: [], notes: [] };
|
|
3449
|
+
var personasOf = (block) => {
|
|
3450
|
+
const tables = pipeTables(block).map(firstTable).filter(({ header }) => columnIndex(header, ...PERSONA_NAME_COLUMNS) !== undefined);
|
|
3451
|
+
const table = tables.find(({ rows }) => rows.length > 0) ?? tables[0];
|
|
3452
|
+
if (table === undefined)
|
|
3453
|
+
return EMPTY;
|
|
3454
|
+
const { header, rows } = table;
|
|
3455
|
+
const nameAt = columnIndex(header, ...PERSONA_NAME_COLUMNS);
|
|
3456
|
+
const scopeAt = columnIndex(header, "Scope");
|
|
3457
|
+
const stagesAt = columnIndex(header, "Stages", "Stage Scope", "Stage");
|
|
3458
|
+
const permissionsAt = columnIndex(header, "Permissions", "Actions");
|
|
3459
|
+
const descriptionAt = columnIndex(header, "Description");
|
|
3460
|
+
const hasScopeColumn = scopeAt !== undefined;
|
|
3461
|
+
const templateShape = hasScopeColumn && permissionsAt !== undefined;
|
|
3462
|
+
const personas = [];
|
|
3463
|
+
const notes = [];
|
|
3464
|
+
for (const [at, row] of rows.entries()) {
|
|
3465
|
+
const name = stripTicks(cellAt(row, nameAt).trim()).trim();
|
|
3466
|
+
if (name === "" || isBlank(name)) {
|
|
3467
|
+
notes.push(`Personas row ${at + 1} has no name and is not read; a persona becomes a role by its name`);
|
|
3468
|
+
continue;
|
|
3469
|
+
}
|
|
3470
|
+
const stagesCell = cellAt(row, stagesAt).trim();
|
|
3471
|
+
const scopeCell = stripTicks(cellAt(row, scopeAt).trim()).trim();
|
|
3472
|
+
const legacyWholeCase = !hasScopeColumn && (stagesCell === "" || isBlank(stagesCell) || WHOLE_CASE_CELLS.has(stagesCell.toLowerCase()));
|
|
3473
|
+
const description = cellAt(row, descriptionAt).trim();
|
|
3474
|
+
personas.push({
|
|
3475
|
+
name,
|
|
3476
|
+
...hasScopeColumn ? { scope: scopeCell } : { scope: legacyWholeCase ? "Case" : "Stages" },
|
|
3477
|
+
stages: legacyWholeCase ? [] : splitList(stagesCell),
|
|
3478
|
+
permissions: splitList(cellAt(row, permissionsAt)),
|
|
3479
|
+
...description !== "" && !isBlank(description) && { description },
|
|
3480
|
+
templateShape
|
|
3481
|
+
});
|
|
3482
|
+
}
|
|
3483
|
+
return { personas, notes };
|
|
3484
|
+
};
|
|
3485
|
+
|
|
3486
|
+
// src/services/strict-sdd-audit/business-events-read.ts
|
|
3487
|
+
var readBusinessEvents = (sdd, title, body) => {
|
|
3488
|
+
const { header, rows } = firstTable(body);
|
|
3489
|
+
const wanted = ["Name", "Event", "Business Event"];
|
|
3490
|
+
const nameAt = columnIndex(header, ...wanted);
|
|
3491
|
+
if (nameAt === undefined) {
|
|
3492
|
+
if (rows.length > 0) {
|
|
3493
|
+
sdd.parseNotes.push(noNameColumnNote(title, wanted, header));
|
|
3494
|
+
}
|
|
3495
|
+
return;
|
|
3496
|
+
}
|
|
3497
|
+
const descriptionAt = columnIndex(header, "Description");
|
|
3498
|
+
const payloadAt = columnIndex(header, "Payload", "Schema", "Payload Schema");
|
|
3499
|
+
const seen = new Set;
|
|
3500
|
+
for (const [at, row] of rows.entries()) {
|
|
3501
|
+
const name = stripTicks(cellAt(row, nameAt)).trim();
|
|
3502
|
+
if (name === "" || isBlank(name)) {
|
|
3503
|
+
sdd.parseNotes.push(`section "${title}": row ${at + 1} has no Name, so no business event was declared for it`);
|
|
3504
|
+
continue;
|
|
3505
|
+
}
|
|
3506
|
+
if (seen.has(name)) {
|
|
3507
|
+
sdd.parseNotes.push(`section "${title}": business event "${name}" is declared twice; rules match on the name, so only the first row is kept`);
|
|
3508
|
+
continue;
|
|
3509
|
+
}
|
|
3510
|
+
seen.add(name);
|
|
3511
|
+
const description = cellAt(row, descriptionAt).trim();
|
|
3512
|
+
const payload = stripTicks(cellAt(row, payloadAt)).trim();
|
|
3513
|
+
sdd.businessEvents.push({
|
|
3514
|
+
row: at + 1,
|
|
3515
|
+
name,
|
|
3516
|
+
...description !== "" && !isBlank(description) && { description },
|
|
3517
|
+
...payload !== "" && !isBlank(payload) && { payload }
|
|
3518
|
+
});
|
|
3519
|
+
}
|
|
3520
|
+
};
|
|
3521
|
+
|
|
3502
3522
|
// src/services/strict-sdd-audit/canvas-notes-read.ts
|
|
3503
3523
|
var cellsOf = (line) => splitRowCells(line.trim().replace(/^\|/, "").replace(/\|$/, ""));
|
|
3504
3524
|
var noteTable = (table) => {
|
|
@@ -3541,9 +3561,8 @@ var readCanvasNotes = (sdd, title, body) => {
|
|
|
3541
3561
|
}
|
|
3542
3562
|
};
|
|
3543
3563
|
var readCanvasNotesSection = (sdd, section, seen) => {
|
|
3544
|
-
const { title
|
|
3545
|
-
const
|
|
3546
|
-
const tables = pipeTables(own).filter((table) => noteTable(table).rows.some((row) => row.some((cell) => cell.trim() !== "")));
|
|
3564
|
+
const { title } = section;
|
|
3565
|
+
const tables = pipeTables(ownBodyOf(section)).filter((table) => noteTable(table).rows.some((row) => row.some((cell) => cell.trim() !== "")));
|
|
3547
3566
|
if (tables.length === 0)
|
|
3548
3567
|
return seen;
|
|
3549
3568
|
const later = seen ? tables.length : tables.length - 1;
|
|
@@ -3602,6 +3621,9 @@ var applyStageSection = (stage, title, block, notes) => {
|
|
|
3602
3621
|
const merged = addConditionTable(stage.exitRows, stage.exitConditions, block);
|
|
3603
3622
|
stage.exitRows = merged.rows;
|
|
3604
3623
|
stage.exitConditions = merged.conditions;
|
|
3624
|
+
if (columnIndex(firstTable(block).header, "Exit-To Stage", "Target Stage") !== undefined) {
|
|
3625
|
+
stage.exitToColumn = true;
|
|
3626
|
+
}
|
|
3605
3627
|
} else if (headingIs(title, STAGE_CONDITIONAL_SLA_HEADINGS)) {
|
|
3606
3628
|
stage.conditionalSlas = conditionalSlaRows(block);
|
|
3607
3629
|
} else if (headingIs(title, STAGE_SLA_HEADINGS)) {
|
|
@@ -3712,16 +3734,16 @@ var parseStage = (name, section, notes) => {
|
|
|
3712
3734
|
});
|
|
3713
3735
|
}
|
|
3714
3736
|
}
|
|
3715
|
-
const tasksUnder = (key,
|
|
3737
|
+
const tasksUnder = (key, name) => stage.tasks.filter((other) => key(other.name) === key(name)).length;
|
|
3716
3738
|
const exactSeen = new Map;
|
|
3717
|
-
const detailFor = (
|
|
3718
|
-
const candidates = details.get(norm(
|
|
3719
|
-
if (candidates.length === 1 && tasksUnder(norm,
|
|
3739
|
+
const detailFor = (name) => {
|
|
3740
|
+
const candidates = details.get(norm(name)) ?? [];
|
|
3741
|
+
if (candidates.length === 1 && tasksUnder(norm, name) === 1) {
|
|
3720
3742
|
return candidates[0].detail;
|
|
3721
3743
|
}
|
|
3722
|
-
const exact = candidates.filter((c) => c.name ===
|
|
3723
|
-
const nth = exactSeen.get(
|
|
3724
|
-
exactSeen.set(
|
|
3744
|
+
const exact = candidates.filter((c) => c.name === name.trim());
|
|
3745
|
+
const nth = exactSeen.get(name.trim()) ?? 0;
|
|
3746
|
+
exactSeen.set(name.trim(), nth + 1);
|
|
3725
3747
|
return exact[nth]?.detail;
|
|
3726
3748
|
};
|
|
3727
3749
|
for (const task of stage.tasks) {
|
|
@@ -3745,6 +3767,7 @@ var parseStage = (name, section, notes) => {
|
|
|
3745
3767
|
task.unreadTables = detail.unreadTables;
|
|
3746
3768
|
task.resolvedResource = detail.resolvedResource;
|
|
3747
3769
|
task.identityResolved = detail.identityResolved;
|
|
3770
|
+
task.identityStated = detail.identityStated;
|
|
3748
3771
|
task.folder = detail.folder;
|
|
3749
3772
|
task.proseLabel = detail.proseLabel;
|
|
3750
3773
|
task.prose = detail.prose;
|
|
@@ -3770,6 +3793,8 @@ var parseStage = (name, section, notes) => {
|
|
|
3770
3793
|
task.type = detail.type;
|
|
3771
3794
|
if (detail.http !== undefined && String(task.type ?? "").trim().toLowerCase() === "http-request")
|
|
3772
3795
|
task.http = detail.http;
|
|
3796
|
+
if (detail.extraction !== undefined && String(task.type ?? "").trim().toLowerCase() === "document-extraction")
|
|
3797
|
+
task.extraction = detail.extraction;
|
|
3773
3798
|
}
|
|
3774
3799
|
const empty = {
|
|
3775
3800
|
conditions: !(stage.entryRows && stage.exitRows),
|
|
@@ -3788,7 +3813,7 @@ var parseStage = (name, section, notes) => {
|
|
|
3788
3813
|
};
|
|
3789
3814
|
|
|
3790
3815
|
// src/services/strict-sdd-audit/sdd-parse.ts
|
|
3791
|
-
var applyCaseSection = (sdd, title, body, headingLine, declaredNames) => {
|
|
3816
|
+
var applyCaseSection = (sdd, title, body, headingLine, declaredNames, own = body) => {
|
|
3792
3817
|
if (headingIs(title, CASE_EXIT_HEADINGS)) {
|
|
3793
3818
|
const merged = addConditionTable(sdd.caseExitRows, sdd.caseExitConditions, body);
|
|
3794
3819
|
sdd.caseExitRows = merged.rows;
|
|
@@ -3882,6 +3907,13 @@ var applyCaseSection = (sdd, title, body, headingLine, declaredNames) => {
|
|
|
3882
3907
|
}
|
|
3883
3908
|
}
|
|
3884
3909
|
}
|
|
3910
|
+
} else if (headingIs(title, PERSONA_HEADINGS)) {
|
|
3911
|
+
const { personas, notes } = personasOf(own);
|
|
3912
|
+
sdd.personas.push(...personas);
|
|
3913
|
+
sdd.personaNotes.push(...notes);
|
|
3914
|
+
sdd.parseNotes.push(...notes.map((note) => `section "${title}": ${note}`));
|
|
3915
|
+
} else if (headingIs(title, BUSINESS_EVENT_HEADINGS)) {
|
|
3916
|
+
readBusinessEvents(sdd, title, body);
|
|
3885
3917
|
} else if (headingIs(title, CASE_VARIABLE_HEADINGS)) {
|
|
3886
3918
|
sdd.sawVariableSection = true;
|
|
3887
3919
|
const { header, rows, rowLines } = firstTable(body);
|
|
@@ -3915,7 +3947,7 @@ var applyCaseSection = (sdd, title, body, headingLine, declaredNames) => {
|
|
|
3915
3947
|
sdd.variableBodies.set(norm(name), bodySchema);
|
|
3916
3948
|
}
|
|
3917
3949
|
const category = norm(cellAt(row, categoryAt));
|
|
3918
|
-
if (
|
|
3950
|
+
if (VARIABLE_PLACEMENT2.has(category)) {
|
|
3919
3951
|
sdd.variableCategories.set(norm(name), category);
|
|
3920
3952
|
}
|
|
3921
3953
|
const boundTrigger = stripTicks(cellAt(row, sourceTriggersAt).trim()).trim();
|
|
@@ -4038,7 +4070,10 @@ var parseSdd = (text) => {
|
|
|
4038
4070
|
caseConditionalSlas: [],
|
|
4039
4071
|
xrefOutputs: xrefProducers(document),
|
|
4040
4072
|
resourceOutputs: resourceOutputsByTask(document),
|
|
4073
|
+
personas: [],
|
|
4074
|
+
personaNotes: [],
|
|
4041
4075
|
canvasNotes: [],
|
|
4076
|
+
businessEvents: [],
|
|
4042
4077
|
parseNotes: []
|
|
4043
4078
|
};
|
|
4044
4079
|
const lookalikes = [];
|
|
@@ -4053,7 +4088,7 @@ var parseSdd = (text) => {
|
|
|
4053
4088
|
if (level !== 3)
|
|
4054
4089
|
continue;
|
|
4055
4090
|
const headingLine = lineAt(text, section.start);
|
|
4056
|
-
if (applyCaseSection(sdd, title, body, headingLine, declaredNames))
|
|
4091
|
+
if (applyCaseSection(sdd, title, body, headingLine, declaredNames, ownBodyOf(section)))
|
|
4057
4092
|
continue;
|
|
4058
4093
|
const stageName = readStageHeading(title);
|
|
4059
4094
|
if (stageName !== undefined) {
|
|
@@ -4083,7 +4118,7 @@ var parseSdd = (text) => {
|
|
|
4083
4118
|
};
|
|
4084
4119
|
|
|
4085
4120
|
// src/services/strict-sdd-audit/entry.ts
|
|
4086
|
-
var
|
|
4121
|
+
var parseSddToJson2 = (text) => {
|
|
4087
4122
|
const sdd = parseSdd(text);
|
|
4088
4123
|
return {
|
|
4089
4124
|
stages: sdd.stages.map((stage) => ({
|
|
@@ -4145,7 +4180,12 @@ var parseSddToJson = (text) => {
|
|
|
4145
4180
|
...sdd.caseDescription !== undefined && {
|
|
4146
4181
|
caseDescription: sdd.caseDescription
|
|
4147
4182
|
},
|
|
4183
|
+
personas: sdd.personas,
|
|
4184
|
+
...sdd.personaNotes.length > 0 && { personaNotes: sdd.personaNotes },
|
|
4148
4185
|
...sdd.canvasNotes.length > 0 && { canvasNotes: sdd.canvasNotes },
|
|
4186
|
+
...sdd.businessEvents.length > 0 && {
|
|
4187
|
+
businessEvents: sdd.businessEvents
|
|
4188
|
+
},
|
|
4149
4189
|
intake: {
|
|
4150
4190
|
caseExitRows: sdd.caseExitRows,
|
|
4151
4191
|
sawVariableSection: sdd.sawVariableSection,
|
|
@@ -4153,8 +4193,8 @@ var parseSddToJson = (text) => {
|
|
|
4153
4193
|
}
|
|
4154
4194
|
};
|
|
4155
4195
|
};
|
|
4156
|
-
var
|
|
4157
|
-
const [parseError, sdd] =
|
|
4196
|
+
var auditCaseplanAgainstSdd2 = (parsed, sddText, sddName, ledger) => {
|
|
4197
|
+
const [parseError, sdd] = catchError2(() => parseSdd(sddText));
|
|
4158
4198
|
if (parseError instanceof SddNameCollisionError) {
|
|
4159
4199
|
return [
|
|
4160
4200
|
error("STRICT_SDD_NAME_COLLISION", sddName, `"${sddName}": ${parseError.message}`)
|
|
@@ -4171,6 +4211,6 @@ var auditCaseplanAgainstSdd = (parsed, sddText, sddName, ledger) => {
|
|
|
4171
4211
|
issues.push(...slaReferenceFindings(sddText, sddName));
|
|
4172
4212
|
return issues;
|
|
4173
4213
|
};
|
|
4174
|
-
export { CASE_SLA_PROPERTY,
|
|
4214
|
+
export { CASE_SLA_PROPERTY, VARIABLE_PLACEMENT2, DEFAULT_GROUP2, parseSddToJson2, auditCaseplanAgainstSdd2 };
|
|
4175
4215
|
|
|
4176
|
-
//# debugId=
|
|
4216
|
+
//# debugId=0BFEE81926C8921664756E2164756E21
|