@canonical/pragma-cli 0.38.0 → 0.40.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 (330) hide show
  1. package/README.md +1 -1
  2. package/dist/package.json +14 -14
  3. package/dist/pragma.conf.js +273 -176
  4. package/dist/pragma.conf.js.map +1 -1
  5. package/dist/src/capabilities/capabilities/capabilities.verb.js +2 -0
  6. package/dist/src/capabilities/capabilities/capabilities.verb.js.map +1 -1
  7. package/dist/src/capabilities/capabilities/catalog.js +47 -51
  8. package/dist/src/capabilities/capabilities/catalog.js.map +1 -1
  9. package/dist/src/capabilities/capabilities/index.js +2 -0
  10. package/dist/src/capabilities/capabilities/index.js.map +1 -1
  11. package/dist/src/capabilities/capabilities/types.js +5 -5
  12. package/dist/src/capabilities/colophon/colophon.render.js +7 -4
  13. package/dist/src/capabilities/colophon/colophon.render.js.map +1 -1
  14. package/dist/src/capabilities/colophon/colophon.verb.js +1 -0
  15. package/dist/src/capabilities/colophon/colophon.verb.js.map +1 -1
  16. package/dist/src/capabilities/config/get.verb.js +2 -0
  17. package/dist/src/capabilities/config/get.verb.js.map +1 -1
  18. package/dist/src/capabilities/config/runField.js +2 -2
  19. package/dist/src/capabilities/config/runField.js.map +1 -1
  20. package/dist/src/capabilities/config/set.verb.js +2 -0
  21. package/dist/src/capabilities/config/set.verb.js.map +1 -1
  22. package/dist/src/capabilities/config/show.verb.js +1 -0
  23. package/dist/src/capabilities/config/show.verb.js.map +1 -1
  24. package/dist/src/capabilities/config/unset.verb.js +2 -0
  25. package/dist/src/capabilities/config/unset.verb.js.map +1 -1
  26. package/dist/src/capabilities/create/constants.js +2 -2
  27. package/dist/src/capabilities/create/create.verb.js +14 -4
  28. package/dist/src/capabilities/create/create.verb.js.map +1 -1
  29. package/dist/src/capabilities/distribution.js +3 -1
  30. package/dist/src/capabilities/distribution.js.map +1 -1
  31. package/dist/src/capabilities/doctor/checks/checkPackageRefs.js +10 -2
  32. package/dist/src/capabilities/doctor/checks/checkPackageRefs.js.map +1 -1
  33. package/dist/src/capabilities/doctor/doctor.verb.js +2 -0
  34. package/dist/src/capabilities/doctor/doctor.verb.js.map +1 -1
  35. package/dist/src/capabilities/graph/connect.verb.js +2 -0
  36. package/dist/src/capabilities/graph/connect.verb.js.map +1 -1
  37. package/dist/src/capabilities/graph/inspect.render.js +7 -1
  38. package/dist/src/capabilities/graph/inspect.render.js.map +1 -1
  39. package/dist/src/capabilities/graph/inspect.verb.js +2 -0
  40. package/dist/src/capabilities/graph/inspect.verb.js.map +1 -1
  41. package/dist/src/capabilities/graph/query.render.js +37 -5
  42. package/dist/src/capabilities/graph/query.render.js.map +1 -1
  43. package/dist/src/capabilities/graph/query.verb.js +2 -0
  44. package/dist/src/capabilities/graph/query.verb.js.map +1 -1
  45. package/dist/src/capabilities/graph/queryText.js +28 -0
  46. package/dist/src/capabilities/graph/queryText.js.map +1 -1
  47. package/dist/src/capabilities/graph/runConnect.js +3 -3
  48. package/dist/src/capabilities/graph/runConnect.js.map +1 -1
  49. package/dist/src/capabilities/graph/runQuery.js +8 -6
  50. package/dist/src/capabilities/graph/runQuery.js.map +1 -1
  51. package/dist/src/capabilities/info/info.verb.js +2 -0
  52. package/dist/src/capabilities/info/info.verb.js.map +1 -1
  53. package/dist/src/capabilities/meta/mcp.verb.js +1 -0
  54. package/dist/src/capabilities/meta/mcp.verb.js.map +1 -1
  55. package/dist/src/capabilities/ontology/verbs.js +7 -10
  56. package/dist/src/capabilities/ontology/verbs.js.map +1 -1
  57. package/dist/src/capabilities/prompt/verbs.js +5 -4
  58. package/dist/src/capabilities/prompt/verbs.js.map +1 -1
  59. package/dist/src/capabilities/resources/provider.js +6 -3
  60. package/dist/src/capabilities/resources/provider.js.map +1 -1
  61. package/dist/src/capabilities/setup/setup.verb.js +13 -5
  62. package/dist/src/capabilities/setup/setup.verb.js.map +1 -1
  63. package/dist/src/capabilities/shared/calls.js +16 -0
  64. package/dist/src/capabilities/shared/calls.js.map +1 -0
  65. package/dist/src/capabilities/skill/render.js +7 -5
  66. package/dist/src/capabilities/skill/render.js.map +1 -1
  67. package/dist/src/capabilities/skill/verbs.js +5 -4
  68. package/dist/src/capabilities/skill/verbs.js.map +1 -1
  69. package/dist/src/capabilities/sources/collectStatus.js +6 -1
  70. package/dist/src/capabilities/sources/collectStatus.js.map +1 -1
  71. package/dist/src/capabilities/sources/index.js +4 -3
  72. package/dist/src/capabilities/sources/index.js.map +1 -1
  73. package/dist/src/capabilities/sources/reset.render.js +37 -0
  74. package/dist/src/capabilities/sources/reset.render.js.map +1 -0
  75. package/dist/src/capabilities/sources/reset.verb.js +51 -0
  76. package/dist/src/capabilities/sources/reset.verb.js.map +1 -0
  77. package/dist/src/capabilities/sources/runReset.js +59 -0
  78. package/dist/src/capabilities/sources/runReset.js.map +1 -0
  79. package/dist/src/capabilities/sources/runUpdate.js +7 -6
  80. package/dist/src/capabilities/sources/runUpdate.js.map +1 -1
  81. package/dist/src/capabilities/sources/status.render.js +24 -8
  82. package/dist/src/capabilities/sources/status.render.js.map +1 -1
  83. package/dist/src/capabilities/sources/status.verb.js +1 -0
  84. package/dist/src/capabilities/sources/status.verb.js.map +1 -1
  85. package/dist/src/capabilities/sources/update.verb.js +2 -0
  86. package/dist/src/capabilities/sources/update.verb.js.map +1 -1
  87. package/dist/src/capabilities/upgrade/upgrade.verb.js +1 -0
  88. package/dist/src/capabilities/upgrade/upgrade.verb.js.map +1 -1
  89. package/dist/src/capabilities/version/version.verb.js +1 -0
  90. package/dist/src/capabilities/version/version.verb.js.map +1 -1
  91. package/dist/src/constants.js +4 -3
  92. package/dist/src/constants.js.map +1 -1
  93. package/dist/src/kernel/error/index.js +1 -1
  94. package/dist/src/kernel/error/index.js.map +1 -1
  95. package/dist/src/kernel/error/recovery.js +24 -42
  96. package/dist/src/kernel/error/recovery.js.map +1 -1
  97. package/dist/src/kernel/packs/collect.js +123 -9
  98. package/dist/src/kernel/packs/collect.js.map +1 -1
  99. package/dist/src/kernel/packs/compile.js +74 -15
  100. package/dist/src/kernel/packs/compile.js.map +1 -1
  101. package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js +3 -3
  102. package/dist/src/kernel/packs/graphql/fetchGraphqlLookup.js.map +1 -1
  103. package/dist/src/kernel/packs/iri.js +2 -4
  104. package/dist/src/kernel/packs/iri.js.map +1 -1
  105. package/dist/src/kernel/packs/renderPack.js +90 -50
  106. package/dist/src/kernel/packs/renderPack.js.map +1 -1
  107. package/dist/src/kernel/packs/resolveEntity.js +202 -90
  108. package/dist/src/kernel/packs/resolveEntity.js.map +1 -1
  109. package/dist/src/kernel/packs/runBodies.js +89 -30
  110. package/dist/src/kernel/packs/runBodies.js.map +1 -1
  111. package/dist/src/kernel/packs/schema.js +66 -12
  112. package/dist/src/kernel/packs/schema.js.map +1 -1
  113. package/dist/src/kernel/packs/sparql/buildListQuery.js +80 -13
  114. package/dist/src/kernel/packs/sparql/buildListQuery.js.map +1 -1
  115. package/dist/src/kernel/packs/sparql/buildLookupQuery.js +41 -13
  116. package/dist/src/kernel/packs/sparql/buildLookupQuery.js.map +1 -1
  117. package/dist/src/kernel/packs/sparql/filterValues.js +32 -23
  118. package/dist/src/kernel/packs/sparql/filterValues.js.map +1 -1
  119. package/dist/src/kernel/packs/sparql/runSelect.js +3 -5
  120. package/dist/src/kernel/packs/sparql/runSelect.js.map +1 -1
  121. package/dist/src/kernel/packs/storyRules.js +31 -4
  122. package/dist/src/kernel/packs/storyRules.js.map +1 -1
  123. package/dist/src/kernel/packs/tierScope.js +3 -3
  124. package/dist/src/kernel/packs/tierScope.js.map +1 -1
  125. package/dist/src/kernel/packs/types.js +4 -0
  126. package/dist/src/kernel/packs/types.js.map +1 -1
  127. package/dist/src/kernel/project/cli/buildProgram.js +4 -0
  128. package/dist/src/kernel/project/cli/buildProgram.js.map +1 -1
  129. package/dist/src/kernel/project/cli/dispatch.js +32 -8
  130. package/dist/src/kernel/project/cli/dispatch.js.map +1 -1
  131. package/dist/src/kernel/project/cli/suggestNames.js +44 -1
  132. package/dist/src/kernel/project/cli/suggestNames.js.map +1 -1
  133. package/dist/src/kernel/project/cli/verbHelp.js +17 -18
  134. package/dist/src/kernel/project/cli/verbHelp.js.map +1 -1
  135. package/dist/src/kernel/project/mcp/buildServer.js +11 -4
  136. package/dist/src/kernel/project/mcp/buildServer.js.map +1 -1
  137. package/dist/src/kernel/project/mcp/envelope.js +3 -2
  138. package/dist/src/kernel/project/mcp/envelope.js.map +1 -1
  139. package/dist/src/kernel/project/mcp/instructions.js +101 -31
  140. package/dist/src/kernel/project/mcp/instructions.js.map +1 -1
  141. package/dist/src/kernel/project/mcp/registerVerb.js +59 -23
  142. package/dist/src/kernel/project/mcp/registerVerb.js.map +1 -1
  143. package/dist/src/kernel/render/renderers.js +3 -3
  144. package/dist/src/kernel/render/turtle.js +21 -4
  145. package/dist/src/kernel/render/turtle.js.map +1 -1
  146. package/dist/src/kernel/runtime/graphpack/build.js +54 -1
  147. package/dist/src/kernel/runtime/graphpack/build.js.map +1 -1
  148. package/dist/src/kernel/runtime/graphpack/manifest.js +14 -0
  149. package/dist/src/kernel/runtime/graphpack/manifest.js.map +1 -1
  150. package/dist/src/kernel/runtime/graphpack/read.js +3 -5
  151. package/dist/src/kernel/runtime/graphpack/read.js.map +1 -1
  152. package/dist/src/kernel/runtime/graphpack/schemas.js +1 -0
  153. package/dist/src/kernel/runtime/graphpack/schemas.js.map +1 -1
  154. package/dist/src/kernel/runtime/loadSession.js +8 -1
  155. package/dist/src/kernel/runtime/loadSession.js.map +1 -1
  156. package/dist/src/kernel/runtime/packVersion.js +75 -0
  157. package/dist/src/kernel/runtime/packVersion.js.map +1 -0
  158. package/dist/src/kernel/runtime/readEntity.js +43 -4
  159. package/dist/src/kernel/runtime/readEntity.js.map +1 -1
  160. package/dist/src/kernel/runtime/resolveSources.js +61 -9
  161. package/dist/src/kernel/runtime/resolveSources.js.map +1 -1
  162. package/dist/src/kernel/runtime/storeReadiness.js +3 -2
  163. package/dist/src/kernel/runtime/storeReadiness.js.map +1 -1
  164. package/dist/src/kernel/spec/call.js +167 -0
  165. package/dist/src/kernel/spec/call.js.map +1 -0
  166. package/dist/src/kernel/spec/emitReference.js +9 -13
  167. package/dist/src/kernel/spec/emitReference.js.map +1 -1
  168. package/dist/src/kernel/spec/emitSurface.js +8 -1
  169. package/dist/src/kernel/spec/emitSurface.js.map +1 -1
  170. package/dist/src/kernel/spec/guidance.js +63 -0
  171. package/dist/src/kernel/spec/guidance.js.map +1 -0
  172. package/dist/src/kernel/spec/wireType.js +36 -0
  173. package/dist/src/kernel/spec/wireType.js.map +1 -0
  174. package/package.json +14 -14
  175. package/pragma.conf.ts +294 -177
  176. package/src/capabilities/block.parity.test.ts +1 -1
  177. package/src/capabilities/callRule.test.ts +147 -0
  178. package/src/capabilities/capabilities/capabilities.test.ts +47 -19
  179. package/src/capabilities/capabilities/capabilities.verb.ts +2 -0
  180. package/src/capabilities/capabilities/catalog.ts +60 -55
  181. package/src/capabilities/capabilities/index.ts +2 -0
  182. package/src/capabilities/capabilities/types.ts +16 -19
  183. package/src/capabilities/colophon/colophon.render.ts +9 -5
  184. package/src/capabilities/colophon/colophon.verb.ts +1 -0
  185. package/src/capabilities/config/get.verb.ts +2 -0
  186. package/src/capabilities/config/runField.ts +3 -3
  187. package/src/capabilities/config/set.verb.ts +3 -0
  188. package/src/capabilities/config/show.verb.ts +2 -0
  189. package/src/capabilities/config/unset.verb.ts +2 -0
  190. package/src/capabilities/create/constants.ts +2 -2
  191. package/src/capabilities/create/create.verb.ts +16 -0
  192. package/src/capabilities/create/createGrammar.test.ts +1 -1
  193. package/src/capabilities/distribution.test.ts +6 -3
  194. package/src/capabilities/distribution.ts +6 -0
  195. package/src/capabilities/doctor/checks/checkPackageRefs.ts +14 -2
  196. package/src/capabilities/doctor/doctor.test.ts +38 -1
  197. package/src/capabilities/doctor/doctor.verb.ts +2 -0
  198. package/src/capabilities/emptyRecovery.test.ts +5 -0
  199. package/src/capabilities/graph/connect.verb.ts +2 -0
  200. package/src/capabilities/graph/inspect.render.ts +9 -1
  201. package/src/capabilities/graph/inspect.test.ts +73 -0
  202. package/src/capabilities/graph/inspect.verb.ts +2 -0
  203. package/src/capabilities/graph/query.render.test.ts +35 -0
  204. package/src/capabilities/graph/query.render.ts +45 -7
  205. package/src/capabilities/graph/query.shipped.exec.test.ts +44 -0
  206. package/src/capabilities/graph/query.verb.ts +2 -0
  207. package/src/capabilities/graph/queryText.test.ts +55 -1
  208. package/src/capabilities/graph/queryText.ts +47 -0
  209. package/src/capabilities/graph/runConnect.ts +4 -5
  210. package/src/capabilities/graph/runQuery.ts +23 -9
  211. package/src/capabilities/info/info.verb.ts +2 -0
  212. package/src/capabilities/links.shipped.exec.test.ts +398 -0
  213. package/src/capabilities/listBudget.shipped.exec.test.ts +2 -2
  214. package/src/capabilities/listLookup.shipped.exec.test.ts +122 -13
  215. package/src/capabilities/listQuery.shipped.exec.test.ts +39 -16
  216. package/src/capabilities/lookupBudget.shipped.exec.test.ts +208 -0
  217. package/src/capabilities/meta/mcp.verb.ts +2 -0
  218. package/src/capabilities/ontology/ontology.test.ts +24 -0
  219. package/src/capabilities/ontology/verbs.ts +16 -12
  220. package/src/capabilities/prompt/verbs.ts +10 -4
  221. package/src/capabilities/resources/provider.ts +7 -3
  222. package/src/capabilities/resources/resources.test.ts +2 -1
  223. package/src/capabilities/setup/setup.verb.ts +16 -0
  224. package/src/capabilities/shared/calls.ts +20 -0
  225. package/src/capabilities/skill/render.ts +9 -6
  226. package/src/capabilities/skill/verbs.ts +8 -4
  227. package/src/capabilities/sources/collectStatus.ts +6 -1
  228. package/src/capabilities/sources/index.ts +4 -3
  229. package/src/capabilities/sources/reset.render.test.ts +33 -0
  230. package/src/capabilities/sources/reset.render.ts +43 -0
  231. package/src/capabilities/sources/reset.verb.ts +59 -0
  232. package/src/capabilities/sources/runReset.ts +65 -0
  233. package/src/capabilities/sources/runUpdate.ts +12 -11
  234. package/src/capabilities/sources/sources.test.ts +158 -3
  235. package/src/capabilities/sources/status.render.test.ts +63 -1
  236. package/src/capabilities/sources/status.render.ts +25 -8
  237. package/src/capabilities/sources/status.verb.ts +2 -0
  238. package/src/capabilities/sources/types.ts +26 -0
  239. package/src/capabilities/sources/update.verb.ts +3 -0
  240. package/src/capabilities/surface.test.ts +2 -2
  241. package/src/capabilities/token.parity.test.ts +3 -2
  242. package/src/capabilities/tokenGraph.shipped.exec.test.ts +14 -2
  243. package/src/capabilities/upgrade/upgrade.verb.ts +1 -0
  244. package/src/capabilities/variable.parity.test.ts +2 -1
  245. package/src/capabilities/version/version.verb.ts +1 -0
  246. package/src/capabilities/wireContract.test.ts +110 -0
  247. package/src/constants.ts +4 -3
  248. package/src/kernel/copy.test.ts +1 -1
  249. package/src/kernel/error/PragmaError.test.ts +7 -3
  250. package/src/kernel/error/index.ts +1 -1
  251. package/src/kernel/error/recovery.test.ts +52 -18
  252. package/src/kernel/error/recovery.ts +25 -46
  253. package/src/kernel/error/types.ts +1 -1
  254. package/src/kernel/packs/collect.test.ts +79 -0
  255. package/src/kernel/packs/collect.ts +151 -11
  256. package/src/kernel/packs/compile.test.ts +107 -10
  257. package/src/kernel/packs/compile.ts +100 -11
  258. package/src/kernel/packs/filteredEmpty.test.ts +22 -5
  259. package/src/kernel/packs/graphql/fetchGraphqlLookup.ts +5 -6
  260. package/src/kernel/packs/graphqlSource.test.ts +42 -0
  261. package/src/kernel/packs/iri.ts +5 -4
  262. package/src/kernel/packs/listNotice.test.ts +1 -1
  263. package/src/kernel/packs/nounLink.test.ts +385 -0
  264. package/src/kernel/packs/renderPack.ts +137 -57
  265. package/src/kernel/packs/resolveEntity.test.ts +170 -1
  266. package/src/kernel/packs/resolveEntity.ts +281 -101
  267. package/src/kernel/packs/runBodies.ts +133 -31
  268. package/src/kernel/packs/schema.ts +77 -12
  269. package/src/kernel/packs/sparql/buildListQuery.test.ts +78 -1
  270. package/src/kernel/packs/sparql/buildListQuery.ts +106 -16
  271. package/src/kernel/packs/sparql/buildLookupQuery.ts +44 -13
  272. package/src/kernel/packs/sparql/filterValues.test.ts +36 -4
  273. package/src/kernel/packs/sparql/filterValues.ts +50 -31
  274. package/src/kernel/packs/sparql/runSelect.ts +4 -6
  275. package/src/kernel/packs/storyRules.ts +37 -4
  276. package/src/kernel/packs/tierScope.test.ts +120 -6
  277. package/src/kernel/packs/tierScope.ts +5 -7
  278. package/src/kernel/packs/toolDescription.test.ts +59 -74
  279. package/src/kernel/packs/types.ts +93 -11
  280. package/src/kernel/project/cli/buildProgram.ts +4 -0
  281. package/src/kernel/project/cli/dispatch.ts +34 -8
  282. package/src/kernel/project/cli/suggest.test.ts +77 -0
  283. package/src/kernel/project/cli/suggestNames.ts +47 -1
  284. package/src/kernel/project/cli/verbHelp.ts +16 -19
  285. package/src/kernel/project/mcp/buildServer.ts +15 -4
  286. package/src/kernel/project/mcp/envelope.ts +3 -2
  287. package/src/kernel/project/mcp/instructions.ts +123 -35
  288. package/src/kernel/project/mcp/prompts/prompts.test.ts +136 -20
  289. package/src/kernel/project/mcp/registerVerb.test.ts +28 -0
  290. package/src/kernel/project/mcp/registerVerb.ts +67 -29
  291. package/src/kernel/render/__snapshots__/renderers.test.ts.snap +1 -1
  292. package/src/kernel/render/renderers.test.ts +2 -2
  293. package/src/kernel/render/renderers.ts +3 -3
  294. package/src/kernel/render/turtle.ts +24 -4
  295. package/src/kernel/runtime/graphpack/build.ts +58 -1
  296. package/src/kernel/runtime/graphpack/graphpack.test.ts +20 -0
  297. package/src/kernel/runtime/graphpack/manifest.ts +15 -0
  298. package/src/kernel/runtime/graphpack/read.ts +4 -6
  299. package/src/kernel/runtime/graphpack/schemas.ts +1 -0
  300. package/src/kernel/runtime/graphpack/types.ts +13 -0
  301. package/src/kernel/runtime/loadSession.ts +8 -1
  302. package/src/kernel/runtime/packVersion.test.ts +61 -0
  303. package/src/kernel/runtime/packVersion.ts +84 -0
  304. package/src/kernel/runtime/readEntity.ts +61 -6
  305. package/src/kernel/runtime/resolveSources.test.ts +90 -7
  306. package/src/kernel/runtime/resolveSources.ts +91 -10
  307. package/src/kernel/runtime/storeReadiness.ts +4 -4
  308. package/src/kernel/runtime/types.ts +12 -0
  309. package/src/kernel/spec/call.test.ts +95 -0
  310. package/src/kernel/spec/call.ts +186 -0
  311. package/src/kernel/spec/emitReference.ts +9 -13
  312. package/src/kernel/spec/emitSurface.ts +9 -1
  313. package/src/kernel/spec/guidance.test.ts +49 -0
  314. package/src/kernel/spec/guidance.ts +68 -0
  315. package/src/kernel/spec/index.ts +4 -0
  316. package/src/kernel/spec/surfaceConformance.test.ts +2 -2
  317. package/src/kernel/spec/types.ts +64 -7
  318. package/src/kernel/spec/wireType.ts +53 -0
  319. package/src/testing/behavioral/errorMatrix.mcp.test.ts +2 -1
  320. package/src/testing/behavioral/journeys.firstInstall.test.ts +72 -2
  321. package/src/testing/behavioral/parity.test.ts +40 -0
  322. package/src/testing/eval/__snapshots__/eval.test.ts.snap +1 -1
  323. package/src/testing/eval/cases/assembly.ts +4 -3
  324. package/src/testing/eval/cases/stable.ts +2 -3
  325. package/src/testing/helpers/callSchema.ts +40 -0
  326. package/src/testing/helpers/parity.ts +35 -2
  327. package/src/testing/setupCallChecking.ts +30 -0
  328. package/dist/src/capabilities/capabilities/hints.js +0 -223
  329. package/dist/src/capabilities/capabilities/hints.js.map +0 -1
  330. package/src/capabilities/capabilities/hints.ts +0 -266
