@lovrozagar/oat 0.9.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/README.md +26 -1936
  2. package/dist/cli.js +326 -60
  3. package/dist/config/define-config.d.ts +15 -1
  4. package/dist/config/define-config.d.ts.map +1 -1
  5. package/dist/config/define-config.js.map +1 -1
  6. package/dist/config/validate.d.ts +12 -0
  7. package/dist/config/validate.d.ts.map +1 -0
  8. package/dist/config/validate.js +86 -0
  9. package/dist/config/validate.js.map +1 -0
  10. package/dist/conformance/effects.d.ts +9 -0
  11. package/dist/conformance/effects.d.ts.map +1 -0
  12. package/dist/conformance/effects.js +276 -0
  13. package/dist/conformance/effects.js.map +1 -0
  14. package/dist/conformance/expected-failures.d.ts +18 -0
  15. package/dist/conformance/expected-failures.d.ts.map +1 -0
  16. package/dist/conformance/expected-failures.js +48 -0
  17. package/dist/conformance/expected-failures.js.map +1 -0
  18. package/dist/conformance/feature-gate.d.ts +7 -0
  19. package/dist/conformance/feature-gate.d.ts.map +1 -0
  20. package/dist/conformance/feature-gate.js +428 -0
  21. package/dist/conformance/feature-gate.js.map +1 -0
  22. package/dist/conformance/fuzz.d.ts +108 -0
  23. package/dist/conformance/fuzz.d.ts.map +1 -0
  24. package/dist/conformance/fuzz.js +246 -0
  25. package/dist/conformance/fuzz.js.map +1 -0
  26. package/dist/conformance/leaks.d.ts +10 -0
  27. package/dist/conformance/leaks.d.ts.map +1 -0
  28. package/dist/conformance/leaks.js +23 -0
  29. package/dist/conformance/leaks.js.map +1 -0
  30. package/dist/conformance/pool.d.ts +49 -0
  31. package/dist/conformance/pool.d.ts.map +1 -0
  32. package/dist/conformance/pool.js +75 -0
  33. package/dist/conformance/pool.js.map +1 -0
  34. package/dist/conformance/rate-limit.d.ts +7 -0
  35. package/dist/conformance/rate-limit.d.ts.map +1 -0
  36. package/dist/conformance/rate-limit.js +372 -0
  37. package/dist/conformance/rate-limit.js.map +1 -0
  38. package/dist/conformance/scope.d.ts +15 -0
  39. package/dist/conformance/scope.d.ts.map +1 -0
  40. package/dist/conformance/scope.js +78 -0
  41. package/dist/conformance/scope.js.map +1 -0
  42. package/dist/conformance/seed-contract.d.ts +11 -0
  43. package/dist/conformance/seed-contract.d.ts.map +1 -0
  44. package/dist/conformance/seed-contract.js +680 -0
  45. package/dist/conformance/seed-contract.js.map +1 -0
  46. package/dist/conformance/shapes.d.ts +35 -0
  47. package/dist/conformance/shapes.d.ts.map +1 -0
  48. package/dist/conformance/shapes.js +162 -0
  49. package/dist/conformance/shapes.js.map +1 -0
  50. package/dist/conformance/specs.d.ts +21 -0
  51. package/dist/conformance/specs.d.ts.map +1 -0
  52. package/dist/conformance/specs.js +306 -0
  53. package/dist/conformance/specs.js.map +1 -0
  54. package/dist/conformance/suite.d.ts +209 -0
  55. package/dist/conformance/suite.d.ts.map +1 -0
  56. package/dist/conformance/suite.js +1538 -0
  57. package/dist/conformance/suite.js.map +1 -0
  58. package/dist/conformance/unique.d.ts +7 -0
  59. package/dist/conformance/unique.d.ts.map +1 -0
  60. package/dist/conformance/unique.js +586 -0
  61. package/dist/conformance/unique.js.map +1 -0
  62. package/dist/conformance/worker.d.ts +3 -0
  63. package/dist/conformance/worker.d.ts.map +1 -0
  64. package/dist/conformance/worker.js +27 -0
  65. package/dist/conformance/worker.js.map +1 -0
  66. package/dist/index.d.ts +10 -3
  67. package/dist/index.js +6 -2
  68. package/dist/reference/defects.d.ts +71 -1
  69. package/dist/reference/defects.d.ts.map +1 -1
  70. package/dist/reference/defects.js +74 -8
  71. package/dist/reference/defects.js.map +1 -1
  72. package/dist/reference/dialect.d.ts +6 -4
  73. package/dist/reference/dialect.d.ts.map +1 -1
  74. package/dist/reference/dialect.js +1 -0
  75. package/dist/reference/dialect.js.map +1 -1
  76. package/dist/reference/http.d.ts +17 -25
  77. package/dist/reference/http.d.ts.map +1 -1
  78. package/dist/reference/http.js +574 -224
  79. package/dist/reference/http.js.map +1 -1
  80. package/dist/reference/listing.d.ts +51 -0
  81. package/dist/reference/listing.d.ts.map +1 -0
  82. package/dist/reference/listing.js +256 -0
  83. package/dist/reference/listing.js.map +1 -0
  84. package/dist/reference/model.d.ts +35 -1
  85. package/dist/reference/model.d.ts.map +1 -1
  86. package/dist/reference/model.js +42 -0
  87. package/dist/reference/model.js.map +1 -1
  88. package/dist/reference/query.d.ts +132 -43
  89. package/dist/reference/query.d.ts.map +1 -1
  90. package/dist/reference/query.js +397 -315
  91. package/dist/reference/query.js.map +1 -1
  92. package/dist/reference/shapes.d.ts +60 -0
  93. package/dist/reference/shapes.d.ts.map +1 -0
  94. package/dist/reference/shapes.js +142 -0
  95. package/dist/reference/shapes.js.map +1 -0
  96. package/dist/reference/spec.d.ts +9 -2
  97. package/dist/reference/spec.d.ts.map +1 -1
  98. package/dist/reference/spec.js +291 -71
  99. package/dist/reference/spec.js.map +1 -1
  100. package/dist/reference/store-api.d.ts +14 -43
  101. package/dist/reference/store-api.d.ts.map +1 -1
  102. package/dist/reference/store-api.js +8 -58
  103. package/dist/reference/store-api.js.map +1 -1
  104. package/dist/reference/stores/memory.d.ts +12 -15
  105. package/dist/reference/stores/memory.d.ts.map +1 -1
  106. package/dist/reference/stores/memory.js +22 -70
  107. package/dist/reference/stores/memory.js.map +1 -1
  108. package/dist/reference/stores/postgres.d.ts +11 -23
  109. package/dist/reference/stores/postgres.d.ts.map +1 -1
  110. package/dist/reference/stores/postgres.js +53 -334
  111. package/dist/reference/stores/postgres.js.map +1 -1
  112. package/dist/reference/stores/sql-compile.d.ts +36 -0
  113. package/dist/reference/stores/sql-compile.d.ts.map +1 -0
  114. package/dist/reference/stores/sql-compile.js +95 -0
  115. package/dist/reference/stores/sql-compile.js.map +1 -0
  116. package/dist/reference/stores/sqlite.d.ts +12 -27
  117. package/dist/reference/stores/sqlite.d.ts.map +1 -1
  118. package/dist/reference/stores/sqlite.js +54 -355
  119. package/dist/reference/stores/sqlite.js.map +1 -1
  120. package/dist/report/console.d.ts +2 -1
  121. package/dist/report/console.d.ts.map +1 -1
  122. package/dist/report/console.js +56 -10
  123. package/dist/report/console.js.map +1 -1
  124. package/dist/report/matrix.d.ts +10 -13
  125. package/dist/report/matrix.d.ts.map +1 -1
  126. package/dist/report/matrix.js +82 -18
  127. package/dist/report/matrix.js.map +1 -1
  128. package/dist/report/render.d.ts +18 -1
  129. package/dist/report/render.d.ts.map +1 -1
  130. package/dist/report/render.js +169 -14
  131. package/dist/report/render.js.map +1 -1
  132. package/dist/runtime/async.d.ts +16 -3
  133. package/dist/runtime/async.d.ts.map +1 -1
  134. package/dist/runtime/async.js +24 -19
  135. package/dist/runtime/async.js.map +1 -1
  136. package/dist/runtime/auth.d.ts +2 -9
  137. package/dist/runtime/auth.d.ts.map +1 -1
  138. package/dist/runtime/auth.js +38 -62
  139. package/dist/runtime/auth.js.map +1 -1
  140. package/dist/runtime/body.d.ts +15 -3
  141. package/dist/runtime/body.d.ts.map +1 -1
  142. package/dist/runtime/body.js +61 -12
  143. package/dist/runtime/body.js.map +1 -1
  144. package/dist/runtime/check-costs.d.ts +15 -0
  145. package/dist/runtime/check-costs.d.ts.map +1 -0
  146. package/dist/runtime/check-costs.js +114 -0
  147. package/dist/runtime/check-costs.js.map +1 -0
  148. package/dist/runtime/check-ids.d.ts +15 -0
  149. package/dist/runtime/check-ids.d.ts.map +1 -0
  150. package/dist/runtime/check-ids.js +140 -0
  151. package/dist/runtime/check-ids.js.map +1 -0
  152. package/dist/runtime/checks.d.ts +122 -18
  153. package/dist/runtime/checks.d.ts.map +1 -1
  154. package/dist/runtime/checks.js +3437 -1933
  155. package/dist/runtime/checks.js.map +1 -1
  156. package/dist/runtime/client.d.ts +112 -0
  157. package/dist/runtime/client.d.ts.map +1 -1
  158. package/dist/runtime/client.js +271 -39
  159. package/dist/runtime/client.js.map +1 -1
  160. package/dist/runtime/cookies.d.ts +16 -2
  161. package/dist/runtime/cookies.d.ts.map +1 -1
  162. package/dist/runtime/cookies.js +83 -22
  163. package/dist/runtime/cookies.js.map +1 -1
  164. package/dist/runtime/exchanges.d.ts +4 -5
  165. package/dist/runtime/exchanges.d.ts.map +1 -1
  166. package/dist/runtime/exchanges.js +33 -55
  167. package/dist/runtime/exchanges.js.map +1 -1
  168. package/dist/runtime/exit.d.ts +40 -0
  169. package/dist/runtime/exit.d.ts.map +1 -0
  170. package/dist/runtime/exit.js +48 -0
  171. package/dist/runtime/exit.js.map +1 -0
  172. package/dist/runtime/feature-gate.d.ts +0 -9
  173. package/dist/runtime/feature-gate.d.ts.map +1 -1
  174. package/dist/runtime/feature-gate.js +0 -21
  175. package/dist/runtime/feature-gate.js.map +1 -1
  176. package/dist/runtime/finding.d.ts +61 -7
  177. package/dist/runtime/finding.d.ts.map +1 -1
  178. package/dist/runtime/finding.js +74 -12
  179. package/dist/runtime/finding.js.map +1 -1
  180. package/dist/runtime/fixture.d.ts +35 -28
  181. package/dist/runtime/fixture.d.ts.map +1 -1
  182. package/dist/runtime/fixture.js +92 -731
  183. package/dist/runtime/fixture.js.map +1 -1
  184. package/dist/runtime/generate.d.ts +63 -0
  185. package/dist/runtime/generate.d.ts.map +1 -0
  186. package/dist/runtime/generate.js +635 -0
  187. package/dist/runtime/generate.js.map +1 -0
  188. package/dist/runtime/headers.d.ts +6 -0
  189. package/dist/runtime/headers.d.ts.map +1 -0
  190. package/dist/runtime/headers.js +16 -0
  191. package/dist/runtime/headers.js.map +1 -0
  192. package/dist/runtime/path.d.ts +7 -0
  193. package/dist/runtime/path.d.ts.map +1 -0
  194. package/dist/runtime/path.js +44 -0
  195. package/dist/runtime/path.js.map +1 -0
  196. package/dist/runtime/payloads.d.ts +2 -0
  197. package/dist/runtime/payloads.d.ts.map +1 -1
  198. package/dist/runtime/payloads.js +2 -1
  199. package/dist/runtime/payloads.js.map +1 -1
  200. package/dist/runtime/rate-limit.d.ts +6 -4
  201. package/dist/runtime/rate-limit.d.ts.map +1 -1
  202. package/dist/runtime/rate-limit.js +22 -33
  203. package/dist/runtime/rate-limit.js.map +1 -1
  204. package/dist/runtime/redact.d.ts +24 -0
  205. package/dist/runtime/redact.d.ts.map +1 -0
  206. package/dist/runtime/redact.js +98 -0
  207. package/dist/runtime/redact.js.map +1 -0
  208. package/dist/runtime/run.d.ts +51 -2
  209. package/dist/runtime/run.d.ts.map +1 -1
  210. package/dist/runtime/run.js +463 -195
  211. package/dist/runtime/run.js.map +1 -1
  212. package/dist/runtime/runs.d.ts +5 -0
  213. package/dist/runtime/runs.d.ts.map +1 -1
  214. package/dist/runtime/runs.js +7 -1
  215. package/dist/runtime/runs.js.map +1 -1
  216. package/dist/runtime/scope.d.ts +163 -0
  217. package/dist/runtime/scope.d.ts.map +1 -0
  218. package/dist/runtime/scope.js +423 -0
  219. package/dist/runtime/scope.js.map +1 -0
  220. package/dist/runtime/teardown.d.ts +17 -4
  221. package/dist/runtime/teardown.d.ts.map +1 -1
  222. package/dist/runtime/teardown.js +49 -16
  223. package/dist/runtime/teardown.js.map +1 -1
  224. package/dist/runtime/transcript.d.ts +34 -2
  225. package/dist/runtime/transcript.d.ts.map +1 -1
  226. package/dist/runtime/transcript.js +119 -24
  227. package/dist/runtime/transcript.js.map +1 -1
  228. package/dist/runtime/unique.d.ts +2 -11
  229. package/dist/runtime/unique.d.ts.map +1 -1
  230. package/dist/runtime/unique.js +27 -53
  231. package/dist/runtime/unique.js.map +1 -1
  232. package/dist/runtime/validate.d.ts +13 -2
  233. package/dist/runtime/validate.d.ts.map +1 -1
  234. package/dist/runtime/validate.js +51 -31
  235. package/dist/runtime/validate.js.map +1 -1
  236. package/dist/runtime/wait.d.ts +2 -1
  237. package/dist/runtime/wait.d.ts.map +1 -1
  238. package/dist/runtime/wait.js +2 -36
  239. package/dist/runtime/wait.js.map +1 -1
  240. package/dist/runtime/world.d.ts +31 -3
  241. package/dist/runtime/world.d.ts.map +1 -1
  242. package/dist/runtime/world.js +113 -32
  243. package/dist/runtime/world.js.map +1 -1
  244. package/dist/spec/collection.d.ts +8 -2
  245. package/dist/spec/collection.d.ts.map +1 -1
  246. package/dist/spec/collection.js +47 -7
  247. package/dist/spec/collection.js.map +1 -1
  248. package/dist/spec/extensions.d.ts +5 -1
  249. package/dist/spec/extensions.d.ts.map +1 -1
  250. package/dist/spec/extensions.js +9 -2
  251. package/dist/spec/extensions.js.map +1 -1
  252. package/dist/spec/graph.d.ts +46 -0
  253. package/dist/spec/graph.d.ts.map +1 -1
  254. package/dist/spec/graph.js +175 -11
  255. package/dist/spec/graph.js.map +1 -1
  256. package/dist/spec/load.d.ts +25 -5
  257. package/dist/spec/load.d.ts.map +1 -1
  258. package/dist/spec/load.js +160 -59
  259. package/dist/spec/load.js.map +1 -1
  260. package/dist/spec/query-capabilities.d.ts +4 -0
  261. package/dist/spec/query-capabilities.d.ts.map +1 -1
  262. package/dist/spec/query-capabilities.js +6 -0
  263. package/dist/spec/query-capabilities.js.map +1 -1
  264. package/dist/spec/schema.d.ts +20 -0
  265. package/dist/spec/schema.d.ts.map +1 -0
  266. package/dist/spec/schema.js +118 -0
  267. package/dist/spec/schema.js.map +1 -0
  268. package/dist/spec/tag-schema.d.ts +12 -0
  269. package/dist/spec/tag-schema.d.ts.map +1 -0
  270. package/dist/spec/tag-schema.js +143 -0
  271. package/dist/spec/tag-schema.js.map +1 -0
  272. package/dist/spec/types.js +17 -1
  273. package/dist/spec/types.js.map +1 -1
  274. package/docs/api.md +59 -0
  275. package/docs/assumptions.md +99 -0
  276. package/docs/checks.md +183 -0
  277. package/docs/commands.md +214 -0
  278. package/docs/configuration.md +650 -0
  279. package/docs/model.md +184 -0
  280. package/docs/reference-defects.md +94 -0
  281. package/docs/reports.md +211 -0
  282. package/docs/tags.md +400 -0
  283. package/package.json +3 -1
