@vertesia/common 1.6.0-dev.20260923.085325Z → 1.6.0-dev.20261004.085740Z

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 (405) hide show
  1. package/lib/access-control-values.d.ts +2 -2
  2. package/lib/access-control-values.d.ts.map +1 -1
  3. package/lib/access-control-values.js +2 -2
  4. package/lib/access-control-values.js.map +1 -1
  5. package/lib/access-control.d.ts +7 -8
  6. package/lib/access-control.d.ts.map +1 -1
  7. package/lib/access-control.js +5 -0
  8. package/lib/access-control.js.map +1 -1
  9. package/lib/analytics.d.ts +6 -7
  10. package/lib/analytics.d.ts.map +1 -1
  11. package/lib/api-contract/components.generated.json +1389 -45
  12. package/lib/api-schemas/access-control.d.ts +33 -10
  13. package/lib/api-schemas/access-control.d.ts.map +1 -1
  14. package/lib/api-schemas/access-control.js +7 -6
  15. package/lib/api-schemas/access-control.js.map +1 -1
  16. package/lib/api-schemas/account.d.ts +5 -4
  17. package/lib/api-schemas/account.d.ts.map +1 -1
  18. package/lib/api-schemas/account.js.map +1 -1
  19. package/lib/api-schemas/agent-budget.d.ts +15 -0
  20. package/lib/api-schemas/agent-budget.d.ts.map +1 -0
  21. package/lib/api-schemas/agent-budget.js +55 -0
  22. package/lib/api-schemas/agent-budget.js.map +1 -0
  23. package/lib/api-schemas/agent-runs.d.ts +1525 -100
  24. package/lib/api-schemas/agent-runs.d.ts.map +1 -1
  25. package/lib/api-schemas/agent-runs.js +60 -28
  26. package/lib/api-schemas/agent-runs.js.map +1 -1
  27. package/lib/api-schemas/apikey.d.ts +11 -10
  28. package/lib/api-schemas/apikey.d.ts.map +1 -1
  29. package/lib/api-schemas/apikey.js.map +1 -1
  30. package/lib/api-schemas/app-lifecycle.d.ts +13 -0
  31. package/lib/api-schemas/app-lifecycle.d.ts.map +1 -1
  32. package/lib/api-schemas/app-lifecycle.js +4 -0
  33. package/lib/api-schemas/app-lifecycle.js.map +1 -1
  34. package/lib/api-schemas/app-runtime.d.ts +1821 -108
  35. package/lib/api-schemas/app-runtime.d.ts.map +1 -1
  36. package/lib/api-schemas/audit-trail.d.ts +20 -0
  37. package/lib/api-schemas/audit-trail.d.ts.map +1 -1
  38. package/lib/api-schemas/audit-trail.js +7 -1
  39. package/lib/api-schemas/audit-trail.js.map +1 -1
  40. package/lib/api-schemas/content.d.ts +665 -30
  41. package/lib/api-schemas/content.d.ts.map +1 -1
  42. package/lib/api-schemas/content.js +47 -0
  43. package/lib/api-schemas/content.js.map +1 -1
  44. package/lib/api-schemas/cost-analytics.d.ts +18 -0
  45. package/lib/api-schemas/cost-analytics.d.ts.map +1 -1
  46. package/lib/api-schemas/cost-analytics.js +31 -2
  47. package/lib/api-schemas/cost-analytics.js.map +1 -1
  48. package/lib/api-schemas/document-processing.d.ts +381 -18
  49. package/lib/api-schemas/document-processing.d.ts.map +1 -1
  50. package/lib/api-schemas/environment.d.ts +0 -5
  51. package/lib/api-schemas/environment.d.ts.map +1 -1
  52. package/lib/api-schemas/environment.js +0 -1
  53. package/lib/api-schemas/environment.js.map +1 -1
  54. package/lib/api-schemas/events.d.ts +1143 -54
  55. package/lib/api-schemas/events.d.ts.map +1 -1
  56. package/lib/api-schemas/group.d.ts +2 -1
  57. package/lib/api-schemas/group.d.ts.map +1 -1
  58. package/lib/api-schemas/group.js.map +1 -1
  59. package/lib/api-schemas/index.d.ts +1 -0
  60. package/lib/api-schemas/index.d.ts.map +1 -1
  61. package/lib/api-schemas/index.js +1 -0
  62. package/lib/api-schemas/index.js.map +1 -1
  63. package/lib/api-schemas/inference-profile.d.ts +5017 -0
  64. package/lib/api-schemas/inference-profile.d.ts.map +1 -0
  65. package/lib/api-schemas/inference-profile.js +135 -0
  66. package/lib/api-schemas/inference-profile.js.map +1 -0
  67. package/lib/api-schemas/integrations.d.ts +6 -6
  68. package/lib/api-schemas/interaction.d.ts +34391 -703
  69. package/lib/api-schemas/interaction.d.ts.map +1 -1
  70. package/lib/api-schemas/interaction.js +34 -1
  71. package/lib/api-schemas/interaction.js.map +1 -1
  72. package/lib/api-schemas/invites.d.ts +10 -9
  73. package/lib/api-schemas/invites.d.ts.map +1 -1
  74. package/lib/api-schemas/invites.js.map +1 -1
  75. package/lib/api-schemas/process.d.ts +653 -34
  76. package/lib/api-schemas/process.d.ts.map +1 -1
  77. package/lib/api-schemas/process.js +66 -0
  78. package/lib/api-schemas/process.js.map +1 -1
  79. package/lib/api-schemas/project-configuration.d.ts +1323 -62
  80. package/lib/api-schemas/project-configuration.d.ts.map +1 -1
  81. package/lib/api-schemas/project-configuration.js +15 -1
  82. package/lib/api-schemas/project-configuration.js.map +1 -1
  83. package/lib/api-schemas/project.d.ts +2031 -116
  84. package/lib/api-schemas/project.d.ts.map +1 -1
  85. package/lib/api-schemas/project.js +9 -1
  86. package/lib/api-schemas/project.js.map +1 -1
  87. package/lib/api-schemas/quota.d.ts +7 -6
  88. package/lib/api-schemas/quota.d.ts.map +1 -1
  89. package/lib/api-schemas/quota.js.map +1 -1
  90. package/lib/api-schemas/registry.d.ts +27820 -35892
  91. package/lib/api-schemas/registry.d.ts.map +1 -1
  92. package/lib/api-schemas/registry.js +55 -5
  93. package/lib/api-schemas/registry.js.map +1 -1
  94. package/lib/api-schemas/run-budget.d.ts +52 -0
  95. package/lib/api-schemas/run-budget.d.ts.map +1 -0
  96. package/lib/api-schemas/run-budget.js +55 -0
  97. package/lib/api-schemas/run-budget.js.map +1 -0
  98. package/lib/api-schemas/store.d.ts +6388 -338
  99. package/lib/api-schemas/store.d.ts.map +1 -1
  100. package/lib/api-schemas/store.js +11 -0
  101. package/lib/api-schemas/store.js.map +1 -1
  102. package/lib/api-schemas/user.d.ts +9 -8
  103. package/lib/api-schemas/user.d.ts.map +1 -1
  104. package/lib/api-schemas/user.js.map +1 -1
  105. package/lib/api-schemas/view-execution.d.ts +8 -0
  106. package/lib/api-schemas/view-execution.d.ts.map +1 -1
  107. package/lib/api-schemas/views.d.ts +14 -0
  108. package/lib/api-schemas/views.d.ts.map +1 -1
  109. package/lib/api-schemas/workflow-runs.d.ts +335 -12
  110. package/lib/api-schemas/workflow-runs.d.ts.map +1 -1
  111. package/lib/api-schemas/workflow-runs.js +2 -0
  112. package/lib/api-schemas/workflow-runs.js.map +1 -1
  113. package/lib/apikey.d.ts +30 -25
  114. package/lib/apikey.d.ts.map +1 -1
  115. package/lib/apikey.js.map +1 -1
  116. package/lib/apps.d.ts +116 -121
  117. package/lib/apps.d.ts.map +1 -1
  118. package/lib/apps.js.map +1 -1
  119. package/lib/audit-trail.d.ts +17 -18
  120. package/lib/audit-trail.d.ts.map +1 -1
  121. package/lib/audit-trail.js +2 -0
  122. package/lib/audit-trail.js.map +1 -1
  123. package/lib/browser-credentials.d.ts +11 -12
  124. package/lib/browser-credentials.d.ts.map +1 -1
  125. package/lib/common.d.ts +17 -24
  126. package/lib/common.d.ts.map +1 -1
  127. package/lib/content-query.d.ts +3 -4
  128. package/lib/content-query.d.ts.map +1 -1
  129. package/lib/cost-analytics.d.ts +13 -21
  130. package/lib/cost-analytics.d.ts.map +1 -1
  131. package/lib/cost-analytics.js +0 -7
  132. package/lib/cost-analytics.js.map +1 -1
  133. package/lib/data-platform.d.ts +79 -88
  134. package/lib/data-platform.d.ts.map +1 -1
  135. package/lib/data-platform.js.map +1 -1
  136. package/lib/delegation.d.ts +4 -5
  137. package/lib/delegation.d.ts.map +1 -1
  138. package/lib/email.d.ts +15 -17
  139. package/lib/email.d.ts.map +1 -1
  140. package/lib/email.js.map +1 -1
  141. package/lib/embeddings.d.ts +25 -26
  142. package/lib/embeddings.d.ts.map +1 -1
  143. package/lib/environment.d.ts +16 -17
  144. package/lib/environment.d.ts.map +1 -1
  145. package/lib/environment.js.map +1 -1
  146. package/lib/facets.d.ts +3 -4
  147. package/lib/facets.d.ts.map +1 -1
  148. package/lib/group.d.ts +6 -7
  149. package/lib/group.d.ts.map +1 -1
  150. package/lib/group.js.map +1 -1
  151. package/lib/index.d.ts +3 -1
  152. package/lib/index.d.ts.map +1 -1
  153. package/lib/index.js.map +1 -1
  154. package/lib/inference-profile.d.ts +17 -0
  155. package/lib/inference-profile.d.ts.map +1 -0
  156. package/lib/inference-profile.js +2 -0
  157. package/lib/inference-profile.js.map +1 -0
  158. package/lib/integrations.d.ts +19 -20
  159. package/lib/integrations.d.ts.map +1 -1
  160. package/lib/integrations.js.map +1 -1
  161. package/lib/interaction.d.ts +68 -73
  162. package/lib/interaction.d.ts.map +1 -1
  163. package/lib/interaction.js.map +1 -1
  164. package/lib/meters.d.ts +4 -5
  165. package/lib/meters.d.ts.map +1 -1
  166. package/lib/oauth-server.d.ts +40 -41
  167. package/lib/oauth-server.d.ts.map +1 -1
  168. package/lib/oauth.d.ts +9 -10
  169. package/lib/oauth.d.ts.map +1 -1
  170. package/lib/payload.d.ts +11 -14
  171. package/lib/payload.d.ts.map +1 -1
  172. package/lib/pending-asks.d.ts +8 -13
  173. package/lib/pending-asks.d.ts.map +1 -1
  174. package/lib/pending-asks.js +0 -4
  175. package/lib/pending-asks.js.map +1 -1
  176. package/lib/platform-event.d.ts +48 -51
  177. package/lib/platform-event.d.ts.map +1 -1
  178. package/lib/platform-event.js.map +1 -1
  179. package/lib/principal-context.d.ts +3 -3
  180. package/lib/principal-context.d.ts.map +1 -1
  181. package/lib/project.d.ts +42 -43
  182. package/lib/project.d.ts.map +1 -1
  183. package/lib/project.js.map +1 -1
  184. package/lib/prompt.d.ts +14 -16
  185. package/lib/prompt.d.ts.map +1 -1
  186. package/lib/prompt.js.map +1 -1
  187. package/lib/query.d.ts +10 -13
  188. package/lib/query.d.ts.map +1 -1
  189. package/lib/rate-limiter.d.ts +7 -11
  190. package/lib/rate-limiter.d.ts.map +1 -1
  191. package/lib/refs.d.ts +2 -3
  192. package/lib/refs.d.ts.map +1 -1
  193. package/lib/refs.js.map +1 -1
  194. package/lib/roles/types.d.ts +5 -6
  195. package/lib/roles/types.d.ts.map +1 -1
  196. package/lib/roles/types.js.map +1 -1
  197. package/lib/run-budget.d.ts +8 -0
  198. package/lib/run-budget.d.ts.map +1 -0
  199. package/lib/run-budget.js +2 -0
  200. package/lib/run-budget.js.map +1 -0
  201. package/lib/runs.d.ts +9 -10
  202. package/lib/runs.d.ts.map +1 -1
  203. package/lib/secrets.d.ts +20 -21
  204. package/lib/secrets.d.ts.map +1 -1
  205. package/lib/store/agent-approval.d.ts +4 -5
  206. package/lib/store/agent-approval.d.ts.map +1 -1
  207. package/lib/store/agent-approval.js.map +1 -1
  208. package/lib/store/agent-run.d.ts +50 -52
  209. package/lib/store/agent-run.d.ts.map +1 -1
  210. package/lib/store/agent-run.js.map +1 -1
  211. package/lib/store/collections.d.ts +11 -12
  212. package/lib/store/collections.d.ts.map +1 -1
  213. package/lib/store/collections.js.map +1 -1
  214. package/lib/store/conversation-state.d.ts +14 -8
  215. package/lib/store/conversation-state.d.ts.map +1 -1
  216. package/lib/store/doc-analyzer.d.ts +7 -8
  217. package/lib/store/doc-analyzer.d.ts.map +1 -1
  218. package/lib/store/dsl-workflow.d.ts +10 -12
  219. package/lib/store/dsl-workflow.d.ts.map +1 -1
  220. package/lib/store/dsl-workflow.js.map +1 -1
  221. package/lib/store/duration.d.ts +12 -0
  222. package/lib/store/duration.d.ts.map +1 -0
  223. package/lib/store/duration.js +2 -0
  224. package/lib/store/duration.js.map +1 -0
  225. package/lib/store/grounded-extraction.d.ts +6 -7
  226. package/lib/store/grounded-extraction.d.ts.map +1 -1
  227. package/lib/store/intake-policy-schema.generated.d.ts.map +1 -1
  228. package/lib/store/intake-policy-schema.generated.js +143 -2
  229. package/lib/store/intake-policy-schema.generated.js.map +1 -1
  230. package/lib/store/process.d.ts +70 -69
  231. package/lib/store/process.d.ts.map +1 -1
  232. package/lib/store/process.js.map +1 -1
  233. package/lib/store/rendering.d.ts +6 -12
  234. package/lib/store/rendering.d.ts.map +1 -1
  235. package/lib/store/rendering.js +0 -5
  236. package/lib/store/rendering.js.map +1 -1
  237. package/lib/store/signals.d.ts +43 -0
  238. package/lib/store/signals.d.ts.map +1 -1
  239. package/lib/store/signals.js +6 -1
  240. package/lib/store/signals.js.map +1 -1
  241. package/lib/store/store.d.ts +92 -91
  242. package/lib/store/store.d.ts.map +1 -1
  243. package/lib/store/store.js.map +1 -1
  244. package/lib/store/task.d.ts +10 -14
  245. package/lib/store/task.d.ts.map +1 -1
  246. package/lib/store/workflow.d.ts +35 -41
  247. package/lib/store/workflow.d.ts.map +1 -1
  248. package/lib/store/workflow.js.map +1 -1
  249. package/lib/sts-token-types.d.ts +14 -19
  250. package/lib/sts-token-types.d.ts.map +1 -1
  251. package/lib/tools.d.ts +12 -13
  252. package/lib/tools.d.ts.map +1 -1
  253. package/lib/transient-tokens.d.ts +3 -3
  254. package/lib/transient-tokens.d.ts.map +1 -1
  255. package/lib/user.d.ts +17 -20
  256. package/lib/user.d.ts.map +1 -1
  257. package/lib/user.js.map +1 -1
  258. package/lib/vertesia-common.js +2 -2
  259. package/lib/vertesia-common.js.map +1 -1
  260. package/lib/views.d.ts +61 -64
  261. package/lib/views.d.ts.map +1 -1
  262. package/lib/views.js.map +1 -1
  263. package/lib/wire-types.generated.d.ts +11231 -0
  264. package/lib/wire-types.generated.d.ts.map +1 -0
  265. package/lib/wire-types.generated.js +4 -0
  266. package/lib/wire-types.generated.js.map +1 -0
  267. package/lib/workflow-analytics.d.ts +21 -19
  268. package/lib/workflow-analytics.d.ts.map +1 -1
  269. package/lib/workflow-analytics.js +2 -2
  270. package/lib/workflow-analytics.js.map +1 -1
  271. package/package.json +15 -10
  272. package/src/access-control-values.ts +2 -2
  273. package/src/access-control.ts +7 -15
  274. package/src/analytics.ts +6 -13
  275. package/src/api-contract/components.generated.json +1389 -45
  276. package/src/api-schemas/access-control.ts +18 -16
  277. package/src/api-schemas/account.ts +5 -4
  278. package/src/api-schemas/agent-budget.ts +60 -0
  279. package/src/api-schemas/agent-runs.ts +63 -27
  280. package/src/api-schemas/apikey.ts +11 -10
  281. package/src/api-schemas/app-lifecycle.ts +5 -0
  282. package/src/api-schemas/audit-trail.ts +7 -1
  283. package/src/api-schemas/content.ts +55 -0
  284. package/src/api-schemas/cost-analytics.ts +34 -2
  285. package/src/api-schemas/environment.ts +0 -1
  286. package/src/api-schemas/group.ts +2 -1
  287. package/src/api-schemas/index.ts +1 -0
  288. package/src/api-schemas/inference-profile.ts +151 -0
  289. package/src/api-schemas/interaction.ts +40 -1
  290. package/src/api-schemas/invites.ts +10 -9
  291. package/src/api-schemas/process.ts +78 -2
  292. package/src/api-schemas/project-configuration.ts +19 -1
  293. package/src/api-schemas/project.ts +19 -10
  294. package/src/api-schemas/quota.ts +7 -6
  295. package/src/api-schemas/registry.ts +131 -33
  296. package/src/api-schemas/run-budget.ts +60 -0
  297. package/src/api-schemas/store.ts +12 -0
  298. package/src/api-schemas/user.ts +9 -8
  299. package/src/api-schemas/workflow-runs.ts +2 -0
  300. package/src/apikey.ts +30 -39
  301. package/src/apps.ts +116 -239
  302. package/src/audit-trail.ts +18 -33
  303. package/src/browser-credentials.ts +11 -23
  304. package/src/common.ts +17 -37
  305. package/src/content-query.ts +3 -4
  306. package/src/cost-analytics.ts +13 -35
  307. package/src/data-platform.ts +79 -168
  308. package/src/delegation.ts +4 -9
  309. package/src/email.ts +15 -29
  310. package/src/embeddings.ts +25 -51
  311. package/src/environment.ts +16 -33
  312. package/src/facets.ts +3 -4
  313. package/src/group.ts +6 -13
  314. package/src/index.ts +27 -0
  315. package/src/inference-profile.ts +20 -0
  316. package/src/integrations.ts +21 -41
  317. package/src/interaction.ts +68 -137
  318. package/src/meters.ts +4 -9
  319. package/src/oauth-server.ts +40 -81
  320. package/src/oauth.ts +9 -19
  321. package/src/payload.ts +11 -25
  322. package/src/pending-asks.ts +8 -22
  323. package/src/platform-event.ts +48 -97
  324. package/src/principal-context.ts +3 -3
  325. package/src/project.ts +43 -86
  326. package/src/prompt.ts +14 -31
  327. package/src/query.ts +10 -20
  328. package/src/rate-limiter.ts +7 -19
  329. package/src/refs.ts +2 -3
  330. package/src/roles/types.ts +5 -11
  331. package/src/run-budget.ts +8 -0
  332. package/src/runs.ts +9 -19
  333. package/src/secrets.ts +20 -41
  334. package/src/store/agent-approval.ts +4 -9
  335. package/src/store/agent-run.ts +53 -99
  336. package/src/store/collections.ts +11 -23
  337. package/src/store/conversation-state.ts +15 -15
  338. package/src/store/doc-analyzer.ts +7 -15
  339. package/src/store/dsl-workflow.ts +10 -23
  340. package/src/store/duration.ts +45 -0
  341. package/src/store/grounded-extraction.ts +6 -13
  342. package/src/store/intake-policy-schema.generated.ts +148 -2
  343. package/src/store/process.ts +70 -140
  344. package/src/store/rendering.ts +6 -19
  345. package/src/store/signals.ts +48 -0
  346. package/src/store/store.ts +92 -181
  347. package/src/store/task.ts +10 -24
  348. package/src/store/workflow.ts +35 -71
  349. package/src/sts-token-types.ts +14 -33
  350. package/src/tools.ts +12 -25
  351. package/src/transient-tokens.ts +3 -3
  352. package/src/user.ts +17 -39
  353. package/src/views.ts +61 -125
  354. package/src/wire-types.generated.ts +11889 -0
  355. package/src/workflow-analytics.ts +21 -19
  356. package/src/agent-resources.test.ts +0 -89
  357. package/src/api-contract/components.contract.test.ts +0 -99
  358. package/src/api-schemas/access-control.contract.test.ts +0 -209
  359. package/src/api-schemas/account-apikey.test.ts +0 -60
  360. package/src/api-schemas/account.contract.test.ts +0 -319
  361. package/src/api-schemas/adapter-edge-cases.test.ts +0 -499
  362. package/src/api-schemas/agent-communication.contract.test.ts +0 -90
  363. package/src/api-schemas/agent-history.contract.test.ts +0 -42
  364. package/src/api-schemas/agent-runs.contract.test.ts +0 -240
  365. package/src/api-schemas/api-discriminators.test.ts +0 -185
  366. package/src/api-schemas/apikey.contract.test.ts +0 -270
  367. package/src/api-schemas/app-delete-summary.contract.test.ts +0 -67
  368. package/src/api-schemas/bulk-operation.contract.test.ts +0 -39
  369. package/src/api-schemas/bundle-isolation.test.ts +0 -44
  370. package/src/api-schemas/commands.contract.test.ts +0 -51
  371. package/src/api-schemas/cost-analytics.contract.test.ts +0 -64
  372. package/src/api-schemas/dashboard.contract.test.ts +0 -138
  373. package/src/api-schemas/data-store.contract.test.ts +0 -97
  374. package/src/api-schemas/document-processing.contract.test.ts +0 -76
  375. package/src/api-schemas/emit-json-schema.test.ts +0 -36
  376. package/src/api-schemas/environment.contract.test.ts +0 -202
  377. package/src/api-schemas/group.contract.test.ts +0 -205
  378. package/src/api-schemas/indexing.contract.test.ts +0 -122
  379. package/src/api-schemas/interaction.contract.test.ts +0 -307
  380. package/src/api-schemas/invites.contract.test.ts +0 -268
  381. package/src/api-schemas/llumiverse.contract.test.ts +0 -179
  382. package/src/api-schemas/mcp-api-key.contract.test.ts +0 -125
  383. package/src/api-schemas/oauth.contract.test.ts +0 -181
  384. package/src/api-schemas/parameters.test.ts +0 -402
  385. package/src/api-schemas/process.contract.test.ts +0 -115
  386. package/src/api-schemas/project.contract.test.ts +0 -472
  387. package/src/api-schemas/pruning-limits.test.ts +0 -189
  388. package/src/api-schemas/quota.contract.test.ts +0 -147
  389. package/src/api-schemas/registry-groups.test.ts +0 -99
  390. package/src/api-schemas/response-pruning.test.ts +0 -260
  391. package/src/api-schemas/store.contract.test.ts +0 -378
  392. package/src/api-schemas/sts.contract.test.ts +0 -112
  393. package/src/api-schemas/user.contract.test.ts +0 -197
  394. package/src/api-schemas/views.contract.test.ts +0 -112
  395. package/src/api-schemas/zeno-response-contracts.test.ts +0 -380
  396. package/src/apps.test.ts +0 -89
  397. package/src/oauth-scopes.test.ts +0 -15
  398. package/src/roles.test.ts +0 -33
  399. package/src/schema-for-extraction.test.ts +0 -191
  400. package/src/store/agent-approval.test.ts +0 -18
  401. package/src/store/content-type-editing.test.ts +0 -17
  402. package/src/store/ms.d.ts +0 -42
  403. package/src/store/schedule.test.ts +0 -59
  404. package/src/store/workflow.test.ts +0 -640
  405. package/src/versions.test.ts +0 -13