@@ -0,0 +1,385 @@
1
+ /**
2
+ * A filter that names a noun accepts what that noun's lookup accepts, and a
3
+ * `many` expand field adds values to a row, never rows.
4
+ */
5
+
6
+ import { beforeAll, describe, expect, it } from "vitest";
7
+ import { buildFixtureRuntime } from "../../testing/helpers/packRuntime.js";
8
+ import type { ConfigLayers } from "../config/index.js";
9
+ import type { PragmaRuntime } from "../runtime/types.js";
10
+ import type { CapabilityModule, VerbSpec } from "../spec/types.js";
11
+ import { assembleEffectiveModules, screenPackageStories } from "./collect.js";
12
+ import { compilePack, compileStoryModule } from "./compile.js";
13
+ import type { LookupOutput } from "./resolveEntity.js";
14
+ import { parsePackDefinition } from "./schema.js";
15
+ import {
16
+ distributionSource,
17
+ type PackDefinition,
18
+ type PackPage,
19
+ } from "./types.js";
20
+ import { verbKey } from "./uniqueness.js";
21
+
22
+ const PREFIXES = {
23
+ ex: "https://example.org/shop#",
24
+ owl: "http://www.w3.org/2002/07/owl#",
25
+ };
26
+
27
+ const TTL = `
28
+ @prefix ex: <https://example.org/shop#> .
29
+ @prefix owl: <http://www.w3.org/2002/07/owl#> .
30
+
31
+ ex:Maker a owl:Class .
32
+ ex:Widget a owl:Class .
33
+
34
+ ex:acme a ex:Maker ; ex:name "Acme" ; ex:code "A1" .
35
+ ex:acme2 a ex:Maker ; ex:name "Acme" .
36
+ ex:bolt a ex:Maker ; ex:name "Bolt" .
37
+ ex:idle a ex:Maker ; ex:name "Idle" .
38
+
39
+ ex:button a ex:Widget ; ex:name "Button" ; ex:madeBy ex:acme ;
40
+ ex:alias "btn" , "Btn" , "push-button" .
41
+ ex:label a ex:Widget ; ex:name "Label" ; ex:madeBy ex:acme2 .
42
+ ex:slider a ex:Widget ; ex:name "Slider" ; ex:madeBy ex:bolt .
43
+
44
+ ex:bolt ex:part
45
+ [ ex:group "g" ; ex:label "f" ; ex:tag "x" , "y" ] ,
46
+ [ ex:group "g" ; ex:label "c" ; ex:tag "x" ] ,
47
+ [ ex:group "g" ; ex:label "e" ] ,
48
+ [ ex:group "g" ; ex:label "a" ; ex:tag "y" ] ,
49
+ [ ex:group "g" ; ex:label "d" ; ex:tag "x" ] ,
50
+ [ ex:group "g" ; ex:label "b" ] .
51
+ `;
52
+
53
+ const MAKER: PackDefinition = {
54
+ noun: "maker",
55
+ lookup: {
56
+ by: "ex:name",
57
+ type: "ex:Maker",
58
+ expand: [
59
+ {
60
+ name: "widgets",
61
+ kind: "table",
62
+ relation: "^ex:madeBy",
63
+ select: [
64
+ { name: "name", property: "ex:name", noun: "widget" },
65
+ { name: "aliases", property: "ex:alias", many: true },
66
+ ],
67
+ },
68
+ {
69
+ name: "parts",
70
+ kind: "table",
71
+ relation: "ex:part",
72
+ // Every part ties on the one declared key.
73
+ orderBy: ["group"],
74
+ select: [
75
+ { name: "group", property: "ex:group" },
76
+ { name: "label", property: "ex:label" },
77
+ { name: "tags", property: "ex:tag", many: true },
78
+ ],
79
+ },
80
+ ],
81
+ },
82
+ };
83
+
84
+ const WIDGET: PackDefinition = {
85
+ noun: "widget",
86
+ list: {
87
+ query: [
88
+ "SELECT ?uri ?name ?maker ?makerUri WHERE {",
89
+ " ?uri a ex:Widget ; ex:name ?name ; ex:madeBy ?makerUri .",
90
+ " ?makerUri ex:name ?maker .",
91
+ "} ORDER BY ?name",
92
+ ].join("\n"),
93
+ columns: [
94
+ { field: "name", label: "Name" },
95
+ { field: "maker", label: "Maker", noun: "maker" },
96
+ ],
97
+ filters: [
98
+ { param: "maker", variable: "maker", noun: "maker", entity: "makerUri" },
99
+ ],
100
+ },
101
+ lookup: { by: "ex:name", type: "ex:Widget" },
102
+ };
103
+
104
+ const STORIES = new Map([MAKER, WIDGET].map((story) => [story.noun, story]));
105
+
106
+ const verb = (story: PackDefinition, name: string): VerbSpec =>
107
+ compilePack(
108
+ story,
109
+ distributionSource("test:links"),
110
+ PREFIXES,
111
+ (noun) => STORIES.get(noun)?.lookup,
112
+ ).find((v) => verbKey(v.path) === `${story.noun} ${name}`) as VerbSpec;
113
+
114
+ let rt: PragmaRuntime;
115
+ beforeAll(async () => {
116
+ ({ rt } = await buildFixtureRuntime({ ttl: TTL, prefixes: PREFIXES }));
117
+ });
118
+
119
+ const widgetsBy = async (maker: unknown): Promise<PackPage["rows"]> =>
120
+ ((await verb(WIDGET, "list").run({ maker }, rt)) as PackPage).rows;
121
+
122
+ describe("a filter that names a noun", () => {
123
+ it("takes a name, and a shared name means every entity it reaches", async () => {
124
+ const rows = await widgetsBy("acme");
125
+ expect(rows.map((row) => row.name)).toEqual(["Button", "Label"]);
126
+ });
127
+
128
+ it("takes a prefixed IRI and an absolute IRI, which reach one entity", async () => {
129
+ expect((await widgetsBy("ex:acme2")).map((row) => row.name)).toEqual([
130
+ "Label",
131
+ ]);
132
+ expect(
133
+ (await widgetsBy("https://example.org/shop#bolt")).map((row) => row.name),
134
+ ).toEqual(["Slider"]);
135
+ });
136
+
137
+ it("takes several values as a union", async () => {
138
+ const rows = await widgetsBy(["Bolt", "ex:acme"]);
139
+ expect(rows.map((row) => row.name)).toEqual(["Button", "Slider"]);
140
+ });
141
+
142
+ it("leaves the entity variable out of the rows", async () => {
143
+ expect(Object.keys((await widgetsBy("Bolt"))[0] ?? {})).toEqual([
144
+ "uri",
145
+ "name",
146
+ "maker",
147
+ ]);
148
+ });
149
+
150
+ it("answers a calm empty list for an entity that exists and no row names", async () => {
151
+ expect(await widgetsBy("Idle")).toEqual([]);
152
+ });
153
+
154
+ it("refuses a value the noun's lookup cannot resolve, offering what a lookup miss offers", async () => {
155
+ await expect(widgetsBy(["Bolt", "Bolts"])).rejects.toMatchObject({
156
+ code: "INVALID_INPUT",
157
+ message: 'No maker is named "Bolts".',
158
+ suggestions: ["Bolt"],
159
+ });
160
+ });
161
+
162
+ it("refuses an empty value as empty input", async () => {
163
+ await expect(widgetsBy("")).rejects.toMatchObject({
164
+ code: "INVALID_INPUT",
165
+ message: 'Invalid maker "(empty)".',
166
+ });
167
+ });
168
+
169
+ it("is a configuration error when no story declares the noun", async () => {
170
+ const orphan = compilePack(
171
+ WIDGET,
172
+ distributionSource("test:links"),
173
+ PREFIXES,
174
+ )[0] as VerbSpec;
175
+ await expect(orphan.run({ maker: "Acme" }, rt)).rejects.toMatchObject({
176
+ code: "CONFIG_ERROR",
177
+ });
178
+ });
179
+ });
180
+
181
+ describe("a noun filter across the shipped and the project's stories", () => {
182
+ const shipped = (...stories: PackDefinition[]): CapabilityModule[] =>
183
+ stories.map((story) =>
184
+ compileStoryModule(
185
+ story,
186
+ distributionSource("test:shipped"),
187
+ PREFIXES,
188
+ (noun) => STORIES.get(noun)?.lookup,
189
+ ),
190
+ );
191
+ const project = (stories: unknown[]): ConfigLayers =>
192
+ ({
193
+ config: { stories, prefixes: PREFIXES },
194
+ origins: { packs: "default", stories: "project" },
195
+ }) as unknown as ConfigLayers;
196
+ const makerFilter = (modules: readonly CapabilityModule[], maker: string) =>
197
+ (
198
+ modules
199
+ .flatMap((module) => module.verbs)
200
+ .find((v) => verbKey(v.path) === "widget list") as VerbSpec
201
+ ).run({ maker: [maker] }, rt) as Promise<PackPage>;
202
+
203
+ it("a project story resolves its filter through a shipped noun", async () => {
204
+ const modules = assembleEffectiveModules(shipped(MAKER), project([WIDGET]));
205
+ const page = await makerFilter(modules, "Bolt");
206
+ expect(page.rows.map((row) => row.name)).toEqual(["Slider"]);
207
+ });
208
+
209
+ it("a shipped filter resolves through the project's override of the noun it names", async () => {
210
+ const byCode = {
211
+ noun: "maker",
212
+ lookup: { by: "ex:code", type: "ex:Maker" },
213
+ };
214
+ const modules = assembleEffectiveModules(
215
+ shipped(MAKER, WIDGET),
216
+ project([byCode]),
217
+ );
218
+ const page = await makerFilter(modules, "A1");
219
+ expect(page.rows.map((row) => row.name)).toEqual(["Button"]);
220
+ await expect(makerFilter(modules, "Acme")).rejects.toMatchObject({
221
+ code: "INVALID_INPUT",
222
+ });
223
+ });
224
+
225
+ it("refuses a story naming a noun no story declares, where it is assembled", () => {
226
+ expect(() => assembleEffectiveModules([], project([WIDGET]))).toThrow(
227
+ /Invalid story in config: "widget" names the noun "maker"/,
228
+ );
229
+ });
230
+
231
+ it("a project overriding a named noun with no lookup breaks only the filter, when used", async () => {
232
+ const listOnly = { noun: "maker", list: WIDGET.list };
233
+ const modules = assembleEffectiveModules(
234
+ shipped(MAKER, WIDGET),
235
+ project([listOnly]),
236
+ );
237
+ const widgets = modules
238
+ .flatMap((module) => module.verbs)
239
+ .find((v) => verbKey(v.path) === "widget list") as VerbSpec;
240
+ expect(((await widgets.run({}, rt)) as PackPage).rows).toHaveLength(3);
241
+ await expect(makerFilter(modules, "Bolt")).rejects.toMatchObject({
242
+ code: "CONFIG_ERROR",
243
+ message: expect.stringMatching(
244
+ /The "maker" story in config declares no lookup/,
245
+ ),
246
+ });
247
+ });
248
+
249
+ it("a package story is screened against the assembled nouns, whatever the order", () => {
250
+ const entry = { source: "pkg/stories/widget.json", definition: WIDGET };
251
+ const withMaker = screenPackageStories(
252
+ assembleEffectiveModules([], project([MAKER]), [entry]),
253
+ );
254
+ expect(withMaker.modules.map((module) => module.name)).toContain("widget");
255
+ expect(withMaker.problems).toEqual([]);
256
+
257
+ const other = { noun: "gadget", lookup: MAKER.lookup };
258
+ const without = screenPackageStories(
259
+ assembleEffectiveModules([], project([other]), [entry]),
260
+ );
261
+ expect(without.modules.map((module) => module.name)).toEqual(["gadget"]);
262
+ expect(without.problems).toEqual([
263
+ {
264
+ source: "pkg/stories/widget.json",
265
+ message: 'it names the noun "maker", and no story declares it.',
266
+ },
267
+ ]);
268
+ });
269
+ });
270
+
271
+ describe("a many expand field", () => {
272
+ it("is one cell per child holding every value, sorted", async () => {
273
+ const output = (await verb(MAKER, "lookup").run(
274
+ { name: ["ex:acme"] },
275
+ rt,
276
+ )) as LookupOutput;
277
+ const widgets = output.results[0]?.widgets as { aliases?: string }[];
278
+ expect(widgets).toHaveLength(1);
279
+ expect(widgets[0]?.aliases).toBe("Btn btn push-button");
280
+ });
281
+ });
282
+
283
+ describe("a grouped expand", () => {
284
+ it("orders rows that tie on orderBy the same way on every store", async () => {
285
+ const labels: string[] = [];
286
+ for (let boot = 0; boot < 3; boot += 1) {
287
+ const fresh = await buildFixtureRuntime({ ttl: TTL, prefixes: PREFIXES });
288
+ const output = (await verb(MAKER, "lookup").run(
289
+ { name: ["Bolt"] },
290
+ fresh.rt,
291
+ )) as LookupOutput;
292
+ const parts = output.results[0]?.parts as { label: string }[];
293
+ labels.push(parts.map((part) => part.label).join(""));
294
+ }
295
+ expect(labels).toEqual(["abcdef", "abcdef", "abcdef"]);
296
+ });
297
+ });
298
+
299
+ describe("the grammar", () => {
300
+ const withFilter = (filter: Record<string, unknown>): unknown => ({
301
+ ...WIDGET,
302
+ list: { ...WIDGET.list, filters: [filter] },
303
+ });
304
+
305
+ it("accepts both stories as declared", () => {
306
+ expect(() => parsePackDefinition(MAKER, "test")).not.toThrow();
307
+ expect(() => parsePackDefinition(WIDGET, "test")).not.toThrow();
308
+ });
309
+
310
+ it.each([
311
+ [
312
+ "a noun filter with no entity",
313
+ { param: "maker", variable: "maker", noun: "maker" },
314
+ /must declare the "entity"/,
315
+ ],
316
+ [
317
+ "a noun filter with a vocabulary of its own",
318
+ {
319
+ param: "maker",
320
+ variable: "maker",
321
+ noun: "maker",
322
+ entity: "makerUri",
323
+ values: ["Acme"],
324
+ },
325
+ /mutually exclusive/,
326
+ ],
327
+ [
328
+ "an entity the query does not project",
329
+ { param: "maker", variable: "maker", noun: "maker", entity: "absent" },
330
+ /"absent" is filtered or searched but not projected/,
331
+ ],
332
+ [
333
+ "an entity without a noun",
334
+ {
335
+ param: "maker",
336
+ variable: "maker",
337
+ values: ["Acme"],
338
+ entity: "makerUri",
339
+ },
340
+ /belong to a filter that names a "noun"/,
341
+ ],
342
+ ])("refuses %s", (_, filter, reason) => {
343
+ expect(() => parsePackDefinition(withFilter(filter), "test")).toThrow(
344
+ reason,
345
+ );
346
+ });
347
+
348
+ it.each([
349
+ [
350
+ "many with blankWhenSelf",
351
+ { many: true, blankWhenSelf: true },
352
+ {},
353
+ /mutually exclusive/,
354
+ ],
355
+ [
356
+ "many on the GraphQL lane",
357
+ { many: true },
358
+ { source: "graphql" },
359
+ /sets "many", which only the SPARQL lane/,
360
+ ],
361
+ [
362
+ "ordering by a many field",
363
+ { many: true },
364
+ { orderBy: ["aliases"] },
365
+ /a "many" field/,
366
+ ],
367
+ ])("refuses %s", (_, field, expand, reason) => {
368
+ const lookup = MAKER.lookup as NonNullable<PackDefinition["lookup"]>;
369
+ const story = {
370
+ ...MAKER,
371
+ lookup: {
372
+ ...lookup,
373
+ ...("source" in expand ? { source: "graphql" } : {}),
374
+ expand: [
375
+ {
376
+ ...lookup.expand?.[0],
377
+ ...("orderBy" in expand ? expand : {}),
378
+ select: [{ name: "aliases", property: "ex:alias", ...field }],
379
+ },
380
+ ],
381
+ },
382
+ };
383
+ expect(() => parsePackDefinition(story, "test")).toThrow(reason);
384
+ });
385
+ });