@@ -0,0 +1,423 @@
1
+ /**
2
+ * Operation scope: which operations a run grades.
3
+ *
4
+ * A full run grades every operation some check judges. A targeted run (`--ops`, `--only`) grades
5
+ * only the named operations. Everything else oat calls — principal sign-up, parent creates, the
6
+ * cohort create behind a list, teardown deletes — is support: called to reach or observe a
7
+ * target, never graded. Resolution is static over the entity graph so `oat plan --ops` can show
8
+ * the scope with no network, and so a typo fails before the first request.
9
+ */
10
+ import { isAuthFlow } from "../config/define-config.js";
11
+ import { owningEntityName } from "../spec/graph.js";
12
+ import { pathTemplateMatches } from "../spec/load.js";
13
+ import { CHECKS } from "./checks.js";
14
+ export class ScopeError extends Error {
15
+ name = "ScopeError";
16
+ }
17
+ /** Union of every check's subjects for this entity. */
18
+ export function staticSubjects(entity, model) {
19
+ const ids = new Set();
20
+ for (const check of CHECKS)
21
+ for (const id of check.subjects(entity, model))
22
+ ids.add(id);
23
+ return ids;
24
+ }
25
+ /** operationIds named in any principal `auth.steps` — those creates already ran during login. */
26
+ export function authStepOperationIds(principals) {
27
+ const ids = new Set();
28
+ for (const principal of principals) {
29
+ const auth = principal.auth;
30
+ if (auth === undefined || !isAuthFlow(auth))
31
+ continue;
32
+ for (const step of auth.steps) {
33
+ if ("operationId" in step)
34
+ ids.add(step.operationId);
35
+ }
36
+ }
37
+ return ids;
38
+ }
39
+ export function createIsAuthProvisioned(createOp, authCreates) {
40
+ return createOp.freshPrincipal || authCreates.has(createOp.operationId);
41
+ }
42
+ /** Why an entity cannot be seeded and checked, or `null` when it can. */
43
+ export function untestableEntityReason(entity, model, authCreates) {
44
+ const createOp = entity.create === undefined ? undefined : model.byOperationId.get(entity.create);
45
+ const authProvisioned = createOp !== undefined && createIsAuthProvisioned(createOp, authCreates);
46
+ /* Register-as-create with no item route is the auth flow, not a fixture. */
47
+ if (authProvisioned && entity.read === undefined) {
48
+ return `"${entity.name}" is created by the auth flow and has no item route`;
49
+ }
50
+ if (entity.invite !== null && entity.list !== undefined)
51
+ return null;
52
+ const missing = [
53
+ ...(entity.list === undefined ? ["a list operation"] : []),
54
+ ...(entity.create === undefined ? ["a create operation"] : []),
55
+ ...(entity.list !== undefined && entity.create !== undefined && !entity.trackable
56
+ ? ["records oat can identify after create"]
57
+ : []),
58
+ ];
59
+ return missing.length === 0 ? null : `"${entity.name}" has no ${missing.join(" and no ")}`;
60
+ }
61
+ export function testableEntities(model, authCreates) {
62
+ return [...model.entities.values()]
63
+ .filter((entity) => untestableEntityReason(entity, model, authCreates) === null)
64
+ .sort((a, b) => a.name.localeCompare(b.name));
65
+ }
66
+ function globToRegExp(pattern) {
67
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
68
+ return new RegExp(`^${escaped}$`);
69
+ }
70
+ function distance(a, b) {
71
+ const row = Array.from({ length: b.length + 1 }, (_, i) => i);
72
+ for (let i = 1; i <= a.length; i++) {
73
+ let diagonal = row[0];
74
+ row[0] = i;
75
+ for (let j = 1; j <= b.length; j++) {
76
+ const above = row[j];
77
+ row[j] = Math.min(above + 1, row[j - 1] + 1, diagonal + (a[i - 1] === b[j - 1] ? 0 : 1));
78
+ diagonal = above;
79
+ }
80
+ }
81
+ return row[b.length];
82
+ }
83
+ function nearest(name, candidates) {
84
+ return [...candidates]
85
+ .map((candidate) => ({ candidate, d: distance(name, candidate) }))
86
+ .filter((item) => item.d <= Math.max(3, Math.floor(name.length / 3)))
87
+ .sort((a, b) => a.d - b.d || a.candidate.localeCompare(b.candidate))
88
+ .slice(0, 3)
89
+ .map((item) => item.candidate);
90
+ }
91
+ function didYouMean(name, candidates) {
92
+ const close = nearest(name, candidates);
93
+ return close.length === 0 ? "" : ` Did you mean: ${close.join(", ")}?`;
94
+ }
95
+ function matchPattern(pattern, model, label) {
96
+ if (!pattern.includes("*")) {
97
+ if (model.byOperationId.has(pattern))
98
+ return { exact: true, ids: [pattern] };
99
+ throw new ScopeError(`oat: --ops "${label}" matches no operation.${didYouMean(pattern, model.byOperationId.keys())}`);
100
+ }
101
+ const re = globToRegExp(pattern);
102
+ const ids = [...model.byOperationId.keys()].filter((id) => re.test(id));
103
+ if (ids.length === 0)
104
+ throw new ScopeError(`oat: --ops "${label}" matches no operation.`);
105
+ return { exact: false, ids };
106
+ }
107
+ function untestableReason(op, model, authCreates) {
108
+ if (op.entity === null) {
109
+ return `unmodeled: no entity owns ${op.method.toUpperCase()} ${op.path}`;
110
+ }
111
+ const entity = model.entities.get(op.entity);
112
+ const why = entity === undefined ? `"${op.entity}" is not modeled` : untestableEntityReason(entity, model, authCreates);
113
+ if (why !== null)
114
+ return `entity-not-testable: ${why}`;
115
+ return `no-applicable-check: no check judges ${op.operationId}`;
116
+ }
117
+ export function resolveTargetScope(model, input) {
118
+ const ops = [...(input.ops ?? [])].map((s) => s.trim()).filter(Boolean);
119
+ const only = [...(input.only ?? [])].map((s) => s.trim()).filter(Boolean);
120
+ const authCreates = input.authCreates ?? new Set();
121
+ const testable = testableEntities(model, authCreates);
122
+ const requested = { only, ops };
123
+ if (ops.length === 0 && only.length === 0) {
124
+ return {
125
+ entities: testable,
126
+ excluded: [],
127
+ inScope: () => true,
128
+ mode: "full",
129
+ origins: new Map(),
130
+ requested,
131
+ targets: new Set(),
132
+ untestable: new Map(),
133
+ };
134
+ }
135
+ const targets = new Set();
136
+ const originTargets = new Map();
137
+ const excluded = [];
138
+ const admit = (id, exact, opModel, into, label) => {
139
+ const op = opModel.byOperationId.get(id);
140
+ const reason = op === undefined || input.exclusion === undefined ? null : input.exclusion.reason(op);
141
+ if (reason === null) {
142
+ into.add(id);
143
+ return;
144
+ }
145
+ if (exact) {
146
+ throw new ScopeError(`oat: target ${label} is excluded by --profile ${input.exclusion?.profile} (${reason}). ` +
147
+ `Drop it from --ops or run --profile full.`);
148
+ }
149
+ excluded.push({ operationId: label, reason });
150
+ };
151
+ for (const pattern of ops) {
152
+ const colon = pattern.indexOf(":");
153
+ const originId = colon > 0 ? pattern.slice(0, colon) : undefined;
154
+ const originModel = originId === undefined ? undefined : input.origins?.get(originId);
155
+ if (originId !== undefined && originModel !== undefined) {
156
+ const local = pattern.slice(colon + 1);
157
+ const into = originTargets.get(originId) ?? new Set();
158
+ originTargets.set(originId, into);
159
+ const matched = matchPattern(local, originModel, pattern);
160
+ for (const id of matched.ids)
161
+ admit(id, matched.exact, originModel, into, `${originId}:${id}`);
162
+ continue;
163
+ }
164
+ if (originId !== undefined && !model.byOperationId.has(pattern) && input.origins !== undefined) {
165
+ const known = [...input.origins.keys()];
166
+ if (!known.includes(originId)) {
167
+ throw new ScopeError(`oat: --ops "${pattern}" names origin "${originId}", which is not configured.` +
168
+ (known.length === 0 ? "" : ` Known: ${known.join(", ")}.`));
169
+ }
170
+ }
171
+ const matched = matchPattern(pattern, model, pattern);
172
+ for (const id of matched.ids)
173
+ admit(id, matched.exact, model, targets, id);
174
+ }
175
+ for (const name of only) {
176
+ let found = false;
177
+ if (model.entities.has(name)) {
178
+ found = true;
179
+ for (const op of model.operations)
180
+ if (op.entity === name)
181
+ admit(op.operationId, false, model, targets, op.operationId);
182
+ }
183
+ for (const [originId, originModel] of input.origins ?? []) {
184
+ if (!originModel.entities.has(name))
185
+ continue;
186
+ found = true;
187
+ const into = originTargets.get(originId) ?? new Set();
188
+ originTargets.set(originId, into);
189
+ for (const op of originModel.operations) {
190
+ if (op.entity === name)
191
+ admit(op.operationId, false, originModel, into, `${originId}:${op.operationId}`);
192
+ }
193
+ }
194
+ if (!found) {
195
+ const known = [
196
+ ...model.entities.keys(),
197
+ ...[...(input.origins?.values() ?? [])].flatMap((m) => [...m.entities.keys()]),
198
+ ];
199
+ throw new ScopeError(`oat: --only "${name}" names no entity.${didYouMean(name, known)}`);
200
+ }
201
+ }
202
+ const graded = new Set();
203
+ const entities = testable.filter((entity) => {
204
+ const subjects = staticSubjects(entity, model);
205
+ const hit = [...subjects].some((id) => targets.has(id));
206
+ if (hit)
207
+ for (const id of subjects)
208
+ if (targets.has(id))
209
+ graded.add(id);
210
+ return hit;
211
+ });
212
+ const untestable = new Map();
213
+ for (const id of targets) {
214
+ if (graded.has(id))
215
+ continue;
216
+ const op = model.byOperationId.get(id);
217
+ if (op !== undefined)
218
+ untestable.set(id, untestableReason(op, model, authCreates));
219
+ }
220
+ const origins = new Map();
221
+ for (const [originId, ids] of originTargets)
222
+ if (ids.size > 0)
223
+ origins.set(originId, [...ids]);
224
+ return {
225
+ entities,
226
+ excluded,
227
+ inScope: (operationId) => targets.has(operationId),
228
+ mode: "targeted",
229
+ origins,
230
+ requested,
231
+ targets,
232
+ untestable,
233
+ };
234
+ }
235
+ /** Which checks graded which operations, filled as checks run. */
236
+ export class GradeLedger {
237
+ ran = new Map();
238
+ suppressed = new Map();
239
+ needs = new Map();
240
+ #add(map, operations, value) {
241
+ for (const op of operations) {
242
+ const set = map.get(op) ?? new Set();
243
+ set.add(value);
244
+ map.set(op, set);
245
+ }
246
+ }
247
+ graded(operations, check) {
248
+ this.#add(this.ran, operations, check);
249
+ }
250
+ suppress(operations, check) {
251
+ this.#add(this.suppressed, operations, check);
252
+ }
253
+ skipped(operations, needs) {
254
+ this.#add(this.needs, operations, needs);
255
+ }
256
+ }
257
+ const NOT_DEFECTS = new Set(["COVERAGE_GAP", "BLOCKED"]);
258
+ /** The operation an exchange hit, preferring literal segments over parameters on a tie. */
259
+ export function operationFor(model, method, url, baseUrl) {
260
+ let pathname;
261
+ let basePath;
262
+ try {
263
+ pathname = new URL(url).pathname;
264
+ basePath = new URL(baseUrl).pathname.replace(/\/$/, "");
265
+ }
266
+ catch {
267
+ return null;
268
+ }
269
+ const local = basePath !== "" && pathname.startsWith(basePath) ? pathname.slice(basePath.length) : pathname;
270
+ let best = null;
271
+ let bestParams = Number.POSITIVE_INFINITY;
272
+ for (const op of model.operations) {
273
+ if (op.method.toUpperCase() !== method.toUpperCase())
274
+ continue;
275
+ if (!pathTemplateMatches(op.path, local) && !pathTemplateMatches(op.path, pathname))
276
+ continue;
277
+ if (op.pathParams.length < bestParams) {
278
+ best = op;
279
+ bestParams = op.pathParams.length;
280
+ }
281
+ }
282
+ return best;
283
+ }
284
+ function statusOf(held, failed, inconclusive, blocked) {
285
+ if (failed > 0)
286
+ return "failed";
287
+ if (held > 0)
288
+ return "held";
289
+ if (inconclusive > 0)
290
+ return "inconclusive";
291
+ return blocked ? "blocked" : "untested";
292
+ }
293
+ export function buildScopeReport(input) {
294
+ const { model, scope, ledger } = input;
295
+ const graded = new Set();
296
+ for (const entity of scope.entities)
297
+ for (const id of staticSubjects(entity, model))
298
+ graded.add(id);
299
+ const ids = scope.mode === "targeted" ? [...scope.targets].sort() : model.operations.map((op) => op.operationId);
300
+ const operations = ids.map((operationId) => {
301
+ const op = model.byOperationId.get(operationId);
302
+ const mine = (attributed) => attributed?.includes(operationId) === true;
303
+ const defects = input.findings.filter((f) => mine(f.operations) && !NOT_DEFECTS.has(f.verdict));
304
+ const blocked = input.findings.filter((f) => mine(f.operations) && f.verdict === "BLOCKED");
305
+ const failed = [...new Set(defects.map((f) => f.check))].sort();
306
+ const inconclusive = [...new Set(input.inconclusive.filter((i) => mine(i.operations)).map((i) => i.check))]
307
+ .filter((check) => !failed.includes(check))
308
+ .sort();
309
+ const ran = ledger.ran.get(operationId) ?? new Set();
310
+ const gapFindings = input.findings.filter((f) => mine(f.operations) && f.verdict === "COVERAGE_GAP" && ran.has(f.check));
311
+ const gaps = [...new Set(gapFindings.map((f) => f.check))].filter((check) => !failed.includes(check)).sort();
312
+ const held = [...ran]
313
+ .filter((check) => !failed.includes(check) && !inconclusive.includes(check) && !gaps.includes(check))
314
+ .sort();
315
+ const suppressed = [...(ledger.suppressed.get(operationId) ?? [])].sort();
316
+ const status = statusOf(held.length, failed.length, inconclusive.length, suppressed.length > 0 || blocked.length > 0);
317
+ let reason = null;
318
+ if (status === "blocked") {
319
+ reason = blocked[0]?.detail ?? `suppressed by an earlier failure (${suppressed.join(", ")})`;
320
+ }
321
+ else if (status === "untested") {
322
+ const statically = scope.untestable.get(operationId) ??
323
+ (op !== undefined && !graded.has(operationId) ? untestableReason(op, model, input.authCreates) : undefined);
324
+ const needs = [...(ledger.needs.get(operationId) ?? [])];
325
+ reason =
326
+ statically ??
327
+ (gapFindings[0] === undefined ? undefined : `coverage gap: ${gapFindings[0].summary}`) ??
328
+ (needs.length > 0 ? `no check applied: needs ${needs.slice(0, 3).join("; ")}` : "no check exercised it");
329
+ }
330
+ return {
331
+ checks: { failed, gaps, held, inconclusive, suppressed },
332
+ entity: op?.entity ?? null,
333
+ findings: defects.length,
334
+ operationId,
335
+ reason,
336
+ status,
337
+ };
338
+ });
339
+ const support = new Map();
340
+ if (scope.mode === "targeted") {
341
+ for (const exchange of input.transcript) {
342
+ const named = exchange.operationId === undefined ? undefined : model.byOperationId.get(exchange.operationId);
343
+ const op = named ?? operationFor(model, exchange.method, exchange.url, input.baseUrl);
344
+ if (op === null || scope.targets.has(op.operationId))
345
+ continue;
346
+ const use = support.get(op.operationId) ?? { calls: 0, non2xx: 0, operationId: op.operationId };
347
+ use.calls++;
348
+ if (exchange.status < 200 || exchange.status >= 300) {
349
+ use.non2xx++;
350
+ use.firstFailure ??= { requestId: exchange.requestId, status: exchange.status };
351
+ }
352
+ support.set(op.operationId, use);
353
+ }
354
+ }
355
+ return {
356
+ excluded: scope.excluded,
357
+ mode: scope.mode,
358
+ operations,
359
+ originsSkipped: [],
360
+ requested: scope.requested,
361
+ support: [...support.values()].sort((a, b) => a.operationId.localeCompare(b.operationId)),
362
+ };
363
+ }
364
+ /** `--ops a,b --only c` as the reader typed it. */
365
+ export function describeRequested(requested) {
366
+ return [
367
+ ...(requested.ops.length > 0 ? [`--ops ${requested.ops.join(",")}`] : []),
368
+ ...(requested.only.length > 0 ? [`--only ${requested.only.join(",")}`] : []),
369
+ ].join(" ");
370
+ }
371
+ /** The scope a run would take, with no network: what `oat plan --ops` prints. */
372
+ export function planScope(model, scope) {
373
+ const checksFor = new Map();
374
+ for (const entity of scope.entities) {
375
+ for (const check of CHECKS) {
376
+ for (const id of check.subjects(entity, model)) {
377
+ if (!scope.inScope(id))
378
+ continue;
379
+ const set = checksFor.get(id) ?? new Set();
380
+ set.add(check.id);
381
+ checksFor.set(id, set);
382
+ }
383
+ }
384
+ }
385
+ const support = new Set();
386
+ const parents = (op, seen) => {
387
+ if (op === undefined || seen.has(op.operationId))
388
+ return;
389
+ seen.add(op.operationId);
390
+ for (const param of op.pathParams) {
391
+ const owner = owningEntityName(op.path, param);
392
+ const create = owner === null ? undefined : model.byOperationId.get(model.entities.get(owner)?.create ?? "");
393
+ if (create === undefined || create.operationId === op.operationId)
394
+ continue;
395
+ support.add(create.operationId);
396
+ parents(create, seen);
397
+ }
398
+ };
399
+ for (const entity of scope.entities) {
400
+ for (const id of [entity.create, entity.list, entity.read, entity.delete])
401
+ if (id !== undefined)
402
+ support.add(id);
403
+ parents(model.byOperationId.get(entity.create ?? ""), new Set());
404
+ }
405
+ const ids = scope.mode === "targeted" ? [...scope.targets].sort() : [...checksFor.keys()].sort();
406
+ return {
407
+ entities: scope.entities.map((entity) => entity.name),
408
+ excluded: scope.excluded,
409
+ mode: scope.mode,
410
+ origins: Object.fromEntries(scope.origins),
411
+ requested: scope.requested,
412
+ support: [...support].filter((id) => scope.mode === "full" || !scope.targets.has(id)).sort(),
413
+ targets: ids
414
+ .filter((id) => !scope.untestable.has(id))
415
+ .map((operationId) => ({
416
+ checks: [...(checksFor.get(operationId) ?? [])].sort(),
417
+ entity: model.byOperationId.get(operationId)?.entity ?? null,
418
+ operationId,
419
+ })),
420
+ untestable: [...scope.untestable].map(([operationId, reason]) => ({ operationId, reason })),
421
+ };
422
+ }
423
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.js","sourceRoot":"","sources":["../../src/runtime/scope.ts"],"names":[],"mappings":"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"}
@@ -8,11 +8,18 @@
8
8
  */
