@maka/maka-cli 5.123.1 → 5.125.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 (342) hide show
  1. package/README.md +64 -64
  2. package/bundle/typescript/package.json +120 -120
  3. package/bundle/typescript/src/README.md +64 -64
  4. package/bundle/typescript/src/commands/aws/aws.command.js +2 -2
  5. package/bundle/typescript/src/commands/aws/ci.sub.cmd.js +5 -5
  6. package/bundle/typescript/src/commands/aws/ckp.sub.cmd.js +12 -12
  7. package/bundle/typescript/src/commands/aws/csg.sub.cmd.js +3 -3
  8. package/bundle/typescript/src/commands/aws/di.sub.cmd.js +8 -8
  9. package/bundle/typescript/src/commands/aws/rkp.sub.cmd.js +5 -5
  10. package/bundle/typescript/src/commands/aws/sth.sub.cmd.js +3 -3
  11. package/bundle/typescript/src/commands/aws/ti.sub.cmd.js +5 -5
  12. package/bundle/typescript/src/commands/build/build.js +16 -16
  13. package/bundle/typescript/src/commands/build/docker.sub.cmd.js +9 -9
  14. package/bundle/typescript/src/commands/create.js +36 -36
  15. package/bundle/typescript/src/commands/debug.js +4 -4
  16. package/bundle/typescript/src/commands/deploy/deploy.command.js +2 -2
  17. package/bundle/typescript/src/commands/deploy/galaxy.sub.cmd.js +2 -2
  18. package/bundle/typescript/src/commands/deploy/github.sub.cmd.js +8 -8
  19. package/bundle/typescript/src/commands/deploy/ubuntu.sub.cmd.js +2 -2
  20. package/bundle/typescript/src/commands/env/env.command.js +8 -8
  21. package/bundle/typescript/src/commands/env/set.sub.cmd.js +11 -11
  22. package/bundle/typescript/src/commands/game/sideQuest/adept-powers.d.ts +0 -8
  23. package/bundle/typescript/src/commands/game/sideQuest/adept-powers.js +6 -50
  24. package/bundle/typescript/src/commands/game/sideQuest/adept-powers.js.map +1 -1
  25. package/bundle/typescript/src/commands/game/sideQuest/archetypes.js +3 -3
  26. package/bundle/typescript/src/commands/game/sideQuest/archetypes.js.map +1 -1
  27. package/bundle/typescript/src/commands/game/sideQuest/augmentations.d.ts +0 -4
  28. package/bundle/typescript/src/commands/game/sideQuest/augmentations.js +0 -1342
  29. package/bundle/typescript/src/commands/game/sideQuest/augmentations.js.map +1 -1
  30. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.d.ts +1 -1
  31. package/bundle/typescript/src/commands/game/sideQuest/commands/cast.js +1 -1
  32. package/bundle/typescript/src/commands/game/sideQuest/commands/catalog.js +5 -6
  33. package/bundle/typescript/src/commands/game/sideQuest/commands/catalog.js.map +1 -1
  34. package/bundle/typescript/src/commands/game/sideQuest/commands/companions.js +13 -5
  35. package/bundle/typescript/src/commands/game/sideQuest/commands/companions.js.map +1 -1
  36. package/bundle/typescript/src/commands/game/sideQuest/commands/drone.js +5 -2
  37. package/bundle/typescript/src/commands/game/sideQuest/commands/drone.js.map +1 -1
  38. package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.js +2 -2
  39. package/bundle/typescript/src/commands/game/sideQuest/commands/equipment.js.map +1 -1
  40. package/bundle/typescript/src/commands/game/sideQuest/commands/go.js +1 -1
  41. package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.d.ts +0 -38
  42. package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.js +11 -45
  43. package/bundle/typescript/src/commands/game/sideQuest/commands/initiate.js.map +1 -1
  44. package/bundle/typescript/src/commands/game/sideQuest/commands/install.js +5 -4
  45. package/bundle/typescript/src/commands/game/sideQuest/commands/install.js.map +1 -1
  46. package/bundle/typescript/src/commands/game/sideQuest/commands/jump.js +10 -4
  47. package/bundle/typescript/src/commands/game/sideQuest/commands/jump.js.map +1 -1
  48. package/bundle/typescript/src/commands/game/sideQuest/commands/lifestyle.d.ts +7 -0
  49. package/bundle/typescript/src/commands/game/sideQuest/commands/lifestyle.js +58 -9
  50. package/bundle/typescript/src/commands/game/sideQuest/commands/lifestyle.js.map +1 -1
  51. package/bundle/typescript/src/commands/game/sideQuest/commands/quality-trade.js +10 -7
  52. package/bundle/typescript/src/commands/game/sideQuest/commands/quality-trade.js.map +1 -1
  53. package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js +7 -1
  54. package/bundle/typescript/src/commands/game/sideQuest/commands/rest.js.map +1 -1
  55. package/bundle/typescript/src/commands/game/sideQuest/commands/search.js +9 -9
  56. package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js +12 -8
  57. package/bundle/typescript/src/commands/game/sideQuest/commands/sheet.js.map +1 -1
  58. package/bundle/typescript/src/commands/game/sideQuest/commands/sin.js +2 -2
  59. package/bundle/typescript/src/commands/game/sideQuest/commands/sin.js.map +1 -1
  60. package/bundle/typescript/src/commands/game/sideQuest/commands/source.js +8 -8
  61. package/bundle/typescript/src/commands/game/sideQuest/commands/source.js.map +1 -1
  62. package/bundle/typescript/src/commands/game/sideQuest/commands/take.js +22 -1
  63. package/bundle/typescript/src/commands/game/sideQuest/commands/take.js.map +1 -1
  64. package/bundle/typescript/src/commands/game/sideQuest/engine-version.d.ts +1 -1
  65. package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +24 -1
  66. package/bundle/typescript/src/commands/game/sideQuest/engine-version.js.map +1 -1
  67. package/bundle/typescript/src/commands/game/sideQuest/factories/chargen-factory.d.ts +39 -1
  68. package/bundle/typescript/src/commands/game/sideQuest/factories/chargen-factory.js +110 -35
  69. package/bundle/typescript/src/commands/game/sideQuest/factories/chargen-factory.js.map +1 -1
  70. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js +445 -445
  71. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-chunks.js.map +1 -1
  72. package/bundle/typescript/src/commands/game/sideQuest/factories/scene-factory.js +6 -6
  73. package/bundle/typescript/src/commands/game/sideQuest/game.d.ts +42 -1
  74. package/bundle/typescript/src/commands/game/sideQuest/game.js +118 -43
  75. package/bundle/typescript/src/commands/game/sideQuest/game.js.map +1 -1
  76. package/bundle/typescript/src/commands/game/sideQuest/metamagics.d.ts +32 -0
  77. package/bundle/typescript/src/commands/game/sideQuest/metamagics.js +12 -0
  78. package/bundle/typescript/src/commands/game/sideQuest/metamagics.js.map +1 -0
  79. package/bundle/typescript/src/commands/game/sideQuest/metatypes.d.ts +6 -4
  80. package/bundle/typescript/src/commands/game/sideQuest/metatypes.js +3 -83
  81. package/bundle/typescript/src/commands/game/sideQuest/metatypes.js.map +1 -1
  82. package/bundle/typescript/src/commands/game/sideQuest/models/home-containers.d.ts +36 -0
  83. package/bundle/typescript/src/commands/game/sideQuest/models/home-containers.js +62 -0
  84. package/bundle/typescript/src/commands/game/sideQuest/models/home-containers.js.map +1 -0
  85. package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +206 -206
  86. package/bundle/typescript/src/commands/game/sideQuest/models/player.d.ts +5 -5
  87. package/bundle/typescript/src/commands/game/sideQuest/models/player.js +10 -14
  88. package/bundle/typescript/src/commands/game/sideQuest/models/player.js.map +1 -1
  89. package/bundle/typescript/src/commands/game/sideQuest/models/room.js +24 -10
  90. package/bundle/typescript/src/commands/game/sideQuest/models/room.js.map +1 -1
  91. package/bundle/typescript/src/commands/game/sideQuest/qualities.d.ts +3 -7
  92. package/bundle/typescript/src/commands/game/sideQuest/qualities.js +15 -715
  93. package/bundle/typescript/src/commands/game/sideQuest/qualities.js.map +1 -1
  94. package/bundle/typescript/src/commands/game/sideQuest/scenes/scene1.json +556 -556
  95. package/bundle/typescript/src/commands/game/sideQuest/scenes/scene2.json +1447 -1447
  96. package/bundle/typescript/src/commands/game/sideQuest/spells.d.ts +5 -7
  97. package/bundle/typescript/src/commands/game/sideQuest/spells.js +5 -63
  98. package/bundle/typescript/src/commands/game/sideQuest/spells.js.map +1 -1
  99. package/bundle/typescript/src/commands/game/sideQuest/types/save-file.d.ts +8 -0
  100. package/bundle/typescript/src/commands/game/sideQuest/ui.js +22 -0
  101. package/bundle/typescript/src/commands/game/sideQuest/ui.js.map +1 -1
  102. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog-data.d.ts +36 -20
  103. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog-data.js +24 -658
  104. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog-data.js.map +1 -1
  105. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog.d.ts +100 -20
  106. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog.js +314 -57
  107. package/bundle/typescript/src/commands/game/sideQuest/utilities/catalog.js.map +1 -1
  108. package/bundle/typescript/src/commands/game/sideQuest/utilities/commerce.d.ts +32 -0
  109. package/bundle/typescript/src/commands/game/sideQuest/utilities/commerce.js +45 -5
  110. package/bundle/typescript/src/commands/game/sideQuest/utilities/commerce.js.map +1 -1
  111. package/bundle/typescript/src/commands/game/sideQuest/utilities/ephemeral.js +14 -14
  112. package/bundle/typescript/src/commands/game/sideQuest/utilities/owned-drones.d.ts +32 -0
  113. package/bundle/typescript/src/commands/game/sideQuest/utilities/owned-drones.js +24 -0
  114. package/bundle/typescript/src/commands/game/sideQuest/utilities/owned-drones.js.map +1 -0
  115. package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.d.ts +34 -0
  116. package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js +66 -29
  117. package/bundle/typescript/src/commands/game/sideQuest/utilities/persistence.js.map +1 -1
  118. package/bundle/typescript/src/commands/game/sideQuest-backlog.sub.cmd.js +2 -2
  119. package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js +28 -2
  120. package/bundle/typescript/src/commands/game/sideQuest.sub.cmd.js.map +1 -1
  121. package/bundle/typescript/src/commands/generate.js +2 -2
  122. package/bundle/typescript/src/commands/lint.js +10 -10
  123. package/bundle/typescript/src/commands/maka.cmd.js +26 -26
  124. package/bundle/typescript/src/commands/murder.js +14 -14
  125. package/bundle/typescript/src/commands/proxy.js +13 -13
  126. package/bundle/typescript/src/commands/remote/reconfig.sub.cmd.js +6 -6
  127. package/bundle/typescript/src/commands/run/docker.sub.cmd.js +11 -11
  128. package/bundle/typescript/src/commands/run/run.command.js +24 -24
  129. package/bundle/typescript/src/commands/set/global.sub.cmd.js +8 -8
  130. package/bundle/typescript/src/commands/set/local.sub.cmd.js +2 -2
  131. package/bundle/typescript/src/commands/set/set.command.js +3 -3
  132. package/bundle/typescript/src/commands/testCmd/test.command.js +2 -2
  133. package/bundle/typescript/src/error.js +1 -1
  134. package/bundle/typescript/src/error.js.map +1 -1
  135. package/bundle/typescript/src/exec.js +0 -0
  136. package/bundle/typescript/src/generators/api.js +11 -11
  137. package/bundle/typescript/src/generators/collection.js +6 -6
  138. package/bundle/typescript/src/generators/hook.js +10 -10
  139. package/bundle/typescript/src/generators/kits/auth/auth.generator.js +2 -2
  140. package/bundle/typescript/src/generators/kits/auth/auth0-meteor.auth.gen.js +612 -612
  141. package/bundle/typescript/src/generators/kits/auth/basic-meteor.auth.gen.js +48 -48
  142. package/bundle/typescript/src/generators/kits/auth/provider.auth.gen.js +12 -12
  143. package/bundle/typescript/src/generators/kits/esri/esri.generator.js +3 -3
  144. package/bundle/typescript/src/generators/kits/esri/map.esri.gen.js +7 -7
  145. package/bundle/typescript/src/generators/kits/frames/frames.generator.js +16 -16
  146. package/bundle/typescript/src/generators/kits/mui/auth.mui.gen.js +2 -2
  147. package/bundle/typescript/src/generators/kits/mui/auth0.mui.gen.js +5 -5
  148. package/bundle/typescript/src/generators/kits/mui/frame.mui.gen.js +4 -4
  149. package/bundle/typescript/src/generators/kits/mui/mui.generator.js +22 -22
  150. package/bundle/typescript/src/generators/services/translation.js +5 -5
  151. package/bundle/typescript/src/templates/aws/cloudformation/create-ec2-instance.template +14 -14
  152. package/bundle/typescript/src/templates/meteor/api/api.js +20 -20
  153. package/bundle/typescript/src/templates/meteor/api/api.js.ts +20 -20
  154. package/bundle/typescript/src/templates/meteor/api/apollo-gateway.js +50 -50
  155. package/bundle/typescript/src/templates/meteor/api/apollo-gateway.js.ts +55 -55
  156. package/bundle/typescript/src/templates/meteor/api/graphql-api.js +63 -63
  157. package/bundle/typescript/src/templates/meteor/api/graphql-api.js.ts +69 -69
  158. package/bundle/typescript/src/templates/meteor/api/methods.js +39 -39
  159. package/bundle/typescript/src/templates/meteor/api/methods.js.ts +40 -40
  160. package/bundle/typescript/src/templates/meteor/api/register-api.js +3 -3
  161. package/bundle/typescript/src/templates/meteor/api/register-api.js.ts +3 -3
  162. package/bundle/typescript/src/templates/meteor/api/resolvers.js +9 -9
  163. package/bundle/typescript/src/templates/meteor/api/resolvers.js.ts +9 -9
  164. package/bundle/typescript/src/templates/meteor/api/typeDefs.js +6 -6
  165. package/bundle/typescript/src/templates/meteor/api/typeDefs.js.ts +6 -6
  166. package/bundle/typescript/src/templates/meteor/app/client/body.html +3 -3
  167. package/bundle/typescript/src/templates/meteor/app/client/head.html +12 -12
  168. package/bundle/typescript/src/templates/meteor/app/client/main.js +15 -15
  169. package/bundle/typescript/src/templates/meteor/app/client/main.js.jsx +15 -15
  170. package/bundle/typescript/src/templates/meteor/app/client/main.js.ts +17 -17
  171. package/bundle/typescript/src/templates/meteor/app/client/main.js.tsx +17 -17
  172. package/bundle/typescript/src/templates/meteor/app/imports/startup/client/index.js +20 -20
  173. package/bundle/typescript/src/templates/meteor/app/imports/startup/client/index.js.jsx +107 -107
  174. package/bundle/typescript/src/templates/meteor/app/imports/startup/client/index.js.ts +20 -20
  175. package/bundle/typescript/src/templates/meteor/app/imports/startup/client/index.js.tsx +108 -108
  176. package/bundle/typescript/src/templates/meteor/app/imports/startup/client/routes.js +39 -39
  177. package/bundle/typescript/src/templates/meteor/app/imports/startup/client/routes.js.jsx +33 -33
  178. package/bundle/typescript/src/templates/meteor/app/imports/startup/client/routes.js.ts +24 -24
  179. package/bundle/typescript/src/templates/meteor/app/imports/startup/client/routes.js.tsx +33 -33
  180. package/bundle/typescript/src/templates/meteor/app/imports/startup/client/templates.js +9 -9
  181. package/bundle/typescript/src/templates/meteor/app/imports/startup/client/templates.js.jsx +9 -9
  182. package/bundle/typescript/src/templates/meteor/app/imports/startup/client/templates.js.ts +9 -9
  183. package/bundle/typescript/src/templates/meteor/app/imports/startup/client/templates.js.tsx +9 -9
  184. package/bundle/typescript/src/templates/meteor/app/imports/startup/lib/index.js +1 -1
  185. package/bundle/typescript/src/templates/meteor/app/imports/startup/lib/index.js.jsx +20 -20
  186. package/bundle/typescript/src/templates/meteor/app/imports/startup/lib/index.js.ts +1 -1
  187. package/bundle/typescript/src/templates/meteor/app/imports/startup/lib/index.js.tsx +20 -20
  188. package/bundle/typescript/src/templates/meteor/app/imports/startup/lib/routes.js.jsx +28 -28
  189. package/bundle/typescript/src/templates/meteor/app/imports/startup/lib/routes.js.tsx +28 -28
  190. package/bundle/typescript/src/templates/meteor/app/imports/startup/server/index.js +1 -1
  191. package/bundle/typescript/src/templates/meteor/app/imports/startup/server/index.js.jsx +95 -95
  192. package/bundle/typescript/src/templates/meteor/app/imports/startup/server/index.js.tsx +98 -98
  193. package/bundle/typescript/src/templates/meteor/app/imports/ui/test-helpers.js +24 -24
  194. package/bundle/typescript/src/templates/meteor/app/imports/ui/test-helpers.js.ts +24 -24
  195. package/bundle/typescript/src/templates/meteor/app/lib/main.js +3 -3
  196. package/bundle/typescript/src/templates/meteor/app/lib/main.js.jsx +3 -3
  197. package/bundle/typescript/src/templates/meteor/app/lib/main.js.ts +3 -3
  198. package/bundle/typescript/src/templates/meteor/app/lib/main.js.tsx +3 -3
  199. package/bundle/typescript/src/templates/meteor/app/maka.d.ts +533 -533
  200. package/bundle/typescript/src/templates/meteor/app/meteor.d.ts +181 -181
  201. package/bundle/typescript/src/templates/meteor/app/public/manifest.json +27 -27
  202. package/bundle/typescript/src/templates/meteor/app/public/sw.js +30 -30
  203. package/bundle/typescript/src/templates/meteor/app/public/sw.js.jsx +30 -30
  204. package/bundle/typescript/src/templates/meteor/app/public/sw.js.ts +30 -30
  205. package/bundle/typescript/src/templates/meteor/app/public/sw.js.tsx +30 -30
  206. package/bundle/typescript/src/templates/meteor/app/server/helmet-csp.js +179 -179
  207. package/bundle/typescript/src/templates/meteor/app/server/helmet-csp.js.jsx +179 -179
  208. package/bundle/typescript/src/templates/meteor/app/server/helmet-csp.js.ts +179 -179
  209. package/bundle/typescript/src/templates/meteor/app/server/helmet-csp.js.tsx +179 -179
  210. package/bundle/typescript/src/templates/meteor/app/server/main.js +13 -13
  211. package/bundle/typescript/src/templates/meteor/app/server/main.js.jsx +13 -13
  212. package/bundle/typescript/src/templates/meteor/app/server/main.js.ts +14 -14
  213. package/bundle/typescript/src/templates/meteor/app/server/main.js.tsx +14 -14
  214. package/bundle/typescript/src/templates/meteor/app/tsconfig.json +42 -42
  215. package/bundle/typescript/src/templates/meteor/collection/collection.js +51 -51
  216. package/bundle/typescript/src/templates/meteor/collection/collection.js.ts +55 -55
  217. package/bundle/typescript/src/templates/meteor/collection/tests.js +27 -27
  218. package/bundle/typescript/src/templates/meteor/collection/tests.js.ts +27 -27
  219. package/bundle/typescript/src/templates/meteor/config/deploy.sh.template +101 -101
  220. package/bundle/typescript/src/templates/meteor/config/docker-compose.yaml +17 -17
  221. package/bundle/typescript/src/templates/meteor/config/nginx.conf.template +31 -31
  222. package/bundle/typescript/src/templates/meteor/config/pm2.config.js +27 -27
  223. package/bundle/typescript/src/templates/meteor/config/process.env +5 -5
  224. package/bundle/typescript/src/templates/meteor/config/settings.json +11 -11
  225. package/bundle/typescript/src/templates/meteor/config/ssh.json +6 -6
  226. package/bundle/typescript/src/templates/meteor/dbc/config.js +70 -70
  227. package/bundle/typescript/src/templates/meteor/dbc/config.js.ts +70 -70
  228. package/bundle/typescript/src/templates/meteor/dbc/conn.js +37 -37
  229. package/bundle/typescript/src/templates/meteor/dbc/conn.js.ts +37 -37
  230. package/bundle/typescript/src/templates/meteor/dbc/register-dbc.js +3 -3
  231. package/bundle/typescript/src/templates/meteor/dbc/register-dbc.js.ts +3 -3
  232. package/bundle/typescript/src/templates/meteor/github/workflows/deploy.yaml +150 -150
  233. package/bundle/typescript/src/templates/meteor/method/method.js +48 -48
  234. package/bundle/typescript/src/templates/meteor/method/method.js.ts +48 -48
  235. package/bundle/typescript/src/templates/meteor/package/package.js +37 -37
  236. package/bundle/typescript/src/templates/meteor/package/package.js.jsx +37 -37
  237. package/bundle/typescript/src/templates/meteor/package/package.js.ts +36 -36
  238. package/bundle/typescript/src/templates/meteor/package/package.js.tsx +37 -37
  239. package/bundle/typescript/src/templates/meteor/package/tests/client/package.js +5 -5
  240. package/bundle/typescript/src/templates/meteor/package/tests/client/package.js.jsx +5 -5
  241. package/bundle/typescript/src/templates/meteor/package/tests/client/package.js.ts +5 -5
  242. package/bundle/typescript/src/templates/meteor/package/tests/client/package.js.tsx +5 -5
  243. package/bundle/typescript/src/templates/meteor/package/tests/server/package.js +5 -5
  244. package/bundle/typescript/src/templates/meteor/package/tests/server/package.js.jsx +5 -5
  245. package/bundle/typescript/src/templates/meteor/package/tests/server/package.js.ts +5 -5
  246. package/bundle/typescript/src/templates/meteor/package/tests/server/package.js.tsx +5 -5
  247. package/bundle/typescript/src/templates/meteor/project/.maka/config.json +29 -29
  248. package/bundle/typescript/src/templates/meteor/project/README.md +33 -33
  249. package/bundle/typescript/src/templates/meteor/project/build/README +4 -4
  250. package/bundle/typescript/src/templates/meteor/project/gitignore +10 -10
  251. package/bundle/typescript/src/templates/meteor/project/package.json +29 -29
  252. package/bundle/typescript/src/templates/meteor/publish/publish.js +7 -7
  253. package/bundle/typescript/src/templates/meteor/publish/publish.js.ts +7 -7
  254. package/bundle/typescript/src/templates/meteor/service/logger/logger.js +27 -27
  255. package/bundle/typescript/src/templates/meteor/service/logger/logger.js.ts +27 -27
  256. package/bundle/typescript/src/templates/meteor/stylesheet/stylesheet.css +5 -5
  257. package/bundle/typescript/src/templates/meteor/stylesheet/stylesheet.css.less +5 -5
  258. package/bundle/typescript/src/templates/meteor/template/template-tests.js +1 -1
  259. package/bundle/typescript/src/templates/meteor/template/template-tests.js.jsx +55 -55
  260. package/bundle/typescript/src/templates/meteor/template/template-tests.js.ts +1 -1
  261. package/bundle/typescript/src/templates/meteor/template/template-tests.js.tsx +55 -55
  262. package/bundle/typescript/src/templates/meteor/template/template.css +3 -3
  263. package/bundle/typescript/src/templates/meteor/template/template.css.less +3 -3
  264. package/bundle/typescript/src/templates/meteor/template/template.css.scss +3 -3
  265. package/bundle/typescript/src/templates/meteor/template/template.html +5 -5
  266. package/bundle/typescript/src/templates/meteor/template/template.js +20 -20
  267. package/bundle/typescript/src/templates/meteor/template/template.js.jsx +77 -77
  268. package/bundle/typescript/src/templates/meteor/template/template.js.ts +1 -1
  269. package/bundle/typescript/src/templates/meteor/template/template.js.tsx +89 -89
  270. package/bundle/typescript/src/templates/react/component/component.css +6 -6
  271. package/bundle/typescript/src/templates/react/component/component.css.less +6 -6
  272. package/bundle/typescript/src/templates/react/component/component.css.scss +3 -3
  273. package/bundle/typescript/src/templates/react/component/component.js.jsx +59 -59
  274. package/bundle/typescript/src/templates/react/component/component.js.tsx +65 -65
  275. package/bundle/typescript/src/templates/react/hook/hook.css +3 -3
  276. package/bundle/typescript/src/templates/react/hook/hook.css.scss +3 -3
  277. package/bundle/typescript/src/templates/react/hook/hook.js.jsx +19 -19
  278. package/bundle/typescript/src/templates/react/hook/hook.js.tsx +19 -19
  279. package/bundle/typescript/src/templates/react/kit/auth/provider/authentication-provider.js.jsx +306 -306
  280. package/bundle/typescript/src/templates/react/kit/auth/provider/authentication-provider.js.tsx +350 -350
  281. package/bundle/typescript/src/templates/react/kit/auth/service/auth0-client.js.jsx +178 -178
  282. package/bundle/typescript/src/templates/react/kit/auth/service/auth0-client.js.tsx +178 -178
  283. package/bundle/typescript/src/templates/react/kit/auth/service/auth0-config.js.jsx +59 -59
  284. package/bundle/typescript/src/templates/react/kit/auth/service/auth0-config.js.tsx +67 -67
  285. package/bundle/typescript/src/templates/react/kit/auth/service/auth0-methods.js.jsx +273 -273
  286. package/bundle/typescript/src/templates/react/kit/auth/service/auth0-methods.js.tsx +275 -275
  287. package/bundle/typescript/src/templates/react/kit/auth/service/auth0-validator.js.jsx +182 -182
  288. package/bundle/typescript/src/templates/react/kit/auth/service/auth0-validator.js.tsx +199 -199
  289. package/bundle/typescript/src/templates/react/kit/auth/service/meteor-auth-service.js.jsx +138 -138
  290. package/bundle/typescript/src/templates/react/kit/auth/service/meteor-auth-service.js.tsx +153 -153
  291. package/bundle/typescript/src/templates/react/kit/esri/map/basic-map.js.jsx +29 -29
  292. package/bundle/typescript/src/templates/react/kit/esri/map/basic-map.js.tsx +32 -32
  293. package/bundle/typescript/src/templates/react/kit/frames/frames-demo.css +63 -63
  294. package/bundle/typescript/src/templates/react/kit/frames/frames-demo.jsx +153 -153
  295. package/bundle/typescript/src/templates/react/kit/frames/frames-demo.tsx +164 -164
  296. package/bundle/typescript/src/templates/react/kit/frames/workspace-manager.css +105 -105
  297. package/bundle/typescript/src/templates/react/kit/frames/workspace-manager.jsx +158 -158
  298. package/bundle/typescript/src/templates/react/kit/frames/workspace-manager.tsx +166 -166
  299. package/bundle/typescript/src/templates/react/kit/mui/auth/forgot-password/basic-forgot-password-page.js.jsx +34 -34
  300. package/bundle/typescript/src/templates/react/kit/mui/auth/forgot-password/basic-forgot-password-page.js.tsx +33 -33
  301. package/bundle/typescript/src/templates/react/kit/mui/auth/login/basic-login-page.js.jsx +53 -53
  302. package/bundle/typescript/src/templates/react/kit/mui/auth/login/basic-login-page.js.tsx +53 -53
  303. package/bundle/typescript/src/templates/react/kit/mui/auth/logout/basic-logout-page.js.jsx +25 -25
  304. package/bundle/typescript/src/templates/react/kit/mui/auth/logout/basic-logout-page.js.tsx +25 -25
  305. package/bundle/typescript/src/templates/react/kit/mui/auth/password-reset/basic-password-reset-page.js.jsx +41 -41
  306. package/bundle/typescript/src/templates/react/kit/mui/auth/password-reset/basic-password-reset-page.js.tsx +34 -34
  307. package/bundle/typescript/src/templates/react/kit/mui/auth/register/basic-register-page.js.jsx +33 -33
  308. package/bundle/typescript/src/templates/react/kit/mui/auth/register/basic-register-page.js.tsx +33 -33
  309. package/bundle/typescript/src/templates/react/kit/mui/auth0/auth-callback.js.jsx +71 -71
  310. package/bundle/typescript/src/templates/react/kit/mui/auth0/auth-callback.js.tsx +71 -71
  311. package/bundle/typescript/src/templates/react/kit/mui/auth0/auth0-account-settings.js.jsx +123 -123
  312. package/bundle/typescript/src/templates/react/kit/mui/auth0/auth0-account-settings.js.tsx +123 -123
  313. package/bundle/typescript/src/templates/react/kit/mui/auth0/auth0-login-button.js.jsx +56 -56
  314. package/bundle/typescript/src/templates/react/kit/mui/auth0/auth0-login-button.js.tsx +74 -74
  315. package/bundle/typescript/src/templates/react/kit/mui/frame/app-bars/basic-app-bar.js.jsx +43 -43
  316. package/bundle/typescript/src/templates/react/kit/mui/frame/app-bars/basic-app-bar.js.tsx +43 -43
  317. package/bundle/typescript/src/templates/react/kit/mui/frame/drawers/basic-drawer.js.jsx +121 -121
  318. package/bundle/typescript/src/templates/react/kit/mui/frame/drawers/basic-drawer.js.tsx +117 -117
  319. package/bundle/typescript/src/templates/react/kit/mui/frame/footers/basic-sticky-footer.js.jsx +41 -41
  320. package/bundle/typescript/src/templates/react/kit/mui/frame/footers/basic-sticky-footer.js.tsx +41 -41
  321. package/bundle/typescript/src/templates/react/route/route.js +8 -8
  322. package/bundle/typescript/src/templates/react/route/route.js.jsx +5 -5
  323. package/bundle/typescript/src/templates/react/route/route.js.ts +8 -8
  324. package/bundle/typescript/src/templates/react/route/route.js.tsx +5 -5
  325. package/bundle/typescript/src/templates/react/test/test.js +1 -1
  326. package/bundle/typescript/src/templates/react/test/test.js.jsx +40 -40
  327. package/bundle/typescript/src/templates/react/test/test.js.ts +1 -1
  328. package/bundle/typescript/src/templates/react/test/test.js.tsx +40 -40
  329. package/bundle/typescript/src/templates/service/docker/Dockerfile +54 -54
  330. package/bundle/typescript/src/templates/service/eslint/babel.config.js +16 -16
  331. package/bundle/typescript/src/templates/service/eslint/config.eslint.js +69 -69
  332. package/bundle/typescript/src/templates/service/i18n/i18n.js +33 -33
  333. package/bundle/typescript/src/templates/service/i18n/i18n.js.ts +33 -33
  334. package/bundle/typescript/src/templates/service/i18n/locales.js +3 -3
  335. package/bundle/typescript/src/templates/service/i18n/locales.js.ts +3 -3
  336. package/bundle/typescript/src/templates/service/openssl/openssl.conf +22 -22
  337. package/bundle/typescript/src/templates/service/test/jest/jest.config.js +24 -24
  338. package/bundle/typescript/src/templates/service/test/jest/jest.setup.js +7 -7
  339. package/bundle/typescript/src/tool.d.ts +2 -0
  340. package/bundle/typescript/src/tool.js +5 -2
  341. package/bundle/typescript/src/tool.js.map +1 -1
  342. package/package.json +120 -120