@@ -1,181 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import {
3
- OAuthAuthorizationServerMetadataSchema,
4
- OAuthDeviceAuthorizationResponseSchema,
5
- OAuthGrantStatusSchema,
6
- OAuthTokenResponseSchema,
7
- } from './oauth-server.js';
8
- import { ApiSchemaComponents, validateApiRequest, validateApiResponse } from './registry.js';
9
-
10
- /**
11
- * The OAuth contracts, pinned where converting them changed what the document says or where the
12
- * conversion had a choice to make.
13
- *
14
- * The provider/client/grant closure is 31 components and almost all of it re-emits byte-identically;
15
- * what is asserted here is the handful of places it does not, plus the request/response secret split
16
- * that is the reason these components are shaped the way they are.
17
- */
18
-
19
- describe('the OAuth grant status filter', () => {
20
- /**
21
- * `?status=` takes the grant statuses plus `all`, and the query schema spells all four out rather
22
- * than composing them, because a union would reach the document as an `anyOf` of a `$ref` and a
23
- * `const` once the parameter is expanded. That leaves two lists that have to agree, so this is
24
- * the assertion that they do: adding a fourth grant status without adding it to the filter would
25
- * publish a status the caller cannot filter for.
26
- */
27
- it('offers every grant status, plus the all sentinel', () => {
28
- expect(statusEnumOf('ListOAuthGrantsQuery')).toEqual([...OAuthGrantStatusSchema.options, 'all']);
29
- });
30
-
31
- it('is the same list on the bulk revoke body as on the listing query', () => {
32
- // Revoking in bulk selects grants exactly the way listing them does — a filter that meant
33
- // something different from the preview the caller looked at is the failure mode here.
34
- expect(statusEnumOf('BulkRevokeOAuthGrantsPayload')).toEqual(statusEnumOf('ListOAuthGrantsQuery'));
35
- });
36
-
37
- it('rejects a status that is neither, rather than listing everything', () => {
38
- expect(validateApiRequest('BulkRevokeOAuthGrantsPayload', { status: 'pending' }).valid).toBe(false);
39
- expect(validateApiRequest('BulkRevokeOAuthGrantsPayload', { status: 'all' }).valid).toBe(true);
40
- });
41
- });
42
-
43
- describe('the OAuth provider secret boundary', () => {
44
- it('accepts a client secret on create', () => {
45
- expect(
46
- validateApiRequest('CreateOAuthProviderPayload', {
47
- name: 'acme',
48
- display_name: 'Acme',
49
- client_id: 'client-123',
50
- client_secret: 'shhh',
51
- }),
52
- ).toMatchObject({ valid: true });
53
- });
54
-
55
- it('refuses to publish one on the way back out', () => {
56
- // The read shape says whether a secret exists and nothing more. `additionalProperties: false`
57
- // is what turns a mapper that spread the document into a failed response instead of a leak.
58
- const response = {
59
- id: '68b1779130afe5403a1589ba',
60
- name: 'acme',
61
- display_name: 'Acme',
62
- project: '69d4762f24d3048c99149d0b',
63
- client_id: 'client-123',
64
- has_client_secret: true,
65
- use_pkce: true,
66
- created_at: '2026-01-01T00:00:00.000Z',
67
- updated_at: '2026-01-01T00:00:00.000Z',
68
- };
69
-
70
- expect(validateApiResponse('OAuthProvider', response)).toMatchObject({ valid: true });
71
- expect(validateApiResponse('OAuthProvider', { ...response, client_secret: 'shhh' }).valid).toBe(false);
72
- });
73
-
74
- it('publishes the client secret on the create response only', () => {
75
- // The one call that generates it returns it once; every later read of the same client gets
76
- // `client_secret_configured`. Two components rather than one optional field is what keeps
77
- // that visible in the document.
78
- const client = {
79
- client_id: 'vts_abc',
80
- client_name: 'Acme CLI',
81
- client_type: 'public',
82
- redirect_uris: ['https://acme.example/callback'],
83
- grant_types: ['authorization_code'],
84
- response_types: ['code'],
85
- token_endpoint_auth_method: 'none',
86
- allowed_scopes: ['openid'],
87
- registration_source: 'admin',
88
- status: 'active',
89
- project_binding_mode: 'user_select',
90
- created_at: '2026-01-01T00:00:00.000Z',
91
- updated_at: '2026-01-01T00:00:00.000Z',
92
- };
93
-
94
- expect(validateApiResponse('OAuthClientCreateResponse', { ...client, client_secret: 'vss_x' })).toMatchObject({
95
- valid: true,
96
- });
97
- expect(validateApiResponse('OAuthClient', { ...client, client_secret: 'vss_x' }).valid).toBe(false);
98
- });
99
- });
100
-
101
- describe('UpdateOAuthProviderPayload', () => {
102
- it('accepts an empty body and a single-field body, and still rejects a typo', () => {
103
- expect(validateApiRequest('UpdateOAuthProviderPayload', {}).valid).toBe(true);
104
- expect(validateApiRequest('UpdateOAuthProviderPayload', { display_name: 'Acme' }).valid).toBe(true);
105
- expect(validateApiRequest('UpdateOAuthProviderPayload', { displayName: 'Acme' }).valid).toBe(false);
106
- });
107
- });
108
-
109
- describe('OAuth protocol response parsing', () => {
110
- it('parses complete authorization-server metadata and ignores registered extensions', () => {
111
- const result = OAuthAuthorizationServerMetadataSchema.safeParse({
112
- issuer: 'https://sts.example.com',
113
- authorization_endpoint: 'https://cloud.example.com/oauth/authorize',
114
- token_endpoint: 'https://sts.example.com/oauth/token',
115
- jwks_uri: 'https://sts.example.com/.well-known/jwks.json',
116
- response_types_supported: ['code'],
117
- grant_types_supported: ['authorization_code'],
118
- code_challenge_methods_supported: ['S256'],
119
- token_endpoint_auth_methods_supported: ['none'],
120
- scopes_supported: ['openid'],
121
- extension_parameter: 'ignored',
122
- });
123
-
124
- expect(result.success).toBe(true);
125
- if (result.success) {
126
- expect(result.data).not.toHaveProperty('extension_parameter');
127
- }
128
- });
129
-
130
- it('rejects incomplete device and token responses', () => {
131
- expect(OAuthDeviceAuthorizationResponseSchema.safeParse({ device_code: 'device' }).success).toBe(false);
132
- expect(
133
- OAuthTokenResponseSchema.safeParse({
134
- access_token: 'token',
135
- token_type: 'Basic',
136
- expires_in: 3600,
137
- }).success,
138
- ).toBe(false);
139
- });
140
-
141
- it('accepts RFC 6749 token responses with an omitted scope and case-insensitive bearer type', () => {
142
- expect(
143
- OAuthTokenResponseSchema.safeParse({
144
- access_token: 'token',
145
- token_type: 'bearer',
146
- expires_in: 3600,
147
- }).success,
148
- ).toBe(true);
149
- });
150
-
151
- it('accepts OAuth token response extensions without widening the inferred contract', () => {
152
- const result = OAuthTokenResponseSchema.safeParse({
153
- access_token: 'token',
154
- token_type: 'Bearer',
155
- expires_in: 3600,
156
- scope: 'openid',
157
- extension_parameter: true,
158
- });
159
-
160
- expect(result.success).toBe(true);
161
- if (result.success) {
162
- expect(result.data).not.toHaveProperty('extension_parameter');
163
- }
164
- });
165
- });
166
-
167
- /**
168
- * A component's `status` enum, as the registry emits it.
169
- *
170
- * Read off the component rather than off a generated document: generating one needs the scanner,
171
- * which is a package this one does not depend on. The expansion of a query component into parameters
172
- * inlines exactly this, and has its own tests in `parameters.test.ts`.
173
- */
174
- function statusEnumOf(component: 'ListOAuthGrantsQuery' | 'BulkRevokeOAuthGrantsPayload'): string[] {
175
- const emitted = ApiSchemaComponents[component] as { properties?: Record<string, { enum?: unknown }> } | undefined;
176
- const status = emitted?.properties?.status;
177
- if (!Array.isArray(status?.enum)) {
178
- throw new Error(`${component} does not publish status as an inline enum`);
179
- }
180
- return status.enum as string[];
181
- }
@@ -1,402 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { type JsonObject, toOpenApiComponents } from './adapter.js';
3
- import { normalizeParameters } from './parameters.js';
4
- import { ApiSchemaComponents, normalizeApiParameters, validateApiRequest } from './registry.js';
5
-
6
- /**
7
- * Fixtures are LOCAL rather than added to the registry, on purpose. `ApiSchemaComponents` is what the
8
- * OpenAPI document publishes, so registering a component to exercise a coercion rule would put it in
9
- * the spec. Everything below drives the pure normalizer against hand-built components; the two tests
10
- * at the bottom then bind to the real registry to prove the published component is the one used.
11
- */
12
- const COMPONENTS: Record<string, JsonObject> = toOpenApiComponents(
13
- {
14
- Params: {
15
- type: 'object',
16
- properties: {
17
- name: { type: 'string' },
18
- limit: { type: 'integer' },
19
- ratio: { type: 'number' },
20
- enabled: { type: 'boolean' },
21
- level: { type: 'string', enum: ['account', 'project'] },
22
- tags: { type: 'array', items: { type: 'string' } },
23
- ids: { type: 'array', items: { type: 'integer' } },
24
- // What zod emits for `z.union([z.string(), z.array(z.string())])`, and what the scanner
25
- // publishes as a single `type: array` parameter with `explode: true`.
26
- single_or_many: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }] },
27
- statuses: { type: 'array', items: { $ref: '#/components/schemas/Status' } },
28
- // The same union shape as `single_or_many`, over an enum: what `ListTasksQuery`
29
- // declares for `status`.
30
- status_or_many: {
31
- anyOf: [
32
- { $ref: '#/components/schemas/Status' },
33
- { type: 'array', items: { $ref: '#/components/schemas/Status' } },
34
- ],
35
- },
36
- // An enum with a comma inside a member, which is what makes splitting ambiguous.
37
- separators: { type: 'array', items: { type: 'string', enum: ['a,b', 'c'] } },
38
- nullable_count: { anyOf: [{ type: 'integer' }, { type: 'null' }] },
39
- listed_type: { type: ['integer', 'null'] },
40
- freeform: {},
41
- status: { $ref: '#/components/schemas/Status' },
42
- },
43
- required: ['name'],
44
- },
45
- Status: { type: 'string', enum: ['on', 'off'] },
46
- Headers: {
47
- type: 'object',
48
- properties: { 'X-Request-Id': { type: 'string' }, 'x-retry-count': { type: 'integer' } },
49
- },
50
- NotAnObject: { type: 'string' },
51
- },
52
- {},
53
- );
54
-
55
- function normalize(raw: Record<string, string | string[] | undefined>, component = 'Params') {
56
- return normalizeParameters(component, raw, 'query', COMPONENTS[component], COMPONENTS);
57
- }
58
-
59
- describe('required and optional values', () => {
60
- it('omits a parameter that did not arrive, leaving requiredness to the component', () => {
61
- // The distinction that matters: omitted, not present-as-undefined. Validating `undefined`
62
- // against `type: string` would report a type error where the contract says "absent".
63
- expect(normalize({ name: 'acme' }).value).toEqual({ name: 'acme' });
64
- expect('limit' in normalize({ name: 'acme' }).value).toBe(false);
65
- });
66
-
67
- it('treats an explicitly undefined transport value as absence too', () => {
68
- // `ctx.headers` exposes this shape for a header that was not sent.
69
- expect(normalize({ name: 'acme', limit: undefined }).value).toEqual({ name: 'acme' });
70
- });
71
-
72
- it('produces an empty object when every declared parameter is optional and none arrived', () => {
73
- expect(normalizeParameters('Headers', {}, 'query', COMPONENTS.Headers, COMPONENTS).value).toEqual({});
74
- });
75
-
76
- it('leaves a missing required parameter for the component to reject, rather than inventing a message', () => {
77
- // The normalizer never rejects: it omits `name` and returns. That the omission is an error is
78
- // the component's own statement, which keeps one contract in charge of requiredness.
79
- expect(() => normalize({ limit: '5' })).not.toThrow();
80
- expect(normalize({ limit: '5' }).value).toEqual({ limit: 5 });
81
- });
82
-
83
- it('keeps an empty string as a value for a string parameter', () => {
84
- // `?name=` is a present, empty value. A `type: string` can hold it, so it passes through and
85
- // the component decides — inventing absence here would let the server read a URL the schema
86
- // does not describe.
87
- expect(normalize({ name: '' }).value).toEqual({ name: '' });
88
- });
89
- });
90
-
91
- describe('scalar coercion', () => {
92
- it.each([
93
- ['integer', 'limit', '25', 25],
94
- ['integer with exponent', 'limit', '1e3', 1000],
95
- ['integer, zero-padded', 'limit', '007', 7],
96
- ['negative integer', 'limit', '-3', -3],
97
- ['number', 'ratio', '1.5', 1.5],
98
- ['number, leading dot', 'ratio', '.5', 0.5],
99
- ['number, explicit plus', 'ratio', '+2', 2],
100
- ['boolean true', 'enabled', 'true', true],
101
- ['boolean false', 'enabled', 'false', false],
102
- ['boolean spelled as 1', 'enabled', '1', true],
103
- ['boolean spelled as 0', 'enabled', '0', false],
104
- ['string is untouched', 'name', '25', '25'],
105
- ['enum is untouched', 'level', 'account', 'account'],
106
- ['a schema with no type is untouched', 'freeform', 'anything', 'anything'],
107
- ])('coerces %s', (_label, key, raw, expected) => {
108
- expect(normalize({ [key]: raw }).value[key]).toBe(expected);
109
- });
110
-
111
- it('coerces through a $ref to a hoisted enum', () => {
112
- // Reached through the adapter's own reference resolution, so what is coerced towards and what
113
- // the spec publishes are read off the same node.
114
- expect(normalize({ status: 'on' }).value.status).toBe('on');
115
- });
116
-
117
- it.each([
118
- ['a nullable integer union', 'nullable_count'],
119
- ['a type list containing null', 'listed_type'],
120
- ])('coerces %s as its non-null type', (_label, key) => {
121
- expect(normalize({ [key]: '4' }).value[key]).toBe(4);
122
- });
123
- });
124
-
125
- describe('invalid coercion is left for the component to report', () => {
126
- it.each([
127
- ['non-numeric text', 'limit', 'abc'],
128
- ['an empty value for a number', 'limit', ''],
129
- ['whitespace around a number', 'limit', ' 1'],
130
- ['hex', 'limit', '0x10'],
131
- ['Infinity', 'ratio', 'Infinity'],
132
- ['NaN', 'ratio', 'NaN'],
133
- ['a trailing exponent', 'limit', '1e'],
134
- ['a boolean spelled as yes', 'enabled', 'yes'],
135
- ['an empty value for a boolean', 'enabled', ''],
136
- ['a capitalised boolean', 'enabled', 'True'],
137
- ])('leaves %s as the original text', (_label, key, raw) => {
138
- const { value } = normalize({ name: 'acme', [key]: raw });
139
- // Left as text rather than coerced to NaN or a guess: the component then reports a type error
140
- // naming this parameter, which is a better message than anything invented here.
141
- expect(value[key]).toBe(raw);
142
- });
143
-
144
- it('does not truncate a non-integral value for an integer parameter', () => {
145
- // Coerced to a number and handed on, so `type: integer` rejects it. `parseInt` would have
146
- // silently produced 1 and accepted a request the document says is invalid.
147
- expect(normalize({ name: 'a', limit: '1.5' }).value.limit).toBe(1.5);
148
- });
149
- });
150
-
151
- describe('arrays and repeated values', () => {
152
- it('wraps a single occurrence, matching the published explode: true', () => {
153
- expect(normalize({ name: 'a', tags: 'x' }).value.tags).toEqual(['x']);
154
- });
155
-
156
- it('collects repeated occurrences in arrival order', () => {
157
- expect(normalize({ name: 'a', tags: ['x', 'y'] }).value.tags).toEqual(['x', 'y']);
158
- });
159
-
160
- it('coerces each item', () => {
161
- expect(normalize({ name: 'a', ids: ['1', '2'] }).value.ids).toEqual([1, 2]);
162
- });
163
-
164
- it('leaves an uncoercible item in place rather than dropping it', () => {
165
- // Dropping would silently shorten the array; the component reports the bad item by index.
166
- expect(normalize({ name: 'a', ids: ['1', 'x'] }).value.ids).toEqual([1, 'x']);
167
- });
168
-
169
- it('reads a string-or-array union as an array, the shape the spec publishes', () => {
170
- // The scanner collapses this union into one `type: array` parameter with `explode: true`, so
171
- // reading the scalar variant would hand the handler a bare string where the spec promised an
172
- // array.
173
- expect(normalize({ name: 'a', single_or_many: 'x' }).value.single_or_many).toEqual(['x']);
174
- expect(normalize({ name: 'a', single_or_many: ['x', 'y'] }).value.single_or_many).toEqual(['x', 'y']);
175
- });
176
-
177
- it('hands repeated occurrences of a single-valued parameter on as an array, so the component rejects them', () => {
178
- // `?level=account&level=project` is ambiguous and nothing published says which wins. Passing the
179
- // array through makes the component report a type error instead of the server silently picking
180
- // one — which is what several hand-written handlers do today.
181
- expect(normalize({ name: 'a', level: ['account', 'project'] }).value.level).toEqual(['account', 'project']);
182
- });
183
- });
184
-
185
- /**
186
- * Comma-splitting applies to every array parameter, not only to enums.
187
- *
188
- * `explode: true` — what the document publishes — describes repeated keys and says nothing about
189
- * commas, so this is something the server accepts BEYOND what it promises. It does so because
190
- * `@vertesia/client` joins every array parameter with `,`, which means every SDK in the field sends
191
- * the comma form, and because it is what all the hand-written readers this layer replaced already
192
- * did: `readArray`, `parseTagsFilter`, `parseSourcesQuery`, and the agent-run search's `sort`,
193
- * `tags` and `categories`.
194
- */
195
- describe('comma-joined values, for an array parameter', () => {
196
- it('splits a plain string array, which is what most filters are', () => {
197
- // The regression this pins: with splitting limited to enums, `@vertesia/client`'s
198
- // `sort: ['lol:sideways', 'started_at:desc']` arrived as ONE clause `lol:sideways,started_at:desc`,
199
- // so the agent-run search sorted on an unmapped field and returned an arbitrary order.
200
- expect(normalize({ name: 'a', tags: 'x,y' }).value.tags).toEqual(['x', 'y']);
201
- });
202
-
203
- it('splits one occurrence into several values', () => {
204
- expect(normalize({ name: 'a', statuses: 'on,off' }).value.statuses).toEqual(['on', 'off']);
205
- });
206
-
207
- it('splits through the single-or-many union, which is how ListTasksQuery declares status', () => {
208
- expect(normalize({ name: 'a', status_or_many: 'on,off' }).value.status_or_many).toEqual(['on', 'off']);
209
- expect(normalize({ name: 'a', status_or_many: 'on' }).value.status_or_many).toEqual(['on']);
210
- });
211
-
212
- it('composes with repetition rather than replacing it', () => {
213
- // Both spellings mean the same list, so mixing them is not a third case to decide.
214
- expect(normalize({ name: 'a', statuses: ['on,off', 'on'] }).value.statuses).toEqual(['on', 'off', 'on']);
215
- });
216
-
217
- it('leaves an unknown member in place for the enum to reject', () => {
218
- // Not dropped: the component reports `bogus` by index, so the caller learns which value was
219
- // wrong rather than getting a silently shorter filter.
220
- expect(normalize({ name: 'a', statuses: 'on,bogus' }).value.statuses).toEqual(['on', 'bogus']);
221
- });
222
-
223
- it('cannot express a value that contains a comma — the cost of the rule above', () => {
224
- // `'a,b'` is a declared member of this enum, and it is now unreachable: the caller has no way
225
- // to escape the separator. Pinned rather than fixed because it is not a new limit — every
226
- // reader this layer replaced split unconditionally — and because no published parameter has
227
- // a comma-bearing member today. It is the reason a parameter whose values may legitimately
228
- // contain commas must not be declared as an array of plain strings.
229
- expect(normalize({ name: 'a', separators: 'a,b' }).value.separators).toEqual(['a', 'b']);
230
- });
231
- });
232
-
233
- describe('undeclared parameters', () => {
234
- it('reports undeclared query keys without removing or rejecting anything', () => {
235
- const { value, undeclared } = normalize({ name: 'a', sort: 'asc', page: '2' });
236
- expect(undeclared).toEqual(['sort', 'page']);
237
- // Not in the validated copy, so the component never sees them: whether they are an error is
238
- // the endpoint's policy, not the component's.
239
- expect(value).toEqual({ name: 'a' });
240
- });
241
-
242
- it('reports nothing when every key is declared', () => {
243
- expect(normalize({ name: 'a', limit: '1' }).undeclared).toEqual([]);
244
- });
245
-
246
- it('does not report a declared key that arrived as undefined', () => {
247
- expect(normalize({ name: 'a', limit: undefined }).undeclared).toEqual([]);
248
- });
249
-
250
- it('never reports undeclared headers, however many arrive', () => {
251
- // Structural, not a policy: every request carries headers no endpoint declares, so a caller
252
- // that rejected on this list could only ever reject correct requests.
253
- const { undeclared } = normalizeParameters(
254
- 'Headers',
255
- { accept: '*/*', 'user-agent': 'curl/8', 'x-datadog-trace-id': '1', authorization: 'Bearer x' },
256
- 'header',
257
- COMPONENTS.Headers,
258
- COMPONENTS,
259
- );
260
- expect(undeclared).toEqual([]);
261
- });
262
- });
263
-
264
- describe('header name casing', () => {
265
- function headers(raw: Record<string, string | string[] | undefined>) {
266
- return normalizeParameters('Headers', raw, 'header', COMPONENTS.Headers, COMPONENTS);
267
- }
268
-
269
- it('matches a declared mixed-case name against the lowercased name Node delivers', () => {
270
- // HTTP field names are case-insensitive and Node has already lowercased them, so a component
271
- // spelling `X-Request-Id` would never match a real request without this.
272
- expect(headers({ 'x-request-id': 'abc' }).value).toEqual({ 'X-Request-Id': 'abc' });
273
- });
274
-
275
- it('keys the copy by the declared spelling, which is what the component validates', () => {
276
- expect(Object.keys(headers({ 'x-request-id': 'abc' }).value)).toEqual(['X-Request-Id']);
277
- });
278
-
279
- it('coerces a lowercase-declared header too', () => {
280
- expect(headers({ 'x-retry-count': '2' }).value['x-retry-count']).toBe(2);
281
- });
282
-
283
- it('does not case-fold query names, where case is significant', () => {
284
- // Query parameter names are octet-exact; folding them would make `?Name=` and `?name=` the
285
- // same parameter, which no part of the document says.
286
- const { value, undeclared } = normalize({ Name: 'a' });
287
- expect(value).toEqual({});
288
- expect(undeclared).toEqual(['Name']);
289
- });
290
- });
291
-
292
- describe('the request is never mutated', () => {
293
- it('leaves the raw object untouched, including values it coerced', () => {
294
- // Koa memoizes `ctx.query` per query string and hands the same object to every later reader,
295
- // so coercing in place would retype values for middleware that never opted in.
296
- const raw = { name: 'acme', limit: '25', enabled: 'true', tags: 'x' };
297
- const snapshot = structuredClone(raw);
298
- const { value } = normalize(raw);
299
- expect(raw).toEqual(snapshot);
300
- expect(value).not.toBe(raw);
301
- expect(value.limit).toBe(25);
302
- });
303
-
304
- it('does not alias an array value into the copy', () => {
305
- const tags = ['x', 'y'];
306
- const { value } = normalize({ name: 'a', tags });
307
- expect(value.tags).not.toBe(tags);
308
- expect(tags).toEqual(['x', 'y']);
309
- });
310
- });
311
-
312
- describe('declaration errors', () => {
313
- it('refuses a component that is not an object schema', () => {
314
- // Cannot be expanded into named parameters, and the scanner would not publish one either.
315
- // Failing is the only honest option: reaching the handler with an unchecked query while the
316
- // spec advertises a checked one is the drift this whole arrangement exists to prevent.
317
- expect(() => normalize({}, 'NotAnObject')).toThrow(/not an object schema with properties/);
318
- });
319
-
320
- it('refuses a component that does not exist', () => {
321
- expect(() => normalizeParameters('Missing', {}, 'query', undefined, COMPONENTS)).toThrow(
322
- /not an object schema with properties/,
323
- );
324
- });
325
- });
326
-
327
- describe('inherited members are not read as parameters', () => {
328
- it('ignores a prototype member with a declared name', () => {
329
- // A `hasOwn` check rather than a truthiness test: without it a component declaring `toString`
330
- // would receive a function.
331
- const components: Record<string, JsonObject> = {
332
- Proto: { type: 'object', properties: { toString: { type: 'string' } } },
333
- };
334
- expect(normalizeParameters('Proto', {}, 'query', components.Proto, components).value).toEqual({});
335
- });
336
- });
337
-
338
- describe('bound to the published registry', () => {
339
- it('normalizes against the same component object the document publishes', () => {
340
- // The property this slice exists for: one declaration drives the published parameter and the
341
- // runtime check. `ApiKeyListQuery` is the component `ListApiKeys` declares.
342
- expect(normalizeApiParameters('ApiKeyListQuery', { level: 'account' }, 'query').value).toEqual({
343
- level: 'account',
344
- });
345
- expect(ApiSchemaComponents.ApiKeyListQuery.properties).toMatchObject({
346
- level: { enum: ['account', 'project'] },
347
- });
348
- });
349
-
350
- it('produces a copy the registry validator accepts, and rejects a value outside the enum', () => {
351
- // End to end through the real validators, which is what the enforcer runs.
352
- const good = normalizeApiParameters('ApiKeyListQuery', { level: 'project' }, 'query');
353
- expect(validateApiRequest('ApiKeyListQuery', good.value)).toMatchObject({ valid: true });
354
-
355
- const bad = normalizeApiParameters('ApiKeyListQuery', { level: 'bogus' }, 'query');
356
- expect(validateApiRequest('ApiKeyListQuery', bad.value)).toMatchObject({ valid: false });
357
- });
358
-
359
- it('accepts an absent optional parameter', () => {
360
- const empty = normalizeApiParameters('ApiKeyListQuery', {}, 'query');
361
- expect(empty.value).toEqual({});
362
- expect(validateApiRequest('ApiKeyListQuery', empty.value)).toMatchObject({ valid: true });
363
- });
364
-
365
- /**
366
- * `GET /tasks` is the endpoint the enum rule exists for. `TaskApi.list` builds its status
367
- * parameter as `query.status.join(',')`, so every SDK in the field sends the comma form; these
368
- * assertions are what stops enforcement from turning our own client's multi-status listing into
369
- * a 400.
370
- */
371
- it('accepts every spelling of the ListTasksQuery status parameter', () => {
372
- for (const raw of ['pending,completed', ['pending', 'completed'], ['pending,completed']]) {
373
- const { value } = normalizeApiParameters('ListTasksQuery', { status: raw }, 'query');
374
- expect(value.status).toEqual(['pending', 'completed']);
375
- expect(validateApiRequest('ListTasksQuery', value)).toMatchObject({ valid: true });
376
- }
377
- });
378
-
379
- it('coerces the ListTasksQuery paging parameters, and rejects text that is not a number', () => {
380
- const good = normalizeApiParameters('ListTasksQuery', { limit: '25', offset: '50' }, 'query');
381
- expect(good.value).toEqual({ limit: 25, offset: 50 });
382
- expect(validateApiRequest('ListTasksQuery', good.value)).toMatchObject({ valid: true });
383
-
384
- // `parseInt('abc')` reached Mongo as `NaN` before this, which disabled the page size cap.
385
- const bad = normalizeApiParameters('ListTasksQuery', { limit: 'abc' }, 'query');
386
- expect(validateApiRequest('ListTasksQuery', bad.value)).toMatchObject({ valid: false });
387
- });
388
-
389
- it('rejects a status outside the published enum', () => {
390
- const { value } = normalizeApiParameters('ListTasksQuery', { status: 'pending,bogus' }, 'query');
391
- expect(validateApiRequest('ListTasksQuery', value)).toMatchObject({ valid: false });
392
- });
393
-
394
- it('leaves an undeclared ListTasksQuery parameter out of the validated copy rather than rejecting', () => {
395
- // `GET /tasks` does not set `rejectUndeclaredQuery`, so a stale `?sort=` keeps working. The
396
- // undeclared list is reported for the endpoint to act on; the component never sees the key.
397
- const { value, undeclared } = normalizeApiParameters('ListTasksQuery', { sort: 'asc' }, 'query');
398
- expect(undeclared).toEqual(['sort']);
399
- expect(value).toEqual({});
400
- expect(validateApiRequest('ListTasksQuery', value)).toMatchObject({ valid: true });
401
- });
402
- });