9
9
  import type { SpecModel } from "../spec/graph.ts";
10
10
  import type { Client } from "./client.ts";
11
+ /** The principal a record belongs to, as far as removing it goes. */
12
+ export interface Owner {
13
+ id: string;
14
+ headers: () => Record<string, string>;
15
+ }
11
16
  export interface Disposable {
12
17
  entity: string;
13
18
  id: string;
14
19
  /** Path parameters in scope when the record was created. */
15
20
  scope: Record<string, string>;
21
+ /** Who created it. Only the creator's view of the record decides whether it is gone. */
22
+ owner: Owner;
16
23
  }
17
24
  export interface TeardownReport {
18
25
  removed: number;
@@ -24,15 +31,21 @@ export interface TeardownReport {
24
31
  unsupported: string[];
25
32
  }
26
33
  /**
27
- * Registry of what a run brought into existence. Entities are recorded in creation order so the
28
- * unwind can reverse it.
34
+ * Registry of what a run brought into existence, recorded the moment each record is created.
35
+ *
36
+ * Entries carry their creator. Removing a record with somebody else's credential is at best a
37
+ * 403 and at worst a 404 that reads as success while the record sits on in the creator's tenant —
38
+ * so the creator deletes first, and a 404 counts as removal only when the creator sees it.
39
+ * Entities are recorded in creation order so the unwind can reverse it. A record oat adopted
40
+ * rather than created must never be recorded here: it was not oat's to remove.
29
41
  */
30
42
  export declare class Ledger {
31
43
  private readonly items;
32
- record(entity: string, id: string, scope: Record<string, string>): void;
44
+ private readonly seen;
45
+ record(entity: string, id: string, scope: Record<string, string>, owner: Owner): void;
33
46
  get size(): number;
34
47
  entries(): readonly Disposable[];
35
- unwind(model: SpecModel, client: Client, headers: () => Record<string, string>, onItem?: (done: number, total: number, item: Disposable) => void): Promise<TeardownReport>;
48
+ unwind(model: SpecModel, client: Client, fallbacks: (owner: Owner) => Owner[], onItem?: (done: number, total: number, item: Disposable) => void): Promise<TeardownReport>;
36
49
  }
37
50
  export declare function renderTeardown(report: TeardownReport, created: number): string[];
38
51
  //# sourceMappingURL=teardown.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"teardown.d.ts","sourceRoot":"","sources":["../../src/runtime/teardown.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAkB,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAIzC,MAAM,WAAW,UAAU;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,EAAE,EAAE,MAAM,CAAA;IACV,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC7B;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC7D,WAAW,EAAE,MAAM,EAAE,CAAA;CACrB;AAED;;;GAGG;AACH,qBAAa,MAAM;IAClB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmB;IAEzC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAKvE,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,IAAI,SAAS,UAAU,EAAE;IAI1B,MAAM,CACX,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACrC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,GAC9D,OAAO,CAAC,cAAc,CAAC;CA2D1B;AAuBD,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAqBhF"}
1
+ {"version":3,"file":"teardown.d.ts","sourceRoot":"","sources":["../../src/runtime/teardown.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAkB,SAAS,EAAE,MAAM,kBAAkB,CAAA;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAIzC,qEAAqE;AACrE,MAAM,WAAW,KAAK;IACrB,EAAE,EAAE,MAAM,CAAA;IACV,OAAO,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACrC;AAED,MAAM,WAAW,UAAU;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,EAAE,EAAE,MAAM,CAAA;IACV,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,wFAAwF;IACxF,KAAK,EAAE,KAAK,CAAA;CACZ;AAED,MAAM,WAAW,cAAc;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC7D,WAAW,EAAE,MAAM,EAAE,CAAA;CACrB;AAED;;;;;;;;GAQG;AACH,qBAAa,MAAM;IAClB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAmB;IACzC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IAEzC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAQrF,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,OAAO,IAAI,SAAS,UAAU,EAAE;IAI1B,MAAM,CACX,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,MAAM,EAGd,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,KAAK,EAAE,EACpC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,GAC9D,OAAO,CAAC,cAAc,CAAC;CA8E1B;AAuBD,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAqBhF"}
@@ -9,15 +9,25 @@
9
9
  import { parseRouteRef } from "../spec/load.js";
10
10
  import { fillPath } from "./world.js";
11
11
  /**
12
- * Registry of what a run brought into existence. Entities are recorded in creation order so the
13
- * unwind can reverse it.
12
+ * Registry of what a run brought into existence, recorded the moment each record is created.
13
+ *
14
+ * Entries carry their creator. Removing a record with somebody else's credential is at best a
15
+ * 403 and at worst a 404 that reads as success while the record sits on in the creator's tenant —
16
+ * so the creator deletes first, and a 404 counts as removal only when the creator sees it.
17
+ * Entities are recorded in creation order so the unwind can reverse it. A record oat adopted
18
+ * rather than created must never be recorded here: it was not oat's to remove.
14
19
  */
15
20
  export class Ledger {
16
21
  items = [];
17
- record(entity, id, scope) {
22
+ seen = new Set();
23
+ record(entity, id, scope, owner) {
18
24
  if (id === "" || id === "undefined")
19
25
  return;
20
- this.items.push({ entity, id, scope: { ...scope } });
26
+ const key = `${entity}\u0000${id}`;
27
+ if (this.seen.has(key))
28
+ return;
29
+ this.seen.add(key);
30
+ this.items.push({ entity, id, owner, scope: { ...scope } });
21
31
  }
22
32
  get size() {
23
33
  return this.items.length;
@@ -25,20 +35,28 @@ export class Ledger {
25
35
  entries() {
26
36
  return this.items;
27
37
  }
28
- async unwind(model, client, headers, onItem) {
38
+ async unwind(model, client,
39
+ /* Principals allowed to remove a record its creator was refused, strongest first — the
40
+ * same tenant's owner removing what a member created. */
41
+ fallbacks, onItem) {
29
42
  const report = { failed: [], removed: 0, unsupported: [] };
30
43
  const unsupported = new Set();
31
- /* Newest first: a child created inside a parent must be removed before the parent, and
32
- * creation order already encodes that dependency. */
44
+ /*
45
+ * Deepest first, one level at a time, and every record within a level at once. A record's
46
+ * depth is how many path parameters its delete route takes: a child's route carries its
47
+ * parent's parameters and its own, so a level never holds a record and its parent.
48
+ * Newest first within a level keeps the order the creation sequence implies.
49
+ */
33
50
  const queue = [...this.items].reverse();
51
+ const depthOf = (item) => deleteOperationFor(item.entity, model)?.pathParams.length ?? 0;
52
+ const levels = [...new Set(queue.map(depthOf))].sort((a, b) => b - a);
34
53
  let done = 0;
35
- for (const item of queue) {
36
- onItem?.(done, queue.length, item);
54
+ const remove = async (item) => {
37
55
  const deleteOp = deleteOperationFor(item.entity, model);
38
56
  if (deleteOp === null) {
39
57
  unsupported.add(item.entity);
40
58
  done += 1;
41
- continue;
59
+ return;
42
60
  }
43
61
  const param = deleteOp.pathParams.at(-1);
44
62
  const scope = param === undefined ? item.scope : { ...item.scope, [param]: item.id };
@@ -53,19 +71,31 @@ export class Ledger {
53
71
  reason: error instanceof Error ? error.message : String(error),
54
72
  });
55
73
  done += 1;
56
- continue;
74
+ return;
57
75
  }
58
76
  try {
59
- const exchange = await client.request("DELETE", path, { headers });
60
- /* 404 means it is already gone — the goal is absence, not a successful call. */
61
- if (exchange.status < 300 || exchange.status === 404 || exchange.status === 410) {
62
- report.removed += 1;
77
+ const attempts = [];
78
+ let removed = false;
79
+ for (const [index, deleter] of [item.owner, ...fallbacks(item.owner)].entries()) {
80
+ const exchange = await client.request("DELETE", path, { headers: deleter.headers });
81
+ attempts.push(`${deleter.id}: ${exchange.status}`);
82
+ /* Gone is the goal, not a successful call — but only the creator's 404 says the
83
+ * record is gone. Anyone else's may just mean they cannot see it. */
84
+ if (exchange.status < 300 || (index === 0 && (exchange.status === 404 || exchange.status === 410))) {
85
+ removed = true;
86
+ break;
87
+ }
88
+ /* Only a denial is worth retrying with a stronger credential. */
89
+ if (exchange.status !== 401 && exchange.status !== 403)
90
+ break;
63
91
  }
92
+ if (removed)
93
+ report.removed += 1;
64
94
  else {
65
95
  report.failed.push({
66
96
  entity: item.entity,
67
97
  id: item.id,
68
- reason: `DELETE ${path} returned ${exchange.status}`,
98
+ reason: `DELETE ${path} returned ${attempts.join(", ")}`,
69
99
  });
70
100
  }
71
101
  }
@@ -78,6 +108,9 @@ export class Ledger {
78
108
  }
79
109
  done += 1;
80
110
  onItem?.(done, queue.length, item);
111
+ };
112
+ for (const level of levels) {
113
+ await Promise.all(queue.filter((item) => depthOf(item) === level).map(remove));
81
114
  }
82
115
  report.unsupported = [...unsupported];
83
116
  return report;