@@ -13,720 +13,16 @@
13
13
  * real hook and says so in its comment. Karma values are RAW (p.73-74
14
14
  * summary tables) throughout.
15
15
  */
16
+ import { qualityFor } from './utilities/catalog.js';
16
17
  /** Everyone starts character creation with 25 karma (SR5e p.71). */
17
18
  export const CREATION_KARMA = 25;
18
- export const QUALITIES = [
19
- // ------------------------------ Positive ------------------------------
20
- {
21
- key: 'toughness', name: 'Toughness', kind: 'positive', karma: 9, page: 'p.77',
22
- family: 'pool',
23
- effect: '+1 die on every damage soak roll',
24
- effects: [{ type: 'dice', category: 'pools', property: 'soak', value: 1 }],
25
- },
26
- {
27
- // RAW: ignore one box per rating when counting wound modifiers, and it
28
- // cannot combine with a pain editor (woundModifier enforces that).
29
- key: 'high-pain-tolerance', name: 'High Pain Tolerance', kind: 'positive', karma: 7, page: 'p.74',
30
- family: 'engine-rule',
31
- effect: 'ignore 1 box of damage when counting wound modifiers',
32
- },
33
- {
34
- key: 'catlike', name: 'Catlike', kind: 'positive', karma: 7, page: 'p.72',
35
- family: 'pool',
36
- effect: '+2 dice on sneaking tests',
37
- effects: [{ type: 'dice', category: 'pools', property: 'sneak', value: 2 }],
38
- },
39
- {
40
- // THE INVITE (Data Trails p.45-46, Prime Datahaven Membership). You
41
- // pick one famous haven -- JackPoint, the Nexus, the Helix -- and
42
- // this game ships JackPoint (see utilities/jackpoint.ts). Access is
43
- // invitation-only, which is the whole point: the host icon is
44
- // "visible only if you have access" (p.225-226), so without this the
45
- // place is not merely locked, it is not THERE.
46
- //
47
- // The mechanical hook is real and is that access, which satisfies
48
- // the catalog's no-flavor-only rule. The book also makes the haven a
49
- // Group Contact at Loyalty 3 / Connection 5 -- NOT modelled here,
50
- // and said rather than quietly dropped: this game's contacts are
51
- // named people you call, and inventing a group entry to carry two
52
- // numbers nothing reads would be a worse lie than the omission.
53
- key: 'prime-datahaven-membership', name: 'Prime Datahaven Membership',
54
- family: 'engine-rule',
55
- kind: 'positive', karma: 7, page: 'Data Trails p.45-46',
56
- effect: 'JackPoint knows your handle -- the haven answers when you call it',
57
- },
58
- {
59
- // RAW: +2 on Running/Gymnastics -- here, the athletics sprint that
60
- // breaks off a fight (go.ts) is where that muscle shows.
61
- key: 'natural-athlete', name: 'Natural Athlete', kind: 'positive', karma: 7, page: 'p.76',
62
- family: 'pool',
63
- effect: '+2 dice on athletics tests (including the sprint out of a fight)',
64
- effects: [{ type: 'dice', category: 'pools', property: 'athletics', value: 2 }],
65
- },
66
- {
67
- // A CEILING, NOT A POINT -- RAG-checked p.76, which is explicit
68
- // about the difference: "Lucky allows a character to possess an Edge
69
- // attribute one point higher than his metatype maximum ... Note that
70
- // taking this quality does not actually increase the character's
71
- // current Edge rating, it just allows her the opportunity to do so;
72
- // the Karma cost for gaining the extra point must still be paid."
73
- // A human's Edge maximum goes 7 -> 8, and the eighth point is bought
74
- // at "advance" like any other.
75
- //
76
- // THE COMMENT THAT USED TO SIT HERE said "RAW raises the Edge CEILING
77
- // without granting the point; with no point-buy attributes here, the
78
- // adaptation grants it outright" -- an honest approximation from the
79
- // days when nothing in this file could express a ceiling. That excuse
80
- // expired when `cap` effects arrived, and by then the approximation
81
- // was costing something real: the web chargen applies the book's
82
- // rule, so the same archetype built in the wizard and in the CLI
83
- // disagreed about Edge by one. Reported by the user on 2026-09-04 --
84
- // "I'm adding Lucky ... I don't see this updating in the chargen".
85
- // The wizard was right.
86
- //
87
- // It is also WHY this quality and Exceptional Attribute exclude each
88
- // other (p.65-66): they are the same mechanic, and Edge is the one
89
- // attribute Exceptional Attribute may not name.
90
- key: 'lucky', name: 'Lucky', kind: 'positive', karma: 12, page: 'p.76',
91
- family: 'ceiling',
92
- excludesQuality: 'exceptional-attribute',
93
- effect: 'your Edge may run 1 past its natural maximum -- buy the point at "advance"',
94
- effects: [{ type: 'cap', category: 'attributes', property: 'edge', value: 1 }],
95
- },
96
- {
97
- // RAW: +2 social dice on a first meeting. Meetings aren't tracked
98
- // per-NPC, so the adaptation is per JOB: the first social play of
99
- // every job (persuade, bluff, haggle) gets the bonus.
100
- key: 'first-impression', name: 'First Impression', kind: 'positive', karma: 11, page: 'p.74',
101
- family: 'engine-rule',
102
- effect: '+2 dice on the first social play of every job',
103
- },
104
- {
105
- // RAW: +2 on all Healing Tests -- exactly the first-aid roll (use.ts)
106
- // and the rest recovery roll (rest.ts).
107
- key: 'quick-healer', name: 'Quick Healer', kind: 'positive', karma: 3, page: 'p.77',
108
- family: 'pool',
109
- effect: '+2 dice on every healing test made on or by you (first aid, rest)',
110
- effects: [{ type: 'dice', category: 'pools', property: 'healing', value: 2 }],
111
- },
112
- {
113
- // RAW: +2 on Logic tests for pattern analysis / puzzle-cracking --
114
- // mapped to the hacking rolls, the game's logic-puzzle work.
115
- key: 'analytical-mind', name: 'Analytical Mind', kind: 'positive', karma: 5, page: 'p.72',
116
- family: 'pool',
117
- effect: '+2 dice on hacking tests -- code is just a puzzle with rent',
118
- effects: [{ type: 'dice', category: 'skills', property: 'hacking', value: 2 }],
119
- },
120
- {
121
- // RAW: +2 juryrigging mechanical/electronic gear -- mapped to the
122
- // piloting roll (drone work IS field rigging) and bench deck repair.
123
- key: 'juryrigger', name: 'Juryrigger', kind: 'positive', karma: 10, page: 'p.75',
124
- family: 'pool',
125
- effect: '+2 dice on piloting and on bench-repairing a damaged deck',
126
- effects: [{ type: 'dice', category: 'pools', property: 'piloting', value: 2 }],
127
- },
128
- {
129
- // RAW: +1 on ALL Perception Tests, and the book says "including
130
- // Astral and Matrix Perception" -- which this engine gets for free,
131
- // because perceptionTest is one function that already switches to
132
- // Computer + Logic on the grid. The rating-2 form (10 karma, +2)
133
- // exists in the book and is not shipped: IQuality carries no rating
134
- // and its shape is co-owned with the web chargen, so a second entry
135
- // would be inventing a rating system in the key names.
136
- key: 'perceptive', name: 'Perceptive', kind: 'positive', karma: 5, page: 'Run Faster p.148',
137
- family: 'pool',
138
- effect: '+1 die on every perception test -- meat, astral or grid',
139
- effects: [{ type: 'dice', category: 'pools', property: 'perception', value: 1 }],
140
- },
141
- {
142
- // RAW: +1 to every skill in the INFLUENCE group (Etiquette,
143
- // Negotiation, Con, Intimidation, Leadership).
144
- //
145
- // TWO PARTS ARE NOT SHIPPED AND THE EFFECT LINE SAYS SO RATHER THAN
146
- // IMPLYING THEM. The book also raises the Social Limit by 2 when
147
- // trust is at stake; this game has no limit system at all (the same
148
- // gap socialRep's comment records), so there is nothing to raise.
149
- // And INTIMIDATION is left out: brandish.ts's pool reads no social
150
- // tap of any kind, and adding one there would silently change what
151
- // social chrome does too -- a bigger change than this entry, made
152
- // sideways. The verbs named below are the ones that actually move.
153
- key: 'trustworthy', name: 'Trustworthy', kind: 'positive', karma: 15, page: 'Run Faster p.151',
154
- family: 'pool',
155
- effect: '+1 die when you ask, bluff, haggle or persuade -- people believe you',
156
- effects: [{ type: 'dice', category: 'pools', property: 'social', value: 1 }],
157
- },
158
- {
159
- // RAW: +2 to ONE Matrix action you choose when you take it (p.237's
160
- // list, test-bearing actions only).
161
- //
162
- // THE CHOICE LIVES IN THE KEY, the same shape `incompetent-<skill>`
163
- // already uses -- IQuality has no field for a sub-selection and its
164
- // shape is co-owned with the web chargen, so the alternative was a
165
- // contract change for one quality. Hack on the Fly is the action
166
- // shipped because it is one the engine actually names: hack.ts logs
167
- // the sleaze route as "Hack on the Fly (sleaze)" per p.240, and
168
- // mark.ts states that placing a mark IS that same action.
169
- key: 'codeslinger', name: 'Codeslinger',
170
- family: 'pool',
171
- kind: 'positive', karma: 10, page: 'p.72',
172
- effect: '+2 dice on ONE Matrix action you choose -- the route you made your own',
173
- choice: {
174
- of: 'matrix-action',
175
- prompt: 'Which way in did you make your own -- the quiet one or the loud one?',
176
- from: ['matrix-sleaze', 'matrix-attack'],
177
- },
178
- effects: [{ type: 'dice', category: 'pools', property: 'chosen', value: 2 }],
179
- },
180
- {
181
- // GOING FOR ALL THREE AT ONCE (Data Trails p.44, cited on
182
- // uMQAhaAysgaKpWFkn's own vetting). Declaring three marks on a single
183
- // Brute Force or Hack on the Fly normally costs -10 dice; this makes
184
- // it -6. It is the one quality that touches the declaration table, so
185
- // it is read there (utilities/matrix-intrusion.ts) rather than as a
186
- // pool bonus -- a -4 handed to every intrusion would be a different,
187
- // much stronger quality than the book's.
188
- key: 'go-big-or-go-home', name: 'Go Big or Go Home',
189
- family: 'engine-rule',
190
- kind: 'positive', karma: 6, page: 'Data Trails p.44',
191
- effect: 'reaching for all three marks in one action costs -6 dice instead of -10',
192
- },
193
- // ------------------------------ Negative ------------------------------
194
- {
195
- key: 'sinner-national', name: 'SINner (National)', kind: 'negative', karma: 5, page: 'p.84',
196
- family: 'engine-rule',
197
- effect: 'a legal SIN with taxes attached: 15% of every job and gig payout withheld',
198
- },
199
- {
200
- key: 'sinner-criminal', name: 'SINner (Criminal)', kind: 'negative', karma: 10, page: 'p.84',
201
- family: 'engine-rule',
202
- effect: 'a felon\'s SIN, broadcast always: 15% of every payout withheld, and the law knows your face',
203
- },
204
- {
205
- key: 'sinner-corp', name: 'SINner (Corporate Limited)', kind: 'negative', karma: 15, page: 'p.84-85',
206
- family: 'engine-rule',
207
- effect: 'a megacorp owns your file: 20% of every payout goes home to the company',
208
- },
209
- {
210
- key: 'low-pain-tolerance', name: 'Low Pain Tolerance', kind: 'negative', karma: 9, page: 'p.82',
211
- family: 'engine-rule',
212
- effect: 'wound modifiers bite every 2 boxes instead of every 3',
213
- },
214
- {
215
- // RAW: first Initiative Score of a combat halved. Mapped to the
216
- // opening exchange of any fresh fight (combat-exchange.ts).
217
- key: 'combat-paralysis', name: 'Combat Paralysis', kind: 'negative', karma: 12, page: 'p.80',
218
- family: 'engine-rule',
219
- effect: 'you freeze when it starts: initiative halved in a fight\'s opening exchange',
220
- },
221
- {
222
- // RAW: spent Edge can turn on you. Adaptation: burning edge rolls a
223
- // die -- on a 1 the point is gone and the boost never arms (edge.ts).
224
- key: 'bad-luck', name: 'Bad Luck', kind: 'negative', karma: 12, page: 'p.79',
225
- family: 'engine-rule',
226
- effect: '1 in 6 edge burns curdle: the point is spent, the boost never comes',
227
- },
228
- {
229
- // RAW: a look people remember, easy to describe, easy to identify.
230
- // Adaptation: -2 on sneaking tests -- witnesses clock you.
231
- key: 'distinctive-style', name: 'Distinctive Style', kind: 'negative', karma: 5, page: 'p.80',
232
- family: 'pool',
233
- effect: 'people remember you: -2 dice on sneaking tests',
234
- effects: [{ type: 'dice', category: 'pools', property: 'sneak', value: -2 }],
235
- },
236
- {
237
- // RAW: rest may not restore you fully. Adaptation: rest recovery
238
- // halved (rest.ts) -- sleep never comes easy.
239
- key: 'insomnia', name: 'Insomnia', kind: 'negative', karma: 10, page: 'p.81',
240
- family: 'engine-rule',
241
- effect: 'rest recovers half the stun it should -- sleep never comes easy',
242
- },
243
- {
244
- key: 'simsense-vertigo', name: 'Simsense Vertigo', kind: 'negative', karma: 5, page: 'p.83',
245
- family: 'pool',
246
- effect: '-2 dice on Matrix actions -- the sim makes your inner ear lie',
247
- effects: [{ type: 'dice', category: 'pools', property: 'matrix', value: -2 }],
248
- },
249
- {
250
- // RAW: an Active skill group you simply cannot have. skillRating
251
- // pins it to 0 and advance.ts refuses to train it.
252
- // PER SKILL GROUP, NOT PER SKILL, and RAG-checked after this entry
253
- // first shipped as a single-skill pick: p.81 applies the quality to
254
- // "a certain Active skill group" and treats the holder as unaware
255
- // "for all skills in the skill group". Naming one skill was a
256
- // deviation I introduced while retiring the old slug -- the slug
257
- // said hacking, and I carried its narrowness across instead of
258
- // reading the quality.
259
- //
260
- // FORBIDDEN, NOT ZEROED. Unaware (p.131) is "You cannot default the
261
- // skill" -- so this is a `forbid` effect and not a ceiling, because
262
- // no number says "absent". A -6 would read as 0 at creation and 6
263
- // against the career ceiling, and even an absolute 0 would still
264
- // default at -1 in this engine.
265
- key: 'incompetent', name: 'Incompetent', kind: 'negative', karma: 5, page: 'p.81',
266
- choice: {
267
- of: 'skill-group',
268
- prompt: 'Which whole set of skills has never taken, however hard anyone tried?',
269
- },
270
- effects: [{ type: 'forbid', category: 'skills', property: 'chosen' }],
271
- // Half-live, and deliberately not tagged inert: advance.ts refuses
272
- // to train the skill at any price (the "never trained" half of the
273
- // rule). What is missing is the other half -- nothing forces the
274
- // rating to 0 at creation, so an archetype could still hand one out.
275
- family: 'engine-rule',
276
- effect: 'one whole skill group is closed to you -- you cannot use it, train it, or even default on it',
277
- },
278
- {
279
- // RAW: you start play with 3 points of Notoriety. Read straight by
280
- // qualityNotoriety, so it costs socialRep dice and feeds public
281
- // awareness exactly as earned notoriety does -- the quality does not
282
- // need its own arithmetic, it needs the number the book gives it.
283
- key: 'bad-rep', name: 'Bad Rep', kind: 'negative', karma: 7, page: 'p.79',
284
- family: 'engine-rule',
285
- effect: 'the street tells stories about you: 3 points of notoriety, before you have earned any',
286
- },
287
- {
288
- // RAW is broader than what ships, and the effect line is scoped to
289
- // the part that is real here: -2 on Social Tests. Not modelled --
290
- // doubled social-skill training costs, no social skill GROUPS (this
291
- // engine has no groups), and "unaware" in unpossessed social skills
292
- // (skillRating already floors at 0 with an unskilled -1 default, so
293
- // there is no lower rung to drop to).
294
- key: 'uncouth', name: 'Uncouth', kind: 'negative', karma: 14, page: 'p.85-86',
295
- family: 'pool',
296
- effect: '-2 dice when you ask, bluff, haggle or persuade -- you rub people wrong',
297
- effects: [{ type: 'dice', category: 'pools', property: 'social', value: -2 }],
298
- },
299
- {
300
- // RAW: -2 on ONE Matrix action you choose. Same key-encoded choice
301
- // as Codeslinger above, and the same reason. Brute Force is the
302
- // action shipped: hack.ts logs the loud route as "Brute Force
303
- // (attack)" per p.238, so the engine names it.
304
- key: 'codeblock', name: 'Codeblock',
305
- family: 'pool',
306
- kind: 'negative', karma: 10, page: 'p.80',
307
- effect: '-2 dice on ONE Matrix action you choose -- the one that never sits right',
308
- choice: {
309
- of: 'matrix-action',
310
- prompt: 'Which way in never sits right -- the quiet one or the loud one?',
311
- from: ['matrix-sleaze', 'matrix-attack'],
312
- },
313
- effects: [{ type: 'dice', category: 'pools', property: 'chosen', value: -2 }],
314
- },
315
- // ================= THE FULL-CATALOG PASS (2026-09-01) =================
316
- // The user's report: "the qualities list is severely lacking" -- and it
317
- // was: augs, items and drones each got their catalog sweep and qualities
318
- // never did. Costs below are the p.73-74 tables (RAG-retrieved twice,
319
- // agreeing); effect lines are the p.71-87 prose read off the page.
320
- // Option (a) discipline: an effect the engine cannot yet roll is stated
321
- // honestly as what the book grants -- the sheet shows it, the table rules
322
- // it -- rather than silently wired to nothing or invented differently.
323
- // Rated qualities enter at Rating 1 with the rating in the name (the
324
- // High Pain Tolerance / Incompetent-variant pattern already in use).
325
- // ------------------------------ Positive ------------------------------
326
- {
327
- key: 'ambidextrous', name: 'Ambidextrous', kind: 'positive', karma: 4, page: 'p.71',
328
- inert: 'the engine applies no off-hand penalty (p.178), so there is none to waive',
329
- family: 'engine-rule',
330
- effect: 'either hand works equally well -- no off-hand penalty (p.178)',
331
- },
332
- {
333
- // RAG-checked p.72: the chosen skill may be rated 7 at creation
334
- // (against the usual 6) and may eventually reach 13 (against 12).
335
- // Both ends are the SAME delta -- one step further than anyone else
336
- // gets -- so the engine applies +1 to its own ceiling at both ends
337
- // rather than writing the book's absolute numbers over house ones.
338
- // Taken once only.
339
- key: 'aptitude', name: 'Aptitude', kind: 'positive', karma: 14, page: 'p.72',
340
- inert: 'the engine path is complete -- the web chargen does not yet ask which skill, and without an answer the ceiling lifts nothing',
341
- family: 'ceiling',
342
- effect: 'one chosen skill may start at 7 and one day reach 13 -- past every limit',
343
- choice: { of: 'skill', prompt: 'Which skill is the one you were born for?' },
344
- effects: [{ type: 'cap', category: 'skills', property: 'chosen', value: 1 }],
345
- },
346
- {
347
- key: 'astral-chameleon', name: 'Astral Chameleon', kind: 'positive', karma: 10, page: 'p.72',
348
- inert: 'no astral signature is recorded, so nothing can fade faster or read fainter',
349
- family: 'engine-rule',
350
- requires: 'awakened',
351
- effect: 'your astral signature fades in half the time and reads at -2 to assensing',
352
- },
353
- {
354
- key: 'bilingual', name: 'Bilingual', kind: 'positive', karma: 5, page: 'p.72',
355
- inert: 'the engine models no languages',
356
- family: 'narrative',
357
- effect: 'a second language as a native tongue, free, from birth',
358
- },
359
- {
360
- key: 'blandness', name: 'Blandness', kind: 'positive', karma: 8, page: 'p.72',
361
- inert: 'no test carries a threshold this could raise; recall and shadowing are not rolled',
362
- family: 'threshold',
363
- effect: 'forgettable on purpose: +1 threshold to recall or shadow you (not vs magic or Matrix)',
364
- },
365
- {
366
- key: 'double-jointed', name: 'Double-Jointed', kind: 'positive', karma: 6, page: 'p.72',
367
- family: 'pool',
368
- effect: '+2 dice on escape-artist work; squeeze where others cannot',
369
- effects: [{ type: 'dice', category: 'skills', property: 'escape-artist', value: 2 }],
370
- },
371
- {
372
- // RAG-checked p.73-74: +1 to the NATURAL MAXIMUM of one attribute,
373
- // never Edge (p.65-66 -- Edge is Lucky's job, and the book says you
374
- // may buy either quality, not both). It does not raise the
375
- // attribute itself: it moves the ceiling and you still pay to climb.
376
- key: 'exceptional-attribute', name: 'Exceptional Attribute', kind: 'positive', karma: 14, page: 'p.72',
377
- inert: 'the engine path is complete -- the web chargen does not yet ask which attribute, and without an answer the ceiling lifts nothing',
378
- family: 'ceiling',
379
- effect: 'one attribute may run 1 above its natural maximum (never Edge -- that is Lucky)',
380
- choice: {
381
- of: 'attribute',
382
- prompt: 'Which attribute runs past the human ceiling?',
383
- excludes: ['edge'],
384
- },
385
- excludesQuality: 'lucky',
386
- effects: [{ type: 'cap', category: 'attributes', property: 'chosen', value: 1 }],
387
- },
388
- {
389
- key: 'focused-concentration', name: 'Focused Concentration',
390
- family: 'engine-rule',
391
- kind: 'positive', karma: 4, maxRating: 6, page: 'p.74', requires: 'caster-or-technomancer',
392
- effect: 'sustain one spell of Force up to your rating with no sustaining penalty',
393
- },
394
- {
395
- key: 'gearhead', name: 'Gearhead', kind: 'positive', karma: 11, page: 'p.74',
396
- inert: 'the piloting roll has no notion of a hard maneuver, and no speed or Handling to boost',
397
- family: 'pool',
398
- effect: 'behind any wheel or stick: +2 dice on hard maneuvers, +20% speed or +1 Handling for 1D6 minutes',
399
- },
400
- {
401
- key: 'guts', name: 'Guts', kind: 'positive', karma: 10, page: 'p.74',
402
- inert: 'nothing rolls to resist fear -- brandish menaces, but the target never tests',
403
- family: 'pool',
404
- effect: '+2 dice to resist fear and intimidation, mundane or magical',
405
- },
406
- {
407
- key: 'home-ground', name: 'Home Ground', kind: 'positive', karma: 10, page: 'p.74',
408
- inert: 'needs a chosen domain (IQualityChoice) and a per-domain bonus seat',
409
- family: 'pool',
410
- effect: 'your turf, one chosen edge on it: the streets, the people, the host, or the bolt-holes',
411
- },
412
- {
413
- key: 'magic-resistance', name: 'Magic Resistance',
414
- family: 'engine-rule',
415
- kind: 'positive', karma: 6, maxRating: 4, page: 'p.76', requires: 'mundane',
416
- effect: '+1 die per rating when you resist a spell -- always on: friendly spells (Heal included) fail on you too',
417
- },
418
- {
419
- key: 'mentor-spirit', name: 'Mentor Spirit', kind: 'positive', karma: 5, page: 'p.76',
420
- inert: 'needs a chosen patron and its per-mentor bonuses and demands (p.320)',
421
- family: 'engine-rule',
422
- requires: 'awakened',
423
- effect: 'a patron spirit guides your magic -- its blessings and its demands (p.320)',
424
- },
425
- {
426
- key: 'natural-hardening', name: 'Natural Hardening', kind: 'positive', karma: 10, page: 'p.76',
427
- family: 'pool',
428
- effect: '1 point of natural biofeedback filtering, cumulative with a filter or living firewall',
429
- effects: [{ type: 'dice', category: 'pools', property: 'biofeedback', value: 1 }],
430
- },
431
- {
432
- key: 'natural-immunity', name: 'Natural Immunity',
433
- choice: {
434
- of: 'hazard-class',
435
- prompt: 'Which does your body simply refuse -- something natural, or something built?',
436
- from: ['natural', 'synthetic'],
437
- },
438
- inert: 'no disease or toxin track exists to be immune to',
439
- family: 'engine-rule',
440
- kind: 'positive', karma: 4, page: 'p.77',
441
- effect: 'immune to ONE chosen natural disease or toxin (never magical ones)',
442
- },
443
- {
444
- key: 'natural-immunity-synthetic', name: 'Natural Immunity (Synthetic)',
445
- inert: 'no disease or toxin track exists to be immune to',
446
- family: 'engine-rule',
447
- kind: 'positive', karma: 10, page: 'p.77',
448
- effect: 'immune to ONE chosen synthetic disease or toxin (never magical ones)',
449
- },
450
- {
451
- key: 'photographic-memory', name: 'Photographic Memory', kind: 'positive', karma: 6, page: 'p.77',
452
- inert: 'nothing rolls a Memory Test',
453
- family: 'pool',
454
- effect: '+2 dice on every Memory Test -- what you saw stays seen',
455
- },
456
- {
457
- key: 'resistance-one', name: 'Resistance (One Class)',
458
- choice: {
459
- of: 'hazard-class',
460
- prompt: 'Which do you shrug off -- pathogens, or toxins?',
461
- from: ['pathogens', 'toxins'],
462
- },
463
- inert: 'nothing rolls to resist a pathogen or a toxin',
464
- family: 'pool',
465
- kind: 'positive', karma: 4, page: 'p.77',
466
- effect: '+1 die to resist one chosen class: pathogens, or toxins',
467
- },
468
- {
469
- key: 'resistance-pathogens-and-toxins', name: 'Resistance to Pathogens AND Toxins',
470
- inert: 'nothing rolls to resist a pathogen or a toxin',
471
- family: 'pool',
472
- kind: 'positive', karma: 8, page: 'p.77',
473
- effect: '+1 die to resist both pathogens and toxins',
474
- },
475
- {
476
- key: 'spirit-affinity', name: 'Spirit Affinity', kind: 'positive', karma: 7, page: 'p.77',
477
- inert: 'needs a chosen spirit type; binding and services are not rolled per type',
478
- family: 'engine-rule',
479
- requires: 'awakened',
480
- effect: 'one spirit type favors you: +1 service from them, +1 die to bind them',
481
- },
482
- // ------------------------------ Negative ------------------------------
483
- {
484
- key: 'addiction-mild', name: 'Addiction (Mild)', kind: 'negative', karma: 4, page: 'p.77-78',
485
- inert: 'no craving clock and no withdrawal state',
486
- family: 'condition',
487
- effect: 'hooked, mildly: cravings monthly; withdrawal bites your tests until fed',
488
- },
489
- {
490
- key: 'addiction-moderate', name: 'Addiction (Moderate)', kind: 'negative', karma: 9, page: 'p.77-78',
491
- inert: 'no craving clock and no withdrawal state',
492
- family: 'condition',
493
- effect: 'hooked: cravings every two weeks; withdrawal bites your tests until fed',
494
- },
495
- {
496
- key: 'allergy-common-mild', name: 'Allergy (Common, Mild)', kind: 'negative', karma: 10, page: 'p.78',
497
- inert: 'no exposure state -- nothing knows you are standing in the sun',
498
- family: 'condition',
499
- effect: 'allergic to something everywhere (sunlight, soy, pollutants): -1 die while exposed, -1 to resist it as a weapon',
500
- },
501
- {
502
- key: 'astral-beacon', name: 'Astral Beacon', kind: 'negative', karma: 10, page: 'p.78-79',
503
- inert: 'no astral signature is recorded',
504
- family: 'engine-rule',
505
- requires: 'awakened',
506
- effect: 'your astral signature burns bright and lasts twice as long -- easier to read, easier to trace',
507
- },
508
- {
509
- key: 'code-of-honor', name: 'Code of Honor', kind: 'negative', karma: 15, page: 'p.80',
510
- inert: 'canon leaves this to the table; the engine adjudicates no vow',
511
- family: 'narrative',
512
- effect: 'a group you will not kill, ever -- non-lethal only, whatever it costs the run',
513
- },
514
- {
515
- key: 'gremlins', name: 'Gremlins', kind: 'negative', karma: 4, maxRating: 4, page: 'p.81',
516
- family: 'threshold',
517
- effect: 'machines hate you: on the deck, a glitch needs one fewer 1 per rating',
518
- },
519
- {
520
- key: 'loss-of-confidence', name: 'Loss of Confidence', kind: 'negative', karma: 10, page: 'p.82',
521
- inert: 'specializations are not modelled, so none can be suspended',
522
- family: 'condition',
523
- effect: 'one skill you were proud of, now doubted -- its specializations unusable while the doubt holds',
524
- },
525
- {
526
- key: 'scorched', name: 'Scorched', kind: 'negative', karma: 10, page: 'p.83',
527
- inert: 'no neurological scar state, and nothing triggers on entering VR',
528
- family: 'condition',
529
- effect: 'black IC or BTL left a mark: one chosen neurological scar that flares in VR',
530
- },
531
- {
532
- key: 'sensitive-system', name: 'Sensitive System', kind: 'negative', karma: 12, page: 'p.83',
533
- inert: 'the clinic charges canon Essence; there is no doubling hook, and bioware is not rejected',
534
- family: 'engine-rule',
535
- effect: 'implants hate you: DOUBLE Essence loss from cyberware; bioware is rejected outright',
536
- },
537
- {
538
- key: 'social-stress', name: 'Social Stress', kind: 'negative', karma: 8, page: 'p.85',
539
- inert: 'no pressure state to read',
540
- family: 'condition',
541
- effect: 'crowds and confrontation wear on you -- emotions interfere when the pressure is social',
542
- },
543
- {
544
- key: 'uneducated', name: 'Uneducated', kind: 'negative', karma: 8, page: 'p.87',
545
- inert: 'knowledge skills are not modelled, and learning costs are flat',
546
- family: 'engine-rule',
547
- effect: 'no schooling: unaware in Technical/Academic/Professional knowledge you do not hold; learning them costs double',
548
- },
549
- {
550
- key: 'unsteady-hands', name: 'Unsteady Hands', kind: 'negative', karma: 7, page: 'p.87',
551
- inert: 'no symptom state, and no fine-handwork test to penalise',
552
- family: 'condition',
553
- effect: 'the shakes: -2 dice on fine handwork when the symptoms surface',
554
- },
555
- {
556
- key: 'weak-immune-system', name: 'Weak Immune System', kind: 'negative', karma: 10, page: 'p.87',
557
- inert: 'no disease track, so no Power to raise',
558
- family: 'engine-rule',
559
- effect: 'disease hits harder: +2 Power on every disease you resist (excludes the immunity qualities)',
560
- },
561
- // ------------------------- Cutting Aces (2026-09-02) -------------------------
562
- // The social sourcebook's qualities (Cutting Aces pp.150-153), read from
563
- // the book itself -- the RAG server returns these pages as sections, not
564
- // as a list. Effects are the book's own words, shortened. Rated and
565
- // table-valued entries follow the catalog's rule (see Focused
566
- // Concentration above): the entry form ships, the key carries no rating
567
- // system. Social Appearance Anxiety ships at rating 1; Favored ships as
568
- // its base row (common target group, biased: 5 karma, one severity
569
- // level) -- the book's Favored Table (p.152) prices the rest.
570
- // ------------------------------ Positive ------------------------------
571
- {
572
- key: 'alibi', name: 'Alibi', kind: 'positive', karma: 4, page: 'Cutting Aces p.150',
573
- inert: 'canon narrows it to plausible evidence, which nothing tracks',
574
- family: 'pool',
575
- effect: 'always the +2 on Con tests for plausible-seeming evidence -- your excuses are pre-made',
576
- },
577
- {
578
- key: 'closer', name: 'Closer', kind: 'positive', karma: 4, page: 'Cutting Aces p.150',
579
- inert: 'canon narrows it to life-or-death stakes, which nothing tracks',
580
- family: 'pool',
581
- effect: '+2 dice on Negotiation when it is a matter of life or death',
582
- },
583
- {
584
- key: 'cynic', name: 'Cynic', kind: 'positive', karma: 6, page: 'Cutting Aces p.150',
585
- inert: 'the engine rolls no opposed pool for an NPC acting socially ON you',
586
- family: 'pool',
587
- effect: 'anyone making a Social test against you rolls at -1',
588
- },
589
- {
590
- key: 'empathic-listener', name: 'Empathic Listener', kind: 'positive', karma: 10, page: 'Cutting Aces p.150',
591
- inert: 'no substitution seat: a pool is assembled from fixed attributes',
592
- family: 'substitution',
593
- effect: 'Intuition stands in for Charisma on Etiquette tests',
594
- },
595
- {
596
- key: 'good-looking-and-knows-it', name: 'Good Looking and Knows It', kind: 'positive', karma: 10, page: 'Cutting Aces p.150',
597
- inert: 'no modifier ledger to ignore two points of',
598
- family: 'threshold',
599
- effect: 'ignore 2 points of negative modifiers on any Social test; +1 Notoriety, and people remember your face (+2 to recall you)',
600
- },
601
- {
602
- key: 'groupthink', name: 'Groupthink', kind: 'positive', karma: 5, page: 'Cutting Aces p.150-151',
603
- inert: 'no social teamwork test, and the assistant/leader roles do not exist',
604
- family: 'pool',
605
- effect: 'social teamwork: as an assistant +1 die and +1 Social limit; as the leader, take -2 to soften an assistant\'s glitch',
606
- },
607
- {
608
- key: 'honest-face', name: 'Honest Face', kind: 'positive', karma: 5, page: 'Cutting Aces p.151',
609
- inert: 'nothing rolls Judge Intentions against you',
610
- family: 'pool',
611
- effect: '+2 dice on your Charisma + Willpower when someone Judges your Intentions',
612
- },
613
- {
614
- key: 'innocuous', name: 'Innocuous', kind: 'positive', karma: 5, page: 'Cutting Aces p.151',
615
- inert: 'canon narrows it to hiding in a crowd, which the room does not model',
616
- family: 'pool',
617
- effect: '+2 on Sneaking tests to hide in a crowd',
618
- },
619
- {
620
- key: 'master-debater', name: 'Master Debater', kind: 'positive', karma: 10, page: 'Cutting Aces p.151',
621
- inert: 'no substitution seat: a pool is assembled from fixed attributes',
622
- family: 'substitution',
623
- effect: 'Logic stands in for Charisma on Diplomacy tests -- you win by crushing their logic',
624
- },
625
- {
626
- key: 'memory-palace', name: 'Memory Palace', kind: 'positive', karma: 6, page: 'Cutting Aces p.151',
627
- inert: 'nothing rolls a Memory Test, and interrogation is not a test',
628
- family: 'pool',
629
- effect: '+1 die on Memory tests and to resist Intimidation (Interrogation)',
630
- },
631
- {
632
- key: 'method-actor', name: 'Method Actor', kind: 'positive', karma: 7, page: 'Cutting Aces p.151',
633
- inert: 'no immersion clock, and Impersonation is not rolled',
634
- family: 'pool',
635
- effect: 'each full day immersed in a role: +1 die and +1 Social limit on Impersonation as that role, up to your Willpower',
636
- },
637
- {
638
- key: 'mnemonic-vault', name: 'Mnemonic Vault', kind: 'positive', karma: 8, page: 'Cutting Aces p.151',
639
- inert: 'interrogation is not a test',
640
- family: 'pool',
641
- requiresQuality: 'memory-palace',
642
- effect: 'lock memories away: +2 dice to resist Interrogation and anything that would compel you to talk; resist, and they believe that was all you knew',
643
- },
644
- {
645
- key: 'watch-the-suit', name: 'Watch the Suit', kind: 'positive', karma: 3, page: 'Cutting Aces p.151',
646
- inert: 'appearance carries no Etiquette penalty to be spared',
647
- family: 'engine-rule',
648
- effect: 'Stun never marks your appearance: no Etiquette penalty for looking roughed up after a fight you took no Physical damage in',
649
- },
650
- // ------------------------------ Negative ------------------------------
651
- {
652
- key: 'alpha-junkie', name: 'Alpha Junkie', kind: 'negative', karma: 12, page: 'Cutting Aces p.151-152',
653
- inert: 'no leadership-over-you state to trigger the Composure test',
654
- family: 'condition',
655
- effect: 'when someone takes charge over you (or wins a Leadership test against you): Charisma + Willpower (3) or you fight for control -- argue, intimidate, or attack',
656
- },
657
- {
658
- key: 'designated-omega', name: 'Designated Omega', kind: 'negative', karma: 5, page: 'Cutting Aces p.152',
659
- family: 'pool',
660
- effect: '-2 dice on every Leadership test, initiating or resisting',
661
- effects: [{ type: 'dice', category: 'skills', property: 'leadership', value: -2 }],
662
- },
663
- {
664
- key: 'disheveled', name: 'Disheveled', kind: 'negative', karma: 5, page: 'Cutting Aces p.152',
665
- inert: 'no positive social modifier ledger to cap',
666
- family: 'threshold',
667
- effect: 'clothing never gives you a positive Social dice pool or limit modifier',
668
- },
669
- {
670
- key: 'favored', name: 'Favored', kind: 'negative', karma: 5, page: 'Cutting Aces p.152',
671
- family: 'pool',
672
- effect: 'a group you cannot see straight (common group, biased): -2 dice on Social tests with them, and they negotiate against you at +2',
673
- effects: [{ type: 'dice', category: 'pools', property: 'social', value: -2 }],
674
- },
675
- {
676
- key: 'one-born-every-minute', name: 'One Born Every Minute', kind: 'negative', karma: 5, page: 'Cutting Aces p.152',
677
- inert: 'nothing rolls for you to resist a Con made against you',
678
- family: 'pool',
679
- effect: '-2 dice to resist every Con test made against you',
680
- },
681
- {
682
- key: 'the-goat', name: 'The Goat', kind: 'negative', karma: 8, page: 'Cutting Aces p.152',
683
- family: 'pool',
684
- effect: 'you just look guilty: everyone meeting you for the first time is suspicious (-1 Social), and security watches you first',
685
- effects: [{ type: 'dice', category: 'pools', property: 'social', value: -1 }],
686
- },
687
- {
688
- key: 'ugly-and-doesnt-care', name: "Ugly and Doesn't Care", kind: 'negative', karma: 10, page: 'Cutting Aces p.152-153',
689
- family: 'attribute',
690
- effect: 'Charisma maximum -1 and starting Charisma -1 (minimum 1)',
691
- effects: [
692
- // BOTH HALVES of the printed line. The attribute effect is the
693
- // "starting Charisma -1"; the cap is the "Charisma maximum -1",
694
- // which nothing enforced until ceilings existed -- the same gap
695
- // Lucky had, found by sweeping the family rather than by a second
696
- // report.
697
- { type: 'attribute', category: 'attributes', property: 'charisma', value: -1 },
698
- { type: 'cap', category: 'attributes', property: 'charisma', value: -1 },
699
- ],
700
- },
701
- ];
702
- /**
703
- * RETIRED FROM CREATION (2026-09-02), resolvable so an existing holder's
704
- * sheet still names them. Each is rated in canon and priced per rating
705
- * here for the day its seat exists.
706
- */
707
- export const RETIRED_QUALITIES = [
708
- {
709
- key: 'indomitable', name: 'Indomitable', kind: 'positive', karma: 8, maxRating: 3, page: 'p.75',
710
- inert: 'the engine models only the Social limit, and no limit may be chosen',
711
- family: 'limit',
712
- effect: '+1 to one Inherent limit of your choice per rating',
713
- retired: 'the engine models only the Social limit, and the choice of limit has no seat yet',
714
- },
715
- {
716
- key: 'will-to-live', name: 'Will to Live', kind: 'positive', karma: 3, maxRating: 3, page: 'p.77',
717
- inert: 'there is no overflow track',
718
- family: 'engine-rule',
719
- effect: '+1 Damage Overflow box per rating -- more room between down and dead, no easier to knock out',
720
- retired: 'the engine has no overflow track: a full Physical track is death',
721
- },
722
- {
723
- key: 'social-appearance-anxiety', name: 'Social Appearance Anxiety', kind: 'negative', karma: 3, maxRating: 3, page: 'Cutting Aces p.152',
724
- inert: 'there is no grooming state',
725
- family: 'condition',
726
- effect: '-1 die per rating on all Social rolls whenever you are not clean, coiffed, and cutting',
727
- retired: 'the engine has no grooming state to judge "clean, coiffed, and cutting"',
728
- },
729
- ];
19
+ // THE ROWS ARE NOT HERE. Qualities -- the ones on sale and the RETIRED
20
+ // ones an existing holder's sheet still names (`quality.retired` on the
21
+ // row) -- are catalog rows of kind 'quality' on maka-cli.com;
22
+ // utilities/catalog.ts's qualityFor() and catalogQualities() rebuild
23
+ // IQuality from a row. This file keeps the shape, the legacy-key
24
+ // resolution and the creation-karma ledger, which the web chargen still
25
+ // loads from the engine.
730
26
  /**
731
27
  * A saved or offered key, resolved: the old fixed "<key>-1" entries
732
28
  * (every rated quality shipped as Rating 1 only) read as key + rating 1.
@@ -771,18 +67,22 @@ export function resolveQualityKey(key) {
771
67
  const m = /^(.*)-1$/.exec(key);
772
68
  if (m) {
773
69
  const base = m[1];
774
- if ([...QUALITIES, ...RETIRED_QUALITIES].some(q => q.key === base && q.maxRating))
70
+ // Rated qualities only: a "-1" suffix on an unrated key is part of
71
+ // the key. Retired rows count -- a save may still hold one.
72
+ if (qualityFor(base)?.maxRating)
775
73
  return { key: base, rating: 1 };
776
74
  }
777
75
  return { key, rating: 1 };
778
76
  }
779
77
  /** Every legacy slug that resolves to a key plus a pick, for tests and tooling. */
780
78
  export const LEGACY_CHOICE_SLUGS = Object.keys(LEGACY_CHOICE_KEYS);
79
+ /** A quality by saved or offered key, legacy forms resolved. Retired
80
+ * rows resolve too (their holder's sheet still names them); a shop
81
+ * filters on `retired` itself. */
781
82
  export function getQuality(key) {
782
83
  if (!key)
783
84
  return undefined;
784
- const k = resolveQualityKey(key).key;
785
- return QUALITIES.find(q => q.key === k) ?? RETIRED_QUALITIES.find(q => q.key === k);
85
+ return qualityFor(resolveQualityKey(key).key);
786
86
  }
787
87
  /** What a quality costs (or refunds) at the rating given: per-rating for rated entries. */
788
88
  export function qualityKarma(q, rating = 1) {